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:
Luke Wagner 2011-10-07 12:02:50 -07:00
Родитель e8ece4c65a
Коммит 0b5000dc38
30 изменённых файлов: 197 добавлений и 499 удалений

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

@ -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, &noteIndex))
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 = &regs.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 = &regs.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, &regs.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() == &regs.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 &regs = 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 {