зеркало из https://github.com/mozilla/gecko-dev.git
Bug 933882 - Invalidate JIT code instead of doing full GC on debug mode toggle. (r=bhackett)
This commit is contained in:
Родитель
882ca67e7a
Коммит
7d47fa4e0c
|
@ -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) \
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -2354,15 +2354,20 @@ InvalidateActivation(FreeOp *fop, uint8_t *ionTop, bool invalidateAll)
|
|||
IonSpew(IonSpew_Invalidate, "END invalidating activation");
|
||||
}
|
||||
|
||||
void
|
||||
jit::InvalidateAll(FreeOp *fop, Zone *zone)
|
||||
static inline void
|
||||
StopOffThreadCompilation(JSCompartment *comp)
|
||||
{
|
||||
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
|
||||
if (!comp->jitCompartment())
|
||||
continue;
|
||||
if (comp->jitCompartment()) {
|
||||
CancelOffThreadIonCompile(comp, nullptr);
|
||||
FinishAllOffThreadCompilations(comp->jitCompartment());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
jit::InvalidateAll(FreeOp *fop, Zone *zone)
|
||||
{
|
||||
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
|
||||
StopOffThreadCompilation(comp);
|
||||
|
||||
for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter) {
|
||||
if (iter.activation()->compartment()->zone() == zone) {
|
||||
|
@ -2521,6 +2526,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)
|
||||
{
|
||||
|
@ -2709,3 +2724,63 @@ 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(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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -725,7 +725,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;
|
||||
|
@ -754,7 +754,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);
|
||||
}
|
||||
|
@ -762,7 +762,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();
|
||||
|
||||
|
@ -772,38 +772,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);
|
||||
|
||||
|
@ -815,9 +808,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;
|
||||
}
|
||||
|
||||
|
@ -826,14 +818,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();
|
||||
|
@ -847,7 +839,7 @@ JSCompartment::removeDebuggee(FreeOp *fop,
|
|||
debugModeBits &= ~DebugFromJS;
|
||||
if (wasEnabled && !debugMode()) {
|
||||
DebugScopes::onCompartmentLeaveDebugMode(this);
|
||||
updateForDebugMode(fop, dmgc);
|
||||
updateForDebugMode(fop, invalidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -4620,13 +4620,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче