Checkpoint, still not quite metacircularly bootstrapped.
git-svn-id: svn://10.0.0.236/trunk@152662 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -50,6 +50,7 @@
|
||||
* Object.prototype.__defineSetter__
|
||||
* Object.prototype.__defineProperty__
|
||||
* Object.prototype.__proto__
|
||||
* filename and line number arguments to *Error constructors
|
||||
*
|
||||
* SpiderMonkey extensions supported metacircularly:
|
||||
* catch guards
|
||||
|
||||
@@ -39,14 +39,10 @@
|
||||
*
|
||||
* Execution of parse trees.
|
||||
*
|
||||
* XXX Standard classes except for eval and Function are bootlegged from the
|
||||
* host JS environment. Until Array and String are metacircular,
|
||||
* load('js.js');
|
||||
* evaluate(snarf('js.js'));
|
||||
* will fail due to pigeon-hole problems in standard class prototypes.
|
||||
* First such error concerns Array.prototype.top not being callable from
|
||||
* the host engine, because it has been overwritten with a target object
|
||||
* (FunctionObject) that the host engine cannot call.
|
||||
* Standard classes except for eval, Function, Array, and String are borrowed
|
||||
* from the host JS environment. Function is metacircular. Array and String
|
||||
* are reflected via wrapping the corresponding native constructor and adding
|
||||
* an extra level of prototype-based delegation.
|
||||
*/
|
||||
|
||||
const GLOBAL_CODE = 0, EVAL_CODE = 1, FUNCTION_CODE = 2;
|
||||
@@ -60,7 +56,7 @@ var global = {
|
||||
NaN: NaN, Infinity: Infinity, undefined: undefined,
|
||||
|
||||
// Function properties.
|
||||
eval: function eval (s) {
|
||||
eval: function eval(s) {
|
||||
if (typeof s != "string")
|
||||
return s;
|
||||
|
||||
@@ -104,8 +100,26 @@ var global = {
|
||||
var s = {object: global, parent: null};
|
||||
return new FunctionObject(f, s);
|
||||
},
|
||||
Array: Array, String: String, Boolean: Boolean, Number: Number,
|
||||
Date: Date, RegExp: RegExp,
|
||||
Array: function Array() {
|
||||
var a = this;
|
||||
if (a instanceof Array) {
|
||||
// Called as constructor.
|
||||
} else {
|
||||
a.__proto__ = Array.prototype;
|
||||
}
|
||||
return a;
|
||||
},
|
||||
String: function String() {
|
||||
var s = this;
|
||||
if (s instanceof String) {
|
||||
// Called as constructor.
|
||||
} else {
|
||||
// Called as function.
|
||||
s = GLOBAL.String.apply(this, arguments);
|
||||
}
|
||||
return s;
|
||||
},
|
||||
Boolean: Boolean, Number: Number, Date: Date, RegExp: RegExp,
|
||||
Error: Error, EvalError: EvalError, RangeError: RangeError,
|
||||
ReferenceError: ReferenceError, SyntaxError: SyntaxError,
|
||||
TypeError: TypeError, URIError: URIError,
|
||||
@@ -114,6 +128,16 @@ var global = {
|
||||
Math: Math
|
||||
};
|
||||
|
||||
// Reflect a host class into the target global environment by delegation.
|
||||
function reflectClass(name, proto) {
|
||||
var gctor = global[name];
|
||||
gctor.__defineProperty__('prototype', proto, true, true, true);
|
||||
proto.__defineProperty__('constructor', gctor, false, false, true);
|
||||
}
|
||||
|
||||
reflectClass('Array', new Array);
|
||||
reflectClass('String', new String);
|
||||
|
||||
var XCp = ExecutionContext.prototype;
|
||||
ExecutionContext.current = XCp.caller = XCp.callee = null;
|
||||
XCp.scope = {object: global, parent: null};
|
||||
@@ -131,17 +155,20 @@ Reference.prototype.toString = function () { return this.node.getSource(); }
|
||||
|
||||
function getValue(v) {
|
||||
if (v instanceof Reference) {
|
||||
if (!v.base)
|
||||
throw new ReferenceError(v.propertyName + " is not defined");
|
||||
if (!v.base) {
|
||||
throw new ReferenceError(v.propertyName + " is not defined",
|
||||
this.node.filename, this.node.lineno);
|
||||
}
|
||||
return v.base[v.propertyName];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
function putValue(v, w) {
|
||||
function putValue(v, w, vn) {
|
||||
if (v instanceof Reference)
|
||||
return (v.base || global)[v.propertyName] = w;
|
||||
throw new ReferenceError("Invalid assignment left-hand side");
|
||||
throw new ReferenceError("Invalid assignment left-hand side",
|
||||
vn.filename, vn.lineno);
|
||||
}
|
||||
|
||||
function isPrimitive(v) {
|
||||
@@ -154,22 +181,25 @@ function isObject(v) {
|
||||
return (t == "object") ? v !== null : t == "function";
|
||||
}
|
||||
|
||||
// If r instanceof Reference, v == getValue(r); else v === r.
|
||||
function toObject(v, r) {
|
||||
// If r instanceof Reference, v == getValue(r); else v === r. If passed, rn
|
||||
// is the node whose execute result was r.
|
||||
function toObject(v, r, rn) {
|
||||
switch (typeof v) {
|
||||
case "undefined":
|
||||
throw new TypeError(r + " has no properties");
|
||||
case "boolean":
|
||||
return new Boolean(v);
|
||||
case "number":
|
||||
return new Number(v);
|
||||
case "string":
|
||||
return new String(v);
|
||||
case "function":
|
||||
return v;
|
||||
case "object":
|
||||
if (v === null)
|
||||
throw new TypeError(r + " has no properties");
|
||||
if (v !== null)
|
||||
return v;
|
||||
}
|
||||
return v;
|
||||
var message = r + " (type " + (typeof v) + ") has no properties";
|
||||
throw rn ? new TypeError(message, rn.filename, rn.lineno)
|
||||
: new TypeError(message);
|
||||
}
|
||||
|
||||
function execute(n, x) {
|
||||
@@ -205,8 +235,10 @@ function execute(n, x) {
|
||||
for (i = 0, j = a.length; i < j; i++) {
|
||||
u = a[i];
|
||||
s = u.name;
|
||||
if (u.readOnly && t.hasOwnProperty(s))
|
||||
throw new TypeError("Redeclaration of const " + s);
|
||||
if (u.readOnly && t.hasOwnProperty(s)) {
|
||||
throw new TypeError("Redeclaration of const " + s,
|
||||
u.filename, u.lineno);
|
||||
}
|
||||
if (u.readOnly || !t.hasOwnProperty(s)) {
|
||||
t.__defineProperty__(s, undefined, x.type != EVAL_CODE,
|
||||
u.readOnly);
|
||||
@@ -288,12 +320,12 @@ function execute(n, x) {
|
||||
execute(u, x);
|
||||
r = n.iterator;
|
||||
s = execute(n.object, x);
|
||||
t = toObject(getValue(s), s);
|
||||
t = toObject(getValue(s), s, n.object);
|
||||
a = [];
|
||||
for (i in t)
|
||||
a.push(i);
|
||||
for (i = 0, j = a.length; i < j; i++) {
|
||||
putValue(execute(r, x), a[i]);
|
||||
putValue(execute(r, x), a[i], r);
|
||||
try {
|
||||
execute(n.body, x);
|
||||
} catch (e if e == BREAK && x.target == n) {
|
||||
@@ -360,7 +392,7 @@ function execute(n, x) {
|
||||
|
||||
case WITH:
|
||||
r = execute(n.object, x);
|
||||
t = toObject(getValue(r), r);
|
||||
t = toObject(getValue(r), r, n.object);
|
||||
x.scope = {object: t, parent: x.scope};
|
||||
try {
|
||||
execute(n.body, x);
|
||||
@@ -429,7 +461,7 @@ function execute(n, x) {
|
||||
case MOD: v = u % v; break;
|
||||
}
|
||||
}
|
||||
putValue(r, v);
|
||||
putValue(r, v, n[0]);
|
||||
break;
|
||||
|
||||
case CONDITIONAL:
|
||||
@@ -572,7 +604,7 @@ function execute(n, x) {
|
||||
u = Number(getValue(t));
|
||||
if (n.postfix)
|
||||
v = u;
|
||||
putValue(t, (n.type == INCREMENT) ? ++u : --u);
|
||||
putValue(t, (n.type == INCREMENT) ? ++u : --u, n[0]);
|
||||
if (!n.postfix)
|
||||
v = u;
|
||||
break;
|
||||
@@ -581,14 +613,14 @@ function execute(n, x) {
|
||||
r = execute(n[0], x);
|
||||
t = getValue(r);
|
||||
u = n[1].value;
|
||||
v = new Reference(toObject(t, r), u, n);
|
||||
v = new Reference(toObject(t, r, n[0]), u, n);
|
||||
break;
|
||||
|
||||
case INDEX:
|
||||
r = execute(n[0], x);
|
||||
t = getValue(r);
|
||||
u = getValue(execute(n[1], x));
|
||||
v = new Reference(toObject(t, r), String(u), n);
|
||||
v = new Reference(toObject(t, r, n[0]), String(u), n);
|
||||
break;
|
||||
|
||||
case LIST:
|
||||
@@ -605,8 +637,10 @@ function execute(n, x) {
|
||||
r = execute(n[0], x);
|
||||
a = execute(n[1], x);
|
||||
f = getValue(r);
|
||||
if (isPrimitive(f) || typeof f.__call__ != "function")
|
||||
throw new TypeError(r + " is not callable");
|
||||
if (isPrimitive(f) || typeof f.__call__ != "function") {
|
||||
throw new TypeError(r + " is not callable",
|
||||
n[0].filename, n[0].lineno);
|
||||
}
|
||||
t = (r instanceof Reference) ? r.base : null;
|
||||
if (t instanceof Activation)
|
||||
t = null;
|
||||
@@ -623,8 +657,10 @@ function execute(n, x) {
|
||||
} else {
|
||||
a = execute(n[1], x);
|
||||
}
|
||||
if (isPrimitive(f) || typeof f.__construct__ != "function")
|
||||
throw new TypeError(r + " is not a constructor");
|
||||
if (isPrimitive(f) || typeof f.__construct__ != "function") {
|
||||
throw new TypeError(r + " is not a constructor",
|
||||
n[0].filename, n[0].lineno);
|
||||
}
|
||||
v = f.__construct__(a, x);
|
||||
break;
|
||||
|
||||
@@ -704,8 +740,8 @@ function FunctionObject(node, scope) {
|
||||
this.scope = scope;
|
||||
this.__defineProperty__('length', node.params.length, true, true, true);
|
||||
var proto = {};
|
||||
proto.__defineProperty__('constructor', this, false, false, true);
|
||||
this.__defineProperty__('prototype', proto, true);
|
||||
proto.__defineProperty__('constructor', this, false, false, true);
|
||||
}
|
||||
|
||||
var FOp = FunctionObject.prototype = {
|
||||
@@ -750,8 +786,10 @@ var FOp = FunctionObject.prototype = {
|
||||
if (isPrimitive(v))
|
||||
return false;
|
||||
var p = this.prototype;
|
||||
if (isPrimitive(p))
|
||||
throw new TypeError("'prototype' property is not an object");
|
||||
if (isPrimitive(p)) {
|
||||
throw new TypeError("'prototype' property is not an object",
|
||||
this.node.filename, this.node.lineno);
|
||||
}
|
||||
var o;
|
||||
while ((o = v.__proto__)) {
|
||||
if (o == p)
|
||||
@@ -789,7 +827,8 @@ var FOp = FunctionObject.prototype = {
|
||||
} else if (!(a instanceof Object)) {
|
||||
// XXX check for a non-arguments object
|
||||
throw new TypeError("Second argument to Function.prototype.apply" +
|
||||
" must be an array or arguments object");
|
||||
" must be an array or arguments object",
|
||||
this.node.filename, this.node.lineno);
|
||||
}
|
||||
|
||||
return this.__call__(t, a, ExecutionContext.current);
|
||||
@@ -802,12 +841,12 @@ var FOp = FunctionObject.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
// Connect Function.prototype and Function.prototype.constructor.
|
||||
global.Function.__defineProperty__('prototype', FOp, true, true, true);
|
||||
FOp.__defineProperty__('constructor', global.Function, false, false, true);
|
||||
// Connect Function.prototype and Function.prototype.constructor in global.
|
||||
reflectClass('Function', FOp);
|
||||
|
||||
// Help native and host-scripted functions be like FunctionObjects.
|
||||
var Fp = Function.prototype;
|
||||
var Rp = RegExp.prototype;
|
||||
|
||||
Fp.__call__ = function (t, a, x) {
|
||||
// Curse ECMA yet again!
|
||||
@@ -815,6 +854,11 @@ Fp.__call__ = function (t, a, x) {
|
||||
return this.apply(t, a);
|
||||
};
|
||||
|
||||
Rp.__call__ = function (t, a, x) {
|
||||
a = Array.prototype.splice.call(a, 0, a.length);
|
||||
return this.exec.apply(this, a);
|
||||
};
|
||||
|
||||
Fp.__construct__ = function (a, x) {
|
||||
switch (a.length) {
|
||||
case 0:
|
||||
@@ -848,12 +892,12 @@ function thunk(f, x) {
|
||||
return function () { return f.__call__(this, arguments, x); };
|
||||
}
|
||||
|
||||
function evaluate(s) {
|
||||
function evaluate(s, f, l) {
|
||||
var x = ExecutionContext.current;
|
||||
var x2 = new ExecutionContext(GLOBAL_CODE);
|
||||
ExecutionContext.current = x2;
|
||||
try {
|
||||
execute(compile(s), x2);
|
||||
execute(compile(s, f, l), x2);
|
||||
} finally {
|
||||
ExecutionContext.current = x;
|
||||
}
|
||||
|
||||
@@ -216,8 +216,8 @@ var CCp = CompilerContext.prototype;
|
||||
CCp.bracketLevel = CCp.curlyLevel = CCp.parenLevel = CCp.hookLevel = 0;
|
||||
CCp.inForLoopInit = false;
|
||||
|
||||
function compile(s) {
|
||||
var t = new Tokenizer(s);
|
||||
function compile(s, f, l) {
|
||||
var t = new Tokenizer(s, f, l);
|
||||
var x = new CompilerContext(false);
|
||||
var n = Script(t, x);
|
||||
if (!t.done)
|
||||
@@ -292,6 +292,9 @@ Np.getSource = function () {
|
||||
return this.tokenizer.source.slice(this.start, this.end);
|
||||
};
|
||||
|
||||
Np.__defineGetter__('filename',
|
||||
function () { return this.tokenizer.filename; });
|
||||
|
||||
String.prototype.repeat = function (n) {
|
||||
var s = "", t = this + s;
|
||||
while (--n >= 0)
|
||||
@@ -398,8 +401,10 @@ function Statement(t, x) {
|
||||
if (n2 && t.match(IN)) {
|
||||
n.type = FOR_IN;
|
||||
if (n2.type == VAR) {
|
||||
if (n2.length != 1)
|
||||
throw new SyntaxError("Invalid for..in left-hand side");
|
||||
if (n2.length != 1) {
|
||||
throw new SyntaxError("Invalid for..in left-hand side",
|
||||
t.filename, n2.lineno);
|
||||
}
|
||||
|
||||
// NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name.
|
||||
n.iterator = n2[0];
|
||||
|
||||
Reference in New Issue
Block a user