Merge tracemonkey into mozilla-central. (a=blockers)

This commit is contained in:
Chris Leary 2011-01-08 00:48:44 -08:00
commit 306a55837a
55 changed files with 654 additions and 738 deletions

View File

@ -0,0 +1,45 @@
/*
* Check that builtin character classes within ranges produce syntax
* errors.
*
* Note that, per the extension in bug 351463, SpiderMonkey permits hyphens
* adjacent to character class escapes in character classes, treating them as a
* hyphen pattern character. Therefore /[\d-\s]/ is okay
*
* Note: /\b/ is the backspace escape, which is a single pattern character,
* though it looks deceptively like a character class.
*/
function isRegExpSyntaxError(pattern) {
try {
var re = new RegExp(pattern);
} catch (e) {
if (e instanceof SyntaxError)
return true;
}
return false;
}
assertEq(isRegExpSyntaxError('[C-\\s]'), true);
assertEq(isRegExpSyntaxError('[C-\\d]'), true);
assertEq(isRegExpSyntaxError('[C-\\W]'), true);
assertEq(isRegExpSyntaxError('[C-]'), false);
assertEq(isRegExpSyntaxError('[-C]'), false);
assertEq(isRegExpSyntaxError('[C-C]'), false);
assertEq(isRegExpSyntaxError('[C-C]'), false);
assertEq(isRegExpSyntaxError('[\\b-\\b]'), false);
assertEq(isRegExpSyntaxError('[\\B-\\B]'), false);
assertEq(isRegExpSyntaxError('[\\b-\\B]'), false);
assertEq(isRegExpSyntaxError('[\\B-\\b]'), true);
assertEq(isRegExpSyntaxError('[\\b-\\w]'), true);
assertEq(isRegExpSyntaxError('[\\B-\\w]'), true);
/* Extension. */
assertEq(isRegExpSyntaxError('[\\s-\\s]'), false);
assertEq(isRegExpSyntaxError('[\\W-\\s]'), false);
assertEq(isRegExpSyntaxError('[\\s-\\W]'), false);
assertEq(isRegExpSyntaxError('[\\W-C]'), false);
assertEq(isRegExpSyntaxError('[\\d-C]'), false);
assertEq(isRegExpSyntaxError('[\\s-C]'), false);
assertEq(isRegExpSyntaxError('[\\w-\\b]'), false);
assertEq(isRegExpSyntaxError('[\\w-\\B]'), false);

View File

@ -0,0 +1,16 @@
// |jit-test| allow-oom
(function() {
Iterator((function() {
switch ((7)) {
default:
return (Float32Array).call([], 4300018)
case Proxy.create((function() {
return {
e:
function() {}
}
})):
}
})())
})()

View File

@ -1,19 +1,6 @@
/* Touch/init early so global shape doesn't change in loop */ X = { value: "" }
var SetOnIter = HOTLOOP - 1; z = 0;
var x = 3; for (var i = 0; i < 20; i++) {
var i = 0; Object.defineProperty(this, "z", X);
assertEq(true, true); z = 1;
for (i = 0; i < SetOnIter + 10; ++i) {
x = 3;
setGlobalPropIf(i == SetOnIter, 'x', 'pretty');
assertEq(x == 'pretty', i == SetOnIter);
x = 3;
}
for (i = 0; i < SetOnIter + 10; ++i) {
x = 3;
defGlobalPropIf(i == SetOnIter, 'x', { value:'pretty' });
assertEq(x == 'pretty', i == SetOnIter);
x = 3;
} }

View File

@ -0,0 +1,17 @@
function f() {
for (var a = 0; a < HOTLOOP; ++a) {
(function () {
var x;
for (var b = 0; b < HOTLOOP; ++b) {
x = 0;
(function () {
for (var i = 0; i < 1; ++i) {
x = 1;
}
})();
x++;
}
})();
}
}
f();

View File

@ -0,0 +1,10 @@
for (var j=0;j<2;++j) { (function(o){o.length})(String.prototype); }
for each(let y in [Number, Number]) {
try {
"".length()
} catch(e) {}
}
/* Don't crash. */

View File

