author | David Mandelin <dmandelin@mozilla.com> |
Wed, 11 Aug 2010 11:05:24 -0700 | |
changeset 53381 | 2cecc5d72edfe977bd795fa8f1fc77617cdda1f3 |
parent 53380 | 13495e9f957ed297e21c723764b05ea550f17f41 (current diff) |
parent 50463 | 68a9a3355a6303f0c704b2df04c9347fd6215dc2 (diff) |
child 53382 | 25bff33134218bafd3ca0d2fa38778765e2417be |
push id | 1 |
push user | root |
push date | Tue, 26 Apr 2011 22:38:44 +0000 |
treeherder | mozilla-beta@bfdb6e623a36 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 2.0b4pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/configure.in | file | annotate | diff | comparison | revisions | |
js/src/jsapi.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsapi.h | file | annotate | diff | comparison | revisions | |
js/src/jscntxt.h | file | annotate | diff | comparison | revisions | |
js/src/jsdtracef.h | file | annotate | diff | comparison | revisions | |
js/src/jsinterp.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsobj.cpp | file | annotate | diff | comparison | revisions | |
js/src/jstl.h | file | annotate | diff | comparison | revisions | |
js/src/jstracer.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsutil.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsvalue.h | file | annotate | diff | comparison | revisions | |
js/src/jsxml.cpp | file | annotate | diff | comparison | revisions | |
js/src/xpconnect/src/xpcjsruntime.cpp | file | annotate | diff | comparison | revisions | |
js/src/xpconnect/src/xpcprivate.h | file | annotate | diff | comparison | revisions |
--- a/config/autoconf.mk.in +++ b/config/autoconf.mk.in @@ -105,16 +105,17 @@ MOZ_JSDEBUGGER = @MOZ_JSDEBUGGER@ MOZ_IPC = @MOZ_IPC@ MOZ_IPDL_TESTS = @MOZ_IPDL_TESTS@ MOZ_LEAKY = @MOZ_LEAKY@ MOZ_MEMORY = @MOZ_MEMORY@ MOZ_JPROF = @MOZ_JPROF@ MOZ_SHARK = @MOZ_SHARK@ MOZ_CALLGRIND = @MOZ_CALLGRIND@ MOZ_VTUNE = @MOZ_VTUNE@ +MOZ_TRACE_JSCALLS = @MOZ_TRACE_JSCALLS@ MOZ_TRACEVIS = @MOZ_TRACEVIS@ DEHYDRA_PATH = @DEHYDRA_PATH@ NS_TRACE_MALLOC = @NS_TRACE_MALLOC@ USE_ELF_DYNSTR_GC = @USE_ELF_DYNSTR_GC@ INCREMENTAL_LINKER = @INCREMENTAL_LINKER@ MACOSX_DEPLOYMENT_TARGET = @MACOSX_DEPLOYMENT_TARGET@ MOZ_MAIL_NEWS = @MOZ_MAIL_NEWS@
--- a/configure.in +++ b/configure.in @@ -7331,16 +7331,27 @@ fi dnl ======================================================== dnl = Location of malloc wrapper lib dnl ======================================================== MOZ_ARG_WITH_STRING(wrap-malloc, [ --with-wrap-malloc=DIR Location of malloc wrapper library], WRAP_MALLOC_LIB=$withval) dnl ======================================================== +dnl = Use JS Call tracing +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(trace-jscalls, +[ --enable-trace-jscalls Enable JS call enter/exit callback (default=no)], + MOZ_TRACE_JSCALLS=1, + MOZ_TRACE_JSCALLS= ) +if test -n "$MOZ_TRACE_JSCALLS"; then + AC_DEFINE(MOZ_TRACE_JSCALLS) +fi + +dnl ======================================================== dnl = Use TraceVis dnl ======================================================== MOZ_ARG_ENABLE_BOOL(tracevis, [ --enable-tracevis Enable TraceVis tracing tool (default=no)], MOZ_TRACEVIS=1, MOZ_TRACEVIS= ) if test -n "$MOZ_TRACEVIS"; then AC_DEFINE(MOZ_TRACEVIS)
--- a/js/narcissus/jsdefs.js +++ b/js/narcissus/jsdefs.js @@ -43,17 +43,17 @@ * separately to take advantage of the simple switch-case constant propagation * done by SpiderMonkey. */ Narcissus = { options: { version: 185 } }; -Narcissus.jsdefs = (function() { +Narcissus.definitions = (function() { var tokens = [ // End of source. "END", // Operators and punctuators. Some pair-wise order matters, e.g. (+, -) // and (UNARY_PLUS, UNARY_MINUS). "\n", ";", @@ -185,19 +185,19 @@ Narcissus.jsdefs = (function() { Object.defineProperty(obj, prop, { get: fn, configurable: !dontDelete, enumerable: !dontEnum }); } function defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) { Object.defineProperty(obj, prop, { value: val, writable: !readOnly, configurable: !dontDelete, enumerable: !dontEnum }); } return { - "tokens": tokens, - "opTypeNames": opTypeNames, - "keywords": keywords, - "tokenIds": tokenIds, - "consts": consts, - "assignOps": assignOps, - "defineGetter": defineGetter, - "defineProperty": defineProperty + tokens: tokens, + opTypeNames: opTypeNames, + keywords: keywords, + tokenIds: tokenIds, + consts: consts, + assignOps: assignOps, + defineGetter: defineGetter, + defineProperty: defineProperty }; }());
--- a/js/narcissus/jsexec.js +++ b/js/narcissus/jsexec.js @@ -43,49 +43,49 @@ * Execution of parse trees. * * 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. */ -Narcissus.jsexec = (function() { +Narcissus.interpreter = (function() { - var jsparse = Narcissus.jsparse; - var jsdefs = Narcissus.jsdefs; + var parser = Narcissus.parser; + var definitions = Narcissus.definitions; // Set constants in the local scope. - eval(jsdefs.consts); + eval(definitions.consts); const GLOBAL_CODE = 0, EVAL_CODE = 1, FUNCTION_CODE = 2; function ExecutionContext(type) { this.type = type; } var global = { // Value properties. NaN: NaN, Infinity: Infinity, undefined: undefined, // Function properties. eval: function eval(s) { - if (typeof s != "string") + if (typeof s !== "string") return s; var x = ExecutionContext.current; var x2 = new ExecutionContext(EVAL_CODE); x2.thisObject = x.thisObject; x2.caller = x.caller; x2.callee = x.callee; x2.scope = x.scope; ExecutionContext.current = x2; try { - execute(jsparse.parse(new jsparse.VanillaBuilder, s), x2); - } catch (e if e == THROW) { + execute(parser.parse(new parser.VanillaBuilder, s), x2); + } catch (e if e === THROW) { x.result = x2.result; throw e; } catch (e if e instanceof SyntaxError) { x.result = e; throw THROW; } catch (e if e instanceof InternalError) { /* * If we get too much recursion during parsing we need to re-throw @@ -106,39 +106,39 @@ Narcissus.jsexec = (function() { return x2.result; }, parseInt: parseInt, parseFloat: parseFloat, isNaN: isNaN, isFinite: isFinite, decodeURI: decodeURI, encodeURI: encodeURI, decodeURIComponent: decodeURIComponent, encodeURIComponent: encodeURIComponent, - // Class constructors. Where ECMA-262 requires C.length == 1, we declare + // Class constructors. Where ECMA-262 requires C.length === 1, we declare // a dummy formal parameter. Object: Object, Function: function Function(dummy) { var p = "", b = "", n = arguments.length; if (n) { var m = n - 1; if (m) { p += arguments[0]; for (var k = 1; k < m; k++) p += "," + arguments[k]; } b += arguments[m]; } // XXX We want to pass a good file and line to the tokenizer. // Note the anonymous name to maintain parity with Spidermonkey. - var t = new jsparse.Tokenizer("anonymous(" + p + ") {" + b + "}"); + var t = new parser.Tokenizer("anonymous(" + p + ") {" + b + "}"); // NB: Use the STATEMENT_FORM constant since we don't want to push this // function onto the fake compilation context. - var x = { builder: new jsparse.VanillaBuilder }; - var f = jsparse.FunctionDefinition(t, x, false, jsparse.STATEMENT_FORM); + var x = { builder: new parser.VanillaBuilder }; + var f = parser.FunctionDefinition(t, x, false, parser.STATEMENT_FORM); var s = {object: global, parent: null}; return newFunction(f,{scope:s}); }, Array: function (dummy) { // Array when called as a function acts as a constructor. return Array.apply(this, arguments); }, String: function String(s) { @@ -158,17 +158,17 @@ Narcissus.jsexec = (function() { TypeError: TypeError, URIError: URIError, // Other properties. Math: Math, // Extensions to ECMA. snarf: snarf, evaluate: evaluate, load: function load(s) { - if (typeof s != "string") + if (typeof s !== "string") return s; evaluate(snarf(s), s, 1) }, print: print, version: function() { return Narcissus.options.version; }, quit: function() { throw END; } }; @@ -176,18 +176,18 @@ Narcissus.jsexec = (function() { // Helper to avoid Object.prototype.hasOwnProperty polluting scope objects. function hasDirectProperty(o, p) { return Object.prototype.hasOwnProperty.call(o, p); } // Reflect a host class into the target global environment by delegation. function reflectClass(name, proto) { var gctor = global[name]; - jsdefs.defineProperty(gctor, "prototype", proto, true, true, true); - jsdefs.defineProperty(proto, "constructor", gctor, false, false, true); + definitions.defineProperty(gctor, "prototype", proto, true, true, true); + definitions.defineProperty(proto, "constructor", gctor, false, false, true); return proto; } // Reflect Array -- note that all Array methods are generic. reflectClass('Array', new Array); // Reflect String, overriding non-generic methods. var gSp = reflectClass('String', new String); @@ -208,17 +208,17 @@ Narcissus.jsexec = (function() { ecma3OnlyMode: false, // Run a thunk in this execution context and return its result. run: function(thunk) { var prev = ExecutionContext.current; ExecutionContext.current = this; try { thunk(); return this.result; - } catch (e if e == THROW) { + } catch (e if e === THROW) { if (prev) { prev.result = this.result; throw THROW; } throw this.result; } finally { ExecutionContext.current = prev; } @@ -248,25 +248,25 @@ Narcissus.jsexec = (function() { if (v instanceof Reference) return (v.base || global)[v.propertyName] = w; throw new ReferenceError("Invalid assignment left-hand side", vn.filename, vn.lineno); } function isPrimitive(v) { var t = typeof v; - return (t == "object") ? v === null : t != "function"; + return (t === "object") ? v === null : t !== "function"; } function isObject(v) { var t = typeof v; - return (t == "object") ? v !== null : t == "function"; + return (t === "object") ? v !== null : t === "function"; } - // If r instanceof Reference, v == getValue(r); else v === r. If passed, rn + // 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 "boolean": return new global.Boolean(v); case "number": return new global.Number(v); case "string": @@ -282,52 +282,52 @@ Narcissus.jsexec = (function() { : new TypeError(message); } function execute(n, x) { var a, f, i, j, r, s, t, u, v; switch (n.type) { case FUNCTION: - if (n.functionForm != jsparse.DECLARED_FORM) { - if (!n.name || n.functionForm == jsparse.STATEMENT_FORM) { + if (n.functionForm !== parser.DECLARED_FORM) { + if (!n.name || n.functionForm === parser.STATEMENT_FORM) { v = newFunction(n, x); - if (n.functionForm == jsparse.STATEMENT_FORM) - jsdefs.defineProperty(x.scope.object, n.name, v, true); + if (n.functionForm === parser.STATEMENT_FORM) + definitions.defineProperty(x.scope.object, n.name, v, true); } else { t = new Object; x.scope = {object: t, parent: x.scope}; try { v = newFunction(n, x); - jsdefs.defineProperty(t, n.name, v, true, true); + definitions.defineProperty(t, n.name, v, true, true); } finally { x.scope = x.scope.parent; } } } break; case SCRIPT: t = x.scope.object; a = n.funDecls; for (i = 0, j = a.length; i < j; i++) { s = a[i].name; f = newFunction(a[i], x); - jsdefs.defineProperty(t, s, f, x.type != EVAL_CODE); + definitions.defineProperty(t, s, f, x.type !== EVAL_CODE); } a = n.varDecls; for (i = 0, j = a.length; i < j; i++) { u = a[i]; s = u.name; if (u.readOnly && hasDirectProperty(t, s)) { throw new TypeError("Redeclaration of const " + s, u.filename, u.lineno); } if (u.readOnly || !hasDirectProperty(t, s)) { - jsdefs.defineProperty(t, s, undefined, x.type != EVAL_CODE, u.readOnly); + definitions.defineProperty(t, s, undefined, x.type !== EVAL_CODE, u.readOnly); } } // FALL THROUGH case BLOCK: for (i = 0, j = n.length; i < j; i++) execute(n[i], x); break; @@ -340,122 +340,124 @@ Narcissus.jsexec = (function() { break; case SWITCH: s = getValue(execute(n.discriminant, x)); a = n.cases; var matchDefault = false; switch_loop: for (i = 0, j = a.length; ; i++) { - if (i == j) { + if (i === j) { if (n.defaultIndex >= 0) { i = n.defaultIndex - 1; // no case matched, do default matchDefault = true; continue; } break; // no default, exit switch_loop } t = a[i]; // next case (might be default!) - if (t.type == CASE) { + if (t.type === CASE) { u = getValue(execute(t.caseLabel, x)); } else { if (!matchDefault) // not defaulting, skip for now continue; u = s; // force match to do default } if (u === s) { for (;;) { // this loop exits switch_loop if (t.statements.length) { try { execute(t.statements, x); - } catch (e if e == BREAK && x.target == n) { + } catch (e if e === BREAK && x.target == n) { break switch_loop; } } - if (++i == j) + if (++i === j) break switch_loop; t = a[i]; } // NOT REACHED } } break; case FOR: n.setup && getValue(execute(n.setup, x)); // FALL THROUGH case WHILE: while (!n.condition || getValue(execute(n.condition, x))) { try { execute(n.body, x); - } catch (e if e == BREAK && x.target == n) { + } catch (e if e === BREAK && x.target === n) { break; - } catch (e if e == CONTINUE && x.target == n) { + } catch (e if e === CONTINUE && x.target === n) { // Must run the update expression. } n.update && getValue(execute(n.update, x)); } break; case FOR_IN: u = n.varDecl; if (u) execute(u, x); r = n.iterator; s = execute(n.object, x); v = getValue(s); // ECMA deviation to track extant browser JS implementation behavior. - t = (v == null && !x.ecma3OnlyMode) ? v : toObject(v, s, n.object); + t = ((v === null || v === undefined) && !x.ecma3OnlyMode) + ? v + : toObject(v, 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], r); try { execute(n.body, x); - } catch (e if e == BREAK && x.target == n) { + } catch (e if e === BREAK && x.target === n) { break; - } catch (e if e == CONTINUE && x.target == n) { + } catch (e if e === CONTINUE && x.target === n) { continue; } } break; case DO: do { try { execute(n.body, x); - } catch (e if e == BREAK && x.target == n) { + } catch (e if e === BREAK && x.target === n) { break; - } catch (e if e == CONTINUE && x.target == n) { + } catch (e if e === CONTINUE && x.target === n) { continue; } } while (getValue(execute(n.condition, x))); break; case BREAK: case CONTINUE: x.target = n.target; throw n.type; case TRY: try { execute(n.tryBlock, x); - } catch (e if e == THROW && (j = n.catchClauses.length)) { + } catch (e if e === THROW && (j = n.catchClauses.length)) { e = x.result; x.result = undefined; for (i = 0; ; i++) { - if (i == j) { + if (i === j) { x.result = e; throw THROW; } t = n.catchClauses[i]; x.scope = {object: {}, parent: x.scope}; - jsdefs.defineProperty(x.scope.object, t.varName, e, true); + definitions.defineProperty(x.scope.object, t.varName, e, true); try { if (t.guard && !getValue(execute(t.guard, x))) continue; execute(t.block, x); break; } finally { x.scope = x.scope.parent; } @@ -492,35 +494,35 @@ Narcissus.jsexec = (function() { if (!u) continue; t = n[i].name; for (s = x.scope; s; s = s.parent) { if (hasDirectProperty(s.object, t)) break; } u = getValue(execute(u, x)); - if (n.type == CONST) - jsdefs.defineProperty(s.object, t, u, x.type != EVAL_CODE, true); + if (n.type === CONST) + definitions.defineProperty(s.object, t, u, x.type !== EVAL_CODE, true); else s.object[t] = u; } break; case DEBUGGER: - throw "NYI: " + jsdefs.tokens[n.type]; + throw "NYI: " + definitions.tokens[n.type]; case SEMICOLON: if (n.expression) x.result = getValue(execute(n.expression, x)); break; case LABEL: try { execute(n.statement, x); - } catch (e if e == BREAK && x.target == n) { + } catch (e if e === BREAK && x.target === n) { } break; case COMMA: for (i = 0, j = n.length; i < j; i++) v = getValue(execute(n[i], x)); break; @@ -607,17 +609,17 @@ Narcissus.jsexec = (function() { case IN: v = getValue(execute(n[0], x)) in getValue(execute(n[1], x)); break; case INSTANCEOF: t = getValue(execute(n[0], x)); u = getValue(execute(n[1], x)); - if (isObject(u) && typeof u.__hasInstance__ == "function") + if (isObject(u) && typeof u.__hasInstance__ === "function") v = u.__hasInstance__(t); else v = t instanceof u; break; case LSH: v = getValue(execute(n[0], x)) << getValue(execute(n[1], x)); break; @@ -683,17 +685,17 @@ Narcissus.jsexec = (function() { break; case INCREMENT: case DECREMENT: t = execute(n[0], x); u = Number(getValue(t)); if (n.postfix) v = u; - putValue(t, (n.type == INCREMENT) ? ++u : --u, n[0]); + putValue(t, (n.type === INCREMENT) ? ++u : --u, n[0]); if (!n.postfix) v = u; break; case DOT: r = execute(n[0], x); t = getValue(r); u = n[1].value; @@ -707,46 +709,46 @@ Narcissus.jsexec = (function() { v = new Reference(toObject(t, r, n[0]), String(u), n); break; case LIST: // Curse ECMA for specifying that arguments is not an Array object! v = {}; for (i = 0, j = n.length; i < j; i++) { u = getValue(execute(n[i], x)); - jsdefs.defineProperty(v, i, u, false, false, true); + definitions.defineProperty(v, i, u, false, false, true); } - jsdefs.defineProperty(v, "length", i, false, false, true); + definitions.defineProperty(v, "length", i, false, false, true); break; case CALL: r = execute(n[0], x); a = execute(n[1], x); f = getValue(r); - if (isPrimitive(f) || typeof f.__call__ != "function") { + 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; v = f.__call__(t, a, x); break; case NEW: case NEW_WITH_ARGS: r = execute(n[0], x); f = getValue(r); - if (n.type == NEW) { + if (n.type === NEW) { a = {}; - jsdefs.defineProperty(a, "length", 0, false, false, true); + definitions.defineProperty(a, "length", 0, false, false, true); } else { a = execute(n[1], x); } - if (isPrimitive(f) || typeof f.__construct__ != "function") { + 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; case ARRAY_INIT: v = []; @@ -756,22 +758,22 @@ Narcissus.jsexec = (function() { } v.length = j; break; case OBJECT_INIT: v = {}; for (i = 0, j = n.length; i < j; i++) { t = n[i]; - if (t.type == PROPERTY_INIT) { + if (t.type === PROPERTY_INIT) { v[t[0].value] = getValue(execute(t[1], x)); } else { f = newFunction(t, x); - u = (t.type == GETTER) ? '__defineGetter__' - : '__defineSetter__'; + u = (t.type === GETTER) ? '__defineGetter__' + : '__defineSetter__'; v[u](t.name, thunk(f, x)); } } break; case NULL: v = null; break; @@ -810,34 +812,34 @@ Narcissus.jsexec = (function() { throw "PANIC: unknown operation " + n.type + ": " + uneval(n); } return v; } function Activation(f, a) { for (var i = 0, j = f.params.length; i < j; i++) - jsdefs.defineProperty(this, f.params[i], a[i], true); - jsdefs.defineProperty(this, "arguments", a, true); + definitions.defineProperty(this, f.params[i], a[i], true); + definitions.defineProperty(this, "arguments", a, true); } // Null Activation.prototype's proto slot so that Object.prototype.* does not // pollute the scope of heavyweight functions. Also delete its 'constructor' // property so that it doesn't pollute function scopes. Activation.prototype.__proto__ = null; delete Activation.prototype.constructor; function FunctionObject(node, scope) { this.node = node; this.scope = scope; - jsdefs.defineProperty(this, "length", node.params.length, true, true, true); + definitions.defineProperty(this, "length", node.params.length, true, true, true); var proto = {}; - jsdefs.defineProperty(this, "prototype", proto, true); - jsdefs.defineProperty(proto, "constructor", this, false, false, true); + definitions.defineProperty(this, "prototype", proto, true); + definitions.defineProperty(proto, "constructor", this, false, false, true); } function getPropertyDescriptor(obj, name) { while (obj) { if (({}).hasOwnProperty.call(obj, name)) return Object.getOwnPropertyDescriptor(obj, name); obj = Object.getPrototypeOf(obj); } @@ -909,26 +911,26 @@ Narcissus.jsexec = (function() { var FOp = FunctionObject.prototype = { // Internal methods. __call__: function (t, a, x) { var x2 = new ExecutionContext(FUNCTION_CODE); x2.thisObject = t || global; x2.caller = x; x2.callee = this; - jsdefs.defineProperty(a, "callee", this, false, false, true); + definitions.defineProperty(a, "callee", this, false, false, true); var f = this.node; x2.scope = {object: new Activation(f, a), parent: this.scope}; ExecutionContext.current = x2; try { execute(f.body, x2); - } catch (e if e == RETURN) { + } catch (e if e === RETURN) { return x2.result; - } catch (e if e == THROW) { + } catch (e if e === THROW) { x.result = x2.result; throw THROW; } finally { ExecutionContext.current = x; } return undefined; }, @@ -950,48 +952,48 @@ Narcissus.jsexec = (function() { return false; var p = this.prototype; 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) + if (o === p) return true; v = o; } return false; }, // Standard methods. toString: function () { return this.node.getSource(); }, apply: function (t, a) { // Curse ECMA again! - if (typeof this.__call__ != "function") { + if (typeof this.__call__ !== "function") { throw new TypeError("Function.prototype.apply called on" + " uncallable object"); } if (t === undefined || t === null) t = global; - else if (typeof t != "object") + else if (typeof t !== "object") t = toObject(t, t); if (a === undefined || a === null) { a = {}; - jsdefs.defineProperty(a, "length", 0, false, false, true); + definitions.defineProperty(a, "length", 0, false, false, true); } else if (a instanceof Array) { var v = {}; for (var i = 0, j = a.length; i < j; i++) - jsdefs.defineProperty(v, i, a[i], false, false, true); - jsdefs.defineProperty(v, "length", i, false, false, true); + definitions.defineProperty(v, i, a[i], false, false, true); + definitions.defineProperty(v, "length", i, false, false, true); a = v; } 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", this.node.filename, this.node.lineno); } @@ -1008,28 +1010,28 @@ Narcissus.jsexec = (function() { // 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 REp = RegExp.prototype; if (!('__call__' in Fp)) { - jsdefs.defineProperty(Fp, "__call__", + definitions.defineProperty(Fp, "__call__", function (t, a, x) { // Curse ECMA yet again! a = Array.prototype.splice.call(a, 0, a.length); return this.apply(t, a); }, true, true, true); - jsdefs.defineProperty(REp, "__call__", + definitions.defineProperty(REp, "__call__", function (t, a, x) { a = Array.prototype.splice.call(a, 0, a.length); return this.exec.apply(this, a); }, true, true, true); - jsdefs.defineProperty(Fp, "__construct__", + definitions.defineProperty(Fp, "__construct__", function (a, x) { a = Array.prototype.splice.call(a, 0, a.length); switch (a.length) { case 0: return new this(); case 1: return new this(a[0]); case 2: @@ -1043,103 +1045,108 @@ Narcissus.jsexec = (function() { } return eval('new this(' + argStr.slice(0,-1) + ');'); } }, true, true, true); // Since we use native functions such as Date along with host ones such // as global.eval, we want both to be considered instances of the native // Function constructor. - jsdefs.defineProperty(Fp, "__hasInstance__", + definitions.defineProperty(Fp, "__hasInstance__", function (v) { return v instanceof Function || v instanceof global.Function; }, true, true, true); } function thunk(f, x) { return function () { return f.__call__(this, arguments, x); }; } function evaluate(s, f, l) { - if (typeof s != "string") + if (typeof s !== "string") return s; var x = ExecutionContext.current; var x2 = new ExecutionContext(GLOBAL_CODE); ExecutionContext.current = x2; try { - execute(jsparse.parse(new jsparse.VanillaBuilder, s, f, l), x2); - } catch (e if e == THROW) { + execute(parser.parse(new parser.VanillaBuilder, s, f, l), x2); + } catch (e if e === THROW) { if (x) { x.result = x2.result; throw THROW; } throw x2.result; } finally { ExecutionContext.current = x; } return x2.result; } // A read-eval-print-loop that roughly tracks the behavior of the js shell. function repl() { // Display a value similarly to the js shell. function display(x) { - if (typeof x == "object") { + if (typeof x === "object") { // At the js shell, objects with no |toSource| don't print. - if (x != null && "toSource" in x) { + if (x !== null && "toSource" in x) { try { print(x.toSource()); } catch (e) { } } else { print("null"); } - } else if (typeof x == "string") { + } else if (typeof x === "string") { print(uneval(x)); - } else if (typeof x != "undefined") { + } else if (typeof x !== "undefined") { // Since x must be primitive, String can't throw. print(String(x)); } } // String conversion that never throws. function string(x) { try { return String(x); } catch (e) { return "unknown (can't convert to string)"; } } - var b = new jsparse.VanillaBuilder; + var b = new parser.VanillaBuilder; var x = new ExecutionContext(GLOBAL_CODE); x.run(function() { for (;;) { + x.result = undefined; putstr("njs> "); var line = readline(); - x.result = undefined; + // If readline receives EOF it returns null. + if (line === null) { + print(""); + break; + } try { - execute(jsparse.parse(b, line, "stdin", 1), x); + execute(parser.parse(b, line, "stdin", 1), x); display(x.result); - } catch (e if e == THROW) { + } catch (e if e === THROW) { print("uncaught exception: " + string(x.result)); - } catch (e if e == END) { + } catch (e if e === END) { break; } catch (e if e instanceof SyntaxError) { print(e.toString()); } catch (e) { print("internal Narcissus error"); throw e; } } }); } return { - "evaluate": evaluate, - "repl": repl + evaluate: evaluate, + repl: repl }; }());
--- a/js/narcissus/jslex.js +++ b/js/narcissus/jslex.js @@ -36,26 +36,26 @@ * ***** END LICENSE BLOCK ***** */ /* * Narcissus - JS implemented in JS. * * Lexical scanner. */ -Narcissus.jslex = (function() { +Narcissus.lexer = (function() { - var jsdefs = Narcissus.jsdefs; + var definitions = Narcissus.definitions; // Set constants in the local scope. - eval(jsdefs.consts); + eval(definitions.consts); // Build up a trie of operator tokens. var opTokens = {}; - for (var op in jsdefs.opTypeNames) { + for (var op in definitions.opTypeNames) { if (op === '\n' || op === '.') continue; var node = opTokens; for (var i = 0; i < op.length; i++) { var ch = op[i]; if (!(ch in node)) node[ch] = {}; @@ -77,38 +77,38 @@ Narcissus.jslex = (function() { this.filename = f || ""; this.lineno = l || 1; } Tokenizer.prototype = { get done() { // We need to set scanOperand to true here because the first thing // might be a regexp. - return this.peek(true) == END; + return this.peek(true) === END; }, get token() { return this.tokens[this.tokenIndex]; }, match: function (tt, scanOperand) { - return this.get(scanOperand) == tt || this.unget(); + return this.get(scanOperand) === tt || this.unget(); }, mustMatch: function (tt) { if (!this.match(tt)) throw this.newSyntaxError("Missing " + tokens[tt].toLowerCase()); return this.token; }, peek: function (scanOperand) { var tt, next; if (this.lookahead) { next = this.tokens[(this.tokenIndex + this.lookahead) & 3]; - tt = (this.scanNewlines && next.lineno != this.lineno) + tt = (this.scanNewlines && next.lineno !== this.lineno) ? NEWLINE : next.type; } else { tt = this.get(scanOperand); this.unget(); } return tt; }, @@ -332,23 +332,23 @@ Narcissus.jslex = (function() { if (next in node) { node = node[next]; this.cursor++; next = input[this.cursor]; } } var op = node.op; - if (jsdefs.assignOps[op] && input[this.cursor] === '=') { + if (definitions.assignOps[op] && input[this.cursor] === '=') { this.cursor++; token.type = ASSIGN; - token.assignOp = jsdefs.tokenIds[jsdefs.opTypeNames[op]]; + token.assignOp = definitions.tokenIds[definitions.opTypeNames[op]]; op += '='; } else { - token.type = jsdefs.tokenIds[jsdefs.opTypeNames[op]]; + token.type = definitions.tokenIds[definitions.opTypeNames[op]]; token.assignOp = null; } token.value = op; }, // FIXME: Unicode escape sequences // FIXME: Unicode identifiers @@ -358,33 +358,33 @@ Narcissus.jslex = (function() { do { ch = input[this.cursor++]; } while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch === '$' || ch === '_'); this.cursor--; // Put the non-word character back. var id = input.substring(token.start, this.cursor); - token.type = jsdefs.keywords[id] || IDENTIFIER; + token.type = definitions.keywords[id] || IDENTIFIER; token.value = id; }, /* * Tokenizer.get :: void -> token type * * Consumes input *only* if there is no lookahead. * Dispatch to the appropriate lexing function depending on the input. */ get: function (scanOperand) { var token; while (this.lookahead) { --this.lookahead; this.tokenIndex = (this.tokenIndex + 1) & 3; token = this.tokens[this.tokenIndex]; - if (token.type != NEWLINE || this.scanNewlines) + if (token.type !== NEWLINE || this.scanNewlines) return token.type; } this.skip(); this.tokenIndex = (this.tokenIndex + 1) & 3; token = this.tokens[this.tokenIndex]; if (!token) @@ -426,17 +426,17 @@ Narcissus.jslex = (function() { }, /* * Tokenizer.unget :: void -> undefined * * Match depends on unget returning undefined. */ unget: function () { - if (++this.lookahead == 4) throw "PANIC: too much lookahead!"; + if (++this.lookahead === 4) throw "PANIC: too much lookahead!"; this.tokenIndex = (this.tokenIndex - 1) & 3; }, newSyntaxError: function (m) { var e = new SyntaxError(m, this.filename, this.lineno); e.source = this.source; e.cursor = this.cursor; return e; @@ -458,12 +458,12 @@ Narcissus.jslex = (function() { this.tokenIndex = point.tokenIndex; this.tokens = point.tokens.slice(); this.lookahead = point.lookahead; this.scanNewline = point.scanNewline; this.lineno = point.lineno; } }; - return { "Tokenizer": Tokenizer }; + return { Tokenizer: Tokenizer }; }());
--- a/js/narcissus/jsparse.js +++ b/js/narcissus/jsparse.js @@ -38,23 +38,23 @@ * ***** END LICENSE BLOCK ***** */ /* * Narcissus - JS implemented in JS. * * Parser. */ -Narcissus.jsparse = (function() { +Narcissus.parser = (function() { - var jslex = Narcissus.jslex; - var jsdefs = Narcissus.jsdefs; + var lexer = Narcissus.lexer; + var definitions = Narcissus.definitions; // Set constants in the local scope. - eval(jsdefs.consts); + eval(definitions.consts); /* * The vanilla AST builder. */ VanillaBuilder = function VanillaBuilder() { } @@ -378,18 +378,18 @@ Narcissus.jsparse = (function() { n.statement = s; }, LABEL$finish: function(n) { }, FUNCTION$build: function(t) { var n = new Node(t); - if (n.type != FUNCTION) - n.type = (n.value == "get") ? GETTER : SETTER; + if (n.type !== FUNCTION) + n.type = (n.value === "get") ? GETTER : SETTER; n.params = []; return n; }, FUNCTION$setName: function(n, v) { n.name = v; }, @@ -655,19 +655,19 @@ Narcissus.jsparse = (function() { }, MULTIPLY$finish: function(n) { }, UNARY$build: function(t) { // NB t.token.type must be DELETE, VOID, TYPEOF, NOT, BITWISE_NOT, // UNARY_PLUS, UNARY_MINUS, INCREMENT, or DECREMENT. - if (t.token.type == PLUS) + if (t.token.type === PLUS) t.token.type = UNARY_PLUS; - else if (t.token.type == MINUS) + else if (t.token.type === MINUS) t.token.type = UNARY_MINUS; return new Node(t); }, UNARY$addOperand: function(n, n2) { n.push(n2); }, @@ -822,17 +822,17 @@ Narcissus.jsparse = (function() { var n = Statements(t, x); n.type = SCRIPT; n.funDecls = x.funDecls; n.varDecls = x.varDecls; return n; } // Node extends Array, which we extend slightly with a top-of-stack method. - jsdefs.defineProperty(Array.prototype, "top", + definitions.defineProperty(Array.prototype, "top", function() { return this.length && this[this.length-1]; }, false, false, true); /* * Node :: (tokenizer, optional type) -> node */ function Node(t, type) { @@ -869,24 +869,24 @@ Narcissus.jsparse = (function() { this.end = kid.end; } return Array.prototype.push.call(this, kid); } Node.indentLevel = 0; function tokenstr(tt) { - var t = jsdefs.tokens[tt]; - return /^\W/.test(t) ? jsdefs.opTypeNames[t] : t.toUpperCase(); + var t = definitions.tokens[tt]; + return /^\W/.test(t) ? definitions.opTypeNames[t] : t.toUpperCase(); } Np.toString = function () { var a = []; for (var i in this) { - if (this.hasOwnProperty(i) && i != 'type' && i != 'target') + if (this.hasOwnProperty(i) && i !== 'type' && i !== 'target') a.push({id: i, value: this[i]}); } a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; }); const INDENTATION = " "; var n = ++Node.indentLevel; var s = "{\n" + INDENTATION.repeat(n) + "type: " + tokenstr(this.type); for (i = 0; i < a.length; i++) s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value; @@ -894,22 +894,22 @@ Narcissus.jsparse = (function() { s += "\n" + INDENTATION.repeat(n) + "}"; return s; } Np.getSource = function () { return this.tokenizer.source.slice(this.start, this.end); }; - jsdefs.defineGetter(Np, "filename", + definitions.defineGetter(Np, "filename", function() { return this.tokenizer.filename; }); - jsdefs.defineProperty(String.prototype, "repeat", + definitions.defineProperty(String.prototype, "repeat", function(n) { var s = "", t = this + s; while (--n >= 0) s += t; return s; }, false, false, true); // Statement stack and nested statement handler. @@ -922,27 +922,40 @@ Narcissus.jsparse = (function() { } /* * Statements :: (tokenizer, compiler context) -> node * * Parses a list of Statements. */ function Statements(t, x) { + /* + * Blocks are uniquely numbered by a blockId within a function that is + * at the top level of the program. blockId starts from 0. + * + * This is done to aid hoisting for parse-time analyses done in custom + * builders. + * + * For more details in its interaction with hoisting, see comments in + * FunctionDefinition. + */ var b = x.builder; var n = b.BLOCK$build(t, x.blockId++); b.BLOCK$hoistLets(n); x.stmtStack.push(n); - while (!t.done && t.peek(true) != RIGHT_CURLY) + while (!t.done && t.peek(true) !== RIGHT_CURLY) b.BLOCK$addStatement(n, Statement(t, x)); x.stmtStack.pop(); b.BLOCK$finish(n); if (n.needsHoisting) { b.setHoists(n.id, n.varDecls); - // Propagate up to the function. + /* + * If a block needs hoisting, we need to propagate this flag up to + * the CompilerContext. + */ x.needsHoisting = true; } return n; } function Block(t, x) { t.mustMatch(LEFT_CURLY); var n = Statements(t, x); @@ -988,64 +1001,64 @@ Narcissus.jsparse = (function() { return n; case SWITCH: // This allows CASEs after a DEFAULT, which is in the standard. n = b.SWITCH$build(t); b.SWITCH$setDiscriminant(n, ParenExpression(t, x)); x.stmtStack.push(n); t.mustMatch(LEFT_CURLY); - while ((tt = t.get()) != RIGHT_CURLY) { + while ((tt = t.get()) !== RIGHT_CURLY) { switch (tt) { case DEFAULT: if (n.defaultIndex >= 0) throw t.newSyntaxError("More than one switch default"); n2 = b.DEFAULT$build(t); b.SWITCH$setDefaultIndex(n, n.cases.length); t.mustMatch(COLON); b.DEFAULT$initializeStatements(n2, t); - while ((tt=t.peek(true)) != CASE && tt != DEFAULT && - tt != RIGHT_CURLY) + while ((tt=t.peek(true)) !== CASE && tt !== DEFAULT && + tt !== RIGHT_CURLY) b.DEFAULT$addStatement(n2, Statement(t, x)); b.DEFAULT$finish(n2); break; case CASE: n2 = b.CASE$build(t); b.CASE$setLabel(n2, Expression(t, x, COLON)); t.mustMatch(COLON); b.CASE$initializeStatements(n2, t); - while ((tt=t.peek(true)) != CASE && tt != DEFAULT && - tt != RIGHT_CURLY) + while ((tt=t.peek(true)) !== CASE && tt !== DEFAULT && + tt !== RIGHT_CURLY) b.CASE$addStatement(n2, Statement(t, x)); b.CASE$finish(n2); break; default: throw t.newSyntaxError("Invalid switch case"); } b.SWITCH$addCase(n, n2); } x.stmtStack.pop(); b.SWITCH$finish(n); return n; case FOR: n = b.FOR$build(t); - if (t.match(IDENTIFIER) && t.token.value == "each") + if (t.match(IDENTIFIER) && t.token.value === "each") b.FOR$rebuildForEach(n); t.mustMatch(LEFT_PAREN); - if ((tt = t.peek()) != SEMICOLON) { + if ((tt = t.peek()) !== SEMICOLON) { x.inForLoopInit = true; - if (tt == VAR || tt == CONST) { + if (tt === VAR || tt === CONST) { t.get(); n2 = Variables(t, x); - } else if (tt == LET) { + } else if (tt === LET) { t.get(); - if (t.peek() == LEFT_PAREN) { + if (t.peek() === LEFT_PAREN) { n2 = LetBlock(t, x, false); } else { /* * Let in for head, we need to add an implicit block * around the rest of the for. */ var forBlock = b.BLOCK$build(t, x.blockId++); x.stmtStack.push(forBlock); @@ -1054,35 +1067,35 @@ Narcissus.jsparse = (function() { } else { n2 = Expression(t, x); } x.inForLoopInit = false; } if (n2 && t.match(IN)) { b.FOR$rebuildForIn(n); b.FOR$setObject(n, Expression(t, x), forBlock); - if (n2.type == VAR || n2.type == LET) { - if (n2.length != 1) { + if (n2.type === VAR || n2.type === LET) { + if (n2.length !== 1) { throw new SyntaxError("Invalid for..in left-hand side", t.filename, n2.lineno); } b.FOR$setIterator(n, n2[0], n2, forBlock); } else { b.FOR$setIterator(n, n2, null, forBlock); } } else { b.FOR$setSetup(n, n2); t.mustMatch(SEMICOLON); if (n.isEach) throw t.newSyntaxError("Invalid for each..in loop"); - b.FOR$setCondition(n, (t.peek() == SEMICOLON) + b.FOR$setCondition(n, (t.peek() === SEMICOLON) ? null : Expression(t, x)); t.mustMatch(SEMICOLON); - b.FOR$setUpdate(n, (t.peek() == RIGHT_PAREN) + b.FOR$setUpdate(n, (t.peek() === RIGHT_PAREN) ? null : Expression(t, x)); } t.mustMatch(RIGHT_PAREN); b.FOR$setBody(n, nest(t, x, n, Statement)); if (forBlock) { b.BLOCK$finish(forBlock); x.stmtStack.pop(); @@ -1108,59 +1121,59 @@ Narcissus.jsparse = (function() { // See http://bugzilla.mozilla.org/show_bug.cgi?id=238945. t.match(SEMICOLON); return n; } break; case BREAK: case CONTINUE: - n = tt == BREAK ? b.BREAK$build(t) : b.CONTINUE$build(t); + n = tt === BREAK ? b.BREAK$build(t) : b.CONTINUE$build(t); - if (t.peekOnSameLine() == IDENTIFIER) { + if (t.peekOnSameLine() === IDENTIFIER) { t.get(); - if (tt == BREAK) + if (tt === BREAK) b.BREAK$setLabel(n, t.token.value); else b.CONTINUE$setLabel(n, t.token.value); } ss = x.stmtStack; i = ss.length; label = n.label; if (label) { do { if (--i < 0) throw t.newSyntaxError("Label not found"); - } while (ss[i].label != label); + } while (ss[i].label !== label); /* * Both break and continue to label need to be handled specially * within a labeled loop, so that they target that loop. If not in * a loop, then break targets its labeled statement. Labels can be * nested so we skip all labels immediately enclosing the nearest * non-label statement. */ - while (i < ss.length - 1 && ss[i+1].type == LABEL) + while (i < ss.length - 1 && ss[i+1].type === LABEL) i++; if (i < ss.length - 1 && ss[i+1].isLoop) i++; - else if (tt == CONTINUE) + else if (tt === CONTINUE) throw t.newSyntaxError("Invalid continue"); } else { do { if (--i < 0) { - throw t.newSyntaxError("Invalid " + ((tt == BREAK) + throw t.newSyntaxError("Invalid " + ((tt === BREAK) ? "break" : "continue")); } - } while (!ss[i].isLoop && !(tt == BREAK && ss[i].type == SWITCH)); + } while (!ss[i].isLoop && !(tt === BREAK && ss[i].type === SWITCH)); } - if (tt == BREAK) { + if (tt === BREAK) { b.BREAK$setTarget(n, ss[i]); b.BREAK$finish(n); } else { b.CONTINUE$setTarget(n, ss[i]); b.CONTINUE$finish(n); } break; @@ -1202,17 +1215,17 @@ Narcissus.jsparse = (function() { b.TRY$setFinallyBlock(n, Block(t, x)); if (!n.catchClauses.length && !n.finallyBlock) throw t.newSyntaxError("Invalid try statement"); b.TRY$finish(n); return n; case CATCH: case FINALLY: - throw t.newSyntaxError(jsdefs.tokens[tt] + " without preceding try"); + throw t.newSyntaxError(definitions.tokens[tt] + " without preceding try"); case THROW: n = b.THROW$build(t); b.THROW$setException(n, Expression(t, x)); b.THROW$finish(n); break; case RETURN: @@ -1227,17 +1240,17 @@ Narcissus.jsparse = (function() { return n; case VAR: case CONST: n = Variables(t, x); break; case LET: - if (t.peek() == LEFT_PAREN) + if (t.peek() === LEFT_PAREN) n = LetBlock(t, x, true); else n = Variables(t, x); break; case DEBUGGER: n = b.DEBUGGER$build(t); break; @@ -1245,24 +1258,24 @@ Narcissus.jsparse = (function() { case NEWLINE: case SEMICOLON: n = b.SEMICOLON$build(t); b.SEMICOLON$setExpression(n, null); b.SEMICOLON$finish(t); return n; default: - if (tt == IDENTIFIER) { + if (tt === IDENTIFIER) { tt = t.peek(); // Labeled statement. - if (tt == COLON) { + if (tt === COLON) { label = t.token.value; ss = x.stmtStack; for (i = ss.length-1; i >= 0; --i) { - if (ss[i].label == label) + if (ss[i].label === label) throw t.newSyntaxError("Duplicate label"); } t.get(); n = b.LABEL$build(t); b.LABEL$setLabel(n, label) b.LABEL$setStatement(n, nest(t, x, n, Statement)); b.LABEL$finish(n); return n; @@ -1280,58 +1293,58 @@ Narcissus.jsparse = (function() { } MagicalSemicolon(t); return n; } function MagicalSemicolon(t) { var tt; - if (t.lineno == t.token.lineno) { + if (t.lineno === t.token.lineno) { tt = t.peekOnSameLine(); - if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY) + if (tt !== END && tt !== NEWLINE && tt !== SEMICOLON && tt !== RIGHT_CURLY) throw t.newSyntaxError("missing ; before statement"); } t.match(SEMICOLON); } function returnOrYield(t, x) { var n, b = x.builder, tt = t.token.type, tt2; - if (tt == RETURN) { + if (tt === RETURN) { if (!x.inFunction) throw t.newSyntaxError("Return not in function"); n = b.RETURN$build(t); - } else /* (tt == YIELD) */ { + } else /* (tt === YIELD) */ { if (!x.inFunction) throw t.newSyntaxError("Yield not in function"); x.isGenerator = true; n = b.YIELD$build(t); } tt2 = t.peek(true); - if (tt2 != END && tt2 != NEWLINE && tt2 != SEMICOLON && tt2 != RIGHT_CURLY - && (tt != YIELD || - (tt2 != tt && tt2 != RIGHT_BRACKET && tt2 != RIGHT_PAREN && - tt2 != COLON && tt2 != COMMA))) { - if (tt == RETURN) { + if (tt2 !== END && tt2 !== NEWLINE && tt2 !== SEMICOLON && tt2 !== RIGHT_CURLY + && (tt !== YIELD || + (tt2 !== tt && tt2 !== RIGHT_BRACKET && tt2 !== RIGHT_PAREN && + tt2 !== COLON && tt2 !== COMMA))) { + if (tt === RETURN) { b.RETURN$setValue(n, Expression(t, x)); x.hasReturnWithValue = true; } else { b.YIELD$setValue(n, AssignExpression(t, x)); } - } else if (tt == RETURN) { + } else if (tt === RETURN) { x.hasEmptyReturn = true; } // Disallow return v; in generator. if (x.hasReturnWithValue && x.isGenerator) throw t.newSyntaxError("Generator returns a value"); - if (tt == RETURN) + if (tt === RETURN) b.RETURN$finish(n); else b.YIELD$finish(n); return n; } /* @@ -1365,91 +1378,122 @@ Narcissus.jsparse = (function() { break; } } while (t.match(COMMA)); t.mustMatch(RIGHT_PAREN); } // Do we have an expression closure or a normal body? var tt = t.get(); - if (tt != LEFT_CURLY) + if (tt !== LEFT_CURLY) t.unget(); var x2 = new StaticContext(true, b); var rp = t.save(); if (x.inFunction) { /* - * Inner functions don't reset block numbering. They also need to - * remember which block they were parsed in for hoisting (see comment - * below). + * Inner functions don't reset block numbering, only functions at + * the top level of the program do. */ x2.blockId = x.blockId; } - if (tt != LEFT_CURLY) { + if (tt !== LEFT_CURLY) { b.FUNCTION$setBody(f, AssignExpression(t, x)); if (x.isGenerator) throw t.newSyntaxError("Generator returns a value"); } else { b.FUNCTION$hoistVars(x2.blockId); b.FUNCTION$setBody(f, Script(t, x2)); } /* - * To linearize hoisting with nested blocks needing hoists, if a toplevel - * function has any hoists we reparse the entire thing. Each toplevel - * function is parsed at most twice. + * Hoisting makes parse-time binding analysis tricky. A taxonomy of hoists: + * + * 1. vars hoist to the top of their function: * - * Pass 1: If there needs to be hoisting at any child block or inner - * function, the entire function gets reparsed. + * var x = 'global'; + * function f() { + * x = 'f'; + * if (false) + * var x; + * } + * f(); + * print(x); // "global" * - * Pass 2: It's possible that hoisting has changed the upvars of - * functions. That is, consider: + * 2. lets hoist to the top of their block: * - * function f() { - * x = 0; - * g(); - * x; // x's forward pointer should be invalidated! - * function g() { - * x = 'g'; - * } - * var x; - * } + * function f() { // id: 0 + * var x = 'f'; + * { + * { + * print(x); // "undefined" + * } + * let x; + * } + * } + * f(); + * + * 3. inner functions at function top-level hoist to the beginning + * of the function. * - * So, a function needs to remember in which block it is parsed under - * (since the function body is _not_ hoisted, only the declaration) and - * upon hoisting, needs to recalculate all its upvars up front. + * If the builder used is doing parse-time analyses, hoisting may + * invalidate earlier conclusions it makes about variable scope. + * + * The builder can opt to set the needsHoisting flag in a + * CompilerContext (in the case of var and function hoisting) or in a + * node of type BLOCK (in the case of let hoisting). This signals for + * the parser to reparse sections of code. + * + * To avoid exponential blowup, if a function at the program top-level + * has any hoists in its child blocks or inner functions, we reparse + * the entire toplevel function. Each toplevel function is parsed at + * most twice. + * + * The list of declarations can be tied to block ids to aid talking + * about declarations of blocks that have not yet been fully parsed. + * + * Blocks are already uniquely numbered; see the comment in + * Statements. */ if (x2.needsHoisting) { - // Order is important here! funDecls must come _after_ varDecls! + + /* + * Order is important here! Builders expect funDecls to come after + * varDecls! + */ b.setHoists(f.body.id, x2.varDecls.concat(x2.funDecls)); if (x.inFunction) { - // Propagate up to the parent function if we're an inner function. + /* + * If an inner function needs hoisting, we need to propagate + * this flag up to the parent function. + */ x.needsHoisting = true; } else { - // Only re-parse toplevel functions. - var x3 = x2; + // Only re-parse functions at the top level of the program. x2 = new StaticContext(true, b); t.rewind(rp); - // Set a flag in case the builder wants to have different behavior - // on the second pass. + /* + * Set a flag in case the builder wants to have different behavior + * on the second pass. + */ b.secondPass = true; b.FUNCTION$hoistVars(f.body.id, true); b.FUNCTION$setBody(f, Script(t, x2)); b.secondPass = false; } } - if (tt == LEFT_CURLY) + if (tt === LEFT_CURLY) t.mustMatch(RIGHT_CURLY); f.end = t.token.end; f.functionForm = functionForm; - if (functionForm == DECLARED_FORM) + if (functionForm === DECLARED_FORM) x.funDecls.push(f); b.FUNCTION$finish(f, x); return f; } /* * Variables :: (tokenizer, compiler context) -> node * @@ -1481,17 +1525,17 @@ Narcissus.jsparse = (function() { if (!letBlock) { ss = x.stmtStack; i = ss.length; while (ss[--i].type !== BLOCK) ; // a BLOCK *must* be found. /* * Lets at the function toplevel are just vars, at least in * SpiderMonkey. */ - if (i == 0) { + if (i === 0) { build = b.VAR$build; addDecl = b.VAR$addDecl; finish = b.VAR$finish; s = x; } else { s = ss[i]; } } else { @@ -1504,24 +1548,24 @@ Narcissus.jsparse = (function() { do { var tt = t.get(); /* * FIXME Should have a special DECLARATION node instead of overloading * IDENTIFIER to mean both identifier declarations and destructured * declarations. */ var n2 = b.DECL$build(t); - if (tt == LEFT_BRACKET || tt == LEFT_CURLY) { + if (tt === LEFT_BRACKET || tt === LEFT_CURLY) { // Pass in s if we need to add each pattern matched into // its varDecls, else pass in x. var data = null; // Need to unget to parse the full destructured expression. t.unget(); b.DECL$setName(n2, DestructuringExpression(t, x, true, s)); - if (x.inForLoopInit && t.peek() == IN) { + if (x.inForLoopInit && t.peek() === IN) { addDecl.call(b, n, n2, s); continue; } t.mustMatch(ASSIGN); if (t.token.assignOp) throw t.newSyntaxError("Invalid variable initialization"); @@ -1533,21 +1577,21 @@ Narcissus.jsparse = (function() { // But only add the rhs as the initializer. b.DECL$setInitializer(n2, n3[1]); b.DECL$finish(n2); addDecl.call(b, n, n2, s); continue; } - if (tt != IDENTIFIER) + if (tt !== IDENTIFIER) throw t.newSyntaxError("missing variable name"); b.DECL$setName(n2, t.token.value); - b.DECL$setReadOnly(n2, n.type == CONST); + b.DECL$setReadOnly(n2, n.type === CONST); addDecl.call(b, n, n2, s); if (t.match(ASSIGN)) { if (t.token.assignOp) throw t.newSyntaxError("Invalid variable initialization"); // Parse the init as a normal assignment with a fake lhs. var id = new Node(n2.tokenizer, IDENTIFIER); @@ -1579,17 +1623,17 @@ Narcissus.jsparse = (function() { var b = x.builder; // t.token.type must be LET n = b.LET_BLOCK$build(t); t.mustMatch(LEFT_PAREN); b.LET_BLOCK$setVariables(n, Variables(t, x, n)); t.mustMatch(RIGHT_PAREN); - if (isStatement && t.peek() != LEFT_CURLY) { + if (isStatement && t.peek() !== LEFT_CURLY) { /* * If this is really an expression in let statement guise, then we * need to wrap the LET_BLOCK node in a SEMICOLON node so that we pop * the return value of the expression. */ n2 = b.SEMICOLON$build(t); b.SEMICOLON$setExpression(n2, n); b.SEMICOLON$finish(n2); @@ -1605,36 +1649,36 @@ Narcissus.jsparse = (function() { } b.LET_BLOCK$finish(n); return n; } function checkDestructuring(t, x, n, simpleNamesOnly, data) { - if (n.type == ARRAY_COMP) + if (n.type === ARRAY_COMP) throw t.newSyntaxError("Invalid array comprehension left-hand side"); - if (n.type != ARRAY_INIT && n.type != OBJECT_INIT) + if (n.type !== ARRAY_INIT && n.type !== OBJECT_INIT) return; var b = x.builder; for (var i = 0, j = n.length; i < j; i++) { var nn = n[i], lhs, rhs; if (!nn) continue; - if (nn.type == PROPERTY_INIT) + if (nn.type === PROPERTY_INIT) lhs = nn[0], rhs = nn[1]; else lhs = null, rhs = null; - if (rhs && (rhs.type == ARRAY_INIT || rhs.type == OBJECT_INIT)) + if (rhs && (rhs.type === ARRAY_INIT || rhs.type === OBJECT_INIT)) checkDestructuring(t, x, rhs, simpleNamesOnly, data); if (lhs && simpleNamesOnly) { // In declarations, lhs must be simple names - if (lhs.type != IDENTIFIER) { + if (lhs.type !== IDENTIFIER) { throw t.newSyntaxError("missing name in pattern"); } else if (data) { var n2 = b.DECL$build(t); b.DECL$setName(n2, lhs.value); // Don't need to set initializer because it's just for // hoisting anyways. b.DECL$finish(n2); // Each pattern needs to be added to varDecls. @@ -1668,17 +1712,17 @@ Narcissus.jsparse = (function() { body = b.COMP_TAIL$build(t); do { n = b.FOR$build(t); // Comprehension tails are always for..in loops. b.FOR$rebuildForIn(n); if (t.match(IDENTIFIER)) { // But sometimes they're for each..in. - if (t.token.value == "each") + if (t.token.value === "each") b.FOR$rebuildForEach(n); else t.unget(); } t.mustMatch(LEFT_PAREN); switch(t.get()) { case LEFT_BRACKET: case LEFT_CURLY: @@ -1729,19 +1773,19 @@ Narcissus.jsparse = (function() { */ var oldLoopInit = x.inForLoopInit; x.inForLoopInit = false; var n = Expression(t, x); x.inForLoopInit = oldLoopInit; var err = "expression must be parenthesized"; if (t.match(FOR)) { - if (n.type == YIELD && !n.parenthesized) + if (n.type === YIELD && !n.parenthesized) throw t.newSyntaxError("Yield " + err); - if (n.type == COMMA && !n.parenthesized) + if (n.type === COMMA && !n.parenthesized) throw t.newSyntaxError("Generator " + err); n = GeneratorExpression(t, x, n); } t.mustMatch(RIGHT_PAREN); return n; } @@ -1757,17 +1801,17 @@ Narcissus.jsparse = (function() { n = AssignExpression(t, x); if (t.match(COMMA)) { n2 = b.COMMA$build(t); b.COMMA$addOperand(n2, n); n = n2; do { n2 = n[n.length-1]; - if (n2.type == YIELD && !n2.parenthesized) + if (n2.type === YIELD && !n2.parenthesized) throw t.newSyntaxError("Yield expression must be parenthesized"); b.COMMA$addOperand(n, AssignExpression(t, x)); } while (t.match(COMMA)); b.COMMA$finish(n); } return n; } @@ -1940,17 +1984,17 @@ Narcissus.jsparse = (function() { /* * Uses of the in operator in shiftExprs are always unambiguous, * so unset the flag that prohibits recognizing it. */ x.inForLoopInit = false; n = ShiftExpression(t, x); while ((t.match(LT) || t.match(LE) || t.match(GE) || t.match(GT) || - (oldLoopInit == false && t.match(IN)) || + (oldLoopInit === false && t.match(IN)) || t.match(INSTANCEOF))) { n2 = b.RELATIONAL$build(t); b.RELATIONAL$addOperand(n2, n); b.RELATIONAL$addOperand(n2, ShiftExpression(t, x)); b.RELATIONAL$finish(n2); n = n2; } x.inForLoopInit = oldLoopInit; @@ -2024,17 +2068,17 @@ Narcissus.jsparse = (function() { b.UNARY$addOperand(n, MemberExpression(t, x, true)); break; default: t.unget(); n = MemberExpression(t, x, true); // Don't look across a newline boundary for a postfix {in,de}crement. - if (t.tokens[(t.tokenIndex + t.lookahead - 1) & 3].lineno == + if (t.tokens[(t.tokenIndex + t.lookahead - 1) & 3].lineno === t.lineno) { if (t.match(INCREMENT) || t.match(DECREMENT)) { n2 = b.UNARY$build(t); b.UNARY$setPostfix(n2); b.UNARY$finish(n); b.UNARY$addOperand(n2, n); n = n2; } @@ -2057,17 +2101,17 @@ Narcissus.jsparse = (function() { b.MEMBER$rebuildNewWithArgs(n); b.MEMBER$addOperand(n, ArgumentList(t, x)); } b.MEMBER$finish(n); } else { n = PrimaryExpression(t, x); } - while ((tt = t.get()) != END) { + while ((tt = t.get()) !== END) { switch (tt) { case DOT: n2 = b.MEMBER$build(t); b.MEMBER$addOperand(n2, n); t.mustMatch(IDENTIFIER); b.MEMBER$addOperand(n2, b.MEMBER$build(t)); break; @@ -2104,21 +2148,21 @@ Narcissus.jsparse = (function() { var b = x.builder; var err = "expression must be parenthesized"; n = b.LIST$build(t); if (t.match(RIGHT_PAREN, true)) return n; do { n2 = AssignExpression(t, x); - if (n2.type == YIELD && !n2.parenthesized && t.peek() == COMMA) + if (n2.type === YIELD && !n2.parenthesized && t.peek() === COMMA) throw t.newSyntaxError("Yield " + err); if (t.match(FOR)) { n2 = GeneratorExpression(t, x, n2); - if (n.length > 1 || t.peek(true) == COMMA) + if (n.length > 1 || t.peek(true) === COMMA) throw t.newSyntaxError("Generator " + err); } b.LIST$addOperand(n, n2); } while (t.match(COMMA)); t.mustMatch(RIGHT_PAREN); b.LIST$finish(n); return n; @@ -2130,30 +2174,30 @@ Narcissus.jsparse = (function() { switch (tt) { case FUNCTION: n = FunctionDefinition(t, x, false, EXPRESSED_FORM); break; case LEFT_BRACKET: n = b.ARRAY_INIT$build(t); - while ((tt = t.peek()) != RIGHT_BRACKET) { - if (tt == COMMA) { + while ((tt = t.peek()) !== RIGHT_BRACKET) { + if (tt === COMMA) { t.get(); b.ARRAY_INIT$addElement(n, null); continue; } b.ARRAY_INIT$addElement(n, AssignExpression(t, x)); - if (tt != COMMA && !t.match(COMMA)) + if (tt !== COMMA && !t.match(COMMA)) break; } // If we matched exactly one element and got a FOR, we have an // array comprehension. - if (n.length == 1 && t.match(FOR)) { + if (n.length === 1 && t.match(FOR)) { n2 = b.ARRAY_COMP$build(t); b.ARRAY_COMP$setExpression(n2, n[0]); b.ARRAY_COMP$setTail(n2, comprehensionTail(t, x)); n = n2; } t.mustMatch(RIGHT_BRACKET); b.PRIMARY$finish(n); break; @@ -2161,50 +2205,50 @@ Narcissus.jsparse = (function() { case LEFT_CURLY: var id; n = b.OBJECT_INIT$build(t); object_init: if (!t.match(RIGHT_CURLY)) { do { tt = t.get(); - if ((t.token.value == "get" || t.token.value == "set") && - t.peek() == IDENTIFIER) { + if ((t.token.value === "get" || t.token.value === "set") && + t.peek() === IDENTIFIER) { if (x.ecma3OnlyMode) throw t.newSyntaxError("Illegal property accessor"); var fd = FunctionDefinition(t, x, true, EXPRESSED_FORM); b.OBJECT_INIT$addProperty(n, fd); } else { switch (tt) { case IDENTIFIER: case NUMBER: case STRING: id = b.PRIMARY$build(t, IDENTIFIER); b.PRIMARY$finish(id); break; case RIGHT_CURLY: if (x.ecma3OnlyMode) throw t.newSyntaxError("Illegal trailing ,"); break object_init; default: - if (t.token.value in jsdefs.keywords) { + if (t.token.value in definitions.keywords) { id = b.PRIMARY$build(t, IDENTIFIER); b.PRIMARY$finish(id); break; } throw t.newSyntaxError("Invalid property name"); } if (t.match(COLON)) { n2 = b.PROPERTY_INIT$build(t); b.PROPERTY_INIT$addOperand(n2, id); b.PROPERTY_INIT$addOperand(n2, AssignExpression(t, x)); b.PROPERTY_INIT$finish(n2); b.OBJECT_INIT$addProperty(n, n2); } else { // Support, e.g., |var {x, y} = o| as destructuring shorthand // for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8. - if (t.peek() != COMMA && t.peek() != RIGHT_CURLY) + if (t.peek() !== COMMA && t.peek() !== RIGHT_CURLY) throw t.newSyntaxError("missing : after property"); b.OBJECT_INIT$addProperty(n, id); } } } while (t.match(COMMA)); t.mustMatch(RIGHT_CURLY); } b.OBJECT_INIT$finish(n); @@ -2235,28 +2279,28 @@ Narcissus.jsparse = (function() { return n; } /* * parse :: (builder, file ptr, path, line number) -> node */ function parse(b, s, f, l) { - var t = new jslex.Tokenizer(s, f, l); + var t = new lexer.Tokenizer(s, f, l); var x = new StaticContext(false, b); var n = Script(t, x); if (!t.done) throw t.newSyntaxError("Syntax error"); return n; } return { - "parse": parse, - "VanillaBuilder": VanillaBuilder, - "DECLARED_FORM": DECLARED_FORM, - "EXPRESSED_FORM": EXPRESSED_FORM, - "STATEMENT_FORM": STATEMENT_FORM, - "Tokenizer": jslex.Tokenizer, - "FunctionDefinition": FunctionDefinition + parse: parse, + VanillaBuilder: VanillaBuilder, + DECLARED_FORM: DECLARED_FORM, + EXPRESSED_FORM: EXPRESSED_FORM, + STATEMENT_FORM: STATEMENT_FORM, + Tokenizer: lexer.Tokenizer, + FunctionDefinition: FunctionDefinition }; }());
--- a/js/src/configure.in +++ b/js/src/configure.in @@ -4398,16 +4398,27 @@ if test "$JS_HAS_CTYPES"; then if test "$_MSC_VER" -a -z $AS; then # Error out if we're on MSVC and MASM is unavailable. AC_MSG_ERROR([No suitable assembler found. An assembler is required to build js-ctypes. If you are building with MS Visual Studio 8 Express, you may download the MASM 8.0 package, upgrade to Visual Studio 9 Express, or install the Vista SDK.]) fi AC_DEFINE(JS_HAS_CTYPES) fi dnl ======================================================== +dnl = Use JS Call tracing +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(trace-jscalls, +[ --enable-trace-jscalls Enable JS call enter/exit callback (default=no)], + MOZ_TRACE_JSCALLS=1, + MOZ_TRACE_JSCALLS= ) +if test -n "$MOZ_TRACE_JSCALLS"; then + AC_DEFINE(MOZ_TRACE_JSCALLS) +fi + +dnl ======================================================== dnl = Use TraceVis dnl ======================================================== MOZ_ARG_ENABLE_BOOL(tracevis, [ --enable-tracevis Enable TraceVis tracing tool (default=no)], MOZ_TRACEVIS=1, MOZ_TRACEVIS= ) if test -n "$MOZ_TRACEVIS"; then AC_DEFINE(MOZ_TRACEVIS)
--- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -52,16 +52,17 @@ CPPSRCS = \ testClassGetter.cpp \ testConservativeGC.cpp \ testContexts.cpp \ testDebugger.cpp \ testDefineGetterSetterNonEnumerable.cpp \ testDefineProperty.cpp \ testExtendedEq.cpp \ testGCChunkAlloc.cpp \ + testFuncCallback.cpp \ testIntString.cpp \ testIsAboutToBeFinalized.cpp \ testLookup.cpp \ testNewObject.cpp \ testOps.cpp \ testPropCache.cpp \ testTrap.cpp \ testSameValue.cpp \
new file mode 100644 --- /dev/null +++ b/js/src/jsapi-tests/testFuncCallback.cpp @@ -0,0 +1,138 @@ +#include "tests.h" +#include "jsfun.h" +#include "jscntxt.h" + +// For TRACING_ENABLED +#include "jstracer.h" + +#ifdef MOZ_TRACE_JSCALLS + +static int depth = 0; +static int enters = 0; +static int leaves = 0; +static int interpreted = 0; + +static void +funcTransition(const JSFunction *, + const JSScript *, + const JSContext *cx, + JSBool entering) +{ + if (entering) { + ++depth; + ++enters; + if (! JS_ON_TRACE(cx)) + ++interpreted; + } else { + --depth; + ++leaves; + } +} + +static JSBool called2 = false; + +static void +funcTransition2(const JSFunction *, const JSScript*, const JSContext*, JSBool) +{ + called2 = true; +} + +static int overlays = 0; +static JSFunctionCallback innerCallback = NULL; +static void +funcTransitionOverlay(const JSFunction *fun, + const JSScript *script, + const JSContext *cx, + JSBool entering) +{ + (*innerCallback)(fun, script, cx, entering); + overlays++; +} +#endif + +BEGIN_TEST(testFuncCallback_bug507012) +{ +#ifdef MOZ_TRACE_JSCALLS + // Call funcTransition() whenever a Javascript method is invoked + JS_SetFunctionCallback(cx, funcTransition); + + EXEC("x = 0; function f (n) { if (n > 1) { f(n - 1); } }"); + interpreted = enters = leaves = depth = 0; + + // Check whether JS_Execute() tracking works + EXEC("42"); + CHECK(enters == 1 && leaves == 1 && depth == 0); + interpreted = enters = leaves = depth = 0; + + // Check whether the basic function tracking works + EXEC("f(1)"); + CHECK(enters == 2 && leaves == 2 && depth == 0); + + // Can we switch to a different callback? + enters = 777; + JS_SetFunctionCallback(cx, funcTransition2); + EXEC("f(1)"); + CHECK(called2 && enters == 777); + + // Check whether we can turn off function tracing + JS_SetFunctionCallback(cx, NULL); + EXEC("f(1)"); + CHECK(enters == 777); + interpreted = enters = leaves = depth = 0; + + // Check nested invocations + JS_SetFunctionCallback(cx, funcTransition); + enters = leaves = depth = 0; + EXEC("f(3)"); + CHECK(enters == 1+3 && leaves == 1+3 && depth == 0); + interpreted = enters = leaves = depth = 0; + + // Check calls invoked while running on trace + EXEC("function g () { ++x; }"); + interpreted = enters = leaves = depth = 0; + EXEC("for (i = 0; i < 50; ++i) { g(); }"); + CHECK(enters == 50+1 && leaves == 50+1 && depth == 0); + + // If this fails, it means that the code was interpreted rather + // than trace-JITted, and so is not testing what it's supposed to + // be testing. Which doesn't necessarily imply that the + // functionality is broken. +#ifdef JS_TRACER + if (TRACING_ENABLED(cx)) + CHECK(interpreted < enters); +#endif + + // Test nesting callbacks via JS_GetFunctionCallback() + JS_SetFunctionCallback(cx, funcTransition); + innerCallback = JS_GetFunctionCallback(cx); + JS_SetFunctionCallback(cx, funcTransitionOverlay); + + EXEC("x = 0; function f (n) { if (n > 1) { f(n - 1); } }"); + interpreted = enters = leaves = depth = overlays = 0; + + EXEC("42.5"); + CHECK(enters == 1); + CHECK(leaves == 1); + CHECK(depth == 0); + CHECK(overlays == 2); // 1 each for enter and exit + interpreted = enters = leaves = depth = overlays = 0; +#endif + + return true; +} + +// Not strictly necessary, but part of the test attempts to check +// whether these callbacks still trigger when traced, so force +// JSOPTION_JIT just to be sure. Once the method jit and tracing jit +// are integrated, this'll probably have to change (and we'll probably +// want to test in all modes.) +virtual +JSContext *createContext() +{ + JSContext *cx = JSAPITest::createContext(); + if (cx) + JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_JIT); + return cx; +} + +END_TEST(testFuncCallback_bug507012)
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -800,16 +800,19 @@ JS_BeginRequest(JSContext *cx) JS_AWAIT_GC_DONE(rt); } /* Indicate that a request is running. */ cx->requestDepth = 1; cx->outstandingRequests++; cx->thread->requestContext = cx; rt->requestCount++; + + if (rt->requestCount == 1 && rt->activityCallback) + rt->activityCallback(rt->activityCallbackArg, true); } #endif } #ifdef JS_THREADSAFE static void StopRequest(JSContext *cx) { @@ -848,18 +851,21 @@ StopRequest(JSContext *cx) cx->outstandingRequests--; cx->thread->requestContext = NULL; js_ShareWaitingTitles(cx); /* Give the GC a chance to run if this was the last request running. */ JS_ASSERT(rt->requestCount > 0); rt->requestCount--; - if (rt->requestCount == 0) + if (rt->requestCount == 0) { JS_NOTIFY_REQUEST_DONE(rt); + if (rt->activityCallback) + rt->activityCallback(rt->activityCallbackArg, false); + } } } #endif JS_PUBLIC_API(void) JS_EndRequest(JSContext *cx) { #ifdef JS_THREADSAFE @@ -5655,16 +5661,30 @@ JS_ClearContextThread(JSContext *cx) js_WaitForGC(rt); js_ClearContextThread(cx); return reinterpret_cast<jsword>(old); #else return 0; #endif } +#ifdef MOZ_TRACE_JSCALLS +JS_PUBLIC_API(void) +JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb) +{ + cx->functionCallback = fcb; +} + +JS_PUBLIC_API(JSFunctionCallback) +JS_GetFunctionCallback(JSContext *cx) +{ + return cx->functionCallback; +} +#endif + #ifdef JS_GC_ZEAL JS_PUBLIC_API(void) JS_SetGCZeal(JSContext *cx, uint8 zeal) { cx->runtime->gcZeal = zeal; } #endif
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3024,16 +3024,29 @@ extern JS_PUBLIC_API(jsword) JS_GetContextThread(JSContext *cx); extern JS_PUBLIC_API(jsword) JS_SetContextThread(JSContext *cx); extern JS_PUBLIC_API(jsword) JS_ClearContextThread(JSContext *cx); +#ifdef MOZ_TRACE_JSCALLS +typedef void (*JSFunctionCallback)(const JSFunction *fun, + const JSScript *scr, + const JSContext *cx, + JSBool entering); + +extern JS_PUBLIC_API(void) +JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb); + +extern JS_PUBLIC_API(JSFunctionCallback) +JS_GetFunctionCallback(JSContext *cx); +#endif + /************************************************************************/ #ifdef DEBUG #define JS_GC_ZEAL 1 #endif #ifdef JS_GC_ZEAL extern JS_PUBLIC_API(void)
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1256,16 +1256,19 @@ struct JSCompartment { bool wrap(JSContext *cx, js::PropertyOp *op); bool wrap(JSContext *cx, js::PropertyDescriptor *desc); bool wrap(JSContext *cx, js::AutoIdVector &props); bool wrapException(JSContext *cx); void sweep(JSContext *cx); }; +typedef void +(* JSActivityCallback)(void *arg, JSBool active); + struct JSRuntime { /* Default compartment. */ JSCompartment *defaultCompartment; /* List of compartments (protected by the GC lock). */ js::Vector<JSCompartment *, 0, js::SystemAllocPolicy> compartments; /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ @@ -1273,16 +1276,30 @@ struct JSRuntime { /* Context create/destroy callback. */ JSContextCallback cxCallback; /* Compartment create/destroy callback. */ JSCompartmentCallback compartmentCallback; /* + * Sets a callback that is run whenever the runtime goes idle - the + * last active request ceases - and begins activity - when it was + * idle and a request begins. Note: The callback is called under the + * GC lock. + */ + void setActivityCallback(JSActivityCallback cb, void *arg) { + activityCallback = cb; + activityCallbackArg = arg; + } + + JSActivityCallback activityCallback; + void *activityCallbackArg; + + /* * Shape regenerated whenever a prototype implicated by an "add property" * property cache fill and induced trace guard has a readonly property or a * setter defined on it. This number proxies for the shapes of all objects * along the prototype chain of all objects in the runtime on which such an * add-property result has been cached/traced. * * See bug 492355 for more details. * @@ -1982,16 +1999,29 @@ struct JSContext #ifdef JS_TRACER jitEnabled = ((options & JSOPTION_JIT) && (debugHooks == &js_NullDebugHooks || (debugHooks == &runtime->globalDebugHooks && !runtime->debuggerInhibitsJIT()))); #endif } +#ifdef MOZ_TRACE_JSCALLS + /* Function entry/exit debugging callback. */ + JSFunctionCallback functionCallback; + + void doFunctionCallback(const JSFunction *fun, + const JSScript *scr, + JSBool entering) const + { + if (functionCallback) + functionCallback(fun, scr, this, entering); + } +#endif + DSTOffsetCache dstOffsetCache; /* List of currently active non-escaping enumerators (for-in). */ JSObject *enumerators; private: /* * To go from a live generator frame (on the stack) to its generator object @@ -3192,21 +3222,35 @@ class AutoValueVector : private AutoGCRo size_t length() const { return vector.length(); } bool append(const Value &v) { return vector.append(v); } void popBack() { vector.popBack(); } bool growBy(size_t inc) { - return vector.growBy(inc); + /* N.B. Value's default ctor leaves the Value undefined */ + size_t oldLength = vector.length(); + if (!vector.growByUninitialized(inc)) + return false; + MakeValueRangeGCSafe(vector.begin() + oldLength, vector.end()); + return true; } bool resize(size_t newLength) { - return vector.resize(newLength); + size_t oldLength = vector.length(); + if (newLength <= oldLength) { + vector.shrinkBy(oldLength - newLength); + return true; + } + /* N.B. Value's default ctor leaves the Value undefined */ + if (!vector.growByUninitialized(newLength - oldLength)) + return false; + MakeValueRangeGCSafe(vector.begin() + oldLength, vector.end()); + return true; } bool reserve(size_t newLength) { return vector.reserve(newLength); } Value &operator[](size_t i) { return vector[i]; } const Value &operator[](size_t i) const { return vector[i]; } @@ -3238,21 +3282,35 @@ class AutoIdVector : private AutoGCRoote size_t length() const { return vector.length(); } bool append(jsid id) { return vector.append(id); } void popBack() { vector.popBack(); } bool growBy(size_t inc) { - return vector.growBy(inc); + /* N.B. jsid's default ctor leaves the jsid undefined */ + size_t oldLength = vector.length(); + if (!vector.growByUninitialized(inc)) + return false; + MakeIdRangeGCSafe(vector.begin() + oldLength, vector.end()); + return true; } bool resize(size_t newLength) { - return vector.resize(newLength); + size_t oldLength = vector.length(); + if (newLength <= oldLength) { + vector.shrinkBy(oldLength - newLength); + return true; + } + /* N.B. jsid's default ctor leaves the jsid undefined */ + if (!vector.growByUninitialized(newLength - oldLength)) + return false; + MakeIdRangeGCSafe(vector.begin() + oldLength, vector.end()); + return true; } bool reserve(size_t newLength) { return vector.reserve(newLength); } jsid &operator[](size_t i) { return vector[i]; } const jsid &operator[](size_t i) const { return vector[i]; } @@ -3270,54 +3328,16 @@ class AutoIdVector : private AutoGCRoote private: Vector<jsid, 8> vector; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; JSIdArray * NewIdArray(JSContext *cx, jsint length); -static JS_ALWAYS_INLINE void -MakeValueRangeGCSafe(Value *vec, uintN len) -{ - PodZero(vec, len); -} - -static JS_ALWAYS_INLINE void -MakeValueRangeGCSafe(Value *beg, Value *end) -{ - PodZero(beg, end - beg); -} - -static JS_ALWAYS_INLINE void -SetValueRangeToUndefined(Value *beg, Value *end) -{ - for (Value *v = beg; v != end; ++v) - v->setUndefined(); -} - -static JS_ALWAYS_INLINE void -SetValueRangeToUndefined(Value *vec, uintN len) -{ - return SetValueRangeToUndefined(vec, vec + len); -} - -static JS_ALWAYS_INLINE void -SetValueRangeToNull(Value *beg, Value *end) -{ - for (Value *v = beg; v != end; ++v) - v->setNull(); -} - -static JS_ALWAYS_INLINE void -SetValueRangeToNull(Value *vec, uintN len) -{ - return SetValueRangeToNull(vec, vec + len); -} - } /* namespace js */ #ifdef _MSC_VER #pragma warning(pop) #pragma warning(pop) #endif #ifdef JS_UNDEFD_MOZALLOC_WRAPPERS
--- a/js/src/jsdtracef.h +++ b/js/src/jsdtracef.h @@ -64,21 +64,22 @@ class DTrace { js::Value *lval = NULL); static void exitJSFun(JSContext *cx, JSStackFrame *fp, JSFunction *fun, const js::Value &rval, js::Value *lval = NULL); static void finalizeObject(JSObject *obj); class ExecutionScope { + const JSContext *cx; const JSScript *script; void startExecution(); void endExecution(); public: - explicit ExecutionScope(JSScript *script); + explicit ExecutionScope(JSContext *cx, JSScript *script); ~ExecutionScope(); }; class ObjectCreationScope { JSContext * const cx; JSStackFrame * const fp; js::Class * const clasp; void handleCreationStart(); @@ -101,60 +102,72 @@ DTrace::enterJSFun(JSContext *cx, JSStac if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED()) enterJSFunImpl(cx, fp, fun); if (JAVASCRIPT_FUNCTION_INFO_ENABLED()) handleFunctionInfo(cx, fp, dfp, fun); if (JAVASCRIPT_FUNCTION_ARGS_ENABLED()) handleFunctionArgs(cx, fp, fun, argc, argv); } #endif +#ifdef MOZ_TRACE_JSCALLS + cx->doFunctionCallback(fun, fun ? FUN_SCRIPT(fun) : NULL, true); +#endif } inline void DTrace::exitJSFun(JSContext *cx, JSStackFrame *fp, JSFunction *fun, const js::Value &rval, js::Value *lval) { #ifdef INCLUDE_MOZILLA_DTRACE if (!lval || IsFunctionObject(*lval)) { if (JAVASCRIPT_FUNCTION_RVAL_ENABLED()) handleFunctionRval(cx, fp, fun, rval); if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) handleFunctionReturn(cx, fp, fun); } #endif +#ifdef MOZ_TRACE_JSCALLS + cx->doFunctionCallback(fun, fun ? FUN_SCRIPT(fun) : NULL, false); +#endif } inline void DTrace::finalizeObject(JSObject *obj) { #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_OBJECT_FINALIZE_ENABLED()) finalizeObjectImpl(obj); #endif } /* Execution scope. */ inline -DTrace::ExecutionScope::ExecutionScope(JSScript *script) - : script(script) +DTrace::ExecutionScope::ExecutionScope(JSContext *cx, JSScript *script) + : cx(cx), script(script) { #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_EXECUTE_START_ENABLED()) startExecution(); #endif +#ifdef MOZ_TRACE_JSCALLS + cx->doFunctionCallback(NULL, script, true); +#endif } inline DTrace::ExecutionScope::~ExecutionScope() { #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_EXECUTE_DONE_ENABLED()) endExecution(); #endif +#ifdef MOZ_TRACE_JSCALLS + cx->doFunctionCallback(NULL, script, false); +#endif } /* Object creation scope. */ inline DTrace::ObjectCreationScope::ObjectCreationScope(JSContext *cx, JSStackFrame *fp, js::Class *clasp) : cx(cx), fp(fp), clasp(clasp) {
--- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -836,17 +836,17 @@ Execute(JSContext *cx, JSObject *chain, if (script->isEmpty()) { if (result) result->setUndefined(); return JS_TRUE; } LeaveTrace(cx); - DTrace::ExecutionScope executionScope(script); + DTrace::ExecutionScope executionScope(cx, script); /* * Get a pointer to new frame/slots. This memory is not "claimed", so the * code before pushExecuteFrame must not reenter the interpreter. * * N.B. when fp->argv is removed (bug 539144), argv will have to be copied * in before execution and copied out after. */ JSFrameRegs regs;
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1638,17 +1638,17 @@ JSBool obj_getPrototypeOf(JSContext *cx, uintN argc, Value *vp) { if (argc == 0) { js_ReportMissingArg(cx, *vp, 0); return JS_FALSE; } if (vp[2].isPrimitive()) { - char *bytes = DecompileValueGenerator(cx, 0 - argc, vp[2], NULL); + char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, vp[2], NULL); if (!bytes) return JS_FALSE; JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE, bytes, "not an object"); JS_free(cx, bytes); return JS_FALSE; }
--- a/js/src/jstl.h +++ b/js/src/jstl.h @@ -370,47 +370,16 @@ class Conditionally { public: Conditionally(bool b) { if (b) t.construct(); } template <class T1> Conditionally(bool b, const T1 &t1) { if (b) t.construct(t1); } }; template <class T> -JS_ALWAYS_INLINE static void -PodZero(T *t) -{ - memset(t, 0, sizeof(T)); -} - -template <class T> -JS_ALWAYS_INLINE static void -PodZero(T *t, size_t nelem) -{ - memset(t, 0, nelem * sizeof(T)); -} - -/* - * Arrays implicitly convert to pointers to their first element, which is - * dangerous when combined with the above PodZero definitions. Adding an - * overload for arrays is ambiguous, so we need another identifier. The - * ambiguous overload is left to catch mistaken uses of PodZero; if you get a - * compile error involving PodZero and array types, use PodArrayZero instead. - */ -template <class T, size_t N> static void PodZero(T (&)[N]); /* undefined */ -template <class T, size_t N> static void PodZero(T (&)[N], size_t); /* undefined */ - -template <class T, size_t N> -JS_ALWAYS_INLINE static void -PodArrayZero(T (&t)[N]) -{ - memset(t, 0, N * sizeof(T)); -} - -template <class T> class AlignedPtrAndFlag { uintptr_t bits; public: AlignedPtrAndFlag(T *t, bool flag) { JS_ASSERT((uintptr_t(t) & 1) == 0); bits = uintptr_t(t) | uintptr_t(flag);
--- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -10561,16 +10561,32 @@ TraceRecorder::record_JSOP_ENTERWITH() } JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_LEAVEWITH() { return ARECORD_STOP; } +#ifdef MOZ_TRACE_JSCALLS +// Usually, cx->doFunctionCallback() is invoked via DTrace::enterJSFun +// and friends, but the DTrace:: probes use fp and therefore would +// need to break out of tracing. So we define a functionProbe() +// callback to be called by generated code when a Javascript function +// is entered or exited. +static JSBool JS_FASTCALL +functionProbe(JSContext *cx, JSFunction *fun, JSBool enter) +{ + cx->doFunctionCallback(fun, FUN_SCRIPT(fun), enter); + return true; +} + +JS_DEFINE_CALLINFO_3(static, BOOL, functionProbe, CONTEXT, FUNCTION, BOOL, 0, 0) +#endif + JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_RETURN() { /* A return from callDepth 0 terminates the current loop, except for recursion. */ if (callDepth == 0) { #if 0 if (IsTraceableRecursion(cx) && tree->recursion != Recursion_Disallowed && tree->script == cx->fp->script) { @@ -10580,16 +10596,24 @@ TraceRecorder::record_JSOP_RETURN() { AUDIT(returnLoopExits); return endLoop(); } } putActivationObjects(); +#ifdef MOZ_TRACE_JSCALLS + if (cx->functionCallback) { + LIns* args[] = { INS_CONST(0), INS_CONSTPTR(cx->fp->fun), cx_ins }; + LIns* call_ins = lir->insCall(&functionProbe_ci, args); + guard(false, lir->insEqI_0(call_ins), MISMATCH_EXIT); + } +#endif + /* If we inlined this function call, make the return value available to the caller code. */ Value& rval = stackval(-1); JSStackFrame *fp = cx->fp; if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && rval.isPrimitive()) { JS_ASSERT(fp->thisv == fp->argv[-1]); rval_ins = get(&fp->argv[-1]); } else { rval_ins = get(&rval); @@ -11602,16 +11626,27 @@ TraceRecorder::functionCall(uintN argc, * was found. So it's sufficient to test here that the particular function * is interpreted, not guard on that condition. * * Bytecode sequences that push shapeless callees must guard on the callee * class being Function and the function being interpreted. */ JSFunction* fun = GET_FUNCTION_PRIVATE(cx, &fval.toObject()); +#ifdef MOZ_TRACE_JSCALLS + if (cx->functionCallback) { + JSScript *script = FUN_SCRIPT(fun); + if (! script || ! script->isEmpty()) { + LIns* args[] = { INS_CONST(1), INS_CONSTPTR(fun), cx_ins }; + LIns* call_ins = lir->insCall(&functionProbe_ci, args); + guard(false, lir->insEqI_0(call_ins), MISMATCH_EXIT); + } + } +#endif + if (FUN_INTERPRETED(fun)) { if (mode == JSOP_NEW) { LIns* args[] = { get(&fval), INS_CONSTPTR(&js_ObjectClass), cx_ins }; LIns* tv_ins = lir->insCall(&js_NewInstance_ci, args); guard(false, lir->insEqP_0(tv_ins), OOM_EXIT); set(&tval, tv_ins); } return interpretedFunctionCall(fval, fun, argc, mode == JSOP_NEW); @@ -11628,17 +11663,25 @@ TraceRecorder::functionCall(uintN argc, CHECK_STATUS(guardNativeConversion(argv[0])); return callImacro(call_imacros.String); } set(&fval, stringify(argv[0])); pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; return RECORD_CONTINUE; } - return callNative(argc, mode); + RecordingStatus rs = callNative(argc, mode); +#ifdef MOZ_TRACE_JSCALLS + if (cx->functionCallback) { + LIns* args[] = { INS_CONST(0), INS_CONSTPTR(fun), cx_ins }; + LIns* call_ins = lir->insCall(&functionProbe_ci, args); + guard(false, lir->insEqI_0(call_ins), MISMATCH_EXIT); + } +#endif + return rs; } JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_NEW() { uintN argc = GET_ARGC(cx->regs->pc); cx->assertValidStackDepth(argc + 2); return InjectStatus(functionCall(argc, JSOP_NEW)); @@ -15668,16 +15711,24 @@ TraceRecorder::record_JSOP_STOP() * the pc after the calling op, still in the same JSStackFrame. */ updateAtoms(fp->script); return ARECORD_CONTINUE; } putActivationObjects(); +#ifdef MOZ_TRACE_JSCALLS + if (cx->functionCallback) { + LIns* args[] = { INS_CONST(0), INS_CONSTPTR(cx->fp->fun), cx_ins }; + LIns* call_ins = lir->insCall(&functionProbe_ci, args); + guard(false, lir->insEqI_0(call_ins), MISMATCH_EXIT); + } +#endif + /* * We know falling off the end of a constructor returns the new object that * was passed in via fp->argv[-1], while falling off the end of a function * returns undefined. * * NB: we do not support script rval (eval, API users who want the result * of the last expression-statement, debugger API calls). */
--- a/js/src/jsutil.cpp +++ b/js/src/jsutil.cpp @@ -41,17 +41,16 @@ /* * PR assertion checker. */ #include <stdio.h> #include <stdlib.h> #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" -#include "jstl.h" #ifdef WIN32 # include "jswin.h" #else # include <signal.h> #endif using namespace js;
--- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -298,11 +298,46 @@ public: #else /* defined(DEBUG) */ #define JS_DECL_USE_GUARD_OBJECT_NOTIFIER #define JS_GUARD_OBJECT_NOTIFIER_PARAM #define JS_GUARD_OBJECT_NOTIFIER_INIT JS_BEGIN_MACRO JS_END_MACRO #endif /* !defined(DEBUG) */ +namespace js { + +template <class T> +JS_ALWAYS_INLINE static void +PodZero(T *t) +{ + memset(t, 0, sizeof(T)); +} + +template <class T> +JS_ALWAYS_INLINE static void +PodZero(T *t, size_t nelem) +{ + memset(t, 0, nelem * sizeof(T)); +} + +/* + * Arrays implicitly convert to pointers to their first element, which is + * dangerous when combined with the above PodZero definitions. Adding an + * overload for arrays is ambiguous, so we need another identifier. The + * ambiguous overload is left to catch mistaken uses of PodZero; if you get a + * compile error involving PodZero and array types, use PodArrayZero instead. + */ +template <class T, size_t N> static void PodZero(T (&)[N]); /* undefined */ +template <class T, size_t N> static void PodZero(T (&)[N], size_t); /* undefined */ + +template <class T, size_t N> +JS_ALWAYS_INLINE static void +PodArrayZero(T (&t)[N]) +{ + memset(t, 0, N * sizeof(T)); +} + +} /* namespace js */ + #endif /* defined(__cplusplus) */ #endif /* jsutil_h___ */
--- a/js/src/jsvalue.h +++ b/js/src/jsvalue.h @@ -965,10 +965,63 @@ typedef js::Value ValueArgType; static JS_ALWAYS_INLINE const Value & ValueArgToConstRef(const Value &v) { return v; } #endif +/******************************************************************************/ + +static JS_ALWAYS_INLINE void +MakeValueRangeGCSafe(Value *vec, size_t len) +{ + PodZero(vec, len); +} + +static JS_ALWAYS_INLINE void +MakeValueRangeGCSafe(Value *beg, Value *end) +{ + PodZero(beg, end - beg); +} + +static JS_ALWAYS_INLINE void +MakeIdRangeGCSafe(jsid *beg, jsid *end) +{ + for (jsid *id = beg; id != end; ++id) + *id = INT_TO_JSID(0); +} + +static JS_ALWAYS_INLINE void +MakeIdRangeGCSafe(jsid *vec, size_t len) +{ + MakeIdRangeGCSafe(vec, vec + len); +} + +static JS_ALWAYS_INLINE void +SetValueRangeToUndefined(Value *beg, Value *end) +{ + for (Value *v = beg; v != end; ++v) + v->setUndefined(); +} + +static JS_ALWAYS_INLINE void +SetValueRangeToUndefined(Value *vec, size_t len) +{ + return SetValueRangeToUndefined(vec, vec + len); +} + +static JS_ALWAYS_INLINE void +SetValueRangeToNull(Value *beg, Value *end) +{ + for (Value *v = beg; v != end; ++v) + v->setNull(); +} + +static JS_ALWAYS_INLINE void +SetValueRangeToNull(Value *vec, size_t len) +{ + return SetValueRangeToNull(vec, vec + len); +} + } /* namespace js */ #endif /* jsvalue_h__ */
--- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -1678,16 +1678,18 @@ ParseXMLSource(JSContext *cx, JSString * static const char suffix[] = "</parent>"; #define constrlen(constr) (sizeof(constr) - 1) if (!js_GetDefaultXMLNamespace(cx, &nsval)) return NULL; uri = GetURI(JSVAL_TO_OBJECT(nsval)); uri = js_EscapeAttributeValue(cx, uri, JS_FALSE); + if (!uri) + return NULL; urilen = uri->length(); srclen = src->length(); length = constrlen(prefix) + urilen + constrlen(middle) + srclen + constrlen(suffix); chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar)); if (!chars)
--- a/js/src/shell/njs +++ b/js/src/shell/njs @@ -1,28 +1,34 @@ #!/usr/bin/python # # Narcissus 'shell' for use with jstests.py # Expects to be in the same directory as ./js # Expects the Narcissus src files to be in ./narcissus/ -import os, re, sys +import os, re, sys, signal from subprocess import * from optparse import OptionParser THIS_DIR = os.path.dirname(__file__) NARC_JS_DIR = os.path.abspath(os.path.join(THIS_DIR, 'narcissus')) js_cmd = os.path.abspath(os.path.join(THIS_DIR, "js")) narc_jsdefs = os.path.join(NARC_JS_DIR, "jsdefs.js") narc_jslex = os.path.join(NARC_JS_DIR, "jslex.js") narc_jsparse = os.path.join(NARC_JS_DIR, "jsparse.js") narc_jsexec = os.path.join(NARC_JS_DIR, "jsexec.js") +def handler(signum, frame): + print '' + # the exit code produced by ./js on SIGINT + sys.exit(130) + +signal.signal(signal.SIGINT, handler) if __name__ == '__main__': op = OptionParser(usage='%prog [TEST-SPECS]') op.add_option('-f', '--file', dest='js_files', action='append', help='JS file to load', metavar='FILE') op.add_option('-e', '--expression', dest='js_exps', action='append', help='JS expression to evaluate') op.add_option('-i', '--interactive', dest='js_interactive', action='store_true', @@ -34,22 +40,22 @@ if __name__ == '__main__': cmd = "" if options.js_harmony: cmd += 'Narcissus.options={version:"harmony"}; ' if options.js_exps: for exp in options.js_exps: - cmd += 'Narcissus.jsexec.evaluate("%s"); ' % exp.replace('"', '\\"') + cmd += 'Narcissus.interpreter.evaluate("%s"); ' % exp.replace('"', '\\"') if options.js_files: for file in options.js_files: - cmd += 'Narcissus.jsexec.evaluate(snarf("%(file)s"), "%(file)s", 1); ' % { 'file':file } + cmd += 'Narcissus.interpreter.evaluate(snarf("%(file)s"), "%(file)s", 1); ' % { 'file':file } if (not options.js_exps) and (not options.js_files): options.js_interactive = True if options.js_interactive: - cmd += 'Narcissus.jsexec.repl();' + cmd += 'Narcissus.interpreter.repl();' Popen([js_cmd, '-f', narc_jsdefs, '-f', narc_jslex, '-f', narc_jsparse, '-f', narc_jsexec, '-e', cmd]).wait()
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug583615.js @@ -0,0 +1,12 @@ +// |trace-test| slow; + +try { + x = <x><y/></x> + x += /x / +} catch (e) {} +for each(a in [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0]) { + x += x; +} +default xml namespace = x; +<x></x>
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/basic/testBug584650.js @@ -0,0 +1,9 @@ +if (typeof gczeal != "function") + gczeal = function() {} + +// don't crash +x = (evalcx('lazy')) +x.watch("", function () {}) +gczeal(1) +for (w in x) {} +
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/basic/testErrorReportIn_getPrototypeOf.js @@ -0,0 +1,9 @@ +actual = ""; +expect = "TypeError: \"kittens\" is not an object"; +try { + Object.getPrototypeOf.apply(null, ["kittens",4,3]) +} catch (e) { + actual = "" + e; +} + +assertEq(actual, expect);
--- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -798,35 +798,60 @@ private: void XPCJSRuntime::WatchdogMain(void *arg) { XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg); // Lock lasts until we return AutoLockJSGC lock(self->mJSRuntime); + PRIntervalTime sleepInterval; while (self->mWatchdogThread) { + // Sleep only 1 second if recently (or currently) active; otherwise, hibernate + if (self->mLastActiveTime == -1 || PR_Now() - self->mLastActiveTime <= 2*PR_USEC_PER_SEC) + sleepInterval = PR_TicksPerSecond(); + else + { + sleepInterval = PR_INTERVAL_NO_TIMEOUT; + self->mWatchdogHibernating = PR_TRUE; + } #ifdef DEBUG PRStatus status = #endif - PR_WaitCondVar(self->mWatchdogWakeup, PR_TicksPerSecond()); + PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval); JS_ASSERT(status == PR_SUCCESS); - JSContext* cx = nsnull; while((cx = js_NextActiveContext(self->mJSRuntime, cx))) { JS_TriggerOperationCallback(cx); } } /* Wake up the main thread waiting for the watchdog to terminate. */ PR_NotifyCondVar(self->mWatchdogWakeup); } +//static +void +XPCJSRuntime::ActivityCallback(void *arg, PRBool active) +{ + XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg); + if (active) { + self->mLastActiveTime = -1; + if (self->mWatchdogHibernating) + { + self->mWatchdogHibernating = PR_FALSE; + PR_NotifyCondVar(self->mWatchdogWakeup); + } + } else { + self->mLastActiveTime = PR_Now(); + } +} + /***************************************************************************/ #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN static JSDHashOperator DEBUG_WrapperChecker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { @@ -1103,17 +1128,19 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* mThreadRunningGC(nsnull), mWrappedJSToReleaseArray(), mNativesToReleaseArray(), mDoingFinalization(JS_FALSE), mVariantRoots(nsnull), mWrappedJSRoots(nsnull), mObjectHolderRoots(nsnull), mWatchdogWakeup(nsnull), - mWatchdogThread(nsnull) + mWatchdogThread(nsnull), + mWatchdogHibernating(PR_FALSE), + mLastActiveTime(-1) { #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN DEBUG_WrappedNativeHashtable = JS_NewDHashTable(JS_DHashGetStubOps(), nsnull, sizeof(JSDHashEntryStub), 128); #endif NS_TIME_FUNCTION; @@ -1133,16 +1160,18 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* // the GC's allocator. JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff); JS_SetContextCallback(mJSRuntime, ContextCallback); JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback); JS_SetGCCallbackRT(mJSRuntime, GCCallback); JS_SetExtraGCRoots(mJSRuntime, TraceJS, this); mWatchdogWakeup = JS_NEW_CONDVAR(mJSRuntime->gcLock); + mJSRuntime->setActivityCallback(ActivityCallback, this); + mJSRuntime->setCustomGCChunkAllocator(&gXPCJSChunkAllocator); NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSRuntimeGCChunks)); } if(!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull, sizeof(ObjectHolder), 512)) mJSHolders.ops = nsnull;
--- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -715,16 +715,18 @@ public: private: JSDHashTable* DEBUG_WrappedNativeHashtable; public: #endif void AddGCCallback(JSGCCallback cb); void RemoveGCCallback(JSGCCallback cb); + static void ActivityCallback(void *arg, PRBool active); + private: XPCJSRuntime(); // no implementation XPCJSRuntime(nsXPConnect* aXPConnect); // The caller must be holding the GC lock void RescheduleWatchdog(XPCContext* ccx); static void WatchdogMain(void *arg); @@ -753,16 +755,18 @@ private: JSBool mDoingFinalization; XPCRootSetElem *mVariantRoots; XPCRootSetElem *mWrappedJSRoots; XPCRootSetElem *mObjectHolderRoots; JSDHashTable mJSHolders; PRCondVar *mWatchdogWakeup; PRThread *mWatchdogThread; nsTArray<JSGCCallback> extraGCCallbacks; + PRBool mWatchdogHibernating; + PRTime mLastActiveTime; // -1 if active NOW }; /***************************************************************************/ /***************************************************************************/ // XPCContext is mostly a dumb class to hold JSContext specific data and // maps that let us find wrappers created for the given JSContext. // no virtuals