зеркало из https://github.com/mozilla/gecko-dev.git
Bug 927782 - Part 11: Optimize block scopes without aliased locals. r=luke
This commit is contained in:
Родитель
7da22095a0
Коммит
44bd6e4790
|
@ -141,15 +141,6 @@ EmitCheck(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
|
|||
return offset;
|
||||
}
|
||||
|
||||
static StaticBlockObject &
|
||||
LastBlockAdded(BytecodeEmitter *bce, jsbytecode *pc)
|
||||
{
|
||||
DebugOnly<uint32_t> index = GET_UINT32_INDEX(pc);
|
||||
JS_ASSERT(index < bce->objectList.length);
|
||||
JS_ASSERT(index == bce->objectList.length - 1);
|
||||
return bce->objectList.lastbox->object->as<StaticBlockObject>();
|
||||
}
|
||||
|
||||
static void
|
||||
UpdateDepth(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
|
||||
{
|
||||
|
@ -168,26 +159,8 @@ UpdateDepth(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
|
|||
bce->maxStackDepth = depth;
|
||||
}
|
||||
|
||||
/*
|
||||
* Specially handle any case in which StackUses or StackDefs would call
|
||||
* NumBlockSlots, since that requires a well-formed script. This allows us
|
||||
* to safely pass nullptr as the 'script' parameter to StackUses and
|
||||
* StackDefs.
|
||||
*/
|
||||
int nuses, ndefs;
|
||||
if (op == JSOP_ENTERBLOCK) {
|
||||
nuses = 0;
|
||||
ndefs = LastBlockAdded(bce, pc).slotCount();
|
||||
} else if (op == JSOP_ENTERLET0) {
|
||||
nuses = ndefs = LastBlockAdded(bce, pc).slotCount();
|
||||
} else if (op == JSOP_ENTERLET1) {
|
||||
nuses = ndefs = LastBlockAdded(bce, pc).slotCount() + 1;
|
||||
} else if (op == JSOP_ENTERLET2) {
|
||||
nuses = ndefs = LastBlockAdded(bce, pc).slotCount() + 2;
|
||||
} else {
|
||||
nuses = StackUses(nullptr, pc);
|
||||
ndefs = StackDefs(nullptr, pc);
|
||||
}
|
||||
int nuses = StackUses(nullptr, pc);
|
||||
int ndefs = StackDefs(nullptr, pc);
|
||||
|
||||
bce->stackDepth -= nuses;
|
||||
JS_ASSERT(bce->stackDepth >= 0);
|
||||
|
@ -568,7 +541,10 @@ class NonLocalExitScope {
|
|||
return false;
|
||||
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
|
||||
return false;
|
||||
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObj.slotCount());
|
||||
if (blockObj.needsClone()) {
|
||||
if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
|
||||
return false;
|
||||
}
|
||||
openScopeIndex = bce->blockScopeList.length() - 1;
|
||||
return true;
|
||||
}
|
||||
|
@ -623,11 +599,11 @@ NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE *toStmt)
|
|||
}
|
||||
|
||||
if (stmt->isBlockScope) {
|
||||
FLUSH_POPS();
|
||||
JS_ASSERT(stmt->blockObj);
|
||||
StaticBlockObject &blockObj = *stmt->blockObj;
|
||||
if (!popScopeForNonLocalExit(blockObj, stmt->blockScopeIndex))
|
||||
return false;
|
||||
npops += blockObj.slotCount();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -773,6 +749,55 @@ ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, StaticBlockObjec
|
|||
static bool
|
||||
EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmitter *bce);
|
||||
|
||||
// ~ Block Scopes ~
|
||||
//
|
||||
// A block scope is a region of a script with an additional set of named
|
||||
// variables. Those variables may be local and thus accessible directly from
|
||||
// the stack, or "aliased" and only accessible through the scope chain.
|
||||
//
|
||||
// A block scope may or may not have a corresponding link on the scope chain.
|
||||
// If no variable declared in the scope is "aliased", then no scope chain node
|
||||
// is allocated.
|
||||
//
|
||||
// To help debuggers, the bytecode emitter arranges to record the PC ranges
|
||||
// comprehended by a block scope, and ultimately attach them to the JSScript.
|
||||
// An element in the "block scope array" specifies the PC range, and links to a
|
||||
// StaticBlockObject in the object list of the script. That block is linked to
|
||||
// the previous block in the scope, if any. The static block chain at any
|
||||
// pre-retire PC can be retrieved using JSScript::getBlockScope(jsbytecode *pc).
|
||||
//
|
||||
// When PUSHBLOCKSCOPE is executed, it assumes that the block's locals are
|
||||
// already on the stack. Initial values of "aliased" locals are copied from the
|
||||
// stack to the ClonedBlockObject, and no further access is made to the stack
|
||||
// slot.
|
||||
//
|
||||
// Likewise after leaving a POPBLOCKSCOPE, we will need to emit code to pop the
|
||||
// stack values.
|
||||
//
|
||||
// Finally, to assist the debugger, we also emit a DEBUGLEAVEBLOCK opcode before
|
||||
// POPBLOCKSCOPE in all cases -- even if the block has no aliased locals. This
|
||||
// allows DebugScopes to invalidate any association between a debugger scope
|
||||
// object, which can proxy access to unaliased stack locals, and the actual live
|
||||
// frame. In normal, non-debug mode, this opcode does not cause any baseline
|
||||
// code to be emitted.
|
||||
//
|
||||
// In this function "extraSlots" indicates the number of slots that are
|
||||
// "floating" on the stack above the scope's slots. This will only be nonzero
|
||||
// in the case of for-let-in and for-let-of loops, where loop iterator state
|
||||
// floats above the block scopes. It would be nice to fix this eventually so
|
||||
// that loop iterator state gets assigned to block-scoped fp-addressable
|
||||
// temporaries, instead of being addressable only via the sp. This would also
|
||||
// make generators more efficient, as the loop state could be heap-allocated, so
|
||||
// that the value stack would likely be empty at yield points inside for-of /
|
||||
// for-in loops.
|
||||
//
|
||||
// Summary: Enter block scopes with EnterBlockScope. It will emit
|
||||
// PUSHBLOCKSCOPE if needed. Leave them with LeaveBlockScope, which will emit
|
||||
// DEBUGLEAVEBLOCK and may emit POPBLOCKSCOPE. Pass EnterBlockScope a fresh
|
||||
// StmtInfoBCE object, and pass that same object to the corresponding
|
||||
// LeaveBlockScope. Push locals before entering a scope, and pop them
|
||||
// afterwards. Brush your teeth, and clean behind your ears!
|
||||
//
|
||||
static bool
|
||||
EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, ObjectBox *objbox,
|
||||
unsigned extraSlots)
|
||||
|
@ -792,19 +817,13 @@ EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, O
|
|||
JS_ASSERT(depth >= 0);
|
||||
blockObj.setStackDepth(depth);
|
||||
|
||||
JSOp op;
|
||||
switch (extraSlots) {
|
||||
case 0: op = JSOP_ENTERLET0; break;
|
||||
case 1: op = JSOP_ENTERLET1; break;
|
||||
case 2: op = JSOP_ENTERLET2; break;
|
||||
default: MOZ_ASSUME_UNREACHABLE("unexpected extraSlots");
|
||||
}
|
||||
|
||||
if (!ComputeAliasedSlots(cx, bce, blockObj))
|
||||
return false;
|
||||
|
||||
if (!EmitInternedObjectOp(cx, scopeObjectIndex, op, bce))
|
||||
return false;
|
||||
if (blockObj.needsClone()) {
|
||||
if (!EmitInternedObjectOp(cx, scopeObjectIndex, JSOP_PUSHBLOCKSCOPE, bce))
|
||||
return false;
|
||||
}
|
||||
|
||||
stmt->blockScopeIndex = bce->blockScopeList.length();
|
||||
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
|
||||
|
@ -837,7 +856,7 @@ PopStatementBCE(ExclusiveContext *cx, BytecodeEmitter *bce)
|
|||
}
|
||||
|
||||
static bool
|
||||
LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
|
||||
LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce)
|
||||
{
|
||||
StmtInfoBCE *stmt = bce->topStmt;
|
||||
JS_ASSERT(stmt->isBlockScope);
|
||||
|
@ -852,7 +871,7 @@ LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
|
|||
JS_ASSERT(blockObj == bce->blockChain);
|
||||
#endif
|
||||
|
||||
uint32_t slotCount = bce->blockChain->slotCount();
|
||||
bool blockOnChain = bce->blockChain->needsClone();
|
||||
|
||||
if (!PopStatementBCE(cx, bce))
|
||||
return false;
|
||||
|
@ -862,8 +881,10 @@ LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
|
|||
|
||||
bce->blockScopeList.recordEnd(blockScopeIndex, bce->offset());
|
||||
|
||||
JS_ASSERT(op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR);
|
||||
EMIT_UINT16_IMM_OP(op, slotCount);
|
||||
if (blockOnChain) {
|
||||
if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2684,10 +2705,10 @@ EmitSwitch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (pn->pn_right->isKind(PNK_LEXICALSCOPE)) {
|
||||
if (!LeaveBlockScope(cx, bce, JSOP_LEAVEBLOCK))
|
||||
if (!LeaveBlockScope(cx, bce))
|
||||
return false;
|
||||
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObj->slotCount());
|
||||
} else {
|
||||
if (!PopStatementBCE(cx, bce))
|
||||
return false;
|
||||
|
@ -3983,7 +4004,8 @@ EmitTry(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
if (ParseNode *pn2 = pn->pn_kid2) {
|
||||
// The emitted code for a catch block looks like:
|
||||
//
|
||||
// enterblock
|
||||
// undefined... as many as there are locals in the catch block
|
||||
// [pushblockscope] only if any local aliased
|
||||
// exception
|
||||
// if there is a catchguard:
|
||||
// dup
|
||||
|
@ -3991,14 +4013,16 @@ EmitTry(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
// if there is a catchguard:
|
||||
// < catchguard code >
|
||||
// ifne POST
|
||||
// throwing pop exception to cx->exception
|
||||
// debugleaveblock
|
||||
// leaveblock
|
||||
// [popblockscope] only if any local aliased
|
||||
// popnv <num block locals> leave exception on top
|
||||
// throwing pop exception to cx->exception
|
||||
// goto <next catch block>
|
||||
// POST: pop
|
||||
// < catch block contents >
|
||||
// debugleaveblock
|
||||
// leaveblock
|
||||
// [popblockscope] only if any local aliased
|
||||
// popn <num block locals>
|
||||
// goto <end of catch blocks> non-local; finally applies
|
||||
//
|
||||
// If there's no catch block without a catchguard, the last <next catch
|
||||
|
@ -4194,11 +4218,13 @@ EmitIf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
* destructure z
|
||||
* pick 1
|
||||
* pop -1
|
||||
* enterlet0
|
||||
* pushblockscope (if needed)
|
||||
* evaluate e +1
|
||||
* leaveblockexpr -3
|
||||
* debugleaveblock
|
||||
* popblockscope (if needed)
|
||||
* popnv 3 -3
|
||||
*
|
||||
* Note that, since enterlet0 simply changes fp->blockChain and does not
|
||||
* Note that, since pushblockscope simply changes fp->scopeChain and does not
|
||||
* otherwise touch the stack, evaluation of the let-var initializers must leave
|
||||
* the initial value in the let-var's future slot.
|
||||
*/
|
||||
|
@ -4236,9 +4262,13 @@ EmitLet(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
|
|||
if (!EmitTree(cx, bce, letBody->pn_expr))
|
||||
return false;
|
||||
|
||||
if (!LeaveBlockScope(cx, bce, letBody->getOp()))
|
||||
if (!LeaveBlockScope(cx, bce))
|
||||
return false;
|
||||
|
||||
JSOp leaveOp = letBody->getOp();
|
||||
JS_ASSERT(leaveOp == JSOP_POPN || leaveOp == JSOP_POPNV);
|
||||
EMIT_UINT16_IMM_OP(leaveOp, blockObj->slotCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4250,7 +4280,7 @@ MOZ_NEVER_INLINE static bool
|
|||
EmitLexicalScope(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
|
||||
JS_ASSERT(pn->getOp() == JSOP_LEAVEBLOCK);
|
||||
JS_ASSERT(pn->getOp() == JSOP_POPN);
|
||||
|
||||
StmtInfoBCE stmtInfo(cx);
|
||||
ObjectBox *objbox = pn->pn_objbox;
|
||||
|
@ -4268,9 +4298,11 @@ EmitLexicalScope(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
if (!EmitTree(cx, bce, pn->pn_expr))
|
||||
return false;
|
||||
|
||||
if (!LeaveBlockScope(cx, bce, JSOP_LEAVEBLOCK))
|
||||
if (!LeaveBlockScope(cx, bce))
|
||||
return false;
|
||||
|
||||
EMIT_UINT16_IMM_OP(JSOP_POPN, slots);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4440,14 +4472,14 @@ EmitForOf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
|
|||
if (!PopStatementBCE(cx, bce))
|
||||
return false;
|
||||
|
||||
// Pop result and iter.
|
||||
EMIT_UINT16_IMM_OP(JSOP_POPN, 2);
|
||||
|
||||
if (letDecl) {
|
||||
if (!LeaveBlockScope(cx, bce, JSOP_LEAVEBLOCK))
|
||||
if (!LeaveBlockScope(cx, bce))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pop result, iter, and slots from the lexical block (if any).
|
||||
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount + 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4468,20 +4500,24 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
|
|||
if (letDecl) {
|
||||
/*
|
||||
* The let's slot(s) will be under the iterator, but the block must not
|
||||
* be entered (i.e. fp->blockChain set) until after evaluating the rhs.
|
||||
* Thus, push to reserve space and enterblock after. The same argument
|
||||
* applies when leaving the loop. Thus, a for-let-in loop looks like:
|
||||
* be entered until after evaluating the rhs. So, we reserve space for
|
||||
* the block scope now, and only push the block onto the scope chain
|
||||
* later. Thus, a for-let-in loop looks like:
|
||||
*
|
||||
* push x N
|
||||
* eval rhs
|
||||
* iter
|
||||
* enterlet1
|
||||
* pushblockscope (if needed)
|
||||
* goto
|
||||
* ... loop body
|
||||
* ifne
|
||||
* leaveforinlet
|
||||
* debugleaveblock
|
||||
* popblockscope (if needed)
|
||||
* enditer
|
||||
* popn(N)
|
||||
*
|
||||
* Note that pushblockscope and popblockscope only get emitted if some
|
||||
* of the variables in the block are captured.
|
||||
*/
|
||||
for (uint32_t i = 0; i < blockObjCount; ++i) {
|
||||
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
|
||||
|
@ -4600,8 +4636,9 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
|
|||
return false;
|
||||
|
||||
if (letDecl) {
|
||||
if (!LeaveBlockScope(cx, bce, JSOP_LEAVEBLOCK))
|
||||
if (!LeaveBlockScope(cx, bce))
|
||||
return false;
|
||||
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -603,9 +603,9 @@ FullParseHandler::addCatchBlock(ParseNode *catchList, ParseNode *letBlock,
|
|||
inline void
|
||||
FullParseHandler::setLeaveBlockResult(ParseNode *block, ParseNode *kid, bool leaveBlockExpr)
|
||||
{
|
||||
JS_ASSERT(block->isOp(JSOP_LEAVEBLOCK));
|
||||
JS_ASSERT(block->isOp(JSOP_POPN));
|
||||
if (leaveBlockExpr)
|
||||
block->setOp(JSOP_LEAVEBLOCKEXPR);
|
||||
block->setOp(JSOP_POPNV);
|
||||
block->pn_expr = kid;
|
||||
}
|
||||
|
||||
|
@ -637,7 +637,7 @@ FullParseHandler::newLexicalScope(ObjectBox *blockbox)
|
|||
if (!pn)
|
||||
return nullptr;
|
||||
|
||||
pn->setOp(JSOP_LEAVEBLOCK);
|
||||
pn->setOp(JSOP_POPN);
|
||||
pn->pn_objbox = blockbox;
|
||||
pn->pn_cookie.makeFree();
|
||||
pn->pn_dflags = 0;
|
||||
|
|
|
@ -407,7 +407,7 @@ enum ParseNodeKind
|
|||
* PNK_NULL,
|
||||
* PNK_THIS
|
||||
*
|
||||
* PNK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR
|
||||
* PNK_LEXICALSCOPE name pn_op: JSOP_POPN or JSOP_POPNV
|
||||
* pn_objbox: block object in ObjectBox holder
|
||||
* pn_expr: block body
|
||||
* PNK_ARRAYCOMP list pn_count: 1
|
||||
|
|
|
@ -3231,7 +3231,6 @@ Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInf
|
|||
if (!pn)
|
||||
return null();
|
||||
|
||||
/* Tell codegen to emit JSOP_ENTERLETx (not JSOP_ENTERBLOCK). */
|
||||
pn->pn_dflags |= PND_LET;
|
||||
|
||||
/* Populate the new scope with decls found in the head with updated blockid. */
|
||||
|
@ -3601,7 +3600,7 @@ Parser<FullParseHandler>::letDeclaration()
|
|||
if (!pn1)
|
||||
return null();
|
||||
|
||||
pn1->setOp(JSOP_LEAVEBLOCK);
|
||||
pn1->setOp(JSOP_POPN);
|
||||
pn1->pn_pos = pc->blockNode->pn_pos;
|
||||
pn1->pn_objbox = blockbox;
|
||||
pn1->pn_expr = pc->blockNode;
|
||||
|
@ -3637,8 +3636,8 @@ Parser<FullParseHandler>::letStatement()
|
|||
if (tokenStream.peekToken() == TOK_LP) {
|
||||
pn = letBlock(LetStatement);
|
||||
JS_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
|
||||
JS_ASSERT_IF(pn && pn->isKind(PNK_LET) && pn->pn_expr->getOp() != JSOP_LEAVEBLOCK,
|
||||
pn->isOp(JSOP_NOP));
|
||||
JS_ASSERT_IF(pn && pn->isKind(PNK_LET) && pn->pn_expr->getOp() != JSOP_POPNV,
|
||||
pn->pn_expr->isOp(JSOP_POPN));
|
||||
} else
|
||||
pn = letDeclaration();
|
||||
return pn;
|
||||
|
@ -6718,16 +6717,21 @@ Parser<ParseHandler>::arrayInitializer()
|
|||
*
|
||||
* Each let () {...} or for (let ...) ... compiles to:
|
||||
*
|
||||
* JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
|
||||
* JSOP_PUSHN <N> // Push space for block-scoped locals.
|
||||
* (JSOP_PUSHBLOCKSCOPE <O>) // If a local is aliased, push on scope
|
||||
* // chain.
|
||||
* ...
|
||||
* JSOP_DEBUGLEAVEBLOCK // Invalidate any DebugScope proxies.
|
||||
* JSOP_POPBLOCKSCOPE? // Pop off scope chain, if needed.
|
||||
* JSOP_POPN <N> // Pop space for block-scoped locals.
|
||||
*
|
||||
* where <o> is a literal object representing the block scope,
|
||||
* with <n> properties, naming each var declared in the block.
|
||||
*
|
||||
* Each var declaration in a let-block binds a name in <o> at
|
||||
* compile time, and allocates a slot on the operand stack at
|
||||
* runtime via JSOP_ENTERBLOCK. A block-local var is accessed by
|
||||
* the JSOP_GETLOCAL and JSOP_SETLOCAL ops. These ops have an
|
||||
* immediate operand, the local slot's stack index from fp->spbase.
|
||||
* Each var declaration in a let-block binds a name in <o> at compile
|
||||
* time. A block-local var is accessed by the JSOP_GETLOCAL and
|
||||
* JSOP_SETLOCAL ops. These ops have an immediate operand, the local
|
||||
* slot's stack index from fp->spbase.
|
||||
*
|
||||
* The array comprehension iteration step, array.push(i * j) in
|
||||
* the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>,
|
||||
|
|
|
@ -860,6 +860,15 @@ BaselineCompiler::emit_JSOP_POPN()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_POPNV()
|
||||
{
|
||||
frame.popRegsAndSync(1);
|
||||
frame.popn(GET_UINT16(pc));
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_DUP()
|
||||
{
|
||||
|
@ -2575,23 +2584,14 @@ BaselineCompiler::emit_JSOP_RETSUB()
|
|||
return emitOpIC(stubCompiler.getStub(&stubSpace_));
|
||||
}
|
||||
|
||||
typedef bool (*EnterBlockFn)(JSContext *, BaselineFrame *, Handle<StaticBlockObject *>);
|
||||
static const VMFunction EnterBlockInfo = FunctionInfo<EnterBlockFn>(jit::EnterBlock);
|
||||
typedef bool (*PushBlockScopeFn)(JSContext *, BaselineFrame *, Handle<StaticBlockObject *>);
|
||||
static const VMFunction PushBlockScopeInfo = FunctionInfo<PushBlockScopeFn>(jit::PushBlockScope);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emitEnterBlock()
|
||||
BaselineCompiler::emit_JSOP_PUSHBLOCKSCOPE()
|
||||
{
|
||||
StaticBlockObject &blockObj = script->getObject(pc)->as<StaticBlockObject>();
|
||||
|
||||
if (JSOp(*pc) == JSOP_ENTERBLOCK) {
|
||||
for (size_t i = 0; i < blockObj.slotCount(); i++)
|
||||
frame.push(UndefinedValue());
|
||||
|
||||
// Pushed values will be accessed using GETLOCAL and SETLOCAL, so ensure
|
||||
// they are synced.
|
||||
frame.syncStack(0);
|
||||
}
|
||||
|
||||
// Call a stub to push the block on the block chain.
|
||||
prepareVMCall();
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
|
@ -2599,31 +2599,22 @@ BaselineCompiler::emitEnterBlock()
|
|||
pushArg(ImmGCPtr(&blockObj));
|
||||
pushArg(R0.scratchReg());
|
||||
|
||||
return callVM(EnterBlockInfo);
|
||||
return callVM(PushBlockScopeInfo);
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ENTERBLOCK()
|
||||
{
|
||||
return emitEnterBlock();
|
||||
}
|
||||
typedef bool (*PopBlockScopeFn)(JSContext *, BaselineFrame *);
|
||||
static const VMFunction PopBlockScopeInfo = FunctionInfo<PopBlockScopeFn>(jit::PopBlockScope);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ENTERLET0()
|
||||
BaselineCompiler::emit_JSOP_POPBLOCKSCOPE()
|
||||
{
|
||||
return emitEnterBlock();
|
||||
}
|
||||
// Call a stub to pop the block from the block chain.
|
||||
prepareVMCall();
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ENTERLET1()
|
||||
{
|
||||
return emitEnterBlock();
|
||||
}
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
pushArg(R0.scratchReg());
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ENTERLET2()
|
||||
{
|
||||
return emitEnterBlock();
|
||||
return callVM(PopBlockScopeInfo);
|
||||
}
|
||||
|
||||
typedef bool (*DebugLeaveBlockFn)(JSContext *, BaselineFrame *, jsbytecode *);
|
||||
|
@ -2643,56 +2634,6 @@ BaselineCompiler::emit_JSOP_DEBUGLEAVEBLOCK()
|
|||
return callVM(DebugLeaveBlockInfo);
|
||||
}
|
||||
|
||||
typedef bool (*LeaveBlockFn)(JSContext *, BaselineFrame *);
|
||||
static const VMFunction LeaveBlockInfo = FunctionInfo<LeaveBlockFn>(jit::LeaveBlock);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emitLeaveBlock()
|
||||
{
|
||||
// Call a stub to pop the block from the block chain.
|
||||
prepareVMCall();
|
||||
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
pushArg(R0.scratchReg());
|
||||
|
||||
return callVM(LeaveBlockInfo);
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_LEAVEBLOCK()
|
||||
{
|
||||
if (!emitLeaveBlock())
|
||||
return false;
|
||||
|
||||
// Pop slots pushed by JSOP_ENTERBLOCK.
|
||||
frame.popn(GET_UINT16(pc));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_LEAVEBLOCKEXPR()
|
||||
{
|
||||
if (!emitLeaveBlock())
|
||||
return false;
|
||||
|
||||
// Pop slots pushed by JSOP_ENTERBLOCK, but leave the topmost value
|
||||
// on the stack.
|
||||
frame.popRegsAndSync(1);
|
||||
frame.popn(GET_UINT16(pc));
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_LEAVEFORLETIN()
|
||||
{
|
||||
if (!emitLeaveBlock())
|
||||
return false;
|
||||
|
||||
// Another op will pop the slots (after the enditer).
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*GetAndClearExceptionFn)(JSContext *, MutableHandleValue);
|
||||
static const VMFunction GetAndClearExceptionInfo =
|
||||
FunctionInfo<GetAndClearExceptionFn>(GetAndClearException);
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace jit {
|
|||
_(JSOP_NOTEARG) \
|
||||
_(JSOP_POP) \
|
||||
_(JSOP_POPN) \
|
||||
_(JSOP_POPNV) \
|
||||
_(JSOP_DUP) \
|
||||
_(JSOP_DUP2) \
|
||||
_(JSOP_SWAP) \
|
||||
|
@ -145,13 +146,8 @@ namespace jit {
|
|||
_(JSOP_FINALLY) \
|
||||
_(JSOP_GOSUB) \
|
||||
_(JSOP_RETSUB) \
|
||||
_(JSOP_ENTERBLOCK) \
|
||||
_(JSOP_ENTERLET0) \
|
||||
_(JSOP_ENTERLET1) \
|
||||
_(JSOP_ENTERLET2) \
|
||||
_(JSOP_LEAVEBLOCK) \
|
||||
_(JSOP_LEAVEBLOCKEXPR) \
|
||||
_(JSOP_LEAVEFORLETIN) \
|
||||
_(JSOP_PUSHBLOCKSCOPE) \
|
||||
_(JSOP_POPBLOCKSCOPE) \
|
||||
_(JSOP_DEBUGLEAVEBLOCK) \
|
||||
_(JSOP_EXCEPTION) \
|
||||
_(JSOP_DEBUGGER) \
|
||||
|
@ -255,9 +251,6 @@ class BaselineCompiler : public BaselineCompilerSpecific
|
|||
|
||||
bool emitFormalArgAccess(uint32_t arg, bool get);
|
||||
|
||||
bool emitEnterBlock();
|
||||
bool emitLeaveBlock();
|
||||
|
||||
bool addPCMappingEntry(bool addIndexEntry);
|
||||
|
||||
void getScopeCoordinateObject(Register reg);
|
||||
|
|
|
@ -36,31 +36,22 @@ BaselineFrame::popOffScopeChain()
|
|||
inline bool
|
||||
BaselineFrame::pushBlock(JSContext *cx, Handle<StaticBlockObject *> block)
|
||||
{
|
||||
JS_ASSERT_IF(hasBlockChain(), blockChain() == *block->enclosingBlock());
|
||||
JS_ASSERT(block->needsClone());
|
||||
|
||||
if (block->needsClone()) {
|
||||
ClonedBlockObject *clone = ClonedBlockObject::create(cx, block, this);
|
||||
if (!clone)
|
||||
return false;
|
||||
ClonedBlockObject *clone = ClonedBlockObject::create(cx, block, this);
|
||||
if (!clone)
|
||||
return false;
|
||||
pushOnScopeChain(*clone);
|
||||
|
||||
pushOnScopeChain(*clone);
|
||||
}
|
||||
|
||||
setBlockChain(*block);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void
|
||||
BaselineFrame::popBlock(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(hasBlockChain());
|
||||
JS_ASSERT(scopeChain_->is<ClonedBlockObject>());
|
||||
|
||||
if (blockChain_->needsClone()) {
|
||||
JS_ASSERT(scopeChain_->as<ClonedBlockObject>().staticBlock() == *blockChain_);
|
||||
popOffScopeChain();
|
||||
}
|
||||
|
||||
setBlockChain(*blockChain_->enclosingBlock());
|
||||
popOffScopeChain();
|
||||
}
|
||||
|
||||
inline CallObject &
|
||||
|
|
|
@ -169,7 +169,7 @@ class BaselineFrame
|
|||
|
||||
Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
|
||||
#ifdef DEBUG
|
||||
CheckLocalUnaliased(checkAliasing, script(), maybeBlockChain(), i);
|
||||
CheckLocalUnaliased(checkAliasing, script(), i);
|
||||
#endif
|
||||
return *valueSlot(i);
|
||||
}
|
||||
|
|
|
@ -1252,6 +1252,7 @@ IonBuilder::traverseBytecode()
|
|||
switch (op) {
|
||||
case JSOP_POP:
|
||||
case JSOP_POPN:
|
||||
case JSOP_POPNV:
|
||||
case JSOP_DUP:
|
||||
case JSOP_DUP2:
|
||||
case JSOP_PICK:
|
||||
|
@ -1504,6 +1505,15 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
current->pop();
|
||||
return true;
|
||||
|
||||
case JSOP_POPNV:
|
||||
{
|
||||
MDefinition *mins = current->pop();
|
||||
for (uint32_t i = 0, n = GET_UINT16(pc); i < n; i++)
|
||||
current->pop();
|
||||
current->push(mins);
|
||||
return true;
|
||||
}
|
||||
|
||||
case JSOP_NEWINIT:
|
||||
if (GET_UINT8(pc) == JSProto_Array)
|
||||
return jsop_newarray(0);
|
||||
|
|
|
@ -883,13 +883,13 @@ OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *m
|
|||
}
|
||||
|
||||
bool
|
||||
EnterBlock(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block)
|
||||
PushBlockScope(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block)
|
||||
{
|
||||
return frame->pushBlock(cx, block);
|
||||
}
|
||||
|
||||
bool
|
||||
LeaveBlock(JSContext *cx, BaselineFrame *frame)
|
||||
PopBlockScope(JSContext *cx, BaselineFrame *frame)
|
||||
{
|
||||
frame->popBlock(cx);
|
||||
return true;
|
||||
|
|
|
@ -652,8 +652,8 @@ JSObject *InitRestParameter(JSContext *cx, uint32_t length, Value *rest, HandleO
|
|||
bool HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, bool *mustReturn);
|
||||
bool OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn);
|
||||
|
||||
bool EnterBlock(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block);
|
||||
bool LeaveBlock(JSContext *cx, BaselineFrame *frame);
|
||||
bool PushBlockScope(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block);
|
||||
bool PopBlockScope(JSContext *cx, BaselineFrame *frame);
|
||||
bool DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc);
|
||||
|
||||
bool InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame,
|
||||
|
|
|
@ -253,7 +253,6 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
|
|||
|
||||
case JSOP_EVAL:
|
||||
case JSOP_SPREADEVAL:
|
||||
case JSOP_ENTERLET2:
|
||||
case JSOP_ENTERWITH:
|
||||
canTrackVars = false;
|
||||
break;
|
||||
|
|
|
@ -114,18 +114,6 @@ js_GetVariableBytecodeLength(jsbytecode *pc)
|
|||
}
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
NumBlockSlots(JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(*pc == JSOP_ENTERBLOCK ||
|
||||
*pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1 || *pc == JSOP_ENTERLET2);
|
||||
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET2_LENGTH);
|
||||
|
||||
return script->getObject(GET_UINT32_INDEX(pc))->as<StaticBlockObject>().propertyCountForCompilation();
|
||||
}
|
||||
|
||||
unsigned
|
||||
js::StackUses(JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
|
@ -138,16 +126,8 @@ js::StackUses(JSScript *script, jsbytecode *pc)
|
|||
switch (op) {
|
||||
case JSOP_POPN:
|
||||
return GET_UINT16(pc);
|
||||
case JSOP_LEAVEBLOCK:
|
||||
return GET_UINT16(pc);
|
||||
case JSOP_LEAVEBLOCKEXPR:
|
||||
case JSOP_POPNV:
|
||||
return GET_UINT16(pc) + 1;
|
||||
case JSOP_ENTERLET0:
|
||||
return NumBlockSlots(script, pc);
|
||||
case JSOP_ENTERLET1:
|
||||
return NumBlockSlots(script, pc) + 1;
|
||||
case JSOP_ENTERLET2:
|
||||
return NumBlockSlots(script, pc) + 2;
|
||||
default:
|
||||
/* stack: fun, this, [argc arguments] */
|
||||
JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL ||
|
||||
|
@ -161,15 +141,8 @@ js::StackDefs(JSScript *script, jsbytecode *pc)
|
|||
{
|
||||
JSOp op = (JSOp) *pc;
|
||||
const JSCodeSpec &cs = js_CodeSpec[op];
|
||||
if (cs.ndefs >= 0)
|
||||
return cs.ndefs;
|
||||
|
||||
uint32_t n = NumBlockSlots(script, pc);
|
||||
if (op == JSOP_ENTERLET1)
|
||||
return n + 1;
|
||||
if (op == JSOP_ENTERLET2)
|
||||
return n + 2;
|
||||
return n;
|
||||
JS_ASSERT (cs.ndefs >= 0);
|
||||
return cs.ndefs;
|
||||
}
|
||||
|
||||
static const char * const countBaseNames[] = {
|
||||
|
|
|
@ -97,7 +97,9 @@ OPDEF(JSOP_SPREADNEW, 42, "spreadnew", NULL, 1, 3, 1, JOF_BYTE|JOF_IN
|
|||
/* spreadcall variant of JSOP_EVAL */
|
||||
OPDEF(JSOP_SPREADEVAL,43, "spreadeval", NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET)
|
||||
|
||||
OPDEF(JSOP_UNUSED44, 44, "unused44", NULL, 1, 0, 0, JOF_BYTE)
|
||||
/* Pop N values, preserving top value. */
|
||||
OPDEF(JSOP_POPNV, 44, "popnv", NULL, 3, -1, 1, JOF_UINT16)
|
||||
|
||||
OPDEF(JSOP_UNUSED45, 45, "unused45", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED46, 46, "unused46", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED47, 47, "unused47", NULL, 1, 0, 0, JOF_BYTE)
|
||||
|
@ -220,9 +222,7 @@ OPDEF(JSOP_UNUSED101, 101, "unused101", NULL, 1, 0, 0, JOF_BYTE)
|
|||
OPDEF(JSOP_UNUSED102, 102, "unused102", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED103, 103, "unused103", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED104, 104, "unused104", NULL, 1, 0, 0, JOF_BYTE)
|
||||
|
||||
/* Leave a for-let-in block leaving its storage pushed (to be popped after enditer). */
|
||||
OPDEF(JSOP_LEAVEFORLETIN, 105,"leaveforletin",NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED105, 105, "unused105", NULL, 1, 0, 0, JOF_BYTE)
|
||||
|
||||
/* The argument is the offset to the next statement and is used by IonMonkey. */
|
||||
OPDEF(JSOP_LABEL, 106,"label", NULL, 5, 0, 0, JOF_JUMP)
|
||||
|
@ -398,13 +398,9 @@ OPDEF(JSOP_UNUSED183, 183,"unused183", NULL, 1, 0, 0, JOF_BYTE)
|
|||
|
||||
OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3)
|
||||
|
||||
/* Enter a let block/expr whose slots are at the top of the stack. */
|
||||
OPDEF(JSOP_ENTERLET0, 185,"enterlet0", NULL, 5, -1, -1, JOF_OBJECT)
|
||||
|
||||
/* Enter a let block/expr whose slots are 1 below the top of the stack. */
|
||||
OPDEF(JSOP_ENTERLET1, 186,"enterlet1", NULL, 5, -1, -1, JOF_OBJECT)
|
||||
/* Enter a let block/expr whose slots are 2 below the top of the stack. */
|
||||
OPDEF(JSOP_ENTERLET2, 187,"enterlet2", NULL, 5, -1, -1, JOF_OBJECT)
|
||||
OPDEF(JSOP_UNUSED185, 185,"unused185", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED186, 186,"unused186", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED187, 187,"unused187", NULL, 1, 0, 0, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Opcode to hold 24-bit immediate integer operands.
|
||||
|
@ -436,8 +432,8 @@ OPDEF(JSOP_TYPEOFEXPR, 197,"typeofexpr", NULL, 1, 1, 1, JOF_BYTE|JOF_DE
|
|||
/*
|
||||
* Block-local scope support.
|
||||
*/
|
||||
OPDEF(JSOP_ENTERBLOCK, 198,"enterblock", NULL, 5, 0, -1, JOF_OBJECT)
|
||||
OPDEF(JSOP_LEAVEBLOCK, 199,"leaveblock", NULL, 3, -1, 0, JOF_UINT16)
|
||||
OPDEF(JSOP_PUSHBLOCKSCOPE,198,"pushblockscope", NULL, 5, 0, 0, JOF_OBJECT)
|
||||
OPDEF(JSOP_POPBLOCKSCOPE, 199,"popblockscope", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_DEBUGLEAVEBLOCK, 200,"debugleaveblock", NULL, 1, 0, 0, JOF_BYTE)
|
||||
|
||||
OPDEF(JSOP_UNUSED201, 201,"unused201", NULL, 1, 0, 0, JOF_BYTE)
|
||||
|
@ -459,12 +455,7 @@ OPDEF(JSOP_GETFUNNS, 205,"getfunns", NULL, 1, 0, 1, JOF_BYTE)
|
|||
*/
|
||||
OPDEF(JSOP_ENUMCONSTELEM, 206,"enumconstelem",NULL, 1, 3, 0, JOF_BYTE|JOF_SET)
|
||||
|
||||
/*
|
||||
* 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, 3, -1, 1, JOF_UINT16)
|
||||
|
||||
OPDEF(JSOP_UNUSED207, 207, "unused207", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED208, 208, "unused208", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED209, 209, "unused209", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE)
|
||||
|
|
|
@ -854,8 +854,8 @@ js::UnwindScope(JSContext *cx, ScopeIter &si, uint32_t stackDepth)
|
|||
return;
|
||||
if (cx->compartment()->debugMode())
|
||||
DebugScopes::onPopBlock(cx, si);
|
||||
JS_ASSERT(&si.staticBlock() == si.frame().maybeBlockChain());
|
||||
si.frame().popBlock(cx);
|
||||
if (si.staticBlock().needsClone())
|
||||
si.frame().popBlock(cx);
|
||||
break;
|
||||
case ScopeIter::With:
|
||||
if (si.scope().as<WithObject>().stackDepth() < stackDepth)
|
||||
|
@ -1589,7 +1589,6 @@ CASE(EnableInterruptsPseudoOpcode)
|
|||
/* Various 1-byte no-ops. */
|
||||
CASE(JSOP_NOP)
|
||||
CASE(JSOP_UNUSED2)
|
||||
CASE(JSOP_UNUSED44)
|
||||
CASE(JSOP_UNUSED45)
|
||||
CASE(JSOP_UNUSED46)
|
||||
CASE(JSOP_UNUSED47)
|
||||
|
@ -1602,6 +1601,7 @@ CASE(JSOP_UNUSED101)
|
|||
CASE(JSOP_UNUSED102)
|
||||
CASE(JSOP_UNUSED103)
|
||||
CASE(JSOP_UNUSED104)
|
||||
CASE(JSOP_UNUSED105)
|
||||
CASE(JSOP_UNUSED107)
|
||||
CASE(JSOP_UNUSED125)
|
||||
CASE(JSOP_UNUSED126)
|
||||
|
@ -1641,6 +1641,9 @@ CASE(JSOP_UNUSED180)
|
|||
CASE(JSOP_UNUSED181)
|
||||
CASE(JSOP_UNUSED182)
|
||||
CASE(JSOP_UNUSED183)
|
||||
CASE(JSOP_UNUSED185)
|
||||
CASE(JSOP_UNUSED186)
|
||||
CASE(JSOP_UNUSED187)
|
||||
CASE(JSOP_UNUSED189)
|
||||
CASE(JSOP_UNUSED190)
|
||||
CASE(JSOP_UNUSED191)
|
||||
|
@ -1649,6 +1652,7 @@ CASE(JSOP_UNUSED194)
|
|||
CASE(JSOP_UNUSED196)
|
||||
CASE(JSOP_UNUSED201)
|
||||
CASE(JSOP_GETFUNNS)
|
||||
CASE(JSOP_UNUSED207)
|
||||
CASE(JSOP_UNUSED208)
|
||||
CASE(JSOP_UNUSED209)
|
||||
CASE(JSOP_UNUSED210)
|
||||
|
@ -1714,11 +1718,24 @@ CASE(JSOP_POPN)
|
|||
JS_ASSERT(GET_UINT16(REGS.pc) <= REGS.stackDepth());
|
||||
REGS.sp -= GET_UINT16(REGS.pc);
|
||||
#ifdef DEBUG
|
||||
if (StaticBlockObject *block = REGS.fp()->maybeBlockChain())
|
||||
if (StaticBlockObject *block = script->getBlockScope(REGS.pc + JSOP_POPN_LENGTH))
|
||||
JS_ASSERT(REGS.stackDepth() >= block->stackDepth() + block->slotCount());
|
||||
#endif
|
||||
END_CASE(JSOP_POPN)
|
||||
|
||||
CASE(JSOP_POPNV)
|
||||
{
|
||||
JS_ASSERT(GET_UINT16(REGS.pc) < REGS.stackDepth());
|
||||
Value val = REGS.sp[-1];
|
||||
REGS.sp -= GET_UINT16(REGS.pc);
|
||||
REGS.sp[-1] = val;
|
||||
#ifdef DEBUG
|
||||
if (StaticBlockObject *block = script->getBlockScope(REGS.pc + JSOP_POPNV_LENGTH))
|
||||
JS_ASSERT(REGS.stackDepth() >= block->stackDepth() + block->slotCount());
|
||||
#endif
|
||||
}
|
||||
END_CASE(JSOP_POPNV)
|
||||
|
||||
CASE(JSOP_SETRVAL)
|
||||
POP_RETURN_VALUE();
|
||||
END_CASE(JSOP_SETRVAL)
|
||||
|
@ -3336,52 +3353,36 @@ CASE(JSOP_DEBUGGER)
|
|||
}
|
||||
END_CASE(JSOP_DEBUGGER)
|
||||
|
||||
CASE(JSOP_ENTERBLOCK)
|
||||
CASE(JSOP_ENTERLET0)
|
||||
CASE(JSOP_ENTERLET1)
|
||||
CASE(JSOP_ENTERLET2)
|
||||
CASE(JSOP_PUSHBLOCKSCOPE)
|
||||
{
|
||||
StaticBlockObject &blockObj = script->getObject(REGS.pc)->as<StaticBlockObject>();
|
||||
|
||||
if (*REGS.pc == JSOP_ENTERBLOCK) {
|
||||
JS_ASSERT(REGS.stackDepth() == blockObj.stackDepth());
|
||||
JS_ASSERT(REGS.stackDepth() + blockObj.slotCount() <= script->nslots());
|
||||
Value *vp = REGS.sp + blockObj.slotCount();
|
||||
SetValueRangeToUndefined(REGS.sp, vp);
|
||||
REGS.sp = vp;
|
||||
}
|
||||
JS_ASSERT(blockObj.needsClone());
|
||||
// FIXME: "Aliased" slots don't need to be on the stack.
|
||||
JS_ASSERT(REGS.stackDepth() >= blockObj.stackDepth() + blockObj.slotCount());
|
||||
|
||||
/* Clone block iff there are any closed-over variables. */
|
||||
// Clone block and push on scope chain.
|
||||
if (!REGS.fp()->pushBlock(cx, blockObj))
|
||||
goto error;
|
||||
}
|
||||
END_CASE(JSOP_ENTERBLOCK)
|
||||
END_CASE(JSOP_PUSHBLOCKSCOPE)
|
||||
|
||||
CASE(JSOP_LEAVEBLOCK)
|
||||
CASE(JSOP_LEAVEFORLETIN)
|
||||
CASE(JSOP_LEAVEBLOCKEXPR)
|
||||
CASE(JSOP_POPBLOCKSCOPE)
|
||||
{
|
||||
blockDepth = REGS.fp()->blockChain().stackDepth();
|
||||
#ifdef DEBUG
|
||||
// Pop block from scope chain.
|
||||
JS_ASSERT(*(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH) == JSOP_DEBUGLEAVEBLOCK);
|
||||
StaticBlockObject *blockObj = script->getBlockScope(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH);
|
||||
JS_ASSERT(blockObj && blockObj->needsClone());
|
||||
|
||||
// FIXME: "Aliased" slots don't need to be on the stack.
|
||||
JS_ASSERT(REGS.stackDepth() >= blockObj->stackDepth() + blockObj->slotCount());
|
||||
#endif
|
||||
|
||||
// Pop block from scope chain.
|
||||
REGS.fp()->popBlock(cx);
|
||||
|
||||
if (*REGS.pc == JSOP_LEAVEBLOCK) {
|
||||
/* Pop the block's slots. */
|
||||
REGS.sp -= GET_UINT16(REGS.pc);
|
||||
JS_ASSERT(REGS.stackDepth() == blockDepth);
|
||||
} else if (*REGS.pc == JSOP_LEAVEBLOCKEXPR) {
|
||||
/* Pop the block's slots maintaining the topmost expr. */
|
||||
Value *vp = ®S.sp[-1];
|
||||
REGS.sp -= GET_UINT16(REGS.pc);
|
||||
JS_ASSERT(REGS.stackDepth() == blockDepth + 1);
|
||||
REGS.sp[-1] = *vp;
|
||||
} else {
|
||||
/* Another op will pop; nothing to do here. */
|
||||
ADVANCE_AND_DISPATCH(JSOP_LEAVEFORLETIN_LENGTH);
|
||||
}
|
||||
}
|
||||
END_CASE(JSOP_LEAVEBLOCK)
|
||||
END_CASE(JSOP_POPBLOCKSCOPE)
|
||||
|
||||
CASE(JSOP_DEBUGLEAVEBLOCK)
|
||||
{
|
||||
|
|
|
@ -110,7 +110,7 @@ inline Value &
|
|||
StackFrame::unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
CheckLocalUnaliased(checkAliasing, script(), maybeBlockChain(), i);
|
||||
CheckLocalUnaliased(checkAliasing, script(), i);
|
||||
#endif
|
||||
return slots()[i];
|
||||
}
|
||||
|
|
|
@ -327,20 +327,14 @@ StackFrame::epilogue(JSContext *cx)
|
|||
bool
|
||||
StackFrame::pushBlock(JSContext *cx, StaticBlockObject &block)
|
||||
{
|
||||
JS_ASSERT_IF(hasBlockChain(), blockChain_ == block.enclosingBlock());
|
||||
JS_ASSERT (block.needsClone());
|
||||
|
||||
if (block.needsClone()) {
|
||||
Rooted<StaticBlockObject *> blockHandle(cx, &block);
|
||||
ClonedBlockObject *clone = ClonedBlockObject::create(cx, blockHandle, this);
|
||||
if (!clone)
|
||||
return false;
|
||||
Rooted<StaticBlockObject *> blockHandle(cx, &block);
|
||||
ClonedBlockObject *clone = ClonedBlockObject::create(cx, blockHandle, this);
|
||||
if (!clone)
|
||||
return false;
|
||||
|
||||
pushOnScopeChain(*clone);
|
||||
|
||||
blockChain_ = blockHandle;
|
||||
} else {
|
||||
blockChain_ = █
|
||||
}
|
||||
pushOnScopeChain(*clone);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -348,12 +342,8 @@ StackFrame::pushBlock(JSContext *cx, StaticBlockObject &block)
|
|||
void
|
||||
StackFrame::popBlock(JSContext *cx)
|
||||
{
|
||||
if (blockChain_->needsClone()) {
|
||||
JS_ASSERT(scopeChain_->as<ClonedBlockObject>().staticBlock() == *blockChain_);
|
||||
popOffScopeChain();
|
||||
}
|
||||
|
||||
blockChain_ = blockChain_->enclosingBlock();
|
||||
JS_ASSERT(scopeChain_->is<ClonedBlockObject>());
|
||||
popOffScopeChain();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1254,8 +1244,7 @@ AbstractFramePtr::hasPushedSPSFrame() const
|
|||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script,
|
||||
StaticBlockObject *maybeBlock, unsigned i)
|
||||
js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, unsigned i)
|
||||
{
|
||||
if (!checkAliasing)
|
||||
return;
|
||||
|
@ -1264,13 +1253,8 @@ js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script,
|
|||
if (i < script->nfixed()) {
|
||||
JS_ASSERT(!script->varIsAliased(i));
|
||||
} else {
|
||||
unsigned depth = i - script->nfixed();
|
||||
for (StaticBlockObject *b = maybeBlock; b; b = b->enclosingBlock()) {
|
||||
if (b->containsVarAtDepth(depth)) {
|
||||
JS_ASSERT(!b->isAliased(depth - b->stackDepth()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// FIXME: The callers of this function do not easily have the PC of the
|
||||
// current frame, and so they do not know the block scope.
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -75,8 +75,7 @@ enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
|
|||
|
||||
#ifdef DEBUG
|
||||
extern void
|
||||
CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script,
|
||||
StaticBlockObject *maybeBlock, unsigned i);
|
||||
CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, unsigned i);
|
||||
#endif
|
||||
|
||||
namespace jit {
|
||||
|
@ -591,14 +590,8 @@ class StackFrame
|
|||
inline void popOffScopeChain();
|
||||
|
||||
/*
|
||||
* Block chain
|
||||
*
|
||||
* Entering/leaving a let (or exception) block may do 1 or 2 things: First,
|
||||
* a static block object (created at compiled time and stored in the
|
||||
* script) is pushed on StackFrame::blockChain. Second, if the static block
|
||||
* may be cloned to hold the dynamic values if this is needed for dynamic
|
||||
* scope access. A clone is created for a static block iff
|
||||
* StaticBlockObject::needsClone.
|
||||
* For blocks with aliased locals, these interfaces push and pop entries on
|
||||
* the scope chain.
|
||||
*/
|
||||
|
||||
bool hasBlockChain() const {
|
||||
|
|
Загрузка…
Ссылка в новой задаче