@ -346,3 +346,4 @@ MSG_DEF(JSMSG_CANT_WRAP_XML_OBJECT, 263, 0, JSEXN_TYPEERR, "can't wrap XML obj
MSG_DEF(JSMSG_BAD_CLONE_VERSION, 264, 0, JSEXN_ERR, "unsupported structured clone version") MSG_DEF(JSMSG_BAD_CLONE_VERSION, 264, 0, JSEXN_ERR, "unsupported structured clone version")
MSG_DEF(JSMSG_CANT_CLONE_OBJECT, 265, 0, JSEXN_TYPEERR, "can't clone object") MSG_DEF(JSMSG_CANT_CLONE_OBJECT, 265, 0, JSEXN_TYPEERR, "can't clone object")
MSG_DEF(JSMSG_NON_NATIVE_SCOPE, 266, 0, JSEXN_TYPEERR, "non-native scope object") MSG_DEF(JSMSG_NON_NATIVE_SCOPE, 266, 0, JSEXN_TYPEERR, "non-native scope object")
MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 267, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")

View File

@ -2996,6 +2996,8 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto)
gc::FinalizeKind kind = GuessObjectGCKind(length, true); gc::FinalizeKind kind = GuessObjectGCKind(length, true);
JSObject *obj = detail::NewObject<WithProto::Class, false>(cx, &js_ArrayClass, proto, NULL, kind); JSObject *obj = detail::NewObject<WithProto::Class, false>(cx, &js_ArrayClass, proto, NULL, kind);
if (!obj)
return NULL;
obj->setArrayLength(length); obj->setArrayLength(length);
@ -3027,6 +3029,9 @@ JSObject *
NewDenseCopiedArray(JSContext *cx, uintN length, Value *vp, JSObject *proto) NewDenseCopiedArray(JSContext *cx, uintN length, Value *vp, JSObject *proto)
{ {
JSObject* obj = NewArray<true>(cx, length, proto); JSObject* obj = NewArray<true>(cx, length, proto);
if (!obj)
return NULL;
JS_ASSERT(obj->getDenseArrayCapacity() >= length); JS_ASSERT(obj->getDenseArrayCapacity() >= length);
if (vp) if (vp)

View File

@ -2019,14 +2019,8 @@ JSContext::resetCompartment()
compartment = scopeobj->compartment(); compartment = scopeobj->compartment();
/*
* If wrapException fails, it overrides this->exception and
* reports OOM. The upshot is that we silently turn the exception
* into an uncatchable OOM error. A bit surprising, but the
* caller is just going to return false either way.
*/
if (isExceptionPending()) if (isExceptionPending())
(void) compartment->wrapException(this); wrapPendingException();
return; return;
error: error:
@ -2038,6 +2032,21 @@ error:
compartment = NULL; compartment = NULL;
} }
/*
* Since this function is only called in the context of a pending exception,
* the caller must subsequently take an error path. If wrapping fails, we leave
* the exception cleared, which, in the context of an error path, will be
* interpreted as an uncatchable exception.
*/
void
JSContext::wrapPendingException()
{
Value v = getPendingException();
clearPendingException();
if (compartment->wrap(this, &v))
setPendingException(v);
}
void void
JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &newregs) JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &newregs)
{ {

View File

@ -896,7 +896,7 @@ private:
#ifdef DEBUG #ifdef DEBUG
# define FUNCTION_KIND_METER_LIST(_) \ # define FUNCTION_KIND_METER_LIST(_) \
_(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \ _(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \
_(display), _(flat), _(setupvar), _(badfunarg), \ _(flat), _(badfunarg), \
_(joinedsetmethod), _(joinedinitmethod), \ _(joinedsetmethod), _(joinedinitmethod), \
_(joinedreplace), _(joinedsort), _(joinedmodulepat), \ _(joinedreplace), _(joinedsort), _(joinedmodulepat), \
_(mreadbarrier), _(mwritebarrier), _(mwslotbarrier), \ _(mreadbarrier), _(mwritebarrier), _(mwslotbarrier), \
@ -1768,6 +1768,7 @@ struct JSContext
friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode); friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode);
void resetCompartment(); void resetCompartment();
void wrapPendingException();
/* 'regs' must only be changed by calling this function. */ /* 'regs' must only be changed by calling this function. */
void setCurrentRegs(JSFrameRegs *regs) { void setCurrentRegs(JSFrameRegs *regs) {

View File

@ -208,17 +208,21 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
if (obj->getCompartment() == this) if (obj->getCompartment() == this)
return true; return true;
if (cx->runtime->preWrapObjectCallback) if (cx->runtime->preWrapObjectCallback) {
obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
if (!obj) if (!obj)
return false; return false;
}
vp->setObject(*obj); vp->setObject(*obj);
if (obj->getCompartment() == this) if (obj->getCompartment() == this)
return true; return true;
} else { } else {
if (cx->runtime->preWrapObjectCallback) if (cx->runtime->preWrapObjectCallback) {
obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
if (!obj)
return false;
}
JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
vp->setObject(*obj); vp->setObject(*obj);
@ -353,21 +357,6 @@ JSCompartment::wrap(JSContext *cx, AutoIdVector &props)
return true; return true;
} }
bool
JSCompartment::wrapException(JSContext *cx)
{
JS_ASSERT(cx->compartment == this);
if (cx->isExceptionPending()) {
Value v = cx->getPendingException();
cx->clearPendingException();
if (wrap(cx, &v))
cx->setPendingException(v);
return false;
}
return true;
}
#if defined JS_METHODJIT && defined JS_MONOIC #if defined JS_METHODJIT && defined JS_MONOIC
/* /*
* Check if the pool containing the code for jit should be destroyed, per the * Check if the pool containing the code for jit should be destroyed, per the

View File

@ -327,7 +327,6 @@ struct JS_FRIEND_API(JSCompartment) {
bool wrap(JSContext *cx, js::PropertyOp *op); bool wrap(JSContext *cx, js::PropertyOp *op);
bool wrap(JSContext *cx, js::PropertyDescriptor *desc); bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
bool wrap(JSContext *cx, js::AutoIdVector &props); bool wrap(JSContext *cx, js::AutoIdVector &props);
bool wrapException(JSContext *cx);
void sweep(JSContext *cx, uint32 releaseInterval); void sweep(JSContext *cx, uint32 releaseInterval);
void purge(JSContext *cx); void purge(JSContext *cx);

View File

@ -1974,99 +1974,6 @@ EmitLeaveBlock(JSContext *cx, JSCodeGenerator *cg, JSOp op,
return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0; return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0;
} }
/*
* When eval is called from a function, the eval code or function code it
* compiles may reference upvars that live in the eval-calling function. The
* eval-invoked compiler does not have explicit definitions for these upvars
* and we do not attempt to create them a-priori (by inspecting the function's
* args and vars) -- we could, but we'd take an avoidable penalty for each
* function local not referenced by any upvar. Instead, we map such upvars
* lazily, growing upvarMap.vector by powers of two.
*
* This function knows that it is called with pn pointing to a PN_NAME-arity
* node, and cg->parser->callerFrame having a non-null fun member, and the
* static level of cg at least one greater than the eval-calling function's
* static level.
*/
static bool
MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg)
{
JSContext *cx = cg->parser->context;
JSFunction *fun = cg->parser->callerFrame->fun();
uintN upvarLevel = fun->u.i.script->staticLevel;
JSFunctionBox *funbox = cg->funbox;
if (funbox) {
/*
* Treat top-level function definitions as escaping (i.e., as funargs),
* required since we compile each such top level function or statement
* and throw away the AST, so we can't yet see all funarg uses of this
* function being compiled (cg->funbox->object). See bug 493177.
*/
if (funbox->level == fun->u.i.script->staticLevel + 1U &&
!(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA)) {
JS_ASSERT_IF(cx->options & JSOPTION_ANONFUNFIX,
((JSFunction *) funbox->object)->atom);
return true;
}
while (funbox->level >= upvarLevel) {
if (funbox->node->pn_dflags & PND_FUNARG)
return true;
funbox = funbox->parent;
if (!funbox)
break;
}
}
JSAtom *atom = pn->pn_atom;
uintN index;
BindingKind kind = fun->script()->bindings.lookup(cx, atom, &index);
if (kind == NONE)
return true;
JS_ASSERT(cg->staticLevel > upvarLevel);
if (cg->staticLevel >= UpvarCookie::UPVAR_LEVEL_LIMIT)
return true;
JSAtomListElement *ale = cg->upvarList.lookup(atom);
if (!ale) {
if (cg->inFunction() && !cg->bindings.addUpvar(cx, atom))
return false;
ale = cg->upvarList.add(cg->parser, atom);
if (!ale)
return false;
JS_ASSERT(ALE_INDEX(ale) == cg->upvarList.count - 1);
UpvarCookie *vector = cg->upvarMap.vector;
uint32 length = cg->upvarMap.length;
JS_ASSERT(ALE_INDEX(ale) <= length);
if (ALE_INDEX(ale) == length) {
length = 2 * JS_MAX(2, length);
vector = reinterpret_cast<UpvarCookie *>(cx->realloc(vector, length * sizeof *vector));
if (!vector)
return false;
cg->upvarMap.vector = vector;
cg->upvarMap.length = length;
}
if (kind != ARGUMENT)
index += fun->nargs;
JS_ASSERT(index < JS_BIT(16));
uintN skip = cg->staticLevel - upvarLevel;
vector[ALE_INDEX(ale)].set(skip, index);
}
pn->pn_op = JSOP_GETUPVAR;
pn->pn_cookie.set(cg->staticLevel, uint16(ALE_INDEX(ale)));
pn->pn_dflags |= PND_BOUND;
return true;
}
/* /*
* Try to convert a *NAME op to a *GNAME op, which optimizes access to * Try to convert a *NAME op to a *GNAME op, which optimizes access to
* undeclared globals. Return true if a conversion was made. * undeclared globals. Return true if a conversion was made.
@ -2232,63 +2139,11 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_TRUE; return JS_TRUE;
} }
if (!caller->isFunctionFrame())
return JS_TRUE;
/* /*
* Make sure the variable object used by the compiler to initialize * Out of tricks, so we must rely on PICs to optimize named
* parent links matches the caller's varobj. Compile-n-go compiler- * accesses from direct eval called from function code.
* created function objects have the top-level cg's scopeChain set
* as their parent by Parser::newFunction.
*/ */
JSObject *scopeobj = cg->inFunction() return JS_TRUE;
? FUN_OBJECT(cg->fun())->getParent()
: cg->scopeChain();
if (scopeobj != cg->parser->callerVarObj)
return JS_TRUE;
/*
* We are compiling eval or debug script inside a function frame
* and the scope chain matches the function's variable object.
* Optimize access to function's arguments and variable and the
* arguments object.
*/
if (op != JSOP_NAME)
return JS_TRUE;
/*
* It is illegal to add upvars to heavyweight functions (and
* unnecessary, since the optimization avoids creating call
* objects). Take the following code as an eval string:
*
* (function () {
* $(init);
* function init() {
* $();
* }
* })();
*
* The first instance of "$" cannot be an upvar, because the
* outermost lambda is on "init"'s scope chain, which escapes.
*
* A similar restriction exists for upvars which do not cross
* eval (see the end of BindNameToSlot and bug 616762).
*/
if (cg->flags & TCF_FUN_HEAVYWEIGHT)
return JS_TRUE;
/*
* Generator functions may be resumed from any call stack, which
* defeats the display optimization to static link searching used
* by JSOP_{GET,CALL}UPVAR.
*/
JSFunction *fun = cg->parser->callerFrame->fun();
JS_ASSERT(cg->staticLevel >= fun->u.i.script->staticLevel);
unsigned skip = cg->staticLevel - fun->u.i.script->staticLevel;
if (cg->skipSpansGenerator(skip))
return JS_TRUE;
return MakeUpvarForEval(pn, cg);
} }
/* Optimize accesses to undeclared globals. */ /* Optimize accesses to undeclared globals. */
@ -2348,50 +2203,6 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
uint16 level = cookie.level(); uint16 level = cookie.level();
JS_ASSERT(cg->staticLevel >= level); JS_ASSERT(cg->staticLevel >= level);
/*
* A JSDefinition witnessed as a declaration by the parser cannot be an
* upvar, unless it is the degenerate kind of upvar selected above (in the
* code before the PND_GVAR test) for the special case of compile-and-go
* code generated from eval called from a function, where the eval code
* uses local vars defined in the function. We detect this upvar-for-eval
* case by checking dn's op.
*/
if (PN_OP(dn) == JSOP_GETUPVAR) {
JS_ASSERT(cg->staticLevel >= level);
if (op != JSOP_NAME)
return JS_TRUE;
#ifdef DEBUG
JSStackFrame *caller = cg->parser->callerFrame;
#endif
JS_ASSERT(caller->isScriptFrame());
JSTreeContext *tc = cg;
while (tc->staticLevel != level)
tc = tc->parent;
JSCodeGenerator *evalcg = tc->asCodeGenerator();
JS_ASSERT(evalcg->compileAndGo());
JS_ASSERT(caller->isFunctionFrame());
JS_ASSERT(cg->parser->callerVarObj == evalcg->scopeChain());
/*
* Don't generate upvars on the left side of a for loop. See
* bug 470758 and bug 520513.
*/
if (evalcg->flags & TCF_IN_FOR_INIT)
return JS_TRUE;
if (cg->staticLevel == level) {
pn->pn_op = JSOP_GETUPVAR;
pn->pn_cookie = cookie;
pn->pn_dflags |= PND_BOUND;
return JS_TRUE;
}
return MakeUpvarForEval(pn, cg);
}
const uintN skip = cg->staticLevel - level; const uintN skip = cg->staticLevel - level;
if (skip != 0) { if (skip != 0) {
JS_ASSERT(cg->inFunction()); JS_ASSERT(cg->inFunction());
@ -2410,29 +2221,8 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (cg->flags & TCF_FUN_HEAVYWEIGHT) if (cg->flags & TCF_FUN_HEAVYWEIGHT)
return JS_TRUE; return JS_TRUE;
if (cg->fun()->isFlatClosure()) { if (!cg->fun()->isFlatClosure())
op = JSOP_GETFCSLOT; return JS_TRUE;
} else {
/*
* The function we're compiling may not be heavyweight, but if it
* escapes as a funarg, we can't use JSOP_GETUPVAR/JSOP_CALLUPVAR.
* Parser::analyzeFunctions has arranged for this function's
* enclosing functions to be heavyweight, so we can safely stick
* with JSOP_NAME/JSOP_CALLNAME.
*/
if (cg->funbox->node->pn_dflags & PND_FUNARG)
return JS_TRUE;
/*
* Generator functions may be resumed from any call stack, which
* defeats the display optimization to static link searching used
* by JSOP_{GET,CALL}UPVAR.
*/
if (cg->skipSpansGenerator(skip))
return JS_TRUE;
op = JSOP_GETUPVAR;
}
ale = cg->upvarList.lookup(atom); ale = cg->upvarList.lookup(atom);
if (ale) { if (ale) {
@ -2473,7 +2263,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
vector[index].set(skip, slot); vector[index].set(skip, slot);
} }
pn->pn_op = op; pn->pn_op = JSOP_GETFCSLOT;
JS_ASSERT((index & JS_BITMASK(16)) == index); JS_ASSERT((index & JS_BITMASK(16)) == index);
pn->pn_cookie.set(0, index); pn->pn_cookie.set(0, index);
pn->pn_dflags |= PND_BOUND; pn->pn_dflags |= PND_BOUND;
@ -2852,9 +2642,6 @@ EmitNameOp(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
case JSOP_GETLOCAL: case JSOP_GETLOCAL:
op = JSOP_CALLLOCAL; op = JSOP_CALLLOCAL;
break; break;
case JSOP_GETUPVAR:
op = JSOP_CALLUPVAR;
break;
case JSOP_GETFCSLOT: case JSOP_GETFCSLOT:
op = JSOP_CALLFCSLOT; op = JSOP_CALLFCSLOT;
break; break;
@ -6070,7 +5857,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* API users may also set the JSOPTION_NO_SCRIPT_RVAL option when * API users may also set the JSOPTION_NO_SCRIPT_RVAL option when
* calling JS_Compile* to suppress JSOP_POPV. * calling JS_Compile* to suppress JSOP_POPV.
*/ */
useful = wantval = !(cg->flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL)); wantval = !(cg->flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL));
useful = wantval || pn->isDirectivePrologueMember();
if (!useful) { if (!useful) {
if (!CheckSideEffects(cx, cg, pn2, &useful)) if (!CheckSideEffects(cx, cg, pn2, &useful))
return JS_FALSE; return JS_FALSE;
@ -6266,7 +6054,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE; return JS_FALSE;
EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex); EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex);
} else { } else {
JS_ASSERT(PN_OP(pn2) != JSOP_GETUPVAR);
EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETGNAME) EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETGNAME)
? JSOP_GETGNAME ? JSOP_GETGNAME
: (PN_OP(pn2) == JSOP_SETGLOBAL) : (PN_OP(pn2) == JSOP_SETGLOBAL)

View File

