Bug 986767 - Fix adjusting stepModeCount when removing a debuggee global from inside the onStep handler. (r=jimb)

This commit is contained in:
Shu-yu Guo 2014-04-24 01:59:37 -07:00
parent 168e132278
commit 2232bbdf1c
3 changed files with 65 additions and 53 deletions

View File

@ -3158,32 +3158,21 @@ JSScript::ensureHasDebugScript(JSContext *cx)
}
void
JSScript::recompileForStepMode(FreeOp *fop)
JSScript::setNewStepMode(FreeOp *fop, uint32_t newValue)
{
#ifdef JS_ION
if (hasBaselineScript())
baseline->toggleDebugTraps(this, nullptr);
#endif
}
bool
JSScript::tryNewStepMode(JSContext *cx, uint32_t newValue)
{
JS_ASSERT(hasDebugScript_);
DebugScript *debug = debugScript();
uint32_t prior = debug->stepMode;
debug->stepMode = newValue;
if (!prior != !newValue) {
/* Step mode has been enabled or disabled. Alert the methodjit. */
recompileForStepMode(cx->runtime()->defaultFreeOp());
#ifdef JS_ION
if (hasBaselineScript())
baseline->toggleDebugTraps(this, nullptr);
#endif
if (!stepModeEnabled() && !debug->numSites)
js_free(releaseDebugScript());
fop->free_(releaseDebugScript());
}
return true;
}
bool
@ -3192,25 +3181,40 @@ JSScript::setStepModeFlag(JSContext *cx, bool step)
if (!ensureHasDebugScript(cx))
return false;
return tryNewStepMode(cx, (debugScript()->stepMode & stepCountMask) |
(step ? stepFlagMask : 0));
setNewStepMode(cx->runtime()->defaultFreeOp(),
(debugScript()->stepMode & stepCountMask) |
(step ? stepFlagMask : 0));
return true;
}
bool
JSScript::changeStepModeCount(JSContext *cx, int delta)
JSScript::incrementStepModeCount(JSContext *cx)
{
assertSameCompartment(cx, this);
MOZ_ASSERT(cx->compartment()->debugMode());
if (!ensureHasDebugScript(cx))
return false;
assertSameCompartment(cx, this);
JS_ASSERT_IF(delta > 0, cx->compartment()->debugMode());
DebugScript *debug = debugScript();
uint32_t count = debug->stepMode & stepCountMask;
JS_ASSERT(((count + delta) & stepCountMask) == count + delta);
return tryNewStepMode(cx,
(debug->stepMode & stepFlagMask) |
((count + delta) & stepCountMask));
MOZ_ASSERT(((count + 1) & stepCountMask) == count + 1);
setNewStepMode(cx->runtime()->defaultFreeOp(),
(debug->stepMode & stepFlagMask) |
((count + 1) & stepCountMask));
return true;
}
void
JSScript::decrementStepModeCount(FreeOp *fop)
{
DebugScript *debug = debugScript();
uint32_t count = debug->stepMode & stepCountMask;
setNewStepMode(fop,
(debug->stepMode & stepFlagMask) |
((count - 1) & stepCountMask));
}
BreakpointSite *

View File

@ -1504,14 +1504,8 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
bool formalLivesInArgumentsObject(unsigned argSlot);
private:
/*
* Recompile with or without single-stepping support, as directed
* by stepModeEnabled().
*/
void recompileForStepMode(js::FreeOp *fop);
/* Attempt to change this->stepMode to |newValue|. */
bool tryNewStepMode(JSContext *cx, uint32_t newValue);
/* Change this->stepMode to |newValue|. */
void setNewStepMode(js::FreeOp *fop, uint32_t newValue);
bool ensureHasDebugScript(JSContext *cx);
js::DebugScript *debugScript();
@ -1549,8 +1543,11 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
* the flag (set by setStepModeFlag) is set, then the script is in
* single-step mode. (JSD uses an on/off-style interface; Debugger uses a
* count-style interface.)
*
* Only incrementing is fallible, as it could allocate a DebugScript.
*/
bool changeStepModeCount(JSContext *cx, int delta);
bool incrementStepModeCount(JSContext *cx);
void decrementStepModeCount(js::FreeOp *fop);
bool stepModeEnabled() { return hasDebugScript_ && !!debugScript()->stepMode; }

