[JAEGER] Merge from tracemonkey.

This commit is contained in:
David Mandelin 2010-08-11 11:05:24 -07:00
commit 4b7d4e43d2
27 changed files with 777 additions and 320 deletions

View File

@ -110,6 +110,7 @@ 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@

View File

@ -7335,6 +7335,17 @@ 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 ========================================================

View File

@ -48,7 +48,7 @@ Narcissus = {
options: { version: 185 }
};
Narcissus.jsdefs = (function() {
Narcissus.definitions = (function() {
var tokens = [
// End of source.
@ -190,14 +190,14 @@ Narcissus.jsdefs = (function() {
}
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
};
}());

View File

@ -48,13 +48,13 @@
* 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;
@ -68,7 +68,7 @@ Narcissus.jsexec = (function() {
// Function properties.
eval: function eval(s) {
if (typeof s != "string")
if (typeof s !== "string")
return s;
var x = ExecutionContext.current;
@ -79,8 +79,8 @@ Narcissus.jsexec = (function() {
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) {
@ -111,7 +111,7 @@ Narcissus.jsexec = (function() {
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) {
@ -128,12 +128,12 @@ Narcissus.jsexec = (function() {
// 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});
},
@ -163,7 +163,7 @@ Narcissus.jsexec = (function() {
// 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)
@ -181,8 +181,8 @@ Narcissus.jsexec = (function() {
// 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;
}
@ -213,7 +213,7 @@ Narcissus.jsexec = (function() {
try {
thunk();
return this.result;
} catch (e if e == THROW) {
} catch (e if e === THROW) {
if (prev) {
prev.result = this.result;
throw THROW;
@ -253,15 +253,15 @@ Narcissus.jsexec = (function() {
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) {
@ -287,17 +287,17 @@ Narcissus.jsexec = (function() {
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;
}
@ -311,7 +311,7 @@ Narcissus.jsexec = (function() {
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++) {
@ -322,7 +322,7 @@ Narcissus.jsexec = (function() {
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
@ -345,7 +345,7 @@ Narcissus.jsexec = (function() {
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;
@ -354,7 +354,7 @@ Narcissus.jsexec = (function() {
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
@ -366,11 +366,11 @@ Narcissus.jsexec = (function() {
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];
}
@ -386,9 +386,9 @@ Narcissus.jsexec = (function() {
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));
@ -404,7 +404,9 @@ Narcissus.jsexec = (function() {
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);
@ -412,9 +414,9 @@ Narcissus.jsexec = (function() {
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;
}
}
@ -424,9 +426,9 @@ Narcissus.jsexec = (function() {
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)));
@ -440,17 +442,17 @@ Narcissus.jsexec = (function() {
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;
@ -497,15 +499,15 @@ Narcissus.jsexec = (function() {
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)
@ -515,7 +517,7 @@ Narcissus.jsexec = (function() {
case LABEL:
try {
execute(n.statement, x);
} catch (e if e == BREAK && x.target == n) {
} catch (e if e === BREAK && x.target === n) {
}
break;
@ -612,7 +614,7 @@ Narcissus.jsexec = (function() {
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;
@ -688,7 +690,7 @@ Narcissus.jsexec = (function() {
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;
@ -712,16 +714,16 @@ Narcissus.jsexec = (function() {
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);
}
@ -735,13 +737,13 @@ Narcissus.jsexec = (function() {
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);
}
@ -761,12 +763,12 @@ Narcissus.jsexec = (function() {
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));
}
}
@ -815,8 +817,8 @@ Narcissus.jsexec = (function() {
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
@ -829,10 +831,10 @@ Narcissus.jsexec = (function() {
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) {
@ -914,16 +916,16 @@ Narcissus.jsexec = (function() {
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 {
@ -955,7 +957,7 @@ Narcissus.jsexec = (function() {
}
var o;
while ((o = v.__proto__)) {
if (o == p)
if (o === p)
return true;
v = o;
}
@ -969,24 +971,24 @@ Narcissus.jsexec = (function() {
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
@ -1013,18 +1015,18 @@ Narcissus.jsexec = (function() {
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) {
@ -1048,7 +1050,7 @@ Narcissus.jsexec = (function() {
// 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);
@ -1059,15 +1061,15 @@ Narcissus.jsexec = (function() {
}
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;
@ -1084,9 +1086,9 @@ Narcissus.jsexec = (function() {
// 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) {
@ -1094,9 +1096,9 @@ Narcissus.jsexec = (function() {
} 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));
}
@ -1111,20 +1113,25 @@ Narcissus.jsexec = (function() {
}
}
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());
@ -1137,8 +1144,8 @@ Narcissus.jsexec = (function() {
}
return {
"evaluate": evaluate,
"repl": repl
evaluate: evaluate,
repl: repl
};
}());

View File

@ -41,16 +41,16 @@
* 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;
@ -82,7 +82,7 @@ Narcissus.jslex = (function() {
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() {
@ -90,7 +90,7 @@ Narcissus.jslex = (function() {
},
match: function (tt, scanOperand) {
return this.get(scanOperand) == tt || this.unget();
return this.get(scanOperand) === tt || this.unget();
},
mustMatch: function (tt) {
@ -103,7 +103,7 @@ Narcissus.jslex = (function() {
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 {
@ -337,13 +337,13 @@ Narcissus.jslex = (function() {
}
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;
}
@ -363,7 +363,7 @@ Narcissus.jslex = (function() {
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;
},
@ -379,7 +379,7 @@ Narcissus.jslex = (function() {
--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;
}
@ -431,7 +431,7 @@ Narcissus.jslex = (function() {
* 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;
},
@ -463,7 +463,7 @@ Narcissus.jslex = (function() {
}
};
return { "Tokenizer": Tokenizer };
return { Tokenizer: Tokenizer };
}());

View File

@ -43,13 +43,13 @@
* 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.
@ -383,8 +383,8 @@ Narcissus.jsparse = (function() {
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;
},
@ -660,9 +660,9 @@ Narcissus.jsparse = (function() {
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);
},
@ -827,7 +827,7 @@ Narcissus.jsparse = (function() {
}
// 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);
@ -874,14 +874,14 @@ Narcissus.jsparse = (function() {
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; });
@ -899,12 +899,12 @@ Narcissus.jsparse = (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)
@ -927,17 +927,30 @@ Narcissus.jsparse = (function() {
* 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;
@ -993,7 +1006,7 @@ Narcissus.jsparse = (function() {
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)
@ -1002,8 +1015,8 @@ Narcissus.jsparse = (function() {
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;
@ -1013,8 +1026,8 @@ Narcissus.jsparse = (function() {
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;
@ -1030,17 +1043,17 @@ Narcissus.jsparse = (function() {
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 {
/*
@ -1059,8 +1072,8 @@ Narcissus.jsparse = (function() {
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);
}
@ -1073,11 +1086,11 @@ Narcissus.jsparse = (function() {
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));
}
@ -1113,11 +1126,11 @@ Narcissus.jsparse = (function() {
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);
@ -1131,7 +1144,7 @@ Narcissus.jsparse = (function() {
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
@ -1140,22 +1153,22 @@ Narcissus.jsparse = (function() {
* 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 {
@ -1207,7 +1220,7 @@ Narcissus.jsparse = (function() {
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);
@ -1232,7 +1245,7 @@ Narcissus.jsparse = (function() {
break;
case LET:
if (t.peek() == LEFT_PAREN)
if (t.peek() === LEFT_PAREN)
n = LetBlock(t, x, true);
else
n = Variables(t, x);
@ -1250,14 +1263,14 @@ Narcissus.jsparse = (function() {
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();
@ -1285,9 +1298,9 @@ Narcissus.jsparse = (function() {
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);
@ -1296,11 +1309,11 @@ Narcissus.jsparse = (function() {
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;
@ -1308,17 +1321,17 @@ Narcissus.jsparse = (function() {
}
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;
}
@ -1326,7 +1339,7 @@ Narcissus.jsparse = (function() {
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);
@ -1370,21 +1383,20 @@ Narcissus.jsparse = (function() {
// 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");
@ -1394,44 +1406,76 @@ Narcissus.jsparse = (function() {
}
/*
* 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:
*
* Pass 1: If there needs to be hoisting at any child block or inner
* function, the entire function gets reparsed.
* 1. vars hoist to the top of their function:
*
* Pass 2: It's possible that hoisting has changed the upvars of
* functions. That is, consider:
* var x = 'global';
* function f() {
* x = 'f';
* if (false)
* var x;
* }
* f();
* print(x); // "global"
*
* function f() {
* x = 0;
* g();
* x; // x's forward pointer should be invalidated!
* function g() {
* x = 'g';
* }
* var x;
* }
* 2. lets hoist to the top of their block:
*
* 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.
* 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.
*
* 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));
@ -1439,12 +1483,12 @@ Narcissus.jsparse = (function() {
}
}
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;
@ -1486,7 +1530,7 @@ Narcissus.jsparse = (function() {
* 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;
@ -1509,14 +1553,14 @@ Narcissus.jsparse = (function() {
* 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;
}
@ -1538,11 +1582,11 @@ Narcissus.jsparse = (function() {
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)) {
@ -1584,7 +1628,7 @@ Narcissus.jsparse = (function() {
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
@ -1610,9 +1654,9 @@ Narcissus.jsparse = (function() {
}
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;
@ -1621,15 +1665,15 @@ Narcissus.jsparse = (function() {
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);
@ -1673,7 +1717,7 @@ Narcissus.jsparse = (function() {
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();
@ -1734,9 +1778,9 @@ Narcissus.jsparse = (function() {
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);
}
@ -1762,7 +1806,7 @@ Narcissus.jsparse = (function() {
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));
@ -1945,7 +1989,7 @@ Narcissus.jsparse = (function() {
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);
@ -2029,7 +2073,7 @@ Narcissus.jsparse = (function() {
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);
@ -2062,7 +2106,7 @@ Narcissus.jsparse = (function() {
n = PrimaryExpression(t, x);
}
while ((tt = t.get()) != END) {
while ((tt = t.get()) !== END) {
switch (tt) {
case DOT:
n2 = b.MEMBER$build(t);
@ -2109,11 +2153,11 @@ Narcissus.jsparse = (function() {
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);
@ -2135,20 +2179,20 @@ Narcissus.jsparse = (function() {
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));
@ -2166,8 +2210,8 @@ Narcissus.jsparse = (function() {
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);
@ -2183,7 +2227,7 @@ Narcissus.jsparse = (function() {
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;
@ -2199,7 +2243,7 @@ Narcissus.jsparse = (function() {
} 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);
}
@ -2240,7 +2284,7 @@ Narcissus.jsparse = (function() {
* 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)
@ -2250,13 +2294,13 @@ Narcissus.jsparse = (function() {
}
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
};
}());

View File

@ -4402,6 +4402,17 @@ if test "$JS_HAS_CTYPES"; then
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 ========================================================

View File

@ -57,6 +57,7 @@ CPPSRCS = \
testDefineProperty.cpp \
testExtendedEq.cpp \
testGCChunkAlloc.cpp \
testFuncCallback.cpp \
testIntString.cpp \
testIsAboutToBeFinalized.cpp \
testLookup.cpp \

View File

@ -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)

View File

@ -805,6 +805,9 @@ JS_BeginRequest(JSContext *cx)
cx->outstandingRequests++;
cx->thread->requestContext = cx;
rt->requestCount++;
if (rt->requestCount == 1 && rt->activityCallback)
rt->activityCallback(rt->activityCallbackArg, true);
}
#endif
}
@ -853,8 +856,11 @@ StopRequest(JSContext *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
@ -5660,6 +5666,20 @@ JS_ClearContextThread(JSContext *cx)
#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)

View File

@ -3029,6 +3029,19 @@ 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

View File

@ -1261,6 +1261,9 @@ struct JSCompartment {
void sweep(JSContext *cx);
};
typedef void
(* JSActivityCallback)(void *arg, JSBool active);
struct JSRuntime {
/* Default compartment. */
JSCompartment *defaultCompartment;
@ -1277,6 +1280,20 @@ struct JSRuntime {
/* 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
@ -1987,6 +2004,19 @@ struct JSContext
#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). */
@ -3197,11 +3227,25 @@ class AutoValueVector : private AutoGCRooter
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) {
@ -3243,11 +3287,25 @@ class AutoIdVector : private AutoGCRooter
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) {
@ -3275,44 +3333,6 @@ class AutoIdVector : private AutoGCRooter
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

View File

@ -69,11 +69,12 @@ class DTrace {
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();
};
@ -106,6 +107,9 @@ DTrace::enterJSFun(JSContext *cx, JSStackFrame *fp, JSFunction *fun, JSStackFram
handleFunctionArgs(cx, fp, fun, argc, argv);
}
#endif
#ifdef MOZ_TRACE_JSCALLS
cx->doFunctionCallback(fun, fun ? FUN_SCRIPT(fun) : NULL, true);
#endif
}
inline void
@ -120,6 +124,9 @@ DTrace::exitJSFun(JSContext *cx, JSStackFrame *fp, JSFunction *fun,
handleFunctionReturn(cx, fp, fun);
}
#endif
#ifdef MOZ_TRACE_JSCALLS
cx->doFunctionCallback(fun, fun ? FUN_SCRIPT(fun) : NULL, false);
#endif
}
inline void
@ -134,13 +141,16 @@ DTrace::finalizeObject(JSObject *obj)
/* 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
@ -150,6 +160,9 @@ DTrace::ExecutionScope::~ExecutionScope()
if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
endExecution();
#endif
#ifdef MOZ_TRACE_JSCALLS
cx->doFunctionCallback(NULL, script, false);
#endif
}
/* Object creation scope. */

View File

@ -841,7 +841,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
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.

View File

@ -1643,7 +1643,7 @@ obj_getPrototypeOf(JSContext *cx, uintN argc, Value *vp)
}
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,

View File

@ -374,37 +374,6 @@ class Conditionally {
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
{

View File

@ -10566,6 +10566,22 @@ 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()
{
@ -10585,6 +10601,14 @@ TraceRecorder::record_JSOP_RETURN()
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;
@ -11607,6 +11631,17 @@ TraceRecorder::functionCall(uintN argc, JSOp mode)
*/
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 };
@ -11633,7 +11668,15 @@ TraceRecorder::functionCall(uintN argc, JSOp mode)
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
@ -15673,6 +15716,14 @@ TraceRecorder::record_JSOP_STOP()
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

View File

@ -46,7 +46,6 @@
#include "jstypes.h"
#include "jsstdint.h"
#include "jsutil.h"
#include "jstl.h"
#ifdef WIN32
# include "jswin.h"

View File

@ -303,6 +303,41 @@ public:
#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___ */

View File

@ -970,5 +970,58 @@ ValueArgToConstRef(const Value &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__ */

View File

@ -1683,6 +1683,8 @@ ParseXMLSource(JSContext *cx, JSString *src)
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();

View File

@ -4,7 +4,7 @@
# 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
@ -18,6 +18,12 @@ 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]')
@ -39,17 +45,17 @@ if __name__ == '__main__':
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()

View File

@ -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>

View File

@ -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) {}

View File

@ -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);

View File

@ -803,14 +803,22 @@ XPCJSRuntime::WatchdogMain(void *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)))
{
@ -822,6 +830,23 @@ XPCJSRuntime::WatchdogMain(void *arg)
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();
}
}
/***************************************************************************/
@ -1108,7 +1133,9 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
mWrappedJSRoots(nsnull),
mObjectHolderRoots(nsnull),
mWatchdogWakeup(nsnull),
mWatchdogThread(nsnull)
mWatchdogThread(nsnull),
mWatchdogHibernating(PR_FALSE),
mLastActiveTime(-1)
{
#ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
DEBUG_WrappedNativeHashtable =
@ -1138,6 +1165,8 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
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));

View File

@ -720,6 +720,8 @@ public:
void AddGCCallback(JSGCCallback cb);
void RemoveGCCallback(JSGCCallback cb);
static void ActivityCallback(void *arg, PRBool active);
private:
XPCJSRuntime(); // no implementation
XPCJSRuntime(nsXPConnect* aXPConnect);
@ -758,6 +760,8 @@ private:
PRCondVar *mWatchdogWakeup;
PRThread *mWatchdogThread;
nsTArray<JSGCCallback> extraGCCallbacks;
PRBool mWatchdogHibernating;
PRTime mLastActiveTime; // -1 if active NOW
};
/***************************************************************************/