@ -234,9 +234,9 @@ struct JSStmtInfo {
/* /*
* Flag to prevent a non-escaping function from being optimized into a null * Flag to prevent a non-escaping function from being optimized into a null
* closure (i.e., a closure that needs only its global object for free variable * closure (i.e., a closure that needs only its global object for free variable
* resolution, thanks to JSOP_{GET,CALL}UPVAR), because this function contains * resolution), because this function contains a closure that needs one or more
* a closure that needs one or more scope objects surrounding it (i.e., Call * scope objects surrounding it (i.e., a Call object for an outer heavyweight
* object for a heavyweight outer function). See bug 560234. * function). See bug 560234.
*/ */
#define TCF_FUN_ENTRAINS_SCOPES 0x400000 #define TCF_FUN_ENTRAINS_SCOPES 0x400000

View File

@ -455,8 +455,6 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
* immediate operand. * immediate operand.
*/ */
switch (op) { switch (op) {
case JSOP_GETUPVAR: *pc = JSOP_GETUPVAR_DBG; break;
case JSOP_CALLUPVAR: *pc = JSOP_CALLUPVAR_DBG; break;
case JSOP_GETFCSLOT: *pc = JSOP_GETUPVAR_DBG; break; case JSOP_GETFCSLOT: *pc = JSOP_GETUPVAR_DBG; break;
case JSOP_CALLFCSLOT: *pc = JSOP_CALLUPVAR_DBG; break; case JSOP_CALLFCSLOT: *pc = JSOP_CALLUPVAR_DBG; break;
case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break; case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break;
@ -1077,6 +1075,12 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
{ {
JSObject &callobj = fp->callObj(); JSObject &callobj = fp->callObj();
/*
* Strict mode eval frames have Call objects to put. Normal eval frames
* never put a Call object.
*/
JS_ASSERT(fp->isEvalFrame() == callobj.callIsForEval());
/* Get the arguments object to snapshot fp's actual argument values. */ /* Get the arguments object to snapshot fp's actual argument values. */
if (fp->hasArgsObj()) { if (fp->hasArgsObj()) {
if (!fp->hasOverriddenArgs()) if (!fp->hasOverriddenArgs())
@ -1084,53 +1088,62 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
js_PutArgsObject(cx, fp); js_PutArgsObject(cx, fp);
} }
JSFunction *fun = fp->fun(); JSScript *script = fp->script();
JS_ASSERT(fun == callobj.getCallObjCalleeFunction()); Bindings &bindings = script->bindings;
Bindings &bindings = fun->script()->bindings; if (callobj.callIsForEval()) {
uintN n = bindings.countArgsAndVars(); JS_ASSERT(script->strictModeCode);
JS_ASSERT(bindings.countArgs() == 0);
if (n != 0) { /* This could be optimized as below, but keep it simple for now. */
JS_ASSERT(JSFunction::CLASS_RESERVED_SLOTS + n <= callobj.numSlots()); CopyValuesToCallObject(callobj, 0, NULL, bindings.countVars(), fp->slots());
} else {
JSFunction *fun = fp->fun();
JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
JS_ASSERT(script == fun->script());
uint32 nvars = bindings.countVars(); if (uintN n = bindings.countArgsAndVars()) {
uint32 nargs = bindings.countArgs(); JS_ASSERT(JSObject::CALL_RESERVED_SLOTS + n <= callobj.numSlots());
JS_ASSERT(fun->nargs == nargs);
JS_ASSERT(nvars + nargs == n);
JSScript *script = fun->script(); uint32 nvars = bindings.countVars();
if (script->usesEval uint32 nargs = bindings.countArgs();
JS_ASSERT(fun->nargs == nargs);
JS_ASSERT(nvars + nargs == n);
JSScript *script = fun->script();
if (script->usesEval
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
|| script->debugMode || script->debugMode
#endif #endif
) { ) {
CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots()); CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
} else { } else {
/* /*
* For each arg & var that is closed over, copy it from the stack * For each arg & var that is closed over, copy it from the stack
* into the call object. * into the call object.
*/ */
uint32 nclosed = script->nClosedArgs; uint32 nclosed = script->nClosedArgs;
for (uint32 i = 0; i < nclosed; i++) { for (uint32 i = 0; i < nclosed; i++) {
uint32 e = script->getClosedArg(i); uint32 e = script->getClosedArg(i);
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e)); callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
} }
nclosed = script->nClosedVars; nclosed = script->nClosedVars;
for (uint32 i = 0; i < nclosed; i++) { for (uint32 i = 0; i < nclosed; i++) {
uint32 e = script->getClosedVar(i); uint32 e = script->getClosedVar(i);
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]); callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
}
} }
} }
}
/* Clear private pointers to fp, which is about to go away (js_Invoke). */ /* Clear private pointers to fp, which is about to go away (js_Invoke). */
if (js_IsNamedLambda(fun)) { if (js_IsNamedLambda(fun)) {
JSObject *env = callobj.getParent(); JSObject *env = callobj.getParent();
JS_ASSERT(env->getClass() == &js_DeclEnvClass); JS_ASSERT(env->getClass() == &js_DeclEnvClass);
JS_ASSERT(env->getPrivate() == fp); JS_ASSERT(env->getPrivate() == fp);
env->setPrivate(NULL); env->setPrivate(NULL);
}
} }
callobj.setPrivate(NULL); callobj.setPrivate(NULL);
@ -1211,30 +1224,25 @@ SetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
} }
JSBool JSBool
GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp) GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{ {
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id)); JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16) JSID_TO_INT(id); uintN i = (uint16) JSID_TO_INT(id);
JSObject *callee = obj->getCallObjCallee(); *vp = obj->getCallObjCallee()->getFlatClosureUpvar(i);
JS_ASSERT(callee);
*vp = callee->getFlatClosureUpvar(i);
return true; return true;
} }
JSBool JSBool
SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp) SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{ {
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id)); JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16) JSID_TO_INT(id); uintN i = (uint16) JSID_TO_INT(id);
JSObject *callee = obj->getCallObjCallee(); Value *up = &obj->getCallObjCallee()->getFlatClosureUpvar(i);
JS_ASSERT(callee);
Value *upvarp = &callee->getFlatClosureUpvar(i); GC_POKE(cx, *up);
GC_POKE(cx, *upvarp); *up = *vp;
*upvarp = *vp;
return true; return true;
} }
@ -1360,8 +1368,7 @@ static void
call_trace(JSTracer *trc, JSObject *obj) call_trace(JSTracer *trc, JSObject *obj)
{ {
JS_ASSERT(obj->isCall()); JS_ASSERT(obj->isCall());
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); if (JSStackFrame *fp = obj->maybeCallObjStackFrame()) {
if (fp) {
/* /*
* FIXME: Hide copies of stack values rooted by fp from the Cycle * FIXME: Hide copies of stack values rooted by fp from the Cycle
* Collector, which currently lacks a non-stub Unlink implementation * Collector, which currently lacks a non-stub Unlink implementation
@ -1370,7 +1377,7 @@ call_trace(JSTracer *trc, JSObject *obj)
* hiding hack. * hiding hack.
*/ */
uintN first = JSObject::CALL_RESERVED_SLOTS; uintN first = JSObject::CALL_RESERVED_SLOTS;
uintN count = fp->fun()->script()->bindings.countArgsAndVars(); uintN count = fp->script()->bindings.countArgsAndVars();
JS_ASSERT(obj->numSlots() >= first + count); JS_ASSERT(obj->numSlots() >= first + count);
SetValueRangeToUndefined(obj->getSlots() + first, count); SetValueRangeToUndefined(obj->getSlots() + first, count);
@ -2780,16 +2787,20 @@ js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure, JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure,
CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY) CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY)
JS_REQUIRES_STACK JSObject * JSObject *
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen) js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
{ {
/* /*
* Flat closures can be partial, they may need to search enclosing scope * Flat closures cannot yet be partial, that is, all upvars must be copied,
* objects via JSOP_NAME, etc. * or the closure won't be flattened. Therefore they do not need to search
* enclosing scope objects via JSOP_NAME, etc.
*
* FIXME: bug 545759 proposes to enable partial flat closures. Fixing this
* bug requires a GetScopeChainFast call here, along with JS_REQUIRES_STACK
* annotations on this function's prototype and definition.
*/ */
JSObject *scopeChain = GetScopeChainFast(cx, cx->fp(), op, oplen); VOUCH_DOES_NOT_REQUIRE_STACK();
if (!scopeChain) JSObject *scopeChain = &cx->fp()->scopeChain();
return NULL;
JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain); JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
if (!closure || !fun->script()->bindings.hasUpvars()) if (!closure || !fun->script()->bindings.hasUpvars())

View File

@ -467,7 +467,7 @@ CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
extern JSObject * JS_FASTCALL extern JSObject * JS_FASTCALL
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain); js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
extern JS_REQUIRES_STACK JSObject * extern JSObject *
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen); js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen);
extern JS_REQUIRES_STACK JSObject * extern JS_REQUIRES_STACK JSObject *
@ -526,7 +526,7 @@ extern JSBool
GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool extern JSBool
GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool extern JSBool
SetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); SetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
@ -535,7 +535,7 @@ extern JSBool
SetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); SetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool extern JSBool
SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
} // namespace js } // namespace js

View File

@ -966,12 +966,12 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
initialVarObj = (cx->options & JSOPTION_VAROBJFIX) ? chain->getGlobal() : chain; initialVarObj = (cx->options & JSOPTION_VAROBJFIX) ? chain->getGlobal() : chain;
} }
#if 0 /* to be reenabled shortly when this works */
/* /*
* Strict mode eval code receives its own, fresh lexical environment; thus * Strict mode eval code receives its own, fresh lexical environment; thus
* strict mode eval can't mutate its calling frame's binding set. * strict mode eval can't mutate its calling frame's binding set.
*/ */
if (script->strictModeCode) { if ((flags & JSFRAME_EVAL) && script->strictModeCode) {
AutoScriptRooter root(cx, script);
initialVarObj = NewCallObject(cx, &script->bindings, *initialVarObj, NULL); initialVarObj = NewCallObject(cx, &script->bindings, *initialVarObj, NULL);
if (!initialVarObj) if (!initialVarObj)
return false; return false;
@ -982,7 +982,6 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
frame.fp()->clearCallObj(); frame.fp()->clearCallObj();
frame.fp()->setScopeChainAndCallObj(*initialVarObj); frame.fp()->setScopeChainAndCallObj(*initialVarObj);
} }
#endif
JS_ASSERT(!initialVarObj->getOps()->defineProperty); JS_ASSERT(!initialVarObj->getOps()->defineProperty);
#if JS_HAS_SHARP_VARS #if JS_HAS_SHARP_VARS
@ -2132,9 +2131,8 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
* same way as non-call bytecodes. * same way as non-call bytecodes.
*/ */
JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH); JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH); JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETFCSLOT_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH); JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETUPVAR_LENGTH);
JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_LENGTH); JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_LENGTH);
JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH); JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH); JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
@ -5290,22 +5288,6 @@ BEGIN_CASE(JSOP_SETLOCAL)
} }
END_SET_CASE(JSOP_SETLOCAL) END_SET_CASE(JSOP_SETLOCAL)
BEGIN_CASE(JSOP_GETUPVAR)
BEGIN_CASE(JSOP_CALLUPVAR)
{
JSUpvarArray *uva = script->upvars();
uintN index = GET_UINT16(regs.pc);
JS_ASSERT(index < uva->length);
const Value &rval = GetUpvar(cx, script->staticLevel, uva->vector[index]);
PUSH_COPY(rval);
if (op == JSOP_CALLUPVAR)
PUSH_UNDEFINED();
}
END_CASE(JSOP_GETUPVAR)
BEGIN_CASE(JSOP_GETUPVAR_DBG) BEGIN_CASE(JSOP_GETUPVAR_DBG)
BEGIN_CASE(JSOP_CALLUPVAR_DBG) BEGIN_CASE(JSOP_CALLUPVAR_DBG)
{ {

View File

@ -722,13 +722,31 @@ ScriptEpilogue(JSContext *cx, JSStackFrame *fp, JSBool ok)
if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData())) if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData()))
hook(cx, fp, JS_FALSE, &ok, hookData); hook(cx, fp, JS_FALSE, &ok, hookData);
/* if (fp->isEvalFrame()) {
* An eval frame's parent owns its activation objects. A yielding frame's /*
* activation objects are transferred to the floating frame, stored in the * The parent (ancestor for nested eval) of a non-strict eval frame
* generator. * owns its activation objects. Strict mode eval frames own their own
*/ * Call objects but never have an arguments object (the first non-eval
if (fp->isFunctionFrame() && !fp->isEvalFrame() && !fp->isYielding()) * parent frame has it).
PutActivationObjects(cx, fp); */
if (fp->script()->strictModeCode) {
JS_ASSERT(!fp->isYielding());
JS_ASSERT(!fp->hasArgsObj());
JS_ASSERT(fp->hasCallObj());
JS_ASSERT(fp->callObj().callIsForEval());
js_PutCallObject(cx, fp);
}
} else {
/*
* Otherwise only function frames have activation objects. A yielding
* frame's activation objects are transferred to the floating frame,
* stored in the generator, and thus need not be synced.
*/
if (fp->isFunctionFrame() && !fp->isYielding()) {
JS_ASSERT_IF(fp->hasCallObj(), !fp->callObj().callIsForEval());
PutActivationObjects(cx, fp);
}
}
/* /*
* If inline-constructing, replace primitive rval with the new object * If inline-constructing, replace primitive rval with the new object

View File

@ -897,6 +897,9 @@ struct JSObject : js::gc::Cell {
/* Number of reserved slots. */ /* Number of reserved slots. */
static const uint32 CALL_RESERVED_SLOTS = 2; static const uint32 CALL_RESERVED_SLOTS = 2;
/* True if this is for a strict mode eval frame or for a function call. */
inline bool callIsForEval() const;
/* The stack frame for this Call object, if the frame is still active. */ /* The stack frame for this Call object, if the frame is still active. */
inline JSStackFrame *maybeCallObjStackFrame() const; inline JSStackFrame *maybeCallObjStackFrame() const;

View File

@ -432,6 +432,16 @@ JSObject::setArgsElement(uint32 i, const js::Value &v)
getArgsData()->slots[i] = v; getArgsData()->slots[i] = v;
} }
inline bool
JSObject::callIsForEval() const
{
JS_ASSERT(isCall());
JS_ASSERT(getSlot(JSSLOT_CALL_CALLEE).isObjectOrNull());
JS_ASSERT_IF(getSlot(JSSLOT_CALL_CALLEE).isObject(),
getSlot(JSSLOT_CALL_CALLEE).toObject().isFunction());
return getSlot(JSSLOT_CALL_CALLEE).isNull();
}
inline JSStackFrame * inline JSStackFrame *
JSObject::maybeCallObjStackFrame() const JSObject::maybeCallObjStackFrame() const
{ {
@ -465,7 +475,7 @@ inline const js::Value &
JSObject::getCallObjArguments() const JSObject::getCallObjArguments() const
{ {
JS_ASSERT(isCall()); JS_ASSERT(isCall());
JS_ASSERT(getCallObjCallee() != NULL); JS_ASSERT(!callIsForEval());
return getSlot(JSSLOT_CALL_ARGUMENTS); return getSlot(JSSLOT_CALL_ARGUMENTS);
} }
@ -473,7 +483,7 @@ inline void
JSObject::setCallObjArguments(const js::Value &v) JSObject::setCallObjArguments(const js::Value &v)
{ {
JS_ASSERT(isCall()); JS_ASSERT(isCall());
JS_ASSERT(getCallObjCallee() != NULL); JS_ASSERT(!callIsForEval());
setSlot(JSSLOT_CALL_ARGUMENTS, v); setSlot(JSSLOT_CALL_ARGUMENTS, v);
} }

View File

@ -2883,8 +2883,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break; break;
} }
case JSOP_GETUPVAR:
case JSOP_CALLUPVAR:
case JSOP_GETUPVAR_DBG: case JSOP_GETUPVAR_DBG:
case JSOP_CALLUPVAR_DBG: case JSOP_CALLUPVAR_DBG:
case JSOP_GETFCSLOT: case JSOP_GETFCSLOT:

View File

@ -443,15 +443,19 @@ OPDEF(JSOP_NOTRACE, 182,"notrace", NULL, 3, 0, 0, 0, JOF_UINT16
OPDEF(JSOP_XMLCDATA, 183,"xmlcdata", NULL, 3, 0, 1, 19, JOF_ATOM) OPDEF(JSOP_XMLCDATA, 183,"xmlcdata", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLCOMMENT, 184,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM) OPDEF(JSOP_XMLCOMMENT, 184,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLPI, 185,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM) OPDEF(JSOP_XMLPI, 185,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM)
OPDEF(JSOP_CALLPROP, 186,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP|JOF_TMPSLOT3) OPDEF(JSOP_DELDESC, 186,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
OPDEF(JSOP_CALLPROP, 187,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP|JOF_TMPSLOT3)
/* /*
* Get a display (free) variable from the closure's reserved slots. * These opcodes contain a reference to the current blockChain object.
* They are emitted directly after instructions, such as DEFFUN, that need fast access to
* the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT
* does not permit NULL object references, since it stores an index into a table of
* objects.
*/ */
OPDEF(JSOP_GETUPVAR, 187,"getupvar", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME) OPDEF(JSOP_BLOCKCHAIN, 188,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
OPDEF(JSOP_CALLUPVAR, 188,"callupvar", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP) OPDEF(JSOP_NULLBLOCKCHAIN,189,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_DELDESC, 189,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
/* /*
* Opcode to hold 24-bit immediate integer operands. * Opcode to hold 24-bit immediate integer operands.
@ -586,7 +590,7 @@ OPDEF(JSOP_OBJTOP, 228,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16
OPDEF(JSOP_TRACE, 229, "trace", NULL, 3, 0, 0, 0, JOF_UINT16) OPDEF(JSOP_TRACE, 229, "trace", NULL, 3, 0, 0, 0, JOF_UINT16)
/* /*
* Debugger versions of JSOP_{GET,CALL}UPVAR and the flat closure (_FC) ops. * Debugger versions of the flat closure (_FC) ops.
*/ */
OPDEF(JSOP_GETUPVAR_DBG, 230,"getupvar_dbg", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME) OPDEF(JSOP_GETUPVAR_DBG, 230,"getupvar_dbg", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLUPVAR_DBG, 231,"callupvar_dbg", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP) OPDEF(JSOP_CALLUPVAR_DBG, 231,"callupvar_dbg", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
@ -614,15 +618,5 @@ OPDEF(JSOP_GLOBALDEC, 245,"globaldec", NULL, 3, 0, 1, 15, JOF_GLOBAL
OPDEF(JSOP_CALLGLOBAL, 246,"callglobal", NULL, 3, 0, 2, 19, JOF_GLOBAL|JOF_NAME|JOF_CALLOP) OPDEF(JSOP_CALLGLOBAL, 246,"callglobal", NULL, 3, 0, 2, 19, JOF_GLOBAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_FORGLOBAL, 247,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL|JOF_NAME|JOF_FOR|JOF_TMPSLOT) OPDEF(JSOP_FORGLOBAL, 247,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL|JOF_NAME|JOF_FOR|JOF_TMPSLOT)
/*
* These opcodes contain a reference to the current blockChain object.
* They are emitted directly after instructions, such as DEFFUN, that need fast access to
* the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT
* does not permit NULL object references, since it stores an index into a table of
* objects.
*/
OPDEF(JSOP_BLOCKCHAIN, 248,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
OPDEF(JSOP_NULLBLOCKCHAIN,249,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
/* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */ /* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */
OPDEF(JSOP_FUNCALL, 250,"funcall", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE) OPDEF(JSOP_FUNCALL, 248,"funcall", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)

View File

@ -1909,8 +1909,8 @@ Parser::analyzeFunctions(JSFunctionBox *funbox, uint32& tcflags)
* but without this extra marking phase, function g will not be marked as a * but without this extra marking phase, function g will not be marked as a
* funarg since it is called from within its parent scope. But g reaches up to * funarg since it is called from within its parent scope. But g reaches up to
* f's parameter p, so if o_m escapes f's activation scope, g does too and * f's parameter p, so if o_m escapes f's activation scope, g does too and
* cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes * cannot assume that p's stack slot is still alive. In contast function h
* nor uses an upvar "above" o_m's level. * neither escapes nor uses an upvar "above" o_m's level.
* *
* If function g itself contained lambdas that contained non-lambdas that reach * If function g itself contained lambdas that contained non-lambdas that reach
* up above its level, then those non-lambdas would have to be marked too. This * up above its level, then those non-lambdas would have to be marked too. This
@ -2050,9 +2050,8 @@ Parser::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
PN_OP(lexdep) == JSOP_CALLEE)) { PN_OP(lexdep) == JSOP_CALLEE)) {
/* /*
* Mark this formerly-Algol-like function as an escaping * Mark this formerly-Algol-like function as an escaping
* function (i.e., as a funarg), because it is used from a * function (i.e., as a funarg), because it is used from
* funarg and therefore can not use JSOP_{GET,CALL}UPVAR to * another funarg.
* access upvars.
* *
* Progress is guaranteed because we set the funarg flag * Progress is guaranteed because we set the funarg flag
* here, which suppresses revisiting this function (thanks * here, which suppresses revisiting this function (thanks
@ -2372,26 +2371,8 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
if (!fn->isFunArg()) { if (!fn->isFunArg()) {
/* /*
* This function is Algol-like, it never escapes. So long as it * This function is Algol-like, it never escapes.
* does not assign to outer variables, it needs only an upvars
* array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
* bytecode to reach up the frame stack at runtime based on
* those upvars' cookies.
* *
* Any assignments to upvars from functions called by this one
* will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
* which load from stack homes when interpreting or from native
* stack slots when executing a trace.
*
* We could add JSOP_SETUPVAR, etc., but it is uncommon for a
* nested function to assign to an outer lexical variable, so
* we defer adding yet more code footprint in the absence of
* evidence motivating these opcodes.
*/
bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
uintN nupvars = 0;
/*
* Check that at least one outer lexical binding was assigned * Check that at least one outer lexical binding was assigned
* to (global variables don't count). This is conservative: we * to (global variables don't count). This is conservative: we
* could limit assignments to those in the current function, * could limit assignments to those in the current function,
@ -2404,30 +2385,13 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
if (!lexdep->isFreeVar()) { if (!lexdep->isFreeVar()) {
JS_ASSERT(lexdep->frameLevel() <= funbox->level); JS_ASSERT(lexdep->frameLevel() <= funbox->level);
++nupvars; break;
if (lexdep->isAssigned())
break;
} }
} }
if (!ale)
mutation = false;
if (nupvars == 0) { if (!ale) {
FUN_METER(onlyfreevar); FUN_METER(onlyfreevar);
FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE); FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
} else if (!mutation &&
!(funbox->tcflags & (TCF_FUN_IS_GENERATOR | TCF_FUN_ENTRAINS_SCOPES))) {
/*
* Algol-like functions can read upvars using the dynamic
* link (cx->fp/fp->down), optimized using the cx->display
* lookup table indexed by static level. They do not need
* to entrain and search their environment objects.
*/
FUN_METER(display);
FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
} else {
if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
FUN_METER(setupvar);
} }
} else { } else {
uintN nupvars = 0, nflattened = 0; uintN nupvars = 0, nflattened = 0;
@ -2902,8 +2866,8 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
pn->pn_cookie.makeFree(); pn->pn_cookie.makeFree();
/* /*
* If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function * If a lambda, mark this function as escaping (as a "funarg") unless it is
* is immediately applied (we clear PND_FUNARG if so -- see memberExpr). * immediately applied (we clear PND_FUNARG if so -- see memberExpr).
* *
* Treat function sub-statements (non-lambda, non-body-level functions) as * Treat function sub-statements (non-lambda, non-body-level functions) as
* escaping funargs, since we can't statically analyze their definitions * escaping funargs, since we can't statically analyze their definitions
@ -3244,6 +3208,13 @@ Parser::functionStmt()
} }
tokenStream.ungetToken(); tokenStream.ungetToken();
} }
/* We forbid function statements in strict mode code. */
if (!tc->atBodyLevel() && tc->inStrictMode()) {
reportErrorNumber(NULL, JSREPORT_STRICT_MODE_ERROR, JSMSG_STRICT_FUNCTION_STATEMENT);
return NULL;
}
return functionDef(name, GENERAL, 0); return functionDef(name, GENERAL, 0);
} }

View File

@ -832,12 +832,8 @@ struct LexicalScopeNode : public JSParseNode {
* and because all uses are contained in the same block as the definition. * and because all uses are contained in the same block as the definition.
* *
* We also analyze function uses to flag upward/downward funargs, optimizing * We also analyze function uses to flag upward/downward funargs, optimizing
* Algol-like (not passed as funargs, only ever called) lightweight functions * those lambdas that post-dominate their upvars inevitable only assignments or
* using cx->display. See JSOP_{GET,CALL}UPVAR. * initializations as flat closures (after Chez Scheme's display closures).
*
* This means that closure optimizations may be frustrated by with, eval, or
* assignment to an outer var. Such hard cases require heavyweight functions
* and JSOP_NAME, etc.
*/ */
#define dn_uses pn_link #define dn_uses pn_link

View File

@ -58,6 +58,8 @@
#include "jsobjinlines.h" #include "jsobjinlines.h"
#include "jsregexpinlines.h" #include "jsregexpinlines.h"
#include "yarr/RegexParser.h"
#ifdef JS_TRACER #ifdef JS_TRACER
#include "jstracer.h" #include "jstracer.h"
using namespace avmplus; using namespace avmplus;
@ -184,26 +186,12 @@ js_ObjectIsRegExp(JSObject *obj)
void void
RegExp::handleYarrError(JSContext *cx, int error) RegExp::handleYarrError(JSContext *cx, int error)
{ {
/* Hack: duplicated from yarr/yarr/RegexParser.h */
enum ErrorCode {
NoError,
PatternTooLarge,
QuantifierOutOfOrder,
QuantifierWithoutAtom,
MissingParentheses,
ParenthesesUnmatched,
ParenthesesTypeInvalid, /* "(?" with bad next char or end of pattern. */
CharacterClassUnmatched,
CharacterClassOutOfOrder,
QuantifierTooLarge,
EscapeUnterminated
};
switch (error) { switch (error) {
case NoError: case JSC::Yarr::NoError:
JS_NOT_REACHED("Precondition violation: an error must have occurred."); JS_NOT_REACHED("Precondition violation: an error must have occurred.");
return; return;
#define COMPILE_EMSG(__code, __msg) \ #define COMPILE_EMSG(__code, __msg) \
case __code: \ case JSC::Yarr::__code: \
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \ JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
return return
COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX); COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX);
@ -211,9 +199,10 @@ RegExp::handleYarrError(JSContext *cx, int error)
COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER); COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER);
COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN); COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN);
COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN); COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN);
COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); /* "(?" with bad next char */
COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE); COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE);
COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE); COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE);
COMPILE_EMSG(CharacterClassRangeSingleChar, JSMSG_BAD_CLASS_RANGE);
COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH); COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH);
COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER); COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER);
#undef COMPILE_EMSG #undef COMPILE_EMSG

