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/. */
7 #ifndef debugger_Frame_h
8 #define debugger_Frame_h
10 #include "mozilla/Maybe.h" // for Maybe
11 #include "mozilla/Range.h" // for Range
12 #include "mozilla/Result.h" // for Result
14 #include <stddef.h> // for size_t
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
25 struct JS_PUBLIC_API JSContext
;
29 class AbstractGeneratorObject
;
33 * An OnStepHandler represents a handler function that is called when a small
34 * amount of progress is made in a frame.
36 struct OnStepHandler
: Handler
{
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.
43 virtual bool onStep(JSContext
* cx
, Handle
<DebuggerFrame
*> frame
,
44 ResumeMode
& resumeMode
, MutableHandleValue vp
) = 0;
47 class ScriptedOnStepHandler final
: public OnStepHandler
{
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
;
59 const HeapPtr
<JSObject
*> object_
;
63 * An OnPopHandler represents a handler function that is called just before a
66 struct OnPopHandler
: Handler
{
68 * The given `frame` is about to be popped; `completion` explains why.
70 * When this method returns true, it must set `resumeMode` and `vp` to a
71 * resumption value specifying how execution should continue.
73 * When this method returns false, it should set an exception on `cx`.
75 virtual bool onPop(JSContext
* cx
, Handle
<DebuggerFrame
*> frame
,
76 const Completion
& completion
, ResumeMode
& resumeMode
,
77 MutableHandleValue vp
) = 0;
80 class ScriptedOnPopHandler final
: public OnPopHandler
{
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
;
93 const HeapPtr
<JSObject
*> object_
;
96 enum class DebuggerFrameType
{ Eval
, Global
, Call
, Module
, WasmCall
};
98 enum class DebuggerFrameImplementation
{ Interpreter
, Baseline
, Ion
, Wasm
};
100 class DebuggerArguments
: public NativeObject
{
102 static const JSClass class_
;
104 static DebuggerArguments
* create(JSContext
* cx
, HandleObject proto
,
105 Handle
<DebuggerFrame
*> frame
);
110 static const unsigned RESERVED_SLOTS
= 1;
113 class DebuggerFrame
: public NativeObject
{
114 friend class DebuggerArguments
;
115 friend class ScriptedOnStepHandler
;
116 friend class ScriptedOnPopHandler
;
119 static const JSClass class_
;
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.
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.
144 void trace(JSTracer
* trc
);
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
);
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
,
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
,
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
);
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
);
191 [[nodiscard
]] static DebuggerFrame
* check(JSContext
* cx
, HandleValue thisv
);
193 bool isOnStack() const;
195 bool isSuspended() const;
197 OnStepHandler
* onStepHandler() const;
198 OnPopHandler
* onPopHandler() const;
199 void setOnPopHandler(JSContext
* cx
, OnPopHandler
* handler
);
201 inline bool hasGeneratorInfo() const;
203 // If hasGeneratorInfo(), return an direct cross-compartment reference to this
204 // Debugger.Frame's generator object.
205 AbstractGeneratorObject
& unwrappedGenerator() const;
208 JSScript
* generatorScript() const;
212 * Associate the generator object genObj with this Debugger.Frame. This
213 * association allows the Debugger.Frame to track the generator's execution
214 * across suspensions and resumptions, and to implement some methods even
215 * while the generator is suspended.
217 * The context `cx` must be in the Debugger.Frame's realm, and `genObj` must
218 * be in a debuggee realm.
220 * Technically, the generator activation need not actually be on the stack
221 * right now; it's okay to call this method on a Debugger.Frame that has no
222 * ScriptFrameIter::Data at present. However, this function has no way to
223 * verify that genObj really is the generator associated with the call for
224 * which this Debugger.Frame was originally created, so it's best to make the
225 * association while the call is on the stack, and the relationships are easy
228 [[nodiscard
]] static bool setGeneratorInfo(
229 JSContext
* cx
, Handle
<DebuggerFrame
*> frame
,
230 Handle
<AbstractGeneratorObject
*> genObj
);
233 * Undo the effects of a prior call to setGenerator.
235 * If provided, owner must be the Debugger to which this Debugger.Frame
236 * belongs; remove this frame's entry from its generatorFrames map, and clean
237 * up its cross-compartment wrapper table entry. The owner must be passed
238 * unless this method is being called from the Debugger.Frame's finalizer. (In
239 * that case, the owner is not reliably available, and is not actually
242 * If maybeGeneratorFramesEnum is non-null, use it to remove this frame's
243 * entry from the Debugger's generatorFrames weak map. In this case, this
244 * function will not otherwise disturb generatorFrames. Passing the enum
245 * allows this function to be used while iterating over generatorFrames.
247 void clearGeneratorInfo(JS::GCContext
* gcx
);
250 * Called after a generator/async frame is resumed, before exposing this
251 * Debugger.Frame object to any hooks.
253 bool resume(const FrameIter
& iter
);
255 bool hasAnyHooks() const;
257 Debugger
* owner() const;
260 static const JSClassOps classOps_
;
262 static const JSPropertySpec properties_
[];
263 static const JSFunctionSpec methods_
[];
265 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
);
267 static AbstractFramePtr
getReferent(Handle
<DebuggerFrame
*> frame
);
268 [[nodiscard
]] static bool requireScriptReferent(JSContext
* cx
,
269 Handle
<DebuggerFrame
*> frame
);
271 [[nodiscard
]] static bool construct(JSContext
* cx
, unsigned argc
, Value
* vp
);
275 [[nodiscard
]] bool incrementStepperCounter(JSContext
* cx
,
276 AbstractFramePtr referent
);
277 [[nodiscard
]] bool incrementStepperCounter(JSContext
* cx
,
278 HandleScript script
);
279 void decrementStepperCounter(JS::GCContext
* gcx
, JSScript
* script
);
280 void decrementStepperCounter(JS::GCContext
* gcx
, AbstractFramePtr referent
);
282 FrameIter::Data
* frameIterData() const;
283 void setFrameIterData(FrameIter::Data
*);
284 void freeFrameIterData(JS::GCContext
* gcx
);
287 FrameIter
getFrameIter(JSContext
* cx
);
289 void terminate(JS::GCContext
* gcx
, AbstractFramePtr frame
);
290 void onGeneratorClosed(JS::GCContext
* gcx
);
291 void suspend(JS::GCContext
* gcx
);
293 [[nodiscard
]] bool replaceFrameIterData(JSContext
* cx
, const FrameIter
&);
296 inline GeneratorInfo
* generatorInfo() const;
301 #endif /* debugger_Frame_h */