1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
|
ESHostobj: module
{
#
# extensible interface for adding host objects
#
# any implementation must obey the rules of the interpreter.
# it is an error to return bogus values, and may cause
# the interpreter to crash.
#
# get/put must return/set the value of property in o
#
# canput and hasproperty return es->true or es->false
#
# defaultval must return a primitive (non-object) value.
# this means it can't return a String object, etc.
#
# call gets the caller's execution context in ex.
# the new this value is passed as an argument,
# but no new scopechain is allocated
# it returns a reference, which is typically just a value
#
# construct should make up a new object
#
get: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
put: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string, val: ref Ecmascript->Val);
canput: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
hasproperty: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
delete: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string);
defaultval: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, tyhint: int): ref Ecmascript->Val;
call: fn(ex: ref Ecmascript->Exec, func, this: ref Ecmascript->Obj, args: array of ref Ecmascript->Val, eval: int): ref Ecmascript->Ref;
construct: fn(ex: ref Ecmascript->Exec, func: ref Ecmascript->Obj, args: array of ref Ecmascript->Val): ref Ecmascript->Obj;
};
#
# calls to init and mkexec do the following
# math->FPcontrol(0, Math->INVAL|Math->ZDIV|Math->OVFL|Math->UNFL|Math->INEX);
#
Ecmascript: module
{
PATH: con "/dis/lib/ecmascript.dis";
#
# an execution context
#
Exec: adt
{
#
# well known glop
#
objproto: cyclic ref Obj;
funcproto: cyclic ref Obj;
strproto: cyclic ref Obj;
numproto: cyclic ref Obj;
boolproto: cyclic ref Obj;
arrayproto: cyclic ref Obj;
dateproto: cyclic ref Obj;
regexpproto: cyclic ref Obj;
errproto: cyclic ref Obj;
evlerrproto: cyclic ref Obj;
ranerrproto: cyclic ref Obj;
referrproto: cyclic ref Obj;
synerrproto: cyclic ref Obj;
typerrproto: cyclic ref Obj;
urierrproto: cyclic ref Obj;
interrproto: cyclic ref Obj;
global: cyclic ref Obj;
this: cyclic ref Obj;
scopechain: cyclic list of ref Obj;
error: string;
errval: cyclic ref Val;
#
# private, keep out
#
stack: cyclic array of ref Ref;
sp: int;
};
#
# must be called at the dawn of time
# returns error string
init: fn(): string;
#
# initialize a new global execution context
# if go is supplied, it's the global object
# if not, one is made up automatically
#
mkexec: fn(go: ref Obj): ref Exec;
#
# throw a runtime error
# msg ends up in ex.error, and an
# "ecmascript runtime error" is raised
#
RUNTIME: con "ecmascript runtime error";
runtime: fn(ex: ref Exec, o: ref Obj, msg: string);
# runtime errors
EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError, InternalError: ref Obj;
#
# debug flags: array of 256 indexed by char
#
# e print ops as they are executed
# f abort on an internal error
# p print parsed code
# r abort on any runtime error
# v print value of expression statements
#
debug: array of int;
#
# parse and runt the source string
#
eval: fn(ex: ref Exec, src: string): Completion;
Re: type ref Arena;
# the fundamental data structure
Obj: adt
{
props: cyclic array of ref Prop;
prototype: cyclic ref Obj; # some builtin properties
val: cyclic ref Val;
call: cyclic ref Call;
construct: cyclic ref Call;
class: string;
host: ESHostobj; # method suite for host objects
re: Re; # compiled regexp for RegExp objects
};
Call: adt
{
params: array of string;
code: cyclic ref Code;
ex: cyclic ref Exec;
};
# attributes
ReadOnly, DontEnum, DontDelete: con 1 << iota;
Prop: adt
{
attr: int;
name: string;
val: cyclic ref RefVal;
};
# an extra level of indirection, because sometimes properties are aliased
RefVal: adt
{
val: cyclic ref Val;
};
# types of js values
TUndef, TNull, TBool, TNum, TStr, TObj, TRegExp, NoHint: con iota;
Val: adt
{
ty: int;
num: real;
str: string;
obj: cyclic ref Obj;
rev: ref REval;
};
# regular expression
REval: adt
{
p: string;
f: string;
i: int;
};
# intermediate result of expression evaluation
Ref: adt
{
isref: int;
val: ref Val;
base: ref Obj;
name: string; # name of property within base
};
# completion values of statements
CNormal, CBreak, CContinue, CReturn, CThrow: con iota;
Completion: adt
{
kind: int;
val: ref Val;
lab: string;
};
Code: adt
{
ops: array of byte; # all instructions
npc: int; # end of active portion of ops
vars: cyclic array of ref Prop; # variables defined in the code
ids: array of string; # ids used in the code
strs: array of string; # string literal
nums: array of real; # numerical literals
fexps: cyclic array of ref Obj; # function expressions
};
#
# stuff for adding host objects
#
# ecmascript is also a host object;
get: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
put: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string, val: ref Ecmascript->Val);
canput: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
hasproperty: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
delete: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string);
defaultval: fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, tyhint: int): ref Ecmascript->Val;
call: fn(ex: ref Ecmascript->Exec, func, this: ref Ecmascript->Obj, args: array of ref Ecmascript->Val, eval: int): ref Ecmascript->Ref;
construct: fn(ex: ref Ecmascript->Exec, func: ref Ecmascript->Obj, args: array of ref Ecmascript->Val): ref Ecmascript->Obj;
#
# return the named variable from the scope chain sc
#
bivar: fn(ex: ref Exec, sc: list of ref Obj, s: string): ref Val;
#
# return the nth argument value, or undefined if too far
#
biarg: fn(args: array of ref Val, n: int): ref Val;
#
# make up a new object
# most often called as mkobj(ex.objproto, "Object")
#
mkobj: fn(proto: ref Obj, class: string): ref Obj;
#
# object installation helpers
#
Builtin: adt
{
name: string;
val: string;
params: array of string;
length: int;
};
biinst: fn(o: ref Obj, bi: Builtin, proto: ref Obj, h: ESHostobj): ref Obj;
biminst: fn(o: ref Obj, bis: array of Builtin, proto: ref Obj, h: ESHostobj);
#
# instantiate a new variable inside an object
#
varinstant: fn(in: ref Obj, attr: int, name: string, val: ref RefVal);
#
# various constructors
#
objval: fn(o: ref Obj): ref Val;
strval: fn(s: string): ref Val;
numval: fn(r: real): ref Val;
valref: fn(v: ref Val): ref Ref;
#
# conversion routines defined in section 9
#
toPrimitive: fn(ex: ref Exec, v: ref Val, ty: int): ref Val;
toBoolean: fn(ex: ref Exec, v: ref Val): ref Val;
toNumber: fn(ex: ref Exec, v: ref Val): real;
toInteger: fn(ex: ref Exec, v: ref Val): real;
toInt32: fn(ex: ref Exec, v: ref Val): int;
toUint32: fn(ex: ref Exec, v: ref Val): big;
toUint16: fn(ex: ref Exec, v: ref Val): int;
toString: fn(ex: ref Exec, v: ref Val): string;
toObject: fn(ex: ref Exec, v: ref Val): ref Obj;
#
# simple coercion routines to force
# Boolean, String, and Number values to objects and vice versa
#
coerceToObj: fn(ex: ref Exec, v: ref Val): ref Val;
coerceToVal: fn(v: ref Val): ref Val;
#
# object/value kind checkers
#
isstrobj: fn(o: ref Obj): int;
isnumobj: fn(o: ref Obj): int;
isboolobj: fn(o: ref Obj): int;
isdateobj: fn(o: ref Obj): int;
isregexpobj: fn(o: ref Obj): int;
isarray: fn(o: ref Obj): int;
isstr: fn(v: ref Val): int;
isnum: fn(v: ref Val): int;
isbool: fn(v: ref Val): int;
isobj: fn(v: ref Val): int;
#
# well-known ecmascript primitive values
#
undefined: ref Val;
true: ref Val;
false: ref Val;
null: ref Val;
# regexp data structures
refRex: type int; # used instead of ref Rex to avoid circularity
Set: adt { # character class
neg: int; # 0 or 1
ascii: array of int; # ascii members, bit array
unicode: list of (int,int); # non-ascii char ranges
subset: cyclic list of ref Set;
};
Nstate: adt{
m: int;
n: int;
};
Rex: adt { # node in parse of regex, or state of fsm
kind: int; # kind of node: char or ALT, CAT, etc
left: refRex; # left descendant
right: refRex; # right descendant, or next state
set: ref Set; # character class
pno: int;
greedy: int;
ns: ref Nstate;
};
Arena: adt { # free store from which nodes are allocated
rex: array of Rex;
ptr: refRex; # next available space
start: refRex; # root of parse, or start of fsm
pno: int;
};
};
|