View File

@ -485,10 +485,10 @@ Shape::getChild(JSContext *cx, const js::Shape &child, Shape **listp)
/* /*
* Beware duplicate formal parameters, allowed by ECMA-262 in * Beware duplicate formal parameters, allowed by ECMA-262 in
* non-strict mode. Otherwise we know that JSFunction::addLocal * non-strict mode. Otherwise we know that Bindings::add (our
* (our caller) won't pass an id already in the table to us. In * caller) won't pass an id already in the table to us. In the
* the case of duplicate formals, the last one wins, so while * case of duplicate formals, the last one wins, so while we
* we must not overcount entries, we must store newShape. * must not overcount entries, we must store newShape.
*/ */
if (!SHAPE_FETCH(spp)) if (!SHAPE_FETCH(spp))
++table->entryCount; ++table->entryCount;

View File

@ -91,7 +91,7 @@ Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const
if (shape->getter() == GetCallArg) if (shape->getter() == GetCallArg)
return ARGUMENT; return ARGUMENT;
if (shape->getter() == GetFlatUpvar) if (shape->getter() == GetCallUpvar)
return UPVAR; return UPVAR;
return shape->writable() ? VARIABLE : CONSTANT; return shape->writable() ? VARIABLE : CONSTANT;
@ -122,8 +122,8 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
slot += nargs; slot += nargs;
} else if (kind == UPVAR) { } else if (kind == UPVAR) {
indexp = &nupvars; indexp = &nupvars;
getter = GetFlatUpvar; getter = GetCallUpvar;
setter = SetFlatUpvar; setter = SetCallUpvar;
slot = SHAPE_INVALID_SLOT; slot = SHAPE_INVALID_SLOT;
} else { } else {
JS_ASSERT(kind == VARIABLE || kind == CONSTANT); JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
@ -218,7 +218,7 @@ Bindings::getLocalNameArray(JSContext *cx, JSArenaPool *pool)
if (shape.getter() == GetCallArg) { if (shape.getter() == GetCallArg) {
JS_ASSERT(index < nargs); JS_ASSERT(index < nargs);
} else if (shape.getter() == GetFlatUpvar) { } else if (shape.getter() == GetCallUpvar) {
JS_ASSERT(index < nupvars); JS_ASSERT(index < nupvars);
index += nargs + nvars; index += nargs + nvars;
} else { } else {
@ -267,7 +267,7 @@ Bindings::lastVariable() const
const js::Shape *shape = lastUpvar(); const js::Shape *shape = lastUpvar();
if (nupvars > 0) { if (nupvars > 0) {
while (shape->getter() == GetFlatUpvar) while (shape->getter() == GetCallUpvar)
shape = shape->previous(); shape = shape->previous();
} }
return shape; return shape;

View File

@ -73,7 +73,7 @@ namespace js {
* *
* TODO: consider giving more bits to the slot value and takings ome from the level. * TODO: consider giving more bits to the slot value and takings ome from the level.
*/ */
class UpvarCookie class UpvarCookie
{ {
uint32 value; uint32 value;
@ -206,11 +206,11 @@ class Bindings {
inline const js::Shape *lastShape() const; inline const js::Shape *lastShape() const;
enum { enum {
/* /*
* A script may have no more than this many arguments, variables, or * A script may have no more than this many arguments, variables, or
* upvars. * upvars.
*/ */
BINDING_COUNT_LIMIT = 0xFFFF BINDING_COUNT_LIMIT = 0xFFFF
}; };
/* /*
@ -224,11 +224,10 @@ class Bindings {
* which must deal with such cases.) Pass null for name when indicating a * which must deal with such cases.) Pass null for name when indicating a
* destructuring argument. Return true on success. * destructuring argument. Return true on success.
* *
*
* The parser builds shape paths for functions, usable by Call objects at * The parser builds shape paths for functions, usable by Call objects at
* runtime, by calling addLocal. All ARGUMENT bindings must be added before * runtime, by calling an "add" method. All ARGUMENT bindings must be added
* before any VARIABLE or CONSTANT bindings, which themselves must be added * before before any VARIABLE or CONSTANT bindings, which themselves must
* before all UPVAR bindings. * be added before all UPVAR bindings.
*/ */
bool add(JSContext *cx, JSAtom *name, BindingKind kind); bool add(JSContext *cx, JSAtom *name, BindingKind kind);
@ -431,7 +430,7 @@ struct JSScript {
* JS_CompileFile, etc.) have these objects. * JS_CompileFile, etc.) have these objects.
* - Function scripts never have script objects; such scripts are owned * - Function scripts never have script objects; such scripts are owned
* by their function objects. * by their function objects.
* - Temporary scripts created by obj_eval, JS_EvaluateScript, and * - Temporary scripts created by obj_eval, JS_EvaluateScript, and
* similar functions never have these objects; such scripts are * similar functions never have these objects; such scripts are
* explicitly destroyed by the code that created them. * explicitly destroyed by the code that created them.
* Debugging API functions (JSDebugHooks::newScriptHook; * Debugging API functions (JSDebugHooks::newScriptHook;

View File

@ -300,7 +300,6 @@ TypeToChar(JSValueType type)
case JSVAL_TYPE_BOXED: return '#'; case JSVAL_TYPE_BOXED: return '#';
case JSVAL_TYPE_STRORNULL: return 's'; case JSVAL_TYPE_STRORNULL: return 's';
case JSVAL_TYPE_OBJORNULL: return 'o'; case JSVAL_TYPE_OBJORNULL: return 'o';
case JSVAL_TYPE_UNINITIALIZED: return '*';
} }
return '?'; return '?';
} }
@ -3660,7 +3659,6 @@ TraceRecorder::importGlobalSlot(unsigned slot)
JS_ASSERT(tree->nGlobalTypes() == tree->globalSlots->length()); JS_ASSERT(tree->nGlobalTypes() == tree->globalSlots->length());
} else { } else {
type = importTypeMap[importStackSlots + index]; type = importTypeMap[importStackSlots + index];
JS_ASSERT(type != JSVAL_TYPE_UNINITIALIZED);
} }
import(EosAddress(eos_ins, slot * sizeof(double)), vp, type, "global", index, NULL); import(EosAddress(eos_ins, slot * sizeof(double)), vp, type, "global", index, NULL);
} }
@ -3809,7 +3807,6 @@ TraceRecorder::getImpl(const void *p)
} else { } else {
unsigned slot = nativeStackSlotImpl(p); unsigned slot = nativeStackSlotImpl(p);
JSValueType type = importTypeMap[slot]; JSValueType type = importTypeMap[slot];
JS_ASSERT(type != JSVAL_TYPE_UNINITIALIZED);
importImpl(StackAddress(lirbuf->sp, -tree->nativeStackBase + slot * sizeof(jsdouble)), importImpl(StackAddress(lirbuf->sp, -tree->nativeStackBase + slot * sizeof(jsdouble)),
p, type, "stack", slot, cx->fp()); p, type, "stack", slot, cx->fp());
} }
@ -4033,7 +4030,6 @@ TraceRecorder::determineSlotType(Value* vp)
} else { } else {
t = importTypeMap[nativeStackSlot(vp)]; t = importTypeMap[nativeStackSlot(vp)];
} }
JS_ASSERT(t != JSVAL_TYPE_UNINITIALIZED);
JS_ASSERT_IF(t == JSVAL_TYPE_INT32, hasInt32Repr(*vp)); JS_ASSERT_IF(t == JSVAL_TYPE_INT32, hasInt32Repr(*vp));
return t; return t;
} }
@ -5194,6 +5190,28 @@ TraceRecorder::prepareTreeCall(TreeFragment* inner)
w.xbarrier(createGuardRecord(exit)); w.xbarrier(createGuardRecord(exit));
} }
class ClearSlotsVisitor : public SlotVisitorBase
{
Tracker &tracker;
public:
ClearSlotsVisitor(Tracker &tracker)
: tracker(tracker)
{}
JS_ALWAYS_INLINE bool
visitStackSlots(Value *vp, size_t count, JSStackFrame *) {
for (Value *vpend = vp + count; vp != vpend; ++vp)
tracker.set(vp, NULL);
return true;
}
JS_ALWAYS_INLINE bool
visitFrameObjPtr(void *p, JSStackFrame *) {
tracker.set(p, NULL);
return true;
}
};
static unsigned static unsigned
BuildGlobalTypeMapFromInnerTree(Queue<JSValueType>& typeMap, VMSideExit* inner) BuildGlobalTypeMapFromInnerTree(Queue<JSValueType>& typeMap, VMSideExit* inner)
{ {
@ -5275,32 +5293,9 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit)
JS_ASSERT(map[i] != JSVAL_TYPE_BOXED); JS_ASSERT(map[i] != JSVAL_TYPE_BOXED);
#endif #endif
/* /* The inner tree may modify currently-tracked upvars, so flush everything. */
* Clear anything from the tracker that the inner tree could have written so ClearSlotsVisitor visitor(tracker);
* that it will be lazily reloaded from the native stack. The only portion VisitStackSlots(visitor, cx, callDepth);
* of the native stack that may be written to by both the inner and outer
* tree are the slots associated with the inner tree's entry frame. We may
* be certain of this by the following argument:
*
* 0. The only way for the inner tree to mutate the outer tree's upvars
* is by writing to the call object associated with cx->fp or the
* callDepth frames below it.
* 1. To write to a given call object, the innerTree must be executing with
* that call object on its scope chain.
* 2. Only function frames' scope chains may contain call objects.
* 3. An interpreted function frame's scope chain is set to be the parent
* of the callee when the callee is called.
* 4. On trace, there is a guard on the identity of the callee's parent.
* 5. From 1, 2, 3 and 4, any call object written to on trace will be the
* exact same call object as was observed at record time.
* 6. Inner trees are recorded before outer trees.
* 7. From 5 and 6, the only call object created by an outer tree that can
* be written to by an inner tree before the call object's associated
* stack frame is popped is cx->fp->callObj.
* 8. setCallProp has an explicit guard against writing to cx->fp->callObj.
* 9. From 0, 7 and 8, an inner tree never mutates its outer tree's upvars.
*/
clearCurrentFrameSlotsFromTracker(tracker);
SlotList& gslots = *tree->globalSlots; SlotList& gslots = *tree->globalSlots;
for (unsigned i = 0; i < gslots.length(); i++) { for (unsigned i = 0; i < gslots.length(); i++) {
unsigned slot = gslots[i]; unsigned slot = gslots[i];
@ -5310,10 +5305,6 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit)
/* Set stack slots from the innermost frame. */ /* Set stack slots from the innermost frame. */
importTypeMap.setLength(NativeStackSlots(cx, callDepth)); importTypeMap.setLength(NativeStackSlots(cx, callDepth));
#ifdef DEBUG
for (unsigned i = importStackSlots; i < importTypeMap.length(); i++)
importTypeMap[i] = JSVAL_TYPE_UNINITIALIZED;
#endif
unsigned startOfInnerFrame = importTypeMap.length() - exit->numStackSlots; unsigned startOfInnerFrame = importTypeMap.length() - exit->numStackSlots;
for (unsigned i = 0; i < exit->numStackSlots; i++) for (unsigned i = 0; i < exit->numStackSlots; i++)
importTypeMap[startOfInnerFrame + i] = exit->stackTypeMap()[i]; importTypeMap[startOfInnerFrame + i] = exit->stackTypeMap()[i];
@ -10171,45 +10162,21 @@ TraceRecorder::guardNativeConversion(Value& v)
return RECORD_CONTINUE; return RECORD_CONTINUE;
} }
/*
* Clear out all slots of this frame in the nativeFrameTracker. Different
* locations on the VM stack might map to different locations on the native
* stack depending on the number of arguments (i.e.) of the next call, so we
* have to make sure we map those in to the cache with the right offsets.
*/
JS_REQUIRES_STACK void JS_REQUIRES_STACK void
TraceRecorder::clearCurrentFrameSlotsFromTracker(Tracker& which) TraceRecorder::clearReturningFrameFromNativeveTracker()
{ {
JSStackFrame *const fp = cx->fp();
/* /*
* Duplicate native stack layout computation: see VisitFrameSlots header comment. * Clear all tracker entries associated with the frame for the same reason
* This doesn't do layout arithmetic, but it must clear out all the slots defined as * described in record_EnterFrame. Reuse the generic visitor to avoid
* imported by VisitFrameSlots. * duplicating logic. The generic visitor stops at 'sp', whereas we need to
* clear up to script->nslots, so finish the job manually.
*/ */
if (fp->isGlobalFrame()) { ClearSlotsVisitor visitor(nativeFrameTracker);
Value *vp = fp->slots() + fp->globalScript()->nfixed; VisitStackSlots(visitor, cx, 0);
Value *vpend = fp->slots() + fp->globalScript()->nslots; Value *vp = cx->regs->sp;
for (; vp < vpend; ++vp) Value *vpend = cx->fp()->slots() + cx->fp()->script()->nslots;
which.set(vp, (LIns*)0);
return;
}
if (!fp->isEvalFrame()) {
/* For simplicitly, flush 'em all, even non-canonical arg slots. */
Value *vp = fp->actualArgs() - 2 /* callee, this */;
Value *vpend = fp->formalArgsEnd();
for (; vp < vpend; ++vp)
which.set(vp, (LIns*)0);
}
which.set(fp->addressOfArgs(), (LIns*)0);
which.set(fp->addressOfScopeChain(), (LIns*)0);
Value *vp = fp->slots();
Value *vpend = fp->slots() + fp->functionScript()->nslots;
for (; vp < vpend; ++vp) for (; vp < vpend; ++vp)
which.set(vp, (LIns*)0); nativeFrameTracker.set(vp, NULL);
} }
class BoxArg class BoxArg
@ -10502,7 +10469,7 @@ TraceRecorder::record_JSOP_RETURN()
fp->fun()->atom ? fp->fun()->atom ?
js_AtomToPrintableString(cx, fp->fun()->atom, &funBytes) : js_AtomToPrintableString(cx, fp->fun()->atom, &funBytes) :
"<anonymous>"); "<anonymous>");
clearCurrentFrameSlotsFromTracker(nativeFrameTracker); clearReturningFrameFromNativeveTracker();
return ARECORD_CONTINUE; return ARECORD_CONTINUE;
} }
@ -13289,30 +13256,6 @@ TraceRecorder::stackLoad(Address addr, uint8 type)
} }
} }
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_GETUPVAR()
{
uintN index = GET_UINT16(cx->regs->pc);
JSScript *script = cx->fp()->script();
JSUpvarArray* uva = script->upvars();
JS_ASSERT(index < uva->length);
Value v;
LIns* upvar_ins = upvar(script, uva, index, v);
if (!upvar_ins)
return ARECORD_STOP;
stack(0, upvar_ins);
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_CALLUPVAR()
{
CHECK_STATUS_A(record_JSOP_GETUPVAR());
stack(1, w.immiUndefined());
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_GETFCSLOT() TraceRecorder::record_JSOP_GETFCSLOT()
{ {
@ -15885,7 +15828,7 @@ TraceRecorder::record_JSOP_STOP()
} else { } else {
rval_ins = w.immiUndefined(); rval_ins = w.immiUndefined();
} }
clearCurrentFrameSlotsFromTracker(nativeFrameTracker); clearReturningFrameFromNativeveTracker();
return ARECORD_CONTINUE; return ARECORD_CONTINUE;
} }

