зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1125120 - Clean up Debugger.Frames when the debug mode in-place Ion bailout fails. (r=jandem)
This commit is contained in:
Родитель
4e73ce230d
Коммит
e8bafaf621
|
@ -389,10 +389,10 @@ HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromEx
|
|||
// debuggee of a Debugger with a live onExceptionUnwind hook, or if a
|
||||
// Debugger has observed this frame (e.g., for onPop).
|
||||
bool shouldBail = Debugger::hasLiveHook(cx->global(), Debugger::OnExceptionUnwind);
|
||||
RematerializedFrame *rematFrame = nullptr;
|
||||
if (!shouldBail) {
|
||||
JitActivation *act = cx->runtime()->activation()->asJit();
|
||||
RematerializedFrame *rematFrame =
|
||||
act->lookupRematerializedFrame(frame.frame().fp(), frame.frameNo());
|
||||
rematFrame = act->lookupRematerializedFrame(frame.frame().fp(), frame.frameNo());
|
||||
shouldBail = rematFrame && rematFrame->isDebuggee();
|
||||
}
|
||||
|
||||
|
@ -414,7 +414,19 @@ HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromEx
|
|||
uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed);
|
||||
if (retval == BAILOUT_RETURN_OK)
|
||||
return;
|
||||
|
||||
// If bailout failed (e.g., due to overrecursion), clean up any
|
||||
// Debugger.Frame instances here. Normally this should happen
|
||||
// inside the debug epilogue, but due to bailout failure, we
|
||||
// cannot honor any Debugger hooks.
|
||||
if (rematFrame)
|
||||
Debugger::handleUnrecoverableIonBailoutError(cx, rematFrame);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (rematFrame)
|
||||
Debugger::assertNotInFrameMaps(rematFrame);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!script->hasTrynotes())
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "jit/JitFrames.h"
|
||||
#include "vm/ArgumentsObject.h"
|
||||
#include "vm/Debugger.h"
|
||||
|
||||
#include "jsscriptinlines.h"
|
||||
#include "jit/JitFrames-inl.h"
|
||||
|
@ -95,6 +96,7 @@ RematerializedFrame::FreeInVector(Vector<RematerializedFrame *> &frames)
|
|||
{
|
||||
for (size_t i = 0; i < frames.length(); i++) {
|
||||
RematerializedFrame *f = frames[i];
|
||||
Debugger::assertNotInFrameMaps(f);
|
||||
f->RematerializedFrame::~RematerializedFrame();
|
||||
js_free(f);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ js::Debugger::onLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok)
|
|||
MOZ_ASSERT_IF(evalTraps, frame.isDebuggee());
|
||||
if (frame.isDebuggee())
|
||||
ok = slowPathOnLeaveFrame(cx, frame, ok);
|
||||
assertNotInFrameMaps(frame);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
|
|
@ -664,30 +664,11 @@ Debugger::slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool frame
|
|||
}
|
||||
|
||||
/*
|
||||
* Clean up all Debugger.Frame instances. Use a fresh FrameRange, as one
|
||||
* debugger's onPop handler could have caused another debugger to create its
|
||||
* own Debugger.Frame instance.
|
||||
* Clean up all Debugger.Frame instances. This call creates a fresh
|
||||
* FrameRange, as one debugger's onPop handler could have caused another
|
||||
* debugger to create its own Debugger.Frame instance.
|
||||
*/
|
||||
for (FrameRange r(frame, global); !r.empty(); r.popFront()) {
|
||||
RootedNativeObject frameobj(cx, r.frontFrame());
|
||||
Debugger *dbg = r.frontDebugger();
|
||||
MOZ_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
|
||||
|
||||
FreeOp *fop = cx->runtime()->defaultFreeOp();
|
||||
DebuggerFrame_freeScriptFrameIterData(fop, frameobj);
|
||||
DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj);
|
||||
|
||||
dbg->frames.remove(frame);
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is an eval frame, then from the debugger's perspective the
|
||||
* script is about to be destroyed. Remove any breakpoints in it.
|
||||
*/
|
||||
if (frame.isEvalFrame()) {
|
||||
RootedScript script(cx, frame.script());
|
||||
script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), nullptr, nullptr);
|
||||
}
|
||||
removeFromFrameMapsAndClearBreakpointsIn(cx, frame);
|
||||
|
||||
/* Establish (status, value) as our resumption value. */
|
||||
switch (status) {
|
||||
|
@ -4866,6 +4847,42 @@ Debugger::replaceFrameGuts(JSContext *cx, AbstractFramePtr from, AbstractFramePt
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Debugger::assertNotInFrameMaps(AbstractFramePtr frame)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
FrameRange r(frame);
|
||||
MOZ_ASSERT(r.empty());
|
||||
#endif
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Debugger::removeFromFrameMapsAndClearBreakpointsIn(JSContext *cx, AbstractFramePtr frame)
|
||||
{
|
||||
Handle<GlobalObject *> global = cx->global();
|
||||
|
||||
for (FrameRange r(frame, global); !r.empty(); r.popFront()) {
|
||||
RootedNativeObject frameobj(cx, r.frontFrame());
|
||||
Debugger *dbg = r.frontDebugger();
|
||||
MOZ_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
|
||||
|
||||
FreeOp *fop = cx->runtime()->defaultFreeOp();
|
||||
DebuggerFrame_freeScriptFrameIterData(fop, frameobj);
|
||||
DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj);
|
||||
|
||||
dbg->frames.remove(frame);
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is an eval frame, then from the debugger's perspective the
|
||||
* script is about to be destroyed. Remove any breakpoints in it.
|
||||
*/
|
||||
if (frame.isEvalFrame()) {
|
||||
RootedScript script(cx, frame.script());
|
||||
script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::handleBaselineOsr(JSContext *cx, InterpreterFrame *from, jit::BaselineFrame *to)
|
||||
{
|
||||
|
@ -4891,6 +4908,15 @@ Debugger::handleIonBailout(JSContext *cx, jit::RematerializedFrame *from, jit::B
|
|||
return replaceFrameGuts(cx, from, to, iter);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Debugger::handleUnrecoverableIonBailoutError(JSContext *cx, jit::RematerializedFrame *frame)
|
||||
{
|
||||
// Ion bailout can fail due to overrecursion. In such cases we cannot
|
||||
// honor any further Debugger hooks on the frame, and need to ensure that
|
||||
// its Debugger.Frame entry is cleaned up.
|
||||
removeFromFrameMapsAndClearBreakpointsIn(cx, frame);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Debugger::propagateForcedReturn(JSContext *cx, AbstractFramePtr frame, HandleValue rval)
|
||||
{
|
||||
|
|
|
@ -425,7 +425,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
static const JSPropertySpec properties[];
|
||||
static const JSFunctionSpec methods[];
|
||||
|
||||
static bool getNewestAbstractFramePtr(JSContext *cx);
|
||||
static void removeFromFrameMapsAndClearBreakpointsIn(JSContext *cx, AbstractFramePtr frame);
|
||||
static bool updateExecutionObservabilityOfFrames(JSContext *cx, const ExecutionObservableSet &obs,
|
||||
IsObserving observing);
|
||||
static bool updateExecutionObservabilityOfScripts(JSContext *cx, const ExecutionObservableSet &obs,
|
||||
|
@ -607,8 +607,10 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
static JSTrapStatus onSingleStep(JSContext *cx, MutableHandleValue vp);
|
||||
static bool handleBaselineOsr(JSContext *cx, InterpreterFrame *from, jit::BaselineFrame *to);
|
||||
static bool handleIonBailout(JSContext *cx, jit::RematerializedFrame *from, jit::BaselineFrame *to);
|
||||
static void handleUnrecoverableIonBailoutError(JSContext *cx, jit::RematerializedFrame *frame);
|
||||
static void propagateForcedReturn(JSContext *cx, AbstractFramePtr frame, HandleValue rval);
|
||||
static bool hasLiveHook(GlobalObject *global, Hook which);
|
||||
static void assertNotInFrameMaps(AbstractFramePtr frame);
|
||||
|
||||
/************************************* Functions for use by Debugger.cpp. */
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче