1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef vm_Debugger_h
8 #define vm_Debugger_h
9 
10 #include "mozilla/GuardObjects.h"
11 #include "mozilla/LinkedList.h"
12 #include "mozilla/Range.h"
13 #include "mozilla/UniquePtr.h"
14 #include "mozilla/Vector.h"
15 
16 #include "jsclist.h"
17 #include "jscntxt.h"
18 #include "jscompartment.h"
19 #include "jsweakmap.h"
20 #include "jswrapper.h"
21 
22 #include "ds/TraceableFifo.h"
23 #include "gc/Barrier.h"
24 #include "js/Debug.h"
25 #include "js/HashTable.h"
26 #include "vm/GlobalObject.h"
27 #include "vm/SavedStacks.h"
28 
29 enum JSTrapStatus {
30     JSTRAP_ERROR,
31     JSTRAP_CONTINUE,
32     JSTRAP_RETURN,
33     JSTRAP_THROW,
34     JSTRAP_LIMIT
35 };
36 
37 namespace js {
38 
39 class LSprinter;
40 
41 class Breakpoint;
42 class DebuggerMemory;
43 
44 typedef HashSet<ReadBarrieredGlobalObject,
45                 MovableCellHasher<ReadBarrieredGlobalObject>,
46                 SystemAllocPolicy> WeakGlobalObjectSet;
47 
48 /*
49  * A weakmap from GC thing keys to JSObject values that supports the keys being
50  * in different compartments to the values. All values must be in the same
51  * compartment.
52  *
53  * The purpose of this is to allow the garbage collector to easily find edges
54  * from debuggee object compartments to debugger compartments when calculating
55  * the compartment groups.  Note that these edges are the inverse of the edges
56  * stored in the cross compartment map.
57  *
58  * The current implementation results in all debuggee object compartments being
59  * swept in the same group as the debugger.  This is a conservative approach,
60  * and compartments may be unnecessarily grouped, however it results in a
61  * simpler and faster implementation.
62  *
63  * If InvisibleKeysOk is true, then the map can have keys in invisible-to-
64  * debugger compartments. If it is false, we assert that such entries are never
65  * created.
66  *
67  * Also note that keys in these weakmaps can be in any compartment, debuggee or
68  * not, because they cannot be deleted when a compartment is no longer a
69  * debuggee: the values need to maintain object identity across add/remove/add
70  * transitions.
71  */
72 template <class UnbarrieredKey, bool InvisibleKeysOk=false>
73 class DebuggerWeakMap : private WeakMap<RelocatablePtr<UnbarrieredKey>, RelocatablePtrObject,
74                                         MovableCellHasher<RelocatablePtr<UnbarrieredKey>>>
75 {
76   private:
77     typedef RelocatablePtr<UnbarrieredKey> Key;
78     typedef RelocatablePtrObject Value;
79 
80     typedef HashMap<JS::Zone*,
81                     uintptr_t,
82                     DefaultHasher<JS::Zone*>,
83                     RuntimeAllocPolicy> CountMap;
84 
85     CountMap zoneCounts;
86     JSCompartment* compartment;
87 
88   public:
89     typedef WeakMap<Key, Value, MovableCellHasher<Key>> Base;
90 
DebuggerWeakMap(JSContext * cx)91     explicit DebuggerWeakMap(JSContext* cx)
92         : Base(cx),
93           zoneCounts(cx->runtime()),
94           compartment(cx->compartment())
95     { }
96 
97   public:
98     /* Expose those parts of HashMap public interface that are used by Debugger methods. */
99 
100     typedef typename Base::Entry Entry;
101     typedef typename Base::Ptr Ptr;
102     typedef typename Base::AddPtr AddPtr;
103     typedef typename Base::Range Range;
104     typedef typename Base::Enum Enum;
105     typedef typename Base::Lookup Lookup;
106 
107     /* Expose WeakMap public interface */
108 
109     using Base::lookupForAdd;
110     using Base::all;
111     using Base::trace;
112 
113     bool init(uint32_t len = 16) {
114         return Base::init(len) && zoneCounts.init();
115     }
116 
117     template<typename KeyInput, typename ValueInput>
relookupOrAdd(AddPtr & p,const KeyInput & k,const ValueInput & v)118     bool relookupOrAdd(AddPtr& p, const KeyInput& k, const ValueInput& v) {
119         MOZ_ASSERT(v->compartment() == this->compartment);
120         MOZ_ASSERT(!k->compartment()->options_.mergeable());
121         MOZ_ASSERT_IF(!InvisibleKeysOk, !k->compartment()->options_.invisibleToDebugger());
122         MOZ_ASSERT(!Base::has(k));
123         if (!incZoneCount(k->zone()))
124             return false;
125         bool ok = Base::relookupOrAdd(p, k, v);
126         if (!ok)
127             decZoneCount(k->zone());
128         return ok;
129     }
130 
remove(const Lookup & l)131     void remove(const Lookup& l) {
132         MOZ_ASSERT(Base::has(l));
133         Base::remove(l);
134         decZoneCount(l->zone());
135     }
136 
137   public:
138     template <void (traceValueEdges)(JSTracer*, JSObject*)>
markCrossCompartmentEdges(JSTracer * tracer)139     void markCrossCompartmentEdges(JSTracer* tracer) {
140         for (Enum e(*static_cast<Base*>(this)); !e.empty(); e.popFront()) {
141             traceValueEdges(tracer, e.front().value());
142             Key key = e.front().key();
143             TraceEdge(tracer, &key, "Debugger WeakMap key");
144             if (key != e.front().key())
145                 e.rekeyFront(key);
146             key.unsafeSet(nullptr);
147         }
148     }
149 
hasKeyInZone(JS::Zone * zone)150     bool hasKeyInZone(JS::Zone* zone) {
151         CountMap::Ptr p = zoneCounts.lookup(zone);
152         MOZ_ASSERT_IF(p.found(), p->value() > 0);
153         return p.found();
154     }
155 
156   private:
157     /* Override sweep method to also update our edge cache. */
sweep()158     void sweep() {
159         for (Enum e(*static_cast<Base*>(this)); !e.empty(); e.popFront()) {
160             if (gc::IsAboutToBeFinalized(&e.front().mutableKey())) {
161                 decZoneCount(e.front().key()->zone());
162                 e.removeFront();
163             }
164         }
165         Base::assertEntriesNotAboutToBeFinalized();
166     }
167 
incZoneCount(JS::Zone * zone)168     bool incZoneCount(JS::Zone* zone) {
169         CountMap::Ptr p = zoneCounts.lookupWithDefault(zone, 0);
170         if (!p)
171             return false;
172         ++p->value();
173         return true;
174     }
175 
decZoneCount(JS::Zone * zone)176     void decZoneCount(JS::Zone* zone) {
177         CountMap::Ptr p = zoneCounts.lookup(zone);
178         MOZ_ASSERT(p);
179         MOZ_ASSERT(p->value() > 0);
180         --p->value();
181         if (p->value() == 0)
182             zoneCounts.remove(zone);
183     }
184 };
185 
186 /*
187  * Env is the type of what ES5 calls "lexical environments" (runtime
188  * activations of lexical scopes). This is currently just JSObject, and is
189  * implemented by Call, Block, With, and DeclEnv objects, among others--but
190  * environments and objects are really two different concepts.
191  */
192 typedef JSObject Env;
193 
194 class Debugger : private mozilla::LinkedListElement<Debugger>
195 {
196     friend class Breakpoint;
197     friend class DebuggerMemory;
198     friend class SavedStacks;
199     friend class mozilla::LinkedListElement<Debugger>;
200     friend class mozilla::LinkedList<Debugger>;
201     friend bool (::JS_DefineDebuggerObject)(JSContext* cx, JS::HandleObject obj);
202     friend bool (::JS::dbg::IsDebugger)(JSObject&);
203     friend bool (::JS::dbg::GetDebuggeeGlobals)(JSContext*, JSObject&, AutoObjectVector&);
204     friend void JS::dbg::onNewPromise(JSContext* cx, HandleObject promise);
205     friend void JS::dbg::onPromiseSettled(JSContext* cx, HandleObject promise);
206     friend bool JS::dbg::FireOnGarbageCollectionHook(JSContext* cx,
207                                                      JS::dbg::GarbageCollectionEvent::Ptr&& data);
208 
209   public:
210     enum Hook {
211         OnDebuggerStatement,
212         OnExceptionUnwind,
213         OnNewScript,
214         OnEnterFrame,
215         OnNewGlobalObject,
216         OnNewPromise,
217         OnPromiseSettled,
218         OnGarbageCollection,
219         OnIonCompilation,
220         HookCount
221     };
222     enum {
223         JSSLOT_DEBUG_PROTO_START,
224         JSSLOT_DEBUG_FRAME_PROTO = JSSLOT_DEBUG_PROTO_START,
225         JSSLOT_DEBUG_ENV_PROTO,
226         JSSLOT_DEBUG_OBJECT_PROTO,
227         JSSLOT_DEBUG_SCRIPT_PROTO,
228         JSSLOT_DEBUG_SOURCE_PROTO,
229         JSSLOT_DEBUG_MEMORY_PROTO,
230         JSSLOT_DEBUG_PROTO_STOP,
231         JSSLOT_DEBUG_HOOK_START = JSSLOT_DEBUG_PROTO_STOP,
232         JSSLOT_DEBUG_HOOK_STOP = JSSLOT_DEBUG_HOOK_START + HookCount,
233         JSSLOT_DEBUG_MEMORY_INSTANCE = JSSLOT_DEBUG_HOOK_STOP,
234         JSSLOT_DEBUG_COUNT
235     };
236 
237     class ExecutionObservableSet
238     {
239       public:
240         typedef HashSet<Zone*>::Range ZoneRange;
241 
singleZone()242         virtual Zone* singleZone() const { return nullptr; }
singleScriptForZoneInvalidation()243         virtual JSScript* singleScriptForZoneInvalidation() const { return nullptr; }
zones()244         virtual const HashSet<Zone*>* zones() const { return nullptr; }
245 
246         virtual bool shouldRecompileOrInvalidate(JSScript* script) const = 0;
247         virtual bool shouldMarkAsDebuggee(ScriptFrameIter& iter) const = 0;
248     };
249 
250     // This enum is converted to and compare with bool values; NotObserving
251     // must be 0 and Observing must be 1.
252     enum IsObserving {
253         NotObserving = 0,
254         Observing = 1
255     };
256 
257     // Return true if the given compartment is a debuggee of this debugger,
258     // false otherwise.
259     bool isDebuggeeUnbarriered(const JSCompartment* compartment) const;
260 
261     // Return true if this Debugger observed a debuggee that participated in the
262     // GC identified by the given GC number. Return false otherwise.
observedGC(uint64_t majorGCNumber)263     bool observedGC(uint64_t majorGCNumber) const {
264         return observedGCs.has(majorGCNumber);
265     }
266 
267     // Notify this Debugger that one or more of its debuggees is participating
268     // in the GC identified by the given GC number.
debuggeeIsBeingCollected(uint64_t majorGCNumber)269     bool debuggeeIsBeingCollected(uint64_t majorGCNumber) {
270         return observedGCs.put(majorGCNumber);
271     }
272 
isTrackingTenurePromotions()273     bool isTrackingTenurePromotions() const {
274         return trackingTenurePromotions;
275     }
276 
isEnabled()277     bool isEnabled() const {
278         return enabled;
279     }
280 
281     void logTenurePromotion(JSRuntime* rt, JSObject& obj, double when);
282     static SavedFrame* getObjectAllocationSite(JSObject& obj);
283 
284     struct TenurePromotionsLogEntry : public JS::Traceable
285     {
286         TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when);
287 
288         const char* className;
289         double when;
290         RelocatablePtrObject frame;
291         size_t size;
292 
traceTenurePromotionsLogEntry293         static void trace(TenurePromotionsLogEntry* e, JSTracer* trc) {
294             if (e->frame)
295                 TraceEdge(trc, &e->frame, "Debugger::TenurePromotionsLogEntry::frame");
296         }
297     };
298 
299     struct AllocationsLogEntry : public JS::Traceable
300     {
AllocationsLogEntryAllocationsLogEntry301         AllocationsLogEntry(HandleObject frame, double when, const char* className,
302                             HandleAtom ctorName, size_t size, bool inNursery)
303             : frame(frame),
304               when(when),
305               className(className),
306               ctorName(ctorName),
307               size(size),
308               inNursery(inNursery)
309         {
310             MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>());
311         };
312 
313         RelocatablePtrObject frame;
314         double when;
315         const char* className;
316         RelocatablePtrAtom ctorName;
317         size_t size;
318         bool inNursery;
319 
traceAllocationsLogEntry320         static void trace(AllocationsLogEntry* e, JSTracer* trc) {
321             if (e->frame)
322                 TraceEdge(trc, &e->frame, "Debugger::AllocationsLogEntry::frame");
323             if (e->ctorName)
324                 TraceEdge(trc, &e->ctorName, "Debugger::AllocationsLogEntry::ctorName");
325         }
326     };
327 
328   private:
329     HeapPtrNativeObject object;         /* The Debugger object. Strong reference. */
330     WeakGlobalObjectSet debuggees;      /* Debuggee globals. Cross-compartment weak references. */
331     JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */
332     js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */
333     bool enabled;
334     bool allowUnobservedAsmJS;
335 
336     // Wether to enable code coverage on the Debuggee.
337     bool collectCoverageInfo;
338 
339     JSCList breakpoints;                /* Circular list of all js::Breakpoints in this debugger */
340 
341     // The set of GC numbers for which one or more of this Debugger's observed
342     // debuggees participated in.
343     js::HashSet<uint64_t> observedGCs;
344 
345     using TenurePromotionsLog = js::TraceableFifo<TenurePromotionsLogEntry>;
346     TenurePromotionsLog tenurePromotionsLog;
347     bool trackingTenurePromotions;
348     size_t maxTenurePromotionsLogLength;
349     bool tenurePromotionsLogOverflowed;
350 
351     using AllocationsLog = js::TraceableFifo<AllocationsLogEntry>;
352 
353     AllocationsLog allocationsLog;
354     bool trackingAllocationSites;
355     double allocationSamplingProbability;
356     size_t maxAllocationsLogLength;
357     bool allocationsLogOverflowed;
358 
359     static const size_t DEFAULT_MAX_LOG_LENGTH = 5000;
360 
361     bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
362                               double when);
363 
364     /*
365      * Recompute the set of debuggee zones based on the set of debuggee globals.
366      */
367     void recomputeDebuggeeZoneSet();
368 
369     /*
370      * Return true if there is an existing object metadata callback for the
371      * given global's compartment that will prevent our instrumentation of
372      * allocations.
373      */
374     static bool cannotTrackAllocations(const GlobalObject& global);
375 
376     /*
377      * Return true if the given global is being observed by at least one
378      * Debugger that is tracking allocations.
379      */
380     static bool isObservedByDebuggerTrackingAllocations(const GlobalObject& global);
381 
382     /*
383      * Add allocations tracking for objects allocated within the given
384      * debuggee's compartment. The given debuggee global must be observed by at
385      * least one Debugger that is enabled and tracking allocations.
386      */
387     static bool addAllocationsTracking(JSContext* cx, Handle<GlobalObject*> debuggee);
388 
389     /*
390      * Remove allocations tracking for objects allocated within the given
391      * global's compartment. This is a no-op if there are still Debuggers
392      * observing this global and who are tracking allocations.
393      */
394     static void removeAllocationsTracking(GlobalObject& global);
395 
396     /*
397      * Add or remove allocations tracking for all debuggees.
398      */
399     bool addAllocationsTrackingForAllDebuggees(JSContext* cx);
400     void removeAllocationsTrackingForAllDebuggees();
401 
402     /*
403      * If this Debugger is enabled, and has a onNewGlobalObject handler, then
404      * this link is inserted into the circular list headed by
405      * JSRuntime::onNewGlobalObjectWatchers. Otherwise, this is set to a
406      * singleton cycle.
407      */
408     JSCList onNewGlobalObjectWatchersLink;
409 
410     /*
411      * Map from stack frames that are currently on the stack to Debugger.Frame
412      * instances.
413      *
414      * The keys are always live stack frames. We drop them from this map as
415      * soon as they leave the stack (see slowPathOnLeaveFrame) and in
416      * removeDebuggee.
417      *
418      * We don't trace the keys of this map (the frames are on the stack and
419      * thus necessarily live), but we do trace the values. It's like a WeakMap
420      * that way, but since stack frames are not gc-things, the implementation
421      * has to be different.
422      */
423     typedef HashMap<AbstractFramePtr,
424                     RelocatablePtrNativeObject,
425                     DefaultHasher<AbstractFramePtr>,
426                     RuntimeAllocPolicy> FrameMap;
427     FrameMap frames;
428 
429     /* An ephemeral map from JSScript* to Debugger.Script instances. */
430     typedef DebuggerWeakMap<JSScript*> ScriptWeakMap;
431     ScriptWeakMap scripts;
432 
433     /* The map from debuggee source script objects to their Debugger.Source instances. */
434     typedef DebuggerWeakMap<JSObject*, true> SourceWeakMap;
435     SourceWeakMap sources;
436 
437     /* The map from debuggee objects to their Debugger.Object instances. */
438     typedef DebuggerWeakMap<JSObject*> ObjectWeakMap;
439     ObjectWeakMap objects;
440 
441     /* The map from debuggee Envs to Debugger.Environment instances. */
442     ObjectWeakMap environments;
443 
444     /*
445      * Keep track of tracelogger last drained identifiers to know if there are
446      * lost events.
447      */
448 #ifdef NIGHTLY_BUILD
449     uint32_t traceLoggerLastDrainedSize;
450     uint32_t traceLoggerLastDrainedIteration;
451 #endif
452     uint32_t traceLoggerScriptedCallsLastDrainedSize;
453     uint32_t traceLoggerScriptedCallsLastDrainedIteration;
454 
455     class FrameRange;
456     class ScriptQuery;
457     class ObjectQuery;
458 
459     bool addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> obj);
460     void removeDebuggeeGlobal(FreeOp* fop, GlobalObject* global,
461                               WeakGlobalObjectSet::Enum* debugEnum);
462 
463     /*
464      * Cope with an error or exception in a debugger hook.
465      *
466      * If callHook is true, then call the uncaughtExceptionHook, if any. If, in
467      * addition, vp is given, then parse the value returned by
468      * uncaughtExceptionHook as a resumption value.
469      *
470      * If there is no uncaughtExceptionHook, or if it fails, report and clear
471      * the pending exception on ac.context and return JSTRAP_ERROR.
472      *
473      * This always calls ac.leave(); ac is a parameter because this method must
474      * do some things in the debugger compartment and some things in the
475      * debuggee compartment.
476      */
477     JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment>& ac, bool callHook);
478     JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment>& ac, MutableHandleValue vp, bool callHook);
479 
480     JSTrapStatus handleUncaughtExceptionHelper(mozilla::Maybe<AutoCompartment>& ac,
481                                                MutableHandleValue* vp, bool callHook);
482 
483     /*
484      * Handle the result of a hook that is expected to return a resumption
485      * value <https://wiki.mozilla.org/Debugger#Resumption_Values>. This is called
486      * when we return from a debugging hook to debuggee code. The interpreter wants
487      * a (JSTrapStatus, Value) pair telling it how to proceed.
488      *
489      * Precondition: ac is entered. We are in the debugger compartment.
490      *
491      * Postcondition: This called ac.leave(). See handleUncaughtException.
492      *
493      * If ok is false, the hook failed. If an exception is pending in
494      * ac.context(), return handleUncaughtException(ac, vp, callhook).
495      * Otherwise just return JSTRAP_ERROR.
496      *
497      * If ok is true, there must be no exception pending in ac.context(). rv may be:
498      *     undefined - Return JSTRAP_CONTINUE to continue execution normally.
499      *     {return: value} or {throw: value} - Call unwrapDebuggeeValue to
500      *         unwrap value. Store the result in *vp and return JSTRAP_RETURN
501      *         or JSTRAP_THROW. The interpreter will force the current frame to
502      *         return or throw an exception.
503      *     null - Return JSTRAP_ERROR to terminate the debuggee with an
504      *         uncatchable error.
505      *     anything else - Make a new TypeError the pending exception and
506      *         return handleUncaughtException(ac, vp, callHook).
507      */
508     JSTrapStatus parseResumptionValue(mozilla::Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
509                                       MutableHandleValue vp, bool callHook = true);
510 
511     GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
512 
513     static void traceObject(JSTracer* trc, JSObject* obj);
514     void trace(JSTracer* trc);
515     static void finalize(FreeOp* fop, JSObject* obj);
516     void markCrossCompartmentEdges(JSTracer* tracer);
517 
518     static const Class jsclass;
519 
520     static bool getHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
521     static bool setHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
522 
523     static Debugger* fromThisValue(JSContext* cx, const CallArgs& ca, const char* fnname);
524     static bool getEnabled(JSContext* cx, unsigned argc, Value* vp);
525     static bool setEnabled(JSContext* cx, unsigned argc, Value* vp);
526     static bool getOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
527     static bool setOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
528     static bool getOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
529     static bool setOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
530     static bool getOnNewScript(JSContext* cx, unsigned argc, Value* vp);
531     static bool setOnNewScript(JSContext* cx, unsigned argc, Value* vp);
532     static bool getOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
533     static bool setOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
534     static bool getOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
535     static bool setOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
536     static bool getOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
537     static bool setOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
538     static bool getOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
539     static bool setOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
540     static bool getUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
541     static bool setUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
542     static bool getAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
543     static bool setAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
544     static bool getCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
545     static bool setCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
546     static bool getMemory(JSContext* cx, unsigned argc, Value* vp);
547     static bool getOnIonCompilation(JSContext* cx, unsigned argc, Value* vp);
548     static bool setOnIonCompilation(JSContext* cx, unsigned argc, Value* vp);
549     static bool addDebuggee(JSContext* cx, unsigned argc, Value* vp);
550     static bool addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp);
551     static bool removeDebuggee(JSContext* cx, unsigned argc, Value* vp);
552     static bool removeAllDebuggees(JSContext* cx, unsigned argc, Value* vp);
553     static bool hasDebuggee(JSContext* cx, unsigned argc, Value* vp);
554     static bool getDebuggees(JSContext* cx, unsigned argc, Value* vp);
555     static bool getNewestFrame(JSContext* cx, unsigned argc, Value* vp);
556     static bool clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp);
557     static bool findScripts(JSContext* cx, unsigned argc, Value* vp);
558     static bool findObjects(JSContext* cx, unsigned argc, Value* vp);
559     static bool findAllGlobals(JSContext* cx, unsigned argc, Value* vp);
560     static bool makeGlobalObjectReference(JSContext* cx, unsigned argc, Value* vp);
561     static bool setupTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp);
562     static bool drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp);
563     static bool startTraceLogger(JSContext* cx, unsigned argc, Value* vp);
564     static bool endTraceLogger(JSContext* cx, unsigned argc, Value* vp);
565 #ifdef NIGHTLY_BUILD
566     static bool setupTraceLogger(JSContext* cx, unsigned argc, Value* vp);
567     static bool drainTraceLogger(JSContext* cx, unsigned argc, Value* vp);
568 #endif
569     static bool construct(JSContext* cx, unsigned argc, Value* vp);
570     static const JSPropertySpec properties[];
571     static const JSFunctionSpec methods[];
572 
573     static void removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame);
574     static bool updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObservableSet& obs,
575                                                      IsObserving observing);
576     static bool updateExecutionObservabilityOfScripts(JSContext* cx, const ExecutionObservableSet& obs,
577                                                       IsObserving observing);
578     static bool updateExecutionObservability(JSContext* cx, ExecutionObservableSet& obs,
579                                              IsObserving observing);
580 
581   public:
582     static bool ensureExecutionObservabilityOfOsrFrame(JSContext* cx, InterpreterFrame* frame);
583 
584     // Public for DebuggerScript_setBreakpoint.
585     static bool ensureExecutionObservabilityOfScript(JSContext* cx, JSScript* script);
586 
587     // Whether the Debugger instance needs to observe all non-AOT JS
588     // execution of its debugees.
589     IsObserving observesAllExecution() const;
590 
591     // Whether the Debugger instance needs to observe AOT-compiled asm.js
592     // execution of its debuggees.
593     IsObserving observesAsmJS() const;
594 
595     // Whether the Debugger instance needs to observe coverage of any JavaScript
596     // execution.
597     IsObserving observesCoverage() const;
598 
599   private:
600     static bool ensureExecutionObservabilityOfFrame(JSContext* cx, AbstractFramePtr frame);
601     static bool ensureExecutionObservabilityOfCompartment(JSContext* cx, JSCompartment* comp);
602 
603     static bool hookObservesAllExecution(Hook which);
604 
605     bool updateObservesAllExecutionOnDebuggees(JSContext* cx, IsObserving observing);
606     bool updateObservesCoverageOnDebuggees(JSContext* cx, IsObserving observing);
607     void updateObservesAsmJSOnDebuggees(IsObserving observing);
608 
609     JSObject* getHook(Hook hook) const;
610     bool hasAnyLiveHooks(JSRuntime* rt) const;
611 
612     static JSTrapStatus slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
613     static bool slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, bool ok);
614     static JSTrapStatus slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
615     static JSTrapStatus slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
616     static void slowPathOnNewScript(JSContext* cx, HandleScript script);
617     static void slowPathOnNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
618     static bool slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
619                                             double when, GlobalObject::DebuggerVector& dbgs);
620     static void slowPathPromiseHook(JSContext* cx, Hook hook, HandleObject promise);
621     static void slowPathOnIonCompilation(JSContext* cx, Handle<ScriptVector> scripts,
622                                          LSprinter& graph);
623 
624     template <typename HookIsEnabledFun /* bool (Debugger*) */,
625               typename FireHookFun /* JSTrapStatus (Debugger*) */>
626     static JSTrapStatus dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
627                                      FireHookFun fireHook);
628 
629     JSTrapStatus fireDebuggerStatement(JSContext* cx, MutableHandleValue vp);
630     JSTrapStatus fireExceptionUnwind(JSContext* cx, MutableHandleValue vp);
631     JSTrapStatus fireEnterFrame(JSContext* cx, AbstractFramePtr frame, MutableHandleValue vp);
632     JSTrapStatus fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global, MutableHandleValue vp);
633     JSTrapStatus firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp);
634 
635     /*
636      * Allocate and initialize a Debugger.Script instance whose referent is
637      * |script|.
638      */
639     JSObject* newDebuggerScript(JSContext* cx, HandleScript script);
640 
641     /*
642      * Allocate and initialize a Debugger.Source instance whose referent is
643      * |source|.
644      */
645     JSObject* newDebuggerSource(JSContext* cx, js::HandleScriptSource source);
646 
647     /*
648      * Receive a "new script" event from the engine. A new script was compiled
649      * or deserialized.
650      */
651     void fireNewScript(JSContext* cx, HandleScript script);
652 
653     /*
654      * Receive a "garbage collection" event from the engine. A GC cycle with the
655      * given data was recently completed.
656      */
657     void fireOnGarbageCollectionHook(JSContext* cx,
658                                      const JS::dbg::GarbageCollectionEvent::Ptr& gcData);
659 
660     /*
661      * Receive a "Ion compilation" event from the engine. An Ion compilation with
662      * the given summary just got linked.
663      */
664     JSTrapStatus fireOnIonCompilationHook(JSContext* cx, Handle<ScriptVector> scripts,
665                                           LSprinter& graph);
666 
667     /*
668      * Gets a Debugger.Frame object. If maybeIter is non-null, we eagerly copy
669      * its data if we need to make a new Debugger.Frame.
670      */
671     bool getScriptFrameWithIter(JSContext* cx, AbstractFramePtr frame,
672                                 const ScriptFrameIter* maybeIter, MutableHandleValue vp);
673 
674     inline Breakpoint* firstBreakpoint() const;
675 
676     static inline Debugger* fromOnNewGlobalObjectWatchersLink(JSCList* link);
677 
678     static bool replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to,
679                                  ScriptFrameIter& iter);
680 
681   public:
682     Debugger(JSContext* cx, NativeObject* dbg);
683     ~Debugger();
684 
685     bool init(JSContext* cx);
686     inline const js::HeapPtrNativeObject& toJSObject() const;
687     inline js::HeapPtrNativeObject& toJSObjectRef();
688     static inline Debugger* fromJSObject(const JSObject* obj);
689     static Debugger* fromChildJSObject(JSObject* obj);
690 
691     bool hasMemory() const;
692     DebuggerMemory& memory() const;
693 
allDebuggees()694     WeakGlobalObjectSet::Range allDebuggees() const { return debuggees.all(); }
695 
696     /*********************************** Methods for interaction with the GC. */
697 
698     /*
699      * A Debugger object is live if:
700      *   * the Debugger JSObject is live (Debugger::trace handles this case); OR
701      *   * it is in the middle of dispatching an event (the event dispatching
702      *     code roots it in this case); OR
703      *   * it is enabled, and it is debugging at least one live compartment,
704      *     and at least one of the following is true:
705      *       - it has a debugger hook installed
706      *       - it has a breakpoint set on a live script
707      *       - it has a watchpoint set on a live object.
708      *
709      * Debugger::markAllIteratively handles the last case. If it finds any
710      * Debugger objects that are definitely live but not yet marked, it marks
711      * them and returns true. If not, it returns false.
712      */
713     static void markIncomingCrossCompartmentEdges(JSTracer* tracer);
714     static bool markAllIteratively(GCMarker* trc);
715     static void markAll(JSTracer* trc);
716     static void sweepAll(FreeOp* fop);
717     static void detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global);
718     static void findZoneEdges(JS::Zone* v, gc::ComponentFinder<JS::Zone>& finder);
719 
720     /*
721      * JSTrapStatus Overview
722      * ---------------------
723      *
724      * The |onEnterFrame|, |onDebuggerStatement|, and |onExceptionUnwind|
725      * methods below return a JSTrapStatus code that indicates how execution
726      * should proceed:
727      *
728      * - JSTRAP_CONTINUE: Continue execution normally.
729      *
730      * - JSTRAP_THROW: Throw an exception. The method has set |cx|'s
731      *   pending exception to the value to be thrown.
732      *
733      * - JSTRAP_ERROR: Terminate execution (as is done when a script is terminated
734      *   for running too long). The method has cleared |cx|'s pending
735      *   exception.
736      *
737      * - JSTRAP_RETURN: Return from the new frame immediately. The method has
738      *   set the youngest JS frame's return value appropriately.
739      */
740 
741     /*
742      * Announce to the debugger that the context has entered a new JavaScript
743      * frame, |frame|. Call whatever hooks have been registered to observe new
744      * frames.
745      */
746     static inline JSTrapStatus onEnterFrame(JSContext* cx, AbstractFramePtr frame);
747 
748     /*
749      * Announce to the debugger a |debugger;| statement on has been
750      * encountered on the youngest JS frame on |cx|. Call whatever hooks have
751      * been registered to observe this.
752      *
753      * Note that this method is called for all |debugger;| statements,
754      * regardless of the frame's debuggee-ness.
755      */
756     static inline JSTrapStatus onDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
757 
758     /*
759      * Announce to the debugger that an exception has been thrown and propagated
760      * to |frame|. Call whatever hooks have been registered to observe this.
761      */
762     static inline JSTrapStatus onExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
763 
764     /*
765      * Announce to the debugger that the thread has exited a JavaScript frame, |frame|.
766      * If |ok| is true, the frame is returning normally; if |ok| is false, the frame
767      * is throwing an exception or terminating.
768      *
769      * Change cx's current exception and |frame|'s return value to reflect the changes
770      * in behavior the hooks request, if any. Return the new error/success value.
771      *
772      * This function may be called twice for the same outgoing frame; only the
773      * first call has any effect. (Permitting double calls simplifies some
774      * cases where an onPop handler's resumption value changes a return to a
775      * throw, or vice versa: we can redirect to a complete copy of the
776      * alternative path, containing its own call to onLeaveFrame.)
777      */
778     static inline bool onLeaveFrame(JSContext* cx, AbstractFramePtr frame, bool ok);
779 
780     static inline void onNewScript(JSContext* cx, HandleScript script);
781     static inline void onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
782     static inline bool onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame,
783                                            double when);
784     static inline bool observesIonCompilation(JSContext* cx);
785     static inline void onIonCompilation(JSContext* cx, Handle<ScriptVector> scripts,
786                                         LSprinter& graph);
787     static JSTrapStatus onTrap(JSContext* cx, MutableHandleValue vp);
788     static JSTrapStatus onSingleStep(JSContext* cx, MutableHandleValue vp);
789     static bool handleBaselineOsr(JSContext* cx, InterpreterFrame* from, jit::BaselineFrame* to);
790     static bool handleIonBailout(JSContext* cx, jit::RematerializedFrame* from, jit::BaselineFrame* to);
791     static void handleUnrecoverableIonBailoutError(JSContext* cx, jit::RematerializedFrame* frame);
792     static void propagateForcedReturn(JSContext* cx, AbstractFramePtr frame, HandleValue rval);
793     static bool hasLiveHook(GlobalObject* global, Hook which);
794     static bool inFrameMaps(AbstractFramePtr frame);
795 
796     /************************************* Functions for use by Debugger.cpp. */
797 
798     inline bool observesEnterFrame() const;
799     inline bool observesNewScript() const;
800     inline bool observesNewGlobalObject() const;
801     inline bool observesGlobal(GlobalObject* global) const;
802     bool observesFrame(AbstractFramePtr frame) const;
803     bool observesFrame(const ScriptFrameIter& iter) const;
804     bool observesScript(JSScript* script) const;
805 
806     /*
807      * If env is nullptr, call vp->setNull() and return true. Otherwise, find
808      * or create a Debugger.Environment object for the given Env. On success,
809      * store the Environment object in *vp and return true.
810      */
811     bool wrapEnvironment(JSContext* cx, Handle<Env*> env, MutableHandleValue vp);
812 
813     /*
814      * Like cx->compartment()->wrap(cx, vp), but for the debugger compartment.
815      *
816      * Preconditions: *vp is a value from a debuggee compartment; cx is in the
817      * debugger's compartment.
818      *
819      * If *vp is an object, this produces a (new or existing) Debugger.Object
820      * wrapper for it. Otherwise this is the same as JSCompartment::wrap.
821      *
822      * If *vp is a magic JS_OPTIMIZED_OUT value, this produces a plain object
823      * of the form { optimizedOut: true }.
824      *
825      * If *vp is a magic JS_OPTIMIZED_ARGUMENTS value signifying missing
826      * arguments, this produces a plain object of the form { missingArguments:
827      * true }.
828      *
829      * If *vp is a magic JS_UNINITIALIZED_LEXICAL value signifying an
830      * unaccessible uninitialized binding, this produces a plain object of the
831      * form { uninitialized: true }.
832      */
833     bool wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
834 
835     /*
836      * Unwrap a Debug.Object, without rewrapping it for any particular debuggee
837      * compartment.
838      *
839      * Preconditions: cx is in the debugger compartment. *vp is a value in that
840      * compartment. (*vp should be a "debuggee value", meaning it is the
841      * debugger's reflection of a value in the debuggee.)
842      *
843      * If *vp is a Debugger.Object, store the referent in *vp. Otherwise, if *vp
844      * is an object, throw a TypeError, because it is not a debuggee
845      * value. Otherwise *vp is a primitive, so leave it alone.
846      *
847      * When passing values from the debuggee to the debugger:
848      *     enter debugger compartment;
849      *     call wrapDebuggeeValue;  // compartment- and debugger-wrapping
850      *
851      * When passing values from the debugger to the debuggee:
852      *     call unwrapDebuggeeValue;  // debugger-unwrapping
853      *     enter debuggee compartment;
854      *     call cx->compartment()->wrap;  // compartment-rewrapping
855      *
856      * (Extreme nerd sidebar: Unwrapping happens in two steps because there are
857      * two different kinds of symmetry at work: regardless of which direction
858      * we're going, we want any exceptions to be created and thrown in the
859      * debugger compartment--mirror symmetry. But compartment wrapping always
860      * happens in the target compartment--rotational symmetry.)
861      */
862     bool unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
863     bool unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj);
864     bool unwrapPropertyDescriptor(JSContext* cx, HandleObject obj,
865                                   MutableHandle<PropertyDescriptor> desc);
866 
867     /*
868      * Store the Debugger.Frame object for frame in *vp.
869      *
870      * Use this if you have already access to a frame pointer without having
871      * to incur the cost of walking the stack.
872      */
getScriptFrame(JSContext * cx,AbstractFramePtr frame,MutableHandleValue vp)873     bool getScriptFrame(JSContext* cx, AbstractFramePtr frame, MutableHandleValue vp) {
874         return getScriptFrameWithIter(cx, frame, nullptr, vp);
875     }
876 
877     /*
878      * Store the Debugger.Frame object for iter in *vp. Eagerly copies a
879      * ScriptFrameIter::Data.
880      *
881      * Use this if you had to make a ScriptFrameIter to get the required
882      * frame, in which case the cost of walking the stack has already been
883      * paid.
884      */
getScriptFrame(JSContext * cx,const ScriptFrameIter & iter,MutableHandleValue vp)885     bool getScriptFrame(JSContext* cx, const ScriptFrameIter& iter, MutableHandleValue vp) {
886         return getScriptFrameWithIter(cx, iter.abstractFramePtr(), &iter, vp);
887     }
888 
889     /*
890      * Set |*status| and |*value| to a (JSTrapStatus, Value) pair reflecting a
891      * standard SpiderMonkey call state: a boolean success value |ok|, a return
892      * value |rv|, and a context |cx| that may or may not have an exception set.
893      * If an exception was pending on |cx|, it is cleared (and |ok| is asserted
894      * to be false).
895      */
896     static void resultToCompletion(JSContext* cx, bool ok, const Value& rv,
897                                    JSTrapStatus* status, MutableHandleValue value);
898 
899     /*
900      * Set |*result| to a JavaScript completion value corresponding to |status|
901      * and |value|. |value| should be the return value or exception value, not
902      * wrapped as a debuggee value. |cx| must be in the debugger compartment.
903      */
904     bool newCompletionValue(JSContext* cx, JSTrapStatus status, Value value,
905                             MutableHandleValue result);
906 
907     /*
908      * Precondition: we are in the debuggee compartment (ac is entered) and ok
909      * is true if the operation in the debuggee compartment succeeded, false on
910      * error or exception.
911      *
912      * Postcondition: we are in the debugger compartment, having called
913      * ac.leave() even if an error occurred.
914      *
915      * On success, a completion value is in vp and ac.context does not have a
916      * pending exception. (This ordinarily returns true even if the ok argument
917      * is false.)
918      */
919     bool receiveCompletionValue(mozilla::Maybe<AutoCompartment>& ac, bool ok,
920                                 HandleValue val,
921                                 MutableHandleValue vp);
922 
923     /*
924      * Return the Debugger.Script object for |script|, or create a new one if
925      * needed. The context |cx| must be in the debugger compartment; |script|
926      * must be a script in a debuggee compartment.
927      */
928     JSObject* wrapScript(JSContext* cx, HandleScript script);
929 
930     /*
931      * Return the Debugger.Source object for |source|, or create a new one if
932      * needed. The context |cx| must be in the debugger compartment; |source|
933      * must be a script source object in a debuggee compartment.
934      */
935     JSObject* wrapSource(JSContext* cx, js::HandleScriptSource source);
936 
937   private:
938     Debugger(const Debugger&) = delete;
939     Debugger & operator=(const Debugger&) = delete;
940 };
941 
942 template<>
943 struct DefaultGCPolicy<Debugger::TenurePromotionsLogEntry> {
944     static void trace(JSTracer* trc, Debugger::TenurePromotionsLogEntry* e, const char*) {
945         Debugger::TenurePromotionsLogEntry::trace(e, trc);
946     }
947 };
948 
949 template<>
950 struct DefaultGCPolicy<Debugger::AllocationsLogEntry> {
951     static void trace(JSTracer* trc, Debugger::AllocationsLogEntry* e, const char*) {
952         Debugger::AllocationsLogEntry::trace(e, trc);
953     }
954 };
955 
956 class BreakpointSite {
957     friend class Breakpoint;
958     friend struct ::JSCompartment;
959     friend class ::JSScript;
960     friend class Debugger;
961 
962   public:
963     JSScript* script;
964     jsbytecode * const pc;
965 
966   private:
967     JSCList breakpoints;  /* cyclic list of all js::Breakpoints at this instruction */
968     size_t enabledCount;  /* number of breakpoints in the list that are enabled */
969 
970     void recompile(FreeOp* fop);
971 
972   public:
973     BreakpointSite(JSScript* script, jsbytecode* pc);
974     Breakpoint* firstBreakpoint() const;
975     bool hasBreakpoint(Breakpoint* bp);
976 
977     void inc(FreeOp* fop);
978     void dec(FreeOp* fop);
979     void destroyIfEmpty(FreeOp* fop);
980 };
981 
982 /*
983  * Each Breakpoint is a member of two linked lists: its debugger's list and its
984  * site's list.
985  *
986  * GC rules:
987  *   - script is live, breakpoint exists, and debugger is enabled
988  *      ==> debugger is live
989  *   - script is live, breakpoint exists, and debugger is live
990  *      ==> retain the breakpoint and the handler object is live
991  *
992  * Debugger::markAllIteratively implements these two rules. It uses
993  * Debugger::hasAnyLiveHooks to check for rule 1.
994  *
995  * Nothing else causes a breakpoint to be retained, so if its script or
996  * debugger is collected, the breakpoint is destroyed during GC sweep phase,
997  * even if the debugger compartment isn't being GC'd. This is implemented in
998  * Zone::sweepBreakpoints.
999  */
1000 class Breakpoint {
1001     friend struct ::JSCompartment;
1002     friend class Debugger;
1003 
1004   public:
1005     Debugger * const debugger;
1006     BreakpointSite * const site;
1007   private:
1008     /* |handler| is marked unconditionally during minor GC. */
1009     js::PreBarrieredObject handler;
1010     JSCList debuggerLinks;
1011     JSCList siteLinks;
1012 
1013   public:
1014     static Breakpoint* fromDebuggerLinks(JSCList* links);
1015     static Breakpoint* fromSiteLinks(JSCList* links);
1016     Breakpoint(Debugger* debugger, BreakpointSite* site, JSObject* handler);
1017     void destroy(FreeOp* fop);
1018     Breakpoint* nextInDebugger();
1019     Breakpoint* nextInSite();
1020     const PreBarrieredObject& getHandler() const { return handler; }
1021     PreBarrieredObject& getHandlerRef() { return handler; }
1022 };
1023 
1024 Breakpoint*
1025 Debugger::firstBreakpoint() const
1026 {
1027     if (JS_CLIST_IS_EMPTY(&breakpoints))
1028         return nullptr;
1029     return Breakpoint::fromDebuggerLinks(JS_NEXT_LINK(&breakpoints));
1030 }
1031 
1032 /* static */ Debugger*
1033 Debugger::fromOnNewGlobalObjectWatchersLink(JSCList* link) {
1034     char* p = reinterpret_cast<char*>(link);
1035     return reinterpret_cast<Debugger*>(p - offsetof(Debugger, onNewGlobalObjectWatchersLink));
1036 }
1037 
1038 const js::HeapPtrNativeObject&
1039 Debugger::toJSObject() const
1040 {
1041     MOZ_ASSERT(object);
1042     return object;
1043 }
1044 
1045 js::HeapPtrNativeObject&
1046 Debugger::toJSObjectRef()
1047 {
1048     MOZ_ASSERT(object);
1049     return object;
1050 }
1051 
1052 bool
1053 Debugger::observesEnterFrame() const
1054 {
1055     return enabled && getHook(OnEnterFrame);
1056 }
1057 
1058 bool
1059 Debugger::observesNewScript() const
1060 {
1061     return enabled && getHook(OnNewScript);
1062 }
1063 
1064 bool
1065 Debugger::observesNewGlobalObject() const
1066 {
1067     return enabled && getHook(OnNewGlobalObject);
1068 }
1069 
1070 bool
1071 Debugger::observesGlobal(GlobalObject* global) const
1072 {
1073     ReadBarriered<GlobalObject*> debuggee(global);
1074     return debuggees.has(debuggee);
1075 }
1076 
1077 /* static */ void
1078 Debugger::onNewScript(JSContext* cx, HandleScript script)
1079 {
1080     // We early return in slowPathOnNewScript for self-hosted scripts, so we can
1081     // ignore those in our assertion here.
1082     MOZ_ASSERT_IF(!script->compartment()->options().invisibleToDebugger() &&
1083                   !script->selfHosted(),
1084                   script->compartment()->firedOnNewGlobalObject);
1085     if (script->compartment()->isDebuggee())
1086         slowPathOnNewScript(cx, script);
1087 }
1088 
1089 /* static */ void
1090 Debugger::onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global)
1091 {
1092     MOZ_ASSERT(!global->compartment()->firedOnNewGlobalObject);
1093 #ifdef DEBUG
1094     global->compartment()->firedOnNewGlobalObject = true;
1095 #endif
1096     if (!JS_CLIST_IS_EMPTY(&cx->runtime()->onNewGlobalObjectWatchers))
1097         Debugger::slowPathOnNewGlobalObject(cx, global);
1098 }
1099 
1100 /* static */ bool
1101 Debugger::onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame, double when)
1102 {
1103     GlobalObject::DebuggerVector* dbgs = cx->global()->getDebuggers();
1104     if (!dbgs || dbgs->empty())
1105         return true;
1106     RootedObject hobj(cx, obj);
1107     return Debugger::slowPathOnLogAllocationSite(cx, hobj, frame, when, *dbgs);
1108 }
1109 
1110 bool ReportObjectRequired(JSContext* cx);
1111 
1112 } /* namespace js */
1113 
1114 namespace JS {
1115 
1116 template <>
1117 struct DeletePolicy<js::Debugger> : public js::GCManagedDeletePolicy<js::Debugger>
1118 {};
1119 
1120 } /* namespace JS */
1121 
1122 #endif /* vm_Debugger_h */
1123