DebugScript.h (5479B)
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 dbg_DebugScript_h 8 #define dbg_DebugScript_h 9 10 #include <stddef.h> // for offsetof 11 #include <stddef.h> // for size_t 12 #include <stdint.h> // for uint32_t 13 14 #include "jstypes.h" 15 16 #include "gc/WeakMap.h" 17 #include "vm/NativeObject.h" 18 19 namespace JS { 20 class JS_PUBLIC_API Realm; 21 } 22 23 namespace js { 24 25 class JSBreakpointSite; 26 class Debugger; 27 class DebugScriptObject; 28 29 // DebugScript manages the internal debugger state for a JSScript, which may be 30 // associated with multiple Debuggers. 31 class DebugScript { 32 friend class DebugAPI; 33 friend class DebugScriptObject; 34 35 /* 36 * If this is a generator script, this is the number of Debugger.Frames 37 * referring to calls to this generator, whether live or suspended. Closed 38 * generators do not contribute a count. 39 * 40 * When greater than zero, this script should be compiled with debug 41 * instrumentation to call Debugger::onResumeFrame at each resumption site, so 42 * that Debugger can reconnect any extant Debugger.Frames with the new 43 * concrete frame. 44 */ 45 uint32_t generatorObserverCount; 46 47 /* 48 * The number of Debugger.Frame objects that refer to frames running this 49 * script and that have onStep handlers. When nonzero, the interpreter and JIT 50 * must arrange to call Debugger::onSingleStep before each bytecode, or at 51 * least at some useful granularity. 52 */ 53 uint32_t stepperCount; 54 55 /* 56 * The size of the script as reported by BaseScript::length. This is the 57 * length of the DebugScript::breakpoints array, below. 58 */ 59 size_t codeLength; 60 61 /* 62 * Number of breakpoint sites at opcodes in the script. This is the number 63 * of populated entries in DebugScript::breakpoints. 64 */ 65 uint32_t numSites; 66 67 /* 68 * Breakpoints set in our script. For speed and simplicity, this array is 69 * parallel to script->code(): the JSBreakpointSite for the opcode at 70 * script->code()[offset] is debugScript->breakpoints[offset]. 71 */ 72 JSBreakpointSite* breakpoints[1]; 73 74 /* 75 * True if this DebugScript carries any useful information. If false, it 76 * should be removed from its JSScript. 77 */ 78 bool needed() const { 79 return generatorObserverCount > 0 || stepperCount > 0 || numSites > 0; 80 } 81 82 static size_t allocSize(size_t codeLength) { 83 return offsetof(DebugScript, breakpoints) + 84 codeLength * sizeof(JSBreakpointSite*); 85 } 86 87 void trace(JSTracer* trc); 88 void delete_(JS::GCContext* gcx, DebugScriptObject* owner); 89 90 static DebugScript* get(JSScript* script); 91 static DebugScript* getUnbarriered(JSScript* script); 92 static DebugScript* getOrCreate(JSContext* cx, HandleScript script); 93 94 public: 95 static bool hasBreakpointSite(JSScript* script, jsbytecode* pc); 96 static JSBreakpointSite* getBreakpointSite(JSScript* script, jsbytecode* pc); 97 static JSBreakpointSite* getOrCreateBreakpointSite(JSContext* cx, 98 HandleScript script, 99 jsbytecode* pc); 100 static void destroyBreakpointSite(JS::GCContext* gcx, JSScript* script, 101 jsbytecode* pc); 102 103 static void clearBreakpointsIn(JS::GCContext* gcx, JSScript* script, 104 Debugger* dbg, JSObject* handler); 105 106 #ifdef DEBUG 107 static uint32_t getStepperCount(JSScript* script); 108 #endif 109 110 /* 111 * Increment or decrement the single-step count. If the count is non-zero 112 * then the script is in single-step mode. 113 * 114 * Only incrementing is fallible, as it could allocate a DebugScript. 115 */ 116 [[nodiscard]] static bool incrementStepperCount(JSContext* cx, 117 HandleScript script); 118 static void decrementStepperCount(JS::GCContext* gcx, JSScript* script); 119 120 /* 121 * Increment or decrement the generator observer count. If the count is 122 * non-zero then the script reports resumptions to the debugger. 123 * 124 * Only incrementing is fallible, as it could allocate a DebugScript. 125 */ 126 [[nodiscard]] static bool incrementGeneratorObserverCount( 127 JSContext* cx, HandleScript script); 128 static void decrementGeneratorObserverCount(JS::GCContext* gcx, 129 JSScript* script); 130 }; 131 132 using UniqueDebugScript = js::UniquePtr<DebugScript, JS::FreePolicy>; 133 134 // A JSObject that wraps a DebugScript, so we can use it as the value in a 135 // WeakMap. This object owns the DebugScript and is responsible for deleting it. 136 class DebugScriptObject : public NativeObject { 137 public: 138 static const JSClass class_; 139 140 enum { ScriptSlot, SlotCount }; 141 142 static DebugScriptObject* create(JSContext* cx, UniqueDebugScript debugScript, 143 size_t nbytes); 144 145 DebugScript* debugScript() const; 146 147 private: 148 static const JSClassOps classOps_; 149 150 static void trace(JSTracer* trc, JSObject* obj); 151 static void finalize(JS::GCContext* gcx, JSObject* obj); 152 }; 153 154 // A weak map from JSScripts to DebugScriptObjects. 155 class DebugScriptMap 156 : public WeakMap<JSScript*, DebugScriptObject*, ZoneAllocPolicy> { 157 public: 158 explicit DebugScriptMap(JSContext* cx) : WeakMap(cx) {} 159 }; 160 161 } /* namespace js */ 162 163 #endif /* dbg_DebugScript_h */