View File

@ -147,8 +147,8 @@ ValueToIdentifier(JSContext *cx, HandleValue v, MutableHandleId id)
* A range of all the Debugger.Frame objects for a particular AbstractFramePtr.
*
* FIXME This checks only current debuggers, so it relies on a hack in
* Debugger::removeDebuggeeGlobal to make sure only current debuggers have Frame
* objects with .live === true.
* Debugger::removeDebuggeeGlobal to make sure only current debuggers
* have Frame objects with .live === true.
*/
class Debugger::FrameRange
{
@ -553,6 +553,10 @@ Debugger::slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHan
return JSTRAP_CONTINUE;
}
static void
DebuggerFrame_maybeDecrementFrameScriptStepModeCount(FreeOp *fop, AbstractFramePtr frame,
JSObject *frameobj);
static void
DebuggerFrame_freeScriptFrameIterData(FreeOp *fop, JSObject *obj);
@ -630,15 +634,9 @@ Debugger::slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool frame
Debugger *dbg = r.frontDebugger();
JS_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
DebuggerFrame_freeScriptFrameIterData(cx->runtime()->defaultFreeOp(), frameobj);
/* If this frame had an onStep handler, adjust the script's count. */
if (!frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() &&
!frame.script()->changeStepModeCount(cx, -1))
{
status = JSTRAP_ERROR;
/* Don't exit the loop; we must mark all frames as dead. */
}
FreeOp *fop = cx->runtime()->defaultFreeOp();
DebuggerFrame_freeScriptFrameIterData(fop, frameobj);
DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj);
dbg->frames.remove(frame);
}
@ -2288,7 +2286,7 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
GlobalObjectSet::Enum *debugEnum)
{
AutoDebugModeInvalidation invalidate(global->compartment());
return removeDebuggeeGlobal(fop, global, invalidate, compartmentEnum, debugEnum);
removeDebuggeeGlobal(fop, global, invalidate, compartmentEnum, debugEnum);
}
void
@ -2319,8 +2317,10 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
*/
for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) {
AbstractFramePtr frame = e.front().key();
JSObject *frameobj = e.front().value();
if (&frame.script()->global() == global) {
DebuggerFrame_freeScriptFrameIterData(fop, e.front().value());
DebuggerFrame_freeScriptFrameIterData(fop, frameobj);
DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj);
e.removeFront();
}
}
@ -4042,6 +4042,15 @@ DebuggerFrame_freeScriptFrameIterData(FreeOp *fop, JSObject *obj)
obj->setPrivate(nullptr);
}
static void
DebuggerFrame_maybeDecrementFrameScriptStepModeCount(FreeOp *fop, AbstractFramePtr frame,
JSObject *frameobj)
{
/* If this frame has an onStep handler, decrement the script's count. */
if (!frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
frame.script()->decrementStepModeCount(fop);
}
static void
DebuggerFrame_finalize(FreeOp *fop, JSObject *obj)
{
@ -4446,12 +4455,14 @@ DebuggerFrame_setOnStep(JSContext *cx, unsigned argc, Value *vp)
}
Value prior = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);
int delta = !args[0].isUndefined() - !prior.isUndefined();
if (delta != 0) {
/* Try to adjust this frame's script single-step mode count. */
if (!args[0].isUndefined() && prior.isUndefined()) {
// Single stepping toggled off->on.
AutoCompartment ac(cx, frame.scopeChain());
if (!frame.script()->changeStepModeCount(cx, delta))
if (!frame.script()->incrementStepModeCount(cx))
return false;
} else if (args[0].isUndefined() && !prior.isUndefined()) {
// Single stepping toggled on->off.
frame.script()->decrementStepModeCount(cx->runtime()->defaultFreeOp());
}
/* Now that the step mode switch has succeeded, we can install the handler. */