View File

@ -1472,7 +1472,7 @@ class TraceRecorder
nanojit::LIns* obj_ins, nanojit::LIns* obj_ins,
VMSideExit *exit); VMSideExit *exit);
JS_REQUIRES_STACK RecordingStatus guardNativeConversion(Value& v); JS_REQUIRES_STACK RecordingStatus guardNativeConversion(Value& v);
JS_REQUIRES_STACK void clearCurrentFrameSlotsFromTracker(Tracker& which); JS_REQUIRES_STACK void clearReturningFrameFromNativeveTracker();
JS_REQUIRES_STACK void putActivationObjects(); JS_REQUIRES_STACK void putActivationObjects();
JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee); JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee);
JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins, JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins,
@ -1594,6 +1594,7 @@ class TraceRecorder
TreeFragment* getTree() const { return tree; } TreeFragment* getTree() const { return tree; }
bool outOfMemory() const { return traceMonitor->outOfMemory(); } bool outOfMemory() const { return traceMonitor->outOfMemory(); }
Oracle* getOracle() const { return oracle; } Oracle* getOracle() const { return oracle; }
JSObject* getGlobal() const { return globalObj; }
/* Entry points / callbacks from the interpreter. */ /* Entry points / callbacks from the interpreter. */
JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op); JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op);
@ -1612,7 +1613,20 @@ class TraceRecorder
* Do slot arithmetic manually to avoid getSlotRef assertions which * Do slot arithmetic manually to avoid getSlotRef assertions which
* do not need to be satisfied for this purpose. * do not need to be satisfied for this purpose.
*/ */
return !tracker.has(globalObj->getSlots() + slot); Value *vp = globalObj->getSlots() + slot;
/* If this global is definitely being tracked, then the write is unexpected. */
if (tracker.has(vp))
return false;
/*
* Otherwise, only abort if the global is not present in the
* import typemap. Just deep aborting false here is not acceptable,
* because the recorder does not guard on every operation that
* could lazily resolve. Since resolving adds properties to
* reserved slots, the tracer will never have imported them.
*/
return tree->globalSlots->offsetOf(nativeGlobalSlot(vp)) == -1;
} }
pendingGlobalSlotToSet = -1; pendingGlobalSlotToSet = -1;
return true; return true;
@ -1868,7 +1882,7 @@ AbortRecordingIfUnexpectedGlobalWrite(JSContext *cx, JSObject *obj, unsigned slo
{ {
#ifdef JS_TRACER #ifdef JS_TRACER
if (TraceRecorder *tr = TRACE_RECORDER(cx)) { if (TraceRecorder *tr = TRACE_RECORDER(cx)) {
if (!obj->parent && !tr->globalSetExpected(slot)) if (obj == tr->getGlobal() && !tr->globalSetExpected(slot))
AbortRecording(cx, "Global slot written outside tracer supervision"); AbortRecording(cx, "Global slot written outside tracer supervision");
} }
#endif #endif

View File

@ -108,8 +108,7 @@ JS_ENUM_HEADER(JSValueType, uint8)
JSVAL_TYPE_STRORNULL = 0x97, JSVAL_TYPE_STRORNULL = 0x97,
JSVAL_TYPE_OBJORNULL = 0x98, JSVAL_TYPE_OBJORNULL = 0x98,
JSVAL_TYPE_BOXED = 0x99, JSVAL_TYPE_BOXED = 0x99
JSVAL_TYPE_UNINITIALIZED = 0xcd
} JS_ENUM_FOOTER(JSValueType); } JS_ENUM_FOOTER(JSValueType);
JS_STATIC_ASSERT(sizeof(JSValueType) == 1); JS_STATIC_ASSERT(sizeof(JSValueType) == 1);

View File

@ -348,19 +348,15 @@ AutoCompartment::enter()
if (origin != destination) { if (origin != destination) {
LeaveTrace(context); LeaveTrace(context);
#ifdef DEBUG if (context->isExceptionPending())
JSCompartment *oldCompartment = context->compartment; return false;
context->resetCompartment();
wasSane = (context->compartment == oldCompartment);
#endif
context->compartment = destination; context->compartment = destination;
JSObject *scopeChain = target->getGlobal(); JSObject *scopeChain = target->getGlobal();
JS_ASSERT(scopeChain->isNative()); JS_ASSERT(scopeChain->isNative());
frame.construct(); frame.construct();
if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref()) || if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref())) {
!destination->wrapException(context)) {
frame.destroy();
context->compartment = origin; context->compartment = origin;
return false; return false;
} }
@ -376,8 +372,6 @@ AutoCompartment::leave()
if (origin != destination) { if (origin != destination) {
frame.destroy(); frame.destroy();
context->resetCompartment(); context->resetCompartment();
JS_ASSERT_IF(wasSane && context->hasfp(), context->compartment == origin);
context->compartment->wrapException(context);
} }
entered = false; entered = false;
} }
@ -642,8 +636,7 @@ JSCrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, uintN arg
return false; return false;
call.leave(); call.leave();
return call.origin->wrap(cx, rval) && return call.origin->wrap(cx, rval);
call.origin->wrapException(cx);
} }
bool bool

