From 5aa112d362aa493515a009f0f6ecb16c0005d66d Mon Sep 17 00:00:00 2001 From: "brendan%mozilla.org" Date: Wed, 11 Feb 2004 07:57:19 +0000 Subject: [PATCH] Checkpoint, still not quite metacircularly bootstrapped. git-svn-id: svn://10.0.0.236/trunk@152662 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/js/narcissus/js.js | 1 + mozilla/js/narcissus/jsexec.js | 132 +++++++++++++++++++++----------- mozilla/js/narcissus/jsparse.js | 13 +++- 3 files changed, 98 insertions(+), 48 deletions(-) diff --git a/mozilla/js/narcissus/js.js b/mozilla/js/narcissus/js.js index a56255b02cb..46ba6c4abb0 100644 --- a/mozilla/js/narcissus/js.js +++ b/mozilla/js/narcissus/js.js @@ -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 diff --git a/mozilla/js/narcissus/jsexec.js b/mozilla/js/narcissus/jsexec.js index f0d8f55bb60..285ad784d06 100644 --- a/mozilla/js/narcissus/jsexec.js +++ b/mozilla/js/narcissus/jsexec.js @@ -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; } diff --git a/mozilla/js/narcissus/jsparse.js b/mozilla/js/narcissus/jsparse.js index 7cc4e46706c..ddbe978cb2a 100644 --- a/mozilla/js/narcissus/jsparse.js +++ b/mozilla/js/narcissus/jsparse.js @@ -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];