mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backed out 12 changesets (bug 927782) for SM rootanalysis orange.
Backed out changeset f86d2d4cfadf (bug 927782) Backed out changeset 51d6617835d1 (bug 927782) Backed out changeset eed9795fa80e (bug 927782) Backed out changeset b971de7edfff (bug 927782) Backed out changeset 5f086f95b305 (bug 927782) Backed out changeset 8c74b1f68590 (bug 927782) Backed out changeset f1237f11edcd (bug 927782) Backed out changeset d6946bd743b3 (bug 927782) Backed out changeset cbdd50c96b85 (bug 927782) Backed out changeset fc7a979712fc (bug 927782) Backed out changeset c8304ccf88e9 (bug 927782) Backed out changeset 9d99e9ca7b32 (bug 927782)
This commit is contained in:
parent
991e77275b
commit
c60e2ccac4
@ -22,6 +22,11 @@ function testSteps()
|
||||
objectStore.add(Bob);
|
||||
yield undefined;
|
||||
|
||||
// This direct eval causes locals to be aliased, and thus allocated on
|
||||
// the scope chain. Comment it out (and the workarounds below) and
|
||||
// the test passes. Bug 943409.
|
||||
eval('');
|
||||
|
||||
db.transaction("foo", "readwrite").objectStore("foo")
|
||||
.index("name").openCursor().onsuccess = function(event) {
|
||||
event.target.transaction.oncomplete = continueToNextStep;
|
||||
|
@ -35,6 +35,11 @@ function testSteps()
|
||||
let objectStore = db.createObjectStore("foo", { keyPath: "ss" });
|
||||
objectStore.createIndex("name", "name", { unique: true });
|
||||
|
||||
// This direct eval causes locals to be aliased, and thus allocated on
|
||||
// the scope chain. Comment it out (and the workarounds below) and
|
||||
// the test passes. Bug 943409.
|
||||
eval('');
|
||||
|
||||
for (let i = 0; i < objectStoreData.length - 1; i++) {
|
||||
objectStore.add(objectStoreData[i]);
|
||||
}
|
||||
|
@ -34,6 +34,9 @@ function testSteps()
|
||||
|
||||
event.target.onsuccess = continueToNextStep;
|
||||
|
||||
// Bug 943409.
|
||||
eval('');
|
||||
|
||||
for (let objectStoreIndex in objectStoreData) {
|
||||
const objectStoreInfo = objectStoreData[objectStoreIndex];
|
||||
let objectStore = db.createObjectStore(objectStoreInfo.name,
|
||||
|
@ -18,6 +18,9 @@ function testSteps()
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
// Bug 943409.
|
||||
eval('');
|
||||
|
||||
for each (let autoIncrement in [false, true]) {
|
||||
let objectStore =
|
||||
db.createObjectStore(autoIncrement, { keyPath: "id",
|
||||
|
@ -97,7 +97,7 @@ jsd_NewThreadState(JSDContext* jsdc, JSContext *cx )
|
||||
{
|
||||
JSAbstractFramePtr frame = iter.abstractFramePtr();
|
||||
JS::RootedScript script(cx, frame.script());
|
||||
uintptr_t pc = (uintptr_t)frame.pc();
|
||||
uintptr_t pc = (uintptr_t)iter.pc();
|
||||
JS::RootedValue dummyThis(cx);
|
||||
|
||||
/*
|
||||
|
@ -339,18 +339,16 @@ JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda);
|
||||
class JS_PUBLIC_API(JSAbstractFramePtr)
|
||||
{
|
||||
uintptr_t ptr_;
|
||||
jsbytecode *pc_;
|
||||
|
||||
protected:
|
||||
JSAbstractFramePtr()
|
||||
: ptr_(0), pc_(nullptr)
|
||||
: ptr_(0)
|
||||
{ }
|
||||
|
||||
public:
|
||||
JSAbstractFramePtr(void *raw, jsbytecode *pc);
|
||||
explicit JSAbstractFramePtr(void *raw);
|
||||
|
||||
uintptr_t raw() const { return ptr_; }
|
||||
jsbytecode *pc() const { return pc_; }
|
||||
|
||||
operator bool() const { return !!ptr_; }
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -46,7 +46,6 @@ struct CGObjectList {
|
||||
unsigned add(ObjectBox *objbox);
|
||||
unsigned indexOf(JSObject *obj);
|
||||
void finish(ObjectArray *array);
|
||||
ObjectBox* find(uint32_t index);
|
||||
};
|
||||
|
||||
struct CGTryNoteList {
|
||||
@ -63,7 +62,6 @@ struct CGBlockScopeList {
|
||||
CGBlockScopeList(ExclusiveContext *cx) : list(cx) {}
|
||||
|
||||
bool append(uint32_t scopeObject, uint32_t offset);
|
||||
uint32_t findEnclosingScope(uint32_t index);
|
||||
void recordEnd(uint32_t index, uint32_t offset);
|
||||
size_t length() const { return list.length(); }
|
||||
void finish(BlockScopeArray *array);
|
||||
|
@ -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_POPN));
|
||||
JS_ASSERT(block->isOp(JSOP_LEAVEBLOCK));
|
||||
if (leaveBlockExpr)
|
||||
block->setOp(JSOP_POPNV);
|
||||
block->setOp(JSOP_LEAVEBLOCKEXPR);
|
||||
block->pn_expr = kid;
|
||||
}
|
||||
|
||||
@ -637,7 +637,7 @@ FullParseHandler::newLexicalScope(ObjectBox *blockbox)
|
||||
if (!pn)
|
||||
return nullptr;
|
||||
|
||||
pn->setOp(JSOP_POPN);
|
||||
pn->setOp(JSOP_LEAVEBLOCK);
|
||||
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_POPN or JSOP_POPNV
|
||||
* PNK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR
|
||||
* pn_objbox: block object in ObjectBox holder
|
||||
* pn_expr: block body
|
||||
* PNK_ARRAYCOMP list pn_count: 1
|
||||
|
@ -278,7 +278,7 @@ AppendPackedBindings(const ParseContext<ParseHandler> *pc, const DeclVector &vec
|
||||
*/
|
||||
JS_ASSERT_IF(dn->isClosed(), pc->decls().lookupFirst(name) == dn);
|
||||
bool aliased = dn->isClosed() ||
|
||||
(pc->sc->allLocalsAliased() &&
|
||||
(pc->sc->bindingsAccessedDynamically() &&
|
||||
pc->decls().lookupFirst(name) == dn);
|
||||
|
||||
*dst = Binding(name, kind, aliased);
|
||||
@ -3231,6 +3231,7 @@ 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. */
|
||||
@ -3600,7 +3601,7 @@ Parser<FullParseHandler>::letDeclaration()
|
||||
if (!pn1)
|
||||
return null();
|
||||
|
||||
pn1->setOp(JSOP_POPN);
|
||||
pn1->setOp(JSOP_LEAVEBLOCK);
|
||||
pn1->pn_pos = pc->blockNode->pn_pos;
|
||||
pn1->pn_objbox = blockbox;
|
||||
pn1->pn_expr = pc->blockNode;
|
||||
@ -3636,8 +3637,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_POPNV,
|
||||
pn->pn_expr->isOp(JSOP_POPN));
|
||||
JS_ASSERT_IF(pn && pn->isKind(PNK_LET) && pn->pn_expr->getOp() != JSOP_LEAVEBLOCK,
|
||||
pn->isOp(JSOP_NOP));
|
||||
} else
|
||||
pn = letDeclaration();
|
||||
return pn;
|
||||
@ -6717,21 +6718,16 @@ Parser<ParseHandler>::arrayInitializer()
|
||||
*
|
||||
* Each let () {...} or for (let ...) ... compiles to:
|
||||
*
|
||||
* 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.
|
||||
* JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
|
||||
*
|
||||
* 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. 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, 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.
|
||||
*
|
||||
* The array comprehension iteration step, array.push(i * j) in
|
||||
* the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>,
|
||||
|
@ -200,8 +200,6 @@ class SharedContext
|
||||
void setBindingsAccessedDynamically() { anyCxFlags.bindingsAccessedDynamically = true; }
|
||||
void setHasDebuggerStatement() { anyCxFlags.hasDebuggerStatement = true; }
|
||||
|
||||
inline bool allLocalsAliased();
|
||||
|
||||
// JSOPTION_EXTRA_WARNINGS warnings or strict mode errors.
|
||||
bool needStrictChecks() {
|
||||
return strict || extraWarnings;
|
||||
@ -309,8 +307,7 @@ class FunctionBox : public ObjectBox, public SharedContext
|
||||
// Note: this should be kept in sync with JSFunction::isHeavyweight().
|
||||
return bindings.hasAnyAliasedBindings() ||
|
||||
hasExtensibleScope() ||
|
||||
needsDeclEnvObject() ||
|
||||
isGenerator();
|
||||
needsDeclEnvObject();
|
||||
}
|
||||
};
|
||||
|
||||
@ -321,18 +318,6 @@ SharedContext::asFunctionBox()
|
||||
return static_cast<FunctionBox*>(this);
|
||||
}
|
||||
|
||||
// In generators, we treat all locals as aliased so that they get stored on the
|
||||
// heap. This way there is less information to copy off the stack when
|
||||
// suspending, and back on when resuming. It also avoids the need to create and
|
||||
// invalidate DebugScope proxies for unaliased locals in a generator frame, as
|
||||
// the generator frame will be copied out to the heap and released only by GC.
|
||||
inline bool
|
||||
SharedContext::allLocalsAliased()
|
||||
{
|
||||
return bindingsAccessedDynamically() || (isFunctionBox() && asFunctionBox()->isGenerator());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* NB: If you add a new type of statement that is a scope, add it between
|
||||
* STMT_WITH and STMT_CATCH, or you will break StmtInfoBase::linksScope. If you
|
||||
|
@ -611,6 +611,10 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||
if (argsObj)
|
||||
blFrame->initArgsObjUnchecked(*argsObj);
|
||||
|
||||
// Ion doesn't compile code with try/catch, so the block object will always be
|
||||
// null.
|
||||
blFrame->setBlockChainNull();
|
||||
|
||||
if (fun) {
|
||||
// The unpacked thisv and arguments should overwrite the pushed args present
|
||||
// in the calling frame.
|
||||
@ -661,6 +665,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||
bool resumeAfter = excInfo ? false : iter.resumeAfter();
|
||||
|
||||
JSOp op = JSOp(*pc);
|
||||
JS_ASSERT_IF(excInfo, op == JSOP_ENTERBLOCK);
|
||||
|
||||
// Fixup inlined JSOP_FUNCALL, JSOP_FUNAPPLY, and accessors on the caller side.
|
||||
// On the caller side this must represent like the function wasn't inlined.
|
||||
|
@ -506,7 +506,7 @@ BaselineCompiler::emitStackCheck(bool earlyCheck)
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*DebugPrologueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool *);
|
||||
typedef bool (*DebugPrologueFn)(JSContext *, BaselineFrame *, bool *);
|
||||
static const VMFunction DebugPrologueInfo = FunctionInfo<DebugPrologueFn>(jit::DebugPrologue);
|
||||
|
||||
bool
|
||||
@ -519,7 +519,6 @@ BaselineCompiler::emitDebugPrologue()
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(ImmPtr(pc));
|
||||
pushArg(R0.scratchReg());
|
||||
if (!callVM(DebugPrologueInfo))
|
||||
return false;
|
||||
@ -858,15 +857,6 @@ 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()
|
||||
{
|
||||
@ -2556,14 +2546,23 @@ BaselineCompiler::emit_JSOP_RETSUB()
|
||||
return emitOpIC(stubCompiler.getStub(&stubSpace_));
|
||||
}
|
||||
|
||||
typedef bool (*PushBlockScopeFn)(JSContext *, BaselineFrame *, Handle<StaticBlockObject *>);
|
||||
static const VMFunction PushBlockScopeInfo = FunctionInfo<PushBlockScopeFn>(jit::PushBlockScope);
|
||||
typedef bool (*EnterBlockFn)(JSContext *, BaselineFrame *, Handle<StaticBlockObject *>);
|
||||
static const VMFunction EnterBlockInfo = FunctionInfo<EnterBlockFn>(jit::EnterBlock);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_PUSHBLOCKSCOPE()
|
||||
BaselineCompiler::emitEnterBlock()
|
||||
{
|
||||
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());
|
||||
@ -2571,14 +2570,38 @@ BaselineCompiler::emit_JSOP_PUSHBLOCKSCOPE()
|
||||
pushArg(ImmGCPtr(&blockObj));
|
||||
pushArg(R0.scratchReg());
|
||||
|
||||
return callVM(PushBlockScopeInfo);
|
||||
return callVM(EnterBlockInfo);
|
||||
}
|
||||
|
||||
typedef bool (*PopBlockScopeFn)(JSContext *, BaselineFrame *);
|
||||
static const VMFunction PopBlockScopeInfo = FunctionInfo<PopBlockScopeFn>(jit::PopBlockScope);
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ENTERBLOCK()
|
||||
{
|
||||
return emitEnterBlock();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_POPBLOCKSCOPE()
|
||||
BaselineCompiler::emit_JSOP_ENTERLET0()
|
||||
{
|
||||
return emitEnterBlock();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ENTERLET1()
|
||||
{
|
||||
return emitEnterBlock();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ENTERLET2()
|
||||
{
|
||||
return emitEnterBlock();
|
||||
}
|
||||
|
||||
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();
|
||||
@ -2586,24 +2609,42 @@ BaselineCompiler::emit_JSOP_POPBLOCKSCOPE()
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
pushArg(R0.scratchReg());
|
||||
|
||||
return callVM(PopBlockScopeInfo);
|
||||
return callVM(LeaveBlockInfo);
|
||||
}
|
||||
|
||||
typedef bool (*DebugLeaveBlockFn)(JSContext *, BaselineFrame *, jsbytecode *);
|
||||
static const VMFunction DebugLeaveBlockInfo = FunctionInfo<DebugLeaveBlockFn>(jit::DebugLeaveBlock);
|
||||
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_DEBUGLEAVEBLOCK()
|
||||
BaselineCompiler::emit_JSOP_LEAVEBLOCKEXPR()
|
||||
{
|
||||
if (!debugMode_)
|
||||
return true;
|
||||
if (!emitLeaveBlock())
|
||||
return false;
|
||||
|
||||
prepareVMCall();
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
pushArg(ImmPtr(pc));
|
||||
pushArg(R0.scratchReg());
|
||||
// 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;
|
||||
}
|
||||
|
||||
return callVM(DebugLeaveBlockInfo);
|
||||
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);
|
||||
@ -2649,7 +2690,7 @@ BaselineCompiler::emit_JSOP_DEBUGGER()
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*DebugEpilogueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool);
|
||||
typedef bool (*DebugEpilogueFn)(JSContext *, BaselineFrame *, bool);
|
||||
static const VMFunction DebugEpilogueInfo = FunctionInfo<DebugEpilogueFn>(jit::DebugEpilogue);
|
||||
|
||||
bool
|
||||
@ -2666,7 +2707,6 @@ BaselineCompiler::emitReturn()
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(Imm32(1));
|
||||
pushArg(ImmPtr(pc));
|
||||
pushArg(R0.scratchReg());
|
||||
if (!callVM(DebugEpilogueInfo))
|
||||
return false;
|
||||
|
@ -27,7 +27,6 @@ namespace jit {
|
||||
_(JSOP_NOTEARG) \
|
||||
_(JSOP_POP) \
|
||||
_(JSOP_POPN) \
|
||||
_(JSOP_POPNV) \
|
||||
_(JSOP_DUP) \
|
||||
_(JSOP_DUP2) \
|
||||
_(JSOP_SWAP) \
|
||||
@ -146,9 +145,13 @@ namespace jit {
|
||||
_(JSOP_FINALLY) \
|
||||
_(JSOP_GOSUB) \
|
||||
_(JSOP_RETSUB) \
|
||||
_(JSOP_PUSHBLOCKSCOPE) \
|
||||
_(JSOP_POPBLOCKSCOPE) \
|
||||
_(JSOP_DEBUGLEAVEBLOCK) \
|
||||
_(JSOP_ENTERBLOCK) \
|
||||
_(JSOP_ENTERLET0) \
|
||||
_(JSOP_ENTERLET1) \
|
||||
_(JSOP_ENTERLET2) \
|
||||
_(JSOP_LEAVEBLOCK) \
|
||||
_(JSOP_LEAVEBLOCKEXPR) \
|
||||
_(JSOP_LEAVEFORLETIN) \
|
||||
_(JSOP_EXCEPTION) \
|
||||
_(JSOP_DEBUGGER) \
|
||||
_(JSOP_ARGUMENTS) \
|
||||
@ -251,6 +254,9 @@ class BaselineCompiler : public BaselineCompilerSpecific
|
||||
|
||||
bool emitFormalArgAccess(uint32_t arg, bool get);
|
||||
|
||||
bool emitEnterBlock();
|
||||
bool emitLeaveBlock();
|
||||
|
||||
bool addPCMappingEntry(bool addIndexEntry);
|
||||
|
||||
void getScopeCoordinateObject(Register reg);
|
||||
|
@ -36,22 +36,34 @@ BaselineFrame::popOffScopeChain()
|
||||
inline bool
|
||||
BaselineFrame::pushBlock(JSContext *cx, Handle<StaticBlockObject *> block)
|
||||
{
|
||||
JS_ASSERT(block->needsClone());
|
||||
JS_ASSERT_IF(hasBlockChain(), blockChain() == *block->enclosingBlock());
|
||||
|
||||
ClonedBlockObject *clone = ClonedBlockObject::create(cx, block, this);
|
||||
if (!clone)
|
||||
return false;
|
||||
pushOnScopeChain(*clone);
|
||||
if (block->needsClone()) {
|
||||
ClonedBlockObject *clone = ClonedBlockObject::create(cx, block, this);
|
||||
if (!clone)
|
||||
return false;
|
||||
|
||||
pushOnScopeChain(*clone);
|
||||
}
|
||||
|
||||
setBlockChain(*block);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void
|
||||
BaselineFrame::popBlock(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(scopeChain_->is<ClonedBlockObject>());
|
||||
JS_ASSERT(hasBlockChain());
|
||||
|
||||
popOffScopeChain();
|
||||
if (cx->compartment()->debugMode())
|
||||
DebugScopes::onPopBlock(cx, this);
|
||||
|
||||
if (blockChain_->needsClone()) {
|
||||
JS_ASSERT(scopeChain_->as<ClonedBlockObject>().staticBlock() == *blockChain_);
|
||||
popOffScopeChain();
|
||||
}
|
||||
|
||||
setBlockChain(*blockChain_->enclosingBlock());
|
||||
}
|
||||
|
||||
inline CallObject &
|
||||
|
@ -113,6 +113,11 @@ BaselineFrame::initForOsr(StackFrame *fp, uint32_t numStackValues)
|
||||
if (fp->hasCallObjUnchecked())
|
||||
flags_ |= BaselineFrame::HAS_CALL_OBJ;
|
||||
|
||||
if (fp->hasBlockChain()) {
|
||||
flags_ |= BaselineFrame::HAS_BLOCKCHAIN;
|
||||
blockChain_ = &fp->blockChain();
|
||||
}
|
||||
|
||||
if (fp->isEvalFrame()) {
|
||||
flags_ |= BaselineFrame::EVAL;
|
||||
evalScript_ = fp->script();
|
||||
|
@ -38,6 +38,9 @@ class BaselineFrame
|
||||
// The frame has a valid return value. See also StackFrame::HAS_RVAL.
|
||||
HAS_RVAL = 1 << 0,
|
||||
|
||||
// Frame has blockChain_ set.
|
||||
HAS_BLOCKCHAIN = 1 << 1,
|
||||
|
||||
// A call object has been pushed on the scope chain.
|
||||
HAS_CALL_OBJ = 1 << 2,
|
||||
|
||||
@ -69,13 +72,11 @@ class BaselineFrame
|
||||
uint32_t hiReturnValue_;
|
||||
uint32_t frameSize_;
|
||||
JSObject *scopeChain_; // Scope chain (always initialized).
|
||||
StaticBlockObject *blockChain_; // If HAS_BLOCKCHAIN, the static block chain.
|
||||
JSScript *evalScript_; // If isEvalFrame(), the current eval script.
|
||||
ArgumentsObject *argsObj_; // If HAS_ARGS_OBJ, the arguments object.
|
||||
void *hookData_; // If HAS_HOOK_DATA, debugger call hook data.
|
||||
uint32_t flags_;
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
uint32_t padding_; // Pad to 8-byte alignment.
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Distance between the frame pointer and the frame header (return address).
|
||||
@ -171,7 +172,7 @@ class BaselineFrame
|
||||
|
||||
Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
|
||||
#ifdef DEBUG
|
||||
CheckLocalUnaliased(checkAliasing, script(), i);
|
||||
CheckLocalUnaliased(checkAliasing, script(), maybeBlockChain(), i);
|
||||
#endif
|
||||
return *valueSlot(i);
|
||||
}
|
||||
@ -211,6 +212,28 @@ class BaselineFrame
|
||||
return reinterpret_cast<Value *>(&loReturnValue_);
|
||||
}
|
||||
|
||||
bool hasBlockChain() const {
|
||||
return (flags_ & HAS_BLOCKCHAIN) && blockChain_;
|
||||
}
|
||||
StaticBlockObject &blockChain() const {
|
||||
JS_ASSERT(hasBlockChain());
|
||||
return *blockChain_;
|
||||
}
|
||||
StaticBlockObject *maybeBlockChain() const {
|
||||
return hasBlockChain() ? blockChain_ : nullptr;
|
||||
}
|
||||
void setBlockChain(StaticBlockObject &block) {
|
||||
flags_ |= HAS_BLOCKCHAIN;
|
||||
blockChain_ = █
|
||||
}
|
||||
void setBlockChainNull() {
|
||||
JS_ASSERT(!hasBlockChain());
|
||||
blockChain_ = nullptr;
|
||||
}
|
||||
StaticBlockObject **addressOfBlockChain() {
|
||||
return &blockChain_;
|
||||
}
|
||||
|
||||
bool hasCallObj() const {
|
||||
return flags_ & HAS_CALL_OBJ;
|
||||
}
|
||||
@ -360,6 +383,9 @@ class BaselineFrame
|
||||
static int reverseOffsetOfScopeChain() {
|
||||
return -int(Size()) + offsetof(BaselineFrame, scopeChain_);
|
||||
}
|
||||
static int reverseOffsetOfBlockChain() {
|
||||
return -int(Size()) + offsetof(BaselineFrame, blockChain_);
|
||||
}
|
||||
static int reverseOffsetOfArgsObj() {
|
||||
return -int(Size()) + offsetof(BaselineFrame, argsObj_);
|
||||
}
|
||||
|
@ -284,6 +284,9 @@ class FrameInfo
|
||||
Address addressOfScopeChain() const {
|
||||
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScopeChain());
|
||||
}
|
||||
Address addressOfBlockChain() const {
|
||||
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfBlockChain());
|
||||
}
|
||||
Address addressOfFlags() const {
|
||||
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags());
|
||||
}
|
||||
|
@ -1252,7 +1252,6 @@ IonBuilder::traverseBytecode()
|
||||
switch (op) {
|
||||
case JSOP_POP:
|
||||
case JSOP_POPN:
|
||||
case JSOP_POPNV:
|
||||
case JSOP_DUP:
|
||||
case JSOP_DUP2:
|
||||
case JSOP_PICK:
|
||||
@ -1505,15 +1504,6 @@ 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);
|
||||
|
@ -434,7 +434,7 @@ HandleExceptionBaseline(JSContext *cx, const IonFrameIterator &frame, ResumeFrom
|
||||
|
||||
case JSTRAP_RETURN:
|
||||
JS_ASSERT(baselineFrame->hasReturnValue());
|
||||
if (jit::DebugEpilogue(cx, baselineFrame, pc, true)) {
|
||||
if (jit::DebugEpilogue(cx, baselineFrame, true)) {
|
||||
rfe->kind = ResumeFromException::RESUME_FORCED_RETURN;
|
||||
rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
|
||||
rfe->stackPointer = reinterpret_cast<uint8_t *>(baselineFrame);
|
||||
@ -457,7 +457,6 @@ HandleExceptionBaseline(JSContext *cx, const IonFrameIterator &frame, ResumeFrom
|
||||
JSTryNote *tnEnd = tn + script->trynotes()->length;
|
||||
|
||||
uint32_t pcOffset = uint32_t(pc - script->main());
|
||||
ScopeIter si(frame.baselineFrame(), pc, cx);
|
||||
for (; tn != tnEnd; ++tn) {
|
||||
if (pcOffset < tn->start)
|
||||
continue;
|
||||
@ -473,7 +472,7 @@ HandleExceptionBaseline(JSContext *cx, const IonFrameIterator &frame, ResumeFrom
|
||||
|
||||
// Unwind scope chain (pop block objects).
|
||||
if (cx->isExceptionPending())
|
||||
UnwindScope(cx, si, tn->stackDepth);
|
||||
UnwindScope(cx, frame.baselineFrame(), tn->stackDepth);
|
||||
|
||||
// Compute base pointer and stack pointer.
|
||||
rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
|
||||
@ -608,10 +607,7 @@ HandleException(ResumeFromException *rfe)
|
||||
// If DebugEpilogue returns |true|, we have to perform a forced
|
||||
// return, e.g. return frame->returnValue() to the caller.
|
||||
BaselineFrame *frame = iter.baselineFrame();
|
||||
RootedScript script(cx);
|
||||
jsbytecode *pc;
|
||||
iter.baselineScriptAndPc(script.address(), &pc);
|
||||
if (jit::DebugEpilogue(cx, frame, pc, false)) {
|
||||
if (jit::DebugEpilogue(cx, frame, false)) {
|
||||
JS_ASSERT(frame->hasReturnValue());
|
||||
rfe->kind = ResumeFromException::RESUME_FORCED_RETURN;
|
||||
rfe->framePointer = iter.fp() - BaselineFrame::FramePointerOffset;
|
||||
|
@ -695,11 +695,11 @@ GetIndexFromString(JSString *str)
|
||||
}
|
||||
|
||||
bool
|
||||
DebugPrologue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn)
|
||||
DebugPrologue(JSContext *cx, BaselineFrame *frame, bool *mustReturn)
|
||||
{
|
||||
*mustReturn = false;
|
||||
|
||||
JSTrapStatus status = ScriptDebugPrologue(cx, frame, pc);
|
||||
JSTrapStatus status = ScriptDebugPrologue(cx, frame);
|
||||
switch (status) {
|
||||
case JSTRAP_CONTINUE:
|
||||
return true;
|
||||
@ -709,7 +709,7 @@ DebugPrologue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustRet
|
||||
// debug epilogue handler as well.
|
||||
JS_ASSERT(frame->hasReturnValue());
|
||||
*mustReturn = true;
|
||||
return jit::DebugEpilogue(cx, frame, pc, true);
|
||||
return jit::DebugEpilogue(cx, frame, true);
|
||||
|
||||
case JSTRAP_THROW:
|
||||
case JSTRAP_ERROR:
|
||||
@ -721,16 +721,15 @@ DebugPrologue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustRet
|
||||
}
|
||||
|
||||
bool
|
||||
DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok)
|
||||
DebugEpilogue(JSContext *cx, BaselineFrame *frame, bool ok)
|
||||
{
|
||||
// Unwind scope chain to stack depth 0.
|
||||
ScopeIter si(frame, pc, cx);
|
||||
UnwindScope(cx, si, 0);
|
||||
UnwindScope(cx, frame, 0);
|
||||
|
||||
// If ScriptDebugEpilogue returns |true| we have to return the frame's
|
||||
// return value. If it returns |false|, the debugger threw an exception.
|
||||
// In both cases we have to pop debug scopes.
|
||||
ok = ScriptDebugEpilogue(cx, frame, pc, ok);
|
||||
ok = ScriptDebugEpilogue(cx, frame, ok);
|
||||
|
||||
if (frame->isNonEvalFunctionFrame()) {
|
||||
JS_ASSERT_IF(ok, frame->hasReturnValue());
|
||||
@ -849,7 +848,7 @@ HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, bool *mus
|
||||
case JSTRAP_RETURN:
|
||||
*mustReturn = true;
|
||||
frame->setReturnValue(rval);
|
||||
return jit::DebugEpilogue(cx, frame, pc, true);
|
||||
return jit::DebugEpilogue(cx, frame, true);
|
||||
|
||||
case JSTRAP_THROW:
|
||||
cx->setPendingException(rval);
|
||||
@ -887,7 +886,7 @@ OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *m
|
||||
case JSTRAP_RETURN:
|
||||
frame->setReturnValue(rval);
|
||||
*mustReturn = true;
|
||||
return jit::DebugEpilogue(cx, frame, pc, true);
|
||||
return jit::DebugEpilogue(cx, frame, true);
|
||||
|
||||
case JSTRAP_THROW:
|
||||
cx->setPendingException(rval);
|
||||
@ -899,28 +898,18 @@ OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *m
|
||||
}
|
||||
|
||||
bool
|
||||
PushBlockScope(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block)
|
||||
EnterBlock(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block)
|
||||
{
|
||||
return frame->pushBlock(cx, block);
|
||||
}
|
||||
|
||||
bool
|
||||
PopBlockScope(JSContext *cx, BaselineFrame *frame)
|
||||
LeaveBlock(JSContext *cx, BaselineFrame *frame)
|
||||
{
|
||||
frame->popBlock(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(cx->compartment()->debugMode());
|
||||
|
||||
DebugScopes::onPopBlock(cx, frame, pc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame, uint32_t numStackValues)
|
||||
{
|
||||
|
@ -638,8 +638,8 @@ void PostGlobalWriteBarrier(JSRuntime *rt, JSObject *obj);
|
||||
|
||||
uint32_t GetIndexFromString(JSString *str);
|
||||
|
||||
bool DebugPrologue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn);
|
||||
bool DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok);
|
||||
bool DebugPrologue(JSContext *cx, BaselineFrame *frame, bool *mustReturn);
|
||||
bool DebugEpilogue(JSContext *cx, BaselineFrame *frame, bool ok);
|
||||
|
||||
bool StrictEvalPrologue(JSContext *cx, BaselineFrame *frame);
|
||||
bool HeavyweightFunPrologue(JSContext *cx, BaselineFrame *frame);
|
||||
@ -652,9 +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 PushBlockScope(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block);
|
||||
bool PopBlockScope(JSContext *cx, BaselineFrame *frame);
|
||||
bool DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc);
|
||||
bool EnterBlock(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block);
|
||||
bool LeaveBlock(JSContext *cx, BaselineFrame *frame);
|
||||
|
||||
bool InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame,
|
||||
uint32_t numStackValues);
|
||||
|
@ -253,6 +253,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
|
||||
|
||||
case JSOP_EVAL:
|
||||
case JSOP_SPREADEVAL:
|
||||
case JSOP_ENTERLET2:
|
||||
case JSOP_ENTERWITH:
|
||||
canTrackVars = false;
|
||||
break;
|
||||
|
@ -100,8 +100,7 @@ class JSFunction : public JSObject
|
||||
// Note: this should be kept in sync with FunctionBox::isHeavyweight().
|
||||
return nonLazyScript()->bindings.hasAnyAliasedBindings() ||
|
||||
nonLazyScript()->funHasExtensibleScope ||
|
||||
nonLazyScript()->funNeedsDeclEnvObject ||
|
||||
isGenerator();
|
||||
nonLazyScript()->funNeedsDeclEnvObject;
|
||||
}
|
||||
|
||||
/* A function can be classified as either native (C++) or interpreted (JS): */
|
||||
|
@ -5674,8 +5674,9 @@ js_DumpStackFrame(JSContext *cx, StackFrame *start)
|
||||
if (jsbytecode *pc = i.pc()) {
|
||||
fprintf(stderr, " pc = %p\n", pc);
|
||||
fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
|
||||
MaybeDumpObject("blockChain", i.script()->getBlockScope(pc));
|
||||
}
|
||||
if (!i.isJit())
|
||||
MaybeDumpObject("blockChain", i.interpFrame()->maybeBlockChain());
|
||||
MaybeDumpValue("this", i.thisv());
|
||||
if (!i.isJit()) {
|
||||
fprintf(stderr, " rval: ");
|
||||
|
@ -117,6 +117,18 @@ 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)
|
||||
{
|
||||
@ -129,8 +141,16 @@ js::StackUses(JSScript *script, jsbytecode *pc)
|
||||
switch (op) {
|
||||
case JSOP_POPN:
|
||||
return GET_UINT16(pc);
|
||||
case JSOP_POPNV:
|
||||
case JSOP_LEAVEBLOCK:
|
||||
return GET_UINT16(pc);
|
||||
case JSOP_LEAVEBLOCKEXPR:
|
||||
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 ||
|
||||
@ -144,8 +164,15 @@ js::StackDefs(JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
JSOp op = (JSOp) *pc;
|
||||
const JSCodeSpec &cs = js_CodeSpec[op];
|
||||
JS_ASSERT (cs.ndefs >= 0);
|
||||
return cs.ndefs;
|
||||
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;
|
||||
}
|
||||
|
||||
static const char * const countBaseNames[] = {
|
||||
@ -754,7 +781,7 @@ js_DisassembleAtPC(JSContext *cx, JSScript *scriptArg, bool lines,
|
||||
if (parser.isReachable(next))
|
||||
Sprint(sp, "%05u ", parser.stackDepthAtPC(next));
|
||||
else
|
||||
Sprint(sp, " ");
|
||||
Sprint(sp, " ", parser.stackDepthAtPC(next));
|
||||
}
|
||||
len = js_Disassemble1(cx, script, next, script->pcToOffset(next), lines, sp);
|
||||
if (!len)
|
||||
@ -1405,6 +1432,30 @@ js_QuoteString(ExclusiveContext *cx, JSString *str, jschar quote)
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
static JSObject *
|
||||
GetBlockChainAtPC(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(script->containsPC(pc));
|
||||
JS_ASSERT(pc >= script->main());
|
||||
|
||||
ptrdiff_t offset = pc - script->main();
|
||||
|
||||
if (!script->hasBlockScopes())
|
||||
return nullptr;
|
||||
|
||||
BlockScopeArray *blockScopes = script->blockScopes();
|
||||
JSObject *blockChain = nullptr;
|
||||
for (uint32_t n = 0; n < blockScopes->length; n++) {
|
||||
const BlockScopeNote *note = &blockScopes->vector[n];
|
||||
if (note->start > offset)
|
||||
break;
|
||||
if (offset <= note->start + note->length)
|
||||
blockChain = script->getObject(note->index);
|
||||
}
|
||||
|
||||
return blockChain;
|
||||
}
|
||||
|
||||
namespace {
|
||||
/*
|
||||
* The expression decompiler is invoked by error handling code to produce a
|
||||
@ -1664,17 +1715,24 @@ ExpressionDecompiler::loadAtom(jsbytecode *pc)
|
||||
JSAtom *
|
||||
ExpressionDecompiler::findLetVar(jsbytecode *pc, unsigned depth)
|
||||
{
|
||||
for (JSObject *chain = script->getBlockScope(pc); chain; chain = chain->getParent()) {
|
||||
StaticBlockObject &block = chain->as<StaticBlockObject>();
|
||||
uint32_t blockDepth = block.stackDepth();
|
||||
uint32_t blockCount = block.slotCount();
|
||||
if (uint32_t(depth - blockDepth) < uint32_t(blockCount)) {
|
||||
for (Shape::Range<NoGC> r(block.lastProperty()); !r.empty(); r.popFront()) {
|
||||
const Shape &shape = r.front();
|
||||
if (shape.shortid() == int(depth - blockDepth))
|
||||
return JSID_TO_ATOM(shape.propid());
|
||||
if (script->hasObjects()) {
|
||||
JSObject *chain = GetBlockChainAtPC(cx, script, pc);
|
||||
if (!chain)
|
||||
return nullptr;
|
||||
JS_ASSERT(chain->is<BlockObject>());
|
||||
do {
|
||||
BlockObject &block = chain->as<BlockObject>();
|
||||
uint32_t blockDepth = block.stackDepth();
|
||||
uint32_t blockCount = block.slotCount();
|
||||
if (uint32_t(depth - blockDepth) < uint32_t(blockCount)) {
|
||||
for (Shape::Range<NoGC> r(block.lastProperty()); !r.empty(); r.popFront()) {
|
||||
const Shape &shape = r.front();
|
||||
if (shape.shortid() == int(depth - blockDepth))
|
||||
return JSID_TO_ATOM(shape.propid());
|
||||
}
|
||||
}
|
||||
}
|
||||
chain = chain->getParent();
|
||||
} while (chain && chain->is<BlockObject>());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -97,9 +97,7 @@ 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)
|
||||
|
||||
/* Pop N values, preserving top value. */
|
||||
OPDEF(JSOP_POPNV, 44, "popnv", NULL, 3, -1, 1, JOF_UINT16)
|
||||
|
||||
OPDEF(JSOP_UNUSED44, 44, "unused44", NULL, 1, 0, 0, JOF_BYTE)
|
||||
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)
|
||||
@ -222,7 +220,9 @@ 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)
|
||||
OPDEF(JSOP_UNUSED105, 105, "unused105", 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)
|
||||
|
||||
/* 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)
|
||||
@ -400,9 +400,13 @@ 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)
|
||||
|
||||
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)
|
||||
/* 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)
|
||||
|
||||
/*
|
||||
* Opcode to hold 24-bit immediate integer operands.
|
||||
@ -434,10 +438,11 @@ OPDEF(JSOP_TYPEOFEXPR, 197,"typeofexpr", NULL, 1, 1, 1, JOF_BYTE|JOF_DE
|
||||
/*
|
||||
* Block-local scope support.
|
||||
*/
|
||||
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_ENTERBLOCK, 198,"enterblock", NULL, 5, 0, -1, JOF_OBJECT)
|
||||
OPDEF(JSOP_LEAVEBLOCK, 199,"leaveblock", NULL, 3, -1, 0, JOF_UINT16)
|
||||
|
||||
|
||||
OPDEF(JSOP_UNUSED200, 200,"unused200", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED201, 201,"unused201", NULL, 1, 0, 0, JOF_BYTE)
|
||||
|
||||
/*
|
||||
@ -457,7 +462,12 @@ OPDEF(JSOP_GETFUNNS, 205,"getfunns", NULL, 1, 0, 1, JOF_BYTE)
|
||||
*/
|
||||
OPDEF(JSOP_ENUMCONSTELEM, 206,"enumconstelem",NULL, 1, 3, 0, JOF_BYTE|JOF_SET)
|
||||
|
||||
OPDEF(JSOP_UNUSED207, 207, "unused207", NULL, 1, 0, 0, JOF_BYTE)
|
||||
/*
|
||||
* 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_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)
|
||||
|
@ -2893,36 +2893,6 @@ LazyScript::finalize(FreeOp *fop)
|
||||
fop->free_(table_);
|
||||
}
|
||||
|
||||
StaticBlockObject *
|
||||
JSScript::getBlockScope(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(containsPC(pc));
|
||||
|
||||
ptrdiff_t offset = pc - main();
|
||||
|
||||
if (offset < 0)
|
||||
return nullptr;
|
||||
|
||||
if (!hasBlockScopes())
|
||||
return nullptr;
|
||||
|
||||
BlockScopeArray *scopeArray = blockScopes();
|
||||
JSObject *blockChain = nullptr;
|
||||
for (uint32_t n = 0; n < scopeArray->length; n++) {
|
||||
const BlockScopeNote *note = &scopeArray->vector[n];
|
||||
if (note->start > offset)
|
||||
break;
|
||||
if (offset < note->start + note->length) {
|
||||
if (note->index == BlockScopeNote::NoBlockScopeIndex)
|
||||
blockChain = nullptr;
|
||||
else
|
||||
blockChain = getObject(note->index);
|
||||
}
|
||||
}
|
||||
|
||||
return blockChain ? &blockChain->as<StaticBlockObject>() : nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
JSScript::setArgumentsHasVarBinding()
|
||||
{
|
||||
|
@ -43,7 +43,6 @@ class RegExpObject;
|
||||
struct SourceCompressionTask;
|
||||
class Shape;
|
||||
class WatchpointMap;
|
||||
class StaticBlockObject;
|
||||
|
||||
namespace analyze {
|
||||
class ScriptAnalysis;
|
||||
@ -82,27 +81,9 @@ struct JSTryNote {
|
||||
|
||||
namespace js {
|
||||
|
||||
// A block scope has a range in bytecode: it is entered at some offset, and left
|
||||
// at some later offset. Scopes can be nested. Given an offset, the
|
||||
// BlockScopeNote containing that offset whose with the highest start value
|
||||
// indicates the block scope. The block scope list is sorted by increasing
|
||||
// start value.
|
||||
//
|
||||
// It is possible to leave a scope nonlocally, for example via a "break"
|
||||
// statement, so there may be short bytecode ranges in a block scope in which we
|
||||
// are popping the block chain in preparation for a goto. These exits are also
|
||||
// nested with respect to outer scopes. The scopes in these exits are indicated
|
||||
// by the "index" field, just like any other block. If a nonlocal exit pops the
|
||||
// last block scope, the index will be NoBlockScopeIndex.
|
||||
//
|
||||
struct BlockScopeNote {
|
||||
static const uint32_t NoBlockScopeIndex = UINT32_MAX;
|
||||
|
||||
uint32_t index; // Index of StaticScopeObject in the object
|
||||
// array, or NoBlockScopeIndex if there is no
|
||||
// block scope in this range.
|
||||
uint32_t start; // Bytecode offset at which this scope starts,
|
||||
// from script->main().
|
||||
uint32_t index; // Index of StaticScopeObject in the object array.
|
||||
uint32_t start; // Bytecode offset at which this scope starts.
|
||||
uint32_t length; // Bytecode length of scope.
|
||||
uint32_t padding; // Pad to 64-bit boundary.
|
||||
};
|
||||
@ -1105,8 +1086,6 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
|
||||
return arr->vector[index];
|
||||
}
|
||||
|
||||
js::StaticBlockObject *getBlockScope(jsbytecode *pc);
|
||||
|
||||
/*
|
||||
* The isEmpty method tells whether this script has code that computes any
|
||||
* result (not return value, result AKA normal completion value) other than
|
||||
|
@ -1512,7 +1512,7 @@ TrapHandler(JSContext *cx, JSScript *, jsbytecode *pc, jsval *rvalArg,
|
||||
JS_ASSERT(!iter.done());
|
||||
|
||||
/* Debug-mode currently disables Ion compilation. */
|
||||
JSAbstractFramePtr frame(iter.abstractFramePtr().raw(), iter.pc());
|
||||
JSAbstractFramePtr frame(Jsvalify(iter.abstractFramePtr()));
|
||||
RootedScript script(cx, iter.script());
|
||||
|
||||
size_t length;
|
||||
@ -2590,7 +2590,7 @@ EvalInFrame(JSContext *cx, unsigned argc, jsval *vp)
|
||||
if (!chars)
|
||||
return false;
|
||||
|
||||
JSAbstractFramePtr frame(fi.abstractFramePtr().raw(), fi.pc());
|
||||
JSAbstractFramePtr frame(Jsvalify(fi.abstractFramePtr()));
|
||||
RootedScript fpscript(cx, frame.script());
|
||||
bool ok = !!frame.evaluateUCInStackFrame(cx, chars, length,
|
||||
fpscript->filename(),
|
||||
|
@ -3987,12 +3987,12 @@ DebuggerFrame_getType(JSContext *cx, unsigned argc, Value *vp)
|
||||
static bool
|
||||
DebuggerFrame_getEnvironment(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_FRAME_OWNER_ITER(cx, argc, vp, "get environment", args, thisobj, _, iter, dbg);
|
||||
THIS_FRAME_OWNER(cx, argc, vp, "get environment", args, thisobj, frame, dbg);
|
||||
|
||||
Rooted<Env*> env(cx);
|
||||
{
|
||||
AutoCompartment ac(cx, iter.abstractFramePtr().scopeChain());
|
||||
env = GetDebugScopeForFrame(cx, iter.abstractFramePtr(), iter.pc());
|
||||
AutoCompartment ac(cx, frame.scopeChain());
|
||||
env = GetDebugScopeForFrame(cx, frame);
|
||||
if (!env)
|
||||
return false;
|
||||
}
|
||||
@ -4438,7 +4438,7 @@ DebuggerGenericEval(JSContext *cx, const char *fullMethodName, const Value &code
|
||||
if (!iter->computeThis(cx))
|
||||
return false;
|
||||
thisv = iter->thisv();
|
||||
env = GetDebugScopeForFrame(cx, iter->abstractFramePtr(), iter->pc());
|
||||
env = GetDebugScopeForFrame(cx, iter->abstractFramePtr());
|
||||
if (!env)
|
||||
return false;
|
||||
} else {
|
||||
|
@ -845,22 +845,22 @@ EnterWith(JSContext *cx, AbstractFramePtr frame, HandleValue val, uint32_t stack
|
||||
|
||||
/* Unwind block and scope chains to match the given depth. */
|
||||
void
|
||||
js::UnwindScope(JSContext *cx, ScopeIter &si, uint32_t stackDepth)
|
||||
js::UnwindScope(JSContext *cx, AbstractFramePtr frame, uint32_t stackDepth)
|
||||
{
|
||||
for (; !si.done(); ++si) {
|
||||
JS_ASSERT_IF(frame.isStackFrame(), frame.asStackFrame() == cx->interpreterFrame());
|
||||
JS_ASSERT_IF(frame.isStackFrame(), stackDepth <= cx->interpreterRegs().stackDepth());
|
||||
|
||||
for (ScopeIter si(frame, cx); !si.done(); ++si) {
|
||||
switch (si.type()) {
|
||||
case ScopeIter::Block:
|
||||
if (si.staticBlock().stackDepth() < stackDepth)
|
||||
return;
|
||||
if (cx->compartment()->debugMode())
|
||||
DebugScopes::onPopBlock(cx, si);
|
||||
if (si.staticBlock().needsClone())
|
||||
si.frame().popBlock(cx);
|
||||
frame.popBlock(cx);
|
||||
break;
|
||||
case ScopeIter::With:
|
||||
if (si.scope().as<WithObject>().stackDepth() < stackDepth)
|
||||
return;
|
||||
si.frame().popWith(cx);
|
||||
frame.popWith(cx);
|
||||
break;
|
||||
case ScopeIter::Call:
|
||||
case ScopeIter::StrictEvalScope:
|
||||
@ -869,24 +869,10 @@ js::UnwindScope(JSContext *cx, ScopeIter &si, uint32_t stackDepth)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ForcedReturn(JSContext *cx, ScopeIter &si, FrameRegs ®s)
|
||||
{
|
||||
UnwindScope(cx, si, 0);
|
||||
regs.setToEndOfScript();
|
||||
}
|
||||
|
||||
static void
|
||||
ForcedReturn(JSContext *cx, FrameRegs ®s)
|
||||
{
|
||||
ScopeIter si(regs.fp(), regs.pc, cx);
|
||||
ForcedReturn(cx, si, regs);
|
||||
}
|
||||
|
||||
void
|
||||
js::UnwindForUncatchableException(JSContext *cx, const FrameRegs ®s)
|
||||
{
|
||||
/* c.f. the regular (catchable) TryNoteIter loop in HandleError. */
|
||||
/* c.f. the regular (catchable) TryNoteIter loop in Interpret. */
|
||||
for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) {
|
||||
JSTryNote *tn = *tni;
|
||||
if (tn->kind == JSTRY_ITER) {
|
||||
@ -955,107 +941,6 @@ TryNoteIter::settle()
|
||||
}
|
||||
}
|
||||
|
||||
enum HandleErrorContinuation
|
||||
{
|
||||
SuccessfulReturnContinuation,
|
||||
ErrorReturnContinuation,
|
||||
CatchContinuation,
|
||||
FinallyContinuation
|
||||
};
|
||||
|
||||
static HandleErrorContinuation
|
||||
HandleError(JSContext *cx, FrameRegs ®s)
|
||||
{
|
||||
JS_ASSERT(regs.fp()->script()->containsPC(regs.pc));
|
||||
|
||||
ScopeIter si(regs.fp(), regs.pc, cx);
|
||||
bool ok = false;
|
||||
|
||||
again:
|
||||
if (cx->isExceptionPending()) {
|
||||
/* Call debugger throw hooks. */
|
||||
if (JS_UNLIKELY(cx->compartment()->debugMode())) {
|
||||
JSTrapStatus status = DebugExceptionUnwind(cx, regs.fp(), regs.pc);
|
||||
switch (status) {
|
||||
case JSTRAP_ERROR:
|
||||
goto again;
|
||||
|
||||
case JSTRAP_CONTINUE:
|
||||
case JSTRAP_THROW:
|
||||
break;
|
||||
|
||||
case JSTRAP_RETURN:
|
||||
ForcedReturn(cx, si, regs);
|
||||
return SuccessfulReturnContinuation;
|
||||
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid trap status");
|
||||
}
|
||||
}
|
||||
|
||||
for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) {
|
||||
JSTryNote *tn = *tni;
|
||||
|
||||
UnwindScope(cx, si, tn->stackDepth);
|
||||
|
||||
/*
|
||||
* Set pc to the first bytecode after the the try note to point
|
||||
* to the beginning of catch or finally or to [enditer] closing
|
||||
* the for-in loop.
|
||||
*/
|
||||
regs.pc = regs.fp()->script()->main() + tn->start + tn->length;
|
||||
regs.sp = regs.spForStackDepth(tn->stackDepth);
|
||||
|
||||
switch (tn->kind) {
|
||||
case JSTRY_CATCH:
|
||||
/* Catch cannot intercept the closing of a generator. */
|
||||
if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
|
||||
break;
|
||||
|
||||
/*
|
||||
* Don't clear exceptions to save cx->exception from GC
|
||||
* until it is pushed to the stack via [exception] in the
|
||||
* catch block.
|
||||
*/
|
||||
return CatchContinuation;
|
||||
|
||||
case JSTRY_FINALLY:
|
||||
return FinallyContinuation;
|
||||
|
||||
case JSTRY_ITER: {
|
||||
/* This is similar to JSOP_ENDITER in the interpreter loop. */
|
||||
JS_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER);
|
||||
RootedObject obj(cx, ®s.sp[-1].toObject());
|
||||
bool ok = UnwindIteratorForException(cx, obj);
|
||||
regs.sp -= 1;
|
||||
if (!ok)
|
||||
goto again;
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRY_LOOP:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Propagate the exception or error to the caller unless the exception
|
||||
* is an asynchronous return from a generator.
|
||||
*/
|
||||
if (JS_UNLIKELY(cx->isExceptionPending() &&
|
||||
cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) {
|
||||
cx->clearPendingException();
|
||||
ok = true;
|
||||
regs.fp()->clearReturnValue();
|
||||
}
|
||||
} else {
|
||||
UnwindForUncatchableException(cx, regs);
|
||||
}
|
||||
|
||||
ForcedReturn(cx, si, regs);
|
||||
return ok ? SuccessfulReturnContinuation : ErrorReturnContinuation;
|
||||
}
|
||||
|
||||
#define REGS (activation.regs())
|
||||
#define PUSH_COPY(v) do { *REGS.sp++ = (v); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0)
|
||||
#define PUSH_COPY_SKIP_CHECK(v) *REGS.sp++ = (v)
|
||||
@ -1486,13 +1371,13 @@ Interpret(JSContext *cx, RunState &state)
|
||||
probes::EnterScript(cx, script, script->function(), activation.entryFrame());
|
||||
}
|
||||
if (JS_UNLIKELY(cx->compartment()->debugMode())) {
|
||||
JSTrapStatus status = ScriptDebugPrologue(cx, activation.entryFrame(), REGS.pc);
|
||||
JSTrapStatus status = ScriptDebugPrologue(cx, activation.entryFrame());
|
||||
switch (status) {
|
||||
case JSTRAP_CONTINUE:
|
||||
break;
|
||||
case JSTRAP_RETURN:
|
||||
ForcedReturn(cx, REGS);
|
||||
goto successful_return_continuation;
|
||||
interpReturnOK = true;
|
||||
goto forced_return;
|
||||
case JSTRAP_THROW:
|
||||
case JSTRAP_ERROR:
|
||||
goto error;
|
||||
@ -1504,6 +1389,7 @@ Interpret(JSContext *cx, RunState &state)
|
||||
if (cx->runtime()->profilingScripts || cx->runtime()->debugHooks.interruptHook)
|
||||
activation.enableInterruptsUnconditionally();
|
||||
|
||||
enterInterpreterLoop:
|
||||
// Enter the interpreter loop starting at the current pc.
|
||||
ADVANCE_AND_DISPATCH(0);
|
||||
|
||||
@ -1543,8 +1429,8 @@ CASE(EnableInterruptsPseudoOpcode)
|
||||
break;
|
||||
case JSTRAP_RETURN:
|
||||
REGS.fp()->setReturnValue(rval);
|
||||
ForcedReturn(cx, REGS);
|
||||
goto successful_return_continuation;
|
||||
interpReturnOK = true;
|
||||
goto forced_return;
|
||||
case JSTRAP_THROW:
|
||||
cx->setPendingException(rval);
|
||||
goto error;
|
||||
@ -1564,8 +1450,8 @@ CASE(EnableInterruptsPseudoOpcode)
|
||||
goto error;
|
||||
case JSTRAP_RETURN:
|
||||
REGS.fp()->setReturnValue(rval);
|
||||
ForcedReturn(cx, REGS);
|
||||
goto successful_return_continuation;
|
||||
interpReturnOK = true;
|
||||
goto forced_return;
|
||||
case JSTRAP_THROW:
|
||||
cx->setPendingException(rval);
|
||||
goto error;
|
||||
@ -1589,6 +1475,7 @@ 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)
|
||||
@ -1601,7 +1488,6 @@ 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,18 +1527,15 @@ 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)
|
||||
CASE(JSOP_UNUSED192)
|
||||
CASE(JSOP_UNUSED194)
|
||||
CASE(JSOP_UNUSED196)
|
||||
CASE(JSOP_UNUSED200)
|
||||
CASE(JSOP_UNUSED201)
|
||||
CASE(JSOP_GETFUNNS)
|
||||
CASE(JSOP_UNUSED207)
|
||||
CASE(JSOP_UNUSED208)
|
||||
CASE(JSOP_UNUSED209)
|
||||
CASE(JSOP_UNUSED210)
|
||||
@ -1718,24 +1601,11 @@ CASE(JSOP_POPN)
|
||||
JS_ASSERT(GET_UINT16(REGS.pc) <= REGS.stackDepth());
|
||||
REGS.sp -= GET_UINT16(REGS.pc);
|
||||
#ifdef DEBUG
|
||||
if (StaticBlockObject *block = script->getBlockScope(REGS.pc + JSOP_POPN_LENGTH))
|
||||
if (StaticBlockObject *block = REGS.fp()->maybeBlockChain())
|
||||
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)
|
||||
@ -1779,9 +1649,7 @@ CASE(JSOP_RETRVAL)
|
||||
*/
|
||||
CHECK_BRANCH();
|
||||
|
||||
successful_return_continuation:
|
||||
interpReturnOK = true;
|
||||
return_continuation:
|
||||
if (activation.entryFrame() != REGS.fp())
|
||||
inline_return:
|
||||
{
|
||||
@ -1790,7 +1658,7 @@ CASE(JSOP_RETRVAL)
|
||||
#endif
|
||||
|
||||
if (JS_UNLIKELY(cx->compartment()->debugMode()))
|
||||
interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK);
|
||||
interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), interpReturnOK);
|
||||
|
||||
if (!REGS.fp()->isYielding())
|
||||
REGS.fp()->epilogue(cx);
|
||||
@ -1823,6 +1691,7 @@ CASE(JSOP_RETRVAL)
|
||||
} else {
|
||||
JS_ASSERT(REGS.stackDepth() == 0);
|
||||
}
|
||||
interpReturnOK = true;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@ -2698,12 +2567,12 @@ CASE(JSOP_FUNCALL)
|
||||
if (!REGS.fp()->prologue(cx))
|
||||
goto error;
|
||||
if (JS_UNLIKELY(cx->compartment()->debugMode())) {
|
||||
switch (ScriptDebugPrologue(cx, REGS.fp(), REGS.pc)) {
|
||||
switch (ScriptDebugPrologue(cx, REGS.fp())) {
|
||||
case JSTRAP_CONTINUE:
|
||||
break;
|
||||
case JSTRAP_RETURN:
|
||||
ForcedReturn(cx, REGS);
|
||||
goto successful_return_continuation;
|
||||
interpReturnOK = true;
|
||||
goto forced_return;
|
||||
case JSTRAP_THROW:
|
||||
case JSTRAP_ERROR:
|
||||
goto error;
|
||||
@ -3343,8 +3212,8 @@ CASE(JSOP_DEBUGGER)
|
||||
break;
|
||||
case JSTRAP_RETURN:
|
||||
REGS.fp()->setReturnValue(rval);
|
||||
ForcedReturn(cx, REGS);
|
||||
goto successful_return_continuation;
|
||||
interpReturnOK = true;
|
||||
goto forced_return;
|
||||
case JSTRAP_THROW:
|
||||
cx->setPendingException(rval);
|
||||
goto error;
|
||||
@ -3353,49 +3222,51 @@ CASE(JSOP_DEBUGGER)
|
||||
}
|
||||
END_CASE(JSOP_DEBUGGER)
|
||||
|
||||
CASE(JSOP_PUSHBLOCKSCOPE)
|
||||
CASE(JSOP_ENTERBLOCK)
|
||||
CASE(JSOP_ENTERLET0)
|
||||
CASE(JSOP_ENTERLET1)
|
||||
CASE(JSOP_ENTERLET2)
|
||||
{
|
||||
StaticBlockObject &blockObj = script->getObject(REGS.pc)->as<StaticBlockObject>();
|
||||
|
||||
JS_ASSERT(blockObj.needsClone());
|
||||
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;
|
||||
}
|
||||
|
||||
// FIXME: "Aliased" slots don't need to be on the stack.
|
||||
JS_ASSERT(REGS.stackDepth() >= blockObj.stackDepth() + blockObj.slotCount());
|
||||
|
||||
// Clone block and push on scope chain.
|
||||
/* Clone block iff there are any closed-over variables. */
|
||||
if (!REGS.fp()->pushBlock(cx, blockObj))
|
||||
goto error;
|
||||
}
|
||||
END_CASE(JSOP_PUSHBLOCKSCOPE)
|
||||
END_CASE(JSOP_ENTERBLOCK)
|
||||
|
||||
CASE(JSOP_POPBLOCKSCOPE)
|
||||
CASE(JSOP_LEAVEBLOCK)
|
||||
CASE(JSOP_LEAVEFORLETIN)
|
||||
CASE(JSOP_LEAVEBLOCKEXPR)
|
||||
{
|
||||
#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());
|
||||
blockDepth = REGS.fp()->blockChain().stackDepth();
|
||||
|
||||
// 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_POPBLOCKSCOPE)
|
||||
|
||||
CASE(JSOP_DEBUGLEAVEBLOCK)
|
||||
{
|
||||
JS_ASSERT(script->getBlockScope(REGS.pc));
|
||||
|
||||
// FIXME: This opcode should not be necessary. The debugger shouldn't need
|
||||
// help from bytecode to do its job. See bug 927782.
|
||||
|
||||
if (JS_UNLIKELY(cx->compartment()->debugMode()))
|
||||
DebugScopes::onPopBlock(cx, REGS.fp(), REGS.pc);
|
||||
}
|
||||
END_CASE(JSOP_DEBUGLEAVEBLOCK)
|
||||
END_CASE(JSOP_LEAVEBLOCK)
|
||||
|
||||
CASE(JSOP_GENERATOR)
|
||||
{
|
||||
@ -3455,32 +3326,118 @@ DEFAULT()
|
||||
MOZ_ASSUME_UNREACHABLE("Interpreter loop exited via fallthrough");
|
||||
|
||||
error:
|
||||
switch (HandleError(cx, REGS)) {
|
||||
case SuccessfulReturnContinuation:
|
||||
goto successful_return_continuation;
|
||||
JS_ASSERT(script->containsPC(REGS.pc));
|
||||
|
||||
case ErrorReturnContinuation:
|
||||
interpReturnOK = false;
|
||||
goto return_continuation;
|
||||
if (cx->isExceptionPending()) {
|
||||
/* Call debugger throw hooks. */
|
||||
if (JS_UNLIKELY(cx->compartment()->debugMode())) {
|
||||
JSTrapStatus status = DebugExceptionUnwind(cx, REGS.fp(), REGS.pc);
|
||||
switch (status) {
|
||||
case JSTRAP_ERROR:
|
||||
goto error;
|
||||
|
||||
case CatchContinuation:
|
||||
ADVANCE_AND_DISPATCH(0);
|
||||
case JSTRAP_CONTINUE:
|
||||
case JSTRAP_THROW:
|
||||
break;
|
||||
|
||||
case JSTRAP_RETURN:
|
||||
interpReturnOK = true;
|
||||
goto forced_return;
|
||||
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid trap status");
|
||||
}
|
||||
}
|
||||
|
||||
for (TryNoteIter tni(cx, REGS); !tni.done(); ++tni) {
|
||||
JSTryNote *tn = *tni;
|
||||
|
||||
UnwindScope(cx, REGS.fp(), tn->stackDepth);
|
||||
|
||||
/*
|
||||
* Set pc to the first bytecode after the the try note to point
|
||||
* to the beginning of catch or finally or to [enditer] closing
|
||||
* the for-in loop.
|
||||
*/
|
||||
REGS.pc = (script)->main() + tn->start + tn->length;
|
||||
REGS.sp = REGS.spForStackDepth(tn->stackDepth);
|
||||
|
||||
switch (tn->kind) {
|
||||
case JSTRY_CATCH:
|
||||
JS_ASSERT(*REGS.pc == JSOP_ENTERBLOCK || *REGS.pc == JSOP_EXCEPTION);
|
||||
|
||||
/* Catch cannot intercept the closing of a generator. */
|
||||
if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
|
||||
break;
|
||||
|
||||
/*
|
||||
* Don't clear exceptions to save cx->exception from GC
|
||||
* until it is pushed to the stack via [exception] in the
|
||||
* catch block.
|
||||
*
|
||||
* Also, see the comment below about the use of goto here.
|
||||
*/
|
||||
goto enterInterpreterLoop;
|
||||
|
||||
case JSTRY_FINALLY:
|
||||
/*
|
||||
* Push (true, exception) pair for finally to indicate that
|
||||
* [retsub] should rethrow the exception.
|
||||
*/
|
||||
PUSH_BOOLEAN(true);
|
||||
PUSH_COPY(cx->getPendingException());
|
||||
cx->clearPendingException();
|
||||
|
||||
/*
|
||||
* Leave the scope via a plain goto (and not via
|
||||
* ADVANCE_AND_DISPATCH, which may be implemented with indirect
|
||||
* goto) so that the TryNoteIter goes out of scope properly.
|
||||
*/
|
||||
goto enterInterpreterLoop;
|
||||
|
||||
case JSTRY_ITER: {
|
||||
/* This is similar to JSOP_ENDITER in the interpreter loop. */
|
||||
JS_ASSERT(JSOp(*REGS.pc) == JSOP_ENDITER);
|
||||
RootedObject &obj = rootObject0;
|
||||
obj = ®S.sp[-1].toObject();
|
||||
bool ok = UnwindIteratorForException(cx, obj);
|
||||
REGS.sp -= 1;
|
||||
if (!ok)
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRY_LOOP:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case FinallyContinuation:
|
||||
/*
|
||||
* Push (true, exception) pair for finally to indicate that [retsub]
|
||||
* should rethrow the exception.
|
||||
* Propagate the exception or error to the caller unless the exception
|
||||
* is an asynchronous return from a generator.
|
||||
*/
|
||||
PUSH_BOOLEAN(true);
|
||||
PUSH_COPY(cx->getPendingException());
|
||||
cx->clearPendingException();
|
||||
ADVANCE_AND_DISPATCH(0);
|
||||
interpReturnOK = false;
|
||||
if (JS_UNLIKELY(cx->isExceptionPending() &&
|
||||
cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) {
|
||||
cx->clearPendingException();
|
||||
interpReturnOK = true;
|
||||
REGS.fp()->clearReturnValue();
|
||||
}
|
||||
} else {
|
||||
UnwindForUncatchableException(cx, REGS);
|
||||
interpReturnOK = false;
|
||||
}
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid HandleError continuation");
|
||||
|
||||
forced_return:
|
||||
UnwindScope(cx, REGS.fp(), 0);
|
||||
REGS.setToEndOfScript();
|
||||
|
||||
if (activation.entryFrame() != REGS.fp())
|
||||
goto inline_return;
|
||||
|
||||
exit:
|
||||
if (JS_UNLIKELY(cx->compartment()->debugMode()))
|
||||
interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK);
|
||||
interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), interpReturnOK);
|
||||
if (!REGS.fp()->isYielding())
|
||||
REGS.fp()->epilogue(cx);
|
||||
else
|
||||
|
@ -18,8 +18,6 @@
|
||||
|
||||
namespace js {
|
||||
|
||||
class ScopeIter;
|
||||
|
||||
/*
|
||||
* Announce to the debugger that the thread has entered a new JavaScript frame,
|
||||
* |frame|. Call whatever hooks have been registered to observe new frames, and
|
||||
@ -38,7 +36,7 @@ class ScopeIter;
|
||||
* has set |frame|'s return value appropriately.
|
||||
*/
|
||||
extern JSTrapStatus
|
||||
ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
|
||||
ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame);
|
||||
|
||||
/*
|
||||
* Announce to the debugger that the thread has exited a JavaScript frame, |frame|.
|
||||
@ -56,7 +54,7 @@ ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
|
||||
* alternative path, containing its own call to ScriptDebugEpilogue.)
|
||||
*/
|
||||
extern bool
|
||||
ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool ok);
|
||||
ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, bool ok);
|
||||
|
||||
/*
|
||||
* Announce to the debugger that an exception has been thrown and propagated
|
||||
@ -320,7 +318,7 @@ HasInstance(JSContext *cx, HandleObject obj, HandleValue v, bool *bp);
|
||||
|
||||
/* Unwind block and scope chains to match the given depth. */
|
||||
extern void
|
||||
UnwindScope(JSContext *cx, ScopeIter &si, uint32_t stackDepth);
|
||||
UnwindScope(JSContext *cx, AbstractFramePtr frame, uint32_t stackDepth);
|
||||
|
||||
/*
|
||||
* Unwind for an uncatchable exception. This means not running finalizers, etc;
|
||||
|
@ -68,19 +68,18 @@ IsTopFrameConstructing(JSContext *cx, AbstractFramePtr frame)
|
||||
}
|
||||
|
||||
JSTrapStatus
|
||||
js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
|
||||
js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame)
|
||||
{
|
||||
JS_ASSERT_IF(frame.isStackFrame(), frame.asStackFrame() == cx->interpreterFrame());
|
||||
|
||||
if (!frame.script()->selfHosted) {
|
||||
JSAbstractFramePtr jsframe(frame.raw(), pc);
|
||||
if (frame.isFramePushedByExecute()) {
|
||||
if (JSInterpreterHook hook = cx->runtime()->debugHooks.executeHook)
|
||||
frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame),
|
||||
frame.setHookData(hook(cx, Jsvalify(frame), IsTopFrameConstructing(cx, frame),
|
||||
true, 0, cx->runtime()->debugHooks.executeHookData));
|
||||
} else {
|
||||
if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook)
|
||||
frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame),
|
||||
frame.setHookData(hook(cx, Jsvalify(frame), IsTopFrameConstructing(cx, frame),
|
||||
true, 0, cx->runtime()->debugHooks.callHookData));
|
||||
}
|
||||
}
|
||||
@ -106,7 +105,7 @@ js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
|
||||
}
|
||||
|
||||
bool
|
||||
js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool okArg)
|
||||
js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, bool okArg)
|
||||
{
|
||||
JS_ASSERT_IF(frame.isStackFrame(), frame.asStackFrame() == cx->interpreterFrame());
|
||||
|
||||
@ -114,13 +113,12 @@ js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, b
|
||||
|
||||
// We don't add hook data for self-hosted scripts, so we don't need to check for them, here.
|
||||
if (void *hookData = frame.maybeHookData()) {
|
||||
JSAbstractFramePtr jsframe(frame.raw(), pc);
|
||||
if (frame.isFramePushedByExecute()) {
|
||||
if (JSInterpreterHook hook = cx->runtime()->debugHooks.executeHook)
|
||||
hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData);
|
||||
hook(cx, Jsvalify(frame), IsTopFrameConstructing(cx, frame), false, &ok, hookData);
|
||||
} else {
|
||||
if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook)
|
||||
hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData);
|
||||
hook(cx, Jsvalify(frame), IsTopFrameConstructing(cx, frame), false, &ok, hookData);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1230,27 +1228,27 @@ JS::FormatStackDump(JSContext *cx, char *buf, bool showArgs, bool showLocals, bo
|
||||
return buf;
|
||||
}
|
||||
|
||||
JSAbstractFramePtr::JSAbstractFramePtr(void *raw, jsbytecode *pc)
|
||||
: ptr_(uintptr_t(raw)), pc_(pc)
|
||||
JSAbstractFramePtr::JSAbstractFramePtr(void *raw)
|
||||
: ptr_(uintptr_t(raw))
|
||||
{ }
|
||||
|
||||
JSObject *
|
||||
JSAbstractFramePtr::scopeChain(JSContext *cx)
|
||||
{
|
||||
AbstractFramePtr frame(*this);
|
||||
AbstractFramePtr frame = Valueify(*this);
|
||||
RootedObject scopeChain(cx, frame.scopeChain());
|
||||
AutoCompartment ac(cx, scopeChain);
|
||||
return GetDebugScopeForFrame(cx, frame, pc());
|
||||
return GetDebugScopeForFrame(cx, frame);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
JSAbstractFramePtr::callObject(JSContext *cx)
|
||||
{
|
||||
AbstractFramePtr frame(*this);
|
||||
AbstractFramePtr frame = Valueify(*this);
|
||||
if (!frame.isFunctionFrame())
|
||||
return nullptr;
|
||||
|
||||
JSObject *o = GetDebugScopeForFrame(cx, frame, pc());
|
||||
JSObject *o = GetDebugScopeForFrame(cx, frame);
|
||||
|
||||
/*
|
||||
* Given that fp is a function frame and GetDebugScopeForFrame always fills
|
||||
@ -1272,21 +1270,21 @@ JSAbstractFramePtr::callObject(JSContext *cx)
|
||||
JSFunction *
|
||||
JSAbstractFramePtr::maybeFun()
|
||||
{
|
||||
AbstractFramePtr frame(*this);
|
||||
AbstractFramePtr frame = Valueify(*this);
|
||||
return frame.maybeFun();
|
||||
}
|
||||
|
||||
JSScript *
|
||||
JSAbstractFramePtr::script()
|
||||
{
|
||||
AbstractFramePtr frame(*this);
|
||||
AbstractFramePtr frame = Valueify(*this);
|
||||
return frame.script();
|
||||
}
|
||||
|
||||
bool
|
||||
JSAbstractFramePtr::getThisValue(JSContext *cx, MutableHandleValue thisv)
|
||||
{
|
||||
AbstractFramePtr frame(*this);
|
||||
AbstractFramePtr frame = Valueify(*this);
|
||||
|
||||
RootedObject scopeChain(cx, frame.scopeChain());
|
||||
js::AutoCompartment ac(cx, scopeChain);
|
||||
@ -1300,7 +1298,7 @@ JSAbstractFramePtr::getThisValue(JSContext *cx, MutableHandleValue thisv)
|
||||
bool
|
||||
JSAbstractFramePtr::isDebuggerFrame()
|
||||
{
|
||||
AbstractFramePtr frame(*this);
|
||||
AbstractFramePtr frame = Valueify(*this);
|
||||
return frame.isDebuggerFrame();
|
||||
}
|
||||
|
||||
@ -1342,7 +1340,7 @@ JSAbstractFramePtr::evaluateUCInStackFrame(JSContext *cx,
|
||||
if (!env)
|
||||
return false;
|
||||
|
||||
AbstractFramePtr frame(*this);
|
||||
AbstractFramePtr frame = Valueify(*this);
|
||||
if (!ComputeThis(cx, frame))
|
||||
return false;
|
||||
RootedValue thisv(cx, frame.thisValue());
|
||||
@ -1384,7 +1382,7 @@ JSAbstractFramePtr
|
||||
JSBrokenFrameIterator::abstractFramePtr() const
|
||||
{
|
||||
NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
|
||||
return JSAbstractFramePtr(iter.abstractFramePtr().raw(), iter.pc());
|
||||
return Jsvalify(iter.abstractFramePtr());
|
||||
}
|
||||
|
||||
jsbytecode *
|
||||
|
@ -874,32 +874,31 @@ ScopeIter::ScopeIter(JSObject &enclosingScope, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
ScopeIter::ScopeIter(AbstractFramePtr frame, jsbytecode *pc, JSContext *cx
|
||||
ScopeIter::ScopeIter(AbstractFramePtr frame, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
||||
: cx(cx),
|
||||
frame_(frame),
|
||||
cur_(cx, frame.scopeChain()),
|
||||
block_(cx, frame.script()->getBlockScope(pc))
|
||||
block_(cx, frame.maybeBlockChain())
|
||||
{
|
||||
assertSameCompartment(cx, frame);
|
||||
settle();
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
ScopeIter::ScopeIter(const ScopeIterKey &key, JSContext *cx
|
||||
ScopeIter::ScopeIter(const ScopeIter &si, AbstractFramePtr frame, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
||||
: cx(cx),
|
||||
frame_(key.frame()),
|
||||
cur_(cx, key.cur()),
|
||||
block_(cx, key.block()),
|
||||
type_(key.type()),
|
||||
hasScopeObject_(key.hasScopeObject())
|
||||
: cx(si.cx),
|
||||
frame_(frame),
|
||||
cur_(cx, si.cur_),
|
||||
block_(cx, si.block_),
|
||||
type_(si.type_),
|
||||
hasScopeObject_(si.hasScopeObject_)
|
||||
{
|
||||
assertSameCompartment(cx, key.frame());
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
ScopeIter::ScopeIter(AbstractFramePtr frame, jsbytecode *pc, ScopeObject &scope, JSContext *cx
|
||||
ScopeIter::ScopeIter(AbstractFramePtr frame, ScopeObject &scope, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
||||
: cx(cx),
|
||||
frame_(frame),
|
||||
@ -916,13 +915,13 @@ ScopeIter::ScopeIter(AbstractFramePtr frame, jsbytecode *pc, ScopeObject &scope,
|
||||
* let (y = 1) g();
|
||||
* }
|
||||
*
|
||||
* g will have x's block in its enclosing scope but not y's. However, at the
|
||||
* debugger statement, both the x's and y's blocks will be on the block
|
||||
* chain. Fortunately, we can compare scope object stack depths to determine
|
||||
* the block (if any) that encloses 'scope'.
|
||||
* g will have x's block in its enclosing scope but not y's. However, at
|
||||
* the debugger statement, both the x's and y's blocks will be on
|
||||
* fp->blockChain. Fortunately, we can compare scope object stack depths to
|
||||
* determine the block (if any) that encloses 'scope'.
|
||||
*/
|
||||
if (cur_->is<NestedScopeObject>()) {
|
||||
block_ = frame.script()->getBlockScope(pc);
|
||||
block_ = frame.maybeBlockChain();
|
||||
while (block_) {
|
||||
if (block_->stackDepth() <= cur_->as<NestedScopeObject>().stackDepth())
|
||||
break;
|
||||
@ -1097,7 +1096,8 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
* the normal Call/BlockObject scope objects and thus must be recovered
|
||||
* from somewhere else:
|
||||
* + if the invocation for which the scope was created is still executing,
|
||||
* there is a StackFrame live on the stack holding the values;
|
||||
* there is a StackFrame (either live on the stack or floating in a
|
||||
* generator object) holding the values;
|
||||
* + if the invocation for which the scope was created finished executing:
|
||||
* - and there was a DebugScopeObject associated with scope, then the
|
||||
* DebugScopes::onPop(Call|Block) handler copied out the unaliased
|
||||
@ -1117,7 +1117,7 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
jsid id, Action action, MutableHandleValue vp)
|
||||
{
|
||||
JS_ASSERT(&debugScope->scope() == scope);
|
||||
ScopeIterKey* maybeLiveScope = DebugScopes::hasLiveScope(*scope);
|
||||
AbstractFramePtr maybeframe = DebugScopes::hasLiveFrame(*scope);
|
||||
|
||||
/* Handle unaliased formals, vars, and consts at function scope. */
|
||||
if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
|
||||
@ -1138,12 +1138,11 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
if (script->varIsAliased(i))
|
||||
return false;
|
||||
|
||||
if (maybeLiveScope) {
|
||||
AbstractFramePtr frame = maybeLiveScope->frame();
|
||||
if (maybeframe) {
|
||||
if (action == GET)
|
||||
vp.set(frame.unaliasedVar(i));
|
||||
vp.set(maybeframe.unaliasedVar(i));
|
||||
else
|
||||
frame.unaliasedVar(i) = vp;
|
||||
maybeframe.unaliasedVar(i) = vp;
|
||||
} else if (JSObject *snapshot = debugScope->maybeSnapshot()) {
|
||||
if (action == GET)
|
||||
vp.set(snapshot->getDenseElement(bindings.numArgs() + i));
|
||||
@ -1160,18 +1159,17 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
if (script->formalIsAliased(i))
|
||||
return false;
|
||||
|
||||
if (maybeLiveScope) {
|
||||
AbstractFramePtr frame = maybeLiveScope->frame();
|
||||
if (script->argsObjAliasesFormals() && frame.hasArgsObj()) {
|
||||
if (maybeframe) {
|
||||
if (script->argsObjAliasesFormals() && maybeframe.hasArgsObj()) {
|
||||
if (action == GET)
|
||||
vp.set(frame.argsObj().arg(i));
|
||||
vp.set(maybeframe.argsObj().arg(i));
|
||||
else
|
||||
frame.argsObj().setArg(i, vp);
|
||||
maybeframe.argsObj().setArg(i, vp);
|
||||
} else {
|
||||
if (action == GET)
|
||||
vp.set(frame.unaliasedFormal(i, DONT_CHECK_ALIASING));
|
||||
vp.set(maybeframe.unaliasedFormal(i, DONT_CHECK_ALIASING));
|
||||
else
|
||||
frame.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp;
|
||||
maybeframe.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp;
|
||||
}
|
||||
} else if (JSObject *snapshot = debugScope->maybeSnapshot()) {
|
||||
if (action == GET)
|
||||
@ -1202,14 +1200,13 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
if (block->staticBlock().isAliased(i))
|
||||
return false;
|
||||
|
||||
if (maybeLiveScope) {
|
||||
AbstractFramePtr frame = maybeLiveScope->frame();
|
||||
JSScript *script = frame.script();
|
||||
if (maybeframe) {
|
||||
JSScript *script = maybeframe.script();
|
||||
unsigned local = block->slotToLocalIndex(script->bindings, shape->slot());
|
||||
if (action == GET)
|
||||
vp.set(frame.unaliasedLocal(local));
|
||||
vp.set(maybeframe.unaliasedLocal(local));
|
||||
else
|
||||
frame.unaliasedLocal(local) = vp;
|
||||
maybeframe.unaliasedLocal(local) = vp;
|
||||
JS_ASSERT(analyze::LocalSlot(script, local) >= analyze::TotalSlots(script));
|
||||
} else {
|
||||
if (action == GET)
|
||||
@ -1266,14 +1263,14 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
if (scope.as<CallObject>().callee().nonLazyScript()->needsArgsObj())
|
||||
return true;
|
||||
|
||||
ScopeIterKey *maybeScope = DebugScopes::hasLiveScope(scope);
|
||||
if (!maybeScope) {
|
||||
AbstractFramePtr maybeframe = DebugScopes::hasLiveFrame(scope);
|
||||
if (!maybeframe) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
|
||||
"Debugger scope");
|
||||
return false;
|
||||
}
|
||||
|
||||
*maybeArgsObj = ArgumentsObject::createUnexpected(cx, maybeScope->frame());
|
||||
*maybeArgsObj = ArgumentsObject::createUnexpected(cx, maybeframe);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1590,8 +1587,9 @@ void
|
||||
DebugScopes::sweep(JSRuntime *rt)
|
||||
{
|
||||
/*
|
||||
* missingScopes points to debug scopes weakly so that debug scopes can be
|
||||
* released more eagerly.
|
||||
* Note: missingScopes points to debug scopes weakly not just so that debug
|
||||
* scopes can be released more eagerly, but, more importantly, to avoid
|
||||
* creating an uncollectable cycle with suspended generator frames.
|
||||
*/
|
||||
for (MissingScopeMap::Enum e(missingScopes); !e.empty(); e.popFront()) {
|
||||
if (IsObjectAboutToBeFinalized(e.front().value().unsafeGet()))
|
||||
@ -1600,6 +1598,7 @@ DebugScopes::sweep(JSRuntime *rt)
|
||||
|
||||
for (LiveScopeMap::Enum e(liveScopes); !e.empty(); e.popFront()) {
|
||||
ScopeObject *scope = e.front().key();
|
||||
AbstractFramePtr frame = e.front().value();
|
||||
|
||||
/*
|
||||
* Scopes can be finalized when a debugger-synthesized ScopeObject is
|
||||
@ -1609,6 +1608,19 @@ DebugScopes::sweep(JSRuntime *rt)
|
||||
e.removeFront();
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* As explained in onGeneratorFrameChange, liveScopes includes
|
||||
* suspended generator frames. Since a generator can be finalized while
|
||||
* its scope is live, we must explicitly detect finalized generators.
|
||||
*/
|
||||
if (JSGenerator *gen = frame.maybeSuspendedGenerator(rt)) {
|
||||
JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
|
||||
if (IsObjectAboutToBeFinalized(&gen->obj)) {
|
||||
e.removeFront();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1699,7 +1711,6 @@ DebugScopes::addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject
|
||||
{
|
||||
JS_ASSERT(!si.hasScopeObject());
|
||||
JS_ASSERT(cx->compartment() == debugScope.compartment());
|
||||
JS_ASSERT_IF(si.frame().isFunctionFrame(), !si.frame().callee()->isGenerator());
|
||||
|
||||
if (!CanUseDebugScopeMaps(cx))
|
||||
return true;
|
||||
@ -1715,7 +1726,7 @@ DebugScopes::addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject
|
||||
}
|
||||
|
||||
JS_ASSERT(!scopes->liveScopes.has(&debugScope.scope()));
|
||||
if (!scopes->liveScopes.put(&debugScope.scope(), si)) {
|
||||
if (!scopes->liveScopes.put(&debugScope.scope(), si.frame())) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
@ -1749,7 +1760,7 @@ DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx)
|
||||
if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&callobj))
|
||||
debugScope = &p->value()->as<DebugScopeObject>();
|
||||
} else {
|
||||
ScopeIter si(frame, frame.script()->main(), cx);
|
||||
ScopeIter si(frame, cx);
|
||||
if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
|
||||
debugScope = p->value();
|
||||
scopes->liveScopes.remove(&debugScope->scope().as<CallObject>());
|
||||
@ -1804,7 +1815,7 @@ DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx)
|
||||
}
|
||||
|
||||
void
|
||||
DebugScopes::onPopBlock(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
|
||||
DebugScopes::onPopBlock(JSContext *cx, AbstractFramePtr frame)
|
||||
{
|
||||
assertSameCompartment(cx, frame);
|
||||
|
||||
@ -1812,27 +1823,16 @@ DebugScopes::onPopBlock(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
|
||||
if (!scopes)
|
||||
return;
|
||||
|
||||
ScopeIter si(frame, pc, cx);
|
||||
onPopBlock(cx, si);
|
||||
}
|
||||
|
||||
void
|
||||
DebugScopes::onPopBlock(JSContext *cx, const ScopeIter &si)
|
||||
{
|
||||
DebugScopes *scopes = cx->compartment()->debugScopes;
|
||||
if (!scopes)
|
||||
return;
|
||||
|
||||
JS_ASSERT(si.type() == ScopeIter::Block);
|
||||
|
||||
if (si.staticBlock().needsClone()) {
|
||||
ClonedBlockObject &clone = si.scope().as<ClonedBlockObject>();
|
||||
clone.copyUnaliasedValues(si.frame());
|
||||
StaticBlockObject &staticBlock = *frame.maybeBlockChain();
|
||||
if (staticBlock.needsClone()) {
|
||||
ClonedBlockObject &clone = frame.scopeChain()->as<ClonedBlockObject>();
|
||||
clone.copyUnaliasedValues(frame);
|
||||
scopes->liveScopes.remove(&clone);
|
||||
} else {
|
||||
ScopeIter si(frame, cx);
|
||||
if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
|
||||
ClonedBlockObject &clone = p->value()->scope().as<ClonedBlockObject>();
|
||||
clone.copyUnaliasedValues(si.frame());
|
||||
clone.copyUnaliasedValues(frame);
|
||||
scopes->liveScopes.remove(&clone);
|
||||
scopes->missingScopes.remove(p);
|
||||
}
|
||||
@ -1862,6 +1862,43 @@ DebugScopes::onPopStrictEvalScope(AbstractFramePtr frame)
|
||||
scopes->liveScopes.remove(&frame.scopeChain()->as<CallObject>());
|
||||
}
|
||||
|
||||
void
|
||||
DebugScopes::onGeneratorFrameChange(AbstractFramePtr from, AbstractFramePtr to, JSContext *cx)
|
||||
{
|
||||
for (ScopeIter toIter(to, cx); !toIter.done(); ++toIter) {
|
||||
DebugScopes *scopes = ensureCompartmentData(cx);
|
||||
if (!scopes)
|
||||
return;
|
||||
|
||||
if (toIter.hasScopeObject()) {
|
||||
/*
|
||||
* Not only must we correctly replace mappings [scope -> from] with
|
||||
* mappings [scope -> to], but we must add [scope -> to] if it
|
||||
* doesn't already exist so that if we need to proxy a generator's
|
||||
* scope while it is suspended, we can find its frame (which would
|
||||
* otherwise not be found by AllFramesIter).
|
||||
*/
|
||||
JS_ASSERT(toIter.scope().compartment() == cx->compartment());
|
||||
LiveScopeMap::AddPtr livePtr = scopes->liveScopes.lookupForAdd(&toIter.scope());
|
||||
if (livePtr) {
|
||||
livePtr->value() = to;
|
||||
} else {
|
||||
scopes->liveScopes.add(livePtr, &toIter.scope(), to); // OOM here?
|
||||
liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &toIter.scope());
|
||||
}
|
||||
} else {
|
||||
ScopeIter si(toIter, from, cx);
|
||||
JS_ASSERT(si.frame().scopeChain()->compartment() == cx->compartment());
|
||||
if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
|
||||
DebugScopeObject &debugScope = *p->value();
|
||||
scopes->liveScopes.lookup(&debugScope.scope())->value() = to;
|
||||
scopes->missingScopes.remove(p);
|
||||
scopes->missingScopes.put(toIter, &debugScope); // OOM here?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DebugScopes::onCompartmentLeaveDebugMode(JSCompartment *c)
|
||||
{
|
||||
@ -1901,16 +1938,13 @@ DebugScopes::updateLiveScopes(JSContext *cx)
|
||||
if (frame.scopeChain()->compartment() != cx->compartment())
|
||||
continue;
|
||||
|
||||
if (frame.isFunctionFrame() && frame.callee()->isGenerator())
|
||||
continue;
|
||||
|
||||
for (ScopeIter si(frame, i.pc(), cx); !si.done(); ++si) {
|
||||
for (ScopeIter si(frame, cx); !si.done(); ++si) {
|
||||
if (si.hasScopeObject()) {
|
||||
JS_ASSERT(si.scope().compartment() == cx->compartment());
|
||||
DebugScopes *scopes = ensureCompartmentData(cx);
|
||||
if (!scopes)
|
||||
return false;
|
||||
if (!scopes->liveScopes.put(&si.scope(), si))
|
||||
if (!scopes->liveScopes.put(&si.scope(), frame))
|
||||
return false;
|
||||
liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &si.scope());
|
||||
}
|
||||
@ -1925,17 +1959,33 @@ DebugScopes::updateLiveScopes(JSContext *cx)
|
||||
return true;
|
||||
}
|
||||
|
||||
ScopeIterKey*
|
||||
DebugScopes::hasLiveScope(ScopeObject &scope)
|
||||
AbstractFramePtr
|
||||
DebugScopes::hasLiveFrame(ScopeObject &scope)
|
||||
{
|
||||
DebugScopes *scopes = scope.compartment()->debugScopes;
|
||||
if (!scopes)
|
||||
return nullptr;
|
||||
return NullFramePtr();
|
||||
|
||||
if (LiveScopeMap::Ptr p = scopes->liveScopes.lookup(&scope))
|
||||
return &p->value();
|
||||
if (LiveScopeMap::Ptr p = scopes->liveScopes.lookup(&scope)) {
|
||||
AbstractFramePtr frame = p->value();
|
||||
|
||||
return nullptr;
|
||||
/*
|
||||
* Since liveScopes is effectively a weak pointer, we need a read
|
||||
* barrier. The scenario where this is necessary is:
|
||||
* 1. GC starts, a suspended generator is not live
|
||||
* 2. hasLiveFrame returns a StackFrame* to the (soon to be dead)
|
||||
* suspended generator
|
||||
* 3. stack frame values (which will neve be marked) are read from the
|
||||
* StackFrame
|
||||
* 4. GC completes, live objects may now point to values that weren't
|
||||
* marked and thus may point to swept GC things
|
||||
*/
|
||||
if (JSGenerator *gen = frame.maybeSuspendedGenerator(scope.compartment()->runtimeFromMainThread()))
|
||||
JSObject::readBarrier(gen->obj);
|
||||
|
||||
return frame;
|
||||
}
|
||||
return NullFramePtr();
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -1996,8 +2046,6 @@ GetDebugScopeForMissing(JSContext *cx, const ScopeIter &si)
|
||||
DebugScopeObject *debugScope = nullptr;
|
||||
switch (si.type()) {
|
||||
case ScopeIter::Call: {
|
||||
// Generators should always reify their scopes.
|
||||
JS_ASSERT(!si.frame().callee()->isGenerator());
|
||||
Rooted<CallObject*> callobj(cx, CallObject::createForFunction(cx, si.frame()));
|
||||
if (!callobj)
|
||||
return nullptr;
|
||||
@ -2014,8 +2062,6 @@ GetDebugScopeForMissing(JSContext *cx, const ScopeIter &si)
|
||||
break;
|
||||
}
|
||||
case ScopeIter::Block: {
|
||||
// Generators should always reify their scopes.
|
||||
JS_ASSERT_IF(si.frame().isFunctionFrame(), !si.frame().callee()->isGenerator());
|
||||
Rooted<StaticBlockObject *> staticBlock(cx, &si.staticBlock());
|
||||
ClonedBlockObject *block = ClonedBlockObject::create(cx, staticBlock, si.frame());
|
||||
if (!block)
|
||||
@ -2056,8 +2102,8 @@ GetDebugScope(JSContext *cx, JSObject &obj)
|
||||
}
|
||||
|
||||
Rooted<ScopeObject*> scope(cx, &obj.as<ScopeObject>());
|
||||
if (ScopeIterKey *maybeLiveScope = DebugScopes::hasLiveScope(*scope)) {
|
||||
ScopeIter si(*maybeLiveScope, cx);
|
||||
if (AbstractFramePtr frame = DebugScopes::hasLiveFrame(*scope)) {
|
||||
ScopeIter si(frame, *scope, cx);
|
||||
return GetDebugScope(cx, si);
|
||||
}
|
||||
ScopeIter si(scope->enclosingScope(), cx);
|
||||
@ -2092,12 +2138,12 @@ js::GetDebugScopeForFunction(JSContext *cx, HandleFunction fun)
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js::GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
|
||||
js::GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame)
|
||||
{
|
||||
assertSameCompartment(cx, frame);
|
||||
if (CanUseDebugScopeMaps(cx) && !DebugScopes::updateLiveScopes(cx))
|
||||
return nullptr;
|
||||
ScopeIter si(frame, pc, cx);
|
||||
ScopeIter si(frame, cx);
|
||||
return GetDebugScope(cx, si);
|
||||
}
|
||||
|
||||
|
@ -534,25 +534,29 @@ class ScopeIter
|
||||
public:
|
||||
|
||||
/* Constructing from a copy of an existing ScopeIter. */
|
||||
ScopeIter(const ScopeIter &si, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
explicit ScopeIter(const ScopeIter &si, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
|
||||
/* Constructing from StackFrame places ScopeIter on the innermost scope. */
|
||||
ScopeIter(AbstractFramePtr frame, jsbytecode *pc, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
explicit ScopeIter(AbstractFramePtr frame, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
|
||||
/*
|
||||
* Without a StackFrame, the resulting ScopeIter is done() with
|
||||
* enclosingScope() as given.
|
||||
*/
|
||||
ScopeIter(JSObject &enclosingScope, JSContext *cx
|
||||
explicit ScopeIter(JSObject &enclosingScope, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
|
||||
/*
|
||||
* For the special case of generators, copy the given ScopeIter, with 'fp'
|
||||
* as the StackFrame instead of si.fp(). Not for general use.
|
||||
*/
|
||||
ScopeIter(const ScopeIter &si, AbstractFramePtr frame, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
|
||||
/* Like ScopeIter(StackFrame *) except start at 'scope'. */
|
||||
ScopeIter(AbstractFramePtr frame, jsbytecode *pc, ScopeObject &scope, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
|
||||
ScopeIter(const ScopeIterKey &key, JSContext *cx
|
||||
ScopeIter(AbstractFramePtr frame, ScopeObject &scope, JSContext *cx
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
|
||||
bool done() const { return !frame_; }
|
||||
@ -581,20 +585,15 @@ class ScopeIterKey
|
||||
JSObject *cur_;
|
||||
StaticBlockObject *block_;
|
||||
ScopeIter::Type type_;
|
||||
bool hasScopeObject_;
|
||||
|
||||
public:
|
||||
ScopeIterKey() : frame_(NullFramePtr()), cur_(nullptr), block_(nullptr), type_() {}
|
||||
ScopeIterKey(const ScopeIter &si)
|
||||
: frame_(si.frame_), cur_(si.cur_), block_(si.block_), type_(si.type_),
|
||||
hasScopeObject_(si.hasScopeObject_)
|
||||
: frame_(si.frame_), cur_(si.cur_), block_(si.block_), type_(si.type_)
|
||||
{}
|
||||
|
||||
AbstractFramePtr frame() const { return frame_; }
|
||||
JSObject *cur() const { return cur_; }
|
||||
StaticBlockObject *block() const { return block_; }
|
||||
ScopeIter::Type type() const { return type_; }
|
||||
bool hasScopeObject() const { return hasScopeObject_; }
|
||||
|
||||
/* For use as hash policy */
|
||||
typedef ScopeIterKey Lookup;
|
||||
@ -633,7 +632,7 @@ extern JSObject *
|
||||
GetDebugScopeForFunction(JSContext *cx, HandleFunction fun);
|
||||
|
||||
extern JSObject *
|
||||
GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
|
||||
GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame);
|
||||
|
||||
/* Provides debugger access to a scope. */
|
||||
class DebugScopeObject : public ProxyObject
|
||||
@ -691,7 +690,7 @@ class DebugScopes
|
||||
* updates of liveScopes need only fill in the new scopes.
|
||||
*/
|
||||
typedef HashMap<ScopeObject *,
|
||||
ScopeIterKey,
|
||||
AbstractFramePtr,
|
||||
DefaultHasher<ScopeObject *>,
|
||||
RuntimeAllocPolicy> LiveScopeMap;
|
||||
LiveScopeMap liveScopes;
|
||||
@ -718,15 +717,17 @@ class DebugScopes
|
||||
static bool addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject &debugScope);
|
||||
|
||||
static bool updateLiveScopes(JSContext *cx);
|
||||
static ScopeIterKey *hasLiveScope(ScopeObject &scope);
|
||||
static AbstractFramePtr hasLiveFrame(ScopeObject &scope);
|
||||
|
||||
// In debug-mode, these must be called whenever exiting a scope that might
|
||||
// have stack-allocated locals.
|
||||
/*
|
||||
* In debug-mode, these must be called whenever exiting a call/block or
|
||||
* when activating/yielding a generator.
|
||||
*/
|
||||
static void onPopCall(AbstractFramePtr frame, JSContext *cx);
|
||||
static void onPopBlock(JSContext *cx, const ScopeIter &si);
|
||||
static void onPopBlock(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
|
||||
static void onPopBlock(JSContext *cx, AbstractFramePtr frame);
|
||||
static void onPopWith(AbstractFramePtr frame);
|
||||
static void onPopStrictEvalScope(AbstractFramePtr frame);
|
||||
static void onGeneratorFrameChange(AbstractFramePtr from, AbstractFramePtr to, JSContext *cx);
|
||||
static void onCompartmentLeaveDebugMode(JSCompartment *c);
|
||||
};
|
||||
|
||||
|
@ -77,7 +77,7 @@ StackFrame::initCallFrame(JSContext *cx, StackFrame *prev, jsbytecode *prevpc, V
|
||||
JS_ASSERT(callee.nonLazyScript() == script);
|
||||
|
||||
/* Initialize stack frame members. */
|
||||
flags_ = FUNCTION | HAS_SCOPECHAIN | flagsArg;
|
||||
flags_ = FUNCTION | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | flagsArg;
|
||||
argv_ = argv;
|
||||
exec.fun = &callee;
|
||||
u.nactual = nactual;
|
||||
@ -85,6 +85,8 @@ StackFrame::initCallFrame(JSContext *cx, StackFrame *prev, jsbytecode *prevpc, V
|
||||
prev_ = prev;
|
||||
prevpc_ = prevpc;
|
||||
prevsp_ = prevsp;
|
||||
blockChain_= nullptr;
|
||||
JS_ASSERT(!hasBlockChain());
|
||||
JS_ASSERT(!hasHookData());
|
||||
|
||||
initVarsToUndefined();
|
||||
@ -108,7 +110,7 @@ inline Value &
|
||||
StackFrame::unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
CheckLocalUnaliased(checkAliasing, script(), i);
|
||||
CheckLocalUnaliased(checkAliasing, script(), maybeBlockChain(), i);
|
||||
#endif
|
||||
return slots()[i];
|
||||
}
|
||||
@ -514,6 +516,25 @@ AbstractFramePtr::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing)
|
||||
#endif
|
||||
}
|
||||
|
||||
inline JSGenerator *
|
||||
AbstractFramePtr::maybeSuspendedGenerator(JSRuntime *rt) const
|
||||
{
|
||||
if (isStackFrame())
|
||||
return asStackFrame()->maybeSuspendedGenerator(rt);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline StaticBlockObject *
|
||||
AbstractFramePtr::maybeBlockChain() const
|
||||
{
|
||||
if (isStackFrame())
|
||||
return asStackFrame()->maybeBlockChain();
|
||||
#ifdef JS_ION
|
||||
return asBaselineFrame()->maybeBlockChain();
|
||||
#else
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid frame");
|
||||
#endif
|
||||
}
|
||||
inline bool
|
||||
AbstractFramePtr::hasCallObj() const
|
||||
{
|
||||
|
@ -37,7 +37,7 @@ StackFrame::initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr e
|
||||
* script in the context of another frame and the frame type is determined
|
||||
* by the context.
|
||||
*/
|
||||
flags_ = type | HAS_SCOPECHAIN;
|
||||
flags_ = type | HAS_SCOPECHAIN | HAS_BLOCKCHAIN;
|
||||
|
||||
JSObject *callee = nullptr;
|
||||
if (!(flags_ & (GLOBAL))) {
|
||||
@ -81,6 +81,7 @@ StackFrame::initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr e
|
||||
prev_ = nullptr;
|
||||
prevpc_ = nullptr;
|
||||
prevsp_ = nullptr;
|
||||
blockChain_ = nullptr;
|
||||
|
||||
JS_ASSERT_IF(evalInFramePrev, isDebuggerFrame());
|
||||
evalInFramePrev_ = evalInFramePrev;
|
||||
@ -122,6 +123,9 @@ StackFrame::copyFrameAndValues(JSContext *cx, Value *vp, StackFrame *otherfp,
|
||||
if (doPostBarrier)
|
||||
HeapValue::writeBarrierPost(*dst, dst);
|
||||
}
|
||||
|
||||
if (JS_UNLIKELY(cx->compartment()->debugMode()))
|
||||
DebugScopes::onGeneratorFrameChange(otherfp, this, cx);
|
||||
}
|
||||
|
||||
/* Note: explicit instantiation for js_NewGenerator located in jsiter.cpp. */
|
||||
@ -151,6 +155,27 @@ StackFrame::writeBarrierPost()
|
||||
HeapValue::writeBarrierPost(rval_, &rval_);
|
||||
}
|
||||
|
||||
JSGenerator *
|
||||
StackFrame::maybeSuspendedGenerator(JSRuntime *rt)
|
||||
{
|
||||
/*
|
||||
* A suspended generator's frame is embedded inside the JSGenerator object
|
||||
* and is not currently running.
|
||||
*/
|
||||
if (!isGeneratorFrame() || !isSuspended())
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* Once we know we have a suspended generator frame, there is a static
|
||||
* offset from the frame's snapshot to beginning of the JSGenerator.
|
||||
*/
|
||||
char *vp = reinterpret_cast<char *>(generatorArgsSnapshotBegin());
|
||||
char *p = vp - offsetof(JSGenerator, stackSnapshot);
|
||||
JSGenerator *gen = reinterpret_cast<JSGenerator *>(p);
|
||||
JS_ASSERT(gen->fp == this);
|
||||
return gen;
|
||||
}
|
||||
|
||||
bool
|
||||
StackFrame::copyRawFrameSlots(AutoValueVector *vec)
|
||||
{
|
||||
@ -272,6 +297,7 @@ void
|
||||
StackFrame::epilogue(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(!isYielding());
|
||||
JS_ASSERT(!hasBlockChain());
|
||||
|
||||
RootedScript script(cx, this->script());
|
||||
probes::ExitScript(cx, script, script->function(), hasPushedSPSFrame());
|
||||
@ -325,23 +351,39 @@ StackFrame::epilogue(JSContext *cx)
|
||||
bool
|
||||
StackFrame::pushBlock(JSContext *cx, StaticBlockObject &block)
|
||||
{
|
||||
JS_ASSERT (block.needsClone());
|
||||
JS_ASSERT_IF(hasBlockChain(), blockChain_ == block.enclosingBlock());
|
||||
|
||||
Rooted<StaticBlockObject *> blockHandle(cx, &block);
|
||||
ClonedBlockObject *clone = ClonedBlockObject::create(cx, blockHandle, this);
|
||||
if (!clone)
|
||||
return false;
|
||||
if (block.needsClone()) {
|
||||
Rooted<StaticBlockObject *> blockHandle(cx, &block);
|
||||
ClonedBlockObject *clone = ClonedBlockObject::create(cx, blockHandle, this);
|
||||
if (!clone)
|
||||
return false;
|
||||
|
||||
pushOnScopeChain(*clone);
|
||||
pushOnScopeChain(*clone);
|
||||
|
||||
blockChain_ = blockHandle;
|
||||
} else {
|
||||
blockChain_ = █
|
||||
}
|
||||
|
||||
flags_ |= HAS_BLOCKCHAIN;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
StackFrame::popBlock(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(scopeChain_->is<ClonedBlockObject>());
|
||||
popOffScopeChain();
|
||||
JS_ASSERT(hasBlockChain());
|
||||
|
||||
if (JS_UNLIKELY(cx->compartment()->debugMode()))
|
||||
DebugScopes::onPopBlock(cx, this);
|
||||
|
||||
if (blockChain_->needsClone()) {
|
||||
JS_ASSERT(scopeChain_->as<ClonedBlockObject>().staticBlock() == *blockChain_);
|
||||
popOffScopeChain();
|
||||
}
|
||||
|
||||
blockChain_ = blockChain_->enclosingBlock();
|
||||
}
|
||||
|
||||
void
|
||||
@ -1242,7 +1284,8 @@ AbstractFramePtr::hasPushedSPSFrame() const
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, unsigned i)
|
||||
js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script,
|
||||
StaticBlockObject *maybeBlock, unsigned i)
|
||||
{
|
||||
if (!checkAliasing)
|
||||
return;
|
||||
@ -1251,8 +1294,13 @@ js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, unsi
|
||||
if (i < script->nfixed) {
|
||||
JS_ASSERT(!script->varIsAliased(i));
|
||||
} else {
|
||||
// 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.
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -75,7 +75,8 @@ enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
|
||||
|
||||
#ifdef DEBUG
|
||||
extern void
|
||||
CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, unsigned i);
|
||||
CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script,
|
||||
StaticBlockObject *maybeBlock, unsigned i);
|
||||
#endif
|
||||
|
||||
namespace jit {
|
||||
@ -171,6 +172,8 @@ class AbstractFramePtr
|
||||
|
||||
operator bool() const { return !!ptr_; }
|
||||
|
||||
inline JSGenerator *maybeSuspendedGenerator(JSRuntime *rt) const;
|
||||
|
||||
inline JSObject *scopeChain() const;
|
||||
inline CallObject &callObj() const;
|
||||
inline bool initFunctionScopeObjects(JSContext *cx);
|
||||
@ -178,6 +181,7 @@ class AbstractFramePtr
|
||||
|
||||
inline JSCompartment *compartment() const;
|
||||
|
||||
inline StaticBlockObject *maybeBlockChain() const;
|
||||
inline bool hasCallObj() const;
|
||||
inline bool isGeneratorFrame() const;
|
||||
inline bool isYielding() const;
|
||||
@ -307,6 +311,7 @@ class StackFrame
|
||||
HAS_HOOK_DATA = 0x400, /* frame has hookData_ set */
|
||||
HAS_RVAL = 0x800, /* frame has rval_ set */
|
||||
HAS_SCOPECHAIN = 0x1000, /* frame has scopeChain_ set */
|
||||
HAS_BLOCKCHAIN = 0x2000, /* frame has blockChain_ set */
|
||||
|
||||
/* Debugger state */
|
||||
PREV_UP_TO_DATE = 0x4000, /* see DebugScopes::updateLiveScopes */
|
||||
@ -336,6 +341,7 @@ class StackFrame
|
||||
} u;
|
||||
mutable JSObject *scopeChain_; /* if HAS_SCOPECHAIN, current scope chain */
|
||||
Value rval_; /* if HAS_RVAL, return value of the frame */
|
||||
StaticBlockObject *blockChain_; /* if HAS_BLOCKCHAIN, innermost let block */
|
||||
ArgumentsObject *argsObj_; /* if HAS_ARGS_OBJ, the call's arguments object */
|
||||
|
||||
/*
|
||||
@ -588,10 +594,29 @@ class StackFrame
|
||||
inline void popOffScopeChain();
|
||||
|
||||
/*
|
||||
* For blocks with aliased locals, these interfaces push and pop entries on
|
||||
* the scope chain.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
bool hasBlockChain() const {
|
||||
return (flags_ & HAS_BLOCKCHAIN) && blockChain_;
|
||||
}
|
||||
|
||||
StaticBlockObject *maybeBlockChain() {
|
||||
return (flags_ & HAS_BLOCKCHAIN) ? blockChain_ : nullptr;
|
||||
}
|
||||
|
||||
StaticBlockObject &blockChain() const {
|
||||
JS_ASSERT(hasBlockChain());
|
||||
return *blockChain_;
|
||||
}
|
||||
|
||||
bool pushBlock(JSContext *cx, StaticBlockObject &block);
|
||||
void popBlock(JSContext *cx);
|
||||
|
||||
@ -839,6 +864,8 @@ class StackFrame
|
||||
void copyFrameAndValues(JSContext *cx, Value *vp, StackFrame *otherfp,
|
||||
const Value *othervp, Value *othersp);
|
||||
|
||||
JSGenerator *maybeSuspendedGenerator(JSRuntime *rt);
|
||||
|
||||
/*
|
||||
* js::Execute pushes both global and function frames (since eval() in a
|
||||
* function pushes a frame with isFunctionFrame() && isEvalFrame()). Most
|
||||
@ -973,6 +1000,9 @@ InitialFrameFlagsAreConstructing(InitialFrameFlags initial)
|
||||
return !!(initial & INITIAL_CONSTRUCT);
|
||||
}
|
||||
|
||||
inline AbstractFramePtr Valueify(JSAbstractFramePtr frame) { return AbstractFramePtr(frame); }
|
||||
static inline JSAbstractFramePtr Jsvalify(AbstractFramePtr frame) { return JSAbstractFramePtr(frame.raw()); }
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
class FrameRegs
|
||||
|
Loading…
Reference in New Issue
Block a user