Bug 927782 - Part 10: Iterate block chain from compile-time block scope maps, not runtime blockChain. r=luke

This commit is contained in:
Andy Wingo 2013-12-06 18:42:08 +01:00
parent 50f4f9bbdd
commit 457223d858
18 changed files with 335 additions and 263 deletions

View File

@ -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)iter.pc();
uintptr_t pc = (uintptr_t)frame.pc();
JS::RootedValue dummyThis(cx);
/*

View File

@ -339,16 +339,18 @@ JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda);
class JS_PUBLIC_API(JSAbstractFramePtr)
{
uintptr_t ptr_;
jsbytecode *pc_;
protected:
JSAbstractFramePtr()
: ptr_(0)
: ptr_(0), pc_(nullptr)
{ }
public:
explicit JSAbstractFramePtr(void *raw);
JSAbstractFramePtr(void *raw, jsbytecode *pc);
uintptr_t raw() const { return ptr_; }
jsbytecode *pc() const { return pc_; }
operator bool() const { return !!ptr_; }

View File

@ -508,7 +508,7 @@ BaselineCompiler::emitStackCheck(bool earlyCheck)
return true;
}
typedef bool (*DebugPrologueFn)(JSContext *, BaselineFrame *, bool *);
typedef bool (*DebugPrologueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool *);
static const VMFunction DebugPrologueInfo = FunctionInfo<DebugPrologueFn>(jit::DebugPrologue);
bool
@ -521,6 +521,7 @@ BaselineCompiler::emitDebugPrologue()
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
prepareVMCall();
pushArg(ImmPtr(pc));
pushArg(R0.scratchReg());
if (!callVM(DebugPrologueInfo))
return false;
@ -2599,16 +2600,18 @@ BaselineCompiler::emit_JSOP_ENTERLET2()
return emitEnterBlock();
}
typedef bool (*DebugLeaveBlockFn)(JSContext *, BaselineFrame *);
typedef bool (*DebugLeaveBlockFn)(JSContext *, BaselineFrame *, jsbytecode *);
static const VMFunction DebugLeaveBlockInfo = FunctionInfo<DebugLeaveBlockFn>(jit::DebugLeaveBlock);
bool
BaselineCompiler::emit_JSOP_DEBUGLEAVEBLOCK()
{
// Call a stub to pop the block from the block chain.
prepareVMCall();
if (!debugMode_)
return true;
prepareVMCall();
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
pushArg(ImmPtr(pc));
pushArg(R0.scratchReg());
return callVM(DebugLeaveBlockInfo);
@ -2707,7 +2710,7 @@ BaselineCompiler::emit_JSOP_DEBUGGER()
return true;
}
typedef bool (*DebugEpilogueFn)(JSContext *, BaselineFrame *, bool);
typedef bool (*DebugEpilogueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool);
static const VMFunction DebugEpilogueInfo = FunctionInfo<DebugEpilogueFn>(jit::DebugEpilogue);
bool
@ -2724,6 +2727,7 @@ BaselineCompiler::emitReturn()
prepareVMCall();
pushArg(Imm32(1));
pushArg(ImmPtr(pc));
pushArg(R0.scratchReg());
if (!callVM(DebugEpilogueInfo))
return false;

View File

@ -434,7 +434,7 @@ HandleExceptionBaseline(JSContext *cx, const IonFrameIterator &frame, ResumeFrom
case JSTRAP_RETURN:
JS_ASSERT(baselineFrame->hasReturnValue());
if (jit::DebugEpilogue(cx, baselineFrame, true)) {
if (jit::DebugEpilogue(cx, baselineFrame, pc, true)) {
rfe->kind = ResumeFromException::RESUME_FORCED_RETURN;
rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
rfe->stackPointer = reinterpret_cast<uint8_t *>(baselineFrame);
@ -457,6 +457,7 @@ 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;
@ -472,7 +473,7 @@ HandleExceptionBaseline(JSContext *cx, const IonFrameIterator &frame, ResumeFrom
// Unwind scope chain (pop block objects).
if (cx->isExceptionPending())
UnwindScope(cx, frame.baselineFrame(), tn->stackDepth);
UnwindScope(cx, si, tn->stackDepth);
// Compute base pointer and stack pointer.
rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
@ -607,7 +608,10 @@ 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();
if (jit::DebugEpilogue(cx, frame, false)) {
RootedScript script(cx);
jsbytecode *pc;
iter.baselineScriptAndPc(script.address(), &pc);
if (jit::DebugEpilogue(cx, frame, pc, false)) {
JS_ASSERT(frame->hasReturnValue());
rfe->kind = ResumeFromException::RESUME_FORCED_RETURN;
rfe->framePointer = iter.fp() - BaselineFrame::FramePointerOffset;

View File

@ -695,11 +695,11 @@ GetIndexFromString(JSString *str)
}
bool
DebugPrologue(JSContext *cx, BaselineFrame *frame, bool *mustReturn)
DebugPrologue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn)
{
*mustReturn = false;
JSTrapStatus status = ScriptDebugPrologue(cx, frame);
JSTrapStatus status = ScriptDebugPrologue(cx, frame, pc);
switch (status) {
case JSTRAP_CONTINUE:
return true;
@ -709,7 +709,7 @@ DebugPrologue(JSContext *cx, BaselineFrame *frame, bool *mustReturn)
// debug epilogue handler as well.
JS_ASSERT(frame->hasReturnValue());
*mustReturn = true;
return jit::DebugEpilogue(cx, frame, true);
return jit::DebugEpilogue(cx, frame, pc, true);
case JSTRAP_THROW:
case JSTRAP_ERROR:
@ -721,15 +721,16 @@ DebugPrologue(JSContext *cx, BaselineFrame *frame, bool *mustReturn)
}
bool
DebugEpilogue(JSContext *cx, BaselineFrame *frame, bool ok)
DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok)
{
// Unwind scope chain to stack depth 0.
UnwindScope(cx, frame, 0);
ScopeIter si(frame, pc, cx);
UnwindScope(cx, si, 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, ok);
ok = ScriptDebugEpilogue(cx, frame, pc, ok);
if (frame->isNonEvalFunctionFrame()) {
JS_ASSERT_IF(ok, frame->hasReturnValue());
@ -848,7 +849,7 @@ HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, bool *mus
case JSTRAP_RETURN:
*mustReturn = true;
frame->setReturnValue(rval);
return jit::DebugEpilogue(cx, frame, true);
return jit::DebugEpilogue(cx, frame, pc, true);
case JSTRAP_THROW:
cx->setPendingException(rval);
@ -886,7 +887,7 @@ OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *m
case JSTRAP_RETURN:
frame->setReturnValue(rval);
*mustReturn = true;
return jit::DebugEpilogue(cx, frame, true);
return jit::DebugEpilogue(cx, frame, pc, true);
case JSTRAP_THROW:
cx->setPendingException(rval);
@ -911,12 +912,11 @@ LeaveBlock(JSContext *cx, BaselineFrame *frame)
}
bool
DebugLeaveBlock(JSContext *cx, BaselineFrame *frame)
DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc)
{
JS_ASSERT(frame->hasBlockChain());
JS_ASSERT(cx->compartment()->debugMode());
if (JS_UNLIKELY(cx->compartment()->debugMode()))
DebugScopes::onPopBlock(cx, frame);
DebugScopes::onPopBlock(cx, frame, pc);
return true;
}

View File

@ -638,8 +638,8 @@ void PostGlobalWriteBarrier(JSRuntime *rt, JSObject *obj);
uint32_t GetIndexFromString(JSString *str);
bool DebugPrologue(JSContext *cx, BaselineFrame *frame, bool *mustReturn);
bool DebugEpilogue(JSContext *cx, BaselineFrame *frame, bool ok);
bool DebugPrologue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn);
bool DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok);
bool StrictEvalPrologue(JSContext *cx, BaselineFrame *frame);
bool HeavyweightFunPrologue(JSContext *cx, BaselineFrame *frame);
@ -654,7 +654,7 @@ bool OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bo
bool EnterBlock(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block);
bool LeaveBlock(JSContext *cx, BaselineFrame *frame);
bool DebugLeaveBlock(JSContext *cx, BaselineFrame *frame);
bool DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc);
bool InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame,
uint32_t numStackValues);

View File

@ -5674,9 +5674,8 @@ 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: ");

View File

@ -1691,24 +1691,17 @@ ExpressionDecompiler::loadAtom(jsbytecode *pc)
JSAtom *
ExpressionDecompiler::findLetVar(jsbytecode *pc, unsigned depth)
{
if (script->hasObjects()) {
JSObject *chain = script->getBlockScope(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());
}
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());
}
chain = chain->getParent();
} while (chain && chain->is<BlockObject>());
}
}
return nullptr;
}

View File

@ -2893,14 +2893,16 @@ LazyScript::finalize(FreeOp *fop)
fop->free_(table_);
}
JSObject *
StaticBlockObject *
JSScript::getBlockScope(jsbytecode *pc)
{
JS_ASSERT(containsPC(pc));
JS_ASSERT(pc >= main());
ptrdiff_t offset = pc - main();
if (offset < 0)
return nullptr;
if (!hasBlockScopes())
return nullptr;
@ -2918,7 +2920,7 @@ JSScript::getBlockScope(jsbytecode *pc)
}
}
return blockChain;
return blockChain ? &blockChain->as<StaticBlockObject>() : nullptr;
}
void

View File

@ -43,6 +43,7 @@ class RegExpObject;
struct SourceCompressionTask;
class Shape;
class WatchpointMap;
class StaticBlockObject;
namespace analyze {
class ScriptAnalysis;
@ -1104,7 +1105,7 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
return arr->vector[index];
}
JSObject *getBlockScope(jsbytecode *pc);
js::StaticBlockObject *getBlockScope(jsbytecode *pc);
/*
* The isEmpty method tells whether this script has code that computes any

View File

@ -1512,7 +1512,7 @@ TrapHandler(JSContext *cx, JSScript *, jsbytecode *pc, jsval *rvalArg,
JS_ASSERT(!iter.done());
/* Debug-mode currently disables Ion compilation. */
JSAbstractFramePtr frame(Jsvalify(iter.abstractFramePtr()));
JSAbstractFramePtr frame(iter.abstractFramePtr().raw(), iter.pc());
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(Jsvalify(fi.abstractFramePtr()));
JSAbstractFramePtr frame(fi.abstractFramePtr().raw(), fi.pc());
RootedScript fpscript(cx, frame.script());
bool ok = !!frame.evaluateUCInStackFrame(cx, chars, length,
fpscript->filename(),

View File

@ -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(cx, argc, vp, "get environment", args, thisobj, frame, dbg);
THIS_FRAME_OWNER_ITER(cx, argc, vp, "get environment", args, thisobj, _, iter, dbg);
Rooted<Env*> env(cx);
{
AutoCompartment ac(cx, frame.scopeChain());
env = GetDebugScopeForFrame(cx, frame);
AutoCompartment ac(cx, iter.abstractFramePtr().scopeChain());
env = GetDebugScopeForFrame(cx, iter.abstractFramePtr(), iter.pc());
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());
env = GetDebugScopeForFrame(cx, iter->abstractFramePtr(), iter->pc());
if (!env)
return false;
} else {

View File

@ -845,24 +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, AbstractFramePtr frame, uint32_t stackDepth)
js::UnwindScope(JSContext *cx, ScopeIter &si, uint32_t stackDepth)
{
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) {
for (; !si.done(); ++si) {
switch (si.type()) {
case ScopeIter::Block:
if (si.staticBlock().stackDepth() < stackDepth)
return;
if (cx->compartment()->debugMode())
DebugScopes::onPopBlock(cx, frame);
frame.popBlock(cx);
DebugScopes::onPopBlock(cx, si);
JS_ASSERT(&si.staticBlock() == si.frame().maybeBlockChain());
si.frame().popBlock(cx);
break;
case ScopeIter::With:
if (si.scope().as<WithObject>().stackDepth() < stackDepth)
return;
frame.popWith(cx);
si.frame().popWith(cx);
break;
case ScopeIter::Call:
case ScopeIter::StrictEvalScope:
@ -871,10 +869,24 @@ js::UnwindScope(JSContext *cx, AbstractFramePtr frame, 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 Interpret. */
/* c.f. the regular (catchable) TryNoteIter loop in HandleError. */
for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) {
JSTryNote *tn = *tni;
if (tn->kind == JSTRY_ITER) {
@ -943,6 +955,107 @@ 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)
@ -1373,13 +1486,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());
JSTrapStatus status = ScriptDebugPrologue(cx, activation.entryFrame(), REGS.pc);
switch (status) {
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
interpReturnOK = true;
goto forced_return;
ForcedReturn(cx, REGS);
goto successful_return_continuation;
case JSTRAP_THROW:
case JSTRAP_ERROR:
goto error;
@ -1391,7 +1504,6 @@ 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);
@ -1431,8 +1543,8 @@ CASE(EnableInterruptsPseudoOpcode)
break;
case JSTRAP_RETURN:
REGS.fp()->setReturnValue(rval);
interpReturnOK = true;
goto forced_return;
ForcedReturn(cx, REGS);
goto successful_return_continuation;
case JSTRAP_THROW:
cx->setPendingException(rval);
goto error;
@ -1452,8 +1564,8 @@ CASE(EnableInterruptsPseudoOpcode)
goto error;
case JSTRAP_RETURN:
REGS.fp()->setReturnValue(rval);
interpReturnOK = true;
goto forced_return;
ForcedReturn(cx, REGS);
goto successful_return_continuation;
case JSTRAP_THROW:
cx->setPendingException(rval);
goto error;
@ -1650,7 +1762,9 @@ CASE(JSOP_RETRVAL)
*/
CHECK_BRANCH();
successful_return_continuation:
interpReturnOK = true;
return_continuation:
if (activation.entryFrame() != REGS.fp())
inline_return:
{
@ -1659,7 +1773,7 @@ CASE(JSOP_RETRVAL)
#endif
if (JS_UNLIKELY(cx->compartment()->debugMode()))
interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), interpReturnOK);
interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK);
if (!REGS.fp()->isYielding())
REGS.fp()->epilogue(cx);
@ -1692,7 +1806,6 @@ CASE(JSOP_RETRVAL)
} else {
JS_ASSERT(REGS.stackDepth() == 0);
}
interpReturnOK = true;
goto exit;
}
@ -2568,12 +2681,12 @@ CASE(JSOP_FUNCALL)
if (!REGS.fp()->prologue(cx))
goto error;
if (JS_UNLIKELY(cx->compartment()->debugMode())) {
switch (ScriptDebugPrologue(cx, REGS.fp())) {
switch (ScriptDebugPrologue(cx, REGS.fp(), REGS.pc)) {
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
interpReturnOK = true;
goto forced_return;
ForcedReturn(cx, REGS);
goto successful_return_continuation;
case JSTRAP_THROW:
case JSTRAP_ERROR:
goto error;
@ -3213,8 +3326,8 @@ CASE(JSOP_DEBUGGER)
break;
case JSTRAP_RETURN:
REGS.fp()->setReturnValue(rval);
interpReturnOK = true;
goto forced_return;
ForcedReturn(cx, REGS);
goto successful_return_continuation;
case JSTRAP_THROW:
cx->setPendingException(rval);
goto error;
@ -3250,6 +3363,7 @@ CASE(JSOP_LEAVEBLOCKEXPR)
{
blockDepth = REGS.fp()->blockChain().stackDepth();
// Pop block from scope chain.
REGS.fp()->popBlock(cx);
if (*REGS.pc == JSOP_LEAVEBLOCK) {
@ -3271,13 +3385,13 @@ END_CASE(JSOP_LEAVEBLOCK)
CASE(JSOP_DEBUGLEAVEBLOCK)
{
JS_ASSERT(REGS.fp()->hasBlockChain());
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());
DebugScopes::onPopBlock(cx, REGS.fp(), REGS.pc);
}
END_CASE(JSOP_DEBUGLEAVEBLOCK)
@ -3339,116 +3453,32 @@ DEFAULT()
MOZ_ASSUME_UNREACHABLE("Interpreter loop exited via fallthrough");
error:
JS_ASSERT(script->containsPC(REGS.pc));
switch (HandleError(cx, REGS)) {
case SuccessfulReturnContinuation:
goto successful_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 ErrorReturnContinuation:
interpReturnOK = false;
goto return_continuation;
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:
/* 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 CatchContinuation:
ADVANCE_AND_DISPATCH(0);
case FinallyContinuation:
/*
* Propagate the exception or error to the caller unless the exception
* is an asynchronous return from a generator.
* Push (true, exception) pair for finally to indicate that [retsub]
* should rethrow the exception.
*/
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;
PUSH_BOOLEAN(true);
PUSH_COPY(cx->getPendingException());
cx->clearPendingException();
ADVANCE_AND_DISPATCH(0);
}
forced_return:
UnwindScope(cx, REGS.fp(), 0);
REGS.setToEndOfScript();
if (activation.entryFrame() != REGS.fp())
goto inline_return;
MOZ_ASSUME_UNREACHABLE("Invalid HandleError continuation");
exit:
if (JS_UNLIKELY(cx->compartment()->debugMode()))
interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), interpReturnOK);
interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK);
if (!REGS.fp()->isYielding())
REGS.fp()->epilogue(cx);
else

View File

@ -18,6 +18,8 @@
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
@ -36,7 +38,7 @@ namespace js {
* has set |frame|'s return value appropriately.
*/
extern JSTrapStatus
ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame);
ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
/*
* Announce to the debugger that the thread has exited a JavaScript frame, |frame|.
@ -54,7 +56,7 @@ ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame);
* alternative path, containing its own call to ScriptDebugEpilogue.)
*/
extern bool
ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, bool ok);
ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool ok);
/*
* Announce to the debugger that an exception has been thrown and propagated
@ -318,7 +320,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, AbstractFramePtr frame, uint32_t stackDepth);
UnwindScope(JSContext *cx, ScopeIter &si, uint32_t stackDepth);
/*
* Unwind for an uncatchable exception. This means not running finalizers, etc;

View File

@ -68,18 +68,19 @@ IsTopFrameConstructing(JSContext *cx, AbstractFramePtr frame)
}
JSTrapStatus
js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame)
js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
{
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, Jsvalify(frame), IsTopFrameConstructing(cx, frame),
frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame),
true, 0, cx->runtime()->debugHooks.executeHookData));
} else {
if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook)
frame.setHookData(hook(cx, Jsvalify(frame), IsTopFrameConstructing(cx, frame),
frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame),
true, 0, cx->runtime()->debugHooks.callHookData));
}
}
@ -105,7 +106,7 @@ js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame)
}
bool
js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, bool okArg)
js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool okArg)
{
JS_ASSERT_IF(frame.isStackFrame(), frame.asStackFrame() == cx->interpreterFrame());
@ -113,12 +114,13 @@ js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, bool okArg)
// 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, Jsvalify(frame), IsTopFrameConstructing(cx, frame), false, &ok, hookData);
hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData);
} else {
if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook)
hook(cx, Jsvalify(frame), IsTopFrameConstructing(cx, frame), false, &ok, hookData);
hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData);
}
}
@ -1228,27 +1230,27 @@ JS::FormatStackDump(JSContext *cx, char *buf, bool showArgs, bool showLocals, bo
return buf;
}
JSAbstractFramePtr::JSAbstractFramePtr(void *raw)
: ptr_(uintptr_t(raw))
JSAbstractFramePtr::JSAbstractFramePtr(void *raw, jsbytecode *pc)
: ptr_(uintptr_t(raw)), pc_(pc)
{ }
JSObject *
JSAbstractFramePtr::scopeChain(JSContext *cx)
{
AbstractFramePtr frame = Valueify(*this);
AbstractFramePtr frame(*this);
RootedObject scopeChain(cx, frame.scopeChain());
AutoCompartment ac(cx, scopeChain);
return GetDebugScopeForFrame(cx, frame);
return GetDebugScopeForFrame(cx, frame, pc());
}
JSObject *
JSAbstractFramePtr::callObject(JSContext *cx)
{
AbstractFramePtr frame = Valueify(*this);
AbstractFramePtr frame(*this);
if (!frame.isFunctionFrame())
return nullptr;
JSObject *o = GetDebugScopeForFrame(cx, frame);
JSObject *o = GetDebugScopeForFrame(cx, frame, pc());
/*
* Given that fp is a function frame and GetDebugScopeForFrame always fills
@ -1270,21 +1272,21 @@ JSAbstractFramePtr::callObject(JSContext *cx)
JSFunction *
JSAbstractFramePtr::maybeFun()
{
AbstractFramePtr frame = Valueify(*this);
AbstractFramePtr frame(*this);
return frame.maybeFun();
}
JSScript *
JSAbstractFramePtr::script()
{
AbstractFramePtr frame = Valueify(*this);
AbstractFramePtr frame(*this);
return frame.script();
}
bool
JSAbstractFramePtr::getThisValue(JSContext *cx, MutableHandleValue thisv)
{
AbstractFramePtr frame = Valueify(*this);
AbstractFramePtr frame(*this);
RootedObject scopeChain(cx, frame.scopeChain());
js::AutoCompartment ac(cx, scopeChain);
@ -1298,7 +1300,7 @@ JSAbstractFramePtr::getThisValue(JSContext *cx, MutableHandleValue thisv)
bool
JSAbstractFramePtr::isDebuggerFrame()
{
AbstractFramePtr frame = Valueify(*this);
AbstractFramePtr frame(*this);
return frame.isDebuggerFrame();
}
@ -1340,7 +1342,7 @@ JSAbstractFramePtr::evaluateUCInStackFrame(JSContext *cx,
if (!env)
return false;
AbstractFramePtr frame = Valueify(*this);
AbstractFramePtr frame(*this);
if (!ComputeThis(cx, frame))
return false;
RootedValue thisv(cx, frame.thisValue());
@ -1382,7 +1384,7 @@ JSAbstractFramePtr
JSBrokenFrameIterator::abstractFramePtr() const
{
NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
return Jsvalify(iter.abstractFramePtr());
return JSAbstractFramePtr(iter.abstractFramePtr().raw(), iter.pc());
}
jsbytecode *

View File

@ -874,19 +874,32 @@ ScopeIter::ScopeIter(JSObject &enclosingScope, JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
ScopeIter::ScopeIter(AbstractFramePtr frame, JSContext *cx
ScopeIter::ScopeIter(AbstractFramePtr frame, jsbytecode *pc, JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: cx(cx),
frame_(frame),
cur_(cx, frame.scopeChain()),
block_(cx, frame.maybeBlockChain())
block_(cx, frame.script()->getBlockScope(pc))
{
assertSameCompartment(cx, frame);
settle();
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
ScopeIter::ScopeIter(AbstractFramePtr frame, ScopeObject &scope, JSContext *cx
ScopeIter::ScopeIter(const ScopeIterKey &key, 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())
{
assertSameCompartment(cx, key.frame());
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
ScopeIter::ScopeIter(AbstractFramePtr frame, jsbytecode *pc, ScopeObject &scope, JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: cx(cx),
frame_(frame),
@ -903,13 +916,13 @@ ScopeIter::ScopeIter(AbstractFramePtr frame, ScopeObject &scope, JSContext *cx
* 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
* fp->blockChain. 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 the block
* chain. Fortunately, we can compare scope object stack depths to determine
* the block (if any) that encloses 'scope'.
*/
if (cur_->is<NestedScopeObject>()) {
block_ = frame.maybeBlockChain();
block_ = frame.script()->getBlockScope(pc);
while (block_) {
if (block_->stackDepth() <= cur_->as<NestedScopeObject>().stackDepth())
break;
@ -1104,7 +1117,7 @@ class DebugScopeProxy : public BaseProxyHandler
jsid id, Action action, MutableHandleValue vp)
{
JS_ASSERT(&debugScope->scope() == scope);
AbstractFramePtr maybeframe = DebugScopes::hasLiveFrame(*scope);
ScopeIterKey* maybeLiveScope = DebugScopes::hasLiveScope(*scope);
/* Handle unaliased formals, vars, and consts at function scope. */
if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
@ -1125,11 +1138,12 @@ class DebugScopeProxy : public BaseProxyHandler
if (script->varIsAliased(i))
return false;
if (maybeframe) {
if (maybeLiveScope) {
AbstractFramePtr frame = maybeLiveScope->frame();
if (action == GET)
vp.set(maybeframe.unaliasedVar(i));
vp.set(frame.unaliasedVar(i));
else
maybeframe.unaliasedVar(i) = vp;
frame.unaliasedVar(i) = vp;
} else if (JSObject *snapshot = debugScope->maybeSnapshot()) {
if (action == GET)
vp.set(snapshot->getDenseElement(bindings.numArgs() + i));
@ -1146,17 +1160,18 @@ class DebugScopeProxy : public BaseProxyHandler
if (script->formalIsAliased(i))
return false;
if (maybeframe) {
if (script->argsObjAliasesFormals() && maybeframe.hasArgsObj()) {
if (maybeLiveScope) {
AbstractFramePtr frame = maybeLiveScope->frame();
if (script->argsObjAliasesFormals() && frame.hasArgsObj()) {
if (action == GET)
vp.set(maybeframe.argsObj().arg(i));
vp.set(frame.argsObj().arg(i));
else
maybeframe.argsObj().setArg(i, vp);
frame.argsObj().setArg(i, vp);
} else {
if (action == GET)
vp.set(maybeframe.unaliasedFormal(i, DONT_CHECK_ALIASING));
vp.set(frame.unaliasedFormal(i, DONT_CHECK_ALIASING));
else
maybeframe.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp;
frame.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp;
}
} else if (JSObject *snapshot = debugScope->maybeSnapshot()) {
if (action == GET)
@ -1187,13 +1202,14 @@ class DebugScopeProxy : public BaseProxyHandler
if (block->staticBlock().isAliased(i))
return false;
if (maybeframe) {
JSScript *script = maybeframe.script();
if (maybeLiveScope) {
AbstractFramePtr frame = maybeLiveScope->frame();
JSScript *script = frame.script();
unsigned local = block->slotToLocalIndex(script->bindings, shape->slot());
if (action == GET)
vp.set(maybeframe.unaliasedLocal(local));
vp.set(frame.unaliasedLocal(local));
else
maybeframe.unaliasedLocal(local) = vp;
frame.unaliasedLocal(local) = vp;
JS_ASSERT(analyze::LocalSlot(script, local) >= analyze::TotalSlots(script));
} else {
if (action == GET)
@ -1250,14 +1266,14 @@ class DebugScopeProxy : public BaseProxyHandler
if (scope.as<CallObject>().callee().nonLazyScript()->needsArgsObj())
return true;
AbstractFramePtr maybeframe = DebugScopes::hasLiveFrame(scope);
if (!maybeframe) {
ScopeIterKey *maybeScope = DebugScopes::hasLiveScope(scope);
if (!maybeScope) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
"Debugger scope");
return false;
}
*maybeArgsObj = ArgumentsObject::createUnexpected(cx, maybeframe);
*maybeArgsObj = ArgumentsObject::createUnexpected(cx, maybeScope->frame());
return true;
}
@ -1699,7 +1715,7 @@ DebugScopes::addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject
}
JS_ASSERT(!scopes->liveScopes.has(&debugScope.scope()));
if (!scopes->liveScopes.put(&debugScope.scope(), si.frame())) {
if (!scopes->liveScopes.put(&debugScope.scope(), si)) {
js_ReportOutOfMemory(cx);
return false;
}
@ -1733,7 +1749,7 @@ DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx)
if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&callobj))
debugScope = &p->value()->as<DebugScopeObject>();
} else {
ScopeIter si(frame, cx);
ScopeIter si(frame, frame.script()->main(), cx);
if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
debugScope = p->value();
scopes->liveScopes.remove(&debugScope->scope().as<CallObject>());
@ -1788,7 +1804,7 @@ DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx)
}
void
DebugScopes::onPopBlock(JSContext *cx, AbstractFramePtr frame)
DebugScopes::onPopBlock(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
{
assertSameCompartment(cx, frame);
@ -1796,16 +1812,27 @@ DebugScopes::onPopBlock(JSContext *cx, AbstractFramePtr frame)
if (!scopes)
return;
StaticBlockObject &staticBlock = *frame.maybeBlockChain();
if (staticBlock.needsClone()) {
ClonedBlockObject &clone = frame.scopeChain()->as<ClonedBlockObject>();
clone.copyUnaliasedValues(frame);
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());
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(frame);
clone.copyUnaliasedValues(si.frame());
scopes->liveScopes.remove(&clone);
scopes->missingScopes.remove(p);
}
@ -1877,13 +1904,13 @@ DebugScopes::updateLiveScopes(JSContext *cx)
if (frame.isFunctionFrame() && frame.callee()->isGenerator())
continue;
for (ScopeIter si(frame, cx); !si.done(); ++si) {
for (ScopeIter si(frame, i.pc(), 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(), frame))
if (!scopes->liveScopes.put(&si.scope(), si))
return false;
liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &si.scope());
}
@ -1898,17 +1925,17 @@ DebugScopes::updateLiveScopes(JSContext *cx)
return true;
}
AbstractFramePtr
DebugScopes::hasLiveFrame(ScopeObject &scope)
ScopeIterKey*
DebugScopes::hasLiveScope(ScopeObject &scope)
{
DebugScopes *scopes = scope.compartment()->debugScopes;
if (!scopes)
return NullFramePtr();
return nullptr;
if (LiveScopeMap::Ptr p = scopes->liveScopes.lookup(&scope))
return p->value();
return &p->value();
return NullFramePtr();
return nullptr;
}
/*****************************************************************************/
@ -2029,8 +2056,8 @@ GetDebugScope(JSContext *cx, JSObject &obj)
}
Rooted<ScopeObject*> scope(cx, &obj.as<ScopeObject>());
if (AbstractFramePtr frame = DebugScopes::hasLiveFrame(*scope)) {
ScopeIter si(frame, *scope, cx);
if (ScopeIterKey *maybeLiveScope = DebugScopes::hasLiveScope(*scope)) {
ScopeIter si(*maybeLiveScope, cx);
return GetDebugScope(cx, si);
}
ScopeIter si(scope->enclosingScope(), cx);
@ -2065,12 +2092,12 @@ js::GetDebugScopeForFunction(JSContext *cx, HandleFunction fun)
}
JSObject *
js::GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame)
js::GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
{
assertSameCompartment(cx, frame);
if (CanUseDebugScopeMaps(cx) && !DebugScopes::updateLiveScopes(cx))
return nullptr;
ScopeIter si(frame, cx);
ScopeIter si(frame, pc, cx);
return GetDebugScope(cx, si);
}

