Frame.h (11802B)
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_Frame_h 8 #define debugger_Frame_h 9 10 #include "mozilla/Maybe.h" // for Maybe 11 #include "mozilla/Range.h" // for Range 12 #include "mozilla/Result.h" // for Result 13 14 #include <stddef.h> // for size_t 15 16 #include "NamespaceImports.h" // for Value, MutableHandleValue, HandleObject 17 #include "debugger/DebugAPI.h" // for ResumeMode 18 #include "debugger/Debugger.h" // for ResumeMode, Handler, Debugger 19 #include "gc/Barrier.h" // for HeapPtr 20 #include "vm/FrameIter.h" // for FrameIter 21 #include "vm/JSObject.h" // for JSObject 22 #include "vm/NativeObject.h" // for NativeObject 23 #include "vm/Stack.h" // for AbstractFramePtr 24 25 struct JS_PUBLIC_API JSContext; 26 27 namespace js { 28 29 class AbstractGeneratorObject; 30 class GlobalObject; 31 32 /* 33 * An OnStepHandler represents a handler function that is called when a small 34 * amount of progress is made in a frame. 35 */ 36 struct OnStepHandler : Handler { 37 /* 38 * If we have made a small amount of progress in a frame, this method is 39 * called with the frame as argument. If succesful, this method should 40 * return true, with `resumeMode` and `vp` set to a resumption value 41 * specifiying how execution should continue. 42 */ 43 virtual bool onStep(JSContext* cx, Handle<DebuggerFrame*> frame, 44 ResumeMode& resumeMode, MutableHandleValue vp) = 0; 45 }; 46 47 class ScriptedOnStepHandler final : public OnStepHandler { 48 public: 49 explicit ScriptedOnStepHandler(JSObject* object); 50 virtual JSObject* object() const override; 51 virtual void hold(JSObject* owner) override; 52 virtual void drop(JS::GCContext* gcx, JSObject* owner) override; 53 virtual void trace(JSTracer* tracer) override; 54 virtual size_t allocSize() const override; 55 virtual bool onStep(JSContext* cx, Handle<DebuggerFrame*> frame, 56 ResumeMode& resumeMode, MutableHandleValue vp) override; 57 58 private: 59 const HeapPtr<JSObject*> object_; 60 }; 61 62 /* 63 * An OnPopHandler represents a handler function that is called just before a 64 * frame is popped. 65 */ 66 struct OnPopHandler : Handler { 67 /* 68 * The given `frame` is about to be popped; `completion` explains why. 69 * 70 * When this method returns true, it must set `resumeMode` and `vp` to a 71 * resumption value specifying how execution should continue. 72 * 73 * When this method returns false, it should set an exception on `cx`. 74 */ 75 virtual bool onPop(JSContext* cx, Handle<DebuggerFrame*> frame, 76 const Completion& completion, ResumeMode& resumeMode, 77 MutableHandleValue vp) = 0; 78 }; 79 80 class ScriptedOnPopHandler final : public OnPopHandler { 81 public: 82 explicit ScriptedOnPopHandler(JSObject* object); 83 virtual JSObject* object() const override; 84 virtual void hold(JSObject* owner) override; 85 virtual void drop(JS::GCContext* gcx, JSObject* owner) override; 86 virtual void trace(JSTracer* tracer) override; 87 virtual size_t allocSize() const override; 88 virtual bool onPop(JSContext* cx, Handle<DebuggerFrame*> frame, 89 const Completion& completion, ResumeMode& resumeMode, 90 MutableHandleValue vp) override; 91 92 private: 93 const HeapPtr<JSObject*> object_; 94 }; 95 96 enum class DebuggerFrameType { Eval, Global, Call, Module, WasmCall }; 97 98 enum class DebuggerFrameImplementation { Interpreter, Baseline, Ion, Wasm }; 99 100 class DebuggerArguments : public NativeObject { 101 public: 102 static const JSClass class_; 103 104 static DebuggerArguments* create(JSContext* cx, HandleObject proto, 105 Handle<DebuggerFrame*> frame); 106 107 private: 108 enum { FRAME_SLOT }; 109 110 static const unsigned RESERVED_SLOTS = 1; 111 }; 112 113 class DebuggerFrame : public NativeObject { 114 friend class DebuggerArguments; 115 friend class ScriptedOnStepHandler; 116 friend class ScriptedOnPopHandler; 117 118 public: 119 static const JSClass class_; 120 121 enum { 122 FRAME_ITER_SLOT = 0, 123 OWNER_SLOT, 124 ARGUMENTS_SLOT, 125 ONSTEP_HANDLER_SLOT, 126 ONPOP_HANDLER_SLOT, 127 128 // If this is a frame for a generator call, and the generator object has 129 // been created (which doesn't happen until after default argument 130 // evaluation and destructuring), then this is a PrivateValue pointing to a 131 // GeneratorInfo struct that points to the call's AbstractGeneratorObject. 132 // This allows us to implement Debugger.Frame methods even while the call is 133 // suspended, and we have no FrameIter::Data. 134 // 135 // While Debugger::generatorFrames maps an AbstractGeneratorObject to its 136 // Debugger.Frame, this link represents the reverse relation, from a 137 // Debugger.Frame to its generator object. This slot is set if and only if 138 // there is a corresponding entry in generatorFrames. 139 GENERATOR_INFO_SLOT, 140 141 RESERVED_SLOTS, 142 }; 143 144 void trace(JSTracer* trc); 145 146 static NativeObject* initClass(JSContext* cx, Handle<GlobalObject*> global, 147 HandleObject dbgCtor); 148 static DebuggerFrame* create(JSContext* cx, HandleObject proto, 149 Handle<NativeObject*> debugger, 150 const FrameIter* maybeIter, 151 Handle<AbstractGeneratorObject*> maybeGenerator); 152 153 [[nodiscard]] static bool getArguments( 154 JSContext* cx, Handle<DebuggerFrame*> frame, 155 MutableHandle<DebuggerArguments*> result); 156 [[nodiscard]] static bool getCallee(JSContext* cx, 157 Handle<DebuggerFrame*> frame, 158 MutableHandle<DebuggerObject*> result); 159 [[nodiscard]] static bool getIsConstructing(JSContext* cx, 160 Handle<DebuggerFrame*> frame, 161 bool& result); 162 [[nodiscard]] static bool getEnvironment( 163 JSContext* cx, Handle<DebuggerFrame*> frame, 164 MutableHandle<DebuggerEnvironment*> result); 165 [[nodiscard]] static bool getOffset(JSContext* cx, 166 Handle<DebuggerFrame*> frame, 167 size_t& result); 168 [[nodiscard]] static bool getOlder(JSContext* cx, 169 Handle<DebuggerFrame*> frame, 170 MutableHandle<DebuggerFrame*> result); 171 [[nodiscard]] static bool getAsyncPromise( 172 JSContext* cx, Handle<DebuggerFrame*> frame, 173 MutableHandle<DebuggerObject*> result); 174 [[nodiscard]] static bool getOlderSavedFrame( 175 JSContext* cx, Handle<DebuggerFrame*> frame, 176 MutableHandle<SavedFrame*> result); 177 [[nodiscard]] static bool getThis(JSContext* cx, Handle<DebuggerFrame*> frame, 178 MutableHandleValue result); 179 static DebuggerFrameType getType(Handle<DebuggerFrame*> frame); 180 static DebuggerFrameImplementation getImplementation( 181 Handle<DebuggerFrame*> frame); 182 [[nodiscard]] static bool setOnStepHandler(JSContext* cx, 183 Handle<DebuggerFrame*> frame, 184 UniquePtr<OnStepHandler> handler); 185 186 [[nodiscard]] static JS::Result<Completion> eval( 187 JSContext* cx, Handle<DebuggerFrame*> frame, 188 mozilla::Range<const char16_t> chars, HandleObject bindings, 189 const EvalOptions& options); 190 191 [[nodiscard]] static DebuggerFrame* check(JSContext* cx, HandleValue thisv); 192 193 bool isOnStack() const; 194 bool isOnStackOrSuspendedWasmStack() const; 195 196 bool isSuspended() const; 197 198 OnStepHandler* onStepHandler() const; 199 OnPopHandler* onPopHandler() const; 200 void setOnPopHandler(JSContext* cx, OnPopHandler* handler); 201 202 inline bool hasGeneratorInfo() const; 203 204 // If hasGeneratorInfo(), return an direct cross-compartment reference to this 205 // Debugger.Frame's generator object. 206 AbstractGeneratorObject& unwrappedGenerator() const; 207 208 #ifdef DEBUG 209 JSScript* generatorScript() const; 210 #endif 211 212 /* 213 * Associate the generator object genObj with this Debugger.Frame. This 214 * association allows the Debugger.Frame to track the generator's execution 215 * across suspensions and resumptions, and to implement some methods even 216 * while the generator is suspended. 217 * 218 * The context `cx` must be in the Debugger.Frame's realm, and `genObj` must 219 * be in a debuggee realm. 220 * 221 * Technically, the generator activation need not actually be on the stack 222 * right now; it's okay to call this method on a Debugger.Frame that has no 223 * ScriptFrameIter::Data at present. However, this function has no way to 224 * verify that genObj really is the generator associated with the call for 225 * which this Debugger.Frame was originally created, so it's best to make the 226 * association while the call is on the stack, and the relationships are easy 227 * to discern. 228 */ 229 [[nodiscard]] static bool setGeneratorInfo( 230 JSContext* cx, Handle<DebuggerFrame*> frame, 231 Handle<AbstractGeneratorObject*> genObj); 232 233 /* 234 * Undo the effects of a prior call to setGenerator. 235 * 236 * If provided, owner must be the Debugger to which this Debugger.Frame 237 * belongs; remove this frame's entry from its generatorFrames map, and clean 238 * up its cross-compartment wrapper table entry. The owner must be passed 239 * unless this method is being called from the Debugger.Frame's finalizer. (In 240 * that case, the owner is not reliably available, and is not actually 241 * necessary.) 242 * 243 * If maybeGeneratorFramesEnum is non-null, use it to remove this frame's 244 * entry from the Debugger's generatorFrames weak map. In this case, this 245 * function will not otherwise disturb generatorFrames. Passing the enum 246 * allows this function to be used while iterating over generatorFrames. 247 */ 248 void clearGeneratorInfo(JS::GCContext* gcx); 249 250 /* 251 * Called after a generator/async frame is resumed, before exposing this 252 * Debugger.Frame object to any hooks. 253 */ 254 bool resume(const FrameIter& iter); 255 256 /* 257 * Called when JS PI sets aside the suspendable stack frames. 258 */ 259 void suspendWasmFrame(JS::GCContext* gcx); 260 261 bool hasAnyHooks() const; 262 263 Debugger* owner() const; 264 265 private: 266 static const JSClassOps classOps_; 267 268 static const JSPropertySpec properties_[]; 269 static const JSFunctionSpec methods_[]; 270 271 static void finalize(JS::GCContext* gcx, JSObject* obj); 272 273 static AbstractFramePtr getReferent(Handle<DebuggerFrame*> frame); 274 [[nodiscard]] static bool requireScriptReferent(JSContext* cx, 275 Handle<DebuggerFrame*> frame); 276 277 [[nodiscard]] static bool construct(JSContext* cx, unsigned argc, Value* vp); 278 279 struct CallData; 280 281 [[nodiscard]] bool incrementStepperCounter(JSContext* cx, 282 AbstractFramePtr referent); 283 [[nodiscard]] bool incrementStepperCounter(JSContext* cx, 284 HandleScript script); 285 void decrementStepperCounter(JS::GCContext* gcx, JSScript* script); 286 void decrementStepperCounter(JS::GCContext* gcx, AbstractFramePtr referent); 287 288 FrameIter::Data* frameIterData() const; 289 void setFrameIterData(FrameIter::Data*); 290 void freeFrameIterData(JS::GCContext* gcx); 291 292 public: 293 FrameIter getFrameIter(JSContext* cx); 294 295 void terminate(JS::GCContext* gcx, AbstractFramePtr frame); 296 void onGeneratorClosed(JS::GCContext* gcx); 297 void suspend(JS::GCContext* gcx); 298 299 [[nodiscard]] bool replaceFrameIterData(JSContext* cx, const FrameIter&); 300 301 class GeneratorInfo; 302 inline GeneratorInfo* generatorInfo() const; 303 }; 304 305 } /* namespace js */ 306 307 #endif /* debugger_Frame_h */