Debugger.h (62760B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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 debugger_Debugger_h 8 #define debugger_Debugger_h 9 10 #include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER1 11 #include "mozilla/Attributes.h" // for MOZ_RAII 12 #include "mozilla/DoublyLinkedList.h" // for DoublyLinkedListElement 13 #include "mozilla/HashTable.h" // for HashSet, DefaultHasher (ptr only) 14 #include "mozilla/LinkedList.h" // for LinkedList (ptr only) 15 #include "mozilla/Maybe.h" // for Maybe, Nothing 16 #include "mozilla/Range.h" // for Range 17 #include "mozilla/Result.h" // for Result 18 #include "mozilla/TimeStamp.h" // for TimeStamp 19 #include "mozilla/Variant.h" // for Variant 20 21 #include <stddef.h> // for size_t 22 #include <stdint.h> // for uint32_t, uint64_t, uintptr_t 23 #include <utility> // for std::move 24 25 #include "jstypes.h" // for JS_GC_ZEAL 26 #include "NamespaceImports.h" // for Value, HandleObject 27 28 #include "debugger/DebugAPI.h" // for DebugAPI 29 #include "debugger/Object.h" // for DebuggerObject 30 #include "ds/TraceableFifo.h" // for TraceableFifo 31 #include "gc/Barrier.h" // 32 #include "gc/Tracer.h" // for TraceNullableEdge, TraceEdge 33 #include "gc/WeakMap.h" // for WeakMap 34 #include "gc/ZoneAllocator.h" // for ZoneAllocPolicy 35 #include "js/Debug.h" // JS_DefineDebuggerObject 36 #include "js/GCAPI.h" // for GarbageCollectionEvent 37 #include "js/GCVariant.h" // for GCVariant 38 #include "js/Proxy.h" // for PropertyDescriptor 39 #include "js/RootingAPI.h" // for Handle 40 #include "js/TracingAPI.h" // for TraceRoot 41 #include "js/Wrapper.h" // for UncheckedUnwrap 42 #include "proxy/DeadObjectProxy.h" // for IsDeadProxyObject 43 #include "vm/GeneratorObject.h" // for AbstractGeneratorObject 44 #include "vm/GlobalObject.h" // for GlobalObject 45 #include "vm/JSContext.h" // for JSContext 46 #include "vm/JSObject.h" // for JSObject 47 #include "vm/JSScript.h" // for JSScript, ScriptSourceObject 48 #include "vm/NativeObject.h" // for NativeObject 49 #include "vm/Runtime.h" // for JSRuntime 50 #include "vm/SavedFrame.h" // for SavedFrame 51 #include "vm/Stack.h" // for AbstractFramePtr, FrameIter 52 #include "vm/StringType.h" // for JSAtom 53 #include "wasm/WasmJS.h" // for WasmInstanceObject 54 55 class JS_PUBLIC_API JSFunction; 56 57 namespace JS { 58 class JS_PUBLIC_API AutoStableStringChars; 59 class JS_PUBLIC_API Compartment; 60 class JS_PUBLIC_API Realm; 61 class JS_PUBLIC_API Zone; 62 } /* namespace JS */ 63 64 namespace js { 65 class AutoRealm; 66 class CrossCompartmentKey; 67 class Debugger; 68 class DebuggerEnvironment; 69 class PromiseObject; 70 namespace gc { 71 struct Cell; 72 } /* namespace gc */ 73 namespace wasm { 74 class Instance; 75 } /* namespace wasm */ 76 } /* namespace js */ 77 78 /* 79 * Windows 3.x used a cooperative multitasking model, with a Yield macro that 80 * let you relinquish control to other cooperative threads. Microsoft replaced 81 * it with an empty macro long ago. We should be free to use it in our code. 82 */ 83 #undef Yield 84 85 namespace js { 86 87 class Breakpoint; 88 class DebuggerFrame; 89 class DebuggerScript; 90 class DebuggerSource; 91 class DebuggerMemory; 92 class ScriptedOnStepHandler; 93 class ScriptedOnPopHandler; 94 class DebuggerDebuggeeLink; 95 96 /** 97 * Tells how the JS engine should resume debuggee execution after firing a 98 * debugger hook. Most debugger hooks get to choose how the debuggee proceeds; 99 * see js/src/doc/Debugger/Conventions.md under "Resumption Values". 100 * 101 * Debugger::processHandlerResult() translates between JavaScript values and 102 * this enum. 103 */ 104 enum class ResumeMode { 105 /** 106 * The debuggee should continue unchanged. 107 * 108 * This corresponds to a resumption value of `undefined`. 109 */ 110 Continue, 111 112 /** 113 * Throw an exception in the debuggee. 114 * 115 * This corresponds to a resumption value of `{throw: <value>}`. 116 */ 117 Throw, 118 119 /** 120 * Terminate the debuggee, as if it had been cancelled via the "slow 121 * script" ribbon. 122 * 123 * This corresponds to a resumption value of `null`. 124 */ 125 Terminate, 126 127 /** 128 * Force the debuggee to return from the current frame. 129 * 130 * This corresponds to a resumption value of `{return: <value>}`. 131 */ 132 Return, 133 }; 134 135 /** 136 * A completion value, describing how some sort of JavaScript evaluation 137 * completed. This is used to tell an onPop handler what's going on with the 138 * frame, and to report the outcome of call, apply, setProperty, and getProperty 139 * operations. 140 * 141 * Local variables of type Completion should be held in Rooted locations, 142 * and passed using Handle and MutableHandle. 143 */ 144 class Completion { 145 public: 146 struct Return { 147 explicit Return(const Value& value) : value(value) {} 148 Value value; 149 150 void trace(JSTracer* trc) { 151 JS::TraceRoot(trc, &value, "js::Completion::Return::value"); 152 } 153 }; 154 155 struct Throw { 156 Throw(const Value& exception, SavedFrame* stack) 157 : exception(exception), stack(stack) {} 158 Value exception; 159 SavedFrame* stack; 160 161 void trace(JSTracer* trc) { 162 JS::TraceRoot(trc, &exception, "js::Completion::Throw::exception"); 163 JS::TraceRoot(trc, &stack, "js::Completion::Throw::stack"); 164 } 165 }; 166 167 struct Terminate { 168 void trace(JSTracer* trc) {} 169 }; 170 171 struct InitialYield { 172 explicit InitialYield(AbstractGeneratorObject* generatorObject) 173 : generatorObject(generatorObject) {} 174 AbstractGeneratorObject* generatorObject; 175 176 void trace(JSTracer* trc) { 177 JS::TraceRoot(trc, &generatorObject, 178 "js::Completion::InitialYield::generatorObject"); 179 } 180 }; 181 182 struct Yield { 183 Yield(AbstractGeneratorObject* generatorObject, const Value& iteratorResult) 184 : generatorObject(generatorObject), iteratorResult(iteratorResult) {} 185 AbstractGeneratorObject* generatorObject; 186 Value iteratorResult; 187 188 void trace(JSTracer* trc) { 189 JS::TraceRoot(trc, &generatorObject, 190 "js::Completion::Yield::generatorObject"); 191 JS::TraceRoot(trc, &iteratorResult, 192 "js::Completion::Yield::iteratorResult"); 193 } 194 }; 195 196 struct Await { 197 Await(AbstractGeneratorObject* generatorObject, const Value& awaitee) 198 : generatorObject(generatorObject), awaitee(awaitee) {} 199 AbstractGeneratorObject* generatorObject; 200 Value awaitee; 201 202 void trace(JSTracer* trc) { 203 JS::TraceRoot(trc, &generatorObject, 204 "js::Completion::Await::generatorObject"); 205 JS::TraceRoot(trc, &awaitee, "js::Completion::Await::awaitee"); 206 } 207 }; 208 209 // The JS::Result macros want to assign to an existing variable, so having a 210 // default constructor is handy. 211 Completion() : variant(Terminate()) {} 212 213 // Construct a completion from a specific variant. 214 // 215 // Unfortunately, using a template here would prevent the implicit definitions 216 // of the copy and move constructor and assignment operators, which is icky. 217 explicit Completion(Return&& variant) 218 : variant(std::forward<Return>(variant)) {} 219 explicit Completion(Throw&& variant) 220 : variant(std::forward<Throw>(variant)) {} 221 explicit Completion(Terminate&& variant) 222 : variant(std::forward<Terminate>(variant)) {} 223 explicit Completion(InitialYield&& variant) 224 : variant(std::forward<InitialYield>(variant)) {} 225 explicit Completion(Yield&& variant) 226 : variant(std::forward<Yield>(variant)) {} 227 explicit Completion(Await&& variant) 228 : variant(std::forward<Await>(variant)) {} 229 230 // Capture a JavaScript operation result as a Completion value. This clears 231 // any exception and stack from cx, taking ownership of them itself. 232 static Completion fromJSResult(JSContext* cx, bool ok, const Value& rv); 233 234 // Construct a completion given an AbstractFramePtr that is being popped. This 235 // clears any exception and stack from cx, taking ownership of them itself. 236 static Completion fromJSFramePop(JSContext* cx, AbstractFramePtr frame, 237 const jsbytecode* pc, bool ok); 238 239 template <typename V> 240 bool is() const { 241 return variant.template is<V>(); 242 } 243 244 template <typename V> 245 V& as() { 246 return variant.template as<V>(); 247 } 248 249 template <typename V> 250 const V& as() const { 251 return variant.template as<V>(); 252 } 253 254 void trace(JSTracer* trc); 255 256 /* True if this completion is a suspension of a generator or async call. */ 257 bool suspending() const { 258 return variant.is<InitialYield>() || variant.is<Yield>() || 259 variant.is<Await>(); 260 } 261 262 /* Set `result` to a Debugger API completion value describing this completion. 263 */ 264 bool buildCompletionValue(JSContext* cx, Debugger* dbg, 265 MutableHandleValue result) const; 266 267 /* 268 * Set `resumeMode`, `value`, and `exnStack` to values describing this 269 * completion. 270 */ 271 void toResumeMode(ResumeMode& resumeMode, MutableHandleValue value, 272 MutableHandle<SavedFrame*> exnStack) const; 273 /* 274 * Given a `ResumeMode` and value (typically derived from a resumption value 275 * returned by a Debugger hook), update this completion as requested. 276 */ 277 void updateFromHookResult(ResumeMode resumeMode, HandleValue value); 278 279 private: 280 using Variant = 281 mozilla::Variant<Return, Throw, Terminate, InitialYield, Yield, Await>; 282 struct BuildValueMatcher; 283 struct ToResumeModeMatcher; 284 285 Variant variant; 286 }; 287 288 using WeakGlobalObjectSet = 289 HashSet<WeakHeapPtr<GlobalObject*>, 290 StableCellHasher<WeakHeapPtr<GlobalObject*>>, ZoneAllocPolicy>; 291 292 #ifdef DEBUG 293 extern void CheckDebuggeeThing(BaseScript* script, bool invisibleOk); 294 295 extern void CheckDebuggeeThing(JSObject* obj, bool invisibleOk); 296 #endif 297 298 /* 299 * [SMDOC] Cross-compartment weakmap entries for Debugger API objects 300 * 301 * The Debugger API creates objects like Debugger.Object, Debugger.Script, 302 * Debugger.Environment, etc. to refer to things in the debuggee. Each Debugger 303 * gets at most one Debugger.Mumble for each referent: Debugger.Mumbles are 304 * unique per referent per Debugger. This is accomplished by storing the 305 * debugger objects in a DebuggerWeakMap, using the debuggee thing as the key. 306 * 307 * Since a Debugger and its debuggee must be in different compartments, a 308 * Debugger.Mumble's pointer to its referent is a cross-compartment edge, from 309 * the debugger's compartment into the debuggee compartment. Like any other sort 310 * of cross-compartment edge, the GC needs to be able to find all of these edges 311 * readily. The GC therefore consults the debugger's weakmap tables as 312 * necessary. This allows the garbage collector to easily find edges between 313 * debuggee object compartments and debugger compartments when calculating the 314 * zone sweep groups. 315 * 316 * The current implementation results in all debuggee object compartments being 317 * swept in the same group as the debugger. This is a conservative approach, and 318 * compartments may be unnecessarily grouped. However this results in a simpler 319 * and faster implementation. 320 */ 321 322 /* 323 * A weakmap from GC thing keys to JSObject values that supports the keys being 324 * in different compartments to the values. All values must be in the same 325 * compartment. 326 * 327 * If InvisibleKeysOk is true, then the map can have keys in invisible-to- 328 * debugger compartments. If it is false, we assert that such entries are never 329 * created. 330 * 331 * Note that keys in these weakmaps can be in any compartment, debuggee or not, 332 * because they are not deleted when a compartment is no longer a debuggee: the 333 * values need to maintain object identity across add/remove/add 334 * transitions. (Frames are an exception to the rule. Existing Debugger.Frame 335 * objects are killed if their realm is removed as a debugger; if the realm 336 * beacomes a debuggee again later, new Frame objects are created.) 337 */ 338 template <class Referent, class Wrapper, bool InvisibleKeysOk = false> 339 class DebuggerWeakMap : private WeakMap<Referent*, Wrapper*, ZoneAllocPolicy> { 340 private: 341 using Key = Referent*; 342 using Value = Wrapper*; 343 344 JS::Compartment* compartment; 345 346 public: 347 using Base = WeakMap<Key, Value, ZoneAllocPolicy>; 348 using ReferentType = Referent; 349 using WrapperType = Wrapper; 350 351 explicit DebuggerWeakMap(JSContext* cx) 352 : Base(cx), compartment(cx->compartment()) {} 353 354 public: 355 // Expose those parts of HashMap public interface that are used by Debugger 356 // methods. 357 358 using Entry = typename Base::Entry; 359 using Ptr = typename Base::Ptr; 360 using AddPtr = typename Base::AddPtr; 361 using Range = typename Base::Range; 362 using Lookup = typename Base::Lookup; 363 364 // Expose WeakMap public interface. 365 366 using Base::all; 367 using Base::has; 368 using Base::lookup; 369 using Base::lookupForAdd; 370 using Base::lookupUnbarriered; 371 using Base::remove; 372 using Base::trace; 373 using Base::zone; 374 #ifdef DEBUG 375 using Base::hasEntry; 376 #endif 377 378 class Enum : public Base::Enum { 379 public: 380 explicit Enum(DebuggerWeakMap& map) : Base::Enum(map) {} 381 }; 382 383 template <typename KeyInput, typename ValueInput> 384 bool relookupOrAdd(AddPtr& p, const KeyInput& k, const ValueInput& v) { 385 MOZ_ASSERT(v->compartment() == this->compartment); 386 #ifdef DEBUG 387 CheckDebuggeeThing(k, InvisibleKeysOk); 388 #endif 389 MOZ_ASSERT(!Base::has(k)); 390 bool ok = Base::relookupOrAdd(p, k, v); 391 return ok; 392 } 393 394 public: 395 void traceCrossCompartmentEdges(JSTracer* tracer) { 396 for (Enum e(*this); !e.empty(); e.popFront()) { 397 // The values are debugger objects which contain a cross-compartment 398 // debuggee pointer, so trace their contents. 399 e.front().value()->trace(tracer); 400 401 // Trace the keys, which are cross compartment debuggee pointers. 402 // This can rekey the entry and invalidate |e.front()|. 403 Base::traceKey(tracer, e); 404 } 405 } 406 407 bool findSweepGroupEdges(JS::Zone* atomsZone) override; 408 409 private: 410 #ifdef JS_GC_ZEAL 411 // Let the weak map marking verifier know that this map can 412 // contain keys in other zones. 413 virtual bool allowKeysInOtherZones() const override { return true; } 414 #endif 415 }; 416 417 class LeaveDebuggeeNoExecute; 418 419 class MOZ_RAII EvalOptions { 420 public: 421 enum class EnvKind { 422 Frame, 423 FrameWithExtraBindings, 424 Global, 425 GlobalWithExtraOuterBindings, 426 GlobalWithExtraInnerBindings, 427 }; 428 429 private: 430 JS::UniqueChars filename_; 431 unsigned lineno_ = 1; 432 bool hideFromDebugger_ = false; 433 bool bypassCSP_ = false; 434 EnvKind kind_; 435 436 public: 437 explicit EvalOptions(EnvKind kind) : kind_(kind) {}; 438 ~EvalOptions() = default; 439 const char* filename() const { return filename_.get(); } 440 unsigned lineno() const { return lineno_; } 441 bool hideFromDebugger() const { return hideFromDebugger_; } 442 bool bypassCSP() const { return bypassCSP_; } 443 EnvKind kind() const { return kind_; } 444 void setUseInnerBindings() { 445 MOZ_ASSERT(kind_ == EvalOptions::EnvKind::GlobalWithExtraOuterBindings); 446 kind_ = EvalOptions::EnvKind::GlobalWithExtraInnerBindings; 447 } 448 [[nodiscard]] bool setFilename(JSContext* cx, const char* filename); 449 void setLineno(unsigned lineno) { lineno_ = lineno; } 450 void setHideFromDebugger(bool hide) { hideFromDebugger_ = hide; } 451 void setBypassCSP(bool bypass) { bypassCSP_ = bypass; } 452 }; 453 454 /* 455 * Env is the type of what ECMA-262 calls "lexical environments" (the records 456 * that represent scopes and bindings). See vm/EnvironmentObject.h. 457 * 458 * This is JSObject rather than js::EnvironmentObject because GlobalObject and 459 * some proxies, despite not being in the EnvironmentObject class hierarchy, 460 * can be in environment chains. 461 */ 462 using Env = JSObject; 463 464 // The referent of a Debugger.Script. 465 // 466 // - For most scripts, we point at their BaseScript. 467 // 468 // - For Web Assembly instances for which we are presenting a script-like 469 // interface, we point at their WasmInstanceObject. 470 // 471 // The DebuggerScript object itself simply stores a Cell* in its private 472 // pointer, but when we're working with that pointer in C++ code, we'd rather 473 // not pass around a Cell* and be constantly asserting that, yes, this really 474 // does point to something okay. Instead, we immediately build an instance of 475 // this type from the Cell* and use that instead, so we can benefit from 476 // Variant's static checks. 477 using DebuggerScriptReferent = 478 mozilla::Variant<BaseScript*, WasmInstanceObject*>; 479 480 // The referent of a Debugger.Source. 481 // 482 // - For most sources, this is a ScriptSourceObject. 483 // 484 // - For Web Assembly instances for which we are presenting a source-like 485 // interface, we point at their WasmInstanceObject. 486 // 487 // The DebuggerSource object actually simply stores a Cell* in its private 488 // pointer. See the comments for DebuggerScriptReferent for the rationale for 489 // this type. 490 using DebuggerSourceReferent = 491 mozilla::Variant<ScriptSourceObject*, WasmInstanceObject*>; 492 493 template <typename HookIsEnabledFun /* bool (Debugger*) */> 494 class MOZ_RAII DebuggerList { 495 private: 496 // Note: In the general case, 'debuggers' contains references to objects in 497 // different compartments--every compartment *except* the debugger's. 498 RootedValueVector debuggers; 499 HookIsEnabledFun hookIsEnabled; 500 501 public: 502 /** 503 * The hook function will be called during `init()` to build the list of 504 * active debuggers, and again during dispatch to validate that the hook is 505 * still active for the given debugger. 506 */ 507 DebuggerList(JSContext* cx, HookIsEnabledFun hookIsEnabled) 508 : debuggers(cx), hookIsEnabled(hookIsEnabled) {} 509 510 [[nodiscard]] bool init(JSContext* cx); 511 512 bool empty() { return debuggers.empty(); } 513 514 template <typename FireHookFun /* ResumeMode (Debugger*) */> 515 bool dispatchHook(JSContext* cx, FireHookFun fireHook); 516 517 template <typename FireHookFun /* void (Debugger*) */> 518 void dispatchQuietHook(JSContext* cx, FireHookFun fireHook); 519 520 template <typename FireHookFun /* bool (Debugger*, ResumeMode&, MutableHandleValue) */> 521 [[nodiscard]] bool dispatchResumptionHook(JSContext* cx, 522 AbstractFramePtr frame, 523 FireHookFun fireHook); 524 }; 525 526 // The Debugger.prototype object. 527 class DebuggerPrototypeObject : public NativeObject { 528 public: 529 static const JSClass class_; 530 }; 531 532 class DebuggerInstanceObject : public NativeObject { 533 private: 534 static const JSClassOps classOps_; 535 536 public: 537 static const JSClass class_; 538 }; 539 540 class Debugger : private mozilla::LinkedListElement<Debugger> { 541 friend class DebugAPI; 542 friend class Breakpoint; 543 friend class DebuggerFrame; 544 friend class DebuggerMemory; 545 friend class DebuggerInstanceObject; 546 547 template <typename> 548 friend class DebuggerList; 549 friend struct JSRuntime::GlobalObjectWatchersLinkAccess<Debugger>; 550 friend struct JSRuntime::GarbageCollectionWatchersLinkAccess<Debugger>; 551 friend class SavedStacks; 552 friend class ScriptedOnStepHandler; 553 friend class ScriptedOnPopHandler; 554 friend class mozilla::LinkedListElement<Debugger>; 555 friend class mozilla::LinkedList<Debugger>; 556 friend bool(::JS_DefineDebuggerObject)(JSContext* cx, JS::HandleObject obj); 557 friend bool(::JS::dbg::IsDebugger)(JSObject&); 558 friend bool(::JS::dbg::GetDebuggeeGlobals)(JSContext*, JSObject&, 559 MutableHandleObjectVector); 560 friend bool JS::dbg::FireOnGarbageCollectionHookRequired(JSContext* cx); 561 friend bool JS::dbg::FireOnGarbageCollectionHook( 562 JSContext* cx, JS::dbg::GarbageCollectionEvent::Ptr&& data); 563 564 public: 565 enum Hook { 566 OnDebuggerStatement, 567 OnExceptionUnwind, 568 OnNewScript, 569 OnEnterFrame, 570 OnNativeCall, 571 OnNewGlobalObject, 572 OnNewPromise, 573 OnPromiseSettled, 574 OnGarbageCollection, 575 HookCount 576 }; 577 enum { 578 JSSLOT_DEBUG_PROTO_START, 579 JSSLOT_DEBUG_FRAME_PROTO = JSSLOT_DEBUG_PROTO_START, 580 JSSLOT_DEBUG_ENV_PROTO, 581 JSSLOT_DEBUG_OBJECT_PROTO, 582 JSSLOT_DEBUG_SCRIPT_PROTO, 583 JSSLOT_DEBUG_SOURCE_PROTO, 584 JSSLOT_DEBUG_MEMORY_PROTO, 585 JSSLOT_DEBUG_PROTO_STOP, 586 JSSLOT_DEBUG_DEBUGGER = JSSLOT_DEBUG_PROTO_STOP, 587 JSSLOT_DEBUG_HOOK_START, 588 JSSLOT_DEBUG_HOOK_STOP = JSSLOT_DEBUG_HOOK_START + HookCount, 589 JSSLOT_DEBUG_MEMORY_INSTANCE = JSSLOT_DEBUG_HOOK_STOP, 590 JSSLOT_DEBUG_DEBUGGEE_LINK, 591 JSSLOT_DEBUG_COUNT 592 }; 593 594 // Bring DebugAPI::IsObserving into the Debugger namespace. 595 using IsObserving = DebugAPI::IsObserving; 596 static const IsObserving Observing = DebugAPI::Observing; 597 static const IsObserving NotObserving = DebugAPI::NotObserving; 598 599 // Return true if the given realm is a debuggee of this debugger, 600 // false otherwise. 601 bool isDebuggeeUnbarriered(const Realm* realm) const; 602 603 // Return true if this Debugger observed a debuggee that participated in the 604 // GC identified by the given GC number. Return false otherwise. 605 // May return false negatives if we have hit OOM. 606 bool observedGC(uint64_t majorGCNumber) const { 607 return observedGCs.has(majorGCNumber); 608 } 609 610 // Notify this Debugger that one or more of its debuggees is participating 611 // in the GC identified by the given GC number. 612 bool debuggeeIsBeingCollected(uint64_t majorGCNumber) { 613 return observedGCs.put(majorGCNumber); 614 } 615 616 static SavedFrame* getObjectAllocationSite(JSObject& obj); 617 618 struct AllocationsLogEntry { 619 AllocationsLogEntry(HandleObject frame, mozilla::TimeStamp when, 620 const char* className, size_t size, bool inNursery) 621 : frame(frame), 622 when(when), 623 className(className), 624 size(size), 625 inNursery(inNursery) { 626 MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>() || 627 IsDeadProxyObject(frame)); 628 } 629 630 HeapPtr<JSObject*> frame; 631 mozilla::TimeStamp when; 632 const char* className; 633 size_t size; 634 bool inNursery; 635 636 void trace(JSTracer* trc) { 637 TraceNullableEdge(trc, &frame, "Debugger::AllocationsLogEntry::frame"); 638 } 639 }; 640 641 private: 642 HeapPtr<NativeObject*> object; /* The Debugger object. Strong reference. */ 643 WeakGlobalObjectSet 644 debuggees; /* Debuggee globals. Cross-compartment weak references. */ 645 JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */ 646 HeapPtr<JSObject*> uncaughtExceptionHook; /* Strong reference. */ 647 bool allowUnobservedAsmJS; 648 bool allowUnobservedWasm; 649 650 // When this flag is true, this debugger should be the only one to have its 651 // hooks called when it evaluates via Frame.evalWithBindings, 652 // Object.executeInGlobalWithBindings or Object.call. 653 bool exclusiveDebuggerOnEval; 654 655 // When this flag is true, the onNativeCall hook is called with additional 656 // arguments which are the native function call arguments and well as a 657 // reference to the object on which the function call (if any). 658 bool inspectNativeCallArguments; 659 660 // Whether to enable code coverage on the Debuggee. 661 bool collectCoverageInfo; 662 663 // Whether to ask avoid side-effects in the native code. 664 // See JS::dbg::ShouldAvoidSideEffects. 665 bool shouldAvoidSideEffects; 666 667 template <typename T> 668 struct DebuggerLinkAccess { 669 static mozilla::DoublyLinkedListElement<T>& Get(T* aThis) { 670 return aThis->debuggerLink; 671 } 672 static const mozilla::DoublyLinkedListElement<T>& Get(const T* aThis) { 673 return aThis->debuggerLink; 674 } 675 }; 676 677 // List of all js::Breakpoints in this debugger. 678 using BreakpointList = 679 mozilla::DoublyLinkedList<js::Breakpoint, 680 DebuggerLinkAccess<js::Breakpoint>>; 681 BreakpointList breakpoints; 682 683 // The set of GC numbers for which one or more of this Debugger's observed 684 // debuggees participated in. 685 using GCNumberSet = 686 HashSet<uint64_t, DefaultHasher<uint64_t>, ZoneAllocPolicy>; 687 GCNumberSet observedGCs; 688 689 using AllocationsLog = js::TraceableFifo<AllocationsLogEntry>; 690 691 AllocationsLog allocationsLog; 692 bool trackingAllocationSites; 693 double allocationSamplingProbability; 694 size_t maxAllocationsLogLength; 695 bool allocationsLogOverflowed; 696 697 static const size_t DEFAULT_MAX_LOG_LENGTH = 5000; 698 699 [[nodiscard]] bool appendAllocationSite(JSContext* cx, HandleObject obj, 700 Handle<SavedFrame*> frame, 701 mozilla::TimeStamp when); 702 703 /* 704 * Recompute the set of debuggee zones based on the set of debuggee globals. 705 */ 706 void recomputeDebuggeeZoneSet(); 707 708 /* 709 * Return true if there is an existing object metadata callback for the 710 * given global's compartment that will prevent our instrumentation of 711 * allocations. 712 */ 713 static bool cannotTrackAllocations(const GlobalObject& global); 714 715 /* 716 * Add allocations tracking for objects allocated within the given 717 * debuggee's compartment. The given debuggee global must be observed by at 718 * least one Debugger that is tracking allocations. 719 */ 720 [[nodiscard]] static bool addAllocationsTracking( 721 JSContext* cx, Handle<GlobalObject*> debuggee); 722 723 /* 724 * Remove allocations tracking for objects allocated within the given 725 * global's compartment. This is a no-op if there are still Debuggers 726 * observing this global and who are tracking allocations. 727 */ 728 static void removeAllocationsTracking(GlobalObject& global); 729 730 /* 731 * Add or remove allocations tracking for all debuggees. 732 */ 733 [[nodiscard]] bool addAllocationsTrackingForAllDebuggees(JSContext* cx); 734 void removeAllocationsTrackingForAllDebuggees(); 735 736 /* 737 * If this Debugger has a onNewGlobalObject handler, then 738 * this link is inserted into the list headed by 739 * JSRuntime::onNewGlobalObjectWatchers. 740 */ 741 mozilla::DoublyLinkedListElement<Debugger> onNewGlobalObjectWatchersLink; 742 743 /* 744 * If this Debugger has a onGarbageCollection handler, then 745 * this link is inserted into the list headed by 746 * JSRuntime::onGarbageCollectionWatchers. 747 */ 748 mozilla::DoublyLinkedListElement<Debugger> onGarbageCollectionWatchersLink; 749 750 /* 751 * Map from stack frames that are currently on the stack to Debugger.Frame 752 * instances. 753 * 754 * The keys are always live stack frames. We drop them from this map as 755 * soon as they leave the stack (see slowPathOnLeaveFrame) and in 756 * removeDebuggee. 757 * 758 * Wasm JS PI allows suspending/resuming a portion of the stack, only 759 * frame pointers and activations are changed. The stack frames are still 760 * live, and shall be present in the frames map if DebuggerFrame is created. 761 * 762 * We don't trace the keys of this map (the frames are on the stack and 763 * thus necessarily live), but we do trace the values. It's like a WeakMap 764 * that way, but since stack frames are not gc-things, the implementation 765 * has to be different. 766 */ 767 using FrameMap = HashMap<AbstractFramePtr, HeapPtr<DebuggerFrame*>, 768 DefaultHasher<AbstractFramePtr>, ZoneAllocPolicy>; 769 FrameMap frames; 770 771 /* 772 * Map from generator objects to their Debugger.Frame instances. 773 * 774 * When a Debugger.Frame is created for a generator frame, it is added to 775 * this map and remains there for the lifetime of the generator, whether 776 * that frame is on the stack at the moment or not. This is in addition to 777 * the entry in `frames` that exists as long as the generator frame is on 778 * the stack. 779 * 780 * We need to keep the Debugger.Frame object alive to deliver it to the 781 * onEnterFrame handler on resume, and to retain onStep and onPop hooks. 782 * 783 * An entry is present in this table when: 784 * - both the debuggee generator object and the Debugger.Frame object exists 785 * - the debuggee generator object belongs to a realm that is a debuggee of 786 * the Debugger.Frame's owner. 787 * 788 * regardless of whether the frame is currently suspended. (This list is 789 * meant to explain why we update the table in the particular places where 790 * we do so.) 791 * 792 * An entry in this table exists if and only if the Debugger.Frame's 793 * GENERATOR_INFO_SLOT is set. 794 */ 795 using GeneratorWeakMap = 796 DebuggerWeakMap<AbstractGeneratorObject, DebuggerFrame>; 797 GeneratorWeakMap generatorFrames; 798 799 // An ephemeral map from BaseScript* to Debugger.Script instances. 800 using ScriptWeakMap = DebuggerWeakMap<BaseScript, DebuggerScript>; 801 ScriptWeakMap scripts; 802 803 using BaseScriptVector = JS::GCVector<BaseScript*>; 804 805 // The map from debuggee source script objects to their Debugger.Source 806 // instances. 807 using SourceWeakMap = 808 DebuggerWeakMap<ScriptSourceObject, DebuggerSource, true>; 809 SourceWeakMap sources; 810 811 // The map from debuggee objects to their Debugger.Object instances. 812 using ObjectWeakMap = DebuggerWeakMap<JSObject, DebuggerObject>; 813 ObjectWeakMap objects; 814 815 // The map from debuggee Envs to Debugger.Environment instances. 816 using EnvironmentWeakMap = DebuggerWeakMap<JSObject, DebuggerEnvironment>; 817 EnvironmentWeakMap environments; 818 819 // The map from WasmInstanceObjects to synthesized Debugger.Script 820 // instances. 821 using WasmInstanceScriptWeakMap = 822 DebuggerWeakMap<WasmInstanceObject, DebuggerScript>; 823 WasmInstanceScriptWeakMap wasmInstanceScripts; 824 825 // The map from WasmInstanceObjects to synthesized Debugger.Source 826 // instances. 827 using WasmInstanceSourceWeakMap = 828 DebuggerWeakMap<WasmInstanceObject, DebuggerSource>; 829 WasmInstanceSourceWeakMap wasmInstanceSources; 830 831 class QueryBase; 832 class ScriptQuery; 833 class SourceQuery; 834 class ObjectQuery; 835 836 enum class FromSweep { No, Yes }; 837 838 [[nodiscard]] bool addDebuggeeGlobal(JSContext* cx, 839 Handle<GlobalObject*> obj); 840 void removeDebuggeeGlobal(JS::GCContext* gcx, GlobalObject* global, 841 WeakGlobalObjectSet::Enum* debugEnum, 842 FromSweep fromSweep); 843 844 /* 845 * Handle the result of a hook that is expected to return a resumption 846 * value <https://wiki.mozilla.org/Debugger#Resumption_Values>. This is 847 * called when we return from a debugging hook to debuggee code. 848 * 849 * If `success` is false, the hook failed. If an exception is pending in 850 * ar.context(), attempt to handle it via the uncaught exception hook, 851 * otherwise report it to the AutoRealm's global. 852 * 853 * If `success` is true, there must be no exception pending in ar.context(). 854 * `rv` may be: 855 * 856 * undefined - Set `resultMode` to `ResumeMode::Continue` to continue 857 * execution normally. 858 * 859 * {return: value} or {throw: value} - Call unwrapDebuggeeValue to 860 * unwrap `value`. Store the result in `vp` and set `resultMode` to 861 * `ResumeMode::Return` or `ResumeMode::Throw`. The interpreter 862 * will force the current frame to return or throw an exception. 863 * 864 * null - Set `resultMode` to `ResumeMode::Terminate` to terminate the 865 * debuggee with an uncatchable error. 866 * 867 * anything else - Make a new TypeError the pending exception and 868 * attempt to handle it with the uncaught exception handler. 869 */ 870 [[nodiscard]] bool processHandlerResult( 871 JSContext* cx, bool success, HandleValue rv, AbstractFramePtr frame, 872 jsbytecode* pc, ResumeMode& resultMode, MutableHandleValue vp); 873 874 [[nodiscard]] bool processParsedHandlerResult( 875 JSContext* cx, AbstractFramePtr frame, const jsbytecode* pc, bool success, 876 ResumeMode resumeMode, HandleValue value, ResumeMode& resultMode, 877 MutableHandleValue vp); 878 879 /** 880 * Given a resumption return value from a hook, parse and validate it based 881 * on the given frame, and split the result into a ResumeMode and Value. 882 */ 883 [[nodiscard]] bool prepareResumption(JSContext* cx, AbstractFramePtr frame, 884 const jsbytecode* pc, 885 ResumeMode& resumeMode, 886 MutableHandleValue vp); 887 888 /** 889 * If there is a pending exception and a handler, call the handler with the 890 * exception so that it can attempt to resolve the error. 891 */ 892 [[nodiscard]] bool callUncaughtExceptionHandler(JSContext* cx, 893 MutableHandleValue vp); 894 895 /** 896 * If the context has a pending exception, report it to the current global. 897 */ 898 void reportUncaughtException(JSContext* cx); 899 900 /* 901 * Call the uncaught exception handler if there is one, returning true 902 * if it handled the error, or false otherwise. 903 */ 904 [[nodiscard]] bool handleUncaughtException(JSContext* cx); 905 906 GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v); 907 908 static void traceObject(JSTracer* trc, JSObject* obj); 909 910 void trace(JSTracer* trc); 911 912 void traceForMovingGC(JSTracer* trc); 913 void traceCrossCompartmentEdges(JSTracer* tracer); 914 915 private: 916 template <typename F> 917 void forEachWeakMap(const F& f); 918 919 [[nodiscard]] static bool getHookImpl(JSContext* cx, const CallArgs& args, 920 Debugger& dbg, Hook which); 921 [[nodiscard]] static bool setHookImpl(JSContext* cx, const CallArgs& args, 922 Debugger& dbg, Hook which); 923 924 [[nodiscard]] static bool getGarbageCollectionHook(JSContext* cx, 925 const CallArgs& args, 926 Debugger& dbg); 927 [[nodiscard]] static bool setGarbageCollectionHook(JSContext* cx, 928 const CallArgs& args, 929 Debugger& dbg); 930 931 static bool isCompilableUnit(JSContext* cx, unsigned argc, Value* vp); 932 static bool recordReplayProcessKind(JSContext* cx, unsigned argc, Value* vp); 933 static bool construct(JSContext* cx, unsigned argc, Value* vp); 934 935 struct CallData; 936 937 static const JSPropertySpec properties[]; 938 static const JSFunctionSpec methods[]; 939 static const JSPropertySpec static_properties[]; 940 static const JSFunctionSpec static_methods[]; 941 942 /** 943 * Suspend the DebuggerFrame, clearing on-stack data but leaving it linked 944 * with the AbstractGeneratorObject so it can be re-used later. 945 */ 946 static void suspendGeneratorDebuggerFrames(JSContext* cx, 947 AbstractFramePtr frame); 948 949 /** 950 * Terminate the DebuggerFrame, clearing all data associated with the frame 951 * so that it cannot be used to introspect stack frame data. 952 */ 953 static void terminateDebuggerFrames(JSContext* cx, AbstractFramePtr frame); 954 955 /** 956 * Terminate a given DebuggerFrame, removing all internal state and all 957 * references to the frame from the Debugger itself. If the frame is being 958 * terminated while 'frames' or 'generatorFrames' are being iterated, pass a 959 * pointer to the iteration Enum to remove the entry and ensure that iteration 960 * behaves properly. 961 * 962 * The AbstractFramePtr may be omited in a call so long as it is either 963 * called again later with the correct 'frame', or the frame itself has never 964 * had on-stack data or a 'frames' entry and has never had an onStep handler. 965 */ 966 static void terminateDebuggerFrame( 967 JS::GCContext* gcx, Debugger* dbg, DebuggerFrame* dbgFrame, 968 AbstractFramePtr frame, FrameMap::Enum* maybeFramesEnum = nullptr, 969 GeneratorWeakMap::Enum* maybeGeneratorFramesEnum = nullptr); 970 971 static bool updateExecutionObservabilityOfFrames( 972 JSContext* cx, const DebugAPI::ExecutionObservableSet& obs, 973 IsObserving observing); 974 static bool updateExecutionObservabilityOfScripts( 975 JSContext* cx, const DebugAPI::ExecutionObservableSet& obs, 976 IsObserving observing); 977 static bool updateExecutionObservability( 978 JSContext* cx, DebugAPI::ExecutionObservableSet& obs, 979 IsObserving observing); 980 981 template <typename FrameFn /* void (Debugger*, DebuggerFrame*) */> 982 static void forEachOnStackDebuggerFrame(AbstractFramePtr frame, 983 const JS::AutoRequireNoGC& nogc, 984 FrameFn fn); 985 template <typename FrameFn /* void (Debugger*, DebuggerFrame*) */> 986 static void forEachOnStackOrSuspendedDebuggerFrame( 987 JSContext* cx, AbstractFramePtr frame, const JS::AutoRequireNoGC& nogc, 988 FrameFn fn); 989 990 /* 991 * Return a vector containing all Debugger.Frame instances referring to 992 * |frame|. |global| is |frame|'s global object; if nullptr or omitted, we 993 * compute it ourselves from |frame|. 994 */ 995 using DebuggerFrameVector = GCVector<DebuggerFrame*, 0, SystemAllocPolicy>; 996 [[nodiscard]] static bool getDebuggerFrames( 997 AbstractFramePtr frame, MutableHandle<DebuggerFrameVector> frames); 998 999 public: 1000 // Public for DebuggerScript::setBreakpoint. 1001 [[nodiscard]] static bool ensureExecutionObservabilityOfScript( 1002 JSContext* cx, JSScript* script); 1003 1004 // Whether the Debugger instance needs to observe all non-AOT JS 1005 // execution of its debugees. 1006 IsObserving observesAllExecution() const; 1007 1008 // Whether the Debugger instance needs to observe AOT-compiled asm.js 1009 // execution of its debuggees. 1010 IsObserving observesAsmJS() const; 1011 1012 // Whether the Debugger instance needs to observe compiled Wasm 1013 // execution of its debuggees. 1014 IsObserving observesWasm() const; 1015 1016 // Whether the Debugger instance needs to observe coverage of any JavaScript 1017 // execution. 1018 IsObserving observesCoverage() const; 1019 1020 // Whether the Debugger instance needs to observe native call invocations. 1021 IsObserving observesNativeCalls() const; 1022 1023 bool isExclusiveDebuggerOnEval() const; 1024 1025 private: 1026 [[nodiscard]] static bool ensureExecutionObservabilityOfFrame( 1027 JSContext* cx, AbstractFramePtr frame); 1028 [[nodiscard]] static bool ensureExecutionObservabilityOfRealm( 1029 JSContext* cx, JS::Realm* realm); 1030 1031 static bool hookObservesAllExecution(Hook which); 1032 1033 [[nodiscard]] bool updateObservesAllExecutionOnDebuggees( 1034 JSContext* cx, IsObserving observing); 1035 [[nodiscard]] bool updateObservesCoverageOnDebuggees(JSContext* cx, 1036 IsObserving observing); 1037 void updateObservesAsmJSOnDebuggees(IsObserving observing); 1038 void updateObservesWasmOnDebuggees(IsObserving observing); 1039 void updateObservesNativeCallOnDebuggees(IsObserving observing); 1040 1041 JSObject* getHook(Hook hook) const; 1042 bool hasAnyLiveHooks() const; 1043 inline bool isHookCallAllowed(JSContext* cx) const; 1044 1045 static void slowPathPromiseHook(JSContext* cx, Hook hook, 1046 Handle<PromiseObject*> promise); 1047 1048 template <typename HookIsEnabledFun /* bool (Debugger*) */, 1049 typename FireHookFun /* void (Debugger*) */> 1050 static void dispatchQuietHook(JSContext* cx, HookIsEnabledFun hookIsEnabled, 1051 FireHookFun fireHook); 1052 template < 1053 typename HookIsEnabledFun /* bool (Debugger*) */, typename FireHookFun /* bool (Debugger*, ResumeMode&, MutableHandleValue) */> 1054 [[nodiscard]] static bool dispatchResumptionHook( 1055 JSContext* cx, AbstractFramePtr frame, HookIsEnabledFun hookIsEnabled, 1056 FireHookFun fireHook); 1057 1058 template <typename RunImpl /* bool () */> 1059 [[nodiscard]] bool enterDebuggerHook(JSContext* cx, RunImpl runImpl) { 1060 if (!isHookCallAllowed(cx)) { 1061 return true; 1062 } 1063 1064 AutoRealm ar(cx, object); 1065 1066 if (!runImpl()) { 1067 // We do not want errors within one hook to effect errors in other hooks, 1068 // so the only errors that we allow to propagate out of a debugger hook 1069 // are OOM errors and general terminations. 1070 if (!cx->isExceptionPending() || cx->isThrowingOutOfMemory()) { 1071 return false; 1072 } 1073 1074 reportUncaughtException(cx); 1075 } 1076 MOZ_ASSERT(!cx->isExceptionPending()); 1077 return true; 1078 } 1079 1080 [[nodiscard]] bool fireDebuggerStatement(JSContext* cx, 1081 ResumeMode& resumeMode, 1082 MutableHandleValue vp); 1083 [[nodiscard]] bool fireExceptionUnwind(JSContext* cx, HandleValue exc, 1084 ResumeMode& resumeMode, 1085 MutableHandleValue vp); 1086 [[nodiscard]] bool fireEnterFrame(JSContext* cx, ResumeMode& resumeMode, 1087 MutableHandleValue vp); 1088 [[nodiscard]] bool fireNativeCall(JSContext* cx, const CallArgs& args, 1089 CallReason reason, ResumeMode& resumeMode, 1090 MutableHandleValue vp); 1091 [[nodiscard]] bool fireNewGlobalObject(JSContext* cx, 1092 Handle<GlobalObject*> global); 1093 [[nodiscard]] bool firePromiseHook(JSContext* cx, Hook hook, 1094 HandleObject promise); 1095 1096 DebuggerScript* newVariantWrapper(JSContext* cx, 1097 Handle<DebuggerScriptReferent> referent) { 1098 return newDebuggerScript(cx, referent); 1099 } 1100 DebuggerSource* newVariantWrapper(JSContext* cx, 1101 Handle<DebuggerSourceReferent> referent) { 1102 return newDebuggerSource(cx, referent); 1103 } 1104 1105 /* 1106 * Helper function to help wrap Debugger objects whose referents may be 1107 * variants. Currently Debugger.Script and Debugger.Source referents may 1108 * be variants. 1109 * 1110 * Prefer using wrapScript, wrapWasmScript, wrapSource, and wrapWasmSource 1111 * whenever possible. 1112 */ 1113 template <typename ReferentType, typename Map> 1114 typename Map::WrapperType* wrapVariantReferent( 1115 JSContext* cx, Map& map, 1116 Handle<typename Map::WrapperType::ReferentVariant> referent); 1117 DebuggerScript* wrapVariantReferent(JSContext* cx, 1118 Handle<DebuggerScriptReferent> referent); 1119 DebuggerSource* wrapVariantReferent(JSContext* cx, 1120 Handle<DebuggerSourceReferent> referent); 1121 1122 /* 1123 * Allocate and initialize a Debugger.Script instance whose referent is 1124 * |referent|. 1125 */ 1126 DebuggerScript* newDebuggerScript(JSContext* cx, 1127 Handle<DebuggerScriptReferent> referent); 1128 1129 /* 1130 * Allocate and initialize a Debugger.Source instance whose referent is 1131 * |referent|. 1132 */ 1133 DebuggerSource* newDebuggerSource(JSContext* cx, 1134 Handle<DebuggerSourceReferent> referent); 1135 1136 /* 1137 * Receive a "new script" event from the engine. A new script was compiled 1138 * or deserialized. 1139 */ 1140 [[nodiscard]] bool fireNewScript( 1141 JSContext* cx, Handle<DebuggerScriptReferent> scriptReferent); 1142 1143 /* 1144 * Receive a "garbage collection" event from the engine. A GC cycle with the 1145 * given data was recently completed. 1146 */ 1147 [[nodiscard]] bool fireOnGarbageCollectionHook( 1148 JSContext* cx, const JS::dbg::GarbageCollectionEvent::Ptr& gcData); 1149 1150 inline Breakpoint* firstBreakpoint() const; 1151 1152 [[nodiscard]] static bool replaceFrameGuts(JSContext* cx, 1153 AbstractFramePtr from, 1154 AbstractFramePtr to, 1155 ScriptFrameIter& iter); 1156 1157 public: 1158 Debugger(JSContext* cx, NativeObject* dbg); 1159 ~Debugger(); 1160 1161 inline const js::HeapPtr<NativeObject*>& toJSObject() const; 1162 inline js::HeapPtr<NativeObject*>& toJSObjectRef(); 1163 static inline Debugger* fromJSObject(const JSObject* obj); 1164 1165 #ifdef DEBUG 1166 static bool isChildJSObject(JSObject* obj); 1167 #endif 1168 1169 Zone* zone() const { return toJSObject()->zone(); } 1170 1171 bool hasMemory() const; 1172 DebuggerMemory& memory() const; 1173 1174 WeakGlobalObjectSet::Range allDebuggees() const { return debuggees.all(); } 1175 1176 #ifdef DEBUG 1177 static bool isDebuggerCrossCompartmentEdge(JSObject* obj, 1178 const js::gc::Cell* cell); 1179 #endif 1180 1181 static bool hasLiveHook(GlobalObject* global, Hook which); 1182 1183 /*** Functions for use by Debugger.cpp. *********************************/ 1184 1185 inline bool observesEnterFrame() const; 1186 inline bool observesNewScript() const; 1187 inline bool observesNewGlobalObject() const; 1188 inline bool observesGlobal(GlobalObject* global) const; 1189 bool observesFrame(AbstractFramePtr frame) const; 1190 bool observesFrame(const FrameIter& iter) const; 1191 bool observesScript(JSScript* script) const; 1192 bool observesWasm(wasm::Instance* instance) const; 1193 1194 /* 1195 * If env is nullptr, call vp->setNull() and return true. Otherwise, find 1196 * or create a Debugger.Environment object for the given Env. On success, 1197 * store the Environment object in *vp and return true. 1198 */ 1199 [[nodiscard]] bool wrapEnvironment(JSContext* cx, Handle<Env*> env, 1200 MutableHandleValue vp); 1201 [[nodiscard]] bool wrapEnvironment( 1202 JSContext* cx, Handle<Env*> env, 1203 MutableHandle<DebuggerEnvironment*> result); 1204 1205 /* 1206 * Like cx->compartment()->wrap(cx, vp), but for the debugger realm. 1207 * 1208 * Preconditions: *vp is a value from a debuggee realm; cx is in the 1209 * debugger's compartment. 1210 * 1211 * If *vp is an object, this produces a (new or existing) Debugger.Object 1212 * wrapper for it. Otherwise this is the same as Compartment::wrap. 1213 * 1214 * If *vp is a magic JS_OPTIMIZED_OUT value, this produces a plain object 1215 * of the form { optimizedOut: true }. 1216 * 1217 * If *vp is a magic JS_MISSING_ARGUMENTS value signifying missing 1218 * arguments, this produces a plain object of the form { missingArguments: 1219 * true }. 1220 * 1221 * If *vp is a magic JS_UNINITIALIZED_LEXICAL value signifying an 1222 * unaccessible uninitialized binding, this produces a plain object of the 1223 * form { uninitialized: true }. 1224 */ 1225 [[nodiscard]] bool wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp); 1226 [[nodiscard]] bool wrapDebuggeeObject(JSContext* cx, HandleObject obj, 1227 MutableHandle<DebuggerObject*> result); 1228 [[nodiscard]] bool wrapNullableDebuggeeObject( 1229 JSContext* cx, HandleObject obj, MutableHandle<DebuggerObject*> result); 1230 1231 /* 1232 * Unwrap a Debug.Object, without rewrapping it for any particular debuggee 1233 * compartment. 1234 * 1235 * Preconditions: cx is in the debugger compartment. *vp is a value in that 1236 * compartment. (*vp should be a "debuggee value", meaning it is the 1237 * debugger's reflection of a value in the debuggee.) 1238 * 1239 * If *vp is a Debugger.Object, store the referent in *vp. Otherwise, if *vp 1240 * is an object, throw a TypeError, because it is not a debuggee 1241 * value. Otherwise *vp is a primitive, so leave it alone. 1242 * 1243 * When passing values from the debuggee to the debugger: 1244 * enter debugger compartment; 1245 * call wrapDebuggeeValue; // compartment- and debugger-wrapping 1246 * 1247 * When passing values from the debugger to the debuggee: 1248 * call unwrapDebuggeeValue; // debugger-unwrapping 1249 * enter debuggee realm; 1250 * call cx->compartment()->wrap; // compartment-rewrapping 1251 * 1252 * (Extreme nerd sidebar: Unwrapping happens in two steps because there are 1253 * two different kinds of symmetry at work: regardless of which direction 1254 * we're going, we want any exceptions to be created and thrown in the 1255 * debugger compartment--mirror symmetry. But compartment wrapping always 1256 * happens in the target compartment--rotational symmetry.) 1257 */ 1258 [[nodiscard]] bool unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp); 1259 [[nodiscard]] bool unwrapDebuggeeObject(JSContext* cx, 1260 MutableHandleObject obj); 1261 [[nodiscard]] bool unwrapPropertyDescriptor( 1262 JSContext* cx, HandleObject obj, MutableHandle<PropertyDescriptor> desc); 1263 1264 /* 1265 * Store the Debugger.Frame object for iter in *vp/result. 1266 * 1267 * If this Debugger does not already have a Frame object for the frame 1268 * `iter` points to, a new Frame object is created, and `iter`'s private 1269 * data is copied into it. 1270 */ 1271 [[nodiscard]] bool getFrame(JSContext* cx, const FrameIter& iter, 1272 MutableHandleValue vp); 1273 [[nodiscard]] bool getFrame(JSContext* cx, 1274 MutableHandle<DebuggerFrame*> result); 1275 [[nodiscard]] bool getFrame(JSContext* cx, const FrameIter& iter, 1276 MutableHandle<DebuggerFrame*> result); 1277 [[nodiscard]] bool getFrame(JSContext* cx, 1278 Handle<AbstractGeneratorObject*> genObj, 1279 MutableHandle<DebuggerFrame*> result); 1280 1281 /* 1282 * Return the Debugger.Script object for |script|, or create a new one if 1283 * needed. The context |cx| must be in the debugger realm; |script| must be 1284 * a script in a debuggee realm. 1285 */ 1286 DebuggerScript* wrapScript(JSContext* cx, Handle<BaseScript*> script); 1287 1288 /* 1289 * Return the Debugger.Script object for |wasmInstance| (the toplevel 1290 * script), synthesizing a new one if needed. The context |cx| must be in 1291 * the debugger compartment; |wasmInstance| must be a WasmInstanceObject in 1292 * the debuggee realm. 1293 */ 1294 DebuggerScript* wrapWasmScript(JSContext* cx, 1295 Handle<WasmInstanceObject*> wasmInstance); 1296 1297 /* 1298 * Return the Debugger.Source object for |source|, or create a new one if 1299 * needed. The context |cx| must be in the debugger compartment; |source| 1300 * must be a script source object in a debuggee realm. 1301 */ 1302 DebuggerSource* wrapSource(JSContext* cx, 1303 js::Handle<ScriptSourceObject*> source); 1304 1305 /* 1306 * Return the Debugger.Source object for |wasmInstance| (the entire module), 1307 * synthesizing a new one if needed. The context |cx| must be in the 1308 * debugger compartment; |wasmInstance| must be a WasmInstanceObject in the 1309 * debuggee realm. 1310 */ 1311 DebuggerSource* wrapWasmSource(JSContext* cx, 1312 Handle<WasmInstanceObject*> wasmInstance); 1313 1314 DebuggerDebuggeeLink* getDebuggeeLink(); 1315 1316 private: 1317 Debugger(const Debugger&) = delete; 1318 Debugger& operator=(const Debugger&) = delete; 1319 }; 1320 1321 // Specialize InternalBarrierMethods so we can have WeakHeapPtr<Debugger*>. 1322 template <> 1323 struct InternalBarrierMethods<Debugger*> { 1324 static bool isMarkable(Debugger* dbg) { return dbg->toJSObject(); } 1325 1326 static void postBarrier(Debugger** vp, Debugger* prev, Debugger* next) {} 1327 1328 static void readBarrier(Debugger* dbg) { 1329 InternalBarrierMethods<JSObject*>::readBarrier(dbg->toJSObject()); 1330 } 1331 1332 #ifdef DEBUG 1333 static void assertThingIsNotGray(Debugger* dbg) {} 1334 #endif 1335 }; 1336 1337 /** 1338 * This class exists for one specific reason. If a given Debugger object is in 1339 * a state where: 1340 * 1341 * a) nothing in the system has a reference to the object 1342 * b) the debugger is currently attached to a live debuggee 1343 * c) the debugger has hooks like 'onEnterFrame' 1344 * 1345 * then we don't want the GC to delete the Debugger, because the system could 1346 * still call the hooks. This means we need to ensure that, whenever the global 1347 * gets marked, the Debugger will get marked as well. Critically, we _only_ 1348 * want that to happen if the debugger has hooks. If it doesn't, then GCing 1349 * the debugger is the right think to do. 1350 * 1351 * Note that there are _other_ cases where the debugger may be held live, but 1352 * those are not addressed by this case. 1353 * 1354 * To accomplish this, we use a bit of roundabout link approach. Both the 1355 * Debugger and the debuggees can reach the link object: 1356 * 1357 * Debugger -> DebuggerDebuggeeLink <- CCW <- Debuggee Global #1 1358 * | | ^ ^---<- CCW <- Debuggee Global #2 1359 * \--<<-optional-<<--/ \------<- CCW <- Debuggee Global #3 1360 * 1361 * and critically, the Debugger is able to conditionally add or remove the link 1362 * going from the DebuggerDebuggeeLink _back_ to the Debugger. When this link 1363 * exists, the GC can trace all the way from the global to the Debugger, 1364 * meaning that any Debugger with this link will be kept alive as long as any 1365 * of its debuggees are alive. 1366 */ 1367 class DebuggerDebuggeeLink : public NativeObject { 1368 private: 1369 enum { 1370 DEBUGGER_LINK_SLOT, 1371 RESERVED_SLOTS, 1372 }; 1373 1374 public: 1375 static const JSClass class_; 1376 1377 void setLinkSlot(Debugger& dbg); 1378 void clearLinkSlot(); 1379 }; 1380 1381 /* 1382 * A Handler represents a Debugger API reflection object's handler function, 1383 * like a Debugger.Frame's onStep handler. These handler functions are called by 1384 * the Debugger API to notify the user of certain events. For each event type, 1385 * we define a separate subclass of Handler. 1386 * 1387 * When a reflection object accepts a Handler, it calls its 'hold' method; and 1388 * if the Handler is replaced by another, or the reflection object is finalized, 1389 * the reflection object calls the Handler's 'drop' method. The reflection 1390 * object does not otherwise manage the Handler's lifetime, say, by calling its 1391 * destructor or freeing its memory. A simple Handler implementation might have 1392 * an empty 'hold' method, and have its 'drop' method delete the Handler. A more 1393 * complex Handler might process many kinds of events, and thus inherit from 1394 * many Handler subclasses and be held by many reflection objects 1395 * simultaneously; a handler like this could use 'hold' and 'drop' to manage a 1396 * reference count. 1397 * 1398 * To support SpiderMonkey's memory use tracking, 'hold' and 'drop' also require 1399 * a pointer to the owning reflection object, so that the Holder implementation 1400 * can properly report changes in ownership to functions using the 1401 * js::gc::MemoryUse categories. 1402 */ 1403 struct Handler { 1404 virtual ~Handler() = default; 1405 1406 /* 1407 * If this Handler is a reference to a callable JSObject, return that 1408 * JSObject. Otherwise, this method returns nullptr. 1409 * 1410 * The JavaScript getters for handler properties on reflection objects use 1411 * this method to obtain the callable the handler represents. When a Handler's 1412 * 'object' method returns nullptr, that handler is simply not visible to 1413 * JavaScript. 1414 */ 1415 virtual JSObject* object() const = 0; 1416 1417 /* Report that this Handler is now held by owner. See comment above. */ 1418 virtual void hold(JSObject* owner) = 0; 1419 1420 /* Report that this Handler is no longer held by owner. See comment above. */ 1421 virtual void drop(JS::GCContext* gcx, JSObject* owner) = 0; 1422 1423 /* 1424 * Trace the reference to the handler. This method will be called by the 1425 * reflection object holding this Handler whenever the former is traced. 1426 */ 1427 virtual void trace(JSTracer* tracer) = 0; 1428 1429 /* Allocation size in bytes for memory accounting purposes. */ 1430 virtual size_t allocSize() const = 0; 1431 }; 1432 1433 class JSBreakpointSite; 1434 class WasmBreakpointSite; 1435 1436 /** 1437 * Breakpoint GC rules: 1438 * 1439 * BreakpointSites and Breakpoints are owned by the code in which they are set. 1440 * Tracing a JSScript or WasmInstance traces all BreakpointSites set in it, 1441 * which traces all Breakpoints; and if the code is garbage collected, the 1442 * BreakpointSite and the Breakpoints set at it are freed as well. Doing so is 1443 * not observable to JS, since the handlers would never fire, and there is no 1444 * way to enumerate all breakpoints without specifying a specific script, in 1445 * which case it must not have been GC'd. 1446 * 1447 * Although BreakpointSites and Breakpoints are not GC things, they should be 1448 * treated as belonging to the code's compartment. This means that the 1449 * BreakpointSite concrete subclasses' pointers to the code are not 1450 * cross-compartment references, but a Breakpoint's pointers to its handler and 1451 * owning Debugger are cross-compartment references, and go through 1452 * cross-compartment wrappers. 1453 */ 1454 1455 /** 1456 * A location in a JSScript or WasmInstance at which we have breakpoints. A 1457 * BreakpointSite owns a linked list of all the breakpoints set at its location. 1458 * In general, this list contains breakpoints set by multiple Debuggers in 1459 * various compartments. 1460 * 1461 * BreakpointSites are created only as needed, for locations at which 1462 * breakpoints are currently set. When the last breakpoint is removed from a 1463 * location, the BreakpointSite is removed as well. 1464 * 1465 * This is an abstract base class, with subclasses specialized for the different 1466 * sorts of code a breakpoint might be set in. JSBreakpointSite manages sites in 1467 * JSScripts, and WasmBreakpointSite manages sites in WasmInstances. 1468 */ 1469 class BreakpointSite { 1470 friend class DebugAPI; 1471 friend class Breakpoint; 1472 friend class Debugger; 1473 1474 private: 1475 template <typename T> 1476 struct SiteLinkAccess { 1477 static mozilla::DoublyLinkedListElement<T>& Get(T* aThis) { 1478 return aThis->siteLink; 1479 } 1480 static const mozilla::DoublyLinkedListElement<T>& Get(const T* aThis) { 1481 return aThis->siteLink; 1482 } 1483 }; 1484 1485 // List of all js::Breakpoints at this instruction. 1486 using BreakpointList = 1487 mozilla::DoublyLinkedList<js::Breakpoint, SiteLinkAccess<js::Breakpoint>>; 1488 BreakpointList breakpoints; 1489 1490 protected: 1491 BreakpointSite() = default; 1492 virtual ~BreakpointSite() = default; 1493 void finalize(JS::GCContext* gcx); 1494 virtual gc::Cell* owningCell() = 0; 1495 1496 public: 1497 Breakpoint* firstBreakpoint() const; 1498 bool hasBreakpoint(Breakpoint* bp); 1499 1500 bool isEmpty() const; 1501 virtual void trace(JSTracer* trc); 1502 virtual void remove(JS::GCContext* gcx) = 0; 1503 void destroyIfEmpty(JS::GCContext* gcx) { 1504 if (isEmpty()) { 1505 remove(gcx); 1506 } 1507 } 1508 virtual Realm* realm() const = 0; 1509 }; 1510 1511 /* 1512 * A breakpoint set at a given BreakpointSite, indicating the owning debugger 1513 * and the handler object. A Breakpoint is a member of two linked lists: its 1514 * owning debugger's list and its site's list. 1515 */ 1516 class Breakpoint { 1517 friend class DebugAPI; 1518 friend class Debugger; 1519 friend class BreakpointSite; 1520 1521 public: 1522 /* Our owning debugger. */ 1523 Debugger* const debugger; 1524 1525 /** 1526 * A cross-compartment wrapper for our owning debugger's object, a CCW in the 1527 * code's compartment to the Debugger object in its own compartment. Holding 1528 * this lets the GC know about the effective cross-compartment reference from 1529 * the code to the debugger; see "Breakpoint GC Rules", above. 1530 * 1531 * This is almost redundant with the `debugger` field, except that we need 1532 * access to our owning `Debugger` regardless of the relative privilege levels 1533 * of debugger and debuggee, regardless of whether we're in the midst of a GC, 1534 * and so on - unwrapping is just too entangled. 1535 */ 1536 const HeapPtr<JSObject*> wrappedDebugger; 1537 1538 /* The site at which we're inserted. */ 1539 BreakpointSite* const site; 1540 1541 private: 1542 /** 1543 * The breakpoint handler object, via a cross-compartment wrapper in the 1544 * code's compartment. 1545 * 1546 * Although eventually we would like this to be a `js::Handler` instance, for 1547 * now it is just cross-compartment wrapper for the JS object supplied to 1548 * `setBreakpoint`, hopefully with a callable `hit` property. 1549 */ 1550 const HeapPtr<JSObject*> handler; 1551 1552 /** 1553 * Link elements for each list this breakpoint can be in. 1554 */ 1555 mozilla::DoublyLinkedListElement<Breakpoint> debuggerLink; 1556 mozilla::DoublyLinkedListElement<Breakpoint> siteLink; 1557 1558 void trace(JSTracer* trc); 1559 1560 public: 1561 Breakpoint(Debugger* debugger, HandleObject wrappedDebugger, 1562 BreakpointSite* site, HandleObject handler); 1563 1564 enum MayDestroySite { False, True }; 1565 1566 /** 1567 * Unlink this breakpoint from its Debugger's and and BreakpointSite's lists, 1568 * and free its memory. 1569 * 1570 * This is the low-level primitive shared by breakpoint removal and script 1571 * finalization code. It is only concerned with cleaning up this Breakpoint; 1572 * it does not check for now-empty BreakpointSites, unneeded DebugScripts, or 1573 * the like. 1574 */ 1575 void delete_(JS::GCContext* gcx); 1576 1577 /** 1578 * Remove this breakpoint. Unlink it from its Debugger's and BreakpointSite's 1579 * lists, and if the BreakpointSite is now empty, clean that up and update JIT 1580 * code as necessary. 1581 */ 1582 void remove(JS::GCContext* gcx); 1583 1584 Breakpoint* nextInDebugger(); 1585 Breakpoint* nextInSite(); 1586 JSObject* getHandler() const { return handler; } 1587 }; 1588 1589 class JSBreakpointSite : public BreakpointSite { 1590 public: 1591 const HeapPtr<JSScript*> script; 1592 jsbytecode* const pc; 1593 1594 public: 1595 JSBreakpointSite(JSScript* script, jsbytecode* pc); 1596 1597 void trace(JSTracer* trc) override; 1598 void delete_(JS::GCContext* gcx); 1599 void remove(JS::GCContext* gcx) override; 1600 Realm* realm() const override; 1601 1602 private: 1603 gc::Cell* owningCell() override; 1604 }; 1605 1606 class WasmBreakpointSite : public BreakpointSite { 1607 public: 1608 const HeapPtr<WasmInstanceObject*> instanceObject; 1609 uint32_t offset; 1610 1611 public: 1612 WasmBreakpointSite(WasmInstanceObject* instanceObject, uint32_t offset); 1613 1614 void trace(JSTracer* trc) override; 1615 void delete_(JS::GCContext* gcx); 1616 void remove(JS::GCContext* gcx) override; 1617 Realm* realm() const override; 1618 1619 private: 1620 gc::Cell* owningCell() override; 1621 }; 1622 1623 Breakpoint* Debugger::firstBreakpoint() const { 1624 if (breakpoints.isEmpty()) { 1625 return nullptr; 1626 } 1627 return &(*breakpoints.begin()); 1628 } 1629 1630 const js::HeapPtr<NativeObject*>& Debugger::toJSObject() const { 1631 MOZ_ASSERT(object); 1632 return object; 1633 } 1634 1635 js::HeapPtr<NativeObject*>& Debugger::toJSObjectRef() { 1636 MOZ_ASSERT(object); 1637 return object; 1638 } 1639 1640 bool Debugger::observesEnterFrame() const { return getHook(OnEnterFrame); } 1641 1642 bool Debugger::observesNewScript() const { return getHook(OnNewScript); } 1643 1644 bool Debugger::observesNewGlobalObject() const { 1645 return getHook(OnNewGlobalObject); 1646 } 1647 1648 bool Debugger::observesGlobal(GlobalObject* global) const { 1649 WeakHeapPtr<GlobalObject*> debuggee(global); 1650 return debuggees.has(debuggee); 1651 } 1652 1653 [[nodiscard]] bool ReportObjectRequired(JSContext* cx); 1654 1655 JSObject* IdVectorToArray(JSContext* cx, HandleIdVector ids); 1656 bool IsInterpretedNonSelfHostedFunction(JSFunction* fun); 1657 JSScript* GetOrCreateFunctionScript(JSContext* cx, HandleFunction fun); 1658 ArrayObject* GetFunctionParameterNamesArray(JSContext* cx, HandleFunction fun); 1659 bool ValueToIdentifier(JSContext* cx, HandleValue v, MutableHandleId id); 1660 bool ValueToStableChars(JSContext* cx, const char* fnname, HandleValue value, 1661 JS::AutoStableStringChars& stableChars); 1662 bool ParseEvalOptions(JSContext* cx, HandleValue value, EvalOptions& options); 1663 1664 Result<Completion> DebuggerGenericEval( 1665 JSContext* cx, const mozilla::Range<const char16_t> chars, 1666 HandleObject bindings, const EvalOptions& options, Debugger* dbg, 1667 HandleObject envArg, FrameIter* iter); 1668 1669 bool ParseResumptionValue(JSContext* cx, HandleValue rval, 1670 ResumeMode& resumeMode, MutableHandleValue vp); 1671 1672 #define JS_DEBUG_PSG(Name, Getter) \ 1673 JS_PSG(Name, CallData::ToNative<&CallData::Getter>, 0) 1674 1675 #define JS_DEBUG_PSGS(Name, Getter, Setter) \ 1676 JS_PSGS(Name, CallData::ToNative<&CallData::Getter>, \ 1677 CallData::ToNative<&CallData::Setter>, 0) 1678 1679 #define JS_DEBUG_FN(Name, Method, NumArgs) \ 1680 JS_FN(Name, CallData::ToNative<&CallData::Method>, NumArgs, 0) 1681 1682 } /* namespace js */ 1683 1684 #endif /* debugger_Debugger_h */