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