summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Debugger.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/Debugger.h')
-rw-r--r--js/src/vm/Debugger.h1584
1 files changed, 1584 insertions, 0 deletions
diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h
new file mode 100644
index 000000000..3239ade6d
--- /dev/null
+++ b/js/src/vm/Debugger.h
@@ -0,0 +1,1584 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 vm_Debugger_h
+#define vm_Debugger_h
+
+#include "mozilla/GuardObjects.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/Range.h"
+#include "mozilla/Vector.h"
+
+#include "jsclist.h"
+#include "jscntxt.h"
+#include "jscompartment.h"
+#include "jsweakmap.h"
+#include "jswrapper.h"
+
+#include "builtin/Promise.h"
+#include "ds/TraceableFifo.h"
+#include "gc/Barrier.h"
+#include "js/Debug.h"
+#include "js/GCVariant.h"
+#include "js/HashTable.h"
+#include "vm/GlobalObject.h"
+#include "vm/SavedStacks.h"
+#include "wasm/WasmJS.h"
+
+enum JSTrapStatus {
+ JSTRAP_ERROR,
+ JSTRAP_CONTINUE,
+ JSTRAP_RETURN,
+ JSTRAP_THROW,
+ JSTRAP_LIMIT
+};
+
+namespace js {
+
+class Breakpoint;
+class DebuggerMemory;
+class WasmInstanceObject;
+
+typedef HashSet<ReadBarrieredGlobalObject,
+ MovableCellHasher<ReadBarrieredGlobalObject>,
+ RuntimeAllocPolicy> WeakGlobalObjectSet;
+
+/*
+ * A weakmap from GC thing keys to JSObject values that supports the keys being
+ * in different compartments to the values. All values must be in the same
+ * compartment.
+ *
+ * The purpose of this is to allow the garbage collector to easily find edges
+ * from debuggee object compartments to debugger compartments when calculating
+ * the compartment groups. Note that these edges are the inverse of the edges
+ * stored in the cross compartment map.
+ *
+ * The current implementation results in all debuggee object compartments being
+ * swept in the same group as the debugger. This is a conservative approach,
+ * and compartments may be unnecessarily grouped, however it results in a
+ * simpler and faster implementation.
+ *
+ * If InvisibleKeysOk is true, then the map can have keys in invisible-to-
+ * debugger compartments. If it is false, we assert that such entries are never
+ * created.
+ *
+ * Also note that keys in these weakmaps can be in any compartment, debuggee or
+ * not, because they cannot be deleted when a compartment is no longer a
+ * debuggee: the values need to maintain object identity across add/remove/add
+ * transitions.
+ */
+template <class UnbarrieredKey, bool InvisibleKeysOk=false>
+class DebuggerWeakMap : private WeakMap<HeapPtr<UnbarrieredKey>, HeapPtr<JSObject*>,
+ MovableCellHasher<HeapPtr<UnbarrieredKey>>>
+{
+ private:
+ typedef HeapPtr<UnbarrieredKey> Key;
+ typedef HeapPtr<JSObject*> Value;
+
+ typedef HashMap<JS::Zone*,
+ uintptr_t,
+ DefaultHasher<JS::Zone*>,
+ RuntimeAllocPolicy> CountMap;
+
+ CountMap zoneCounts;
+ JSCompartment* compartment;
+
+ public:
+ typedef WeakMap<Key, Value, MovableCellHasher<Key>> Base;
+
+ explicit DebuggerWeakMap(JSContext* cx)
+ : Base(cx),
+ zoneCounts(cx->runtime()),
+ compartment(cx->compartment())
+ { }
+
+ public:
+ /* Expose those parts of HashMap public interface that are used by Debugger methods. */
+
+ typedef typename Base::Entry Entry;
+ typedef typename Base::Ptr Ptr;
+ typedef typename Base::AddPtr AddPtr;
+ typedef typename Base::Range Range;
+ typedef typename Base::Enum Enum;
+ typedef typename Base::Lookup Lookup;
+
+ /* Expose WeakMap public interface */
+
+ using Base::lookupForAdd;
+ using Base::all;
+ using Base::trace;
+
+ MOZ_MUST_USE bool init(uint32_t len = 16) {
+ return Base::init(len) && zoneCounts.init();
+ }
+
+ template<typename KeyInput, typename ValueInput>
+ bool relookupOrAdd(AddPtr& p, const KeyInput& k, const ValueInput& v) {
+ MOZ_ASSERT(v->compartment() == this->compartment);
+ MOZ_ASSERT(!k->compartment()->creationOptions().mergeable());
+ MOZ_ASSERT_IF(!InvisibleKeysOk,
+ !k->compartment()->creationOptions().invisibleToDebugger());
+ MOZ_ASSERT(!Base::has(k));
+ if (!incZoneCount(k->zone()))
+ return false;
+ bool ok = Base::relookupOrAdd(p, k, v);
+ if (!ok)
+ decZoneCount(k->zone());
+ return ok;
+ }
+
+ void remove(const Lookup& l) {
+ MOZ_ASSERT(Base::has(l));
+ Base::remove(l);
+ decZoneCount(l->zone());
+ }
+
+ public:
+ template <void (traceValueEdges)(JSTracer*, JSObject*)>
+ void markCrossCompartmentEdges(JSTracer* tracer) {
+ for (Enum e(*static_cast<Base*>(this)); !e.empty(); e.popFront()) {
+ traceValueEdges(tracer, e.front().value());
+ Key key = e.front().key();
+ TraceEdge(tracer, &key, "Debugger WeakMap key");
+ if (key != e.front().key())
+ e.rekeyFront(key);
+ key.unsafeSet(nullptr);
+ }
+ }
+
+ bool hasKeyInZone(JS::Zone* zone) {
+ CountMap::Ptr p = zoneCounts.lookup(zone);
+ MOZ_ASSERT_IF(p.found(), p->value() > 0);
+ return p.found();
+ }
+
+ private:
+ /* Override sweep method to also update our edge cache. */
+ void sweep() {
+ for (Enum e(*static_cast<Base*>(this)); !e.empty(); e.popFront()) {
+ if (gc::IsAboutToBeFinalized(&e.front().mutableKey())) {
+ decZoneCount(e.front().key()->zone());
+ e.removeFront();
+ }
+ }
+ Base::assertEntriesNotAboutToBeFinalized();
+ }
+
+ MOZ_MUST_USE bool incZoneCount(JS::Zone* zone) {
+ CountMap::Ptr p = zoneCounts.lookupWithDefault(zone, 0);
+ if (!p)
+ return false;
+ ++p->value();
+ return true;
+ }
+
+ void decZoneCount(JS::Zone* zone) {
+ CountMap::Ptr p = zoneCounts.lookup(zone);
+ MOZ_ASSERT(p);
+ MOZ_ASSERT(p->value() > 0);
+ --p->value();
+ if (p->value() == 0)
+ zoneCounts.remove(zone);
+ }
+};
+
+class LeaveDebuggeeNoExecute;
+
+// Suppresses all debuggee NX checks, i.e., allow all execution. Used to allow
+// certain whitelisted operations to execute code.
+//
+// WARNING
+// WARNING Do not use this unless you know what you are doing!
+// WARNING
+class AutoSuppressDebuggeeNoExecuteChecks
+{
+ EnterDebuggeeNoExecute** stack_;
+ EnterDebuggeeNoExecute* prev_;
+
+ public:
+ explicit AutoSuppressDebuggeeNoExecuteChecks(JSContext* cx) {
+ stack_ = &cx->runtime()->noExecuteDebuggerTop;
+ prev_ = *stack_;
+ *stack_ = nullptr;
+ }
+
+ ~AutoSuppressDebuggeeNoExecuteChecks() {
+ MOZ_ASSERT(!*stack_);
+ *stack_ = prev_;
+ }
+};
+
+class MOZ_RAII EvalOptions {
+ const char* filename_;
+ unsigned lineno_;
+
+ public:
+ EvalOptions() : filename_(nullptr), lineno_(1) {}
+ ~EvalOptions();
+ const char* filename() const { return filename_; }
+ unsigned lineno() const { return lineno_; }
+ MOZ_MUST_USE bool setFilename(JSContext* cx, const char* filename);
+ void setLineno(unsigned lineno) { lineno_ = lineno; }
+};
+
+/*
+ * Env is the type of what ES5 calls "lexical environments" (runtime activations
+ * of lexical scopes). This is currently just JSObject, and is implemented by
+ * CallObject, LexicalEnvironmentObject, and WithEnvironmentObject, among
+ * others--but environments and objects are really two different concepts.
+ */
+typedef JSObject Env;
+
+// Either a real JSScript or synthesized.
+//
+// If synthesized, the referent is one of the following:
+//
+// 1. A WasmInstanceObject, denoting a synthesized toplevel wasm module
+// script.
+// 2. A wasm JSFunction, denoting a synthesized wasm function script.
+// NYI!
+typedef mozilla::Variant<JSScript*, WasmInstanceObject*> DebuggerScriptReferent;
+
+// Either a ScriptSourceObject, for ordinary JS, or a WasmInstanceObject,
+// denoting the synthesized source of a wasm module.
+typedef mozilla::Variant<ScriptSourceObject*, WasmInstanceObject*> DebuggerSourceReferent;
+
+class Debugger : private mozilla::LinkedListElement<Debugger>
+{
+ friend class Breakpoint;
+ friend class DebuggerMemory;
+ friend class SavedStacks;
+ friend class mozilla::LinkedListElement<Debugger>;
+ friend class mozilla::LinkedList<Debugger>;
+ friend bool (::JS_DefineDebuggerObject)(JSContext* cx, JS::HandleObject obj);
+ friend bool (::JS::dbg::IsDebugger)(JSObject&);
+ friend bool (::JS::dbg::GetDebuggeeGlobals)(JSContext*, JSObject&, AutoObjectVector&);
+ friend void JS::dbg::onNewPromise(JSContext* cx, HandleObject promise);
+ friend void JS::dbg::onPromiseSettled(JSContext* cx, HandleObject promise);
+ friend bool JS::dbg::FireOnGarbageCollectionHook(JSContext* cx,
+ JS::dbg::GarbageCollectionEvent::Ptr&& data);
+
+ public:
+ enum Hook {
+ OnDebuggerStatement,
+ OnExceptionUnwind,
+ OnNewScript,
+ OnEnterFrame,
+ OnNewGlobalObject,
+ OnNewPromise,
+ OnPromiseSettled,
+ OnGarbageCollection,
+ HookCount
+ };
+ enum {
+ JSSLOT_DEBUG_PROTO_START,
+ JSSLOT_DEBUG_FRAME_PROTO = JSSLOT_DEBUG_PROTO_START,
+ JSSLOT_DEBUG_ENV_PROTO,
+ JSSLOT_DEBUG_OBJECT_PROTO,
+ JSSLOT_DEBUG_SCRIPT_PROTO,
+ JSSLOT_DEBUG_SOURCE_PROTO,
+ JSSLOT_DEBUG_MEMORY_PROTO,
+ JSSLOT_DEBUG_PROTO_STOP,
+ JSSLOT_DEBUG_HOOK_START = JSSLOT_DEBUG_PROTO_STOP,
+ JSSLOT_DEBUG_HOOK_STOP = JSSLOT_DEBUG_HOOK_START + HookCount,
+ JSSLOT_DEBUG_MEMORY_INSTANCE = JSSLOT_DEBUG_HOOK_STOP,
+ JSSLOT_DEBUG_COUNT
+ };
+
+ class ExecutionObservableSet
+ {
+ public:
+ typedef HashSet<Zone*>::Range ZoneRange;
+
+ virtual Zone* singleZone() const { return nullptr; }
+ virtual JSScript* singleScriptForZoneInvalidation() const { return nullptr; }
+ virtual const HashSet<Zone*>* zones() const { return nullptr; }
+
+ virtual bool shouldRecompileOrInvalidate(JSScript* script) const = 0;
+ virtual bool shouldMarkAsDebuggee(ScriptFrameIter& iter) const = 0;
+ };
+
+ // This enum is converted to and compare with bool values; NotObserving
+ // must be 0 and Observing must be 1.
+ enum IsObserving {
+ NotObserving = 0,
+ Observing = 1
+ };
+
+ // Return true if the given compartment is a debuggee of this debugger,
+ // false otherwise.
+ bool isDebuggeeUnbarriered(const JSCompartment* compartment) const;
+
+ // Return true if this Debugger observed a debuggee that participated in the
+ // GC identified by the given GC number. Return false otherwise.
+ // May return false negatives if we have hit OOM.
+ bool observedGC(uint64_t majorGCNumber) const {
+ return observedGCs.has(majorGCNumber);
+ }
+
+ // Notify this Debugger that one or more of its debuggees is participating
+ // in the GC identified by the given GC number.
+ bool debuggeeIsBeingCollected(uint64_t majorGCNumber) {
+ return observedGCs.put(majorGCNumber);
+ }
+
+ bool isEnabled() const {
+ return enabled;
+ }
+
+ static SavedFrame* getObjectAllocationSite(JSObject& obj);
+
+ struct AllocationsLogEntry
+ {
+ AllocationsLogEntry(HandleObject frame, double when, const char* className,
+ HandleAtom ctorName, size_t size, bool inNursery)
+ : frame(frame),
+ when(when),
+ className(className),
+ ctorName(ctorName),
+ size(size),
+ inNursery(inNursery)
+ {
+ MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>());
+ };
+
+ HeapPtr<JSObject*> frame;
+ double when;
+ const char* className;
+ HeapPtr<JSAtom*> ctorName;
+ size_t size;
+ bool inNursery;
+
+ void trace(JSTracer* trc) {
+ TraceNullableEdge(trc, &frame, "Debugger::AllocationsLogEntry::frame");
+ TraceNullableEdge(trc, &ctorName, "Debugger::AllocationsLogEntry::ctorName");
+ }
+ };
+
+ // Barrier methods so we can have ReadBarriered<Debugger*>.
+ static void readBarrier(Debugger* dbg) {
+ InternalBarrierMethods<JSObject*>::readBarrier(dbg->object);
+ }
+ static void writeBarrierPost(Debugger** vp, Debugger* prev, Debugger* next) {}
+
+ private:
+ GCPtrNativeObject object; /* The Debugger object. Strong reference. */
+ WeakGlobalObjectSet debuggees; /* Debuggee globals. Cross-compartment weak references. */
+ JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */
+ js::GCPtrObject uncaughtExceptionHook; /* Strong reference. */
+ bool enabled;
+ bool allowUnobservedAsmJS;
+
+ // Whether to enable code coverage on the Debuggee.
+ bool collectCoverageInfo;
+
+ JSCList breakpoints; /* Circular list of all js::Breakpoints in this debugger */
+
+ // The set of GC numbers for which one or more of this Debugger's observed
+ // debuggees participated in.
+ using GCNumberSet = HashSet<uint64_t, DefaultHasher<uint64_t>, RuntimeAllocPolicy>;
+ GCNumberSet observedGCs;
+
+ using AllocationsLog = js::TraceableFifo<AllocationsLogEntry>;
+
+ AllocationsLog allocationsLog;
+ bool trackingAllocationSites;
+ double allocationSamplingProbability;
+ size_t maxAllocationsLogLength;
+ bool allocationsLogOverflowed;
+
+ static const size_t DEFAULT_MAX_LOG_LENGTH = 5000;
+
+ MOZ_MUST_USE bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
+ double when);
+
+ /*
+ * Recompute the set of debuggee zones based on the set of debuggee globals.
+ */
+ void recomputeDebuggeeZoneSet();
+
+ /*
+ * Return true if there is an existing object metadata callback for the
+ * given global's compartment that will prevent our instrumentation of
+ * allocations.
+ */
+ static bool cannotTrackAllocations(const GlobalObject& global);
+
+ /*
+ * Return true if the given global is being observed by at least one
+ * Debugger that is tracking allocations.
+ */
+ static bool isObservedByDebuggerTrackingAllocations(const GlobalObject& global);
+
+ /*
+ * Add allocations tracking for objects allocated within the given
+ * debuggee's compartment. The given debuggee global must be observed by at
+ * least one Debugger that is enabled and tracking allocations.
+ */
+ static MOZ_MUST_USE bool addAllocationsTracking(JSContext* cx, Handle<GlobalObject*> debuggee);
+
+ /*
+ * Remove allocations tracking for objects allocated within the given
+ * global's compartment. This is a no-op if there are still Debuggers
+ * observing this global and who are tracking allocations.
+ */
+ static void removeAllocationsTracking(GlobalObject& global);
+
+ /*
+ * Add or remove allocations tracking for all debuggees.
+ */
+ MOZ_MUST_USE bool addAllocationsTrackingForAllDebuggees(JSContext* cx);
+ void removeAllocationsTrackingForAllDebuggees();
+
+ /*
+ * If this Debugger is enabled, and has a onNewGlobalObject handler, then
+ * this link is inserted into the circular list headed by
+ * JSRuntime::onNewGlobalObjectWatchers. Otherwise, this is set to a
+ * singleton cycle.
+ */
+ JSCList onNewGlobalObjectWatchersLink;
+
+ /*
+ * Map from stack frames that are currently on the stack to Debugger.Frame
+ * instances.
+ *
+ * The keys are always live stack frames. We drop them from this map as
+ * soon as they leave the stack (see slowPathOnLeaveFrame) and in
+ * removeDebuggee.
+ *
+ * We don't trace the keys of this map (the frames are on the stack and
+ * thus necessarily live), but we do trace the values. It's like a WeakMap
+ * that way, but since stack frames are not gc-things, the implementation
+ * has to be different.
+ */
+ typedef HashMap<AbstractFramePtr,
+ HeapPtr<DebuggerFrame*>,
+ DefaultHasher<AbstractFramePtr>,
+ RuntimeAllocPolicy> FrameMap;
+ FrameMap frames;
+
+ /* An ephemeral map from JSScript* to Debugger.Script instances. */
+ typedef DebuggerWeakMap<JSScript*> ScriptWeakMap;
+ ScriptWeakMap scripts;
+
+ /* The map from debuggee source script objects to their Debugger.Source instances. */
+ typedef DebuggerWeakMap<JSObject*, true> SourceWeakMap;
+ SourceWeakMap sources;
+
+ /* The map from debuggee objects to their Debugger.Object instances. */
+ typedef DebuggerWeakMap<JSObject*> ObjectWeakMap;
+ ObjectWeakMap objects;
+
+ /* The map from debuggee Envs to Debugger.Environment instances. */
+ ObjectWeakMap environments;
+
+ /* The map from WasmInstanceObjects to synthesized Debugger.Script instances. */
+ typedef DebuggerWeakMap<WasmInstanceObject*> WasmInstanceWeakMap;
+ WasmInstanceWeakMap wasmInstanceScripts;
+
+ /* The map from WasmInstanceObjects to synthesized Debugger.Source instances. */
+ WasmInstanceWeakMap wasmInstanceSources;
+
+ /*
+ * Keep track of tracelogger last drained identifiers to know if there are
+ * lost events.
+ */
+#ifdef NIGHTLY_BUILD
+ uint32_t traceLoggerLastDrainedSize;
+ uint32_t traceLoggerLastDrainedIteration;
+#endif
+ uint32_t traceLoggerScriptedCallsLastDrainedSize;
+ uint32_t traceLoggerScriptedCallsLastDrainedIteration;
+
+ class ScriptQuery;
+ class ObjectQuery;
+
+ MOZ_MUST_USE bool addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> obj);
+ void removeDebuggeeGlobal(FreeOp* fop, GlobalObject* global,
+ WeakGlobalObjectSet::Enum* debugEnum);
+
+ /*
+ * Report and clear the pending exception on ac.context, if any, and return
+ * JSTRAP_ERROR.
+ */
+ JSTrapStatus reportUncaughtException(mozilla::Maybe<AutoCompartment>& ac);
+
+ /*
+ * Cope with an error or exception in a debugger hook.
+ *
+ * If callHook is true, then call the uncaughtExceptionHook, if any. If, in
+ * addition, vp is given, then parse the value returned by
+ * uncaughtExceptionHook as a resumption value.
+ *
+ * If there is no uncaughtExceptionHook, or if it fails, report and clear
+ * the pending exception on ac.context and return JSTRAP_ERROR.
+ *
+ * This always calls ac.leave(); ac is a parameter because this method must
+ * do some things in the debugger compartment and some things in the
+ * debuggee compartment.
+ */
+ JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment>& ac);
+ JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment>& ac,
+ MutableHandleValue vp,
+ const mozilla::Maybe<HandleValue>& thisVForCheck = mozilla::Nothing(),
+ AbstractFramePtr frame = NullFramePtr());
+
+ JSTrapStatus handleUncaughtExceptionHelper(mozilla::Maybe<AutoCompartment>& ac,
+ MutableHandleValue* vp,
+ const mozilla::Maybe<HandleValue>& thisVForCheck,
+ AbstractFramePtr frame);
+
+ /*
+ * Handle the result of a hook that is expected to return a resumption
+ * value <https://wiki.mozilla.org/Debugger#Resumption_Values>. This is called
+ * when we return from a debugging hook to debuggee code. The interpreter wants
+ * a (JSTrapStatus, Value) pair telling it how to proceed.
+ *
+ * Precondition: ac is entered. We are in the debugger compartment.
+ *
+ * Postcondition: This called ac.leave(). See handleUncaughtException.
+ *
+ * If ok is false, the hook failed. If an exception is pending in
+ * ac.context(), return handleUncaughtException(ac, vp, callhook).
+ * Otherwise just return JSTRAP_ERROR.
+ *
+ * If ok is true, there must be no exception pending in ac.context(). rv may be:
+ * undefined - Return JSTRAP_CONTINUE to continue execution normally.
+ * {return: value} or {throw: value} - Call unwrapDebuggeeValue to
+ * unwrap value. Store the result in *vp and return JSTRAP_RETURN
+ * or JSTRAP_THROW. The interpreter will force the current frame to
+ * return or throw an exception.
+ * null - Return JSTRAP_ERROR to terminate the debuggee with an
+ * uncatchable error.
+ * anything else - Make a new TypeError the pending exception and
+ * return handleUncaughtException(ac, vp, callHook).
+ */
+ JSTrapStatus processHandlerResult(mozilla::Maybe<AutoCompartment>& ac, bool OK, const Value& rv,
+ AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp);
+
+ JSTrapStatus processParsedHandlerResult(mozilla::Maybe<AutoCompartment>& ac,
+ AbstractFramePtr frame, jsbytecode* pc,
+ bool success, JSTrapStatus status,
+ MutableHandleValue vp);
+
+ JSTrapStatus processParsedHandlerResultHelper(mozilla::Maybe<AutoCompartment>& ac,
+ AbstractFramePtr frame,
+ const mozilla::Maybe<HandleValue>& maybeThisv,
+ bool success, JSTrapStatus status,
+ MutableHandleValue vp);
+
+ bool processResumptionValue(mozilla::Maybe<AutoCompartment>& ac, AbstractFramePtr frame,
+ const mozilla::Maybe<HandleValue>& maybeThis, HandleValue rval,
+ JSTrapStatus& statusp, MutableHandleValue vp);
+
+ GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
+
+ static void traceObject(JSTracer* trc, JSObject* obj);
+ void trace(JSTracer* trc);
+ static void finalize(FreeOp* fop, JSObject* obj);
+ void markCrossCompartmentEdges(JSTracer* tracer);
+
+ static const ClassOps classOps_;
+
+ public:
+ static const Class class_;
+
+ private:
+ static MOZ_MUST_USE bool getHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
+ static MOZ_MUST_USE bool setHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
+
+ static bool getEnabled(JSContext* cx, unsigned argc, Value* vp);
+ static bool setEnabled(JSContext* cx, unsigned argc, Value* vp);
+ static bool getOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
+ static bool setOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
+ static bool getOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
+ static bool setOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
+ static bool getOnNewScript(JSContext* cx, unsigned argc, Value* vp);
+ static bool setOnNewScript(JSContext* cx, unsigned argc, Value* vp);
+ static bool getOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
+ static bool setOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
+ static bool getOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
+ static bool setOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
+ static bool getOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
+ static bool setOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
+ static bool getOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
+ static bool setOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
+ static bool getUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
+ static bool setUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
+ static bool getAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
+ static bool setAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
+ static bool getCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
+ static bool setCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
+ static bool getMemory(JSContext* cx, unsigned argc, Value* vp);
+ static bool addDebuggee(JSContext* cx, unsigned argc, Value* vp);
+ static bool addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp);
+ static bool removeDebuggee(JSContext* cx, unsigned argc, Value* vp);
+ static bool removeAllDebuggees(JSContext* cx, unsigned argc, Value* vp);
+ static bool hasDebuggee(JSContext* cx, unsigned argc, Value* vp);
+ static bool getDebuggees(JSContext* cx, unsigned argc, Value* vp);
+ static bool getNewestFrame(JSContext* cx, unsigned argc, Value* vp);
+ static bool clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp);
+ static bool findScripts(JSContext* cx, unsigned argc, Value* vp);
+ static bool findObjects(JSContext* cx, unsigned argc, Value* vp);
+ static bool findAllGlobals(JSContext* cx, unsigned argc, Value* vp);
+ static bool makeGlobalObjectReference(JSContext* cx, unsigned argc, Value* vp);
+ static bool setupTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp);
+ static bool drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp);
+ static bool startTraceLogger(JSContext* cx, unsigned argc, Value* vp);
+ static bool endTraceLogger(JSContext* cx, unsigned argc, Value* vp);
+ static bool isCompilableUnit(JSContext* cx, unsigned argc, Value* vp);
+#ifdef NIGHTLY_BUILD
+ static bool setupTraceLogger(JSContext* cx, unsigned argc, Value* vp);
+ static bool drainTraceLogger(JSContext* cx, unsigned argc, Value* vp);
+#endif
+ static bool adoptDebuggeeValue(JSContext* cx, unsigned argc, Value* vp);
+ static bool construct(JSContext* cx, unsigned argc, Value* vp);
+ static const JSPropertySpec properties[];
+ static const JSFunctionSpec methods[];
+ static const JSFunctionSpec static_methods[];
+
+ static void removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame);
+ static bool updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObservableSet& obs,
+ IsObserving observing);
+ static bool updateExecutionObservabilityOfScripts(JSContext* cx, const ExecutionObservableSet& obs,
+ IsObserving observing);
+ static bool updateExecutionObservability(JSContext* cx, ExecutionObservableSet& obs,
+ IsObserving observing);
+
+ template <typename FrameFn /* void (NativeObject*) */>
+ static void forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn);
+
+ /*
+ * Return a vector containing all Debugger.Frame instances referring to
+ * |frame|. |global| is |frame|'s global object; if nullptr or omitted, we
+ * compute it ourselves from |frame|.
+ */
+ using DebuggerFrameVector = GCVector<DebuggerFrame*>;
+ static MOZ_MUST_USE bool getDebuggerFrames(AbstractFramePtr frame,
+ MutableHandle<DebuggerFrameVector> frames);
+
+ public:
+ static MOZ_MUST_USE bool ensureExecutionObservabilityOfOsrFrame(JSContext* cx,
+ InterpreterFrame* frame);
+
+ // Public for DebuggerScript_setBreakpoint.
+ static MOZ_MUST_USE bool ensureExecutionObservabilityOfScript(JSContext* cx, JSScript* script);
+
+ // Whether the Debugger instance needs to observe all non-AOT JS
+ // execution of its debugees.
+ IsObserving observesAllExecution() const;
+
+ // Whether the Debugger instance needs to observe AOT-compiled asm.js
+ // execution of its debuggees.
+ IsObserving observesAsmJS() const;
+
+ // Whether the Debugger instance needs to observe coverage of any JavaScript
+ // execution.
+ IsObserving observesCoverage() const;
+
+ private:
+ static MOZ_MUST_USE bool ensureExecutionObservabilityOfFrame(JSContext* cx,
+ AbstractFramePtr frame);
+ static MOZ_MUST_USE bool ensureExecutionObservabilityOfCompartment(JSContext* cx,
+ JSCompartment* comp);
+
+ static bool hookObservesAllExecution(Hook which);
+
+ MOZ_MUST_USE bool updateObservesAllExecutionOnDebuggees(JSContext* cx, IsObserving observing);
+ MOZ_MUST_USE bool updateObservesCoverageOnDebuggees(JSContext* cx, IsObserving observing);
+ void updateObservesAsmJSOnDebuggees(IsObserving observing);
+
+ JSObject* getHook(Hook hook) const;
+ bool hasAnyLiveHooks(JSRuntime* rt) const;
+
+ static MOZ_MUST_USE bool slowPathCheckNoExecute(JSContext* cx, HandleScript script);
+ static JSTrapStatus slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
+ static MOZ_MUST_USE bool slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame,
+ jsbytecode* pc, bool ok);
+ static JSTrapStatus slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
+ static JSTrapStatus slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
+ static void slowPathOnNewScript(JSContext* cx, HandleScript script);
+ static void slowPathOnNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
+ static void slowPathOnNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
+ static MOZ_MUST_USE bool slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj,
+ HandleSavedFrame frame, double when,
+ GlobalObject::DebuggerVector& dbgs);
+ static void slowPathPromiseHook(JSContext* cx, Hook hook, HandleObject promise);
+
+ template <typename HookIsEnabledFun /* bool (Debugger*) */,
+ typename FireHookFun /* JSTrapStatus (Debugger*) */>
+ static JSTrapStatus dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
+ FireHookFun fireHook);
+
+ JSTrapStatus fireDebuggerStatement(JSContext* cx, MutableHandleValue vp);
+ JSTrapStatus fireExceptionUnwind(JSContext* cx, MutableHandleValue vp);
+ JSTrapStatus fireEnterFrame(JSContext* cx, MutableHandleValue vp);
+ JSTrapStatus fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global, MutableHandleValue vp);
+ JSTrapStatus firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp);
+
+ NativeObject* newVariantWrapper(JSContext* cx, Handle<DebuggerScriptReferent> referent) {
+ return newDebuggerScript(cx, referent);
+ }
+ NativeObject* newVariantWrapper(JSContext* cx, Handle<DebuggerSourceReferent> referent) {
+ return newDebuggerSource(cx, referent);
+ }
+
+ /*
+ * Helper function to help wrap Debugger objects whose referents may be
+ * variants. Currently Debugger.Script and Debugger.Source referents may
+ * be variants.
+ *
+ * Prefer using wrapScript, wrapWasmScript, wrapSource, and wrapWasmSource
+ * whenever possible.
+ */
+ template <typename ReferentVariant, typename Referent, typename Map>
+ JSObject* wrapVariantReferent(JSContext* cx, Map& map, Handle<CrossCompartmentKey> key,
+ Handle<ReferentVariant> referent);
+ JSObject* wrapVariantReferent(JSContext* cx, Handle<DebuggerScriptReferent> referent);
+ JSObject* wrapVariantReferent(JSContext* cx, Handle<DebuggerSourceReferent> referent);
+
+ /*
+ * Allocate and initialize a Debugger.Script instance whose referent is
+ * |referent|.
+ */
+ NativeObject* newDebuggerScript(JSContext* cx, Handle<DebuggerScriptReferent> referent);
+
+ /*
+ * Allocate and initialize a Debugger.Source instance whose referent is
+ * |referent|.
+ */
+ NativeObject* newDebuggerSource(JSContext* cx, Handle<DebuggerSourceReferent> referent);
+
+ /*
+ * Receive a "new script" event from the engine. A new script was compiled
+ * or deserialized.
+ */
+ void fireNewScript(JSContext* cx, Handle<DebuggerScriptReferent> scriptReferent);
+
+ /*
+ * Receive a "garbage collection" event from the engine. A GC cycle with the
+ * given data was recently completed.
+ */
+ void fireOnGarbageCollectionHook(JSContext* cx,
+ const JS::dbg::GarbageCollectionEvent::Ptr& gcData);
+
+ /*
+ * Gets a Debugger.Frame object. If maybeIter is non-null, we eagerly copy
+ * its data if we need to make a new Debugger.Frame.
+ */
+ MOZ_MUST_USE bool getScriptFrameWithIter(JSContext* cx, AbstractFramePtr frame,
+ const ScriptFrameIter* maybeIter,
+ MutableHandleValue vp);
+ MOZ_MUST_USE bool getScriptFrameWithIter(JSContext* cx, AbstractFramePtr frame,
+ const ScriptFrameIter* maybeIter,
+ MutableHandleDebuggerFrame result);
+
+ inline Breakpoint* firstBreakpoint() const;
+
+ static inline Debugger* fromOnNewGlobalObjectWatchersLink(JSCList* link);
+
+ static MOZ_MUST_USE bool replaceFrameGuts(JSContext* cx, AbstractFramePtr from,
+ AbstractFramePtr to,
+ ScriptFrameIter& iter);
+
+ public:
+ Debugger(JSContext* cx, NativeObject* dbg);
+ ~Debugger();
+
+ MOZ_MUST_USE bool init(JSContext* cx);
+ inline const js::GCPtrNativeObject& toJSObject() const;
+ inline js::GCPtrNativeObject& toJSObjectRef();
+ static inline Debugger* fromJSObject(const JSObject* obj);
+ static Debugger* fromChildJSObject(JSObject* obj);
+
+ bool hasMemory() const;
+ DebuggerMemory& memory() const;
+
+ WeakGlobalObjectSet::Range allDebuggees() const { return debuggees.all(); }
+
+ /*********************************** Methods for interaction with the GC. */
+
+ /*
+ * A Debugger object is live if:
+ * * the Debugger JSObject is live (Debugger::trace handles this case); OR
+ * * it is in the middle of dispatching an event (the event dispatching
+ * code roots it in this case); OR
+ * * it is enabled, and it is debugging at least one live compartment,
+ * and at least one of the following is true:
+ * - it has a debugger hook installed
+ * - it has a breakpoint set on a live script
+ * - it has a watchpoint set on a live object.
+ *
+ * Debugger::markAllIteratively handles the last case. If it finds any
+ * Debugger objects that are definitely live but not yet marked, it marks
+ * them and returns true. If not, it returns false.
+ */
+ static void markIncomingCrossCompartmentEdges(JSTracer* tracer);
+ static MOZ_MUST_USE bool markAllIteratively(GCMarker* trc);
+ static void markAll(JSTracer* trc);
+ static void sweepAll(FreeOp* fop);
+ static void detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global);
+ static void findZoneEdges(JS::Zone* v, gc::ZoneComponentFinder& finder);
+
+ // Checks it the current compartment is allowed to execute code.
+ static inline MOZ_MUST_USE bool checkNoExecute(JSContext* cx, HandleScript script);
+
+ /*
+ * JSTrapStatus Overview
+ * ---------------------
+ *
+ * The |onEnterFrame|, |onDebuggerStatement|, and |onExceptionUnwind|
+ * methods below return a JSTrapStatus code that indicates how execution
+ * should proceed:
+ *
+ * - JSTRAP_CONTINUE: Continue execution normally.
+ *
+ * - JSTRAP_THROW: Throw an exception. The method has set |cx|'s
+ * pending exception to the value to be thrown.
+ *
+ * - JSTRAP_ERROR: Terminate execution (as is done when a script is terminated
+ * for running too long). The method has cleared |cx|'s pending
+ * exception.
+ *
+ * - JSTRAP_RETURN: Return from the new frame immediately. The method has
+ * set the youngest JS frame's return value appropriately.
+ */
+
+ /*
+ * Announce to the debugger that the context has entered a new JavaScript
+ * frame, |frame|. Call whatever hooks have been registered to observe new
+ * frames.
+ */
+ static inline JSTrapStatus onEnterFrame(JSContext* cx, AbstractFramePtr frame);
+
+ /*
+ * Announce to the debugger a |debugger;| statement on has been
+ * encountered on the youngest JS frame on |cx|. Call whatever hooks have
+ * been registered to observe this.
+ *
+ * Note that this method is called for all |debugger;| statements,
+ * regardless of the frame's debuggee-ness.
+ */
+ static inline JSTrapStatus onDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
+
+ /*
+ * Announce to the debugger that an exception has been thrown and propagated
+ * to |frame|. Call whatever hooks have been registered to observe this.
+ */
+ static inline JSTrapStatus onExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
+
+ /*
+ * Announce to the debugger that the thread has exited a JavaScript frame, |frame|.
+ * If |ok| is true, the frame is returning normally; if |ok| is false, the frame
+ * is throwing an exception or terminating.
+ *
+ * Change cx's current exception and |frame|'s return value to reflect the changes
+ * in behavior the hooks request, if any. Return the new error/success value.
+ *
+ * This function may be called twice for the same outgoing frame; only the
+ * first call has any effect. (Permitting double calls simplifies some
+ * cases where an onPop handler's resumption value changes a return to a
+ * throw, or vice versa: we can redirect to a complete copy of the
+ * alternative path, containing its own call to onLeaveFrame.)
+ */
+ static inline MOZ_MUST_USE bool onLeaveFrame(JSContext* cx, AbstractFramePtr frame,
+ jsbytecode* pc, bool ok);
+
+ static inline void onNewScript(JSContext* cx, HandleScript script);
+ static inline void onNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
+ static inline void onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
+ static inline MOZ_MUST_USE bool onLogAllocationSite(JSContext* cx, JSObject* obj,
+ HandleSavedFrame frame, double when);
+ static JSTrapStatus onTrap(JSContext* cx, MutableHandleValue vp);
+ static JSTrapStatus onSingleStep(JSContext* cx, MutableHandleValue vp);
+ static MOZ_MUST_USE bool handleBaselineOsr(JSContext* cx, InterpreterFrame* from,
+ jit::BaselineFrame* to);
+ static MOZ_MUST_USE bool handleIonBailout(JSContext* cx, jit::RematerializedFrame* from,
+ jit::BaselineFrame* to);
+ static void handleUnrecoverableIonBailoutError(JSContext* cx, jit::RematerializedFrame* frame);
+ static void propagateForcedReturn(JSContext* cx, AbstractFramePtr frame, HandleValue rval);
+ static bool hasLiveHook(GlobalObject* global, Hook which);
+ static bool inFrameMaps(AbstractFramePtr frame);
+
+ /************************************* Functions for use by Debugger.cpp. */
+
+ inline bool observesEnterFrame() const;
+ inline bool observesNewScript() const;
+ inline bool observesNewGlobalObject() const;
+ inline bool observesGlobal(GlobalObject* global) const;
+ bool observesFrame(AbstractFramePtr frame) const;
+ bool observesFrame(const FrameIter& iter) const;
+ bool observesScript(JSScript* script) const;
+
+ /*
+ * If env is nullptr, call vp->setNull() and return true. Otherwise, find
+ * or create a Debugger.Environment object for the given Env. On success,
+ * store the Environment object in *vp and return true.
+ */
+ MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env, MutableHandleValue vp);
+ MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env,
+ MutableHandleDebuggerEnvironment result);
+
+ /*
+ * Like cx->compartment()->wrap(cx, vp), but for the debugger compartment.
+ *
+ * Preconditions: *vp is a value from a debuggee compartment; cx is in the
+ * debugger's compartment.
+ *
+ * If *vp is an object, this produces a (new or existing) Debugger.Object
+ * wrapper for it. Otherwise this is the same as JSCompartment::wrap.
+ *
+ * If *vp is a magic JS_OPTIMIZED_OUT value, this produces a plain object
+ * of the form { optimizedOut: true }.
+ *
+ * If *vp is a magic JS_OPTIMIZED_ARGUMENTS value signifying missing
+ * arguments, this produces a plain object of the form { missingArguments:
+ * true }.
+ *
+ * If *vp is a magic JS_UNINITIALIZED_LEXICAL value signifying an
+ * unaccessible uninitialized binding, this produces a plain object of the
+ * form { uninitialized: true }.
+ */
+ MOZ_MUST_USE bool wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
+ MOZ_MUST_USE bool wrapDebuggeeObject(JSContext* cx, HandleObject obj,
+ MutableHandleDebuggerObject result);
+
+ /*
+ * Unwrap a Debug.Object, without rewrapping it for any particular debuggee
+ * compartment.
+ *
+ * Preconditions: cx is in the debugger compartment. *vp is a value in that
+ * compartment. (*vp should be a "debuggee value", meaning it is the
+ * debugger's reflection of a value in the debuggee.)
+ *
+ * If *vp is a Debugger.Object, store the referent in *vp. Otherwise, if *vp
+ * is an object, throw a TypeError, because it is not a debuggee
+ * value. Otherwise *vp is a primitive, so leave it alone.
+ *
+ * When passing values from the debuggee to the debugger:
+ * enter debugger compartment;
+ * call wrapDebuggeeValue; // compartment- and debugger-wrapping
+ *
+ * When passing values from the debugger to the debuggee:
+ * call unwrapDebuggeeValue; // debugger-unwrapping
+ * enter debuggee compartment;
+ * call cx->compartment()->wrap; // compartment-rewrapping
+ *
+ * (Extreme nerd sidebar: Unwrapping happens in two steps because there are
+ * two different kinds of symmetry at work: regardless of which direction
+ * we're going, we want any exceptions to be created and thrown in the
+ * debugger compartment--mirror symmetry. But compartment wrapping always
+ * happens in the target compartment--rotational symmetry.)
+ */
+ MOZ_MUST_USE bool unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
+ MOZ_MUST_USE bool unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj);
+ MOZ_MUST_USE bool unwrapPropertyDescriptor(JSContext* cx, HandleObject obj,
+ MutableHandle<PropertyDescriptor> desc);
+
+ /*
+ * Store the Debugger.Frame object for frame in *vp.
+ *
+ * Use this if you have already access to a frame pointer without having
+ * to incur the cost of walking the stack.
+ */
+ MOZ_MUST_USE bool getScriptFrame(JSContext* cx, AbstractFramePtr frame, MutableHandleValue vp) {
+ return getScriptFrameWithIter(cx, frame, nullptr, vp);
+ }
+
+ /*
+ * Store the Debugger.Frame object for iter in *vp/result. Eagerly copies a
+ * ScriptFrameIter::Data.
+ *
+ * Use this if you had to make a ScriptFrameIter to get the required
+ * frame, in which case the cost of walking the stack has already been
+ * paid.
+ */
+ MOZ_MUST_USE bool getScriptFrame(JSContext* cx, const ScriptFrameIter& iter,
+ MutableHandleValue vp) {
+ return getScriptFrameWithIter(cx, iter.abstractFramePtr(), &iter, vp);
+ }
+ MOZ_MUST_USE bool getScriptFrame(JSContext* cx, const ScriptFrameIter& iter,
+ MutableHandleDebuggerFrame result);
+
+
+ /*
+ * Set |*status| and |*value| to a (JSTrapStatus, Value) pair reflecting a
+ * standard SpiderMonkey call state: a boolean success value |ok|, a return
+ * value |rv|, and a context |cx| that may or may not have an exception set.
+ * If an exception was pending on |cx|, it is cleared (and |ok| is asserted
+ * to be false).
+ */
+ static void resultToCompletion(JSContext* cx, bool ok, const Value& rv,
+ JSTrapStatus* status, MutableHandleValue value);
+
+ /*
+ * Set |*result| to a JavaScript completion value corresponding to |status|
+ * and |value|. |value| should be the return value or exception value, not
+ * wrapped as a debuggee value. |cx| must be in the debugger compartment.
+ */
+ MOZ_MUST_USE bool newCompletionValue(JSContext* cx, JSTrapStatus status, const Value& value,
+ MutableHandleValue result);
+
+ /*
+ * Precondition: we are in the debuggee compartment (ac is entered) and ok
+ * is true if the operation in the debuggee compartment succeeded, false on
+ * error or exception.
+ *
+ * Postcondition: we are in the debugger compartment, having called
+ * ac.leave() even if an error occurred.
+ *
+ * On success, a completion value is in vp and ac.context does not have a
+ * pending exception. (This ordinarily returns true even if the ok argument
+ * is false.)
+ */
+ MOZ_MUST_USE bool receiveCompletionValue(mozilla::Maybe<AutoCompartment>& ac, bool ok,
+ HandleValue val,
+ MutableHandleValue vp);
+
+ /*
+ * Return the Debugger.Script object for |script|, or create a new one if
+ * needed. The context |cx| must be in the debugger compartment; |script|
+ * must be a script in a debuggee compartment.
+ */
+ JSObject* wrapScript(JSContext* cx, HandleScript script);
+
+ /*
+ * Return the Debugger.Script object for |wasmInstance| (the toplevel
+ * script), synthesizing a new one if needed. The context |cx| must be in
+ * the debugger compartment; |wasmInstance| must be a WasmInstanceObject in
+ * the debuggee compartment.
+ */
+ JSObject* wrapWasmScript(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
+
+ /*
+ * Return the Debugger.Source object for |source|, or create a new one if
+ * needed. The context |cx| must be in the debugger compartment; |source|
+ * must be a script source object in a debuggee compartment.
+ */
+ JSObject* wrapSource(JSContext* cx, js::HandleScriptSource source);
+
+ /*
+ * Return the Debugger.Source object for |wasmInstance| (the entire module),
+ * synthesizing a new one if needed. The context |cx| must be in the
+ * debugger compartment; |wasmInstance| must be a WasmInstanceObject in the
+ * debuggee compartment.
+ */
+ JSObject* wrapWasmSource(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
+
+ private:
+ Debugger(const Debugger&) = delete;
+ Debugger & operator=(const Debugger&) = delete;
+};
+
+enum class DebuggerEnvironmentType {
+ Declarative,
+ With,
+ Object
+};
+
+class DebuggerEnvironment : public NativeObject
+{
+ public:
+ enum {
+ OWNER_SLOT
+ };
+
+ static const unsigned RESERVED_SLOTS = 1;
+
+ static const Class class_;
+
+ static NativeObject* initClass(JSContext* cx, HandleObject dbgCtor, HandleObject objProto);
+ static DebuggerEnvironment* create(JSContext* cx, HandleObject proto, HandleObject referent,
+ HandleNativeObject debugger);
+
+ DebuggerEnvironmentType type() const;
+ MOZ_MUST_USE bool getParent(JSContext* cx, MutableHandleDebuggerEnvironment result) const;
+ MOZ_MUST_USE bool getObject(JSContext* cx, MutableHandleDebuggerObject result) const;
+ MOZ_MUST_USE bool getCallee(JSContext* cx, MutableHandleDebuggerObject result) const;
+ bool isDebuggee() const;
+ bool isOptimized() const;
+
+ static MOZ_MUST_USE bool getNames(JSContext* cx, HandleDebuggerEnvironment environment,
+ MutableHandle<IdVector> result);
+ static MOZ_MUST_USE bool find(JSContext* cx, HandleDebuggerEnvironment environment,
+ HandleId id, MutableHandleDebuggerEnvironment result);
+ static MOZ_MUST_USE bool getVariable(JSContext* cx, HandleDebuggerEnvironment environment,
+ HandleId id, MutableHandleValue result);
+ static MOZ_MUST_USE bool setVariable(JSContext* cx, HandleDebuggerEnvironment environment,
+ HandleId id, HandleValue value);
+
+ private:
+ static const ClassOps classOps_;
+
+ static const JSPropertySpec properties_[];
+ static const JSFunctionSpec methods_[];
+
+ Env* referent() const {
+ Env* env = static_cast<Env*>(getPrivate());
+ MOZ_ASSERT(env);
+ return env;
+ }
+
+ Debugger* owner() const;
+
+ bool requireDebuggee(JSContext* cx) const;
+
+ static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
+
+ static MOZ_MUST_USE bool typeGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool parentGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool objectGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool calleeGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool inspectableGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool optimizedOutGetter(JSContext* cx, unsigned argc, Value* vp);
+
+ static MOZ_MUST_USE bool namesMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool findMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool getVariableMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool setVariableMethod(JSContext* cx, unsigned argc, Value* vp);
+};
+
+enum class DebuggerFrameType {
+ Eval,
+ Global,
+ Call,
+ Module
+};
+
+enum class DebuggerFrameImplementation {
+ Interpreter,
+ Baseline,
+ Ion
+};
+
+class DebuggerFrame : public NativeObject
+{
+ public:
+ enum {
+ OWNER_SLOT
+ };
+
+ static const unsigned RESERVED_SLOTS = 1;
+
+ static const Class class_;
+
+ static NativeObject* initClass(JSContext* cx, HandleObject dbgCtor, HandleObject objProto);
+ static DebuggerFrame* create(JSContext* cx, HandleObject proto, AbstractFramePtr referent,
+ const ScriptFrameIter* maybeIter, HandleNativeObject debugger);
+
+ static MOZ_MUST_USE bool getCallee(JSContext* cx, HandleDebuggerFrame frame,
+ MutableHandleDebuggerObject result);
+ static MOZ_MUST_USE bool getIsConstructing(JSContext* cx, HandleDebuggerFrame frame,
+ bool& result);
+ static MOZ_MUST_USE bool getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
+ MutableHandleDebuggerEnvironment result);
+ static bool getIsGenerator(HandleDebuggerFrame frame);
+ static MOZ_MUST_USE bool getOffset(JSContext* cx, HandleDebuggerFrame frame, size_t& result);
+ static MOZ_MUST_USE bool getOlder(JSContext* cx, HandleDebuggerFrame frame,
+ MutableHandleDebuggerFrame result);
+ static MOZ_MUST_USE bool getThis(JSContext* cx, HandleDebuggerFrame frame,
+ MutableHandleValue result);
+ static DebuggerFrameType getType(HandleDebuggerFrame frame);
+ static DebuggerFrameImplementation getImplementation(HandleDebuggerFrame frame);
+
+ static MOZ_MUST_USE bool eval(JSContext* cx, HandleDebuggerFrame frame,
+ mozilla::Range<const char16_t> chars, HandleObject bindings,
+ const EvalOptions& options, JSTrapStatus& status,
+ MutableHandleValue value);
+
+ bool isLive() const;
+
+ private:
+ static const ClassOps classOps_;
+
+ static const JSPropertySpec properties_[];
+ static const JSFunctionSpec methods_[];
+
+ static AbstractFramePtr getReferent(HandleDebuggerFrame frame);
+ static MOZ_MUST_USE bool getScriptFrameIter(JSContext* cx, HandleDebuggerFrame frame,
+ mozilla::Maybe<ScriptFrameIter>& result);
+
+ static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
+
+ static MOZ_MUST_USE bool calleeGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool constructingGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool generatorGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool liveGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool offsetGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool olderGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool thisGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool typeGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool implementationGetter(JSContext* cx, unsigned argc, Value* vp);
+
+ static MOZ_MUST_USE bool evalMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool evalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp);
+
+ Debugger* owner() const;
+};
+
+class DebuggerObject : public NativeObject
+{
+ public:
+ static const Class class_;
+
+ static NativeObject* initClass(JSContext* cx, HandleObject obj, HandleObject debugCtor);
+ static DebuggerObject* create(JSContext* cx, HandleObject proto, HandleObject obj,
+ HandleNativeObject debugger);
+
+ // Properties
+ static MOZ_MUST_USE bool getClassName(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleString result);
+ static MOZ_MUST_USE bool getGlobal(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleDebuggerObject result);
+ static MOZ_MUST_USE bool getParameterNames(JSContext* cx, HandleDebuggerObject object,
+ MutableHandle<StringVector> result);
+ static MOZ_MUST_USE bool getBoundTargetFunction(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleDebuggerObject result);
+ static MOZ_MUST_USE bool getBoundThis(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleValue result);
+ static MOZ_MUST_USE bool getBoundArguments(JSContext* cx, HandleDebuggerObject object,
+ MutableHandle<ValueVector> result);
+ static MOZ_MUST_USE bool getAllocationSite(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleObject result);
+ static MOZ_MUST_USE bool getErrorMessageName(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleString result);
+ static MOZ_MUST_USE bool getErrorLineNumber(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleValue result);
+ static MOZ_MUST_USE bool getErrorColumnNumber(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleValue result);
+ static MOZ_MUST_USE bool getScriptedProxyTarget(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleDebuggerObject result);
+ static MOZ_MUST_USE bool getScriptedProxyHandler(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleDebuggerObject result);
+#ifdef SPIDERMONKEY_PROMISE
+ static MOZ_MUST_USE bool getPromiseValue(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleValue result);
+ static MOZ_MUST_USE bool getPromiseReason(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleValue result);
+#endif // SPIDERMONKEY_PROMISE
+
+ // Methods
+ static MOZ_MUST_USE bool isExtensible(JSContext* cx, HandleDebuggerObject object,
+ bool& result);
+ static MOZ_MUST_USE bool isSealed(JSContext* cx, HandleDebuggerObject object, bool& result);
+ static MOZ_MUST_USE bool isFrozen(JSContext* cx, HandleDebuggerObject object, bool& result);
+ static MOZ_MUST_USE bool getPrototypeOf(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleDebuggerObject result);
+ static MOZ_MUST_USE bool getOwnPropertyNames(JSContext* cx, HandleDebuggerObject object,
+ MutableHandle<IdVector> result);
+ static MOZ_MUST_USE bool getOwnPropertySymbols(JSContext* cx, HandleDebuggerObject object,
+ MutableHandle<IdVector> result);
+ static MOZ_MUST_USE bool getOwnPropertyDescriptor(JSContext* cx, HandleDebuggerObject object,
+ HandleId id,
+ MutableHandle<PropertyDescriptor> desc);
+ static MOZ_MUST_USE bool preventExtensions(JSContext* cx, HandleDebuggerObject object);
+ static MOZ_MUST_USE bool seal(JSContext* cx, HandleDebuggerObject object);
+ static MOZ_MUST_USE bool freeze(JSContext* cx, HandleDebuggerObject object);
+ static MOZ_MUST_USE bool defineProperty(JSContext* cx, HandleDebuggerObject object,
+ HandleId id, Handle<PropertyDescriptor> desc);
+ static MOZ_MUST_USE bool defineProperties(JSContext* cx, HandleDebuggerObject object,
+ Handle<IdVector> ids,
+ Handle<PropertyDescriptorVector> descs);
+ static MOZ_MUST_USE bool deleteProperty(JSContext* cx, HandleDebuggerObject object,
+ HandleId id, ObjectOpResult& result);
+ static MOZ_MUST_USE bool call(JSContext* cx, HandleDebuggerObject object, HandleValue thisv,
+ Handle<ValueVector> args, MutableHandleValue result);
+ static MOZ_MUST_USE bool forceLexicalInitializationByName(JSContext* cx,
+ HandleDebuggerObject object,
+ HandleId id, bool& result);
+ static MOZ_MUST_USE bool executeInGlobal(JSContext* cx, HandleDebuggerObject object,
+ mozilla::Range<const char16_t> chars,
+ HandleObject bindings, const EvalOptions& options,
+ JSTrapStatus& status, MutableHandleValue value);
+ static MOZ_MUST_USE bool makeDebuggeeValue(JSContext* cx, HandleDebuggerObject object,
+ HandleValue value, MutableHandleValue result);
+ static MOZ_MUST_USE bool unsafeDereference(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleObject result);
+ static MOZ_MUST_USE bool unwrap(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleDebuggerObject result);
+
+ // Infallible properties
+ bool isCallable() const;
+ bool isFunction() const;
+ bool isDebuggeeFunction() const;
+ bool isBoundFunction() const;
+ bool isArrowFunction() const;
+ bool isGlobal() const;
+ bool isScriptedProxy() const;
+#ifdef SPIDERMONKEY_PROMISE
+ bool isPromise() const;
+#endif // SPIDERMONKEY_PROMISE
+ JSAtom* name() const;
+ JSAtom* displayName() const;
+#ifdef SPIDERMONKEY_PROMISE
+ JS::PromiseState promiseState() const;
+ double promiseLifetime() const;
+ double promiseTimeToResolution() const;
+#endif // SPIDERMONKEY_PROMISE
+
+ private:
+ enum {
+ OWNER_SLOT
+ };
+
+ static const unsigned RESERVED_SLOTS = 1;
+
+ static const ClassOps classOps_;
+
+ static const JSPropertySpec properties_[];
+#ifdef SPIDERMONKEY_PROMISE
+ static const JSPropertySpec promiseProperties_[];
+#endif // SPIDERMONKEY_PROMISE
+ static const JSFunctionSpec methods_[];
+
+ JSObject* referent() const {
+ JSObject* obj = (JSObject*) getPrivate();
+ MOZ_ASSERT(obj);
+ return obj;
+ }
+
+ Debugger* owner() const;
+
+#ifdef SPIDERMONKEY_PROMISE
+ PromiseObject* promise() const;
+#endif // SPIDERMONKEY_PROMISE
+
+ static MOZ_MUST_USE bool requireGlobal(JSContext* cx, HandleDebuggerObject object);
+#ifdef SPIDERMONKEY_PROMISE
+ static MOZ_MUST_USE bool requirePromise(JSContext* cx, HandleDebuggerObject object);
+#endif // SPIDERMONKEY_PROMISE
+
+ static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
+
+ // JSNative properties
+ static MOZ_MUST_USE bool callableGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool isBoundFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool isArrowFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool protoGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool classGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool nameGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool displayNameGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool parameterNamesGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool scriptGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool boundTargetFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool boundThisGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool boundArgumentsGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool globalGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool allocationSiteGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool errorMessageNameGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool errorLineNumberGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool errorColumnNumberGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool isProxyGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool proxyTargetGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool proxyHandlerGetter(JSContext* cx, unsigned argc, Value* vp);
+#ifdef SPIDERMONKEY_PROMISE
+ static MOZ_MUST_USE bool isPromiseGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool promiseStateGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool promiseValueGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool promiseReasonGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool promiseLifetimeGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool promiseTimeToResolutionGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool promiseAllocationSiteGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool promiseResolutionSiteGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool promiseIDGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool promiseDependentPromisesGetter(JSContext* cx, unsigned argc, Value* vp);
+#endif // SPIDERMONKEY_PROMISE
+
+ // JSNative methods
+ static MOZ_MUST_USE bool isExtensibleMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool isSealedMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool isFrozenMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool getOwnPropertyNamesMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool getOwnPropertySymbolsMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool getOwnPropertyDescriptorMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool preventExtensionsMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool sealMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool freezeMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool definePropertyMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool definePropertiesMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool deletePropertyMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool callMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool applyMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool asEnvironmentMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool forceLexicalInitializationByNameMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool executeInGlobalMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool executeInGlobalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool makeDebuggeeValueMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool unsafeDereferenceMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool unwrapMethod(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool getErrorReport(JSContext* cx, HandleObject maybeError,
+ JSErrorReport*& report);
+};
+
+class BreakpointSite {
+ friend class Breakpoint;
+ friend struct ::JSCompartment;
+ friend class ::JSScript;
+ friend class Debugger;
+
+ public:
+ JSScript* script;
+ jsbytecode * const pc;
+
+ private:
+ JSCList breakpoints; /* cyclic list of all js::Breakpoints at this instruction */
+ size_t enabledCount; /* number of breakpoints in the list that are enabled */
+
+ void recompile(FreeOp* fop);
+
+ public:
+ BreakpointSite(JSScript* script, jsbytecode* pc);
+ Breakpoint* firstBreakpoint() const;
+ bool hasBreakpoint(Breakpoint* bp);
+
+ void inc(FreeOp* fop);
+ void dec(FreeOp* fop);
+ void destroyIfEmpty(FreeOp* fop);
+};
+
+/*
+ * Each Breakpoint is a member of two linked lists: its debugger's list and its
+ * site's list.
+ *
+ * GC rules:
+ * - script is live, breakpoint exists, and debugger is enabled
+ * ==> debugger is live
+ * - script is live, breakpoint exists, and debugger is live
+ * ==> retain the breakpoint and the handler object is live
+ *
+ * Debugger::markAllIteratively implements these two rules. It uses
+ * Debugger::hasAnyLiveHooks to check for rule 1.
+ *
+ * Nothing else causes a breakpoint to be retained, so if its script or
+ * debugger is collected, the breakpoint is destroyed during GC sweep phase,
+ * even if the debugger compartment isn't being GC'd. This is implemented in
+ * Zone::sweepBreakpoints.
+ */
+class Breakpoint {
+ friend struct ::JSCompartment;
+ friend class Debugger;
+
+ public:
+ Debugger * const debugger;
+ BreakpointSite * const site;
+ private:
+ /* |handler| is marked unconditionally during minor GC. */
+ js::PreBarrieredObject handler;
+ JSCList debuggerLinks;
+ JSCList siteLinks;
+
+ public:
+ static Breakpoint* fromDebuggerLinks(JSCList* links);
+ static Breakpoint* fromSiteLinks(JSCList* links);
+ Breakpoint(Debugger* debugger, BreakpointSite* site, JSObject* handler);
+ void destroy(FreeOp* fop);
+ Breakpoint* nextInDebugger();
+ Breakpoint* nextInSite();
+ const PreBarrieredObject& getHandler() const { return handler; }
+ PreBarrieredObject& getHandlerRef() { return handler; }
+};
+
+Breakpoint*
+Debugger::firstBreakpoint() const
+{
+ if (JS_CLIST_IS_EMPTY(&breakpoints))
+ return nullptr;
+ return Breakpoint::fromDebuggerLinks(JS_NEXT_LINK(&breakpoints));
+}
+
+/* static */ Debugger*
+Debugger::fromOnNewGlobalObjectWatchersLink(JSCList* link) {
+ char* p = reinterpret_cast<char*>(link);
+ return reinterpret_cast<Debugger*>(p - offsetof(Debugger, onNewGlobalObjectWatchersLink));
+}
+
+const js::GCPtrNativeObject&
+Debugger::toJSObject() const
+{
+ MOZ_ASSERT(object);
+ return object;
+}
+
+js::GCPtrNativeObject&
+Debugger::toJSObjectRef()
+{
+ MOZ_ASSERT(object);
+ return object;
+}
+
+bool
+Debugger::observesEnterFrame() const
+{
+ return enabled && getHook(OnEnterFrame);
+}
+
+bool
+Debugger::observesNewScript() const
+{
+ return enabled && getHook(OnNewScript);
+}
+
+bool
+Debugger::observesNewGlobalObject() const
+{
+ return enabled && getHook(OnNewGlobalObject);
+}
+
+bool
+Debugger::observesGlobal(GlobalObject* global) const
+{
+ ReadBarriered<GlobalObject*> debuggee(global);
+ return debuggees.has(debuggee);
+}
+
+/* static */ void
+Debugger::onNewScript(JSContext* cx, HandleScript script)
+{
+ // We early return in slowPathOnNewScript for self-hosted scripts, so we can
+ // ignore those in our assertion here.
+ MOZ_ASSERT_IF(!script->compartment()->creationOptions().invisibleToDebugger() &&
+ !script->selfHosted(),
+ script->compartment()->firedOnNewGlobalObject);
+ if (script->compartment()->isDebuggee())
+ slowPathOnNewScript(cx, script);
+}
+
+/* static */ void
+Debugger::onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global)
+{
+ MOZ_ASSERT(!global->compartment()->firedOnNewGlobalObject);
+#ifdef DEBUG
+ global->compartment()->firedOnNewGlobalObject = true;
+#endif
+ if (!JS_CLIST_IS_EMPTY(&cx->runtime()->onNewGlobalObjectWatchers))
+ Debugger::slowPathOnNewGlobalObject(cx, global);
+}
+
+/* static */ bool
+Debugger::onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame, double when)
+{
+ GlobalObject::DebuggerVector* dbgs = cx->global()->getDebuggers();
+ if (!dbgs || dbgs->empty())
+ return true;
+ RootedObject hobj(cx, obj);
+ return Debugger::slowPathOnLogAllocationSite(cx, hobj, frame, when, *dbgs);
+}
+
+MOZ_MUST_USE bool ReportObjectRequired(JSContext* cx);
+
+} /* namespace js */
+
+namespace JS {
+
+template <>
+struct DeletePolicy<js::Debugger> : public js::GCManagedDeletePolicy<js::Debugger>
+{};
+
+} /* namespace JS */
+
+#endif /* vm_Debugger_h */