mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
[JAEGER] Merge from tracemonkey.
This commit is contained in:
commit
4b7d4e43d2
@ -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@
|
||||
|
||||
|
11
configure.in
11
configure.in
@ -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 ========================================================
|
||||
|
@ -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
|
||||
};
|
||||
}());
|
||||
|
||||
|
@ -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,11 +763,11 @@ 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__'
|
||||
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
|
||||
};
|
||||
|
||||
}());
|
||||
|
@ -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 };
|
||||
|
||||
}());
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
* Pass 2: It's possible that hoisting has changed the upvars of
|
||||
* functions. That is, consider:
|
||||
* 1. vars hoist to the top of their function:
|
||||
*
|
||||
* var x = 'global';
|
||||
* function f() {
|
||||
* x = 0;
|
||||
* g();
|
||||
* x; // x's forward pointer should be invalidated!
|
||||
* function g() {
|
||||
* x = 'g';
|
||||
* }
|
||||
* x = 'f';
|
||||
* if (false)
|
||||
* var x;
|
||||
* }
|
||||
* f();
|
||||
* print(x); // "global"
|
||||
*
|
||||
* 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.
|
||||
* 2. lets hoist to the top of their block:
|
||||
*
|
||||
* 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
|
||||
};
|
||||
|
||||
}());
|
||||
|
@ -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 ========================================================
|
||||
|
@ -57,6 +57,7 @@ CPPSRCS = \
|
||||
testDefineProperty.cpp \
|
||||
testExtendedEq.cpp \
|
||||
testGCChunkAlloc.cpp \
|
||||
testFuncCallback.cpp \
|
||||
testIntString.cpp \
|
||||
testIsAboutToBeFinalized.cpp \
|
||||
testLookup.cpp \
|
||||
|
138
js/src/jsapi-tests/testFuncCallback.cpp
Normal file
138
js/src/jsapi-tests/testFuncCallback.cpp
Normal 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)
|
@ -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)
|
||||
|
@ -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
|
||||
|
104
js/src/jscntxt.h
104
js/src/jscntxt.h
@ -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
|
||||
|
@ -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. */
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -46,7 +46,6 @@
|
||||
#include "jstypes.h"
|
||||
#include "jsstdint.h"
|
||||
#include "jsutil.h"
|
||||
#include "jstl.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# include "jswin.h"
|
||||
|
@ -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___ */
|
||||
|
@ -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__ */
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
||||
|
12
js/src/trace-test/tests/basic/bug583615.js
Normal file
12
js/src/trace-test/tests/basic/bug583615.js
Normal 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>
|
9
js/src/trace-test/tests/basic/testBug584650.js
Normal file
9
js/src/trace-test/tests/basic/testBug584650.js
Normal 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) {}
|
||||
|
@ -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);
|
@ -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));
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
|
Loading…
Reference in New Issue
Block a user