Bug 771039 - assert some invariants in BindNameToSlot (r=dvander)

--HG--
extra : rebase_source : fb7cbfa0cfcfd612c67cfc4961609ad424b59a1d
This commit is contained in:
Luke Wagner 2012-07-05 20:29:24 -07:00
Родитель eef470cd4c
Коммит de8ea2a8cc
1 изменённых файлов: 59 добавлений и 54 удалений

Просмотреть файл

@ -872,6 +872,11 @@ ClonedBlockDepth(BytecodeEmitter *bce)
static bool
EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
{
/* This restriction will be removed shortly by bug 753158. */
JS_ASSERT(pn->isUsed() || pn->isDefn());
JS_ASSERT_IF(pn->isUsed(), pn->pn_cookie.level() == 0);
JS_ASSERT_IF(pn->isDefn(), pn->pn_cookie.level() == bce->script->staticLevel);
/*
* The contents of the dynamic scope chain (fp->scopeChain) exactly reflect
* the needsClone-subset of the block chain. Use this to determine the
@ -914,8 +919,12 @@ EmitVarOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
JS_ASSERT_IF(pn->isKind(PNK_NAME), JOF_OPTYPE(op) == JOF_QARG || JOF_OPTYPE(op) == JOF_LOCAL);
JS_ASSERT(!pn->pn_cookie.isFree());
if (!bce->isAliasedName(pn))
if (!bce->isAliasedName(pn)) {
JS_ASSERT(pn->isUsed() || pn->isDefn());
JS_ASSERT_IF(pn->isUsed(), pn->pn_cookie.level() == 0);
JS_ASSERT_IF(pn->isDefn(), pn->pn_cookie.level() == bce->script->staticLevel);
return EmitUnaliasedVarOp(cx, op, pn->pn_cookie.slot(), bce);
}
switch (op) {
case JSOP_GETARG: case JSOP_GETLOCAL: op = JSOP_GETALIASEDVAR; break;
@ -1171,44 +1180,34 @@ TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
static bool
BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
{
Definition *dn;
JSOp op;
Definition::Kind dn_kind;
JS_ASSERT(pn->isKind(PNK_NAME));
/* Idempotency tests come first, since we may be called more than once. */
if (pn->pn_dflags & PND_BOUND)
/* Don't attempt if 'pn' is already bound, deoptimized, or a nop. */
if ((pn->pn_dflags & PND_BOUND) || pn->isDeoptimized() || pn->getOp() == JSOP_NOP)
return true;
/* No cookie initialized for callee; it is pre-bound by definition. */
/* JSOP_CALLEE is pre-bound by definition. */
JS_ASSERT(!pn->isOp(JSOP_CALLEE));
/*
* The parser linked all uses (including forward references) to their
* definitions, unless a with statement or direct eval intervened.
* The parser already linked name uses to definitions when (where not
* prevented by non-lexical constructs like 'with' and 'eval').
*/
Definition *dn;
if (pn->isUsed()) {
JS_ASSERT(pn->pn_cookie.isFree());
dn = pn->pn_lexdef;
JS_ASSERT(dn->isDefn());
if (pn->isDeoptimized())
return true;
pn->pn_dflags |= (dn->pn_dflags & PND_CONST);
} else {
if (!pn->isDefn())
return true;
} else if (pn->isDefn()) {
dn = (Definition *) pn;
} else {
return true;
}
op = pn->getOp();
if (op == JSOP_NOP)
return true;
JSOp op = pn->getOp();
JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
RootedAtom atom(cx, pn->pn_atom);
UpvarCookie cookie = dn->pn_cookie;
dn_kind = dn->kind();
JS_ASSERT_IF(dn->kind() == Definition::CONST, pn->pn_dflags & PND_CONST);
/*
* Turn attempts to mutate const-declared bindings into get ops (for
@ -1224,7 +1223,7 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
case JSOP_SETCONST:
break;
case JSOP_DELNAME:
if (dn_kind != Definition::UNKNOWN) {
if (dn->kind() != Definition::UNKNOWN) {
if (bce->callerFrame && dn->isTopLevel())
JS_ASSERT(bce->script->compileAndGo);
else
@ -1237,7 +1236,7 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (pn->isConst()) {
if (bce->sc->needStrictChecks()) {
JSAutoByteString name;
if (!js_AtomToPrintableString(cx, atom, &name) ||
if (!js_AtomToPrintableString(cx, pn->pn_atom, &name) ||
!bce->reportStrictModeError(pn, JSMSG_READ_ONLY, name.ptr()))
{
return false;
@ -1247,7 +1246,7 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
}
}
if (cookie.isFree()) {
if (dn->pn_cookie.isFree()) {
StackFrame *caller = bce->callerFrame;
if (caller) {
JS_ASSERT(bce->script->compileAndGo);
@ -1288,34 +1287,40 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
return true;
}
uint16_t level = cookie.level();
JS_ASSERT(bce->script->staticLevel >= level);
/*
* At this point, we are only dealing with uses that have already been
* bound to definitions via pn_lexdef. The rest of this routine converts
* the parse node of the use from its initial JSOP_*NAME* op to a LOCAL/ARG
* op. This requires setting the node's pn_cookie with a pair (level, slot)
* where 'level' is the number of function scopes between the use and the
* def and 'slot' is the index to emit as the immediate of the ARG/LOCAL
* op. For example, in this code:
*
* function(a,b,x) { return x }
* function(y) { function() { return y } }
*
* x will get (level = 0, slot = 2) and y will get (level = 1, slot = 0).
*/
JS_ASSERT(!pn->isDefn());
JS_ASSERT(pn->isUsed());
JS_ASSERT(pn->pn_lexdef);
JS_ASSERT(pn->pn_cookie.isFree());
const unsigned skip = bce->script->staticLevel - level;
if (skip != 0)
/* TODO: actually the comment lies; until bug 753158, y will stay a NAME. */
if (dn->pn_cookie.level() != bce->script->staticLevel) {
JS_ASSERT(dn->isClosed());
return true;
}
/*
* We are compiling a function body and may be able to optimize name
* to stack slot. Look for an argument or variable in the function and
* rewrite pn_op and update pn accordingly.
*/
switch (dn_kind) {
switch (dn->kind()) {
case Definition::UNKNOWN:
return true;
case Definition::LET:
switch (op) {
case JSOP_NAME: op = JSOP_GETLOCAL; break;
case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
case JSOP_INCNAME: op = JSOP_INCLOCAL; break;
case JSOP_NAMEINC: op = JSOP_LOCALINC; break;
case JSOP_DECNAME: op = JSOP_DECLOCAL; break;
case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break;
default: JS_NOT_REACHED("let");
}
break;
case Definition::ARG:
switch (op) {
case JSOP_NAME: op = JSOP_GETARG; break;
@ -1332,7 +1337,8 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
case Definition::VAR:
if (dn->isOp(JSOP_CALLEE)) {
JS_ASSERT(op != JSOP_CALLEE);
JS_ASSERT((bce->sc->fun()->flags & JSFUN_LAMBDA) && atom == bce->sc->fun()->atom);
JS_ASSERT(bce->sc->fun()->flags & JSFUN_LAMBDA);
JS_ASSERT(pn->pn_atom == bce->sc->fun()->atom);
/*
* Leave pn->isOp(JSOP_NAME) if bce->fun is heavyweight to
@ -1370,10 +1376,9 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
}
/* FALL THROUGH */
default:
JS_ASSERT_IF(dn_kind != Definition::FUNCTION,
dn_kind == Definition::VAR ||
dn_kind == Definition::CONST);
case Definition::FUNCTION:
case Definition::CONST:
case Definition::LET:
switch (op) {
case JSOP_NAME: op = JSOP_GETLOCAL; break;
case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
@ -1384,14 +1389,17 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break;
default: JS_NOT_REACHED("local");
}
JS_ASSERT_IF(dn_kind == Definition::CONST, pn->pn_dflags & PND_CONST);
break;
default:
JS_NOT_REACHED("unexpected dn->kind()");
}
JS_ASSERT(!pn->isOp(op));
pn->setOp(op);
if (!pn->pn_cookie.set(bce->sc->context, 0, cookie.slot()))
if (!pn->pn_cookie.set(bce->sc->context, 0, dn->pn_cookie.slot()))
return false;
pn->pn_dflags |= PND_BOUND;
return true;
}
@ -3432,17 +3440,14 @@ EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, Par
* Specialize to avoid ECMA "reference type" values on the operand
* stack, which impose pervasive runtime "GetValue" costs.
*/
jsatomid atomIndex = (jsatomid) -1; /* quell GCC overwarning */
jsatomid atomIndex = (jsatomid) -1;
jsbytecode offset = 1;
switch (lhs->getKind()) {
case PNK_NAME:
if (!BindNameToSlot(cx, bce, lhs))
return false;
if (!lhs->pn_cookie.isFree()) {
JS_ASSERT(lhs->pn_cookie.level() == 0);
atomIndex = lhs->pn_cookie.slot();
} else {
if (lhs->pn_cookie.isFree()) {
if (!bce->makeAtomIndex(lhs->pn_atom, &atomIndex))
return false;
if (!lhs->isConst()) {