зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1566885 - Factor uses of internal Debugger state out of non-debugger-related files, r=jimb.
--HG-- extra : rebase_source : 80d5d339e673f4f943a354e126a6c3f96bb557f6
This commit is contained in:
Родитель
1a3b16017c
Коммит
e777b59fb6
|
@ -13,6 +13,21 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::stepModeEnabled(JSScript* script) {
|
||||
return script->hasDebugScript() && stepModeEnabledSlow(script);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::hasBreakpointsAt(JSScript* script, jsbytecode* pc) {
|
||||
return script->hasDebugScript() && hasBreakpointsAtSlow(script, pc);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::hasAnyBreakpointsOrStepMode(JSScript* script) {
|
||||
return script->hasDebugScript();
|
||||
}
|
||||
|
||||
/* static */
|
||||
void DebugAPI::onNewScript(JSContext* cx, HandleScript script) {
|
||||
// We early return in slowPathOnNewScript for self-hosted scripts, so we can
|
||||
|
@ -43,6 +58,15 @@ void DebugAPI::onNewGlobalObject(JSContext* cx,
|
|||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void DebugAPI::notifyParticipatesInGC(GlobalObject* global,
|
||||
uint64_t majorGCNumber) {
|
||||
GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
|
||||
if (dbgs && !dbgs->empty()) {
|
||||
slowPathNotifyParticipatesInGC(majorGCNumber, *dbgs);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::onLogAllocationSite(JSContext* cx, JSObject* obj,
|
||||
HandleSavedFrame frame,
|
||||
|
@ -64,7 +88,7 @@ bool DebugAPI::onLeaveFrame(JSContext* cx, AbstractFramePtr frame,
|
|||
frame.isDebuggee());
|
||||
/* Traps must be cleared from eval frames, see slowPathOnLeaveFrame. */
|
||||
mozilla::DebugOnly<bool> evalTraps =
|
||||
frame.isEvalFrame() && frame.script()->hasAnyBreakpointsOrStepMode();
|
||||
frame.isEvalFrame() && frame.script()->hasDebugScript();
|
||||
MOZ_ASSERT_IF(evalTraps, frame.isDebuggee());
|
||||
if (frame.isDebuggee()) {
|
||||
ok = slowPathOnLeaveFrame(cx, frame, pc, ok);
|
||||
|
@ -149,6 +173,13 @@ void DebugAPI::onPromiseSettled(JSContext* cx, Handle<PromiseObject*> promise) {
|
|||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void DebugAPI::sweepBreakpoints(FreeOp* fop, JSScript* script) {
|
||||
if (script->hasDebugScript()) {
|
||||
sweepBreakpointsSlow(fop, script);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* debugger_DebugAPI_inl_h */
|
||||
|
|
|
@ -65,6 +65,9 @@ enum class ResumeMode {
|
|||
Return,
|
||||
};
|
||||
|
||||
class DebugScript;
|
||||
class DebuggerVector;
|
||||
|
||||
class DebugAPI {
|
||||
public:
|
||||
friend class Debugger;
|
||||
|
@ -91,6 +94,19 @@ class DebugAPI {
|
|||
static void traceAllForMovingGC(JSTracer* trc);
|
||||
static void sweepAll(FreeOp* fop);
|
||||
static MOZ_MUST_USE bool findSweepGroupEdges(JSRuntime* rt);
|
||||
static inline void sweepBreakpoints(FreeOp* fop, JSScript* script);
|
||||
static void destroyDebugScript(FreeOp* fop, JSScript* script);
|
||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||
static void checkDebugScriptAfterMovingGC(DebugScript* ds);
|
||||
#endif
|
||||
|
||||
/*** Methods for querying script breakpoint state. **************************/
|
||||
|
||||
static inline bool stepModeEnabled(JSScript* script);
|
||||
|
||||
static inline bool hasBreakpointsAt(JSScript* script, jsbytecode* pc);
|
||||
|
||||
static inline bool hasAnyBreakpointsOrStepMode(JSScript* script);
|
||||
|
||||
/*** Methods for interacting with the runtime. ******************************/
|
||||
|
||||
|
@ -213,6 +229,15 @@ class DebugAPI {
|
|||
static inline void onPromiseSettled(JSContext* cx,
|
||||
Handle<PromiseObject*> promise);
|
||||
|
||||
// Whether any debugger is observing execution in a global.
|
||||
static bool debuggerObservesAllExecution(GlobalObject* global);
|
||||
|
||||
// Whether any debugger is observing JS execution coverage in a global.
|
||||
static bool debuggerObservesCoverage(GlobalObject* global);
|
||||
|
||||
// Whether any Debugger is observing asm.js execution in a global.
|
||||
static bool debuggerObservesAsmJS(GlobalObject* global);
|
||||
|
||||
/*
|
||||
* Announce to the debugger that a generator object has been created,
|
||||
* via JSOP_GENERATOR.
|
||||
|
@ -238,10 +263,36 @@ class DebugAPI {
|
|||
static bool isObservedByDebuggerTrackingAllocations(
|
||||
const GlobalObject& debuggee);
|
||||
|
||||
// Announce to the debugger that a global object is being collected by the
|
||||
// specified major GC.
|
||||
static inline void notifyParticipatesInGC(GlobalObject* global,
|
||||
uint64_t majorGCNumber);
|
||||
|
||||
static mozilla::Maybe<double> allocationSamplingProbability(
|
||||
GlobalObject* global);
|
||||
|
||||
// Allocate an object which holds a GlobalObject::DebuggerVector.
|
||||
static JSObject* newGlobalDebuggersHolder(JSContext* cx);
|
||||
|
||||
// Get the GlobalObject::DebuggerVector for an object allocated by
|
||||
// newGlobalDebuggersObject.
|
||||
static GlobalObject::DebuggerVector* getGlobalDebuggers(JSObject* holder);
|
||||
|
||||
// Whether any debugger is observing exception unwinds in a realm.
|
||||
static bool hasExceptionUnwindHook(GlobalObject* global);
|
||||
|
||||
// Whether any debugger is observing debugger statements in a realm.
|
||||
static bool hasDebuggerStatementHook(GlobalObject* global);
|
||||
|
||||
private:
|
||||
static bool stepModeEnabledSlow(JSScript* script);
|
||||
static bool hasBreakpointsAtSlow(JSScript* script, jsbytecode* pc);
|
||||
static void sweepBreakpointsSlow(FreeOp* fop, JSScript* script);
|
||||
static void slowPathOnNewScript(JSContext* cx, HandleScript script);
|
||||
static void slowPathOnNewGlobalObject(JSContext* cx,
|
||||
Handle<GlobalObject*> global);
|
||||
static void slowPathNotifyParticipatesInGC(
|
||||
uint64_t majorGCNumber, GlobalObject::DebuggerVector& dbgs);
|
||||
static MOZ_MUST_USE bool slowPathOnLogAllocationSite(
|
||||
JSContext* cx, HandleObject obj, HandleSavedFrame frame,
|
||||
mozilla::TimeStamp when, GlobalObject::DebuggerVector& dbgs);
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "debugger/DebugScript.h"
|
||||
|
||||
#include "jit/BaselineJIT.h"
|
||||
|
||||
#include "gc/FreeOp-inl.h"
|
||||
#include "gc/GC-inl.h"
|
||||
#include "gc/Marking-inl.h"
|
||||
#include "vm/JSContext-inl.h"
|
||||
#include "vm/Realm-inl.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
/* static */
|
||||
DebugScript* DebugScript::get(JSScript* script) {
|
||||
MOZ_ASSERT(script->hasDebugScript());
|
||||
DebugScriptMap* map = script->realm()->debugScriptMap.get();
|
||||
MOZ_ASSERT(map);
|
||||
DebugScriptMap::Ptr p = map->lookup(script);
|
||||
MOZ_ASSERT(p);
|
||||
return p->value().get();
|
||||
}
|
||||
|
||||
/* static */
|
||||
DebugScript* DebugScript::getOrCreate(JSContext* cx, JSScript* script) {
|
||||
if (script->hasDebugScript()) {
|
||||
return get(script);
|
||||
}
|
||||
|
||||
size_t nbytes = allocSize(script->length());
|
||||
UniqueDebugScript debug(
|
||||
reinterpret_cast<DebugScript*>(cx->pod_calloc<uint8_t>(nbytes)));
|
||||
if (!debug) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Create realm's debugScriptMap if necessary. */
|
||||
if (!script->realm()->debugScriptMap) {
|
||||
auto map = cx->make_unique<DebugScriptMap>();
|
||||
if (!map) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
script->realm()->debugScriptMap = std::move(map);
|
||||
}
|
||||
|
||||
DebugScript* borrowed = debug.get();
|
||||
if (!script->realm()->debugScriptMap->putNew(script, std::move(debug))) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// It is safe to set this: we can't fail after this point.
|
||||
script->setHasDebugScript(true);
|
||||
AddCellMemory(script, nbytes, MemoryUse::ScriptDebugScript);
|
||||
|
||||
/*
|
||||
* Ensure that any Interpret() instances running on this script have
|
||||
* interrupts enabled. The interrupts must stay enabled until the
|
||||
* debug state is destroyed.
|
||||
*/
|
||||
for (ActivationIterator iter(cx); !iter.done(); ++iter) {
|
||||
if (iter->isInterpreter()) {
|
||||
iter->asInterpreter()->enableInterruptsIfRunning(script);
|
||||
}
|
||||
}
|
||||
|
||||
return borrowed;
|
||||
}
|
||||
|
||||
/* static */
|
||||
BreakpointSite* DebugScript::getBreakpointSite(JSScript* script,
|
||||
jsbytecode* pc) {
|
||||
uint32_t offset = script->pcToOffset(pc);
|
||||
return script->hasDebugScript() ? get(script)->breakpoints[offset] : nullptr;
|
||||
}
|
||||
|
||||
/* static */
|
||||
BreakpointSite* DebugScript::getOrCreateBreakpointSite(JSContext* cx,
|
||||
JSScript* script,
|
||||
jsbytecode* pc) {
|
||||
AutoRealm ar(cx, script);
|
||||
|
||||
DebugScript* debug = getOrCreate(cx, script);
|
||||
if (!debug) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
|
||||
|
||||
if (!site) {
|
||||
site = cx->new_<JSBreakpointSite>(script, pc);
|
||||
if (!site) {
|
||||
return nullptr;
|
||||
}
|
||||
debug->numSites++;
|
||||
AddCellMemory(script, sizeof(JSBreakpointSite), MemoryUse::BreakpointSite);
|
||||
}
|
||||
|
||||
return site;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void DebugScript::destroyBreakpointSite(FreeOp* fop, JSScript* script,
|
||||
jsbytecode* pc) {
|
||||
DebugScript* debug = get(script);
|
||||
BreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
|
||||
MOZ_ASSERT(site);
|
||||
|
||||
size_t size = site->type() == BreakpointSite::Type::JS
|
||||
? sizeof(JSBreakpointSite)
|
||||
: sizeof(WasmBreakpointSite);
|
||||
fop->delete_(script, site, size, MemoryUse::BreakpointSite);
|
||||
site = nullptr;
|
||||
|
||||
debug->numSites--;
|
||||
if (!debug->needed()) {
|
||||
DebugAPI::destroyDebugScript(fop, script);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void DebugScript::clearBreakpointsIn(FreeOp* fop, Realm* realm,
|
||||
Debugger* dbg, JSObject* handler) {
|
||||
for (auto script = realm->zone()->cellIter<JSScript>(); !script.done();
|
||||
script.next()) {
|
||||
if (script->realm() == realm && script->hasDebugScript()) {
|
||||
clearBreakpointsIn(fop, script, dbg, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void DebugScript::clearBreakpointsIn(FreeOp* fop, JSScript* script,
|
||||
Debugger* dbg, JSObject* handler) {
|
||||
if (!script->hasDebugScript()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (jsbytecode* pc = script->code(); pc < script->codeEnd(); pc++) {
|
||||
BreakpointSite* site = getBreakpointSite(script, pc);
|
||||
if (site) {
|
||||
Breakpoint* nextbp;
|
||||
for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
|
||||
nextbp = bp->nextInSite();
|
||||
if ((!dbg || bp->debugger == dbg) &&
|
||||
(!handler || bp->getHandler() == handler)) {
|
||||
bp->destroy(fop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/* static */
|
||||
uint32_t DebugScript::getStepperCount(JSScript* script) {
|
||||
return script->hasDebugScript() ? get(script)->stepperCount : 0;
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
/* static */
|
||||
bool DebugScript::incrementStepperCount(JSContext* cx, JSScript* script) {
|
||||
cx->check(script);
|
||||
MOZ_ASSERT(cx->realm()->isDebuggee());
|
||||
|
||||
AutoRealm ar(cx, script);
|
||||
|
||||
DebugScript* debug = getOrCreate(cx, script);
|
||||
if (!debug) {
|
||||
return false;
|
||||
}
|
||||
|
||||
debug->stepperCount++;
|
||||
|
||||
if (debug->stepperCount == 1) {
|
||||
if (script->hasBaselineScript()) {
|
||||
script->baselineScript()->toggleDebugTraps(script, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void DebugScript::decrementStepperCount(FreeOp* fop, JSScript* script) {
|
||||
DebugScript* debug = get(script);
|
||||
MOZ_ASSERT(debug);
|
||||
MOZ_ASSERT(debug->stepperCount > 0);
|
||||
|
||||
debug->stepperCount--;
|
||||
|
||||
if (debug->stepperCount == 0) {
|
||||
if (script->hasBaselineScript()) {
|
||||
script->baselineScript()->toggleDebugTraps(script, nullptr);
|
||||
}
|
||||
|
||||
if (!debug->needed()) {
|
||||
DebugAPI::destroyDebugScript(fop, script);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugScript::incrementGeneratorObserverCount(JSContext* cx,
|
||||
JSScript* script) {
|
||||
cx->check(script);
|
||||
MOZ_ASSERT(cx->realm()->isDebuggee());
|
||||
|
||||
AutoRealm ar(cx, script);
|
||||
|
||||
DebugScript* debug = getOrCreate(cx, script);
|
||||
if (!debug) {
|
||||
return false;
|
||||
}
|
||||
|
||||
debug->generatorObserverCount++;
|
||||
|
||||
// It is our caller's responsibility, before bumping the generator observer
|
||||
// count, to make sure that the baseline code includes the necessary
|
||||
// JS_AFTERYIELD instrumentation by calling
|
||||
// {ensure,update}ExecutionObservabilityOfScript.
|
||||
MOZ_ASSERT_IF(script->hasBaselineScript(),
|
||||
script->baselineScript()->hasDebugInstrumentation());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void DebugScript::decrementGeneratorObserverCount(FreeOp* fop,
|
||||
JSScript* script) {
|
||||
DebugScript* debug = get(script);
|
||||
MOZ_ASSERT(debug);
|
||||
MOZ_ASSERT(debug->generatorObserverCount > 0);
|
||||
|
||||
debug->generatorObserverCount--;
|
||||
|
||||
if (!debug->needed()) {
|
||||
DebugAPI::destroyDebugScript(fop, script);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void DebugAPI::destroyDebugScript(FreeOp* fop, JSScript* script) {
|
||||
if (script->hasDebugScript()) {
|
||||
DebugScriptMap* map = script->realm()->debugScriptMap.get();
|
||||
MOZ_ASSERT(map);
|
||||
DebugScriptMap::Ptr p = map->lookup(script);
|
||||
MOZ_ASSERT(p);
|
||||
DebugScript* debug = p->value().release();
|
||||
map->remove(p);
|
||||
script->setHasDebugScript(false);
|
||||
|
||||
fop->free_(script, debug, DebugScript::allocSize(script->length()),
|
||||
MemoryUse::ScriptDebugScript);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||
/* static */
|
||||
void DebugAPI::checkDebugScriptAfterMovingGC(DebugScript* ds) {
|
||||
for (uint32_t i = 0; i < ds->numSites; i++) {
|
||||
BreakpointSite* site = ds->breakpoints[i];
|
||||
if (site && site->type() == BreakpointSite::Type::JS) {
|
||||
CheckGCThingAfterMovingGC(site->asJS()->script);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // JSGC_HASH_TABLE_CHECKS
|
||||
|
||||
/* static */
|
||||
void DebugAPI::sweepBreakpointsSlow(FreeOp* fop, JSScript* script) {
|
||||
bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script);
|
||||
for (unsigned i = 0; i < script->length(); i++) {
|
||||
BreakpointSite* site =
|
||||
DebugScript::getBreakpointSite(script, script->offsetToPC(i));
|
||||
if (!site) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Breakpoint* nextbp;
|
||||
for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
|
||||
nextbp = bp->nextInSite();
|
||||
GCPtrNativeObject& dbgobj = bp->debugger->toJSObjectRef();
|
||||
|
||||
// If we are sweeping, then we expect the script and the
|
||||
// debugger object to be swept in the same sweep group, except
|
||||
// if the breakpoint was added after we computed the sweep
|
||||
// groups. In this case both script and debugger object must be
|
||||
// live.
|
||||
MOZ_ASSERT_IF(script->zone()->isGCSweeping() &&
|
||||
dbgobj->zone()->isCollecting(),
|
||||
dbgobj->zone()->isGCSweeping() ||
|
||||
(!scriptGone && dbgobj->asTenured().isMarkedAny()));
|
||||
|
||||
bool dying = scriptGone || IsAboutToBeFinalized(&dbgobj);
|
||||
MOZ_ASSERT_IF(!dying, !IsAboutToBeFinalized(&bp->getHandlerRef()));
|
||||
if (dying) {
|
||||
bp->destroy(fop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::stepModeEnabledSlow(JSScript* script) {
|
||||
return DebugScript::get(script)->stepperCount > 0;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::hasBreakpointsAtSlow(JSScript* script, jsbytecode* pc) {
|
||||
BreakpointSite* site = DebugScript::getBreakpointSite(script, pc);
|
||||
return site && site->enabledCount > 0;
|
||||
}
|
||||
|
||||
} // namespace js
|
|
@ -0,0 +1,107 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef dbg_DebugScript_h
|
||||
#define dbg_DebugScript_h
|
||||
|
||||
#include "debugger/Debugger.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
// DebugScript manages the internal debugger state for a JSScript, which may be
|
||||
// associated with multiple Debuggers.
|
||||
class DebugScript {
|
||||
friend class DebugAPI;
|
||||
|
||||
/*
|
||||
* If this is a generator script, this is the number of Debugger.Frames
|
||||
* referring to calls to this generator, whether live or suspended. Closed
|
||||
* generators do not contribute a count.
|
||||
*
|
||||
* When greater than zero, this script should be compiled with debug
|
||||
* instrumentation to call Debugger::onResumeFrame at each resumption site, so
|
||||
* that Debugger can reconnect any extant Debugger.Frames with the new
|
||||
* concrete frame.
|
||||
*/
|
||||
uint32_t generatorObserverCount;
|
||||
|
||||
/*
|
||||
* The number of Debugger.Frame objects that refer to frames running this
|
||||
* script and that have onStep handlers. When nonzero, the interpreter and JIT
|
||||
* must arrange to call Debugger::onSingleStep before each bytecode, or at
|
||||
* least at some useful granularity.
|
||||
*/
|
||||
uint32_t stepperCount;
|
||||
|
||||
/*
|
||||
* Number of breakpoint sites at opcodes in the script. This is the number
|
||||
* of populated entries in DebugScript::breakpoints, below.
|
||||
*/
|
||||
uint32_t numSites;
|
||||
|
||||
/*
|
||||
* Breakpoints set in our script. For speed and simplicity, this array is
|
||||
* parallel to script->code(): the BreakpointSite for the opcode at
|
||||
* script->code()[offset] is debugScript->breakpoints[offset]. Naturally,
|
||||
* this array's true length is script->length().
|
||||
*/
|
||||
BreakpointSite* breakpoints[1];
|
||||
|
||||
/*
|
||||
* True if this DebugScript carries any useful information. If false, it
|
||||
* should be removed from its JSScript.
|
||||
*/
|
||||
bool needed() const {
|
||||
return generatorObserverCount > 0 || stepperCount > 0 || numSites > 0;
|
||||
}
|
||||
|
||||
static size_t allocSize(size_t codeLength) {
|
||||
return offsetof(DebugScript, breakpoints) +
|
||||
codeLength * sizeof(BreakpointSite*);
|
||||
}
|
||||
|
||||
static DebugScript* get(JSScript* script);
|
||||
static DebugScript* getOrCreate(JSContext* cx, JSScript* script);
|
||||
|
||||
public:
|
||||
static BreakpointSite* getBreakpointSite(JSScript* script, jsbytecode* pc);
|
||||
static BreakpointSite* getOrCreateBreakpointSite(JSContext* cx,
|
||||
JSScript* script,
|
||||
jsbytecode* pc);
|
||||
static void destroyBreakpointSite(FreeOp* fop, JSScript* script,
|
||||
jsbytecode* pc);
|
||||
|
||||
static void clearBreakpointsIn(FreeOp* fop, Realm* realm,
|
||||
Debugger* dbg, JSObject* handler);
|
||||
static void clearBreakpointsIn(FreeOp* fop, JSScript* script,
|
||||
Debugger* dbg, JSObject* handler);
|
||||
|
||||
#ifdef DEBUG
|
||||
static uint32_t getStepperCount(JSScript* script);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Increment or decrement the single-step count. If the count is non-zero
|
||||
* then the script is in single-step mode.
|
||||
*
|
||||
* Only incrementing is fallible, as it could allocate a DebugScript.
|
||||
*/
|
||||
static bool incrementStepperCount(JSContext* cx, JSScript* script);
|
||||
static void decrementStepperCount(FreeOp* fop, JSScript* script);
|
||||
|
||||
/*
|
||||
* Increment or decrement the generator observer count. If the count is
|
||||
* non-zero then the script reports resumptions to the debugger.
|
||||
*
|
||||
* Only incrementing is fallible, as it could allocate a DebugScript.
|
||||
*/
|
||||
static bool incrementGeneratorObserverCount(JSContext* cx, JSScript* script);
|
||||
static void decrementGeneratorObserverCount(FreeOp* fop, JSScript* script);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* dbg_DebugScript_h */
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "builtin/Promise.h"
|
||||
#include "debugger/DebuggerMemory.h"
|
||||
#include "debugger/DebugScript.h"
|
||||
#include "debugger/Environment.h"
|
||||
#include "debugger/Frame.h"
|
||||
#include "debugger/NoExecute.h"
|
||||
|
@ -54,6 +55,7 @@
|
|||
#include "vm/WrapperObject.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
|
||||
#include "debugger/DebugAPI-inl.h"
|
||||
#include "debugger/Frame-inl.h"
|
||||
#include "debugger/Script-inl.h"
|
||||
#include "gc/GC-inl.h"
|
||||
|
@ -332,7 +334,7 @@ Breakpoint* Breakpoint::nextInSite() { return siteLink.mNext; }
|
|||
|
||||
JSBreakpointSite::JSBreakpointSite(JSScript* script, jsbytecode* pc)
|
||||
: BreakpointSite(Type::JS), script(script), pc(pc) {
|
||||
MOZ_ASSERT(!script->hasBreakpointsAt(pc));
|
||||
MOZ_ASSERT(!DebugAPI::hasBreakpointsAt(script, pc));
|
||||
}
|
||||
|
||||
void JSBreakpointSite::recompile(FreeOp* fop) {
|
||||
|
@ -343,7 +345,7 @@ void JSBreakpointSite::recompile(FreeOp* fop) {
|
|||
|
||||
void JSBreakpointSite::destroyIfEmpty(FreeOp* fop) {
|
||||
if (isEmpty()) {
|
||||
script->destroyBreakpointSite(fop, pc);
|
||||
DebugScript::destroyBreakpointSite(fop, script, pc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -459,6 +461,57 @@ DebuggerMemory& Debugger::memory() const {
|
|||
.as<DebuggerMemory>();
|
||||
}
|
||||
|
||||
/*** DebuggerVectorHolder *****************************************************/
|
||||
|
||||
static void GlobalDebuggerVectorHolder_finalize(FreeOp* fop, JSObject* obj) {
|
||||
MOZ_ASSERT(fop->maybeOnHelperThread());
|
||||
void* ptr = obj->as<NativeObject>().getPrivate();
|
||||
auto debuggers = static_cast<GlobalObject::DebuggerVector*>(ptr);
|
||||
fop->delete_(obj, debuggers, MemoryUse::GlobalDebuggerVector);
|
||||
}
|
||||
|
||||
static const ClassOps GlobalDebuggerVectorHolder_classOps = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
GlobalDebuggerVectorHolder_finalize
|
||||
};
|
||||
|
||||
static const Class GlobalDebuggerVectorHolder_class = {
|
||||
"GlobalDebuggerVectorHolder",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
|
||||
&GlobalDebuggerVectorHolder_classOps};
|
||||
|
||||
/* static */
|
||||
JSObject* DebugAPI::newGlobalDebuggersHolder(JSContext* cx) {
|
||||
NativeObject* obj =
|
||||
NewNativeObjectWithGivenProto(cx, &GlobalDebuggerVectorHolder_class,
|
||||
nullptr);
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GlobalObject::DebuggerVector* debuggers =
|
||||
cx->new_<GlobalObject::DebuggerVector>(cx->zone());
|
||||
if (!debuggers) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InitObjectPrivate(obj, debuggers, MemoryUse::GlobalDebuggerVector);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* static */
|
||||
GlobalObject::DebuggerVector* DebugAPI::getGlobalDebuggers(JSObject* holder) {
|
||||
MOZ_ASSERT(holder->getClass() == &GlobalDebuggerVectorHolder_class);
|
||||
return (GlobalObject::DebuggerVector*)holder->as<NativeObject>().getPrivate();
|
||||
}
|
||||
|
||||
/*** Debugger accessors *******************************************************/
|
||||
|
||||
bool Debugger::getFrame(JSContext* cx, const FrameIter& iter,
|
||||
MutableHandleValue vp) {
|
||||
RootedDebuggerFrame result(cx);
|
||||
|
@ -551,12 +604,17 @@ bool Debugger::getFrame(JSContext* cx, const FrameIter& iter,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool Debugger::hasLiveHook(GlobalObject* global, Hook which) {
|
||||
static bool DebuggerExists(GlobalObject* global,
|
||||
const std::function<bool(Debugger* dbg)>& predicate) {
|
||||
// The GC analysis can't determine that the predicate can't GC, so let it know
|
||||
// explicitly.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
|
||||
for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
|
||||
Debugger* dbg = *p;
|
||||
if (dbg->enabled && dbg->getHook(which)) {
|
||||
// Callbacks should not create new references to the debugger, so don't
|
||||
// use a barrier. This allows this method to be called during GC.
|
||||
if (predicate(p->unbarrieredGet())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -564,6 +622,44 @@ bool Debugger::hasLiveHook(GlobalObject* global, Hook which) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool Debugger::hasLiveHook(GlobalObject* global, Hook which) {
|
||||
return DebuggerExists(global, [=](Debugger* dbg) {
|
||||
return dbg->enabled && dbg->getHook(which);
|
||||
});
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::debuggerObservesAllExecution(GlobalObject* global) {
|
||||
return DebuggerExists(global, [=](Debugger* dbg) {
|
||||
return dbg->observesAllExecution();
|
||||
});
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::debuggerObservesCoverage(GlobalObject* global) {
|
||||
return DebuggerExists(global, [=](Debugger* dbg) {
|
||||
return dbg->observesCoverage();
|
||||
});
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::debuggerObservesAsmJS(GlobalObject* global) {
|
||||
return DebuggerExists(global, [=](Debugger* dbg) {
|
||||
return dbg->observesAsmJS();
|
||||
});
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::hasExceptionUnwindHook(GlobalObject* global) {
|
||||
return Debugger::hasLiveHook(global, Debugger::OnExceptionUnwind);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::hasDebuggerStatementHook(GlobalObject* global) {
|
||||
return Debugger::hasLiveHook(global, Debugger::OnDebuggerStatement);
|
||||
}
|
||||
|
||||
JSObject* Debugger::getHook(Hook hook) const {
|
||||
MOZ_ASSERT(hook >= 0 && hook < HookCount);
|
||||
const Value& v = object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + hook);
|
||||
|
@ -2197,7 +2293,7 @@ ResumeMode DebugAPI::onTrap(JSContext* cx, MutableHandleValue vp) {
|
|||
isJS = true;
|
||||
pc = iter.pc();
|
||||
bytecodeOffset = 0;
|
||||
site = script->getBreakpointSite(pc);
|
||||
site = DebugScript::getBreakpointSite(script, pc);
|
||||
} else {
|
||||
MOZ_ASSERT(iter.isWasm());
|
||||
global.set(&iter.wasmInstance()->object()->global());
|
||||
|
@ -2271,7 +2367,7 @@ ResumeMode DebugAPI::onTrap(JSContext* cx, MutableHandleValue vp) {
|
|||
|
||||
// Calling JS code invalidates site. Reload it.
|
||||
if (isJS) {
|
||||
site = iter.script()->getBreakpointSite(pc);
|
||||
site = DebugScript::getBreakpointSite(iter.script(), pc);
|
||||
} else {
|
||||
site = iter.wasmInstance()->debug().getBreakpointSite(cx,
|
||||
bytecodeOffset);
|
||||
|
@ -2367,7 +2463,7 @@ ResumeMode DebugAPI::onSingleStep(JSContext* cx, MutableHandleValue vp) {
|
|||
}
|
||||
|
||||
MOZ_ASSERT(liveStepperCount + suspendedStepperCount ==
|
||||
trappingScript->stepperCount());
|
||||
DebugScript::getStepperCount(trappingScript));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -2509,6 +2605,52 @@ void DebugAPI::slowPathOnNewGlobalObject(JSContext* cx,
|
|||
MOZ_ASSERT(!cx->isExceptionPending());
|
||||
}
|
||||
|
||||
/* static */
|
||||
void DebugAPI::slowPathNotifyParticipatesInGC(
|
||||
uint64_t majorGCNumber,
|
||||
GlobalObject::DebuggerVector& dbgs) {
|
||||
for (GlobalObject::DebuggerVector::Range r = dbgs.all(); !r.empty();
|
||||
r.popFront()) {
|
||||
if (!r.front().unbarrieredGet()->debuggeeIsBeingCollected(majorGCNumber)) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,
|
||||
"OOM while notifying observing Debuggers of a GC: The "
|
||||
"onGarbageCollection\n"
|
||||
"hook will not be fired for this GC for some Debuggers!\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
Maybe<double> DebugAPI::allocationSamplingProbability(GlobalObject* global) {
|
||||
GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
|
||||
if (!dbgs || dbgs->empty()) {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
DebugOnly<WeakHeapPtr<Debugger*>*> begin = dbgs->begin();
|
||||
|
||||
double probability = 0;
|
||||
bool foundAnyDebuggers = false;
|
||||
for (auto p = dbgs->begin(); p < dbgs->end(); p++) {
|
||||
// The set of debuggers had better not change while we're iterating,
|
||||
// such that the vector gets reallocated.
|
||||
MOZ_ASSERT(dbgs->begin() == begin);
|
||||
// Use unbarrieredGet() to prevent triggering read barrier while collecting,
|
||||
// this is safe as long as dbgp does not escape.
|
||||
Debugger* dbgp = p->unbarrieredGet();
|
||||
|
||||
if (dbgp->trackingAllocationSites && dbgp->enabled) {
|
||||
foundAnyDebuggers = true;
|
||||
probability = std::max(dbgp->allocationSamplingProbability, probability);
|
||||
}
|
||||
}
|
||||
|
||||
return foundAnyDebuggers ? Some(probability) : Nothing();
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DebugAPI::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj,
|
||||
HandleSavedFrame frame,
|
||||
|
@ -4158,8 +4300,8 @@ bool Debugger::clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp) {
|
|||
THIS_DEBUGGER(cx, argc, vp, "clearAllBreakpoints", args, dbg);
|
||||
for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty();
|
||||
r.popFront()) {
|
||||
r.front()->realm()->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), dbg,
|
||||
nullptr);
|
||||
DebugScript::clearBreakpointsIn(cx->runtime()->defaultFreeOp(),
|
||||
r.front()->realm(), dbg, nullptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -6114,8 +6256,8 @@ void Debugger::removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx,
|
|||
// script is about to be destroyed. Remove any breakpoints in it.
|
||||
if (frame.isEvalFrame()) {
|
||||
RootedScript script(cx, frame.script());
|
||||
script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), nullptr,
|
||||
nullptr);
|
||||
DebugScript::clearBreakpointsIn(cx->runtime()->defaultFreeOp(), script,
|
||||
nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1178,8 +1178,8 @@ class WasmBreakpoint;
|
|||
class WasmBreakpointSite;
|
||||
|
||||
class BreakpointSite {
|
||||
friend class DebugAPI;
|
||||
friend class Breakpoint;
|
||||
friend class ::JSScript;
|
||||
friend class Debugger;
|
||||
|
||||
public:
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
|
||||
#include "debugger/DebugScript.h"
|
||||
#include "debugger/Environment.h"
|
||||
#include "debugger/NoExecute.h"
|
||||
#include "debugger/Object.h"
|
||||
|
@ -303,7 +304,7 @@ bool DebuggerFrame::setGenerator(JSContext* cx,
|
|||
|
||||
{
|
||||
AutoRealm ar(cx, script);
|
||||
if (!script->incrementGeneratorObserverCount(cx)) {
|
||||
if (!DebugScript::incrementGeneratorObserverCount(cx, script)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -334,11 +335,11 @@ void DebuggerFrame::clearGenerator(FreeOp* fop) {
|
|||
// calls may.
|
||||
HeapPtr<JSScript*>& generatorScript = info->generatorScript();
|
||||
if (!IsAboutToBeFinalized(&generatorScript)) {
|
||||
generatorScript->decrementGeneratorObserverCount(fop);
|
||||
DebugScript::decrementGeneratorObserverCount(fop, generatorScript);
|
||||
|
||||
OnStepHandler* handler = onStepHandler();
|
||||
if (handler) {
|
||||
generatorScript->decrementStepperCount(fop);
|
||||
DebugScript::decrementStepperCount(fop, generatorScript);
|
||||
handler->drop(fop, this);
|
||||
setReservedSlot(ONSTEP_HANDLER_SLOT, UndefinedValue());
|
||||
}
|
||||
|
@ -667,12 +668,13 @@ bool DebuggerFrame::setOnStepHandler(JSContext* cx, HandleDebuggerFrame frame,
|
|||
referent.script())) {
|
||||
return false;
|
||||
}
|
||||
if (!referent.script()->incrementStepperCount(cx)) {
|
||||
if (!DebugScript::incrementStepperCount(cx, referent.script())) {
|
||||
return false;
|
||||
}
|
||||
} else if (!handler && prior) {
|
||||
// Single stepping toggled on->off.
|
||||
referent.script()->decrementStepperCount(cx->runtime()->defaultFreeOp());
|
||||
DebugScript::decrementStepperCount(cx->runtime()->defaultFreeOp(),
|
||||
referent.script());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1008,7 +1010,7 @@ void DebuggerFrame::maybeDecrementFrameScriptStepperCount(
|
|||
instance->debug().decrementStepperCount(
|
||||
fop, frame.asWasmDebugFrame()->funcIndex());
|
||||
} else {
|
||||
frame.script()->decrementStepperCount(fop);
|
||||
DebugScript::decrementStepperCount(fop, frame.script());
|
||||
}
|
||||
|
||||
// In the case of generator frames, we may end up trying to clean up the step
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "debugger/Script-inl.h"
|
||||
|
||||
#include "debugger/Debugger.h"
|
||||
#include "debugger/DebugScript.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
|
||||
#include "vm/BytecodeUtil-inl.h"
|
||||
|
@ -1749,7 +1750,8 @@ struct DebuggerScript::SetBreakpointMatcher {
|
|||
}
|
||||
|
||||
jsbytecode* pc = script->offsetToPC(offset_);
|
||||
BreakpointSite* site = script->getOrCreateBreakpointSite(cx_, pc);
|
||||
BreakpointSite* site =
|
||||
DebugScript::getOrCreateBreakpointSite(cx_, script, pc);
|
||||
if (!site) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1844,7 +1846,8 @@ bool DebuggerScript::getBreakpoints(JSContext* cx, unsigned argc, Value* vp) {
|
|||
}
|
||||
|
||||
for (unsigned i = 0; i < script->length(); i++) {
|
||||
BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
|
||||
BreakpointSite* site =
|
||||
DebugScript::getBreakpointSite(script, script->offsetToPC(i));
|
||||
if (!site) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1874,7 +1877,8 @@ class DebuggerScript::ClearBreakpointMatcher {
|
|||
using ReturnType = bool;
|
||||
|
||||
ReturnType match(HandleScript script) {
|
||||
script->clearBreakpointsIn(cx_->runtime()->defaultFreeOp(), dbg_, handler_);
|
||||
DebugScript::clearBreakpointsIn(cx_->runtime()->defaultFreeOp(), script,
|
||||
dbg_, handler_);
|
||||
return true;
|
||||
}
|
||||
ReturnType match(Handle<LazyScript*> lazyScript) {
|
||||
|
|
|
@ -24,6 +24,7 @@ include('../js-cxxflags.mozbuild')
|
|||
SOURCES = [
|
||||
'Debugger.cpp',
|
||||
'DebuggerMemory.cpp',
|
||||
'DebugScript.cpp',
|
||||
'Environment.cpp',
|
||||
'Frame.cpp',
|
||||
'NoExecute.cpp',
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include "jsutil.h"
|
||||
|
||||
#include "debugger/Debugger.h"
|
||||
#include "gc/FreeOp.h"
|
||||
#include "gc/Policy.h"
|
||||
#include "gc/PublicIterators.h"
|
||||
|
@ -19,6 +18,7 @@
|
|||
#include "vm/Runtime.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
|
||||
#include "debugger/DebugAPI-inl.h"
|
||||
#include "gc/GC-inl.h"
|
||||
#include "gc/Marking-inl.h"
|
||||
#include "gc/Nursery-inl.h"
|
||||
|
@ -191,39 +191,7 @@ void Zone::sweepBreakpoints(FreeOp* fop) {
|
|||
MOZ_ASSERT(isGCSweepingOrCompacting());
|
||||
for (auto iter = cellIterUnsafe<JSScript>(); !iter.done(); iter.next()) {
|
||||
JSScript* script = iter;
|
||||
if (!script->hasAnyBreakpointsOrStepMode()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script);
|
||||
MOZ_ASSERT(script == iter);
|
||||
for (unsigned i = 0; i < script->length(); i++) {
|
||||
BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
|
||||
if (!site) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Breakpoint* nextbp;
|
||||
for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
|
||||
nextbp = bp->nextInSite();
|
||||
GCPtrNativeObject& dbgobj = bp->debugger->toJSObjectRef();
|
||||
|
||||
// If we are sweeping, then we expect the script and the
|
||||
// debugger object to be swept in the same sweep group, except
|
||||
// if the breakpoint was added after we computed the sweep
|
||||
// groups. In this case both script and debugger object must be
|
||||
// live.
|
||||
MOZ_ASSERT_IF(isGCSweeping() && dbgobj->zone()->isCollecting(),
|
||||
dbgobj->zone()->isGCSweeping() ||
|
||||
(!scriptGone && dbgobj->asTenured().isMarkedAny()));
|
||||
|
||||
bool dying = scriptGone || IsAboutToBeFinalized(&dbgobj);
|
||||
MOZ_ASSERT_IF(!dying, !IsAboutToBeFinalized(&bp->getHandlerRef()));
|
||||
if (dying) {
|
||||
bp->destroy(fop);
|
||||
}
|
||||
}
|
||||
}
|
||||
DebugAPI::sweepBreakpoints(fop, script);
|
||||
}
|
||||
|
||||
for (RealmsInZoneIter realms(this); !realms.done(); realms.next()) {
|
||||
|
@ -475,24 +443,7 @@ void Zone::notifyObservingDebuggers() {
|
|||
continue;
|
||||
}
|
||||
|
||||
GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
|
||||
if (!dbgs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (GlobalObject::DebuggerVector::Range r = dbgs->all(); !r.empty();
|
||||
r.popFront()) {
|
||||
if (!r.front().unbarrieredGet()->debuggeeIsBeingCollected(
|
||||
rt->gc.majorGCCount())) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,
|
||||
"OOM while notifying observing Debuggers of a GC: The "
|
||||
"onGarbageCollection\n"
|
||||
"hook will not be fired for this GC for some Debuggers!\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
DebugAPI::notifyParticipatesInGC(global, rt->gc.majorGCCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "vm/TraceLogging.h"
|
||||
#include "vtune/VTuneWrapper.h"
|
||||
|
||||
#include "debugger/DebugAPI-inl.h"
|
||||
#include "jit/BaselineFrameInfo-inl.h"
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
#include "jit/SharedICHelpers-inl.h"
|
||||
|
@ -1578,7 +1579,8 @@ bool BaselineCompiler::emitDebugTrap() {
|
|||
|
||||
JSScript* script = handler.script();
|
||||
bool enabled =
|
||||
script->stepModeEnabled() || script->hasBreakpointsAt(handler.pc());
|
||||
DebugAPI::stepModeEnabled(script) ||
|
||||
DebugAPI::hasBreakpointsAt(script, handler.pc());
|
||||
|
||||
#if defined(JS_CODEGEN_ARM64)
|
||||
// Flush any pending constant pools to prevent incorrect
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "vm/Interpreter.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
||||
#include "debugger/DebugAPI-inl.h"
|
||||
#include "gc/PrivateIterators-inl.h"
|
||||
#include "jit/JitFrames-inl.h"
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
|
@ -855,7 +856,8 @@ void BaselineScript::toggleDebugTraps(JSScript* script, jsbytecode* pc) {
|
|||
|
||||
if (!pc || pc == curPC) {
|
||||
bool enabled =
|
||||
script->stepModeEnabled() || script->hasBreakpointsAt(curPC);
|
||||
DebugAPI::stepModeEnabled(script) ||
|
||||
DebugAPI::hasBreakpointsAt(script, curPC);
|
||||
|
||||
// Patch the trap.
|
||||
CodeLocationLabel label(method(), CodeOffset(nativeOffset));
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include "jsutil.h"
|
||||
|
||||
#include "debugger/Debugger.h"
|
||||
#include "gc/Marking.h"
|
||||
#include "jit/BaselineDebugModeOSR.h"
|
||||
#include "jit/BaselineFrame.h"
|
||||
|
@ -160,8 +159,7 @@ static void HandleExceptionIon(JSContext* cx, const InlineFrameIterator& frame,
|
|||
// We need to bail when there is a catchable exception, and we are the
|
||||
// debuggee of a Debugger with a live onExceptionUnwind hook, or if a
|
||||
// Debugger has observed this frame (e.g., for onPop).
|
||||
bool shouldBail =
|
||||
Debugger::hasLiveHook(cx->global(), Debugger::OnExceptionUnwind);
|
||||
bool shouldBail = DebugAPI::hasExceptionUnwindHook(cx->global());
|
||||
RematerializedFrame* rematFrame = nullptr;
|
||||
if (!shouldBail) {
|
||||
JitActivation* act = cx->activation()->asJit();
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include "builtin/Promise.h"
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "debugger/Debugger.h"
|
||||
#include "frontend/BytecodeCompiler.h"
|
||||
#include "jit/arm/Simulator-arm.h"
|
||||
#include "jit/BaselineIC.h"
|
||||
|
@ -1127,9 +1126,10 @@ bool HandleDebugTrap(JSContext* cx, BaselineFrame* frame, uint8_t* retAddr,
|
|||
// is in step mode or has breakpoints. The Baseline Compiler can toggle
|
||||
// breakpoints more granularly for specific bytecode PCs.
|
||||
if (frame->runningInInterpreter()) {
|
||||
MOZ_ASSERT(script->hasAnyBreakpointsOrStepMode());
|
||||
MOZ_ASSERT(DebugAPI::hasAnyBreakpointsOrStepMode(script));
|
||||
} else {
|
||||
MOZ_ASSERT(script->stepModeEnabled() || script->hasBreakpointsAt(pc));
|
||||
MOZ_ASSERT(DebugAPI::stepModeEnabled(script) ||
|
||||
DebugAPI::hasBreakpointsAt(script, pc));
|
||||
}
|
||||
|
||||
if (*pc == JSOP_AFTERYIELD) {
|
||||
|
@ -1157,11 +1157,12 @@ bool HandleDebugTrap(JSContext* cx, BaselineFrame* frame, uint8_t* retAddr,
|
|||
RootedValue rval(cx);
|
||||
ResumeMode resumeMode = ResumeMode::Continue;
|
||||
|
||||
if (script->stepModeEnabled()) {
|
||||
if (DebugAPI::stepModeEnabled(script)) {
|
||||
resumeMode = DebugAPI::onSingleStep(cx, &rval);
|
||||
}
|
||||
|
||||
if (resumeMode == ResumeMode::Continue && script->hasBreakpointsAt(pc)) {
|
||||
if (resumeMode == ResumeMode::Continue &&
|
||||
DebugAPI::hasBreakpointsAt(script, pc)) {
|
||||
resumeMode = DebugAPI::onTrap(cx, &rval);
|
||||
}
|
||||
|
||||
|
@ -1212,7 +1213,7 @@ bool OnDebuggerStatement(JSContext* cx, BaselineFrame* frame, jsbytecode* pc,
|
|||
bool GlobalHasLiveOnDebuggerStatement(JSContext* cx) {
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
return cx->realm()->isDebuggee() &&
|
||||
Debugger::hasLiveHook(cx->global(), Debugger::OnDebuggerStatement);
|
||||
DebugAPI::hasDebuggerStatementHook(cx->global());
|
||||
}
|
||||
|
||||
bool PushLexicalEnv(JSContext* cx, BaselineFrame* frame,
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "builtin/TypedObject.h"
|
||||
#include "builtin/WeakMapObject.h"
|
||||
#include "builtin/WeakSetObject.h"
|
||||
#include "debugger/Debugger.h"
|
||||
#include "debugger/DebugAPI.h"
|
||||
#include "gc/FreeOp.h"
|
||||
#include "js/ProtoKey.h"
|
||||
#include "vm/DateObject.h"
|
||||
|
@ -841,32 +841,12 @@ bool js::DefineToStringTag(JSContext* cx, HandleObject obj, JSAtom* tag) {
|
|||
return DefineDataProperty(cx, obj, toStringTagId, tagString, JSPROP_READONLY);
|
||||
}
|
||||
|
||||
static void GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj) {
|
||||
MOZ_ASSERT(fop->maybeOnHelperThread());
|
||||
void* ptr = obj->as<NativeObject>().getPrivate();
|
||||
auto debuggers = static_cast<GlobalObject::DebuggerVector*>(ptr);
|
||||
fop->delete_(obj, debuggers, MemoryUse::GlobalDebuggerVector);
|
||||
}
|
||||
|
||||
static const ClassOps GlobalDebuggees_classOps = {nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
GlobalDebuggees_finalize};
|
||||
|
||||
static const Class GlobalDebuggees_class = {
|
||||
"GlobalDebuggee", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
|
||||
&GlobalDebuggees_classOps};
|
||||
|
||||
GlobalObject::DebuggerVector* GlobalObject::getDebuggers() const {
|
||||
Value debuggers = getReservedSlot(DEBUGGERS);
|
||||
if (debuggers.isUndefined()) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(debuggers.toObject().getClass() == &GlobalDebuggees_class);
|
||||
return (DebuggerVector*)debuggers.toObject().as<NativeObject>().getPrivate();
|
||||
return DebugAPI::getGlobalDebuggers(&debuggers.toObject());
|
||||
}
|
||||
|
||||
/* static */ GlobalObject::DebuggerVector* GlobalObject::getOrCreateDebuggers(
|
||||
|
@ -877,18 +857,13 @@ GlobalObject::DebuggerVector* GlobalObject::getDebuggers() const {
|
|||
return debuggers;
|
||||
}
|
||||
|
||||
NativeObject* obj =
|
||||
NewNativeObjectWithGivenProto(cx, &GlobalDebuggees_class, nullptr);
|
||||
JSObject* obj = DebugAPI::newGlobalDebuggersHolder(cx);
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
debuggers = cx->new_<DebuggerVector>(cx->zone());
|
||||
if (!debuggers) {
|
||||
return nullptr;
|
||||
}
|
||||
InitObjectPrivate(obj, debuggers, MemoryUse::GlobalDebuggerVector);
|
||||
|
||||
global->setReservedSlot(DEBUGGERS, ObjectValue(*obj));
|
||||
return debuggers;
|
||||
return global->getDebuggers();
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -1797,7 +1797,7 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
|
|||
JS_BEGIN_MACRO \
|
||||
script = (s); \
|
||||
MOZ_ASSERT(cx->realm() == script->realm()); \
|
||||
if (script->hasAnyBreakpointsOrStepMode() || script->hasScriptCounts()) \
|
||||
if (DebugAPI::hasAnyBreakpointsOrStepMode(script) || script->hasScriptCounts()) \
|
||||
activation.enableInterruptsUnconditionally(); \
|
||||
JS_END_MACRO
|
||||
|
||||
|
@ -1888,7 +1888,7 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
|
|||
}
|
||||
|
||||
if (script->isDebuggee()) {
|
||||
if (script->stepModeEnabled()) {
|
||||
if (DebugAPI::stepModeEnabled(script)) {
|
||||
RootedValue rval(cx);
|
||||
ResumeMode mode = DebugAPI::onSingleStep(cx, &rval);
|
||||
switch (mode) {
|
||||
|
@ -1910,11 +1910,11 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
|
|||
moreInterrupts = true;
|
||||
}
|
||||
|
||||
if (script->hasAnyBreakpointsOrStepMode()) {
|
||||
if (DebugAPI::hasAnyBreakpointsOrStepMode(script)) {
|
||||
moreInterrupts = true;
|
||||
}
|
||||
|
||||
if (script->hasBreakpointsAt(REGS.pc)) {
|
||||
if (DebugAPI::hasBreakpointsAt(script, REGS.pc)) {
|
||||
RootedValue rval(cx);
|
||||
ResumeMode mode = DebugAPI::onTrap(cx, &rval);
|
||||
switch (mode) {
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include "jstypes.h"
|
||||
#include "jsutil.h"
|
||||
|
||||
#include "debugger/Debugger.h"
|
||||
#include "frontend/BytecodeCompiler.h"
|
||||
#include "frontend/BytecodeEmitter.h"
|
||||
#include "frontend/SharedContext.h"
|
||||
|
@ -4249,7 +4248,7 @@ void JSScript::finalize(FreeOp* fop) {
|
|||
jit::DestroyJitScripts(fop, this);
|
||||
|
||||
destroyScriptCounts();
|
||||
destroyDebugScript(fop);
|
||||
DebugAPI::destroyDebugScript(fop, this);
|
||||
|
||||
#ifdef MOZ_VTUNE
|
||||
if (realm()->scriptVTuneIdMap) {
|
||||
|
@ -4795,234 +4794,6 @@ JSScript* js::CloneScriptIntoFunction(
|
|||
return dst;
|
||||
}
|
||||
|
||||
DebugScript* JSScript::debugScript() {
|
||||
MOZ_ASSERT(hasDebugScript());
|
||||
DebugScriptMap* map = realm()->debugScriptMap.get();
|
||||
MOZ_ASSERT(map);
|
||||
DebugScriptMap::Ptr p = map->lookup(this);
|
||||
MOZ_ASSERT(p);
|
||||
return p->value().get();
|
||||
}
|
||||
|
||||
DebugScript* JSScript::releaseDebugScript() {
|
||||
MOZ_ASSERT(hasDebugScript());
|
||||
DebugScriptMap* map = realm()->debugScriptMap.get();
|
||||
MOZ_ASSERT(map);
|
||||
DebugScriptMap::Ptr p = map->lookup(this);
|
||||
MOZ_ASSERT(p);
|
||||
DebugScript* debug = p->value().release();
|
||||
map->remove(p);
|
||||
clearFlag(MutableFlags::HasDebugScript);
|
||||
return debug;
|
||||
}
|
||||
|
||||
void JSScript::destroyDebugScript(FreeOp* fop) {
|
||||
if (hasDebugScript()) {
|
||||
#ifdef DEBUG
|
||||
for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
|
||||
MOZ_ASSERT(!getBreakpointSite(pc));
|
||||
}
|
||||
#endif
|
||||
freeDebugScript(fop);
|
||||
}
|
||||
}
|
||||
|
||||
void JSScript::freeDebugScript(FreeOp* fop) {
|
||||
MOZ_ASSERT(hasDebugScript());
|
||||
fop->free_(this, releaseDebugScript(), DebugScript::allocSize(length()),
|
||||
MemoryUse::ScriptDebugScript);
|
||||
}
|
||||
|
||||
DebugScript* JSScript::getOrCreateDebugScript(JSContext* cx) {
|
||||
if (hasDebugScript()) {
|
||||
return debugScript();
|
||||
}
|
||||
|
||||
size_t nbytes = DebugScript::allocSize(length());
|
||||
UniqueDebugScript debug(
|
||||
reinterpret_cast<DebugScript*>(cx->pod_calloc<uint8_t>(nbytes)));
|
||||
if (!debug) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Create realm's debugScriptMap if necessary. */
|
||||
if (!realm()->debugScriptMap) {
|
||||
auto map = cx->make_unique<DebugScriptMap>();
|
||||
if (!map) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
realm()->debugScriptMap = std::move(map);
|
||||
}
|
||||
|
||||
DebugScript* borrowed = debug.get();
|
||||
if (!realm()->debugScriptMap->putNew(this, std::move(debug))) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
setFlag(MutableFlags::HasDebugScript); // safe to set this; we can't fail
|
||||
// after this point
|
||||
AddCellMemory(this, nbytes, MemoryUse::ScriptDebugScript);
|
||||
|
||||
/*
|
||||
* Ensure that any Interpret() instances running on this script have
|
||||
* interrupts enabled. The interrupts must stay enabled until the
|
||||
* debug state is destroyed.
|
||||
*/
|
||||
for (ActivationIterator iter(cx); !iter.done(); ++iter) {
|
||||
if (iter->isInterpreter()) {
|
||||
iter->asInterpreter()->enableInterruptsIfRunning(this);
|
||||
}
|
||||
}
|
||||
|
||||
return borrowed;
|
||||
}
|
||||
|
||||
bool JSScript::incrementGeneratorObserverCount(JSContext* cx) {
|
||||
cx->check(this);
|
||||
MOZ_ASSERT(cx->realm()->isDebuggee());
|
||||
|
||||
AutoRealm ar(cx, this);
|
||||
|
||||
DebugScript* debug = getOrCreateDebugScript(cx);
|
||||
if (!debug) {
|
||||
return false;
|
||||
}
|
||||
|
||||
debug->generatorObserverCount++;
|
||||
|
||||
// It is our caller's responsibility, before bumping the generator observer
|
||||
// count, to make sure that the baseline code includes the necessary
|
||||
// JS_AFTERYIELD instrumentation by calling
|
||||
// {ensure,update}ExecutionObservabilityOfScript.
|
||||
MOZ_ASSERT_IF(hasBaselineScript(), baseline->hasDebugInstrumentation());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void JSScript::decrementGeneratorObserverCount(js::FreeOp* fop) {
|
||||
DebugScript* debug = debugScript();
|
||||
MOZ_ASSERT(debug);
|
||||
MOZ_ASSERT(debug->generatorObserverCount > 0);
|
||||
|
||||
debug->generatorObserverCount--;
|
||||
|
||||
if (!debug->needed()) {
|
||||
destroyDebugScript(fop);
|
||||
}
|
||||
}
|
||||
|
||||
bool JSScript::incrementStepperCount(JSContext* cx) {
|
||||
cx->check(this);
|
||||
MOZ_ASSERT(cx->realm()->isDebuggee());
|
||||
|
||||
AutoRealm ar(cx, this);
|
||||
|
||||
DebugScript* debug = getOrCreateDebugScript(cx);
|
||||
if (!debug) {
|
||||
return false;
|
||||
}
|
||||
|
||||
debug->stepperCount++;
|
||||
|
||||
if (debug->stepperCount == 1) {
|
||||
if (hasBaselineScript()) {
|
||||
baseline->toggleDebugTraps(this, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void JSScript::decrementStepperCount(FreeOp* fop) {
|
||||
DebugScript* debug = debugScript();
|
||||
MOZ_ASSERT(debug);
|
||||
MOZ_ASSERT(debug->stepperCount > 0);
|
||||
|
||||
debug->stepperCount--;
|
||||
|
||||
if (debug->stepperCount == 0) {
|
||||
if (hasBaselineScript()) {
|
||||
baseline->toggleDebugTraps(this, nullptr);
|
||||
}
|
||||
|
||||
if (!debug->needed()) {
|
||||
freeDebugScript(fop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BreakpointSite* JSScript::getOrCreateBreakpointSite(JSContext* cx,
|
||||
jsbytecode* pc) {
|
||||
AutoRealm ar(cx, this);
|
||||
|
||||
DebugScript* debug = getOrCreateDebugScript(cx);
|
||||
if (!debug) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
|
||||
|
||||
if (!site) {
|
||||
site = cx->new_<JSBreakpointSite>(this, pc);
|
||||
if (!site) {
|
||||
return nullptr;
|
||||
}
|
||||
debug->numSites++;
|
||||
AddCellMemory(this, sizeof(JSBreakpointSite), MemoryUse::BreakpointSite);
|
||||
}
|
||||
|
||||
return site;
|
||||
}
|
||||
|
||||
void JSScript::destroyBreakpointSite(FreeOp* fop, jsbytecode* pc) {
|
||||
DebugScript* debug = debugScript();
|
||||
BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
|
||||
MOZ_ASSERT(site);
|
||||
|
||||
size_t size = site->type() == BreakpointSite::Type::JS
|
||||
? sizeof(JSBreakpointSite)
|
||||
: sizeof(WasmBreakpointSite);
|
||||
fop->delete_(this, site, size, MemoryUse::BreakpointSite);
|
||||
site = nullptr;
|
||||
|
||||
debug->numSites--;
|
||||
if (!debug->needed()) {
|
||||
freeDebugScript(fop);
|
||||
}
|
||||
}
|
||||
|
||||
void JSScript::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg,
|
||||
JSObject* handler) {
|
||||
if (!hasAnyBreakpointsOrStepMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
|
||||
BreakpointSite* site = getBreakpointSite(pc);
|
||||
if (site) {
|
||||
Breakpoint* nextbp;
|
||||
for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
|
||||
nextbp = bp->nextInSite();
|
||||
if ((!dbg || bp->debugger == dbg) &&
|
||||
(!handler || bp->getHandler() == handler)) {
|
||||
bp->destroy(fop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool JSScript::hasBreakpointsAt(jsbytecode* pc) {
|
||||
BreakpointSite* site = getBreakpointSite(pc);
|
||||
if (!site) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return site->enabledCount > 0;
|
||||
}
|
||||
|
||||
/* static */ bool ImmutableScriptData::InitFromEmitter(
|
||||
JSContext* cx, js::HandleScript script, frontend::BytecodeEmitter* bce,
|
||||
uint32_t nslots) {
|
||||
|
|
|
@ -68,14 +68,14 @@ class JitScript;
|
|||
#define BASELINE_DISABLED_SCRIPT ((js::jit::BaselineScript*)0x1)
|
||||
|
||||
class AutoSweepJitScript;
|
||||
class BreakpointSite;
|
||||
class Debugger;
|
||||
class GCParallelTask;
|
||||
class LazyScript;
|
||||
class ModuleObject;
|
||||
class RegExpObject;
|
||||
class SourceCompressionTask;
|
||||
class Shape;
|
||||
class DebugAPI;
|
||||
class DebugScript;
|
||||
|
||||
namespace frontend {
|
||||
struct BytecodeEmitter;
|
||||
|
@ -253,58 +253,6 @@ using ScriptVTuneIdMap =
|
|||
HashMap<JSScript*, uint32_t, DefaultHasher<JSScript*>, SystemAllocPolicy>;
|
||||
#endif
|
||||
|
||||
class DebugScript {
|
||||
friend class ::JSScript;
|
||||
friend class JS::Realm;
|
||||
|
||||
/*
|
||||
* If this is a generator script, this is the number of Debugger.Frames
|
||||
* referring to calls to this generator, whether live or suspended. Closed
|
||||
* generators do not contribute a count.
|
||||
*
|
||||
* When greater than zero, this script should be compiled with debug
|
||||
* instrumentation to call Debugger::onResumeFrame at each resumption site, so
|
||||
* that Debugger can reconnect any extant Debugger.Frames with the new
|
||||
* concrete frame.
|
||||
*/
|
||||
uint32_t generatorObserverCount;
|
||||
|
||||
/*
|
||||
* The number of Debugger.Frame objects that refer to frames running this
|
||||
* script and that have onStep handlers. When nonzero, the interpreter and JIT
|
||||
* must arrange to call Debugger::onSingleStep before each bytecode, or at
|
||||
* least at some useful granularity.
|
||||
*/
|
||||
uint32_t stepperCount;
|
||||
|
||||
/*
|
||||
* Number of breakpoint sites at opcodes in the script. This is the number
|
||||
* of populated entries in DebugScript::breakpoints, below.
|
||||
*/
|
||||
uint32_t numSites;
|
||||
|
||||
/*
|
||||
* Breakpoints set in our script. For speed and simplicity, this array is
|
||||
* parallel to script->code(): the BreakpointSite for the opcode at
|
||||
* script->code()[offset] is debugScript->breakpoints[offset]. Naturally,
|
||||
* this array's true length is script->length().
|
||||
*/
|
||||
BreakpointSite* breakpoints[1];
|
||||
|
||||
/*
|
||||
* True if this DebugScript carries any useful information. If false, it
|
||||
* should be removed from its JSScript.
|
||||
*/
|
||||
bool needed() const {
|
||||
return generatorObserverCount > 0 || stepperCount > 0 || numSites > 0;
|
||||
}
|
||||
|
||||
static size_t allocSize(size_t codeLength) {
|
||||
return offsetof(DebugScript, breakpoints) +
|
||||
codeLength * sizeof(BreakpointSite*);
|
||||
}
|
||||
};
|
||||
|
||||
using UniqueDebugScript = js::UniquePtr<DebugScript, JS::FreePolicy>;
|
||||
using DebugScriptMap = HashMap<JSScript*, UniqueDebugScript,
|
||||
DefaultHasher<JSScript*>, SystemAllocPolicy>;
|
||||
|
@ -3104,65 +3052,14 @@ class JSScript : public js::BaseScript {
|
|||
bool formalIsAliased(unsigned argSlot);
|
||||
bool formalLivesInArgumentsObject(unsigned argSlot);
|
||||
|
||||
private:
|
||||
/* Change this->stepMode to |newValue|. */
|
||||
void setNewStepMode(js::FreeOp* fop, uint32_t newValue);
|
||||
|
||||
js::DebugScript* getOrCreateDebugScript(JSContext* cx);
|
||||
js::DebugScript* debugScript();
|
||||
js::DebugScript* releaseDebugScript();
|
||||
void destroyDebugScript(js::FreeOp* fop);
|
||||
void freeDebugScript(js::FreeOp* fop);
|
||||
|
||||
bool hasDebugScript() const { return hasFlag(MutableFlags::HasDebugScript); }
|
||||
|
||||
public:
|
||||
bool hasBreakpointsAt(jsbytecode* pc);
|
||||
bool hasAnyBreakpointsOrStepMode() { return hasDebugScript(); }
|
||||
|
||||
// See comment above 'debugMode' in Realm.h for explanation of
|
||||
// invariants of debuggee compartments, scripts, and frames.
|
||||
inline bool isDebuggee() const;
|
||||
|
||||
js::BreakpointSite* getBreakpointSite(jsbytecode* pc) {
|
||||
return hasDebugScript() ? debugScript()->breakpoints[pcToOffset(pc)]
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
js::BreakpointSite* getOrCreateBreakpointSite(JSContext* cx, jsbytecode* pc);
|
||||
|
||||
void destroyBreakpointSite(js::FreeOp* fop, jsbytecode* pc);
|
||||
|
||||
void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg,
|
||||
JSObject* handler);
|
||||
|
||||
/*
|
||||
* Increment or decrement the single-step count. If the count is non-zero
|
||||
* then the script is in single-step mode.
|
||||
*
|
||||
* Only incrementing is fallible, as it could allocate a DebugScript.
|
||||
*/
|
||||
bool incrementStepperCount(JSContext* cx);
|
||||
void decrementStepperCount(js::FreeOp* fop);
|
||||
|
||||
bool stepModeEnabled() {
|
||||
return hasDebugScript() && debugScript()->stepperCount > 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t stepperCount() {
|
||||
return hasDebugScript() ? debugScript()->stepperCount : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Increment or decrement the generator observer count. If the count is
|
||||
* non-zero then the script reports resumptions to the debugger.
|
||||
*
|
||||
* Only incrementing is fallible, as it could allocate a DebugScript.
|
||||
*/
|
||||
bool incrementGeneratorObserverCount(JSContext* cx);
|
||||
void decrementGeneratorObserverCount(js::FreeOp* fop);
|
||||
// Access the flag for whether this script has a DebugScript in its realm's
|
||||
// map. This should only be used by the DebugScript class.
|
||||
bool hasDebugScript() const { return hasFlag(MutableFlags::HasDebugScript); }
|
||||
void setHasDebugScript(bool b) { setFlag(MutableFlags::HasDebugScript, b); }
|
||||
|
||||
void finalize(js::FreeOp* fop);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include "debugger/Debugger.h"
|
||||
#include "debugger/DebugAPI.h"
|
||||
#include "gc/Policy.h"
|
||||
#include "gc/PublicIterators.h"
|
||||
#include "jit/JitOptions.h"
|
||||
|
@ -565,12 +565,7 @@ void Realm::checkScriptMapsAfterMovingGC() {
|
|||
MOZ_ASSERT(script->realm() == this);
|
||||
CheckGCThingAfterMovingGC(script);
|
||||
DebugScript* ds = r.front().value().get();
|
||||
for (uint32_t i = 0; i < ds->numSites; i++) {
|
||||
BreakpointSite* site = ds->breakpoints[i];
|
||||
if (site && site->type() == BreakpointSite::Type::JS) {
|
||||
CheckGCThingAfterMovingGC(site->asJS()->script);
|
||||
}
|
||||
}
|
||||
DebugAPI::checkDebugScriptAfterMovingGC(ds);
|
||||
auto ptr = debugScriptMap->lookup(script);
|
||||
MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
|
||||
}
|
||||
|
@ -775,23 +770,21 @@ void Realm::updateDebuggerObservesFlag(unsigned flag) {
|
|||
zone()->runtimeFromMainThread()->gc.isForegroundSweeping()
|
||||
? unsafeUnbarrieredMaybeGlobal()
|
||||
: maybeGlobal();
|
||||
const GlobalObject::DebuggerVector* v = global->getDebuggers();
|
||||
for (auto p = v->begin(); p != v->end(); p++) {
|
||||
// Use unbarrieredGet() to prevent triggering read barrier while collecting,
|
||||
// this is safe as long as dbg does not escape.
|
||||
Debugger* dbg = p->unbarrieredGet();
|
||||
if (flag == DebuggerObservesAllExecution
|
||||
? dbg->observesAllExecution()
|
||||
: flag == DebuggerObservesCoverage
|
||||
? dbg->observesCoverage()
|
||||
: flag == DebuggerObservesAsmJS && dbg->observesAsmJS()) {
|
||||
debugModeBits_ |= flag;
|
||||
return;
|
||||
}
|
||||
bool observes = false;
|
||||
if (flag == DebuggerObservesAllExecution) {
|
||||
observes = DebugAPI::debuggerObservesAllExecution(global);
|
||||
} else if (flag == DebuggerObservesCoverage) {
|
||||
observes = DebugAPI::debuggerObservesCoverage(global);
|
||||
} else if (flag == DebuggerObservesAsmJS) {
|
||||
observes = DebugAPI::debuggerObservesAsmJS(global);
|
||||
}
|
||||
|
||||
if (observes) {
|
||||
debugModeBits_ |= flag;
|
||||
} else {
|
||||
debugModeBits_ &= ~flag;
|
||||
}
|
||||
}
|
||||
|
||||
void Realm::setIsDebuggee() {
|
||||
if (!isDebuggee()) {
|
||||
|
@ -871,16 +864,6 @@ void Realm::clearScriptCounts() {
|
|||
|
||||
void Realm::clearScriptNames() { scriptNameMap.reset(); }
|
||||
|
||||
void Realm::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg,
|
||||
HandleObject handler) {
|
||||
for (auto script = zone()->cellIter<JSScript>(); !script.done();
|
||||
script.next()) {
|
||||
if (script->realm() == this && script->hasAnyBreakpointsOrStepMode()) {
|
||||
script->clearBreakpointsIn(fop, dbg, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectRealm::addSizeOfExcludingThis(
|
||||
mozilla::MallocSizeOf mallocSizeOf, size_t* innerViewsArg,
|
||||
size_t* lazyArrayBuffersArg, size_t* objectMetadataTablesArg,
|
||||
|
|
|
@ -768,9 +768,6 @@ class JS::Realm : public JS::shadow::Realm {
|
|||
// scripts.
|
||||
bool ensureDelazifyScriptsForDebugger(JSContext* cx);
|
||||
|
||||
void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg,
|
||||
JS::HandleObject handler);
|
||||
|
||||
// Initializes randomNumberGenerator if needed.
|
||||
mozilla::non_crypto::XorShift128PlusRNG& getOrCreateRandomNumberGenerator();
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "jsmath.h"
|
||||
|
||||
#include "builtin/Promise.h"
|
||||
#include "debugger/DebugAPI.h"
|
||||
#include "gc/FreeOp.h"
|
||||
#include "gc/GCInternals.h"
|
||||
#include "gc/PublicIterators.h"
|
||||
|
@ -53,6 +52,7 @@
|
|||
#include "vm/TraceLoggingGraph.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
|
||||
#include "debugger/DebugAPI-inl.h"
|
||||
#include "gc/GC-inl.h"
|
||||
#include "vm/JSContext-inl.h"
|
||||
#include "vm/Realm-inl.h"
|
||||
|
@ -434,7 +434,7 @@ static bool HandleInterrupt(JSContext* cx, bool invokeCallback) {
|
|||
if (cx->realm()->isDebuggee()) {
|
||||
ScriptFrameIter iter(cx);
|
||||
if (!iter.done() && cx->compartment() == iter.compartment() &&
|
||||
iter.script()->stepModeEnabled()) {
|
||||
DebugAPI::stepModeEnabled(iter.script())) {
|
||||
RootedValue rval(cx);
|
||||
switch (DebugAPI::onSingleStep(cx, &rval)) {
|
||||
case ResumeMode::Terminate:
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "jsmath.h"
|
||||
#include "jsnum.h"
|
||||
|
||||
#include "debugger/Debugger.h"
|
||||
#include "gc/FreeOp.h"
|
||||
#include "gc/HashUtil.h"
|
||||
#include "gc/Marking.h"
|
||||
|
@ -1816,31 +1815,12 @@ void SavedStacks::chooseSamplingProbability(Realm* realm) {
|
|||
return;
|
||||
}
|
||||
|
||||
GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
|
||||
if (!dbgs || dbgs->empty()) {
|
||||
Maybe<double> probability = DebugAPI::allocationSamplingProbability(global);
|
||||
if (probability.isNothing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mozilla::DebugOnly<WeakHeapPtr<Debugger*>*> begin = dbgs->begin();
|
||||
mozilla::DebugOnly<bool> foundAnyDebuggers = false;
|
||||
|
||||
double probability = 0;
|
||||
for (auto p = dbgs->begin(); p < dbgs->end(); p++) {
|
||||
// The set of debuggers had better not change while we're iterating,
|
||||
// such that the vector gets reallocated.
|
||||
MOZ_ASSERT(dbgs->begin() == begin);
|
||||
// Use unbarrieredGet() to prevent triggering read barrier while collecting,
|
||||
// this is safe as long as dbgp does not escape.
|
||||
Debugger* dbgp = p->unbarrieredGet();
|
||||
|
||||
if (dbgp->trackingAllocationSites && dbgp->enabled) {
|
||||
foundAnyDebuggers = true;
|
||||
probability = std::max(dbgp->allocationSamplingProbability, probability);
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(foundAnyDebuggers);
|
||||
|
||||
this->setSamplingProbability(probability);
|
||||
this->setSamplingProbability(*probability);
|
||||
}
|
||||
|
||||
void SavedStacks::setSamplingProbability(double probability) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче