Bug 933882 - Invalidate JIT code instead of doing full GC on debug mode toggle. (r=bhackett)

This commit is contained in:
Shu-yu Guo 2013-11-20 22:50:28 -08:00
parent e223c546c9
commit b7a4c97e80
11 changed files with 239 additions and 143 deletions

View File

@ -25,7 +25,6 @@ namespace JS {
D(TOO_MUCH_MALLOC) \
D(ALLOC_TRIGGER) \
D(DEBUG_GC) \
D(DEBUG_MODE_GC) \
D(TRANSPLANT) \
D(RESET) \
D(OUT_OF_NURSERY) \

View File

@ -226,13 +226,8 @@ Zone::discardJitCode(FreeOp *fop)
script->resetUseCount();
}
for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
/* Free optimized baseline stubs. */
if (comp->jitCompartment())
comp->jitCompartment()->optimizedStubSpace()->free();
comp->types.clearCompilerOutputs(fop);
}
for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next())
jit::FinishDiscardJitCode(fop, comp);
}
#endif
}

View File

@ -2376,15 +2376,20 @@ InvalidateActivation(FreeOp *fop, uint8_t *ionTop, bool invalidateAll)
IonSpew(IonSpew_Invalidate, "END invalidating activation");
}
static void
StopOffThreadCompilation(JSCompartment *comp)
{
if (!comp->jitCompartment())
return;
CancelOffThreadIonCompile(comp, nullptr);
FinishAllOffThreadCompilations(comp->jitCompartment());
}
void
jit::InvalidateAll(FreeOp *fop, Zone *zone)
{
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
if (!comp->jitCompartment())
continue;
CancelOffThreadIonCompile(comp, nullptr);
FinishAllOffThreadCompilations(comp->jitCompartment());
}
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
StopOffThreadCompilation(comp);
for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter) {
if (iter.activation()->compartment()->zone() == zone) {
@ -2543,6 +2548,16 @@ jit::FinishInvalidation(FreeOp *fop, JSScript *script)
FinishInvalidationOf(fop, script, script->parallelIonScript(), true);
}
void
jit::FinishDiscardJitCode(FreeOp *fop, JSCompartment *comp)
{
// Free optimized baseline stubs.
if (comp->jitCompartment())
comp->jitCompartment()->optimizedStubSpace()->free();
comp->types.clearCompilerOutputs(fop);
}
void
jit::MarkValueFromIon(JSRuntime *rt, Value *vp)
{
@ -2731,3 +2746,62 @@ jit::TraceIonScripts(JSTracer* trc, JSScript *script)
if (script->hasBaselineScript())
jit::BaselineScript::Trace(trc, script->baselineScript());
}
AutoDebugModeInvalidation::~AutoDebugModeInvalidation()
{
MOZ_ASSERT(!!comp_ != !!zone_);
if (needInvalidation_ == NoNeed)
return;
// Invalidate the stack if any compartments toggled from on->off, because
// we allow scripts to be on stack when turning off debug mode.
bool invalidateStack = needInvalidation_ == ToggledOff;
Zone *zone = zone_ ? zone_ : comp_->zone();
JSRuntime *rt = zone->runtimeFromMainThread();
FreeOp *fop = rt->defaultFreeOp();
if (comp_) {
StopOffThreadCompilation(comp_);
} else {
for (CompartmentsInZoneIter comp(zone_); !comp.done(); comp.next())
StopOffThreadCompilation(comp);
}
if (invalidateStack) {
jit::MarkActiveBaselineScripts(zone);
for (JitActivationIterator iter(rt); !iter.done(); ++iter) {
JSCompartment *comp = iter.activation()->compartment();
if ((comp_ && comp_ == comp) ||
(zone_ && zone_ == comp->zone() && comp->principals))
{
IonContext ictx(CompileRuntime::get(rt));
AutoFlushCache afc("AutoDebugModeInvalidation", rt->jitRuntime());
IonSpew(IonSpew_Invalidate, "Invalidating frames for debug mode toggle");
InvalidateActivation(fop, iter.jitTop(), true);
}
}
}
for (gc::CellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if ((comp_ && script->compartment() == comp_) ||
(zone_ && script->compartment()->principals))
{
FinishInvalidation(fop, script);
FinishDiscardBaselineScript(fop, script);
// script->clearAnalysis();
script->resetUseCount();
} else if (script->hasBaselineScript()) {
script->baselineScript()->resetActive();
}
}
if (comp_) {
FinishDiscardJitCode(fop, comp_);
} else {
for (CompartmentsInZoneIter comp(zone_); !comp.done(); comp.next())
FinishDiscardJitCode(fop, comp);
}
}

View File

@ -440,6 +440,7 @@ class JitCompartment
// Called from JSCompartment::discardJitCode().
void InvalidateAll(FreeOp *fop, JS::Zone *zone);
void FinishInvalidation(FreeOp *fop, JSScript *script);
void FinishDiscardJitCode(FreeOp *fop, JSCompartment *comp);
// On windows systems, really large frames need to be incrementally touched.
// The following constant defines the minimum increment of the touch.

View File

@ -727,7 +727,7 @@ CreateLazyScriptsForCompartment(JSContext *cx)
}
bool
JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeGC &dmgc)
JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidation &invalidate)
{
bool enabledBefore = debugMode();
bool enabledAfter = (debugModeBits & ~unsigned(DebugFromC)) || b;
@ -756,7 +756,7 @@ JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeGC &dmgc)
debugModeBits = (debugModeBits & ~unsigned(DebugFromC)) | (b ? DebugFromC : 0);
JS_ASSERT(debugMode() == enabledAfter);
if (enabledBefore != enabledAfter) {
updateForDebugMode(cx->runtime()->defaultFreeOp(), dmgc);
updateForDebugMode(cx->runtime()->defaultFreeOp(), invalidate);
if (!enabledAfter)
DebugScopes::onCompartmentLeaveDebugMode(this);
}
@ -764,7 +764,7 @@ JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeGC &dmgc)
}
void
JSCompartment::updateForDebugMode(FreeOp *fop, AutoDebugModeGC &dmgc)
JSCompartment::updateForDebugMode(FreeOp *fop, AutoDebugModeInvalidation &invalidate)
{
JSRuntime *rt = runtimeFromMainThread();
@ -774,38 +774,31 @@ JSCompartment::updateForDebugMode(FreeOp *fop, AutoDebugModeGC &dmgc)
}
#ifdef JS_ION
MOZ_ASSERT(invalidate.isFor(this));
JS_ASSERT_IF(debugMode(), !hasScriptsOnStack());
// When we change a compartment's debug mode, whether we're turning it
// on or off, we must always throw away all analyses: debug mode
// affects various aspects of the analysis, which then get baked into
// SSA results, which affects code generation in complicated ways. We
// must also throw away all JIT code, as its soundness depends on the
// analyses.
// Invalidate all JIT code since debug mode invalidates assumptions made
// by the JIT.
//
// It suffices to do a garbage collection cycle or to finish the
// ongoing GC cycle. The necessary cleanup happens in
// JSCompartment::sweep.
//
// dmgc makes sure we can't forget to GC, but it is also important not
// to run any scripts in this compartment until the dmgc is destroyed.
// That is the caller's responsibility.
if (!rt->isHeapBusy())
dmgc.scheduleGC(zone());
// The AutoDebugModeInvalidation argument makes sure we can't forget to
// invalidate, but it is also important not to run any scripts in this
// compartment until the invalidate is destroyed. That is the caller's
// responsibility.
invalidate.scheduleInvalidation(debugMode());
#endif
}
bool
JSCompartment::addDebuggee(JSContext *cx, js::GlobalObject *global)
{
AutoDebugModeGC dmgc(cx->runtime());
return addDebuggee(cx, global, dmgc);
AutoDebugModeInvalidation invalidate(this);
return addDebuggee(cx, global, invalidate);
}
bool
JSCompartment::addDebuggee(JSContext *cx,
GlobalObject *globalArg,
AutoDebugModeGC &dmgc)
AutoDebugModeInvalidation &invalidate)
{
Rooted<GlobalObject*> global(cx, globalArg);
@ -817,9 +810,8 @@ JSCompartment::addDebuggee(JSContext *cx,
return false;
}
debugModeBits |= DebugFromJS;
if (!wasEnabled) {
updateForDebugMode(cx->runtime()->defaultFreeOp(), dmgc);
}
if (!wasEnabled)
updateForDebugMode(cx->runtime()->defaultFreeOp(), invalidate);
return true;
}
@ -828,14 +820,14 @@ JSCompartment::removeDebuggee(FreeOp *fop,
js::GlobalObject *global,
js::GlobalObjectSet::Enum *debuggeesEnum)
{
AutoDebugModeGC dmgc(fop->runtime());
return removeDebuggee(fop, global, dmgc, debuggeesEnum);
AutoDebugModeInvalidation invalidate(this);
return removeDebuggee(fop, global, invalidate, debuggeesEnum);
}
void
JSCompartment::removeDebuggee(FreeOp *fop,
js::GlobalObject *global,
AutoDebugModeGC &dmgc,
AutoDebugModeInvalidation &invalidate,
js::GlobalObjectSet::Enum *debuggeesEnum)
{
bool wasEnabled = debugMode();
@ -849,7 +841,7 @@ JSCompartment::removeDebuggee(FreeOp *fop,
debugModeBits &= ~DebugFromJS;
if (wasEnabled && !debugMode()) {
DebugScopes::onCompartmentLeaveDebugMode(this);
updateForDebugMode(fop, dmgc);
updateForDebugMode(fop, invalidate);
}
}
}

