mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 716647 - Part 5: Relax the no on-stack scripts restriction for addDebuggee. (r=jimb)
This commit is contained in:
parent
6e13279fe5
commit
e31d8af3d3
@ -12,7 +12,9 @@
|
||||
#include "jit/IonSpewer.h"
|
||||
#include "jit/Recover.h"
|
||||
#include "jit/RematerializedFrame.h"
|
||||
|
||||
#include "vm/ArgumentsObject.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
||||
#include "jsscriptinlines.h"
|
||||
@ -1536,7 +1538,7 @@ HandleBaselineInfoBailout(JSContext *cx, JSScript *outerScript, JSScript *innerS
|
||||
return Invalidate(cx, outerScript);
|
||||
}
|
||||
|
||||
static void
|
||||
static bool
|
||||
CopyFromRematerializedFrame(JSContext *cx, JitActivation *act, uint8_t *fp, size_t inlineDepth,
|
||||
BaselineFrame *frame)
|
||||
{
|
||||
@ -1545,7 +1547,7 @@ CopyFromRematerializedFrame(JSContext *cx, JitActivation *act, uint8_t *fp, size
|
||||
// We might not have rematerialized a frame if the user never requested a
|
||||
// Debugger.Frame for it.
|
||||
if (!rematFrame)
|
||||
return;
|
||||
return true;
|
||||
|
||||
MOZ_ASSERT(rematFrame->script() == frame->script());
|
||||
MOZ_ASSERT(rematFrame->numActualArgs() == frame->numActualArgs());
|
||||
@ -1562,6 +1564,11 @@ CopyFromRematerializedFrame(JSContext *cx, JitActivation *act, uint8_t *fp, size
|
||||
IonSpew(IonSpew_BaselineBailouts,
|
||||
" Copied from rematerialized frame at (%p,%u)",
|
||||
fp, inlineDepth);
|
||||
|
||||
if (cx->compartment()->debugMode())
|
||||
return Debugger::handleIonBailout(cx, rematFrame, frame);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
@ -1661,9 +1668,11 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
|
||||
JitFrameIterator iter(cx);
|
||||
size_t inlineDepth = numFrames;
|
||||
while (inlineDepth > 0) {
|
||||
if (iter.isBaselineJS()) {
|
||||
inlineDepth--;
|
||||
CopyFromRematerializedFrame(cx, act, outerFp, inlineDepth, iter.baselineFrame());
|
||||
if (iter.isBaselineJS() &&
|
||||
!CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth,
|
||||
iter.baselineFrame()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
@ -3036,6 +3036,28 @@ jit::RematerializeAllFrames(JSContext *cx, JSCompartment *comp)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jit::UpdateForDebugMode(JSContext *maybecx, JSCompartment *comp,
|
||||
AutoDebugModeInvalidation &invalidate)
|
||||
{
|
||||
MOZ_ASSERT(invalidate.isFor(comp));
|
||||
|
||||
// Schedule invalidation of all optimized JIT code since debug mode
|
||||
// invalidates assumptions.
|
||||
invalidate.scheduleInvalidation(comp->debugMode());
|
||||
|
||||
// Recompile on-stack baseline scripts if we have a cx.
|
||||
if (maybecx) {
|
||||
IonContext ictx(maybecx, nullptr);
|
||||
if (!RecompileOnStackBaselineScriptsForDebugMode(maybecx, comp)) {
|
||||
js_ReportOutOfMemory(maybecx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoDebugModeInvalidation::~AutoDebugModeInvalidation()
|
||||
{
|
||||
MOZ_ASSERT(!!comp_ != !!zone_);
|
||||
@ -3054,11 +3076,13 @@ AutoDebugModeInvalidation::~AutoDebugModeInvalidation()
|
||||
StopAllOffThreadCompilations(comp);
|
||||
}
|
||||
|
||||
// Don't discard active baseline scripts. They are recompiled for debug
|
||||
// mode.
|
||||
jit::MarkActiveBaselineScripts(zone);
|
||||
|
||||
for (JitActivationIterator iter(rt); !iter.done(); ++iter) {
|
||||
JSCompartment *comp = iter.activation()->compartment();
|
||||
if ((comp_ && comp_ == comp) || (zone_ && zone_ == comp->zone())) {
|
||||
if (comp_ == comp || zone_ == comp->zone()) {
|
||||
IonContext ictx(CompileRuntime::get(rt));
|
||||
AutoFlushCache afc("AutoDebugModeInvalidation", rt->jitRuntime());
|
||||
IonSpew(IonSpew_Invalidate, "Invalidating frames for debug mode toggle");
|
||||
@ -3068,7 +3092,7 @@ AutoDebugModeInvalidation::~AutoDebugModeInvalidation()
|
||||
|
||||
for (gc::CellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
JSScript *script = i.get<JSScript>();
|
||||
if ((comp_ && script->compartment() == comp_) || zone_) {
|
||||
if (script->compartment() == comp_ || zone_) {
|
||||
FinishInvalidation<SequentialExecution>(fop, script);
|
||||
FinishInvalidation<ParallelExecution>(fop, script);
|
||||
FinishDiscardBaselineScript(fop, script);
|
||||
|
@ -189,6 +189,8 @@ void TraceIonScripts(JSTracer* trc, JSScript *script);
|
||||
void RequestInterruptForIonCode(JSRuntime *rt, JSRuntime::InterruptMode mode);
|
||||
|
||||
bool RematerializeAllFrames(JSContext *cx, JSCompartment *comp);
|
||||
bool UpdateForDebugMode(JSContext *maybecx, JSCompartment *comp,
|
||||
AutoDebugModeInvalidation &invalidate);
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -766,16 +766,13 @@ JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidatio
|
||||
bool enabledBefore = debugMode();
|
||||
bool enabledAfter = (debugModeBits & DebugModeFromMask & ~DebugFromC) || b;
|
||||
|
||||
// Debug mode can be enabled only when no scripts from the target
|
||||
// compartment are on the stack. It would even be incorrect to discard just
|
||||
// the non-live scripts' JITScripts because they might share ICs with live
|
||||
// scripts (bug 632343).
|
||||
// Enabling debug mode from C (vs of from JS) can only be done when no
|
||||
// scripts from the target compartment are on the stack.
|
||||
//
|
||||
// We do allow disabling debug mode while scripts are on the stack. In
|
||||
// that case the debug-mode code for those scripts remains, so subsequently
|
||||
// hooks may be called erroneously, even though debug mode is supposedly
|
||||
// off, and we have to live with it.
|
||||
//
|
||||
bool onStack = false;
|
||||
if (enabledBefore != enabledAfter) {
|
||||
onStack = hasScriptsOnStack();
|
||||
@ -788,36 +785,28 @@ JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidatio
|
||||
debugModeBits = (debugModeBits & ~DebugFromC) | (b ? DebugFromC : 0);
|
||||
JS_ASSERT(debugMode() == enabledAfter);
|
||||
if (enabledBefore != enabledAfter) {
|
||||
updateForDebugMode(cx->runtime()->defaultFreeOp(), invalidate);
|
||||
// Pass in a nullptr cx to not bother recompiling for JSD1, since
|
||||
// we're still enforcing the idle-stack invariant here.
|
||||
if (!updateJITForDebugMode(nullptr, invalidate))
|
||||
return false;
|
||||
if (!enabledAfter)
|
||||
DebugScopes::onCompartmentLeaveDebugMode(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::updateForDebugMode(FreeOp *fop, AutoDebugModeInvalidation &invalidate)
|
||||
bool
|
||||
JSCompartment::updateJITForDebugMode(JSContext *maybecx, AutoDebugModeInvalidation &invalidate)
|
||||
{
|
||||
JSRuntime *rt = runtimeFromMainThread();
|
||||
|
||||
for (ContextIter acx(rt); !acx.done(); acx.next()) {
|
||||
if (acx->compartment() == this)
|
||||
acx->updateJITEnabled();
|
||||
}
|
||||
|
||||
#ifdef JS_ION
|
||||
MOZ_ASSERT(invalidate.isFor(this));
|
||||
JS_ASSERT_IF(debugMode(), !hasScriptsOnStack());
|
||||
|
||||
// Invalidate all JIT code since debug mode invalidates assumptions made
|
||||
// by the JIT.
|
||||
//
|
||||
// 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());
|
||||
if (!jit::UpdateForDebugMode(maybecx, this, invalidate))
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -840,25 +829,47 @@ JSCompartment::addDebuggee(JSContext *cx,
|
||||
return false;
|
||||
}
|
||||
debugModeBits |= DebugFromJS;
|
||||
if (!wasEnabled)
|
||||
updateForDebugMode(cx->runtime()->defaultFreeOp(), invalidate);
|
||||
if (!wasEnabled && !updateJITForDebugMode(cx, invalidate))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::removeDebuggee(FreeOp *fop,
|
||||
bool
|
||||
JSCompartment::removeDebuggee(JSContext *cx,
|
||||
js::GlobalObject *global,
|
||||
js::GlobalObjectSet::Enum *debuggeesEnum)
|
||||
{
|
||||
AutoDebugModeInvalidation invalidate(this);
|
||||
return removeDebuggee(fop, global, invalidate, debuggeesEnum);
|
||||
return removeDebuggee(cx, global, invalidate, debuggeesEnum);
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::removeDebuggee(FreeOp *fop,
|
||||
bool
|
||||
JSCompartment::removeDebuggee(JSContext *cx,
|
||||
js::GlobalObject *global,
|
||||
AutoDebugModeInvalidation &invalidate,
|
||||
js::GlobalObjectSet::Enum *debuggeesEnum)
|
||||
{
|
||||
bool wasEnabled = debugMode();
|
||||
removeDebuggeeUnderGC(cx->runtime()->defaultFreeOp(), global, invalidate, debuggeesEnum);
|
||||
if (wasEnabled && !debugMode() && !updateJITForDebugMode(cx, invalidate))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::removeDebuggeeUnderGC(FreeOp *fop,
|
||||
js::GlobalObject *global,
|
||||
js::GlobalObjectSet::Enum *debuggeesEnum)
|
||||
{
|
||||
AutoDebugModeInvalidation invalidate(this);
|
||||
removeDebuggeeUnderGC(fop, global, invalidate, debuggeesEnum);
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::removeDebuggeeUnderGC(FreeOp *fop,
|
||||
js::GlobalObject *global,
|
||||
AutoDebugModeInvalidation &invalidate,
|
||||
js::GlobalObjectSet::Enum *debuggeesEnum)
|
||||
{
|
||||
bool wasEnabled = debugMode();
|
||||
JS_ASSERT(debuggees.has(global));
|
||||
@ -869,10 +880,8 @@ JSCompartment::removeDebuggee(FreeOp *fop,
|
||||
|
||||
if (debuggees.empty()) {
|
||||
debugModeBits &= ~DebugFromJS;
|
||||
if (wasEnabled && !debugMode()) {
|
||||
if (wasEnabled && !debugMode())
|
||||
DebugScopes::onCompartmentLeaveDebugMode(this);
|
||||
updateForDebugMode(fop, invalidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,18 +395,23 @@ struct JSCompartment
|
||||
private:
|
||||
|
||||
/* This is called only when debugMode() has just toggled. */
|
||||
void updateForDebugMode(js::FreeOp *fop, js::AutoDebugModeInvalidation &invalidate);
|
||||
bool updateJITForDebugMode(JSContext *maybecx, js::AutoDebugModeInvalidation &invalidate);
|
||||
|
||||
public:
|
||||
js::GlobalObjectSet &getDebuggees() { return debuggees; }
|
||||
bool addDebuggee(JSContext *cx, js::GlobalObject *global);
|
||||
bool addDebuggee(JSContext *cx, js::GlobalObject *global,
|
||||
js::AutoDebugModeInvalidation &invalidate);
|
||||
void removeDebuggee(js::FreeOp *fop, js::GlobalObject *global,
|
||||
bool removeDebuggee(JSContext *cx, js::GlobalObject *global,
|
||||
js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
|
||||
void removeDebuggee(js::FreeOp *fop, js::GlobalObject *global,
|
||||
bool removeDebuggee(JSContext *cx, js::GlobalObject *global,
|
||||
js::AutoDebugModeInvalidation &invalidate,
|
||||
js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
|
||||
void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global,
|
||||
js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
|
||||
void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global,
|
||||
js::AutoDebugModeInvalidation &invalidate,
|
||||
js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
|
||||
bool setDebugModeFromC(JSContext *cx, bool b,
|
||||
js::AutoDebugModeInvalidation &invalidate);
|
||||
|
||||
|
@ -1655,8 +1655,12 @@ Debugger::sweepAll(FreeOp *fop)
|
||||
* might be GC'd too. Since detaching requires access to both
|
||||
* objects, this must be done before finalize time.
|
||||
*/
|
||||
for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
|
||||
dbg->removeDebuggeeGlobal(fop, e.front(), nullptr, &e);
|
||||
for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
|
||||
// We can't recompile on-stack scripts here, and we
|
||||
// can only toggle debug mode to off, so we use an
|
||||
// infallible variant of removeDebuggeeGlobal.
|
||||
dbg->removeDebuggeeGlobalUnderGC(fop, e.front(), nullptr, &e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1665,8 +1669,10 @@ Debugger::sweepAll(FreeOp *fop)
|
||||
GlobalObjectSet &debuggees = comp->getDebuggees();
|
||||
for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
|
||||
GlobalObject *global = e.front();
|
||||
if (IsObjectAboutToBeFinalized(&global))
|
||||
if (IsObjectAboutToBeFinalized(&global)) {
|
||||
// See infallibility note above.
|
||||
detachAllDebuggersFromGlobal(fop, global, &e);
|
||||
}
|
||||
else if (global != e.front())
|
||||
e.rekeyFront(global);
|
||||
}
|
||||
@ -1680,7 +1686,7 @@ Debugger::detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global,
|
||||
const GlobalObject::DebuggerVector *debuggers = global->getDebuggers();
|
||||
JS_ASSERT(!debuggers->empty());
|
||||
while (!debuggers->empty())
|
||||
debuggers->back()->removeDebuggeeGlobal(fop, global, compartmentEnum, nullptr);
|
||||
debuggers->back()->removeDebuggeeGlobalUnderGC(fop, global, compartmentEnum, nullptr);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
@ -2018,6 +2024,7 @@ Debugger::addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp)
|
||||
// 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;
|
||||
@ -2043,8 +2050,10 @@ Debugger::removeDebuggee(JSContext *cx, unsigned argc, Value *vp)
|
||||
GlobalObject *global = dbg->unwrapDebuggeeArgument(cx, args[0]);
|
||||
if (!global)
|
||||
return false;
|
||||
if (dbg->debuggees.has(global))
|
||||
dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, nullptr, nullptr);
|
||||
if (dbg->debuggees.has(global)) {
|
||||
if (!dbg->removeDebuggeeGlobal(cx, global, nullptr, nullptr))
|
||||
return false;
|
||||
}
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
@ -2053,8 +2062,11 @@ bool
|
||||
Debugger::removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg);
|
||||
for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
|
||||
dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), e.front(), nullptr, &e);
|
||||
|
||||
for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
|
||||
if (!dbg->removeDebuggeeGlobal(cx, e.front(), nullptr, &e))
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
@ -2098,15 +2110,14 @@ Debugger::getNewestFrame(JSContext *cx, unsigned argc, Value *vp)
|
||||
|
||||
/* Since there may be multiple contexts, use AllFramesIter. */
|
||||
for (AllFramesIter i(cx); !i.done(); ++i) {
|
||||
/*
|
||||
* Debug-mode currently disables Ion compilation in the compartment of
|
||||
* the debuggee.
|
||||
*/
|
||||
if (i.isIon())
|
||||
continue;
|
||||
if (dbg->observesFrame(i.abstractFramePtr())) {
|
||||
if (dbg->observesFrame(i)) {
|
||||
// Ensure that Ion frames are rematerialized. Only rematerialized
|
||||
// Ion frames may be used as AbstractFramePtrs.
|
||||
if (i.isIon() && !i.ensureHasRematerializedFrame())
|
||||
return false;
|
||||
AbstractFramePtr frame = i.abstractFramePtr();
|
||||
ScriptFrameIter iter(i.activation()->cx(), ScriptFrameIter::GO_THROUGH_SAVED);
|
||||
while (iter.isIon() || iter.abstractFramePtr() != i.abstractFramePtr())
|
||||
while (!iter.hasUsableAbstractFramePtr() || iter.abstractFramePtr() != frame)
|
||||
++iter;
|
||||
return dbg->getScriptFrame(cx, iter, args.rval());
|
||||
}
|
||||
@ -2248,12 +2259,6 @@ Debugger::addDebuggeeGlobal(JSContext *cx,
|
||||
}
|
||||
}
|
||||
|
||||
/* Refuse to enable debug mode for a compartment that has running scripts. */
|
||||
if (!debuggeeCompartment->debugMode() && debuggeeCompartment->hasScriptsOnStack()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_IDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Each debugger-debuggee relation must be stored in up to three places.
|
||||
* JSCompartment::addDebuggee enables debug mode if needed.
|
||||
@ -2281,19 +2286,10 @@ Debugger::addDebuggeeGlobal(JSContext *cx,
|
||||
}
|
||||
|
||||
void
|
||||
Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum)
|
||||
{
|
||||
AutoDebugModeInvalidation invalidate(global->compartment());
|
||||
removeDebuggeeGlobal(fop, global, invalidate, compartmentEnum, debugEnum);
|
||||
}
|
||||
|
||||
void
|
||||
Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
||||
AutoDebugModeInvalidation &invalidate,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum)
|
||||
Debugger::cleanupDebuggeeGlobalBeforeRemoval(FreeOp *fop, GlobalObject *global,
|
||||
AutoDebugModeInvalidation &invalidate,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum)
|
||||
{
|
||||
/*
|
||||
* Each debuggee is in two HashSets: one for its compartment and one for
|
||||
@ -2351,14 +2347,57 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
||||
bp->destroy(fop);
|
||||
}
|
||||
JS_ASSERT_IF(debuggees.empty(), !firstBreakpoint());
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum)
|
||||
{
|
||||
AutoDebugModeInvalidation invalidate(global->compartment());
|
||||
return removeDebuggeeGlobal(cx, global, invalidate, compartmentEnum, debugEnum);
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
|
||||
AutoDebugModeInvalidation &invalidate,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum)
|
||||
{
|
||||
cleanupDebuggeeGlobalBeforeRemoval(cx->runtime()->defaultFreeOp(), global,
|
||||
invalidate, compartmentEnum, debugEnum);
|
||||
|
||||
// The debuggee needs to be removed from the compartment last to save a root.
|
||||
if (global->getDebuggers()->empty())
|
||||
return global->compartment()->removeDebuggee(cx, global, invalidate, compartmentEnum);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Debugger::removeDebuggeeGlobalUnderGC(FreeOp *fop, GlobalObject *global,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum)
|
||||
{
|
||||
AutoDebugModeInvalidation invalidate(global->compartment());
|
||||
removeDebuggeeGlobalUnderGC(fop, global, invalidate, compartmentEnum, debugEnum);
|
||||
}
|
||||
|
||||
void
|
||||
Debugger::removeDebuggeeGlobalUnderGC(FreeOp *fop, GlobalObject *global,
|
||||
AutoDebugModeInvalidation &invalidate,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum)
|
||||
{
|
||||
cleanupDebuggeeGlobalBeforeRemoval(fop, global, invalidate, compartmentEnum, debugEnum);
|
||||
|
||||
/*
|
||||
* The debuggee needs to be removed from the compartment last, as this can
|
||||
* trigger GCs if the compartment's debug mode is being changed, and the
|
||||
* global cannot be rooted on the stack without a cx.
|
||||
*/
|
||||
if (v->empty())
|
||||
global->compartment()->removeDebuggee(fop, global, invalidate, compartmentEnum);
|
||||
if (global->getDebuggers()->empty())
|
||||
global->compartment()->removeDebuggeeUnderGC(fop, global, invalidate, compartmentEnum);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3560,6 +3599,12 @@ Debugger::observesFrame(AbstractFramePtr frame) const
|
||||
return observesScript(frame.script());
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::observesFrame(const ScriptFrameIter &iter) const
|
||||
{
|
||||
return observesScript(iter.script());
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::observesScript(JSScript *script) const
|
||||
{
|
||||
@ -3570,12 +3615,10 @@ Debugger::observesScript(JSScript *script) const
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::handleBaselineOsr(JSContext *cx, InterpreterFrame *from, jit::BaselineFrame *to)
|
||||
Debugger::replaceFrameGuts(JSContext *cx, AbstractFramePtr from, AbstractFramePtr to,
|
||||
ScriptFrameIter &iter)
|
||||
{
|
||||
ScriptFrameIter iter(cx);
|
||||
JS_ASSERT(iter.abstractFramePtr() == to);
|
||||
|
||||
for (FrameRange r(from); !r.empty(); r.popFront()) {
|
||||
for (Debugger::FrameRange r(from); !r.empty(); r.popFront()) {
|
||||
RootedObject frameobj(cx, r.frontFrame());
|
||||
Debugger *dbg = r.frontDebugger();
|
||||
JS_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
|
||||
@ -3600,6 +3643,31 @@ Debugger::handleBaselineOsr(JSContext *cx, InterpreterFrame *from, jit::Baseline
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::handleBaselineOsr(JSContext *cx, InterpreterFrame *from, jit::BaselineFrame *to)
|
||||
{
|
||||
ScriptFrameIter iter(cx);
|
||||
JS_ASSERT(iter.abstractFramePtr() == to);
|
||||
return replaceFrameGuts(cx, from, to, iter);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::handleIonBailout(JSContext *cx, jit::RematerializedFrame *from, jit::BaselineFrame *to)
|
||||
{
|
||||
// When we return to a bailed-out Ion real frame, we must update all
|
||||
// Debugger.Frames that refer to its inline frames. However, since we
|
||||
// can't pop individual inline frames off the stack (we can only pop the
|
||||
// real frame that contains them all, as a unit), we cannot assume that
|
||||
// the frame we're dealing with is the top frame. Advance the iterator
|
||||
// across any inlined frames younger than |to|, the baseline frame
|
||||
// reconstructed during bailout from the Ion frame corresponding to
|
||||
// |from|.
|
||||
ScriptFrameIter iter(cx);
|
||||
while (iter.abstractFramePtr() != to)
|
||||
++iter;
|
||||
return replaceFrameGuts(cx, from, to, iter);
|
||||
}
|
||||
|
||||
static bool
|
||||
DebuggerScript_setBreakpoint(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
@ -4033,6 +4101,43 @@ static const JSFunctionSpec DebuggerSource_methods[] = {
|
||||
|
||||
/*** Debugger.Frame ******************************************************************************/
|
||||
|
||||
static void
|
||||
UpdateFrameIterPc(FrameIter &iter)
|
||||
{
|
||||
if (iter.abstractFramePtr().isRematerializedFrame()) {
|
||||
#ifdef DEBUG
|
||||
// Rematerialized frames don't need their pc updated. The reason we
|
||||
// need to update pc is because we might get the same Debugger.Frame
|
||||
// object for multiple re-entries into debugger code from debuggee
|
||||
// code. This reentrancy is not possible with rematerialized frames,
|
||||
// because when returning to debuggee code, we would have bailed out
|
||||
// to baseline.
|
||||
//
|
||||
// We walk the stack to assert that it doesn't need updating.
|
||||
jit::RematerializedFrame *frame = iter.abstractFramePtr().asRematerializedFrame();
|
||||
jit::IonJSFrameLayout *jsFrame = (jit::IonJSFrameLayout *)frame->top();
|
||||
jit::JitActivation *activation = iter.activation()->asJit();
|
||||
|
||||
ActivationIterator activationIter(activation->cx()->runtime());
|
||||
while (activationIter.activation() != activation)
|
||||
++activationIter;
|
||||
|
||||
jit::JitFrameIterator jitIter(activationIter);
|
||||
while (!jitIter.isIonJS() || jitIter.jsFrame() != jsFrame)
|
||||
++jitIter;
|
||||
|
||||
jit::InlineFrameIterator ionInlineIter(activation->cx(), &jitIter);
|
||||
while (ionInlineIter.frameNo() != frame->frameNo())
|
||||
++ionInlineIter;
|
||||
|
||||
MOZ_ASSERT(ionInlineIter.pc() == iter.pc());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
iter.updatePcQuadratic();
|
||||
}
|
||||
|
||||
static void
|
||||
DebuggerFrame_freeScriptFrameIterData(FreeOp *fop, JSObject *obj)
|
||||
{
|
||||
@ -4175,6 +4280,27 @@ DebuggerFrame_getType(JSContext *cx, unsigned argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DebuggerFrame_getImplementation(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_FRAME(cx, argc, vp, "get implementation", args, thisobj, frame);
|
||||
|
||||
const char *s;
|
||||
if (frame.isBaselineFrame())
|
||||
s = "baseline";
|
||||
else if (frame.isRematerializedFrame())
|
||||
s = "ion";
|
||||
else
|
||||
s = "interpreter";
|
||||
|
||||
JSAtom *str = Atomize(cx, s, strlen(s));
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DebuggerFrame_getEnvironment(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
@ -4183,7 +4309,7 @@ DebuggerFrame_getEnvironment(JSContext *cx, unsigned argc, Value *vp)
|
||||
Rooted<Env*> env(cx);
|
||||
{
|
||||
AutoCompartment ac(cx, iter.abstractFramePtr().scopeChain());
|
||||
iter.updatePcQuadratic();
|
||||
UpdateFrameIterPc(iter);
|
||||
env = GetDebugScopeForFrame(cx, iter.abstractFramePtr(), iter.pc());
|
||||
if (!env)
|
||||
return false;
|
||||
@ -4243,10 +4369,11 @@ DebuggerFrame_getOlder(JSContext *cx, unsigned argc, Value *vp)
|
||||
Debugger *dbg = Debugger::fromChildJSObject(thisobj);
|
||||
|
||||
for (++iter; !iter.done(); ++iter) {
|
||||
if (iter.isIon())
|
||||
continue;
|
||||
if (dbg->observesFrame(iter.abstractFramePtr()))
|
||||
if (dbg->observesFrame(iter)) {
|
||||
if (iter.isIon() && !iter.ensureHasRematerializedFrame())
|
||||
return false;
|
||||
return dbg->getScriptFrame(cx, iter, args.rval());
|
||||
}
|
||||
}
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
@ -4408,7 +4535,7 @@ DebuggerFrame_getOffset(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_FRAME_ITER(cx, argc, vp, "get offset", args, thisobj, _, iter);
|
||||
JSScript *script = iter.script();
|
||||
iter.updatePcQuadratic();
|
||||
UpdateFrameIterPc(iter);
|
||||
jsbytecode *pc = iter.pc();
|
||||
size_t offset = script->pcToOffset(pc);
|
||||
args.rval().setNumber(double(offset));
|
||||
@ -4683,7 +4810,7 @@ DebuggerFrame_eval(JSContext *cx, unsigned argc, Value *vp)
|
||||
THIS_FRAME_ITER(cx, argc, vp, "eval", args, thisobj, _, iter);
|
||||
REQUIRE_ARGC("Debugger.Frame.prototype.eval", 1);
|
||||
Debugger *dbg = Debugger::fromChildJSObject(thisobj);
|
||||
iter.updatePcQuadratic();
|
||||
UpdateFrameIterPc(iter);
|
||||
return DebuggerGenericEval(cx, "Debugger.Frame.prototype.eval",
|
||||
args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue,
|
||||
args.get(1), args.rval(), dbg, js::NullPtr(), &iter);
|
||||
@ -4695,7 +4822,7 @@ DebuggerFrame_evalWithBindings(JSContext *cx, unsigned argc, Value *vp)
|
||||
THIS_FRAME_ITER(cx, argc, vp, "evalWithBindings", args, thisobj, _, iter);
|
||||
REQUIRE_ARGC("Debugger.Frame.prototype.evalWithBindings", 2);
|
||||
Debugger *dbg = Debugger::fromChildJSObject(thisobj);
|
||||
iter.updatePcQuadratic();
|
||||
UpdateFrameIterPc(iter);
|
||||
return DebuggerGenericEval(cx, "Debugger.Frame.prototype.evalWithBindings",
|
||||
args[0], EvalHasExtraBindings, args[1], args.get(2),
|
||||
args.rval(), dbg, js::NullPtr(), &iter);
|
||||
@ -4721,6 +4848,7 @@ static const JSPropertySpec DebuggerFrame_properties[] = {
|
||||
JS_PSG("script", DebuggerFrame_getScript, 0),
|
||||
JS_PSG("this", DebuggerFrame_getThis, 0),
|
||||
JS_PSG("type", DebuggerFrame_getType, 0),
|
||||
JS_PSG("implementation", DebuggerFrame_getImplementation, 0),
|
||||
JS_PSGS("onStep", DebuggerFrame_getOnStep, DebuggerFrame_setOnStep, 0),
|
||||
JS_PSGS("onPop", DebuggerFrame_getOnPop, DebuggerFrame_setOnPop, 0),
|
||||
JS_PS_END
|
||||
|
@ -239,13 +239,24 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj);
|
||||
bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj,
|
||||
AutoDebugModeInvalidation &invalidate);
|
||||
void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
||||
void cleanupDebuggeeGlobalBeforeRemoval(FreeOp *fop, GlobalObject *global,
|
||||
AutoDebugModeInvalidation &invalidate,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnu);
|
||||
bool removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum);
|
||||
void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
|
||||
bool removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
|
||||
AutoDebugModeInvalidation &invalidate,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum);
|
||||
void removeDebuggeeGlobalUnderGC(FreeOp *fop, GlobalObject *global,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum);
|
||||
void removeDebuggeeGlobalUnderGC(FreeOp *fop, GlobalObject *global,
|
||||
AutoDebugModeInvalidation &invalidate,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum);
|
||||
|
||||
/*
|
||||
* Cope with an error or exception in a debugger hook.
|
||||
@ -382,6 +393,9 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
|
||||
static inline Debugger *fromOnNewGlobalObjectWatchersLink(JSCList *link);
|
||||
|
||||
static bool replaceFrameGuts(JSContext *cx, AbstractFramePtr from, AbstractFramePtr to,
|
||||
ScriptFrameIter &iter);
|
||||
|
||||
public:
|
||||
Debugger(JSContext *cx, JSObject *dbg);
|
||||
~Debugger();
|
||||
@ -428,6 +442,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
static JSTrapStatus onTrap(JSContext *cx, MutableHandleValue vp);
|
||||
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);
|
||||
|
||||
/************************************* Functions for use by Debugger.cpp. */
|
||||
|
||||
@ -436,6 +451,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
inline bool observesNewGlobalObject() const;
|
||||
inline bool observesGlobal(GlobalObject *global) const;
|
||||
bool observesFrame(AbstractFramePtr frame) const;
|
||||
bool observesFrame(const ScriptFrameIter &iter) const;
|
||||
bool observesScript(JSScript *script) const;
|
||||
|
||||
/*
|
||||
|
@ -762,7 +762,7 @@ GlobalObject::addDebugger(JSContext *cx, Handle<GlobalObject*> global, Debugger
|
||||
if (debuggers->empty() && !global->compartment()->addDebuggee(cx, global))
|
||||
return false;
|
||||
if (!debuggers->append(dbg)) {
|
||||
global->compartment()->removeDebuggee(cx->runtime()->defaultFreeOp(), global);
|
||||
(void) global->compartment()->removeDebuggee(cx, global);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -1059,9 +1059,44 @@ FrameIter::isConstructing() const
|
||||
MOZ_ASSUME_UNREACHABLE("Unexpected state");
|
||||
}
|
||||
|
||||
bool
|
||||
FrameIter::ensureHasRematerializedFrame()
|
||||
{
|
||||
#ifdef JS_ION
|
||||
MOZ_ASSERT(isIon());
|
||||
return !!activation()->asJit()->getRematerializedFrame(activation()->cx(), data_.jitFrames_);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
FrameIter::hasUsableAbstractFramePtr() const
|
||||
{
|
||||
switch (data_.state_) {
|
||||
case DONE:
|
||||
case ASMJS:
|
||||
return false;
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.jitFrames_.isBaselineJS())
|
||||
return true;
|
||||
|
||||
MOZ_ASSERT(data_.jitFrames_.isIonJS());
|
||||
return !!activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
|
||||
ionInlineFrames_.frameNo());
|
||||
#endif
|
||||
break;
|
||||
case INTERP:
|
||||
return true;
|
||||
}
|
||||
MOZ_ASSUME_UNREACHABLE("Unexpected state");
|
||||
}
|
||||
|
||||
AbstractFramePtr
|
||||
FrameIter::abstractFramePtr() const
|
||||
{
|
||||
MOZ_ASSERT(hasUsableAbstractFramePtr());
|
||||
switch (data_.state_) {
|
||||
case DONE:
|
||||
case ASMJS:
|
||||
@ -1072,11 +1107,8 @@ FrameIter::abstractFramePtr() const
|
||||
return data_.jitFrames_.baselineFrame();
|
||||
|
||||
MOZ_ASSERT(data_.jitFrames_.isIonJS());
|
||||
jit::RematerializedFrame *frame =
|
||||
activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
|
||||
ionInlineFrames_.frameNo());
|
||||
MOZ_ASSERT(frame);
|
||||
return frame;
|
||||
return activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
|
||||
ionInlineFrames_.frameNo());
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
@ -1666,9 +1666,19 @@ class FrameIter
|
||||
size_t numFrameSlots() const;
|
||||
Value frameSlotValue(size_t index) const;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// The following functions can only be called when isInterp() or isBaseline()
|
||||
// --------------------------------------------------------------------------
|
||||
// Ensures that we have rematerialized the top frame and its associated
|
||||
// inline frames. Can only be called when isIon().
|
||||
bool ensureHasRematerializedFrame();
|
||||
|
||||
// True when isInterp() or isBaseline(). True when isIon() if it
|
||||
// has a rematerialized frame. False otherwise false otherwise.
|
||||
bool hasUsableAbstractFramePtr() const;
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// The following functions can only be called when isInterp(),
|
||||
// isBaseline(), or isIon(). Further, abstractFramePtr() can
|
||||
// only be called when hasUsableAbstractFramePtr().
|
||||
// -----------------------------------------------------------
|
||||
|
||||
AbstractFramePtr abstractFramePtr() const;
|
||||
AbstractFramePtr copyDataAsAbstractFramePtr() const;
|
||||
|
Loading…
Reference in New Issue
Block a user