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:
Ryan VanderMeulen 2013-12-06 15:03:19 -05:00
Родитель 6420b4588d
Коммит db4e60059b
40 изменённых файлов: 1128 добавлений и 1020 удалений

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

@ -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_; }

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -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_ = &block;
}
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 &regs)
{
UnwindScope(cx, si, 0);
regs.setToEndOfScript();
}
static void
ForcedReturn(JSContext *cx, FrameRegs &regs)
{
ScopeIter si(regs.fp(), regs.pc, cx);
ForcedReturn(cx, si, regs);
}
void
js::UnwindForUncatchableException(JSContext *cx, const FrameRegs &regs)
{
/* 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 &regs)
{
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, &regs.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 = &REGS.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 = &REGS.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_ = &block;
}
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