зеркало из https://github.com/mozilla/gecko-dev.git
Bug 692274, part 3 - Remove JSOP_BLOCKCHAIN and JSOP_NULLBLOCKCHAIN, which produces incorrect let scoping until the next patch (r=jorendorff)
This commit is contained in:
Родитель
e8ece4c65a
Коммит
0b5000dc38
|
@ -49,7 +49,7 @@ namespace js {
|
|||
inline
|
||||
TreeContext::TreeContext(Parser *prs)
|
||||
: flags(0), bodyid(0), blockidGen(0), parenDepth(0), yieldCount(0), argumentsCount(0),
|
||||
topStmt(NULL), topScopeStmt(NULL), blockChainBox(NULL), blockNode(NULL),
|
||||
topStmt(NULL), topScopeStmt(NULL), blockChain(NULL), blockNode(NULL),
|
||||
decls(prs->context), parser(prs), yieldNode(NULL), argumentsNode(NULL), scopeChain_(NULL),
|
||||
lexdeps(prs->context), parent(prs->tc), staticLevel(0), funbox(NULL), functionList(NULL),
|
||||
innermostWith(NULL), bindings(prs->context), sharpSlotBase(-1)
|
||||
|
|
|
@ -96,9 +96,6 @@ NewTryNote(JSContext *cx, BytecodeEmitter *bce, JSTryNoteKind kind, uintN stackD
|
|||
static bool
|
||||
EmitIndexOp(JSContext *cx, JSOp op, uintN index, BytecodeEmitter *bce, JSOp *psuffix = NULL);
|
||||
|
||||
static JSBool
|
||||
EmitLeaveBlock(JSContext *cx, BytecodeEmitter *bce, JSOp op, ObjectBox *box);
|
||||
|
||||
static JSBool
|
||||
SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, uintN index, uintN which, ptrdiff_t offset);
|
||||
|
||||
|
@ -298,24 +295,6 @@ frontend::Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1,
|
|||
return offset;
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
frontend::Emit5(JSContext *cx, BytecodeEmitter *bce, JSOp op, uint16_t op1, uint16_t op2)
|
||||
{
|
||||
ptrdiff_t offset = EmitCheck(cx, bce, 5);
|
||||
|
||||
if (offset >= 0) {
|
||||
jsbytecode *next = bce->next();
|
||||
next[0] = (jsbytecode)op;
|
||||
next[1] = UINT16_HI(op1);
|
||||
next[2] = UINT16_LO(op1);
|
||||
next[3] = UINT16_HI(op2);
|
||||
next[4] = UINT16_LO(op2);
|
||||
bce->current->next = next + 5;
|
||||
UpdateDepth(cx, bce, offset);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
frontend::EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra)
|
||||
{
|
||||
|
@ -1366,7 +1345,7 @@ frontend::PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_
|
|||
stmt->blockid = tc->blockid();
|
||||
SET_STATEMENT_TOP(stmt, top);
|
||||
stmt->label = NULL;
|
||||
JS_ASSERT(!stmt->blockBox);
|
||||
JS_ASSERT(!stmt->blockObj);
|
||||
stmt->down = tc->topStmt;
|
||||
tc->topStmt = stmt;
|
||||
if (STMT_LINKS_SCOPE(stmt)) {
|
||||
|
@ -1378,16 +1357,15 @@ frontend::PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_
|
|||
}
|
||||
|
||||
void
|
||||
frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, ObjectBox *blockBox, ptrdiff_t top)
|
||||
frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, JSObject *blockObj, ptrdiff_t top)
|
||||
{
|
||||
PushStatement(tc, stmt, STMT_BLOCK, top);
|
||||
stmt->flags |= SIF_SCOPE;
|
||||
blockBox->parent = tc->blockChainBox;
|
||||
blockBox->object->setStaticBlockScopeChain(tc->blockChain());
|
||||
blockObj->setStaticBlockScopeChain(tc->blockChain);
|
||||
stmt->downScope = tc->topScopeStmt;
|
||||
tc->topScopeStmt = stmt;
|
||||
tc->blockChainBox = blockBox;
|
||||
stmt->blockBox = blockBox;
|
||||
tc->blockChain = blockObj;
|
||||
stmt->blockObj = blockObj;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1581,8 +1559,8 @@ EmitNonLocalJumpFixup(JSContext *cx, BytecodeEmitter *bce, StmtInfo *toStmt)
|
|||
FLUSH_POPS();
|
||||
if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
|
||||
return JS_FALSE;
|
||||
if (!EmitLeaveBlock(cx, bce, JSOP_LEAVEBLOCK, stmt->blockBox))
|
||||
return JS_FALSE;
|
||||
uintN i = OBJ_BLOCK_COUNT(cx, stmt->blockObj);
|
||||
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1593,20 +1571,6 @@ EmitNonLocalJumpFixup(JSContext *cx, BytecodeEmitter *bce, StmtInfo *toStmt)
|
|||
#undef FLUSH_POPS
|
||||
}
|
||||
|
||||
static JSBool
|
||||
EmitKnownBlockChain(JSContext *cx, BytecodeEmitter *bce, ObjectBox *box)
|
||||
{
|
||||
if (box)
|
||||
return EmitIndexOp(cx, JSOP_BLOCKCHAIN, box->index, bce);
|
||||
return Emit1(cx, bce, JSOP_NULLBLOCKCHAIN) >= 0;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
EmitBlockChain(JSContext *cx, BytecodeEmitter *bce)
|
||||
{
|
||||
return EmitKnownBlockChain(cx, bce, bce->blockChainBox);
|
||||
}
|
||||
|
||||
static const jsatomid INVALID_ATOMID = -1;
|
||||
|
||||
static ptrdiff_t
|
||||
|
@ -1627,14 +1591,7 @@ EmitGoto(JSContext *cx, BytecodeEmitter *bce, StmtInfo *toStmt, ptrdiff_t *lastp
|
|||
if (index < 0)
|
||||
return -1;
|
||||
|
||||
ptrdiff_t result = EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, lastp);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
if (!EmitBlockChain(cx, bce))
|
||||
return -1;
|
||||
|
||||
return result;
|
||||
return EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, lastp);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
@ -1668,9 +1625,8 @@ frontend::PopStatementTC(TreeContext *tc)
|
|||
tc->topStmt = stmt->down;
|
||||
if (STMT_LINKS_SCOPE(stmt)) {
|
||||
tc->topScopeStmt = stmt->downScope;
|
||||
if (stmt->flags & SIF_SCOPE) {
|
||||
tc->blockChainBox = stmt->blockBox->parent;
|
||||
}
|
||||
if (stmt->flags & SIF_SCOPE)
|
||||
tc->blockChain = stmt->blockObj->staticBlockScopeChain();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1712,7 +1668,7 @@ frontend::LexicalLookup(TreeContext *tc, JSAtom *atom, jsint *slotp, StmtInfo *s
|
|||
if (!(stmt->flags & SIF_SCOPE))
|
||||
continue;
|
||||
|
||||
JSObject *obj = stmt->blockBox->object;
|
||||
JSObject *obj = stmt->blockObj;
|
||||
JS_ASSERT(obj->isStaticBlock());
|
||||
|
||||
const Shape *shape = obj->nativeLookup(tc->parser->context, ATOM_TO_JSID(atom));
|
||||
|
@ -2031,20 +1987,6 @@ EmitEnterBlock(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce)
|
|||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
EmitLeaveBlock(JSContext *cx, BytecodeEmitter *bce, JSOp op, ObjectBox *box)
|
||||
{
|
||||
JSOp bigSuffix;
|
||||
uintN count = OBJ_BLOCK_COUNT(cx, box->object);
|
||||
|
||||
bigSuffix = EmitBigIndexPrefix(cx, bce, box->index);
|
||||
if (bigSuffix == JSOP_FALSE)
|
||||
return JS_FALSE;
|
||||
if (Emit5(cx, bce, op, count, box->index) < 0)
|
||||
return JS_FALSE;
|
||||
return bigSuffix == JSOP_NOP || Emit1(cx, bce, bigSuffix) >= 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to convert a *NAME op to a *GNAME op, which optimizes access to
|
||||
* undeclared globals. Return true if a conversion was made.
|
||||
|
@ -3223,56 +3165,6 @@ AllocateSwitchConstant(JSContext *cx)
|
|||
return cx->tempLifoAlloc().new_<Value>();
|
||||
}
|
||||
|
||||
/*
|
||||
* Sometimes, let-slots are pushed to the JS stack before we logically enter
|
||||
* the let scope. For example,
|
||||
* let (x = EXPR) BODY
|
||||
* compiles to roughly {enterblock; EXPR; setlocal x; BODY; leaveblock} even
|
||||
* though EXPR is evaluated in the enclosing scope; it does not see x.
|
||||
*
|
||||
* In those cases we use TempPopScope around the code to emit EXPR. It
|
||||
* temporarily removes the let-scope from the BytecodeEmitter's scope stack and
|
||||
* emits extra bytecode to ensure that js::GetBlockChain also finds the correct
|
||||
* scope at run time.
|
||||
*/
|
||||
class TempPopScope {
|
||||
StmtInfo *savedStmt;
|
||||
StmtInfo *savedScopeStmt;
|
||||
ObjectBox *savedBlockBox;
|
||||
|
||||
public:
|
||||
TempPopScope() : savedStmt(NULL), savedScopeStmt(NULL), savedBlockBox(NULL) {}
|
||||
|
||||
bool popBlock(JSContext *cx, BytecodeEmitter *bce) {
|
||||
savedStmt = bce->topStmt;
|
||||
savedScopeStmt = bce->topScopeStmt;
|
||||
savedBlockBox = bce->blockChainBox;
|
||||
|
||||
if (bce->topStmt->type == STMT_FOR_LOOP || bce->topStmt->type == STMT_FOR_IN_LOOP)
|
||||
PopStatementTC(bce);
|
||||
JS_ASSERT(STMT_LINKS_SCOPE(bce->topStmt));
|
||||
JS_ASSERT(bce->topStmt->flags & SIF_SCOPE);
|
||||
PopStatementTC(bce);
|
||||
|
||||
/*
|
||||
* Since we have changed the block chain, emit an instruction marking
|
||||
* the change for the benefit of dynamic GetScopeChain callers such as
|
||||
* the debugger.
|
||||
*
|
||||
* FIXME bug 671360 - The JSOP_NOP instruction should not be necessary.
|
||||
*/
|
||||
return Emit1(cx, bce, JSOP_NOP) >= 0 && EmitBlockChain(cx, bce);
|
||||
}
|
||||
|
||||
bool repushBlock(JSContext *cx, BytecodeEmitter *bce) {
|
||||
JS_ASSERT(savedStmt);
|
||||
bce->topStmt = savedStmt;
|
||||
bce->topScopeStmt = savedScopeStmt;
|
||||
bce->blockChainBox = savedBlockBox;
|
||||
return Emit1(cx, bce, JSOP_NOP) >= 0 && EmitBlockChain(cx, bce);
|
||||
}
|
||||
};
|
||||
|
||||
static JSBool
|
||||
EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
|
@ -3287,7 +3179,7 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
size_t switchSize, tableSize;
|
||||
jsbytecode *pc, *savepc;
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
ObjectBox *box;
|
||||
int count;
|
||||
#endif
|
||||
StmtInfo stmtInfo;
|
||||
|
||||
|
@ -3305,7 +3197,6 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
*/
|
||||
pn2 = pn->pn_right;
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
TempPopScope tps;
|
||||
if (pn2->isKind(PNK_LEXICALSCOPE)) {
|
||||
/*
|
||||
* Push the body's block scope before discriminant code-gen to reflect
|
||||
|
@ -3313,24 +3204,17 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
* the discriminant on the stack so that case-dispatch bytecodes can
|
||||
* find the discriminant on top of stack.
|
||||
*/
|
||||
box = pn2->pn_objbox;
|
||||
PushBlockScope(bce, &stmtInfo, box, -1);
|
||||
count = OBJ_BLOCK_COUNT(cx, pn2->pn_objbox->object);
|
||||
PushBlockScope(bce, &stmtInfo, pn2->pn_objbox->object, -1);
|
||||
stmtInfo.type = STMT_SWITCH;
|
||||
|
||||
/* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */
|
||||
if (!EmitEnterBlock(cx, pn2, bce))
|
||||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
* Pop the switch's statement info around discriminant code-gen, which
|
||||
* belongs in the enclosing scope.
|
||||
*/
|
||||
if (!tps.popBlock(cx, bce))
|
||||
return JS_FALSE;
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
else {
|
||||
box = NULL;
|
||||
count = 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
@ -3350,10 +3234,6 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
if (pn2->isKind(PNK_STATEMENTLIST)) {
|
||||
PushStatement(bce, &stmtInfo, STMT_SWITCH, top);
|
||||
} else {
|
||||
/* Re-push the switch's statement info record. */
|
||||
if (!tps.repushBlock(cx, bce))
|
||||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
* Set the statement info record's idea of top. Reset top too, since
|
||||
* repushBlock emits code.
|
||||
|
@ -3806,7 +3686,7 @@ out:
|
|||
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
if (ok && pn->pn_right->isKind(PNK_LEXICALSCOPE))
|
||||
ok = EmitLeaveBlock(cx, bce, JSOP_LEAVEBLOCK, box);
|
||||
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
|
||||
#endif
|
||||
}
|
||||
return ok;
|
||||
|
@ -4956,7 +4836,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
|
||||
ptrdiff_t tryEnd = bce->offset();
|
||||
|
||||
ObjectBox *prevBox = NULL;
|
||||
/* If this try has a catch block, emit it. */
|
||||
ParseNode *lastCatch = NULL;
|
||||
if (ParseNode *pn2 = pn->pn_kid2) {
|
||||
|
@ -4965,7 +4844,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
/*
|
||||
* The emitted code for a catch block looks like:
|
||||
*
|
||||
* blockchain
|
||||
* [throwing] only if 2nd+ catch block
|
||||
* [leaveblock] only if 2nd+ catch block
|
||||
* enterblock with SRC_CATCH
|
||||
|
@ -4991,9 +4869,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
JS_ASSERT(bce->stackDepth == depth);
|
||||
guardJump = GUARDJUMP(stmtInfo);
|
||||
if (guardJump != -1) {
|
||||
if (EmitKnownBlockChain(cx, bce, prevBox) < 0)
|
||||
return false;
|
||||
|
||||
/* Fix up and clean up previous catch block. */
|
||||
CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, guardJump);
|
||||
|
||||
|
@ -5016,8 +4891,7 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
}
|
||||
if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
|
||||
return false;
|
||||
if (!EmitLeaveBlock(cx, bce, JSOP_LEAVEBLOCK, prevBox))
|
||||
return false;
|
||||
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
|
||||
JS_ASSERT(bce->stackDepth == depth);
|
||||
}
|
||||
|
||||
|
@ -5040,7 +4914,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
*/
|
||||
JS_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
|
||||
count = OBJ_BLOCK_COUNT(cx, pn3->pn_objbox->object);
|
||||
prevBox = pn3->pn_objbox;
|
||||
if (!EmitTree(cx, bce, pn3))
|
||||
return false;
|
||||
|
||||
|
@ -5075,9 +4948,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
* stack unbalanced.
|
||||
*/
|
||||
if (lastCatch && lastCatch->pn_kid2) {
|
||||
if (EmitKnownBlockChain(cx, bce, prevBox) < 0)
|
||||
return false;
|
||||
|
||||
CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, GUARDJUMP(stmtInfo));
|
||||
|
||||
/* Sync the stack to take into account pushed exception. */
|
||||
|
@ -5090,9 +4960,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
*/
|
||||
if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0 || Emit1(cx, bce, JSOP_THROW) < 0)
|
||||
return false;
|
||||
|
||||
if (EmitBlockChain(cx, bce) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_ASSERT(bce->stackDepth == depth);
|
||||
|
@ -5261,26 +5128,12 @@ EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
pn2 = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-null pn2 means that pn is the variable list from a let head.
|
||||
*
|
||||
* Use TempPopScope to evaluate the expressions in the enclosing scope.
|
||||
* This also causes the initializing assignments to be emitted in the
|
||||
* enclosing scope, but the assignment opcodes emitted here
|
||||
* (essentially just setlocal, though destructuring assignment uses
|
||||
* other additional opcodes) do not care about the block chain.
|
||||
*/
|
||||
/* Non-null pn2 means that pn is the variable list from a let head. */
|
||||
JS_ASSERT(pn->isArity(PN_LIST));
|
||||
TempPopScope tps;
|
||||
bool popScope = pn2 || (bce->flags & TCF_IN_FOR_INIT);
|
||||
if (popScope && !tps.popBlock(cx, bce))
|
||||
return false;
|
||||
ptrdiff_t noteIndex;
|
||||
if (!EmitVariables(cx, bce, pn, pn2 != NULL, ¬eIndex))
|
||||
return false;
|
||||
ptrdiff_t tmp = bce->offset();
|
||||
if (popScope && !tps.repushBlock(cx, bce))
|
||||
return false;
|
||||
|
||||
/* Thus non-null pn2 is the body of the let block or expression. */
|
||||
if (pn2 && !EmitTree(cx, bce, pn2))
|
||||
|
@ -5374,7 +5227,7 @@ EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
StmtInfo stmtInfo;
|
||||
StmtInfo *stmt;
|
||||
ObjectBox *objbox = pn->pn_objbox;
|
||||
PushBlockScope(bce, &stmtInfo, objbox, bce->offset());
|
||||
PushBlockScope(bce, &stmtInfo, objbox->object, bce->offset());
|
||||
|
||||
/*
|
||||
* If this lexical scope is not for a catch block, let block or let
|
||||
|
@ -5419,8 +5272,8 @@ EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
}
|
||||
|
||||
/* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */
|
||||
if (!EmitLeaveBlock(cx, bce, op, objbox))
|
||||
return false;
|
||||
uintN count = OBJ_BLOCK_COUNT(cx, objbox->object);
|
||||
EMIT_UINT16_IMM_OP(op, count);
|
||||
|
||||
return PopStatementBCE(cx, bce);
|
||||
}
|
||||
|
@ -5435,9 +5288,6 @@ EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
if (Emit1(cx, bce, JSOP_ENTERWITH) < 0)
|
||||
return false;
|
||||
|
||||
/* Make blockChain determination quicker. */
|
||||
if (EmitBlockChain(cx, bce) < 0)
|
||||
return false;
|
||||
if (!EmitTree(cx, bce, pn->pn_right))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
|
||||
|
@ -5495,10 +5345,8 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
|||
* see Parser::forStatement. 'for (let x = i in o)' is mercifully
|
||||
* banned.
|
||||
*/
|
||||
bool forLet = false;
|
||||
if (ParseNode *decl = forHead->pn_kid1) {
|
||||
JS_ASSERT(decl->isKind(PNK_VAR) || decl->isKind(PNK_LET));
|
||||
forLet = decl->isKind(PNK_LET);
|
||||
bce->flags |= TCF_IN_FOR_INIT;
|
||||
if (!EmitTree(cx, bce, decl))
|
||||
return false;
|
||||
|
@ -5506,15 +5354,8 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
|||
}
|
||||
|
||||
/* Compile the object expression to the right of 'in'. */
|
||||
{
|
||||
TempPopScope tps;
|
||||
if (forLet && !tps.popBlock(cx, bce))
|
||||
return false;
|
||||
if (!EmitTree(cx, bce, forHead->pn_kid3))
|
||||
return false;
|
||||
if (forLet && !tps.repushBlock(cx, bce))
|
||||
return false;
|
||||
}
|
||||
if (!EmitTree(cx, bce, forHead->pn_kid3))
|
||||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
* Emit a bytecode to convert top of stack value to the iterator
|
||||
|
@ -5825,9 +5666,7 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
return false;
|
||||
}
|
||||
EMIT_INDEX_OP(pn->getOp(), index);
|
||||
|
||||
/* Make blockChain determination quicker. */
|
||||
return EmitBlockChain(cx, bce) >= 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -5847,10 +5686,6 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
bce->switchToProlog();
|
||||
JSOp op = fun->isFlatClosure() ? JSOP_DEFFUN_FC : JSOP_DEFFUN;
|
||||
EMIT_INDEX_OP(op, index);
|
||||
|
||||
/* Make blockChain determination quicker. */
|
||||
if (EmitBlockChain(cx, bce) < 0)
|
||||
return false;
|
||||
bce->switchToMain();
|
||||
}
|
||||
|
||||
|
@ -5870,11 +5705,7 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
{
|
||||
return false;
|
||||
}
|
||||
if (!EmitSlotIndexOp(cx, op, slot, index, bce))
|
||||
return false;
|
||||
|
||||
/* Make blockChain determination quicker. */
|
||||
return EmitBlockChain(cx, bce) >= 0;
|
||||
return EmitSlotIndexOp(cx, op, slot, index, bce);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -6068,8 +5899,6 @@ EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
bce->base()[top] = JSOP_SETRVAL;
|
||||
if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
|
||||
return false;
|
||||
if (EmitBlockChain(cx, bce) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -6381,11 +6210,8 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
|||
if (Emit3(cx, bce, pn->getOp(), ARGC_HI(argc), ARGC_LO(argc)) < 0)
|
||||
return false;
|
||||
CheckTypeSet(cx, bce, pn->getOp());
|
||||
if (pn->isOp(JSOP_EVAL)) {
|
||||
if (pn->isOp(JSOP_EVAL))
|
||||
EMIT_UINT16_IMM_OP(JSOP_LINENO, pn->pn_pos.begin.lineno);
|
||||
if (EmitBlockChain(cx, bce) < 0)
|
||||
return false;
|
||||
}
|
||||
if (pn->pn_xflags & PNX_SETCALL) {
|
||||
if (Emit1(cx, bce, JSOP_SETCALL) < 0)
|
||||
return false;
|
||||
|
@ -7212,10 +7038,6 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, jmp);
|
||||
if (EmitJump(cx, bce, JSOP_ENDFILTER, top - bce->offset()) < 0)
|
||||
return JS_FALSE;
|
||||
|
||||
/* Make blockChain determination quicker. */
|
||||
if (EmitBlockChain(cx, bce) < 0)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
@ -7852,8 +7674,7 @@ CGObjectList::index(ObjectBox *objbox)
|
|||
JS_ASSERT(!objbox->emitLink);
|
||||
objbox->emitLink = lastbox;
|
||||
lastbox = objbox;
|
||||
objbox->index = length++;
|
||||
return objbox->index;
|
||||
return length++;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -138,7 +138,7 @@ struct StmtInfo {
|
|||
ptrdiff_t continues; /* offset of last continue in loop */
|
||||
union {
|
||||
JSAtom *label; /* name of LABEL */
|
||||
ObjectBox *blockBox; /* block scope object */
|
||||
JSObject *blockObj; /* block scope object */
|
||||
};
|
||||
StmtInfo *down; /* info for enclosing statement */
|
||||
StmtInfo *downScope; /* next enclosing lexical scope */
|
||||
|
@ -299,7 +299,7 @@ struct TreeContext { /* tree context for semantic checks */
|
|||
at non-zero depth in current paren tree */
|
||||
StmtInfo *topStmt; /* top of statement info stack */
|
||||
StmtInfo *topScopeStmt; /* top lexical scope statement */
|
||||
ObjectBox *blockChainBox; /* compile time block scope chain (NB: one
|
||||
JSObject *blockChain; /* compile time block scope chain (NB: one
|
||||
deeper than the topScopeStmt/downScope
|
||||
chain when in head of let block/expr) */
|
||||
ParseNode *blockNode; /* parse node for a block with let declarations
|
||||
|
@ -376,10 +376,6 @@ struct TreeContext { /* tree context for semantic checks */
|
|||
|
||||
uintN blockid() { return topStmt ? topStmt->blockid : bodyid; }
|
||||
|
||||
JSObject *blockChain() {
|
||||
return blockChainBox ? blockChainBox->object : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* True if we are at the topmost level of a entire script or function body.
|
||||
* For example, while parsing this code we would encounter f1 and f2 at
|
||||
|
@ -796,12 +792,6 @@ Emit2(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1);
|
|||
ptrdiff_t
|
||||
Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1, jsbytecode op2);
|
||||
|
||||
/*
|
||||
* Emit five bytecodes, an opcode with two 16-bit immediates.
|
||||
*/
|
||||
ptrdiff_t
|
||||
Emit5(JSContext *cx, BytecodeEmitter *bce, JSOp op, uint16_t op1, uint16_t op2);
|
||||
|
||||
/*
|
||||
* Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
|
||||
*/
|
||||
|
@ -843,7 +833,7 @@ PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top);
|
|||
* (if generating code), PopStatementBCE.
|
||||
*/
|
||||
void
|
||||
PushBlockScope(TreeContext *tc, StmtInfo *stmt, ObjectBox *blockBox, ptrdiff_t top);
|
||||
PushBlockScope(TreeContext *tc, StmtInfo *stmt, JSObject *blockObj, ptrdiff_t top);
|
||||
|
||||
/*
|
||||
* Pop tc->topStmt. If the top StmtInfo struct is not stack-allocated, it
|
||||
|
|
|
@ -1293,8 +1293,6 @@ struct ObjectBox {
|
|||
ObjectBox *traceLink;
|
||||
ObjectBox *emitLink;
|
||||
JSObject *object;
|
||||
ObjectBox *parent;
|
||||
uintN index;
|
||||
bool isFunctionBox;
|
||||
};
|
||||
|
||||
|
|
|
@ -1882,7 +1882,7 @@ MatchLabel(JSContext *cx, TokenStream *ts, PropertyName **label)
|
|||
* must already be in such a scope.
|
||||
*
|
||||
* Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a
|
||||
* property for the new variable on the block object, tc->blockChain();
|
||||
* property for the new variable on the block object, tc->blockChain;
|
||||
* populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to
|
||||
* data->pn in a slot of the block object.
|
||||
*/
|
||||
|
@ -1903,7 +1903,7 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc)
|
|||
if (!CheckStrictBinding(cx, tc, atom->asPropertyName(), pn))
|
||||
return false;
|
||||
|
||||
blockObj = tc->blockChain();
|
||||
blockObj = tc->blockChain;
|
||||
Definition *dn = tc->decls.lookupFirst(atom);
|
||||
if (dn && dn->pn_blockid == tc->blockid()) {
|
||||
JSAutoByteString name;
|
||||
|
@ -1966,7 +1966,7 @@ PopStatement(TreeContext *tc)
|
|||
StmtInfo *stmt = tc->topStmt;
|
||||
|
||||
if (stmt->flags & SIF_SCOPE) {
|
||||
JSObject *obj = stmt->blockBox->object;
|
||||
JSObject *obj = stmt->blockObj;
|
||||
JS_ASSERT(!obj->isClonedBlock());
|
||||
|
||||
for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
|
||||
|
@ -2613,8 +2613,8 @@ CheckDestructuring(JSContext *cx, BindData *data, ParseNode *left, TreeContext *
|
|||
*/
|
||||
if (data &&
|
||||
data->binder == BindLet &&
|
||||
OBJ_BLOCK_COUNT(cx, tc->blockChain()) == 0 &&
|
||||
!DefineNativeProperty(cx, tc->blockChain(),
|
||||
OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0 &&
|
||||
!DefineNativeProperty(cx, tc->blockChain,
|
||||
ATOM_TO_JSID(cx->runtime->atomState.emptyAtom),
|
||||
UndefinedValue(), NULL, NULL,
|
||||
JSPROP_ENUMERATE | JSPROP_PERMANENT,
|
||||
|
@ -2778,7 +2778,7 @@ PushLexicalScope(JSContext *cx, TokenStream *ts, TreeContext *tc, StmtInfo *stmt
|
|||
if (!blockbox)
|
||||
return NULL;
|
||||
|
||||
PushBlockScope(tc, stmt, blockbox, -1);
|
||||
PushBlockScope(tc, stmt, obj, -1);
|
||||
pn->setOp(JSOP_LEAVEBLOCK);
|
||||
pn->pn_objbox = blockbox;
|
||||
pn->pn_cookie.makeFree();
|
||||
|
@ -3639,7 +3639,7 @@ Parser::letStatement()
|
|||
}
|
||||
|
||||
if (stmt && (stmt->flags & SIF_SCOPE)) {
|
||||
JS_ASSERT(tc->blockChainBox == stmt->blockBox);
|
||||
JS_ASSERT(tc->blockChain == stmt->blockObj);
|
||||
} else {
|
||||
if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) {
|
||||
/*
|
||||
|
@ -3685,10 +3685,9 @@ Parser::letStatement()
|
|||
stmt->downScope = tc->topScopeStmt;
|
||||
tc->topScopeStmt = stmt;
|
||||
|
||||
obj->setStaticBlockScopeChain(tc->blockChain());
|
||||
blockbox->parent = tc->blockChainBox;
|
||||
tc->blockChainBox = blockbox;
|
||||
stmt->blockBox = blockbox;
|
||||
obj->setStaticBlockScopeChain(tc->blockChain);
|
||||
tc->blockChain = obj;
|
||||
stmt->blockObj = obj;
|
||||
|
||||
#ifdef DEBUG
|
||||
ParseNode *tmp = tc->blockNode;
|
||||
|
@ -4196,7 +4195,7 @@ Parser::variables(ParseNodeKind kind, bool inLetHead)
|
|||
* this code will change soon.
|
||||
*/
|
||||
if (let) {
|
||||
JS_ASSERT(tc->blockChainBox == scopeStmt->blockBox);
|
||||
JS_ASSERT(tc->blockChain == scopeStmt->blockObj);
|
||||
data.binder = BindLet;
|
||||
data.let.overflow = JSMSG_TOO_MANY_LOCALS;
|
||||
} else {
|
||||
|
|
|
@ -11,5 +11,5 @@ f = (function() {
|
|||
} catch (e) {}
|
||||
}
|
||||
})
|
||||
trap(f, 52, undefined);
|
||||
trap(f, 39, undefined);
|
||||
f()
|
||||
|
|
|
@ -6,5 +6,5 @@ function caller(code, obj) {
|
|||
eval(code); // Make the compiler give up on binding analysis.
|
||||
return x;
|
||||
}
|
||||
trap(caller, 14, "var x = 'success'; nop()");
|
||||
trap(caller, 12, "var x = 'success'; nop()");
|
||||
assertEq(caller("var y = 'ignominy'", this), "success");
|
||||
|
|
|
@ -3,7 +3,7 @@ setDebug(true);
|
|||
x = "notset";
|
||||
function main() {
|
||||
/* The JSOP_STOP in main. */
|
||||
a = { valueOf: function () { trap(main, 36, "success()"); } };
|
||||
a = { valueOf: function () { trap(main, 34, "success()"); } };
|
||||
a + "";
|
||||
x = "failure";
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ setDebug(true);
|
|||
x = "notset";
|
||||
function main() {
|
||||
/* The JSOP_STOP in main. */
|
||||
a = { valueOf: function () { trap(main, 57, "success()"); } };
|
||||
a = { valueOf: function () { trap(main, 55, "success()"); } };
|
||||
b = "";
|
||||
eval();
|
||||
a + b;
|
||||
|
|
|
@ -5,7 +5,7 @@ x = "notset";
|
|||
function myparent(nested) {
|
||||
if (nested) {
|
||||
/* noop call in myparent */
|
||||
trap(myparent, 50, "success()");
|
||||
trap(myparent, 49, "success()");
|
||||
} else {
|
||||
myparent(true);
|
||||
x = "failure";
|
||||
|
|
|
@ -14,7 +14,7 @@ function myparent(nested) {
|
|||
}
|
||||
}
|
||||
/* JSOP_CALL to doNothing in myparent with nested = false. */
|
||||
trap(myparent, 35, "myparent(true)");
|
||||
trap(myparent, 34, "myparent(true)");
|
||||
|
||||
function success() {
|
||||
x = "success";
|
||||
|
|
|
@ -2281,7 +2281,7 @@ js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
|
|||
}
|
||||
|
||||
JSFunction *
|
||||
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
|
||||
js_NewFlatClosure(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
/*
|
||||
* Flat closures cannot yet be partial, that is, all upvars must be copied,
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
#include "jsatom.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsstr.h"
|
||||
#include "jsopcode.h"
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
|
||||
|
@ -322,7 +321,7 @@ extern JSFunction * JS_FASTCALL
|
|||
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
|
||||
|
||||
extern JSFunction *
|
||||
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen);
|
||||
js_NewFlatClosure(JSContext *cx, JSFunction *fun);
|
||||
|
||||
extern JSFunction *
|
||||
js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, JSNative native,
|
||||
|
|
|
@ -3405,8 +3405,6 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||
case JSOP_INDEXBASE3:
|
||||
case JSOP_RESETBASE:
|
||||
case JSOP_RESETBASE0:
|
||||
case JSOP_BLOCKCHAIN:
|
||||
case JSOP_NULLBLOCKCHAIN:
|
||||
case JSOP_POPV:
|
||||
case JSOP_DEBUGGER:
|
||||
case JSOP_SETCALL:
|
||||
|
|
|
@ -83,7 +83,6 @@
|
|||
#include "jsinferinlines.h"
|
||||
#include "jsinterpinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsopcodeinlines.h"
|
||||
#include "jsprobes.h"
|
||||
#include "jspropertycacheinlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
|
@ -107,124 +106,6 @@ using namespace js;
|
|||
using namespace js::gc;
|
||||
using namespace js::types;
|
||||
|
||||
JSObject *
|
||||
js::GetScopeChain(JSContext *cx)
|
||||
{
|
||||
/*
|
||||
* Note: we don't need to expand inline frames here, because frames are
|
||||
* only inlined when the caller and callee share the same scope chain.
|
||||
*/
|
||||
StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
|
||||
if (!fp) {
|
||||
/*
|
||||
* There is no code active on this context. In place of an actual
|
||||
* scope chain, use the context's global object, which is set in
|
||||
* js_InitFunctionAndObjectClasses, and which represents the default
|
||||
* scope chain for the embedding. See also js_FindClassObject.
|
||||
*
|
||||
* For embeddings that use the inner and outer object hooks, the inner
|
||||
* object represents the ultimate global object, with the outer object
|
||||
* acting as a stand-in.
|
||||
*/
|
||||
JSObject *obj = cx->globalObject;
|
||||
if (!obj) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
OBJ_TO_INNER_OBJECT(cx, obj);
|
||||
return obj;
|
||||
}
|
||||
return GetScopeChain(cx, fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* This computes the blockChain by iterating through the bytecode
|
||||
* of the current script until it reaches the PC. Each time it sees
|
||||
* an ENTERBLOCK or LEAVEBLOCK instruction, it records the new
|
||||
* blockChain. A faster variant of this function that doesn't
|
||||
* require bytecode scanning appears below.
|
||||
*/
|
||||
JSObject *
|
||||
js::GetBlockChain(JSContext *cx, StackFrame *fp)
|
||||
{
|
||||
if (!fp->isScriptFrame())
|
||||
return NULL;
|
||||
|
||||
jsbytecode *target = fp->pcQuadratic(cx->stack);
|
||||
|
||||
JSScript *script = fp->script();
|
||||
jsbytecode *start = script->code;
|
||||
|
||||
/*
|
||||
* If the debugger asks for the scope chain at a pc where we are about to
|
||||
* fix it up, advance target past the fixup. See bug 672804.
|
||||
*/
|
||||
JSOp op = JSOp(*target);
|
||||
while (op == JSOP_NOP || op == JSOP_INDEXBASE || op == JSOP_INDEXBASE1 ||
|
||||
op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3 ||
|
||||
op == JSOP_BLOCKCHAIN || op == JSOP_NULLBLOCKCHAIN)
|
||||
{
|
||||
target += js_CodeSpec[op].length;
|
||||
op = JSOp(*target);
|
||||
}
|
||||
JS_ASSERT(target >= start && target < start + script->length);
|
||||
|
||||
JSObject *blockChain = NULL;
|
||||
uintN indexBase = 0;
|
||||
ptrdiff_t oplen;
|
||||
for (jsbytecode *pc = start; pc < target; pc += oplen) {
|
||||
JSOp op = JSOp(*pc);
|
||||
const JSCodeSpec *cs = &js_CodeSpec[op];
|
||||
oplen = cs->length;
|
||||
if (oplen < 0)
|
||||
oplen = js_GetVariableBytecodeLength(pc);
|
||||
|
||||
if (op == JSOP_INDEXBASE)
|
||||
indexBase = GET_INDEXBASE(pc);
|
||||
else if (op == JSOP_INDEXBASE1 || op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3)
|
||||
indexBase = (op - JSOP_INDEXBASE1 + 1) << 16;
|
||||
else if (op == JSOP_RESETBASE || op == JSOP_RESETBASE0)
|
||||
indexBase = 0;
|
||||
else if (op == JSOP_ENTERBLOCK)
|
||||
blockChain = script->getObject(indexBase + GET_INDEX(pc));
|
||||
else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR)
|
||||
blockChain = blockChain->getStaticBlockScopeChain();
|
||||
else if (op == JSOP_BLOCKCHAIN)
|
||||
blockChain = script->getObject(indexBase + GET_INDEX(pc));
|
||||
else if (op == JSOP_NULLBLOCKCHAIN)
|
||||
blockChain = NULL;
|
||||
}
|
||||
|
||||
return blockChain;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function computes the current blockChain, but only in
|
||||
* the special case where a BLOCKCHAIN or NULLBLOCKCHAIN
|
||||
* instruction appears immediately after the current PC.
|
||||
* We ensure this happens for a few important ops like DEFFUN.
|
||||
* |oplen| is the length of opcode at the current PC.
|
||||
*/
|
||||
JSObject *
|
||||
js::GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen)
|
||||
{
|
||||
/* Assume that we're in a script frame. */
|
||||
jsbytecode *pc = fp->pcQuadratic(cx->stack);
|
||||
JS_ASSERT(JSOp(*pc) == op);
|
||||
|
||||
pc += oplen;
|
||||
op = JSOp(*pc);
|
||||
|
||||
/* The fast paths assume no JSOP_RESETBASE/INDEXBASE noise. */
|
||||
if (op == JSOP_NULLBLOCKCHAIN)
|
||||
return NULL;
|
||||
if (op == JSOP_BLOCKCHAIN)
|
||||
return fp->script()->getObject(GET_INDEX(pc));
|
||||
|
||||
return GetBlockChain(cx, fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't determine in advance which local variables can live on the stack and
|
||||
* be freed when their dynamic scope ends, and which will be closed over and
|
||||
|
@ -255,10 +136,10 @@ js::GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen)
|
|||
* This lazy cloning is implemented in GetScopeChain, which is also used in
|
||||
* some other cases --- entering 'with' blocks, for example.
|
||||
*/
|
||||
static JSObject *
|
||||
GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain)
|
||||
JSObject *
|
||||
js::GetScopeChain(JSContext *cx, StackFrame *fp)
|
||||
{
|
||||
JSObject *sharedBlock = blockChain;
|
||||
JSObject *sharedBlock = fp->maybeBlockChain();
|
||||
|
||||
if (!sharedBlock) {
|
||||
/*
|
||||
|
@ -341,7 +222,7 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain)
|
|||
JSObject *newChild = innermostNewChild;
|
||||
for (;;) {
|
||||
JS_ASSERT(newChild->getProto() == sharedBlock);
|
||||
sharedBlock = sharedBlock->getStaticBlockScopeChain();
|
||||
sharedBlock = sharedBlock->staticBlockScopeChain();
|
||||
|
||||
/* Sometimes limitBlock will be NULL, so check that first. */
|
||||
if (sharedBlock == limitBlock || !sharedBlock)
|
||||
|
@ -375,15 +256,34 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain)
|
|||
}
|
||||
|
||||
JSObject *
|
||||
js::GetScopeChain(JSContext *cx, StackFrame *fp)
|
||||
js::GetScopeChain(JSContext *cx)
|
||||
{
|
||||
return GetScopeChainFull(cx, fp, GetBlockChain(cx, fp));
|
||||
}
|
||||
/*
|
||||
* Note: we don't need to expand inline frames here, because frames are
|
||||
* only inlined when the caller and callee share the same scope chain.
|
||||
*/
|
||||
StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
|
||||
if (!fp) {
|
||||
/*
|
||||
* There is no code active on this context. In place of an actual
|
||||
* scope chain, use the context's global object, which is set in
|
||||
* js_InitFunctionAndObjectClasses, and which represents the default
|
||||
* scope chain for the embedding. See also js_FindClassObject.
|
||||
*
|
||||
* For embeddings that use the inner and outer object hooks, the inner
|
||||
* object represents the ultimate global object, with the outer object
|
||||
* acting as a stand-in.
|
||||
*/
|
||||
JSObject *obj = cx->globalObject;
|
||||
if (!obj) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js::GetScopeChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen)
|
||||
{
|
||||
return GetScopeChainFull(cx, fp, GetBlockChainFast(cx, fp, op, oplen));
|
||||
OBJ_TO_INNER_OBJECT(cx, obj);
|
||||
return obj;
|
||||
}
|
||||
return GetScopeChain(cx, fp);
|
||||
}
|
||||
|
||||
/* Some objects (e.g., With) delegate 'this' to another object. */
|
||||
|
@ -1162,7 +1062,7 @@ js::ValueToId(JSContext *cx, const Value &v, jsid *idp)
|
|||
* of the with block with sp + stackIndex.
|
||||
*/
|
||||
static bool
|
||||
EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen)
|
||||
EnterWith(JSContext *cx, jsint stackIndex)
|
||||
{
|
||||
StackFrame *fp = cx->fp();
|
||||
Value *sp = cx->regs().sp;
|
||||
|
@ -1179,7 +1079,7 @@ EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen)
|
|||
sp[-1].setObject(*obj);
|
||||
}
|
||||
|
||||
JSObject *parent = GetScopeChainFast(cx, fp, op, oplen);
|
||||
JSObject *parent = GetScopeChain(cx, fp);
|
||||
if (!parent)
|
||||
return JS_FALSE;
|
||||
|
||||
|
@ -1228,6 +1128,15 @@ js::UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind)
|
|||
JS_ASSERT(cx->fp()->base() + stackDepth <= cx->regs().sp);
|
||||
|
||||
StackFrame *fp = cx->fp();
|
||||
JSObject *obj = fp->maybeBlockChain();
|
||||
while (obj) {
|
||||
JS_ASSERT(obj->isStaticBlock());
|
||||
if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
|
||||
break;
|
||||
obj = obj->staticBlockScopeChain();
|
||||
}
|
||||
fp->setBlockChain(obj);
|
||||
|
||||
for (;;) {
|
||||
JSObject &scopeChain = fp->scopeChain();
|
||||
if (!IsActiveWithOrBlock(cx, scopeChain, stackDepth))
|
||||
|
@ -1949,13 +1858,14 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
|
|||
ADD_EMPTY_CASE(JSOP_NOP)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED0)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED1)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED2)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED3)
|
||||
ADD_EMPTY_CASE(JSOP_CONDSWITCH)
|
||||
ADD_EMPTY_CASE(JSOP_TRY)
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
ADD_EMPTY_CASE(JSOP_STARTXML)
|
||||
ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
|
||||
#endif
|
||||
ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN)
|
||||
ADD_EMPTY_CASE(JSOP_LOOPHEAD)
|
||||
END_EMPTY_CASES
|
||||
|
||||
|
@ -2001,9 +1911,6 @@ check_backedge:
|
|||
BEGIN_CASE(JSOP_LINENO)
|
||||
END_CASE(JSOP_LINENO)
|
||||
|
||||
BEGIN_CASE(JSOP_BLOCKCHAIN)
|
||||
END_CASE(JSOP_BLOCKCHAIN)
|
||||
|
||||
BEGIN_CASE(JSOP_UNDEFINED)
|
||||
PUSH_UNDEFINED();
|
||||
END_CASE(JSOP_UNDEFINED)
|
||||
|
@ -2017,7 +1924,7 @@ BEGIN_CASE(JSOP_POPN)
|
|||
regs.sp -= GET_UINT16(regs.pc);
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(regs.fp()->base() <= regs.sp);
|
||||
JSObject *obj = GetBlockChain(cx, regs.fp());
|
||||
JSObject *obj = regs.fp()->maybeBlockChain();
|
||||
JS_ASSERT_IF(obj,
|
||||
OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
|
||||
<= (size_t) (regs.sp - regs.fp()->base()));
|
||||
|
@ -2040,7 +1947,7 @@ BEGIN_CASE(JSOP_POPV)
|
|||
END_CASE(JSOP_POPV)
|
||||
|
||||
BEGIN_CASE(JSOP_ENTERWITH)
|
||||
if (!EnterWith(cx, -1, JSOP_ENTERWITH, JSOP_ENTERWITH_LENGTH))
|
||||
if (!EnterWith(cx, -1))
|
||||
goto error;
|
||||
|
||||
/*
|
||||
|
@ -2078,6 +1985,7 @@ BEGIN_CASE(JSOP_STOP)
|
|||
if (entryFrame != regs.fp())
|
||||
inline_return:
|
||||
{
|
||||
JS_ASSERT(!regs.fp()->hasBlockChain());
|
||||
JS_ASSERT(!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0));
|
||||
|
||||
if (cx->compartment->debugMode())
|
||||
|
@ -4064,7 +3972,7 @@ BEGIN_CASE(JSOP_DEFFUN)
|
|||
} else {
|
||||
JS_ASSERT(!fun->isFlatClosure());
|
||||
|
||||
obj2 = GetScopeChainFast(cx, regs.fp(), JSOP_DEFFUN, JSOP_DEFFUN_LENGTH);
|
||||
obj2 = GetScopeChain(cx, regs.fp());
|
||||
if (!obj2)
|
||||
goto error;
|
||||
}
|
||||
|
@ -4163,7 +4071,7 @@ BEGIN_CASE(JSOP_DEFFUN_FC)
|
|||
JSFunction *fun;
|
||||
LOAD_FUNCTION(0);
|
||||
|
||||
JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_DEFFUN_FC, JSOP_DEFFUN_FC_LENGTH);
|
||||
JSObject *obj = js_NewFlatClosure(cx, fun);
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
||||
|
@ -4207,8 +4115,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
|
|||
if (fun->isNullClosure()) {
|
||||
parent = ®s.fp()->scopeChain();
|
||||
} else {
|
||||
parent = GetScopeChainFast(cx, regs.fp(), JSOP_DEFLOCALFUN,
|
||||
JSOP_DEFLOCALFUN_LENGTH);
|
||||
parent = GetScopeChain(cx, regs.fp());
|
||||
if (!parent)
|
||||
goto error;
|
||||
}
|
||||
|
@ -4228,7 +4135,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
|
|||
JSFunction *fun;
|
||||
LOAD_FUNCTION(SLOTNO_LEN);
|
||||
|
||||
JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH);
|
||||
JSObject *obj = js_NewFlatClosure(cx, fun);
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
||||
|
@ -4251,7 +4158,7 @@ BEGIN_CASE(JSOP_LAMBDA)
|
|||
parent = ®s.fp()->scopeChain();
|
||||
|
||||
if (fun->joinable()) {
|
||||
jsbytecode *pc2 = AdvanceOverBlockchainOp(regs.pc + JSOP_LAMBDA_LENGTH);
|
||||
jsbytecode *pc2 = regs.pc + JSOP_LAMBDA_LENGTH;
|
||||
JSOp op2 = JSOp(*pc2);
|
||||
|
||||
/*
|
||||
|
@ -4266,7 +4173,7 @@ BEGIN_CASE(JSOP_LAMBDA)
|
|||
JSObject *obj2 = &lref.toObject();
|
||||
JS_ASSERT(obj2->isObject());
|
||||
#endif
|
||||
JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
|
||||
JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(JSOP_LAMBDA_LENGTH)));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -4277,7 +4184,7 @@ BEGIN_CASE(JSOP_LAMBDA)
|
|||
#endif
|
||||
const Value &lref = regs.sp[-1];
|
||||
if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
|
||||
JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
|
||||
JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(JSOP_LAMBDA_LENGTH)));
|
||||
break;
|
||||
}
|
||||
} else if (op2 == JSOP_CALL) {
|
||||
|
@ -4315,7 +4222,7 @@ BEGIN_CASE(JSOP_LAMBDA)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
parent = GetScopeChainFast(cx, regs.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
|
||||
parent = GetScopeChain(cx, regs.fp());
|
||||
if (!parent)
|
||||
goto error;
|
||||
}
|
||||
|
@ -4337,7 +4244,7 @@ BEGIN_CASE(JSOP_LAMBDA_FC)
|
|||
JSFunction *fun;
|
||||
LOAD_FUNCTION(0);
|
||||
|
||||
JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
|
||||
JSObject *obj = js_NewFlatClosure(cx, fun);
|
||||
if (!obj)
|
||||
goto error;
|
||||
JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
|
||||
|
@ -5079,7 +4986,7 @@ BEGIN_CASE(JSOP_ENDFILTER)
|
|||
* temporaries.
|
||||
*/
|
||||
JS_ASSERT(IsXML(regs.sp[-1]));
|
||||
if (!EnterWith(cx, -2, JSOP_ENDFILTER, JSOP_ENDFILTER_LENGTH))
|
||||
if (!EnterWith(cx, -2))
|
||||
goto error;
|
||||
regs.sp--;
|
||||
len = GET_JUMP_OFFSET(regs.pc);
|
||||
|
@ -5214,6 +5121,8 @@ BEGIN_CASE(JSOP_ENTERBLOCK)
|
|||
regs.sp = vp;
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(regs.fp()->maybeBlockChain() == obj->staticBlockScopeChain());
|
||||
|
||||
/*
|
||||
* The young end of fp->scopeChain may omit blocks if we haven't closed
|
||||
* over them, but if there are any closure blocks on fp->scopeChain, they'd
|
||||
|
@ -5233,17 +5142,17 @@ BEGIN_CASE(JSOP_ENTERBLOCK)
|
|||
JS_ASSERT(parent);
|
||||
}
|
||||
#endif
|
||||
|
||||
regs.fp()->setBlockChain(obj);
|
||||
}
|
||||
END_CASE(JSOP_ENTERBLOCK)
|
||||
|
||||
BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
|
||||
BEGIN_CASE(JSOP_LEAVEBLOCK)
|
||||
{
|
||||
JSObject *blockChain;
|
||||
LOAD_OBJECT(UINT16_LEN, blockChain);
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(blockChain->isStaticBlock());
|
||||
uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain);
|
||||
JS_ASSERT(regs.fp()->blockChain().isStaticBlock());
|
||||
uintN blockDepth = OBJ_BLOCK_DEPTH(cx, ®s.fp()->blockChain());
|
||||
JS_ASSERT(blockDepth <= StackDepth(script));
|
||||
#endif
|
||||
/*
|
||||
|
@ -5252,12 +5161,15 @@ BEGIN_CASE(JSOP_LEAVEBLOCK)
|
|||
* the stack into the clone, and pop it off the chain.
|
||||
*/
|
||||
JSObject &obj = regs.fp()->scopeChain();
|
||||
if (obj.getProto() == blockChain) {
|
||||
if (obj.getProto() == ®s.fp()->blockChain()) {
|
||||
JS_ASSERT(obj.isClonedBlock());
|
||||
if (!js_PutBlockObject(cx, JS_TRUE))
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Pop the block chain, too. */
|
||||
regs.fp()->setBlockChain(regs.fp()->blockChain().staticBlockScopeChain());
|
||||
|
||||
/* Move the result of the expression to the new topmost stack slot. */
|
||||
Value *vp = NULL; /* silence GCC warnings */
|
||||
if (op == JSOP_LEAVEBLOCKEXPR)
|
||||
|
@ -5478,6 +5390,8 @@ END_CASE(JSOP_ARRAYPUSH)
|
|||
|
||||
switch (tn->kind) {
|
||||
case JSTRY_CATCH:
|
||||
JS_ASSERT(*regs.pc == JSOP_ENTERBLOCK);
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
/* Catch cannot intercept the closing of a generator. */
|
||||
if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
|
||||
|
@ -5565,9 +5479,10 @@ END_CASE(JSOP_ARRAYPUSH)
|
|||
* frame pc.
|
||||
*/
|
||||
JS_ASSERT(entryFrame == regs.fp());
|
||||
|
||||
JS_ASSERT_IF(!regs.fp()->isGeneratorFrame(),
|
||||
!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0));
|
||||
if (!regs.fp()->isGeneratorFrame()) {
|
||||
JS_ASSERT(!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0));
|
||||
JS_ASSERT(!regs.fp()->hasBlockChain());
|
||||
}
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
/*
|
||||
|
|
|
@ -51,15 +51,6 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
extern JSObject *
|
||||
GetBlockChain(JSContext *cx, StackFrame *fp);
|
||||
|
||||
extern JSObject *
|
||||
GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen);
|
||||
|
||||
extern JSObject *
|
||||
GetScopeChain(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Refresh and return fp->scopeChain. It may be stale if block scopes are
|
||||
* active but not yet reflected by objects in the scope chain. If a block
|
||||
|
@ -67,11 +58,12 @@ GetScopeChain(JSContext *cx);
|
|||
* dynamically scoped construct, then compile-time block scope at fp->blocks
|
||||
* must reflect at runtime.
|
||||
*/
|
||||
extern JSObject *
|
||||
GetScopeChain(JSContext *cx, StackFrame *fp);
|
||||
|
||||
extern JSObject *
|
||||
GetScopeChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen);
|
||||
GetScopeChain(JSContext *cx);
|
||||
|
||||
extern JSObject *
|
||||
GetScopeChain(JSContext *cx, StackFrame *fp);
|
||||
|
||||
/*
|
||||
* ScriptPrologue/ScriptEpilogue must be called in pairs. ScriptPrologue
|
||||
|
|
|
@ -1318,12 +1318,14 @@ DirectEval(JSContext *cx, const CallArgs &args)
|
|||
|
||||
AutoFunctionCallProbe callProbe(cx, args.callee().toFunction(), caller->script());
|
||||
|
||||
JSObject *scopeChain =
|
||||
GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH);
|
||||
JSObject *scopeChain = GetScopeChain(cx, caller);
|
||||
if (!scopeChain)
|
||||
return false;
|
||||
|
||||
return scopeChain &&
|
||||
WarnOnTooManyArgs(cx, args) &&
|
||||
EvalKernel(cx, args, DIRECT_EVAL, caller, *scopeChain);
|
||||
if (!WarnOnTooManyArgs(cx, args))
|
||||
return false;
|
||||
|
||||
return EvalKernel(cx, args, DIRECT_EVAL, caller, *scopeChain);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -4134,7 +4136,7 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
|
|||
|
||||
if (xdr->mode == JSXDR_ENCODE) {
|
||||
obj = *objp;
|
||||
parent = obj->getStaticBlockScopeChain();
|
||||
parent = obj->staticBlockScopeChain();
|
||||
parentId = JSScript::isValidOffset(xdr->script->objectsOffset)
|
||||
? FindObjectIndex(xdr->script->objects(), parent)
|
||||
: NO_PARENT_INDEX;
|
||||
|
@ -7458,6 +7460,7 @@ js_DumpStackFrame(JSContext *cx, StackFrame *start)
|
|||
fprintf(stderr, "\n");
|
||||
}
|
||||
MaybeDumpObject("argsobj", fp->maybeArgsObj());
|
||||
MaybeDumpObject("blockChain", fp->maybeBlockChain());
|
||||
if (!fp->isDummyFrame()) {
|
||||
MaybeDumpValue("this", fp->thisValue());
|
||||
fprintf(stderr, " rval: ");
|
||||
|
|
|
@ -908,7 +908,7 @@ struct JSObject : js::gc::Cell
|
|||
* on scope chains but mirror their structure, and can have a NULL
|
||||
* scope chain.
|
||||
*/
|
||||
inline JSObject *getStaticBlockScopeChain() const;
|
||||
inline JSObject *staticBlockScopeChain() const;
|
||||
inline void setStaticBlockScopeChain(JSObject *obj);
|
||||
|
||||
/* Common fixed slot for the scope chain of internal scope objects. */
|
||||
|
|
|
@ -324,7 +324,7 @@ JSObject::scopeChain() const
|
|||
}
|
||||
|
||||
inline JSObject *
|
||||
JSObject::getStaticBlockScopeChain() const
|
||||
JSObject::staticBlockScopeChain() const
|
||||
{
|
||||
JS_ASSERT(isStaticBlock());
|
||||
return getFixedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
|
||||
|
|
|
@ -2902,14 +2902,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
todo = -2;
|
||||
pc2 = pc + oplen;
|
||||
|
||||
/* Skip a block chain annotation if one appears here. */
|
||||
if (*pc2 == JSOP_NOP) {
|
||||
if (pc2[JSOP_NOP_LENGTH] == JSOP_NULLBLOCKCHAIN)
|
||||
pc2 += JSOP_NOP_LENGTH + JSOP_NULLBLOCKCHAIN_LENGTH;
|
||||
else if (pc2[JSOP_NOP_LENGTH] == JSOP_BLOCKCHAIN)
|
||||
pc2 += JSOP_NOP_LENGTH + JSOP_BLOCKCHAIN_LENGTH;
|
||||
}
|
||||
|
||||
if (*pc2 == JSOP_NOP) {
|
||||
sn = js_GetSrcNote(jp->script, pc2);
|
||||
if (sn) {
|
||||
|
@ -4409,12 +4401,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
* arrange to advance over the call to this lambda.
|
||||
*/
|
||||
pc += len;
|
||||
if (*pc == JSOP_BLOCKCHAIN) {
|
||||
pc += JSOP_BLOCKCHAIN_LENGTH;
|
||||
} else {
|
||||
LOCAL_ASSERT(*pc == JSOP_NULLBLOCKCHAIN);
|
||||
pc += JSOP_NULLBLOCKCHAIN_LENGTH;
|
||||
}
|
||||
LOCAL_ASSERT(*pc == JSOP_UNDEFINED);
|
||||
pc += JSOP_UNDEFINED_LENGTH;
|
||||
LOCAL_ASSERT(*pc == JSOP_CALL);
|
||||
|
|
|
@ -232,7 +232,7 @@ OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE)
|
|||
/* Call a function as a constructor; operand is argc. */
|
||||
OPDEF(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16|JOF_INVOKE|JOF_TYPESET)
|
||||
|
||||
OPDEF(JSOP_UNUSED1, 83, "unused1", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED0, 83, "unused1", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/* Fast get/set ops for function arguments and local variables. */
|
||||
OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME)
|
||||
|
@ -271,7 +271,7 @@ OPDEF(JSOP_DECLOCAL, 102,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL|
|
|||
OPDEF(JSOP_LOCALINC, 103,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3)
|
||||
OPDEF(JSOP_LOCALDEC, 104,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3)
|
||||
|
||||
OPDEF(JSOP_UNUSED0, 105,"unused0", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED1, 105,"unused0", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/* The argument is the offset to the next statement and is used by IonMonkey. */
|
||||
OPDEF(JSOP_LABEL, 106,"label", NULL, 3, 0, 0, 0, JOF_JUMP)
|
||||
|
@ -440,15 +440,8 @@ OPDEF(JSOP_DELDESC, 183,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|J
|
|||
|
||||
OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_CALLOP|JOF_TMPSLOT3)
|
||||
|
||||
/*
|
||||
* 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, 185,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
|
||||
OPDEF(JSOP_NULLBLOCKCHAIN,186,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED2, 185,"unused1", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED3, 186,"unused2", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Opcode to hold 24-bit immediate integer operands.
|
||||
|
@ -496,7 +489,7 @@ OPDEF(JSOP_TYPEOFEXPR, 197,"typeofexpr", NULL, 1, 1, 1, 15, JOF_BYTE|J
|
|||
* Block-local scope support.
|
||||
*/
|
||||
OPDEF(JSOP_ENTERBLOCK, 198,"enterblock", NULL, 3, 0, -1, 0, JOF_OBJECT)
|
||||
OPDEF(JSOP_LEAVEBLOCK, 199,"leaveblock", NULL, 5, -1, 0, 0, JOF_UINT16)
|
||||
OPDEF(JSOP_LEAVEBLOCK, 199,"leaveblock", NULL, 3, -1, 0, 0, JOF_UINT16)
|
||||
|
||||
/* Jump to target if top of stack value isn't callable. */
|
||||
OPDEF(JSOP_IFCANTCALLTOP, 200,"ifcantcalltop",NULL, 3, 1, 1, 0, JOF_JUMP|JOF_DETECTING)
|
||||
|
@ -525,7 +518,7 @@ OPDEF(JSOP_ENUMCONSTELEM, 206,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|J
|
|||
* Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals,
|
||||
* which must be moved down when the block pops.
|
||||
*/
|
||||
OPDEF(JSOP_LEAVEBLOCKEXPR,207,"leaveblockexpr",NULL, 5, -1, 1, 3, JOF_UINT16)
|
||||
OPDEF(JSOP_LEAVEBLOCKEXPR,207,"leaveblockexpr",NULL, 3, -1, 1, 3, JOF_UINT16)
|
||||
\
|
||||
/*
|
||||
* Optimize atom segments 1-3. These must be followed by JSOP_RESETBASE0 after
|
||||
|
|
|
@ -57,20 +57,6 @@ class BytecodeRange {
|
|||
jsbytecode *pc, *end;
|
||||
};
|
||||
|
||||
/*
|
||||
* Warning: this does not skip JSOP_RESETBASE* or JSOP_INDEXBASE* ops, so it is
|
||||
* useful only when checking for optimization opportunities.
|
||||
*/
|
||||
JS_ALWAYS_INLINE jsbytecode *
|
||||
AdvanceOverBlockchainOp(jsbytecode *pc)
|
||||
{
|
||||
if (*pc == JSOP_NULLBLOCKCHAIN)
|
||||
return pc + JSOP_NULLBLOCKCHAIN_LENGTH;
|
||||
if (*pc == JSOP_BLOCKCHAIN)
|
||||
return pc + JSOP_BLOCKCHAIN_LENGTH;
|
||||
return pc;
|
||||
}
|
||||
|
||||
class SrcNoteLineScanner
|
||||
{
|
||||
/* offset of the current JSOp in the bytecode */
|
||||
|
|
|
@ -2470,12 +2470,6 @@ mjit::Compiler::generateMethod()
|
|||
frame.popn(2);
|
||||
END_CASE(JSOP_ENUMELEM)
|
||||
|
||||
BEGIN_CASE(JSOP_BLOCKCHAIN)
|
||||
END_CASE(JSOP_BLOCKCHAIN)
|
||||
|
||||
BEGIN_CASE(JSOP_NULLBLOCKCHAIN)
|
||||
END_CASE(JSOP_NULLBLOCKCHAIN)
|
||||
|
||||
BEGIN_CASE(JSOP_CONDSWITCH)
|
||||
/* No-op for the decompiler. */
|
||||
END_CASE(JSOP_CONDSWITCH)
|
||||
|
@ -2544,7 +2538,7 @@ mjit::Compiler::generateMethod()
|
|||
|
||||
jsbytecode *pc2 = NULL;
|
||||
if (fun->joinable()) {
|
||||
pc2 = AdvanceOverBlockchainOp(PC + JSOP_LAMBDA_LENGTH);
|
||||
pc2 = PC + JSOP_LAMBDA_LENGTH;
|
||||
JSOp next = JSOp(*pc2);
|
||||
|
||||
if (next == JSOP_INITMETHOD) {
|
||||
|
@ -2568,9 +2562,6 @@ mjit::Compiler::generateMethod()
|
|||
prepareStubCall(Uses(uses));
|
||||
masm.move(ImmPtr(fun), Registers::ArgReg1);
|
||||
|
||||
if (stub != stubs::Lambda)
|
||||
masm.storePtr(ImmPtr(pc2), FrameAddress(offsetof(VMFrame, scratch)));
|
||||
|
||||
INLINE_STUBCALL(stub, REJOIN_PUSH_OBJECT);
|
||||
|
||||
frame.takeReg(Registers::ReturnReg);
|
||||
|
@ -7156,9 +7147,7 @@ mjit::Compiler::leaveBlock()
|
|||
* PutBlockObject, and do away with the muckiness in PutBlockObject.
|
||||
*/
|
||||
uint32_t n = js_GetVariableStackUses(JSOP_LEAVEBLOCK, PC);
|
||||
JSObject *obj = script->getObject(fullAtomIndex(PC + UINT16_LEN));
|
||||
prepareStubCall(Uses(n));
|
||||
masm.move(ImmPtr(obj), Registers::ArgReg1);
|
||||
INLINE_STUBCALL(stubs::LeaveBlock, REJOIN_NONE);
|
||||
frame.leaveBlock(n);
|
||||
}
|
||||
|
|
|
@ -177,6 +177,7 @@ InlineReturn(VMFrame &f)
|
|||
{
|
||||
JS_ASSERT(f.fp() != f.entryfp);
|
||||
JS_ASSERT(!IsActiveWithOrBlock(f.cx, f.fp()->scopeChain(), 0));
|
||||
JS_ASSERT(!f.fp()->hasBlockChain());
|
||||
f.cx->stack.popInlineFrame(f.regs);
|
||||
|
||||
DebugOnly<JSOp> op = JSOp(*f.regs.pc);
|
||||
|
@ -638,6 +639,7 @@ js_InternalThrow(VMFrame &f)
|
|||
cx->clearPendingException();
|
||||
cx->regs().sp++;
|
||||
cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH;
|
||||
cx->regs().fp()->setBlockChain(obj);
|
||||
}
|
||||
|
||||
*f.oldregs = f.regs;
|
||||
|
|
|
@ -673,7 +673,7 @@ stubs::DefFun(VMFrame &f, JSFunction *fun)
|
|||
} else {
|
||||
JS_ASSERT(!fun->isFlatClosure());
|
||||
|
||||
obj2 = GetScopeChainFast(cx, fp, JSOP_DEFFUN, JSOP_DEFFUN_LENGTH);
|
||||
obj2 = GetScopeChain(cx, fp);
|
||||
if (!obj2)
|
||||
THROW();
|
||||
}
|
||||
|
@ -1333,8 +1333,7 @@ stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
|
|||
if (fun->isNullClosure()) {
|
||||
parent = &f.fp()->scopeChain();
|
||||
} else {
|
||||
parent = GetScopeChainFast(f.cx, f.fp(), JSOP_DEFLOCALFUN,
|
||||
JSOP_DEFLOCALFUN_LENGTH);
|
||||
parent = GetScopeChain(f.cx, f.fp());
|
||||
if (!parent)
|
||||
THROWV(NULL);
|
||||
}
|
||||
|
@ -1350,7 +1349,7 @@ stubs::DefLocalFun(VMFrame &f, JSFunction *fun)
|
|||
JSObject * JS_FASTCALL
|
||||
stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun)
|
||||
{
|
||||
JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH);
|
||||
JSObject *obj = js_NewFlatClosure(f.cx, fun);
|
||||
if (!obj)
|
||||
THROWV(NULL);
|
||||
return obj;
|
||||
|
@ -1376,8 +1375,8 @@ stubs::RegExp(VMFrame &f, JSObject *regex)
|
|||
JSObject * JS_FASTCALL
|
||||
stubs::LambdaJoinableForInit(VMFrame &f, JSFunction *fun)
|
||||
{
|
||||
DebugOnly<jsbytecode*> nextpc = (jsbytecode *) f.scratch;
|
||||
JS_ASSERT(fun->joinable());
|
||||
DebugOnly<jsbytecode*> nextpc = f.regs.pc + JSOP_LAMBDA_LENGTH;
|
||||
JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_SLOTNO(nextpc)));
|
||||
return fun;
|
||||
}
|
||||
|
@ -1386,9 +1385,9 @@ JSObject * JS_FASTCALL
|
|||
stubs::LambdaJoinableForSet(VMFrame &f, JSFunction *fun)
|
||||
{
|
||||
JS_ASSERT(fun->joinable());
|
||||
DebugOnly<jsbytecode*> nextpc = (jsbytecode *) f.scratch;
|
||||
const Value &lref = f.regs.sp[-1];
|
||||
if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
|
||||
DebugOnly<jsbytecode*> nextpc = f.regs.pc + JSOP_LAMBDA_LENGTH;
|
||||
JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_SLOTNO(nextpc)));
|
||||
return fun;
|
||||
}
|
||||
|
@ -1399,7 +1398,6 @@ JSObject * JS_FASTCALL
|
|||
stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun)
|
||||
{
|
||||
JS_ASSERT(fun->joinable());
|
||||
jsbytecode *nextpc = (jsbytecode *) f.scratch;
|
||||
|
||||
/*
|
||||
* Array.prototype.sort and String.prototype.replace are optimized as if
|
||||
|
@ -1407,7 +1405,7 @@ stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun)
|
|||
* object fun, therefore we don't need to clone that compiler-created
|
||||
* function object for identity/mutation reasons.
|
||||
*/
|
||||
int iargc = GET_ARGC(nextpc);
|
||||
int iargc = GET_ARGC(f.regs.pc + JSOP_LAMBDA_LENGTH);
|
||||
|
||||
/*
|
||||
* Note that we have not yet pushed fun as the final argument, so
|
||||
|
@ -1444,7 +1442,7 @@ stubs::Lambda(VMFrame &f, JSFunction *fun)
|
|||
if (fun->isNullClosure()) {
|
||||
parent = &f.fp()->scopeChain();
|
||||
} else {
|
||||
parent = GetScopeChainFast(f.cx, f.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
|
||||
parent = GetScopeChain(f.cx, f.fp());
|
||||
if (!parent)
|
||||
THROWV(NULL);
|
||||
}
|
||||
|
@ -1763,7 +1761,7 @@ stubs::Throw(VMFrame &f)
|
|||
JSObject * JS_FASTCALL
|
||||
stubs::FlatLambda(VMFrame &f, JSFunction *fun)
|
||||
{
|
||||
JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
|
||||
JSObject *obj = js_NewFlatClosure(f.cx, fun);
|
||||
if (!obj)
|
||||
THROWV(NULL);
|
||||
return obj;
|
||||
|
@ -1819,9 +1817,7 @@ void JS_FASTCALL
|
|||
stubs::EnterBlock(VMFrame &f, JSObject *obj)
|
||||
{
|
||||
FrameRegs ®s = f.regs;
|
||||
#ifdef DEBUG
|
||||
StackFrame *fp = f.fp();
|
||||
#endif
|
||||
|
||||
JS_ASSERT(!f.regs.inlined());
|
||||
JS_ASSERT(obj->isStaticBlock());
|
||||
|
@ -1834,6 +1830,7 @@ stubs::EnterBlock(VMFrame &f, JSObject *obj)
|
|||
|
||||
#ifdef DEBUG
|
||||
JSContext *cx = f.cx;
|
||||
JS_ASSERT(fp->maybeBlockChain() == obj->staticBlockScopeChain());
|
||||
|
||||
/*
|
||||
* The young end of fp->scopeChain() may omit blocks if we haven't closed
|
||||
|
@ -1854,17 +1851,19 @@ stubs::EnterBlock(VMFrame &f, JSObject *obj)
|
|||
JS_ASSERT(parent);
|
||||
}
|
||||
#endif
|
||||
|
||||
fp->setBlockChain(obj);
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
stubs::LeaveBlock(VMFrame &f, JSObject *blockChain)
|
||||
stubs::LeaveBlock(VMFrame &f)
|
||||
{
|
||||
JSContext *cx = f.cx;
|
||||
StackFrame *fp = f.fp();
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(blockChain->isStaticBlock());
|
||||
uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain);
|
||||
JS_ASSERT(fp->blockChain().isBlock());
|
||||
uintN blockDepth = OBJ_BLOCK_DEPTH(cx, &fp->blockChain());
|
||||
|
||||
JS_ASSERT(blockDepth <= StackDepth(fp->script()));
|
||||
#endif
|
||||
|
@ -1873,12 +1872,15 @@ stubs::LeaveBlock(VMFrame &f, JSObject *blockChain)
|
|||
* cloned onto fp->scopeChain(), clear its private data, move its locals from
|
||||
* the stack into the clone, and pop it off the chain.
|
||||
*/
|
||||
JSObject *obj = &fp->scopeChain();
|
||||
if (obj->getProto() == blockChain) {
|
||||
JS_ASSERT(obj->isBlock());
|
||||
JSObject &obj = fp->scopeChain();
|
||||
JSObject &blockChain = fp->blockChain();
|
||||
if (obj.getProto() == &blockChain) {
|
||||
JS_ASSERT(obj.isBlock());
|
||||
if (!js_PutBlockObject(cx, JS_TRUE))
|
||||
THROW();
|
||||
}
|
||||
|
||||
fp->setBlockChain(blockChain.staticBlockScopeChain());
|
||||
}
|
||||
|
||||
void * JS_FASTCALL
|
||||
|
|
|
@ -154,7 +154,7 @@ JSObject * JS_FASTCALL LambdaJoinableForNull(VMFrame &f, JSFunction *fun);
|
|||
JSObject * JS_FASTCALL FlatLambda(VMFrame &f, JSFunction *fun);
|
||||
void JS_FASTCALL Arguments(VMFrame &f);
|
||||
void JS_FASTCALL EnterBlock(VMFrame &f, JSObject *obj);
|
||||
void JS_FASTCALL LeaveBlock(VMFrame &f, JSObject *blockChain);
|
||||
void JS_FASTCALL LeaveBlock(VMFrame &f);
|
||||
|
||||
JSBool JS_FASTCALL LessThan(VMFrame &f);
|
||||
JSBool JS_FASTCALL LessEqual(VMFrame &f);
|
||||
|
|
|
@ -154,12 +154,14 @@ StackFrame::initCallFrame(JSContext *cx, JSFunction &callee,
|
|||
JS_ASSERT(script == callee.toFunction()->script());
|
||||
|
||||
/* Initialize stack frame members. */
|
||||
flags_ = FUNCTION | HAS_PREVPC | HAS_SCOPECHAIN | flagsArg;
|
||||
flags_ = FUNCTION | HAS_PREVPC | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | flagsArg;
|
||||
exec.fun = &callee;
|
||||
args.nactual = nactual;
|
||||
scopeChain_ = callee.toFunction()->environment();
|
||||
ncode_ = NULL;
|
||||
initPrev(cx);
|
||||
blockChain_= NULL;
|
||||
JS_ASSERT(!hasBlockChain());
|
||||
JS_ASSERT(!hasHookData());
|
||||
JS_ASSERT(annotation() == NULL);
|
||||
JS_ASSERT(!hasCallObj());
|
||||
|
|
|
@ -78,7 +78,7 @@ StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs
|
|||
* script in the context of another frame and the frame type is determined
|
||||
* by the context.
|
||||
*/
|
||||
flags_ = type | HAS_SCOPECHAIN | HAS_PREVPC;
|
||||
flags_ = type | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | HAS_PREVPC;
|
||||
if (!(flags_ & GLOBAL))
|
||||
flags_ |= (prev->flags_ & (FUNCTION | GLOBAL));
|
||||
|
||||
|
@ -102,6 +102,7 @@ StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs
|
|||
prev_ = prev;
|
||||
prevpc_ = regs ? regs->pc : (jsbytecode *)0xbad;
|
||||
prevInline_ = regs ? regs->inlined() : NULL;
|
||||
blockChain_ = NULL;
|
||||
|
||||
#ifdef DEBUG
|
||||
ncode_ = (void *)0xbad;
|
||||
|
|
|
@ -345,10 +345,11 @@ class StackFrame
|
|||
HAS_RVAL = 0x20000, /* frame has rval_ set */
|
||||
HAS_SCOPECHAIN = 0x40000, /* frame has scopeChain_ set */
|
||||
HAS_PREVPC = 0x80000, /* frame has prevpc_ and prevInline_ set */
|
||||
HAS_BLOCKCHAIN = 0x100000, /* frame has blockChain_ set */
|
||||
|
||||
/* Method JIT state */
|
||||
DOWN_FRAMES_EXPANDED = 0x100000, /* inlining in down frames has been expanded */
|
||||
LOWERED_CALL_APPLY = 0x200000 /* Pushed by a lowered call/apply */
|
||||
DOWN_FRAMES_EXPANDED = 0x400000, /* inlining in down frames has been expanded */
|
||||
LOWERED_CALL_APPLY = 0x800000 /* Pushed by a lowered call/apply */
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -368,6 +369,7 @@ class StackFrame
|
|||
|
||||
/* Lazily initialized */
|
||||
Value rval_; /* return value of the frame */
|
||||
JSObject *blockChain_; /* innermost let block */
|
||||
jsbytecode *prevpc_; /* pc of previous frame*/
|
||||
JSInlinedSite *prevInline_; /* inlined site in previous frame */
|
||||
void *hookData_; /* closure returned by call hook */
|
||||
|
@ -840,6 +842,26 @@ class StackFrame
|
|||
inline void setScopeChainNoCallObj(JSObject &obj);
|
||||
inline void setScopeChainWithOwnCallObj(CallObject &obj);
|
||||
|
||||
/* Block chain */
|
||||
|
||||
bool hasBlockChain() const {
|
||||
return (flags_ & HAS_BLOCKCHAIN) && blockChain_;
|
||||
}
|
||||
|
||||
JSObject *maybeBlockChain() {
|
||||
return (flags_ & HAS_BLOCKCHAIN) ? blockChain_ : NULL;
|
||||
}
|
||||
|
||||
JSObject &blockChain() const {
|
||||
JS_ASSERT(hasBlockChain());
|
||||
return *blockChain_;
|
||||
}
|
||||
|
||||
void setBlockChain(JSObject *obj) {
|
||||
flags_ |= HAS_BLOCKCHAIN;
|
||||
blockChain_ = obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prologue for function frames: make a call object for heavyweight
|
||||
* functions, and maintain type nesting invariants.
|
||||
|
|
|
@ -55,7 +55,7 @@ class GeneratorFrameGuard;
|
|||
enum InitialFrameFlags {
|
||||
INITIAL_NONE = 0,
|
||||
INITIAL_CONSTRUCT = 0x80, /* == StackFrame::CONSTRUCTING, asserted in Stack.h */
|
||||
INITIAL_LOWERED = 0x200000 /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */
|
||||
INITIAL_LOWERED = 0x800000 /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */
|
||||
};
|
||||
|
||||
enum ExecuteType {
|
||||
|
|
Загрузка…
Ссылка в новой задаче