mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 933882 - Invalidate JIT code instead of doing full GC on debug mode toggle. (r=bhackett)
This commit is contained in:
parent
4aff69f533
commit
470bcaae2e
@ -25,7 +25,6 @@ namespace JS {
|
|||||||
D(TOO_MUCH_MALLOC) \
|
D(TOO_MUCH_MALLOC) \
|
||||||
D(ALLOC_TRIGGER) \
|
D(ALLOC_TRIGGER) \
|
||||||
D(DEBUG_GC) \
|
D(DEBUG_GC) \
|
||||||
D(DEBUG_MODE_GC) \
|
|
||||||
D(TRANSPLANT) \
|
D(TRANSPLANT) \
|
||||||
D(RESET) \
|
D(RESET) \
|
||||||
D(OUT_OF_NURSERY) \
|
D(OUT_OF_NURSERY) \
|
||||||
|
@ -226,13 +226,8 @@ Zone::discardJitCode(FreeOp *fop)
|
|||||||
script->resetUseCount();
|
script->resetUseCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
|
for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next())
|
||||||
/* Free optimized baseline stubs. */
|
jit::FinishDiscardJitCode(fop, comp);
|
||||||
if (comp->jitCompartment())
|
|
||||||
comp->jitCompartment()->optimizedStubSpace()->free();
|
|
||||||
|
|
||||||
comp->types.clearCompilerOutputs(fop);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -2376,15 +2376,20 @@ InvalidateActivation(FreeOp *fop, uint8_t *ionTop, bool invalidateAll)
|
|||||||
IonSpew(IonSpew_Invalidate, "END invalidating activation");
|
IonSpew(IonSpew_Invalidate, "END invalidating activation");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
StopOffThreadCompilation(JSCompartment *comp)
|
||||||
|
{
|
||||||
|
if (!comp->jitCompartment())
|
||||||
|
return;
|
||||||
|
CancelOffThreadIonCompile(comp, nullptr);
|
||||||
|
FinishAllOffThreadCompilations(comp->jitCompartment());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
jit::InvalidateAll(FreeOp *fop, Zone *zone)
|
jit::InvalidateAll(FreeOp *fop, Zone *zone)
|
||||||
{
|
{
|
||||||
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
|
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
|
||||||
if (!comp->jitCompartment())
|
StopOffThreadCompilation(comp);
|
||||||
continue;
|
|
||||||
CancelOffThreadIonCompile(comp, nullptr);
|
|
||||||
FinishAllOffThreadCompilations(comp->jitCompartment());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter) {
|
for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter) {
|
||||||
if (iter.activation()->compartment()->zone() == zone) {
|
if (iter.activation()->compartment()->zone() == zone) {
|
||||||
@ -2543,6 +2548,16 @@ jit::FinishInvalidation(FreeOp *fop, JSScript *script)
|
|||||||
FinishInvalidationOf(fop, script, script->parallelIonScript(), true);
|
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
|
void
|
||||||
jit::MarkValueFromIon(JSRuntime *rt, Value *vp)
|
jit::MarkValueFromIon(JSRuntime *rt, Value *vp)
|
||||||
{
|
{
|
||||||
@ -2731,3 +2746,62 @@ jit::TraceIonScripts(JSTracer* trc, JSScript *script)
|
|||||||
if (script->hasBaselineScript())
|
if (script->hasBaselineScript())
|
||||||
jit::BaselineScript::Trace(trc, script->baselineScript());
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -440,6 +440,7 @@ class JitCompartment
|
|||||||
// Called from JSCompartment::discardJitCode().
|
// Called from JSCompartment::discardJitCode().
|
||||||
void InvalidateAll(FreeOp *fop, JS::Zone *zone);
|
void InvalidateAll(FreeOp *fop, JS::Zone *zone);
|
||||||
void FinishInvalidation(FreeOp *fop, JSScript *script);
|
void FinishInvalidation(FreeOp *fop, JSScript *script);
|
||||||
|
void FinishDiscardJitCode(FreeOp *fop, JSCompartment *comp);
|
||||||
|
|
||||||
// On windows systems, really large frames need to be incrementally touched.
|
// On windows systems, really large frames need to be incrementally touched.
|
||||||
// The following constant defines the minimum increment of the touch.
|
// The following constant defines the minimum increment of the touch.
|
||||||
|
@ -727,7 +727,7 @@ CreateLazyScriptsForCompartment(JSContext *cx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeGC &dmgc)
|
JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidation &invalidate)
|
||||||
{
|
{
|
||||||
bool enabledBefore = debugMode();
|
bool enabledBefore = debugMode();
|
||||||
bool enabledAfter = (debugModeBits & ~unsigned(DebugFromC)) || b;
|
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);
|
debugModeBits = (debugModeBits & ~unsigned(DebugFromC)) | (b ? DebugFromC : 0);
|
||||||
JS_ASSERT(debugMode() == enabledAfter);
|
JS_ASSERT(debugMode() == enabledAfter);
|
||||||
if (enabledBefore != enabledAfter) {
|
if (enabledBefore != enabledAfter) {
|
||||||
updateForDebugMode(cx->runtime()->defaultFreeOp(), dmgc);
|
updateForDebugMode(cx->runtime()->defaultFreeOp(), invalidate);
|
||||||
if (!enabledAfter)
|
if (!enabledAfter)
|
||||||
DebugScopes::onCompartmentLeaveDebugMode(this);
|
DebugScopes::onCompartmentLeaveDebugMode(this);
|
||||||
}
|
}
|
||||||
@ -764,7 +764,7 @@ JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeGC &dmgc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSCompartment::updateForDebugMode(FreeOp *fop, AutoDebugModeGC &dmgc)
|
JSCompartment::updateForDebugMode(FreeOp *fop, AutoDebugModeInvalidation &invalidate)
|
||||||
{
|
{
|
||||||
JSRuntime *rt = runtimeFromMainThread();
|
JSRuntime *rt = runtimeFromMainThread();
|
||||||
|
|
||||||
@ -774,38 +774,31 @@ JSCompartment::updateForDebugMode(FreeOp *fop, AutoDebugModeGC &dmgc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_ION
|
#ifdef JS_ION
|
||||||
|
MOZ_ASSERT(invalidate.isFor(this));
|
||||||
JS_ASSERT_IF(debugMode(), !hasScriptsOnStack());
|
JS_ASSERT_IF(debugMode(), !hasScriptsOnStack());
|
||||||
|
|
||||||
// When we change a compartment's debug mode, whether we're turning it
|
// Invalidate all JIT code since debug mode invalidates assumptions made
|
||||||
// on or off, we must always throw away all analyses: debug mode
|
// by the JIT.
|
||||||
// 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.
|
|
||||||
//
|
//
|
||||||
// It suffices to do a garbage collection cycle or to finish the
|
// The AutoDebugModeInvalidation argument makes sure we can't forget to
|
||||||
// ongoing GC cycle. The necessary cleanup happens in
|
// invalidate, but it is also important not to run any scripts in this
|
||||||
// JSCompartment::sweep.
|
// compartment until the invalidate is destroyed. That is the caller's
|
||||||
//
|
// responsibility.
|
||||||
// dmgc makes sure we can't forget to GC, but it is also important not
|
invalidate.scheduleInvalidation(debugMode());
|
||||||
// to run any scripts in this compartment until the dmgc is destroyed.
|
|
||||||
// That is the caller's responsibility.
|
|
||||||
if (!rt->isHeapBusy())
|
|
||||||
dmgc.scheduleGC(zone());
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
JSCompartment::addDebuggee(JSContext *cx, js::GlobalObject *global)
|
JSCompartment::addDebuggee(JSContext *cx, js::GlobalObject *global)
|
||||||
{
|
{
|
||||||
AutoDebugModeGC dmgc(cx->runtime());
|
AutoDebugModeInvalidation invalidate(this);
|
||||||
return addDebuggee(cx, global, dmgc);
|
return addDebuggee(cx, global, invalidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
JSCompartment::addDebuggee(JSContext *cx,
|
JSCompartment::addDebuggee(JSContext *cx,
|
||||||
GlobalObject *globalArg,
|
GlobalObject *globalArg,
|
||||||
AutoDebugModeGC &dmgc)
|
AutoDebugModeInvalidation &invalidate)
|
||||||
{
|
{
|
||||||
Rooted<GlobalObject*> global(cx, globalArg);
|
Rooted<GlobalObject*> global(cx, globalArg);
|
||||||
|
|
||||||
@ -817,9 +810,8 @@ JSCompartment::addDebuggee(JSContext *cx,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
debugModeBits |= DebugFromJS;
|
debugModeBits |= DebugFromJS;
|
||||||
if (!wasEnabled) {
|
if (!wasEnabled)
|
||||||
updateForDebugMode(cx->runtime()->defaultFreeOp(), dmgc);
|
updateForDebugMode(cx->runtime()->defaultFreeOp(), invalidate);
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -828,14 +820,14 @@ JSCompartment::removeDebuggee(FreeOp *fop,
|
|||||||
js::GlobalObject *global,
|
js::GlobalObject *global,
|
||||||
js::GlobalObjectSet::Enum *debuggeesEnum)
|
js::GlobalObjectSet::Enum *debuggeesEnum)
|
||||||
{
|
{
|
||||||
AutoDebugModeGC dmgc(fop->runtime());
|
AutoDebugModeInvalidation invalidate(this);
|
||||||
return removeDebuggee(fop, global, dmgc, debuggeesEnum);
|
return removeDebuggee(fop, global, invalidate, debuggeesEnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
JSCompartment::removeDebuggee(FreeOp *fop,
|
JSCompartment::removeDebuggee(FreeOp *fop,
|
||||||
js::GlobalObject *global,
|
js::GlobalObject *global,
|
||||||
AutoDebugModeGC &dmgc,
|
AutoDebugModeInvalidation &invalidate,
|
||||||
js::GlobalObjectSet::Enum *debuggeesEnum)
|
js::GlobalObjectSet::Enum *debuggeesEnum)
|
||||||
{
|
{
|
||||||
bool wasEnabled = debugMode();
|
bool wasEnabled = debugMode();
|
||||||
@ -849,7 +841,7 @@ JSCompartment::removeDebuggee(FreeOp *fop,
|
|||||||
debugModeBits &= ~DebugFromJS;
|
debugModeBits &= ~DebugFromJS;
|
||||||
if (wasEnabled && !debugMode()) {
|
if (wasEnabled && !debugMode()) {
|
||||||
DebugScopes::onCompartmentLeaveDebugMode(this);
|
DebugScopes::onCompartmentLeaveDebugMode(this);
|
||||||
updateForDebugMode(fop, dmgc);
|
updateForDebugMode(fop, invalidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ struct TypeInferenceSizes;
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
class AutoDebugModeGC;
|
class AutoDebugModeInvalidation;
|
||||||
class ArrayBufferObject;
|
class ArrayBufferObject;
|
||||||
class DebugScopes;
|
class DebugScopes;
|
||||||
class WeakMapBase;
|
class WeakMapBase;
|
||||||
@ -364,19 +364,20 @@ struct JSCompartment
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/* This is called only when debugMode() has just toggled. */
|
/* 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:
|
public:
|
||||||
js::GlobalObjectSet &getDebuggees() { return debuggees; }
|
js::GlobalObjectSet &getDebuggees() { return debuggees; }
|
||||||
bool addDebuggee(JSContext *cx, js::GlobalObject *global);
|
bool addDebuggee(JSContext *cx, js::GlobalObject *global);
|
||||||
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,
|
void removeDebuggee(js::FreeOp *fop, js::GlobalObject *global,
|
||||||
js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
|
js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
|
||||||
void removeDebuggee(js::FreeOp *fop, js::GlobalObject *global,
|
void removeDebuggee(js::FreeOp *fop, js::GlobalObject *global,
|
||||||
js::AutoDebugModeGC &dmgc,
|
js::AutoDebugModeInvalidation &invalidate,
|
||||||
js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
|
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 clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JSObject *handler);
|
||||||
void clearTraps(js::FreeOp *fop);
|
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.
|
// 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
|
// Invalidate and discard JIT code since debug mode breaks JIT assumptions.
|
||||||
// object. See comment in updateForDebugMode.
|
|
||||||
//
|
//
|
||||||
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;
|
JSCompartment *comp_;
|
||||||
bool needGC;
|
JS::Zone *zone_;
|
||||||
public:
|
|
||||||
explicit AutoDebugModeGC(JSRuntime *rt) : rt(rt), needGC(false) {}
|
|
||||||
|
|
||||||
~AutoDebugModeGC() {
|
enum {
|
||||||
// Under some circumstances (say, in the midst of an animation),
|
NoNeed = 0,
|
||||||
// the garbage collector may try to retain JIT code and analyses.
|
ToggledOn = 1,
|
||||||
// The DEBUG_MODE_GC reason forces the collector to always throw
|
ToggledOff = 2
|
||||||
// everything away, as required for debug mode transitions.
|
} needInvalidation_;
|
||||||
if (needGC)
|
|
||||||
GC(rt, GC_NORMAL, JS::gcreason::DEBUG_MODE_GC);
|
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) {
|
void scheduleInvalidation(bool debugMode) {
|
||||||
JS_ASSERT(!rt->isHeapBusy());
|
// If we are scheduling invalidation for multiple compartments, they
|
||||||
PrepareZoneForGC(zone);
|
// must all agree on the toggle. This is so we can decide if we need
|
||||||
needGC = true;
|
// to invalidate on-stack scripts.
|
||||||
|
MOZ_ASSERT_IF(needInvalidation_ != NoNeed,
|
||||||
|
needInvalidation_ == debugMode ? ToggledOn : ToggledOff);
|
||||||
|
needInvalidation_ = debugMode ? ToggledOn : ToggledOff;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4661,13 +4661,8 @@ ShouldCleanUpEverything(JSRuntime *rt, JS::gcreason::Reason reason, JSGCInvocati
|
|||||||
// During shutdown, we must clean everything up, for the sake of leak
|
// 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
|
// 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.
|
// 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 ||
|
return reason == JS::gcreason::DESTROY_RUNTIME ||
|
||||||
reason == JS::gcreason::SHUTDOWN_CC ||
|
reason == JS::gcreason::SHUTDOWN_CC ||
|
||||||
reason == JS::gcreason::DEBUG_MODE_GC ||
|
|
||||||
gckind == GC_SHRINK;
|
gckind == GC_SHRINK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1123,6 +1123,8 @@ Debugger::slowPathOnNewScript(JSContext *cx, HandleScript script, GlobalObject *
|
|||||||
JSTrapStatus
|
JSTrapStatus
|
||||||
Debugger::onTrap(JSContext *cx, MutableHandleValue vp)
|
Debugger::onTrap(JSContext *cx, MutableHandleValue vp)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(cx->compartment()->debugMode());
|
||||||
|
|
||||||
ScriptFrameIter iter(cx);
|
ScriptFrameIter iter(cx);
|
||||||
RootedScript script(cx, iter.script());
|
RootedScript script(cx, iter.script());
|
||||||
Rooted<GlobalObject*> scriptGlobal(cx, &script->global());
|
Rooted<GlobalObject*> scriptGlobal(cx, &script->global());
|
||||||
@ -1949,16 +1951,20 @@ bool
|
|||||||
Debugger::addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp)
|
Debugger::addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp)
|
||||||
{
|
{
|
||||||
THIS_DEBUGGER(cx, argc, vp, "addAllGlobalsAsDebuggees", args, dbg);
|
THIS_DEBUGGER(cx, argc, vp, "addAllGlobalsAsDebuggees", args, dbg);
|
||||||
AutoDebugModeGC dmgc(cx->runtime());
|
for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
|
||||||
for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
|
// Invalidate a zone at a time to avoid doing a zone-wide CellIter
|
||||||
if (c == dbg->object->compartment() || c->options().invisibleToDebugger())
|
// per compartment.
|
||||||
continue;
|
AutoDebugModeInvalidation invalidate(zone);
|
||||||
c->zone()->scheduledForDestruction = false;
|
for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
|
||||||
GlobalObject *global = c->maybeGlobal();
|
if (c == dbg->object->compartment() || c->options().invisibleToDebugger())
|
||||||
if (global) {
|
continue;
|
||||||
Rooted<GlobalObject*> rg(cx, global);
|
c->zone()->scheduledForDestruction = false;
|
||||||
if (!dbg->addDebuggeeGlobal(cx, rg, dmgc))
|
GlobalObject *global = c->maybeGlobal();
|
||||||
return false;
|
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)
|
Debugger::removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp)
|
||||||
{
|
{
|
||||||
THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg);
|
THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg);
|
||||||
AutoDebugModeGC dmgc(cx->runtime());
|
|
||||||
for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
|
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();
|
args.rval().setUndefined();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2117,14 +2123,14 @@ Debugger::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||||||
bool
|
bool
|
||||||
Debugger::addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> global)
|
Debugger::addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> global)
|
||||||
{
|
{
|
||||||
AutoDebugModeGC dmgc(cx->runtime());
|
AutoDebugModeInvalidation invalidate(global->compartment());
|
||||||
return addDebuggeeGlobal(cx, global, dmgc);
|
return addDebuggeeGlobal(cx, global, invalidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Debugger::addDebuggeeGlobal(JSContext *cx,
|
Debugger::addDebuggeeGlobal(JSContext *cx,
|
||||||
Handle<GlobalObject*> global,
|
Handle<GlobalObject*> global,
|
||||||
AutoDebugModeGC &dmgc)
|
AutoDebugModeInvalidation &invalidate)
|
||||||
{
|
{
|
||||||
if (debuggees.has(global))
|
if (debuggees.has(global))
|
||||||
return true;
|
return true;
|
||||||
@ -2190,7 +2196,7 @@ Debugger::addDebuggeeGlobal(JSContext *cx,
|
|||||||
} else {
|
} else {
|
||||||
if (global->getDebuggers()->length() > 1)
|
if (global->getDebuggers()->length() > 1)
|
||||||
return true;
|
return true;
|
||||||
if (debuggeeCompartment->addDebuggee(cx, global, dmgc))
|
if (debuggeeCompartment->addDebuggee(cx, global, invalidate))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/* Maintain consistency on error. */
|
/* Maintain consistency on error. */
|
||||||
@ -2207,13 +2213,13 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
|||||||
GlobalObjectSet::Enum *compartmentEnum,
|
GlobalObjectSet::Enum *compartmentEnum,
|
||||||
GlobalObjectSet::Enum *debugEnum)
|
GlobalObjectSet::Enum *debugEnum)
|
||||||
{
|
{
|
||||||
AutoDebugModeGC dmgc(fop->runtime());
|
AutoDebugModeInvalidation invalidate(global->compartment());
|
||||||
return removeDebuggeeGlobal(fop, global, dmgc, compartmentEnum, debugEnum);
|
return removeDebuggeeGlobal(fop, global, invalidate, compartmentEnum, debugEnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
||||||
AutoDebugModeGC &dmgc,
|
AutoDebugModeInvalidation &invalidate,
|
||||||
GlobalObjectSet::Enum *compartmentEnum,
|
GlobalObjectSet::Enum *compartmentEnum,
|
||||||
GlobalObjectSet::Enum *debugEnum)
|
GlobalObjectSet::Enum *debugEnum)
|
||||||
{
|
{
|
||||||
@ -2269,7 +2275,7 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
|||||||
* global cannot be rooted on the stack without a cx.
|
* global cannot be rooted on the stack without a cx.
|
||||||
*/
|
*/
|
||||||
if (v->empty())
|
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);
|
||||||
bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj,
|
bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj,
|
||||||
AutoDebugModeGC &dmgc);
|
AutoDebugModeInvalidation &invalidate);
|
||||||
void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
||||||
GlobalObjectSet::Enum *compartmentEnum,
|
GlobalObjectSet::Enum *compartmentEnum,
|
||||||
GlobalObjectSet::Enum *debugEnum);
|
GlobalObjectSet::Enum *debugEnum);
|
||||||
void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
||||||
AutoDebugModeGC &dmgc,
|
AutoDebugModeInvalidation &invalidate,
|
||||||
GlobalObjectSet::Enum *compartmentEnum,
|
GlobalObjectSet::Enum *compartmentEnum,
|
||||||
GlobalObjectSet::Enum *debugEnum);
|
GlobalObjectSet::Enum *debugEnum);
|
||||||
|
|
||||||
|
@ -1412,52 +1412,55 @@ CASE(EnableInterruptsPseudoOpcode)
|
|||||||
moreInterrupts = true;
|
moreInterrupts = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSInterruptHook hook = cx->runtime()->debugHooks.interruptHook;
|
if (cx->compartment()->debugMode()) {
|
||||||
if (hook || script->stepModeEnabled()) {
|
JSInterruptHook hook = cx->runtime()->debugHooks.interruptHook;
|
||||||
RootedValue rval(cx);
|
if (hook || script->stepModeEnabled()) {
|
||||||
JSTrapStatus status = JSTRAP_CONTINUE;
|
RootedValue rval(cx);
|
||||||
if (hook)
|
JSTrapStatus status = JSTRAP_CONTINUE;
|
||||||
status = hook(cx, script, REGS.pc, rval.address(), cx->runtime()->debugHooks.interruptHookData);
|
if (hook)
|
||||||
if (status == JSTRAP_CONTINUE && script->stepModeEnabled())
|
status = hook(cx, script, REGS.pc, rval.address(),
|
||||||
status = Debugger::onSingleStep(cx, &rval);
|
cx->runtime()->debugHooks.interruptHookData);
|
||||||
switch (status) {
|
if (status == JSTRAP_CONTINUE && script->stepModeEnabled())
|
||||||
case JSTRAP_ERROR:
|
status = Debugger::onSingleStep(cx, &rval);
|
||||||
goto error;
|
switch (status) {
|
||||||
case JSTRAP_CONTINUE:
|
case JSTRAP_ERROR:
|
||||||
break;
|
goto error;
|
||||||
case JSTRAP_RETURN:
|
case JSTRAP_CONTINUE:
|
||||||
REGS.fp()->setReturnValue(rval);
|
break;
|
||||||
interpReturnOK = true;
|
case JSTRAP_RETURN:
|
||||||
goto forced_return;
|
REGS.fp()->setReturnValue(rval);
|
||||||
case JSTRAP_THROW:
|
interpReturnOK = true;
|
||||||
cx->setPendingException(rval);
|
goto forced_return;
|
||||||
goto error;
|
case JSTRAP_THROW:
|
||||||
default:;
|
cx->setPendingException(rval);
|
||||||
|
goto error;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
moreInterrupts = true;
|
||||||
}
|
}
|
||||||
moreInterrupts = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (script->hasAnyBreakpointsOrStepMode())
|
if (script->hasAnyBreakpointsOrStepMode())
|
||||||
moreInterrupts = true;
|
moreInterrupts = true;
|
||||||
|
|
||||||
if (script->hasBreakpointsAt(REGS.pc)) {
|
if (script->hasBreakpointsAt(REGS.pc)) {
|
||||||
RootedValue rval(cx);
|
RootedValue rval(cx);
|
||||||
JSTrapStatus status = Debugger::onTrap(cx, &rval);
|
JSTrapStatus status = Debugger::onTrap(cx, &rval);
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case JSTRAP_ERROR:
|
case JSTRAP_ERROR:
|
||||||
goto error;
|
goto error;
|
||||||
case JSTRAP_RETURN:
|
case JSTRAP_RETURN:
|
||||||
REGS.fp()->setReturnValue(rval);
|
REGS.fp()->setReturnValue(rval);
|
||||||
interpReturnOK = true;
|
interpReturnOK = true;
|
||||||
goto forced_return;
|
goto forced_return;
|
||||||
case JSTRAP_THROW:
|
case JSTRAP_THROW:
|
||||||
cx->setPendingException(rval);
|
cx->setPendingException(rval);
|
||||||
goto error;
|
goto error;
|
||||||
default:
|
default:
|
||||||
break;
|
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);
|
JS_ASSERT(activation.opMask() == EnableInterruptsPseudoOpcode);
|
||||||
|
@ -170,13 +170,16 @@ js::DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
|
|||||||
JS_FRIEND_API(bool)
|
JS_FRIEND_API(bool)
|
||||||
JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug)
|
JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug)
|
||||||
{
|
{
|
||||||
AutoDebugModeGC dmgc(cx->runtime());
|
for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
|
||||||
|
// Invalidate a zone at a time to avoid doing a zone-wide CellIter
|
||||||
for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
|
// per compartment.
|
||||||
// Ignore special compartments (atoms, JSD compartments)
|
AutoDebugModeInvalidation invalidate(zone);
|
||||||
if (c->principals) {
|
for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
|
||||||
if (!c->setDebugModeFromC(cx, !!debug, dmgc))
|
// Ignore special compartments (atoms, JSD compartments)
|
||||||
return false;
|
if (c->principals) {
|
||||||
|
if (!c->setDebugModeFromC(cx, !!debug, invalidate))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -185,8 +188,8 @@ JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug)
|
|||||||
JS_FRIEND_API(bool)
|
JS_FRIEND_API(bool)
|
||||||
JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, bool debug)
|
JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, bool debug)
|
||||||
{
|
{
|
||||||
AutoDebugModeGC dmgc(cx->runtime());
|
AutoDebugModeInvalidation invalidate(comp);
|
||||||
return comp->setDebugModeFromC(cx, !!debug, dmgc);
|
return comp->setDebugModeFromC(cx, !!debug, invalidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
Loading…
Reference in New Issue
Block a user