View File

@ -168,9 +168,6 @@ class AutoCompartment
JSFrameRegs regs; JSFrameRegs regs;
AutoStringRooter input; AutoStringRooter input;
bool entered; bool entered;
#ifdef DEBUG
bool wasSane;
#endif
public: public:
AutoCompartment(JSContext *cx, JSObject *target); AutoCompartment(JSContext *cx, JSObject *target);

View File

@ -206,7 +206,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match * before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file. * the current version, abort deserialization and invalidate the file.
*/ */
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 80) #define JSXDR_BYTECODE_VERSION (0xb973c0de - 81)
/* /*
* Library-private functions. * Library-private functions.

View File

@ -1954,22 +1954,6 @@ mjit::Compiler::generateMethod()
return Compile_Error; return Compile_Error;
END_CASE(JSOP_CALLPROP) END_CASE(JSOP_CALLPROP)
BEGIN_CASE(JSOP_GETUPVAR)
BEGIN_CASE(JSOP_CALLUPVAR)
{
uint32 index = GET_UINT16(PC);
JSUpvarArray *uva = script->upvars();
JS_ASSERT(index < uva->length);
prepareStubCall(Uses(0));
masm.move(Imm32(uva->vector[index].asInteger()), Registers::ArgReg1);
INLINE_STUBCALL(stubs::GetUpvar);
frame.pushSynced();
if (op == JSOP_CALLUPVAR)
frame.push(UndefinedValue());
}
END_CASE(JSOP_CALLUPVAR)
BEGIN_CASE(JSOP_UINT24) BEGIN_CASE(JSOP_UINT24)
frame.push(Value(Int32Value((int32_t) GET_UINT24(PC)))); frame.push(Value(Int32Value((int32_t) GET_UINT24(PC))));
END_CASE(JSOP_UINT24) END_CASE(JSOP_UINT24)
@ -2336,6 +2320,12 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
emitReturnValue(&stubcc.masm, fe); emitReturnValue(&stubcc.masm, fe);
emitFinalReturn(stubcc.masm); emitFinalReturn(stubcc.masm);
} }
} else {
if (fp->isEvalFrame() && script->strictModeCode) {
/* There will always be a call object. */
prepareStubCall(Uses(fe ? 1 : 0));
INLINE_STUBCALL(stubs::PutStrictEvalCallObject);
}
} }
emitReturnValue(&masm, fe); emitReturnValue(&masm, fe);
@ -3294,9 +3284,14 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom)
return true; return true;
} }
/* Bake in String.prototype. Is this safe? */ /*
* Bake in String.prototype. This is safe because of compileAndGo.
* We must pass an explicit scope chain only because JSD calls into
* here via the recompiler with a dummy context, and we need to use
* the global object for the script we are now compiling.
*/
JSObject *obj; JSObject *obj;
if (!js_GetClassPrototype(cx, NULL, JSProto_String, &obj)) if (!js_GetClassPrototype(cx, &fp->scopeChain(), JSProto_String, &obj))
return false; return false;
/* Force into a register because getprop won't expect a constant. */ /* Force into a register because getprop won't expect a constant. */

View File

@ -923,7 +923,7 @@ mjit::Compiler::jsop_typeof()
if (op == JSOP_STRICTEQ || op == JSOP_EQ || op == JSOP_STRICTNE || op == JSOP_NE) { if (op == JSOP_STRICTEQ || op == JSOP_EQ || op == JSOP_STRICTNE || op == JSOP_NE) {
JSAtom *atom = script->getAtom(fullAtomIndex(PC + JSOP_TYPEOF_LENGTH)); JSAtom *atom = script->getAtom(fullAtomIndex(PC + JSOP_TYPEOF_LENGTH));
JSRuntime *rt = cx->runtime; JSRuntime *rt = cx->runtime;
JSValueType type = JSVAL_TYPE_UNINITIALIZED; JSValueType type = JSVAL_TYPE_BOXED;
Assembler::Condition cond = (op == JSOP_STRICTEQ || op == JSOP_EQ) Assembler::Condition cond = (op == JSOP_STRICTEQ || op == JSOP_EQ)
? Assembler::Equal ? Assembler::Equal
: Assembler::NotEqual; : Assembler::NotEqual;
@ -941,7 +941,7 @@ mjit::Compiler::jsop_typeof()
cond = (cond == Assembler::Equal) ? Assembler::BelowOrEqual : Assembler::Above; cond = (cond == Assembler::Equal) ? Assembler::BelowOrEqual : Assembler::Above;
} }
if (type != JSVAL_TYPE_UNINITIALIZED) { if (type != JSVAL_TYPE_BOXED) {
PC += JSOP_STRING_LENGTH;; PC += JSOP_STRING_LENGTH;;
PC += JSOP_EQ_LENGTH; PC += JSOP_EQ_LENGTH;

View File

@ -480,8 +480,10 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
} }
void JS_FASTCALL void JS_FASTCALL
stubs::PutCallObject(VMFrame &f) stubs::PutStrictEvalCallObject(VMFrame &f)
{ {
JS_ASSERT(f.fp()->isEvalFrame());
JS_ASSERT(f.fp()->script()->strictModeCode);
JS_ASSERT(f.fp()->hasCallObj()); JS_ASSERT(f.fp()->hasCallObj());
js_PutCallObject(f.cx, f.fp()); js_PutCallObject(f.cx, f.fp());
} }

View File

@ -905,8 +905,9 @@ class GetPropCompiler : public PICStubCompiler
Assembler masm; Assembler masm;
Jump notStringObj = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass()); Jump notStringObj = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
masm.loadPayload(Address(pic.objReg, JSObject::getFixedSlotOffset( masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
JSObject::JSSLOT_PRIMITIVE_THIS)), pic.objReg); masm.loadPayload(Address(pic.objReg, JSObject::JSSLOT_PRIMITIVE_THIS * sizeof(Value)),
pic.objReg);
masm.loadPtr(Address(pic.objReg, JSString::offsetOfLengthAndFlags()), pic.objReg); masm.loadPtr(Address(pic.objReg, JSString::offsetOfLengthAndFlags()), pic.objReg);
masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg); masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg);
masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);

View File

@ -107,7 +107,7 @@ void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr);
void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto); void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
void JS_FASTCALL Throw(VMFrame &f); void JS_FASTCALL Throw(VMFrame &f);
void JS_FASTCALL PutCallObject(VMFrame &f); void JS_FASTCALL PutStrictEvalCallObject(VMFrame &f);
void JS_FASTCALL PutActivationObjects(VMFrame &f); void JS_FASTCALL PutActivationObjects(VMFrame &f);
void JS_FASTCALL GetCallObject(VMFrame &f); void JS_FASTCALL GetCallObject(VMFrame &f);
#if JS_MONOIC #if JS_MONOIC

View File

@ -4246,59 +4246,6 @@ Deserialize(JSContext *cx, uintN argc, jsval *vp)
return true; return true;
} }
JSBool
SetGlobalPropIf(JSContext *cx, uintN argc, jsval *vp)
{
if (argc != 3) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "setGlobalPropIf");
return false;
}
jsval *argv = JS_ARGV(cx, vp);
JSBool doSet;
if (!JS_ValueToBoolean(cx, argv[0], &doSet))
return false;
JS_SET_RVAL(cx, vp, JSVAL_VOID);
if (!doSet)
return true;
jsid id;
if (!JS_ValueToId(cx, argv[1], &id))
return false;
JSObject *global = JS_GetGlobalForScopeChain(cx);
return global && JS_SetPropertyById(cx, global, id, &argv[2]);
}
JSBool
DefGlobalPropIf(JSContext *cx, uintN argc, jsval *vp)
{
if (argc != 3) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "setGlobalPropIf");
return false;
}
jsval *argv = JS_ARGV(cx, vp);
JSBool doSet;
if (!JS_ValueToBoolean(cx, argv[0], &doSet))
return false;
JS_SET_RVAL(cx, vp, JSVAL_VOID);
if (!doSet)
return true;
jsid id;
if (!JS_ValueToId(cx, argv[1], &id))
return false;
JSObject *global = JS_GetGlobalForScopeChain(cx);
JSBool ignore;
return global && JS_DefineOwnProperty(cx, global, id, argv[2], &ignore);
}
JSBool JSBool
MJitStats(JSContext *cx, uintN argc, jsval *vp) MJitStats(JSContext *cx, uintN argc, jsval *vp)
{ {
@ -4410,8 +4357,6 @@ static JSFunctionSpec shell_functions[] = {
JS_FN("wrap", Wrap, 1,0), JS_FN("wrap", Wrap, 1,0),
JS_FN("serialize", Serialize, 1,0), JS_FN("serialize", Serialize, 1,0),
JS_FN("deserialize", Deserialize, 1,0), JS_FN("deserialize", Deserialize, 1,0),
JS_FN("setGlobalPropIf",SetGlobalPropIf,3,0),
JS_FN("defGlobalPropIf",DefGlobalPropIf,3,0),
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
JS_FN("mjitstats", MJitStats, 0,0), JS_FN("mjitstats", MJitStats, 0,0),
#endif #endif
@ -4545,9 +4490,6 @@ static const char *const shell_help_messages[] = {
"wrap(obj) Wrap an object into a noop wrapper.\n", "wrap(obj) Wrap an object into a noop wrapper.\n",
"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n", "serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n",
"deserialize(a) Deserialize data generated by serialize.\n", "deserialize(a) Deserialize data generated by serialize.\n",
"setGlobalPropIf(b,id,v) If b, get the global object o and perform o[id] = v.\n",
"defGlobalPropIf(b,id,dsc)If b, get the global object o and perform\n"
" Object.defineProperty(o, id, dsc).\n",
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
"mjitstats() Return stats on mjit memory usage.\n", "mjitstats() Return stats on mjit memory usage.\n",
#endif #endif

View File

@ -16,22 +16,22 @@ url-prefix ../../jsreftest.html?test=ecma_5/eval/
# optimizations we might perform -- add new tests for such changes as needed. # optimizations we might perform -- add new tests for such changes as needed.
# #
# script exhaustive-fun-normalcaller-direct-normalcode.js script exhaustive-fun-normalcaller-direct-normalcode.js
# script exhaustive-fun-normalcaller-direct-strictcode.js script exhaustive-fun-normalcaller-direct-strictcode.js
# script exhaustive-fun-normalcaller-indirect-normalcode.js script exhaustive-fun-normalcaller-indirect-normalcode.js
# script exhaustive-fun-normalcaller-indirect-strictcode.js script exhaustive-fun-normalcaller-indirect-strictcode.js
# script exhaustive-fun-strictcaller-direct-normalcode.js script exhaustive-fun-strictcaller-direct-normalcode.js
# script exhaustive-fun-strictcaller-direct-strictcode.js script exhaustive-fun-strictcaller-direct-strictcode.js
# script exhaustive-fun-strictcaller-indirect-normalcode.js script exhaustive-fun-strictcaller-indirect-normalcode.js
# script exhaustive-fun-strictcaller-indirect-strictcode.js script exhaustive-fun-strictcaller-indirect-strictcode.js
# script exhaustive-global-normalcaller-direct-normalcode.js script exhaustive-global-normalcaller-direct-normalcode.js
# script exhaustive-global-normalcaller-direct-strictcode.js script exhaustive-global-normalcaller-direct-strictcode.js
# script exhaustive-global-normalcaller-indirect-normalcode.js script exhaustive-global-normalcaller-indirect-normalcode.js
# script exhaustive-global-normalcaller-indirect-strictcode.js script exhaustive-global-normalcaller-indirect-strictcode.js
# script exhaustive-global-strictcaller-direct-normalcode.js script exhaustive-global-strictcaller-direct-normalcode.js
# script exhaustive-global-strictcaller-direct-strictcode.js script exhaustive-global-strictcaller-direct-strictcode.js
# script exhaustive-global-strictcaller-indirect-normalcode.js script exhaustive-global-strictcaller-indirect-normalcode.js
# script exhaustive-global-strictcaller-indirect-strictcode.js script exhaustive-global-strictcaller-indirect-strictcode.js
# Not written yet! These require a new shell primitive to work there, though # Not written yet! These require a new shell primitive to work there, though
# browser could probably use setTimeout for this. Moreover, we haven't # browser could probably use setTimeout for this. Moreover, we haven't

View File

@ -21,3 +21,4 @@ script bug472534.js
script bug496985.js script bug496985.js
script bug566661.js script bug566661.js
script iterator-in-catch.js script iterator-in-catch.js
script strict-function-statements.js

View File

@ -1,37 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
/*
* Return true if both of these return true:
* - LENIENT_PRED applied to CODE
* - STRICT_PRED applied to CODE with a use strict directive added to the front
*
* Run STRICT_PRED first, for testing code that affects the global environment
* in loose mode, but fails in strict mode.
*/
function testLenientAndStrict(code, lenient_pred, strict_pred) {
return (strict_pred("'use strict'; " + code) &&
lenient_pred(code));
}
/*
* raisesException(EXCEPTION)(CODE) returns true if evaluating CODE (as eval
* code) throws an exception object whose prototype is
* EXCEPTION.prototype, and returns false if it throws any other error
* or evaluates successfully. For example: raises(TypeError)("0()") ==
* true.
*/
function raisesException(exception) {
return function (code) {
try {
eval(code);
return false;
} catch (actual) {
return exception.prototype.isPrototypeOf(actual);
}
};
};

View File

@ -0,0 +1,100 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
// Ordinary function definitions should be unaffected.
assertEq(testLenientAndStrict("function f() { }",
parsesSuccessfully,
parsesSuccessfully),
true);
// Function statements within blocks are forbidden in strict mode code.
assertEq(testLenientAndStrict("{ function f() { } }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
// Lambdas are always permitted within blocks.
assertEq(testLenientAndStrict("{ (function f() { }) }",
parsesSuccessfully,
parsesSuccessfully),
true);
// Function statements within any sort of statement are forbidden in strict mode code.
assertEq(testLenientAndStrict("if (true) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("while (true) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("do function f() { } while (true);",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("for(;;) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("for(x in []) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("with(o) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("switch(1) { case 1: function f() { } }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("x: function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("try { function f() { } } catch (x) { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
// Lambdas are always permitted within any sort of statement.
assertEq(testLenientAndStrict("if (true) (function f() { })",
parsesSuccessfully,
parsesSuccessfully),
true);
// Function statements are permitted in blocks within lenient functions.
assertEq(parsesSuccessfully("function f() { function g() { } }"),
true);
// Function statements are permitted in any statement within lenient functions.
assertEq(parsesSuccessfully("function f() { if (true) function g() { } }"),
true);
assertEq(parseRaisesException(SyntaxError)
("function f() { 'use strict'; if (true) function g() { } }"),
true);
assertEq(parseRaisesException(SyntaxError)
("function f() { 'use strict'; { function g() { } } }"),
true);
assertEq(parsesSuccessfully("function f() { 'use strict'; if (true) (function g() { }) }"),
true);
assertEq(parsesSuccessfully("function f() { 'use strict'; { (function g() { }) } }"),
true);
// Eval should behave the same way. (The parse-only tests use the Function constructor.)
assertEq(testLenientAndStrict("function f() { }",
completesNormally,
completesNormally),
true);
assertEq(testLenientAndStrict("{ function f() { } }",
completesNormally,
raisesException(SyntaxError)),
true);
reportCompare(true, true);

View File

@ -39,4 +39,4 @@ script unbrand-this.js
script this-for-function-expression-recursion.js script this-for-function-expression-recursion.js
script assign-to-callee-name.js script assign-to-callee-name.js
script directive-prologue-01.js script directive-prologue-01.js
# script eval-variable-environment.js script eval-variable-environment.js

View File

@ -65,7 +65,7 @@ function test()
(function (){ (function (){
var x; var x;
eval("const x; (function ()x)"); eval("var x; (function ()x)");
} }
)(); )();

View File

@ -78,7 +78,7 @@ function test()
function f() { function f() {
var x; var x;
(function(){})(); (function(){})();
eval("if(x|=[]) {const x; }"); eval("if(x|=[]) {var x; }");
} }
f(); f();

View File

@ -15,6 +15,8 @@ script regress-552432.js
script regress-553778.js script regress-553778.js
script regress-555246-0.js script regress-555246-0.js
script regress-555246-1.js script regress-555246-1.js
script regress-559402-1.js
script regress-559402-2.js
script regress-559438.js script regress-559438.js
script regress-560101.js script regress-560101.js
script regress-560998-1.js script regress-560998-1.js
@ -38,6 +40,10 @@ script regress-586482-4.js
script regress-586482-5.js script regress-586482-5.js
script regress-588339.js script regress-588339.js
script regress-yarr-regexp.js script regress-yarr-regexp.js
script regress-592202-1.js
script regress-592202-2.js
script regress-592202-3.js
script regress-592202-4.js
script regress-592217.js script regress-592217.js
script regress-592556-c35.js script regress-592556-c35.js
script regress-593256.js script regress-593256.js

View File

@ -0,0 +1,22 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var expect = "No error";
var actual = expect;
if (typeof options == "function") {
var opts = options();
if (!/\bstrict\b/.test(opts))
options("strict");
if (!/\bwerror\b/.test(opts))
options("werror");
}
try {
eval('function foo() { "use strict"; }');
} catch (e) {
actual = '' + e;
}
reportCompare(expect, actual, "ok");

View File

@ -0,0 +1,8 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var expect = undefined;
var actual = (function foo() { "bogus"; })();
reportCompare(expect, actual, "ok");

View File

@ -0,0 +1,7 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
i = 42
eval("let(y){(function(){let({}=y){(function(){let({}=y=[])(i)})()}})()}")
reportCompare(0, 0, "ok");

View File

@ -0,0 +1,15 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
eval("\
let(b)((\
function(){\
let(d=b)\
((function(){\
b=b\
})())\
}\
)())\
")
reportCompare(0, 0, "ok");

View File

@ -0,0 +1,28 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
test();
function test()
{
var counter = 0;
function f(x,y) {
try
{
throw 42;
}
catch(e2)
{
foo(function(){ return x; }| "9.2" && 5 || counter && e);
++counter;
}
}
f(2, 1);
}
function foo(bar) { return ""+bar; }
reportCompare(0, 0, "ok");

View File

@ -0,0 +1,31 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function p() { }
function test()
{
var counter = 0;
function f(x) {
try
{
throw 42;
}
catch(e)
{
assertEq(counter, 0);
p(function(){x;});
counter = 1;
}
}
f(2);
assertEq(counter, 1);
}
test();
reportCompare(0, 0, "ok");

View File

@ -144,6 +144,8 @@ GetExpandoObject(JSContext *cx, JSObject *holder)
JSObject *expando = holder->getSlot(JSSLOT_EXPANDO).toObjectOrNull(); JSObject *expando = holder->getSlot(JSSLOT_EXPANDO).toObjectOrNull();
if (!expando) { if (!expando) {
expando = JS_NewObjectWithGivenProto(cx, nsnull, nsnull, holder->getParent()); expando = JS_NewObjectWithGivenProto(cx, nsnull, nsnull, holder->getParent());
if (!expando)
return NULL;
holder->setSlot(JSSLOT_EXPANDO, ObjectValue(*expando)); holder->setSlot(JSSLOT_EXPANDO, ObjectValue(*expando));
} }
return expando; return expando;

View File

@ -39,6 +39,22 @@ enum BuiltInCharacterClassID {
NewlineClassID NewlineClassID
}; };
enum ErrorCode {
NoError,
PatternTooLarge,
QuantifierOutOfOrder,
QuantifierWithoutAtom,
MissingParentheses,
ParenthesesUnmatched,
ParenthesesTypeInvalid,
CharacterClassUnmatched,
CharacterClassOutOfOrder,
CharacterClassRangeSingleChar,
EscapeUnterminated,
QuantifierTooLarge,
NumberOfErrorCodes
};
// The Parser class should not be used directly - only via the Yarr::parse() method. // The Parser class should not be used directly - only via the Yarr::parse() method.
template<class Delegate> template<class Delegate>
class Parser { class Parser {
@ -46,21 +62,6 @@ private:
template<class FriendDelegate> template<class FriendDelegate>
friend int parse(FriendDelegate& delegate, const UString& pattern, unsigned backReferenceLimit); friend int parse(FriendDelegate& delegate, const UString& pattern, unsigned backReferenceLimit);
enum ErrorCode {
NoError,
PatternTooLarge,
QuantifierOutOfOrder,
QuantifierWithoutAtom,
MissingParentheses,
ParenthesesUnmatched,
ParenthesesTypeInvalid,
CharacterClassUnmatched,
CharacterClassOutOfOrder,
EscapeUnterminated,
QuantifierTooLarge,
NumberOfErrorCodes
};
/* /*
* CharacterClassParserDelegate: * CharacterClassParserDelegate:
* *
@ -147,6 +148,15 @@ private:
*/ */
void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert)
{ {
if (m_state == cachedCharacterHyphen) {
// If the RHS of a range does not contain exacly one character then a SyntaxError
// must be thrown. SpiderMonkey only errors out in the [c-\s] case as an extension.
// (This assumes none of the built in character classes contain a single
// character.)
m_err = CharacterClassRangeSingleChar;
m_state = empty;
return;
}
flush(); flush();
m_delegate.atomCharacterClassBuiltIn(classID, invert); m_delegate.atomCharacterClassBuiltIn(classID, invert);
} }
@ -404,7 +414,7 @@ private:
/* /*
* parseCharacterClass(): * parseCharacterClass():
* *
* Helper for parseTokens(); calls dirctly and indirectly (via parseCharacterClassEscape) * Helper for parseTokens(); calls directly and indirectly (via parseCharacterClassEscape)
* to an instance of CharacterClassParserDelegate, to describe the character class to the * to an instance of CharacterClassParserDelegate, to describe the character class to the
* delegate. * delegate.
*/ */