mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 927782 - Part 10: Iterate block chain from compile-time block scope maps, not runtime blockChain. r=luke
This commit is contained in:
parent
50f4f9bbdd
commit
457223d858
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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_; }
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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: ");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
|
@ -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 {
|
||||
|
@ -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 ®s)
|
||||
{
|
||||
UnwindScope(cx, si, 0);
|
||||
regs.setToEndOfScript();
|
||||
}
|
||||
|
||||
static void
|
||||
ForcedReturn(JSContext *cx, FrameRegs ®s)
|
||||
{
|
||||
ScopeIter si(regs.fp(), regs.pc, cx);
|
||||
ForcedReturn(cx, si, regs);
|
||||
}
|
||||
|
||||
void
|
||||
js::UnwindForUncatchableException(JSContext *cx, const FrameRegs ®s)
|
||||
{
|
||||
/* c.f. the regular (catchable) TryNoteIter loop in 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 ®s)
|
||||
{
|
||||
JS_ASSERT(regs.fp()->script()->containsPC(regs.pc));
|
||||
|
||||
ScopeIter si(regs.fp(), regs.pc, cx);
|
||||
bool ok = false;
|
||||
|
||||
again:
|
||||
if (cx->isExceptionPending()) {
|
||||
/* Call debugger throw hooks. */
|
||||
if (JS_UNLIKELY(cx->compartment()->debugMode())) {
|
||||
JSTrapStatus status = DebugExceptionUnwind(cx, regs.fp(), regs.pc);
|
||||
switch (status) {
|
||||
case JSTRAP_ERROR:
|
||||
goto again;
|
||||
|
||||
case JSTRAP_CONTINUE:
|
||||
case JSTRAP_THROW:
|
||||
break;
|
||||
|
||||
case JSTRAP_RETURN:
|
||||
ForcedReturn(cx, si, regs);
|
||||
return SuccessfulReturnContinuation;
|
||||
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid trap status");
|
||||
}
|
||||
}
|
||||
|
||||
for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) {
|
||||
JSTryNote *tn = *tni;
|
||||
|
||||
UnwindScope(cx, si, tn->stackDepth);
|
||||
|
||||
/*
|
||||
* Set pc to the first bytecode after the the try note to point
|
||||
* to the beginning of catch or finally or to [enditer] closing
|
||||
* the for-in loop.
|
||||
*/
|
||||
regs.pc = regs.fp()->script()->main() + tn->start + tn->length;
|
||||
regs.sp = regs.spForStackDepth(tn->stackDepth);
|
||||
|
||||
switch (tn->kind) {
|
||||
case JSTRY_CATCH:
|
||||
/* Catch cannot intercept the closing of a generator. */
|
||||
if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
|
||||
break;
|
||||
|
||||
/*
|
||||
* Don't clear exceptions to save cx->exception from GC
|
||||
* until it is pushed to the stack via [exception] in the
|
||||
* catch block.
|
||||
*/
|
||||
return CatchContinuation;
|
||||
|
||||
case JSTRY_FINALLY:
|
||||
return FinallyContinuation;
|
||||
|
||||
case JSTRY_ITER: {
|
||||
/* This is similar to JSOP_ENDITER in the interpreter loop. */
|
||||
JS_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER);
|
||||
RootedObject obj(cx, ®s.sp[-1].toObject());
|
||||
bool ok = UnwindIteratorForException(cx, obj);
|
||||
regs.sp -= 1;
|
||||
if (!ok)
|
||||
goto again;
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRY_LOOP:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Propagate the exception or error to the caller unless the exception
|
||||
* is an asynchronous return from a generator.
|
||||
*/
|
||||
if (JS_UNLIKELY(cx->isExceptionPending() &&
|
||||
cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) {
|
||||
cx->clearPendingException();
|
||||
ok = true;
|
||||
regs.fp()->clearReturnValue();
|
||||
}
|
||||
} else {
|
||||
UnwindForUncatchableException(cx, regs);
|
||||
}
|
||||
|
||||
ForcedReturn(cx, si, regs);
|
||||
return ok ? SuccessfulReturnContinuation : ErrorReturnContinuation;
|
||||
}
|
||||
|
||||
#define REGS (activation.regs())
|
||||
#define PUSH_COPY(v) do { *REGS.sp++ = (v); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0)
|
||||
#define PUSH_COPY_SKIP_CHECK(v) *REGS.sp++ = (v)
|
||||
@ -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 = ®S.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
|
||||
|
@ -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;
|
||||
|
@ -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 *
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user