View File

@ -110,7 +110,7 @@ struct TypeInferenceSizes;
}
namespace js {
class AutoDebugModeGC;
class AutoDebugModeInvalidation;
class ArrayBufferObject;
class DebugScopes;
class WeakMapBase;
@ -364,19 +364,20 @@ struct JSCompartment
private:
/* This is called only when debugMode() has just toggled. */
void updateForDebugMode(js::FreeOp *fop, js::AutoDebugModeGC &dmgc);
void updateForDebugMode(js::FreeOp *fop, js::AutoDebugModeInvalidation &invalidate);
public:
js::GlobalObjectSet &getDebuggees() { return debuggees; }
bool addDebuggee(JSContext *cx, js::GlobalObject *global);
bool addDebuggee(JSContext *cx, js::GlobalObject *global,
js::AutoDebugModeGC &dmgc);
js::AutoDebugModeInvalidation &invalidate);
void removeDebuggee(js::FreeOp *fop, js::GlobalObject *global,
js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
void removeDebuggee(js::FreeOp *fop, js::GlobalObject *global,
js::AutoDebugModeGC &dmgc,
js::AutoDebugModeInvalidation &invalidate,
js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
bool setDebugModeFromC(JSContext *cx, bool b, js::AutoDebugModeGC &dmgc);
bool setDebugModeFromC(JSContext *cx, bool b,
js::AutoDebugModeInvalidation &invalidate);
void clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JSObject *handler);
void clearTraps(js::FreeOp *fop);
@ -422,29 +423,56 @@ JSRuntime::isAtomsZone(JS::Zone *zone)
}
// For use when changing the debug mode flag on one or more compartments.
// Do not run scripts in any compartment that is scheduled for GC using this
// object. See comment in updateForDebugMode.
// Invalidate and discard JIT code since debug mode breaks JIT assumptions.
//
class js::AutoDebugModeGC
// AutoDebugModeInvalidation has two modes: compartment or zone
// invalidation. While it is correct to always use compartment invalidation,
// if you know ahead of time you need to invalidate a whole zone, it is faster
// to invalidate the zone.
//
// Compartment invalidation only invalidates scripts belonging to that
// compartment.
//
// Zone invalidation invalidates all scripts belonging to non-special
// (i.e. those with principals) compartments of the zone.
//
// FIXME: Remove entirely once bug 716647 lands.
//
class js::AutoDebugModeInvalidation
{
JSRuntime *rt;
bool needGC;
public:
explicit AutoDebugModeGC(JSRuntime *rt) : rt(rt), needGC(false) {}
JSCompartment *comp_;
JS::Zone *zone_;
~AutoDebugModeGC() {
// Under some circumstances (say, in the midst of an animation),
// the garbage collector may try to retain JIT code and analyses.
// The DEBUG_MODE_GC reason forces the collector to always throw
// everything away, as required for debug mode transitions.
if (needGC)
GC(rt, GC_NORMAL, JS::gcreason::DEBUG_MODE_GC);
enum {
NoNeed = 0,
ToggledOn = 1,
ToggledOff = 2
} needInvalidation_;
public:
explicit AutoDebugModeInvalidation(JSCompartment *comp)
: comp_(comp), zone_(nullptr), needInvalidation_(NoNeed)
{ }
explicit AutoDebugModeInvalidation(JS::Zone *zone)
: comp_(nullptr), zone_(zone), needInvalidation_(NoNeed)
{ }
~AutoDebugModeInvalidation();
bool isFor(JSCompartment *comp) {
if (comp_)
return comp == comp_;
return comp->zone() == zone_;
}
void scheduleGC(Zone *zone) {
JS_ASSERT(!rt->isHeapBusy());
PrepareZoneForGC(zone);
needGC = true;
void scheduleInvalidation(bool debugMode) {
// If we are scheduling invalidation for multiple compartments, they
// must all agree on the toggle. This is so we can decide if we need
// to invalidate on-stack scripts.
MOZ_ASSERT_IF(needInvalidation_ != NoNeed,
needInvalidation_ == debugMode ? ToggledOn : ToggledOff);
needInvalidation_ = debugMode ? ToggledOn : ToggledOff;
}
};

View File

@ -4661,13 +4661,8 @@ ShouldCleanUpEverything(JSRuntime *rt, JS::gcreason::Reason reason, JSGCInvocati
// During shutdown, we must clean everything up, for the sake of leak
// detection. When a runtime has no contexts, or we're doing a GC before a
// shutdown CC, those are strong indications that we're shutting down.
//
// DEBUG_MODE_GC indicates we're discarding code because the debug mode
// has changed; debug mode affects the results of bytecode analysis, so
// we need to clear everything away.
return reason == JS::gcreason::DESTROY_RUNTIME ||
reason == JS::gcreason::SHUTDOWN_CC ||
reason == JS::gcreason::DEBUG_MODE_GC ||
gckind == GC_SHRINK;
}

View File

@ -1123,6 +1123,8 @@ Debugger::slowPathOnNewScript(JSContext *cx, HandleScript script, GlobalObject *
JSTrapStatus
Debugger::onTrap(JSContext *cx, MutableHandleValue vp)
{
MOZ_ASSERT(cx->compartment()->debugMode());
ScriptFrameIter iter(cx);
RootedScript script(cx, iter.script());
Rooted<GlobalObject*> scriptGlobal(cx, &script->global());
@ -1949,16 +1951,20 @@ bool
Debugger::addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp)
{
THIS_DEBUGGER(cx, argc, vp, "addAllGlobalsAsDebuggees", args, dbg);
AutoDebugModeGC dmgc(cx->runtime());
for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
if (c == dbg->object->compartment() || c->options().invisibleToDebugger())
continue;
c->zone()->scheduledForDestruction = false;
GlobalObject *global = c->maybeGlobal();
if (global) {
Rooted<GlobalObject*> rg(cx, global);
if (!dbg->addDebuggeeGlobal(cx, rg, dmgc))
return false;
for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
// Invalidate a zone at a time to avoid doing a zone-wide CellIter
// per compartment.
AutoDebugModeInvalidation invalidate(zone);
for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
if (c == dbg->object->compartment() || c->options().invisibleToDebugger())
continue;
c->zone()->scheduledForDestruction = false;
GlobalObject *global = c->maybeGlobal();
if (global) {
Rooted<GlobalObject*> rg(cx, global);
if (!dbg->addDebuggeeGlobal(cx, rg, invalidate))
return false;
}
}
}
@ -1984,9 +1990,9 @@ bool
Debugger::removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp)
{
THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg);
AutoDebugModeGC dmgc(cx->runtime());
for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), e.front(), dmgc, nullptr, &e);
dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), e.front(), nullptr, &e);
args.rval().setUndefined();
return true;
}
@ -2117,14 +2123,14 @@ Debugger::construct(JSContext *cx, unsigned argc, Value *vp)
bool
Debugger::addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> global)
{
AutoDebugModeGC dmgc(cx->runtime());
return addDebuggeeGlobal(cx, global, dmgc);
AutoDebugModeInvalidation invalidate(global->compartment());
return addDebuggeeGlobal(cx, global, invalidate);
}
bool
Debugger::addDebuggeeGlobal(JSContext *cx,
Handle<GlobalObject*> global,
AutoDebugModeGC &dmgc)
AutoDebugModeInvalidation &invalidate)
{
if (debuggees.has(global))
return true;
@ -2190,7 +2196,7 @@ Debugger::addDebuggeeGlobal(JSContext *cx,
} else {
if (global->getDebuggers()->length() > 1)
return true;
if (debuggeeCompartment->addDebuggee(cx, global, dmgc))
if (debuggeeCompartment->addDebuggee(cx, global, invalidate))
return true;
/* Maintain consistency on error. */
@ -2207,13 +2213,13 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
GlobalObjectSet::Enum *compartmentEnum,
GlobalObjectSet::Enum *debugEnum)
{
AutoDebugModeGC dmgc(fop->runtime());
return removeDebuggeeGlobal(fop, global, dmgc, compartmentEnum, debugEnum);
AutoDebugModeInvalidation invalidate(global->compartment());
return removeDebuggeeGlobal(fop, global, invalidate, compartmentEnum, debugEnum);
}
void
Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
AutoDebugModeGC &dmgc,
AutoDebugModeInvalidation &invalidate,
GlobalObjectSet::Enum *compartmentEnum,
GlobalObjectSet::Enum *debugEnum)
{
@ -2269,7 +2275,7 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
* global cannot be rooted on the stack without a cx.
*/
if (v->empty())
global->compartment()->removeDebuggee(fop, global, dmgc, compartmentEnum);
global->compartment()->removeDebuggee(fop, global, invalidate, compartmentEnum);
}
/*

View File

@ -230,12 +230,12 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj);
bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj,
AutoDebugModeGC &dmgc);
AutoDebugModeInvalidation &invalidate);
void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
GlobalObjectSet::Enum *compartmentEnum,
GlobalObjectSet::Enum *debugEnum);
void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
AutoDebugModeGC &dmgc,
AutoDebugModeInvalidation &invalidate,
GlobalObjectSet::Enum *compartmentEnum,
GlobalObjectSet::Enum *debugEnum);

View File

@ -1412,52 +1412,55 @@ CASE(EnableInterruptsPseudoOpcode)
moreInterrupts = true;
}
JSInterruptHook hook = cx->runtime()->debugHooks.interruptHook;
if (hook || script->stepModeEnabled()) {
RootedValue rval(cx);
JSTrapStatus status = JSTRAP_CONTINUE;
if (hook)
status = hook(cx, script, REGS.pc, rval.address(), cx->runtime()->debugHooks.interruptHookData);
if (status == JSTRAP_CONTINUE && script->stepModeEnabled())
status = Debugger::onSingleStep(cx, &rval);
switch (status) {
case JSTRAP_ERROR:
goto error;
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
REGS.fp()->setReturnValue(rval);
interpReturnOK = true;
goto forced_return;
case JSTRAP_THROW:
cx->setPendingException(rval);
goto error;
default:;
if (cx->compartment()->debugMode()) {
JSInterruptHook hook = cx->runtime()->debugHooks.interruptHook;
if (hook || script->stepModeEnabled()) {
RootedValue rval(cx);
JSTrapStatus status = JSTRAP_CONTINUE;
if (hook)
status = hook(cx, script, REGS.pc, rval.address(),
cx->runtime()->debugHooks.interruptHookData);
if (status == JSTRAP_CONTINUE && script->stepModeEnabled())
status = Debugger::onSingleStep(cx, &rval);
switch (status) {
case JSTRAP_ERROR:
goto error;
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
REGS.fp()->setReturnValue(rval);
interpReturnOK = true;
goto forced_return;
case JSTRAP_THROW:
cx->setPendingException(rval);
goto error;
default:;
}
moreInterrupts = true;
}
moreInterrupts = true;
}
if (script->hasAnyBreakpointsOrStepMode())
moreInterrupts = true;
if (script->hasAnyBreakpointsOrStepMode())
moreInterrupts = true;
if (script->hasBreakpointsAt(REGS.pc)) {
RootedValue rval(cx);
JSTrapStatus status = Debugger::onTrap(cx, &rval);
switch (status) {
case JSTRAP_ERROR:
goto error;
case JSTRAP_RETURN:
REGS.fp()->setReturnValue(rval);
interpReturnOK = true;
goto forced_return;
case JSTRAP_THROW:
cx->setPendingException(rval);
goto error;
default:
break;
if (script->hasBreakpointsAt(REGS.pc)) {
RootedValue rval(cx);
JSTrapStatus status = Debugger::onTrap(cx, &rval);
switch (status) {
case JSTRAP_ERROR:
goto error;
case JSTRAP_RETURN:
REGS.fp()->setReturnValue(rval);
interpReturnOK = true;
goto forced_return;
case JSTRAP_THROW:
cx->setPendingException(rval);
goto error;
default:
break;
}
JS_ASSERT(status == JSTRAP_CONTINUE);
JS_ASSERT(rval.isInt32() && rval.toInt32() == op);
}
JS_ASSERT(status == JSTRAP_CONTINUE);
JS_ASSERT(rval.isInt32() && rval.toInt32() == op);
}
JS_ASSERT(activation.opMask() == EnableInterruptsPseudoOpcode);

View File

@ -170,13 +170,16 @@ js::DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
JS_FRIEND_API(bool)
JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug)
{
AutoDebugModeGC dmgc(cx->runtime());
for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
// Ignore special compartments (atoms, JSD compartments)
if (c->principals) {
if (!c->setDebugModeFromC(cx, !!debug, dmgc))
return false;
for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
// Invalidate a zone at a time to avoid doing a zone-wide CellIter
// per compartment.
AutoDebugModeInvalidation invalidate(zone);
for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
// Ignore special compartments (atoms, JSD compartments)
if (c->principals) {
if (!c->setDebugModeFromC(cx, !!debug, invalidate))
return false;
}
}
}
return true;
@ -185,8 +188,8 @@ JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug)
JS_FRIEND_API(bool)
JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, bool debug)
{
AutoDebugModeGC dmgc(cx->runtime());
return comp->setDebugModeFromC(cx, !!debug, dmgc);
AutoDebugModeInvalidation invalidate(comp);
return comp->setDebugModeFromC(cx, !!debug, invalidate);
}
static bool