View File

@ -534,22 +534,25 @@ class ScopeIter
public:
/* Constructing from a copy of an existing ScopeIter. */
explicit ScopeIter(const ScopeIter &si, JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
ScopeIter(const ScopeIter &si, JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
/* Constructing from StackFrame places ScopeIter on the innermost scope. */
explicit ScopeIter(AbstractFramePtr frame, JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
ScopeIter(AbstractFramePtr frame, jsbytecode *pc, JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
/*
* Without a StackFrame, the resulting ScopeIter is done() with
* enclosingScope() as given.
*/
explicit ScopeIter(JSObject &enclosingScope, JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
ScopeIter(JSObject &enclosingScope, JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
/* Like ScopeIter(StackFrame *) except start at 'scope'. */
ScopeIter(AbstractFramePtr frame, ScopeObject &scope, JSContext *cx
ScopeIter(AbstractFramePtr frame, jsbytecode *pc, ScopeObject &scope, JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
ScopeIter(const ScopeIterKey &key, JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
bool done() const { return !frame_; }
@ -578,15 +581,20 @@ 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_)
: frame_(si.frame_), cur_(si.cur_), block_(si.block_), type_(si.type_),
hasScopeObject_(si.hasScopeObject_)
{}
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;
@ -625,7 +633,7 @@ extern JSObject *
GetDebugScopeForFunction(JSContext *cx, HandleFunction fun);
extern JSObject *
GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame);
GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
/* Provides debugger access to a scope. */
class DebugScopeObject : public ProxyObject
@ -683,7 +691,7 @@ class DebugScopes
* updates of liveScopes need only fill in the new scopes.
*/
typedef HashMap<ScopeObject *,
AbstractFramePtr,
ScopeIterKey,
DefaultHasher<ScopeObject *>,
RuntimeAllocPolicy> LiveScopeMap;
LiveScopeMap liveScopes;
@ -710,12 +718,13 @@ class DebugScopes
static bool addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject &debugScope);
static bool updateLiveScopes(JSContext *cx);
static AbstractFramePtr hasLiveFrame(ScopeObject &scope);
static ScopeIterKey *hasLiveScope(ScopeObject &scope);
// In debug-mode, these must be called whenever exiting a scope that might
// have stack-allocated locals.
static void onPopCall(AbstractFramePtr frame, JSContext *cx);
static void onPopBlock(JSContext *cx, AbstractFramePtr frame);
static void onPopBlock(JSContext *cx, const ScopeIter &si);
static void onPopBlock(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
static void onPopWith(AbstractFramePtr frame);
static void onPopStrictEvalScope(AbstractFramePtr frame);
static void onCompartmentLeaveDebugMode(JSCompartment *c);

View File

@ -995,9 +995,6 @@ 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