1 /* ***** BEGIN LICENSE BLOCK *****
2 * vim: set ts=4 sw=4 et tw=80:
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is the Narcissus JavaScript engine.
18 * The Initial Developer of the Original Code is
19 * Brendan Eich <brendan@mozilla.org>.
20 * Portions created by the Initial Developer are Copyright (C) 2004
21 * the Initial Developer. All Rights Reserved.
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 * Narcissus - JS implemented in JS.
42 * Execution of parse trees.
44 * Standard classes except for eval, Function, Array, and String are borrowed
45 * from the host JS environment. Function is metacircular. Array and String
46 * are reflected via wrapping the corresponding native constructor and adding
47 * an extra level of prototype-based delegation.
50 const GLOBAL_CODE
= 0, EVAL_CODE
= 1, FUNCTION_CODE
= 2;
52 function ExecutionContext(type
) {
58 NaN
: NaN
, Infinity
: Infinity
, undefined: undefined,
60 // Function properties.
61 eval
: function eval(s
) {
62 if (typeof s
!= "string")
65 var x
= ExecutionContext
.current
;
66 var x2
= new ExecutionContext(EVAL_CODE
);
67 x2
.thisObject
= x
.thisObject
;
71 ExecutionContext
.current
= x2
;
73 execute(parse(s
), x2
);
74 } catch (e
if e
== THROW
) {
78 ExecutionContext
.current
= x
;
82 parseInt
: parseInt
, parseFloat
: parseFloat
,
83 isNaN
: isNaN
, isFinite
: isFinite
,
84 decodeURI
: decodeURI
, encodeURI
: encodeURI
,
85 decodeURIComponent
: decodeURIComponent
,
86 encodeURIComponent
: encodeURIComponent
,
88 // Class constructors. Where ECMA-262 requires C.length == 1, we declare
89 // a dummy formal parameter.
91 Function
: function Function(dummy
) {
92 var p
= "", b
= "", n
= arguments
.length
;
97 for (var k
= 1; k
< m
; k
++)
98 p
+= "," + arguments
[k
];
103 // XXX We want to pass a good file and line to the tokenizer.
104 // Note the anonymous name to maintain parity with Spidermonkey.
105 var t
= new Tokenizer("anonymous(" + p
+ ") {" + b
+ "}");
107 // NB: Use the STATEMENT_FORM constant since we don't want to push this
108 // function onto the null compilation context.
109 var f
= FunctionDefinition(t
, null, false, STATEMENT_FORM
);
110 var s
= {object
: global
, parent
: null};
111 return new FunctionObject(f
, s
);
113 Array
: function Array(dummy
) {
114 // Array when called as a function acts as a constructor.
115 return GLOBAL
.Array
.apply(this, arguments
);
117 String
: function String(s
) {
118 // Called as function or constructor: convert argument to string type.
119 s
= arguments
.length
? "" + s
: "";
120 if (this instanceof String
) {
121 // Called as constructor: save the argument as the string value
122 // of this String object and return this object.
128 Boolean
: Boolean
, Number
: Number
, Date
: Date
, RegExp
: RegExp
,
129 Error
: Error
, EvalError
: EvalError
, RangeError
: RangeError
,
130 ReferenceError
: ReferenceError
, SyntaxError
: SyntaxError
,
131 TypeError
: TypeError
, URIError
: URIError
,
136 // Extensions to ECMA.
137 snarf
: snarf
, evaluate
: evaluate
,
138 load
: function load(s
) {
139 if (typeof s
!= "string")
142 evaluate(snarf(s
), s
, 1)
144 print
: print
, version
: null
147 // Helper to avoid Object.prototype.hasOwnProperty polluting scope objects.
148 function hasDirectProperty(o
, p
) {
149 return Object
.prototype.hasOwnProperty
.call(o
, p
);
152 // Reflect a host class into the target global environment by delegation.
153 function reflectClass(name
, proto
) {
154 var gctor
= global
[name
];
155 gctor
.__defineProperty__('prototype', proto
, true, true, true);
156 proto
.__defineProperty__('constructor', gctor
, false, false, true);
160 // Reflect Array -- note that all Array methods are generic.
161 reflectClass('Array', new Array
);
163 // Reflect String, overriding non-generic methods.
164 var gSp
= reflectClass('String', new String
);
165 gSp
.toSource = function () { return this.value
.toSource(); };
166 gSp
.toString = function () { return this.value
; };
167 gSp
.valueOf = function () { return this.value
; };
168 global
.String
.fromCharCode
= String
.fromCharCode
;
170 var XCp
= ExecutionContext
.prototype;
171 ExecutionContext
.current
= XCp
.caller
= XCp
.callee
= null;
172 XCp
.scope
= {object
: global
, parent
: null};
173 XCp
.thisObject
= global
;
174 XCp
.result
= undefined;
176 XCp
.ecmaStrictMode
= false;
178 function Reference(base
, propertyName
, node
) {
180 this.propertyName
= propertyName
;
184 Reference
.prototype.toString = function () { return this.node
.getSource(); }
186 function getValue(v
) {
187 if (v
instanceof Reference
) {
189 throw new ReferenceError(v
.propertyName
+ " is not defined",
190 v
.node
.filename
, v
.node
.lineno
);
192 return v
.base
[v
.propertyName
];
197 function putValue(v
, w
, vn
) {
198 if (v
instanceof Reference
)
199 return (v
.base
|| global
)[v
.propertyName
] = w
;
200 throw new ReferenceError("Invalid assignment left-hand side",
201 vn
.filename
, vn
.lineno
);
204 function isPrimitive(v
) {
206 return (t
== "object") ? v
=== null : t
!= "function";
209 function isObject(v
) {
211 return (t
== "object") ? v
!== null : t
== "function";
214 // If r instanceof Reference, v == getValue(r); else v === r. If passed, rn
215 // is the node whose execute result was r.
216 function toObject(v
, r
, rn
) {
219 return new global
.Boolean(v
);
221 return new global
.Number(v
);
223 return new global
.String(v
);
230 var message
= r
+ " (type " + (typeof v
) + ") has no properties";
231 throw rn
? new TypeError(message
, rn
.filename
, rn
.lineno
)
232 : new TypeError(message
);
235 function execute(n
, x
) {
236 var a
, f
, i
, j
, r
, s
, t
, u
, v
;
240 if (n
.functionForm
!= DECLARED_FORM
) {
241 if (!n
.name
|| n
.functionForm
== STATEMENT_FORM
) {
242 v
= new FunctionObject(n
, x
.scope
);
243 if (n
.functionForm
== STATEMENT_FORM
)
244 x
.scope
.object
.__defineProperty__(n
.name
, v
, true);
247 x
.scope
= {object
: t
, parent
: x
.scope
};
249 v
= new FunctionObject(n
, x
.scope
);
250 t
.__defineProperty__(n
.name
, v
, true, true);
252 x
.scope
= x
.scope
.parent
;
261 for (i
= 0, j
= a
.length
; i
< j
; i
++) {
263 f
= new FunctionObject(a
[i
], x
.scope
);
264 t
.__defineProperty__(s
, f
, x
.type
!= EVAL_CODE
);
267 for (i
= 0, j
= a
.length
; i
< j
; i
++) {
270 if (u
.readOnly
&& hasDirectProperty(t
, s
)) {
271 throw new TypeError("Redeclaration of const " + s
,
272 u
.filename
, u
.lineno
);
274 if (u
.readOnly
|| !hasDirectProperty(t
, s
)) {
275 t
.__defineProperty__(s
, undefined, x
.type
!= EVAL_CODE
,
282 for (i
= 0, j
= n
.length
; i
< j
; i
++)
287 if (getValue(execute(n
.condition
, x
)))
288 execute(n
.thenPart
, x
);
290 execute(n
.elsePart
, x
);
294 s
= getValue(execute(n
.discriminant
, x
));
296 var matchDefault
= false;
298 for (i
= 0, j
= a
.length
; ; i
++) {
300 if (n
.defaultIndex
>= 0) {
301 i
= n
.defaultIndex
- 1; // no case matched, do default
305 break; // no default, exit switch_loop
307 t
= a
[i
]; // next case (might be default!)
308 if (t
.type
== CASE
) {
309 u
= getValue(execute(t
.caseLabel
, x
));
311 if (!matchDefault
) // not defaulting, skip for now
313 u
= s
; // force match to do default
316 for (;;) { // this loop exits switch_loop
317 if (t
.statements
.length
) {
319 execute(t
.statements
, x
);
320 } catch (e
if e
== BREAK
&& x
.target
== n
) {
334 n
.setup
&& getValue(execute(n
.setup
, x
));
337 while (!n
.condition
|| getValue(execute(n
.condition
, x
))) {
340 } catch (e
if e
== BREAK
&& x
.target
== n
) {
342 } catch (e
if e
== CONTINUE
&& x
.target
== n
) {
345 n
.update
&& getValue(execute(n
.update
, x
));
354 s
= execute(n
.object
, x
);
357 // ECMA deviation to track extant browser JS implementation behavior.
358 t
= (v
== null && !x
.ecmaStrictMode
) ? v
: toObject(v
, s
, n
.object
);
362 for (i
= 0, j
= a
.length
; i
< j
; i
++) {
363 putValue(execute(r
, x
), a
[i
], r
);
366 } catch (e
if e
== BREAK
&& x
.target
== n
) {
368 } catch (e
if e
== CONTINUE
&& x
.target
== n
) {
378 } catch (e
if e
== BREAK
&& x
.target
== n
) {
380 } catch (e
if e
== CONTINUE
&& x
.target
== n
) {
383 } while (getValue(execute(n
.condition
, x
)));
393 execute(n
.tryBlock
, x
);
394 } catch (e
if e
== THROW
&& (j
= n
.catchClauses
.length
)) {
396 x
.result
= undefined;
402 t
= n
.catchClauses
[i
];
403 x
.scope
= {object
: {}, parent
: x
.scope
};
404 x
.scope
.object
.__defineProperty__(t
.varName
, e
, true);
406 if (t
.guard
&& !getValue(execute(t
.guard
, x
)))
411 x
.scope
= x
.scope
.parent
;
416 execute(n
.finallyBlock
, x
);
421 x
.result
= getValue(execute(n
.exception
, x
));
425 x
.result
= getValue(execute(n
.value
, x
));
429 r
= execute(n
.object
, x
);
430 t
= toObject(getValue(r
), r
, n
.object
);
431 x
.scope
= {object
: t
, parent
: x
.scope
};
435 x
.scope
= x
.scope
.parent
;
441 for (i
= 0, j
= n
.length
; i
< j
; i
++) {
442 u
= n
[i
].initializer
;
446 for (s
= x
.scope
; s
; s
= s
.parent
) {
447 if (hasDirectProperty(s
.object
, t
))
450 u
= getValue(execute(u
, x
));
452 s
.object
.__defineProperty__(t
, u
, x
.type
!= EVAL_CODE
, true);
459 throw "NYI: " + tokens
[n
.type
];
463 x
.result
= getValue(execute(n
.expression
, x
));
468 execute(n
.statement
, x
);
469 } catch (e
if e
== BREAK
&& x
.target
== n
) {
474 for (i
= 0, j
= n
.length
; i
< j
; i
++)
475 v
= getValue(execute(n
[i
], x
));
479 r
= execute(n
[0], x
);
483 v
= getValue(execute(n
[1], x
));
486 case BITWISE_OR
: v
= u
| v
; break;
487 case BITWISE_XOR
: v
= u
^ v
; break;
488 case BITWISE_AND
: v
= u
& v
; break;
489 case LSH
: v
= u
<< v
; break;
490 case RSH
: v
= u
>> v
; break;
491 case URSH
: v
= u
>>> v
; break;
492 case PLUS
: v
= u
+ v
; break;
493 case MINUS
: v
= u
- v
; break;
494 case MUL
: v
= u
* v
; break;
495 case DIV
: v
= u
/ v
; break;
496 case MOD
: v
= u
% v
; break;
499 putValue(r
, v
, n
[0]);
503 v
= getValue(execute(n
[0], x
)) ? getValue(execute(n
[1], x
))
504 : getValue(execute(n
[2], x
));
508 v
= getValue(execute(n
[0], x
)) || getValue(execute(n
[1], x
));
512 v
= getValue(execute(n
[0], x
)) && getValue(execute(n
[1], x
));
516 v
= getValue(execute(n
[0], x
)) | getValue(execute(n
[1], x
));
520 v
= getValue(execute(n
[0], x
)) ^ getValue(execute(n
[1], x
));
524 v
= getValue(execute(n
[0], x
)) & getValue(execute(n
[1], x
));
528 v
= getValue(execute(n
[0], x
)) == getValue(execute(n
[1], x
));
532 v
= getValue(execute(n
[0], x
)) != getValue(execute(n
[1], x
));
536 v
= getValue(execute(n
[0], x
)) === getValue(execute(n
[1], x
));
540 v
= getValue(execute(n
[0], x
)) !== getValue(execute(n
[1], x
));
544 v
= getValue(execute(n
[0], x
)) < getValue(execute(n
[1], x
));
548 v
= getValue(execute(n
[0], x
)) <= getValue(execute(n
[1], x
));
552 v
= getValue(execute(n
[0], x
)) >= getValue(execute(n
[1], x
));
556 v
= getValue(execute(n
[0], x
)) > getValue(execute(n
[1], x
));
560 v
= getValue(execute(n
[0], x
)) in getValue(execute(n
[1], x
));
564 t
= getValue(execute(n
[0], x
));
565 u
= getValue(execute(n
[1], x
));
566 if (isObject(u
) && typeof u
.__hasInstance__
== "function")
567 v
= u
.__hasInstance__(t
);
573 v
= getValue(execute(n
[0], x
)) << getValue(execute(n
[1], x
));
577 v
= getValue(execute(n
[0], x
)) >> getValue(execute(n
[1], x
));
581 v
= getValue(execute(n
[0], x
)) >>> getValue(execute(n
[1], x
));
585 v
= getValue(execute(n
[0], x
)) + getValue(execute(n
[1], x
));
589 v
= getValue(execute(n
[0], x
)) - getValue(execute(n
[1], x
));
593 v
= getValue(execute(n
[0], x
)) * getValue(execute(n
[1], x
));
597 v
= getValue(execute(n
[0], x
)) / getValue(execute(n
[1], x
));
601 v
= getValue(execute(n
[0], x
)) % getValue(execute(n
[1], x
));
605 t
= execute(n
[0], x
);
606 v
= !(t
instanceof Reference
) || delete t
.base
[t
.propertyName
];
610 getValue(execute(n
[0], x
));
614 t
= execute(n
[0], x
);
615 if (t
instanceof Reference
)
616 t
= t
.base
? t
.base
[t
.propertyName
] : undefined;
621 v
= !getValue(execute(n
[0], x
));
625 v
= ~getValue(execute(n
[0], x
));
629 v
= +getValue(execute(n
[0], x
));
633 v
= -getValue(execute(n
[0], x
));
638 t
= execute(n
[0], x
);
639 u
= Number(getValue(t
));
642 putValue(t
, (n
.type
== INCREMENT
) ? ++u
: --u
, n
[0]);
648 r
= execute(n
[0], x
);
651 v
= new Reference(toObject(t
, r
, n
[0]), u
, n
);
655 r
= execute(n
[0], x
);
657 u
= getValue(execute(n
[1], x
));
658 v
= new Reference(toObject(t
, r
, n
[0]), String(u
), n
);
662 // Curse ECMA for specifying that arguments is not an Array object!
664 for (i
= 0, j
= n
.length
; i
< j
; i
++) {
665 u
= getValue(execute(n
[i
], x
));
666 v
.__defineProperty__(i
, u
, false, false, true);
668 v
.__defineProperty__('length', i
, false, false, true);
672 r
= execute(n
[0], x
);
673 a
= execute(n
[1], x
);
675 if (isPrimitive(f
) || typeof f
.__call__
!= "function") {
676 throw new TypeError(r
+ " is not callable",
677 n
[0].filename
, n
[0].lineno
);
679 t
= (r
instanceof Reference
) ? r
.base
: null;
680 if (t
instanceof Activation
)
682 v
= f
.__call__(t
, a
, x
);
687 r
= execute(n
[0], x
);
691 a
.__defineProperty__('length', 0, false, false, true);
693 a
= execute(n
[1], x
);
695 if (isPrimitive(f
) || typeof f
.__construct__
!= "function") {
696 throw new TypeError(r
+ " is not a constructor",
697 n
[0].filename
, n
[0].lineno
);
699 v
= f
.__construct__(a
, x
);
704 for (i
= 0, j
= n
.length
; i
< j
; i
++) {
706 v
[i
] = getValue(execute(n
[i
], x
));
713 for (i
= 0, j
= n
.length
; i
< j
; i
++) {
715 if (t
.type
== PROPERTY_INIT
) {
716 v
[t
[0].value
] = getValue(execute(t
[1], x
));
718 f
= new FunctionObject(t
, x
.scope
);
719 u
= (t
.type
== GETTER
) ? '__defineGetter__'
720 : '__defineSetter__';
721 v
[u
](t
.name
, thunk(f
, x
));
743 for (s
= x
.scope
; s
; s
= s
.parent
) {
744 if (n
.value
in s
.object
)
747 v
= new Reference(s
&& s
.object
, n
.value
, n
);
757 v
= execute(n
[0], x
);
761 throw "PANIC: unknown operation " + n
.type
+ ": " + uneval(n
);
767 function Activation(f
, a
) {
768 for (var i
= 0, j
= f
.params
.length
; i
< j
; i
++)
769 this.__defineProperty__(f
.params
[i
], a
[i
], true);
770 this.__defineProperty__('arguments', a
, true);
773 // Null Activation.prototype's proto slot so that Object.prototype.* does not
774 // pollute the scope of heavyweight functions. Also delete its 'constructor'
775 // property so that it doesn't pollute function scopes. But first, we must
776 // copy __defineProperty__ down from Object.prototype.
778 Activation
.prototype.__defineProperty__
= Object
.prototype.__defineProperty__
;
779 Activation
.prototype.__proto__
= null;
780 delete Activation
.prototype.constructor;
782 function FunctionObject(node
, scope
) {
785 this.__defineProperty__('length', node
.params
.length
, true, true, true);
787 this.__defineProperty__('prototype', proto
, true);
788 proto
.__defineProperty__('constructor', this, false, false, true);
791 var FOp
= FunctionObject
.prototype = {
793 __call__: function (t
, a
, x
) {
794 var x2
= new ExecutionContext(FUNCTION_CODE
);
795 x2
.thisObject
= t
|| global
;
798 a
.__defineProperty__('callee', this, false, false, true);
800 x2
.scope
= {object
: new Activation(f
, a
), parent
: this.scope
};
802 ExecutionContext
.current
= x2
;
805 } catch (e
if e
== RETURN
) {
807 } catch (e
if e
== THROW
) {
808 x
.result
= x2
.result
;
811 ExecutionContext
.current
= x
;
816 __construct__: function (a
, x
) {
818 var p
= this.prototype;
821 // else o.__proto__ defaulted to Object.prototype
823 var v
= this.__call__(o
, a
, x
);
829 __hasInstance__: function (v
) {
832 var p
= this.prototype;
833 if (isPrimitive(p
)) {
834 throw new TypeError("'prototype' property is not an object",
835 this.node
.filename
, this.node
.lineno
);
838 while ((o
= v
.__proto__
)) {
847 toString: function () {
848 return this.node
.getSource();
851 apply: function (t
, a
) {
853 if (typeof this.__call__
!= "function") {
854 throw new TypeError("Function.prototype.apply called on" +
855 " uncallable object");
858 if (t
=== undefined || t
=== null)
860 else if (typeof t
!= "object")
863 if (a
=== undefined || a
=== null) {
865 a
.__defineProperty__('length', 0, false, false, true);
866 } else if (a
instanceof Array
) {
868 for (var i
= 0, j
= a
.length
; i
< j
; i
++)
869 v
.__defineProperty__(i
, a
[i
], false, false, true);
870 v
.__defineProperty__('length', i
, false, false, true);
872 } else if (!(a
instanceof Object
)) {
873 // XXX check for a non-arguments object
874 throw new TypeError("Second argument to Function.prototype.apply" +
875 " must be an array or arguments object",
876 this.node
.filename
, this.node
.lineno
);
879 return this.__call__(t
, a
, ExecutionContext
.current
);
883 // Curse ECMA a third time!
884 var a
= Array
.prototype.splice
.call(arguments
, 1);
885 return this.apply(t
, a
);
889 // Connect Function.prototype and Function.prototype.constructor in global.
890 reflectClass('Function', FOp
);
892 // Help native and host-scripted functions be like FunctionObjects.
893 var Fp
= Function
.prototype;
894 var REp
= RegExp
.prototype;
896 if (!('__call__' in Fp
)) {
897 Fp
.__defineProperty__('__call__', function (t
, a
, x
) {
898 // Curse ECMA yet again!
899 a
= Array
.prototype.splice
.call(a
, 0, a
.length
);
900 return this.apply(t
, a
);
901 }, true, true, true);
903 REp
.__defineProperty__('__call__', function (t
, a
, x
) {
904 a
= Array
.prototype.splice
.call(a
, 0, a
.length
);
905 return this.exec
.apply(this, a
);
906 }, true, true, true);
908 Fp
.__defineProperty__('__construct__', function (a
, x
) {
909 a
= Array
.prototype.splice
.call(a
, 0, a
.length
);
910 return this.__applyConstructor__(a
);
911 }, true, true, true);
913 // Since we use native functions such as Date along with host ones such
914 // as global.eval, we want both to be considered instances of the native
915 // Function constructor.
916 Fp
.__defineProperty__('__hasInstance__', function (v
) {
917 return v
instanceof Function
|| v
instanceof global
.Function
;
918 }, true, true, true);
921 function thunk(f
, x
) {
922 return function () { return f
.__call__(this, arguments
, x
); };
925 function evaluate(s
, f
, l
) {
926 if (typeof s
!= "string")
929 var x
= ExecutionContext
.current
;
930 var x2
= new ExecutionContext(GLOBAL_CODE
);
931 ExecutionContext
.current
= x2
;
933 execute(parse(s
, f
, l
), x2
);
934 } catch (e
if e
== THROW
) {
936 x
.result
= x2
.result
;
941 ExecutionContext
.current
= x
;