Line data Source code
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 : // Interfaces by which the embedding can interact with the Debugger API.
8 :
9 : #ifndef js_Debug_h
10 : #define js_Debug_h
11 :
12 : #include "mozilla/Assertions.h"
13 : #include "mozilla/Attributes.h"
14 : #include "mozilla/MemoryReporting.h"
15 :
16 : #include "jsapi.h"
17 : #include "jspubtd.h"
18 :
19 : #include "js/GCAPI.h"
20 : #include "js/RootingAPI.h"
21 : #include "js/TypeDecls.h"
22 :
23 : namespace js {
24 : class Debugger;
25 : } // namespace js
26 :
27 : namespace JS {
28 : namespace dbg {
29 :
30 : // Helping embedding code build objects for Debugger
31 : // -------------------------------------------------
32 : //
33 : // Some Debugger API features lean on the embedding application to construct
34 : // their result values. For example, Debugger.Frame.prototype.scriptEntryReason
35 : // calls hooks provided by the embedding to construct values explaining why it
36 : // invoked JavaScript; if F is a frame called from a mouse click event handler,
37 : // F.scriptEntryReason would return an object of the form:
38 : //
39 : // { eventType: "mousedown", event: <object> }
40 : //
41 : // where <object> is a Debugger.Object whose referent is the event being
42 : // dispatched.
43 : //
44 : // However, Debugger implements a trust boundary. Debuggee code may be
45 : // considered untrusted; debugger code needs to be protected from debuggee
46 : // getters, setters, proxies, Object.watch watchpoints, and any other feature
47 : // that might accidentally cause debugger code to set the debuggee running. The
48 : // Debugger API tries to make it easy to write safe debugger code by only
49 : // offering access to debuggee objects via Debugger.Object instances, which
50 : // ensure that only those operations whose explicit purpose is to invoke
51 : // debuggee code do so. But this protective membrane is only helpful if we
52 : // interpose Debugger.Object instances in all the necessary spots.
53 : //
54 : // SpiderMonkey's compartment system also implements a trust boundary. The
55 : // debuggee and debugger are always in different compartments. Inter-compartment
56 : // work requires carefully tracking which compartment each JSObject or JS::Value
57 : // belongs to, and ensuring that is is correctly wrapped for each operation.
58 : //
59 : // It seems precarious to expect the embedding's hooks to implement these trust
60 : // boundaries. Instead, the JS::dbg::Builder API segregates the code which
61 : // constructs trusted objects from that which deals with untrusted objects.
62 : // Trusted objects have an entirely different C++ type, so code that improperly
63 : // mixes trusted and untrusted objects is caught at compile time.
64 : //
65 : // In the structure shown above, there are two trusted objects, and one
66 : // untrusted object:
67 : //
68 : // - The overall object, with the 'eventType' and 'event' properties, is a
69 : // trusted object. We're going to return it to D.F.p.scriptEntryReason's
70 : // caller, which will handle it directly.
71 : //
72 : // - The Debugger.Object instance appearing as the value of the 'event' property
73 : // is a trusted object. It belongs to the same Debugger instance as the
74 : // Debugger.Frame instance whose scriptEntryReason accessor was called, and
75 : // presents a safe reflection-oriented API for inspecting its referent, which
76 : // is:
77 : //
78 : // - The actual event object, an untrusted object, and the referent of the
79 : // Debugger.Object above. (Content can do things like replacing accessors on
80 : // Event.prototype.)
81 : //
82 : // Using JS::dbg::Builder, all objects and values the embedding deals with
83 : // directly are considered untrusted, and are assumed to be debuggee values. The
84 : // only way to construct trusted objects is to use Builder's own methods, which
85 : // return a separate Object type. The only way to set a property on a trusted
86 : // object is through that Object type. The actual trusted object is never
87 : // exposed to the embedding.
88 : //
89 : // So, for example, the embedding might use code like the following to construct
90 : // the object shown above, given a Builder passed to it by Debugger:
91 : //
92 : // bool
93 : // MyScriptEntryReason::explain(JSContext* cx,
94 : // Builder& builder,
95 : // Builder::Object& result)
96 : // {
97 : // JSObject* eventObject = ... obtain debuggee event object somehow ...;
98 : // if (!eventObject)
99 : // return false;
100 : // result = builder.newObject(cx);
101 : // return result &&
102 : // result.defineProperty(cx, "eventType", SafelyFetchType(eventObject)) &&
103 : // result.defineProperty(cx, "event", eventObject);
104 : // }
105 : //
106 : //
107 : // Object::defineProperty also accepts an Object as the value to store on the
108 : // property. By its type, we know that the value is trusted, so we set it
109 : // directly as the property's value, without interposing a Debugger.Object
110 : // wrapper. This allows the embedding to builted nested structures of trusted
111 : // objects.
112 : //
113 : // The Builder and Builder::Object methods take care of doing whatever
114 : // compartment switching and wrapping are necessary to construct the trusted
115 : // values in the Debugger's compartment.
116 : //
117 : // The Object type is self-rooting. Construction, assignment, and destruction
118 : // all properly root the referent object.
119 :
120 : class BuilderOrigin;
121 :
122 : class Builder {
123 : // The Debugger instance whose client we are building a value for. We build
124 : // objects in this object's compartment.
125 : PersistentRootedObject debuggerObject;
126 :
127 : // debuggerObject's Debugger structure, for convenience.
128 : js::Debugger* debugger;
129 :
130 : // Check that |thing| is in the same compartment as our debuggerObject. Used
131 : // for assertions when constructing BuiltThings. We can overload this as we
132 : // add more instantiations of BuiltThing.
133 : #if DEBUG
134 : void assertBuilt(JSObject* obj);
135 : #else
136 : void assertBuilt(JSObject* obj) { }
137 : #endif
138 :
139 : protected:
140 : // A reference to a trusted object or value. At the moment, we only use it
141 : // with JSObject*.
142 : template<typename T>
143 : class BuiltThing {
144 : friend class BuilderOrigin;
145 :
146 : protected:
147 : // The Builder to which this trusted thing belongs.
148 : Builder& owner;
149 :
150 : // A rooted reference to our value.
151 : PersistentRooted<T> value;
152 :
153 0 : BuiltThing(JSContext* cx, Builder& owner_, T value_ = GCPolicy<T>::initial())
154 0 : : owner(owner_), value(cx, value_)
155 : {
156 0 : owner.assertBuilt(value_);
157 0 : }
158 :
159 : // Forward some things from our owner, for convenience.
160 0 : js::Debugger* debugger() const { return owner.debugger; }
161 0 : JSObject* debuggerObject() const { return owner.debuggerObject; }
162 :
163 : public:
164 : BuiltThing(const BuiltThing& rhs) : owner(rhs.owner), value(rhs.value) { }
165 : BuiltThing& operator=(const BuiltThing& rhs) {
166 : MOZ_ASSERT(&owner == &rhs.owner);
167 : owner.assertBuilt(rhs.value);
168 : value = rhs.value;
169 : return *this;
170 : }
171 :
172 : explicit operator bool() const {
173 : // If we ever instantiate BuiltThing<Value>, this might not suffice.
174 : return value;
175 : }
176 :
177 : private:
178 : BuiltThing() = delete;
179 : };
180 :
181 : public:
182 : // A reference to a trusted object, possibly null. Instances of Object are
183 : // always properly rooted. They can be copied and assigned, as if they were
184 : // pointers.
185 : class Object: private BuiltThing<JSObject*> {
186 : friend class Builder; // for construction
187 : friend class BuilderOrigin; // for unwrapping
188 :
189 : typedef BuiltThing<JSObject*> Base;
190 :
191 : // This is private, because only Builders can create Objects that
192 : // actually point to something (hence the 'friend' declaration).
193 0 : Object(JSContext* cx, Builder& owner_, HandleObject obj) : Base(cx, owner_, obj.get()) { }
194 :
195 : bool definePropertyToTrusted(JSContext* cx, const char* name,
196 : JS::MutableHandleValue value);
197 :
198 : public:
199 : Object(JSContext* cx, Builder& owner_) : Base(cx, owner_, nullptr) { }
200 : Object(const Object& rhs) : Base(rhs) { }
201 :
202 : // Our automatically-generated assignment operator can see our base
203 : // class's assignment operator, so we don't need to write one out here.
204 :
205 : // Set the property named |name| on this object to |value|.
206 : //
207 : // If |value| is a string or primitive, re-wrap it for the debugger's
208 : // compartment.
209 : //
210 : // If |value| is an object, assume it is a debuggee object and make a
211 : // Debugger.Object instance referring to it. Set that as the propery's
212 : // value.
213 : //
214 : // If |value| is another trusted object, store it directly as the
215 : // property's value.
216 : //
217 : // On error, report the problem on cx and return false.
218 : bool defineProperty(JSContext* cx, const char* name, JS::HandleValue value);
219 : bool defineProperty(JSContext* cx, const char* name, JS::HandleObject value);
220 : bool defineProperty(JSContext* cx, const char* name, Object& value);
221 :
222 : using Base::operator bool;
223 : };
224 :
225 : // Build an empty object for direct use by debugger code, owned by this
226 : // Builder. If an error occurs, report it on cx and return a false Object.
227 : Object newObject(JSContext* cx);
228 :
229 : protected:
230 : Builder(JSContext* cx, js::Debugger* debugger);
231 : };
232 :
233 : // Debugger itself instantiates this subclass of Builder, which can unwrap
234 : // BuiltThings that belong to it.
235 : class BuilderOrigin : public Builder {
236 : template<typename T>
237 : T unwrapAny(const BuiltThing<T>& thing) {
238 : MOZ_ASSERT(&thing.owner == this);
239 : return thing.value.get();
240 : }
241 :
242 : public:
243 : BuilderOrigin(JSContext* cx, js::Debugger* debugger_)
244 : : Builder(cx, debugger_)
245 : { }
246 :
247 : JSObject* unwrap(Object& object) { return unwrapAny(object); }
248 : };
249 :
250 :
251 :
252 : // Finding the size of blocks allocated with malloc
253 : // ------------------------------------------------
254 : //
255 : // Debugger.Memory wants to be able to report how many bytes items in memory are
256 : // consuming. To do this, it needs a function that accepts a pointer to a block,
257 : // and returns the number of bytes allocated to that block. SpiderMonkey itself
258 : // doesn't know which function is appropriate to use, but the embedding does.
259 :
260 : // Tell Debuggers in |cx| to use |mallocSizeOf| to find the size of
261 : // malloc'd blocks.
262 : JS_PUBLIC_API(void)
263 : SetDebuggerMallocSizeOf(JSContext* cx, mozilla::MallocSizeOf mallocSizeOf);
264 :
265 : // Get the MallocSizeOf function that the given context is using to find the
266 : // size of malloc'd blocks.
267 : JS_PUBLIC_API(mozilla::MallocSizeOf)
268 : GetDebuggerMallocSizeOf(JSContext* cx);
269 :
270 :
271 :
272 : // Debugger and Garbage Collection Events
273 : // --------------------------------------
274 : //
275 : // The Debugger wants to report about its debuggees' GC cycles, however entering
276 : // JS after a GC is troublesome since SpiderMonkey will often do something like
277 : // force a GC and then rely on the nursery being empty. If we call into some
278 : // Debugger's hook after the GC, then JS runs and the nursery won't be
279 : // empty. Instead, we rely on embedders to call back into SpiderMonkey after a
280 : // GC and notify Debuggers to call their onGarbageCollection hook.
281 :
282 : // Determine whether it's necessary to call FireOnGarbageCollectionHook() after
283 : // a GC. This is only required if there are debuggers with an
284 : // onGarbageCollection hook observing a global in the set of collected zones.
285 : JS_PUBLIC_API(bool)
286 : FireOnGarbageCollectionHookRequired(JSContext* cx);
287 :
288 : // For each Debugger that observed a debuggee involved in the given GC event,
289 : // call its `onGarbageCollection` hook.
290 : JS_PUBLIC_API(bool)
291 : FireOnGarbageCollectionHook(JSContext* cx, GarbageCollectionEvent::Ptr&& data);
292 :
293 :
294 :
295 : // Handlers for observing Promises
296 : // -------------------------------
297 : //
298 : // The Debugger wants to observe behavior of promises, which are implemented by
299 : // Gecko with webidl and which SpiderMonkey knows nothing about. On the other
300 : // hand, Gecko knows nothing about which (if any) debuggers are observing a
301 : // promise's global. The compromise is that Gecko is responsible for calling
302 : // these handlers at the appropriate times, and SpiderMonkey will handle
303 : // notifying any Debugger instances that are observing the given promise's
304 : // global.
305 :
306 : // Notify any Debugger instances observing this promise's global that a new
307 : // promise was allocated.
308 : JS_PUBLIC_API(void)
309 : onNewPromise(JSContext* cx, HandleObject promise);
310 :
311 : // Notify any Debugger instances observing this promise's global that the
312 : // promise has settled (ie, it has either been fulfilled or rejected). Note that
313 : // this is *not* equivalent to the promise resolution (ie, the promise's fate
314 : // getting locked in) because you can resolve a promise with another pending
315 : // promise, in which case neither promise has settled yet.
316 : //
317 : // It is Gecko's responsibility to ensure that this is never called on the same
318 : // promise more than once (because a promise can only make the transition from
319 : // unsettled to settled once).
320 : JS_PUBLIC_API(void)
321 : onPromiseSettled(JSContext* cx, HandleObject promise);
322 :
323 :
324 :
325 : // Return true if the given value is a Debugger object, false otherwise.
326 : JS_PUBLIC_API(bool)
327 : IsDebugger(JSObject& obj);
328 :
329 : // Append each of the debuggee global objects observed by the Debugger object
330 : // |dbgObj| to |vector|. Returns true on success, false on failure.
331 : JS_PUBLIC_API(bool)
332 : GetDebuggeeGlobals(JSContext* cx, JSObject& dbgObj, AutoObjectVector& vector);
333 :
334 :
335 : // Hooks for reporting where JavaScript execution began.
336 : //
337 : // Our performance tools would like to be able to label blocks of JavaScript
338 : // execution with the function name and source location where execution began:
339 : // the event handler, the callback, etc.
340 : //
341 : // Construct an instance of this class on the stack, providing a JSContext
342 : // belonging to the runtime in which execution will occur. Each time we enter
343 : // JavaScript --- specifically, each time we push a JavaScript stack frame that
344 : // has no older JS frames younger than this AutoEntryMonitor --- we will
345 : // call the appropriate |Entry| member function to indicate where we've begun
346 : // execution.
347 :
348 : class MOZ_STACK_CLASS JS_PUBLIC_API(AutoEntryMonitor) {
349 : JSContext* cx_;
350 : AutoEntryMonitor* savedMonitor_;
351 :
352 : public:
353 : explicit AutoEntryMonitor(JSContext* cx);
354 : ~AutoEntryMonitor();
355 :
356 : // SpiderMonkey reports the JavaScript entry points occuring within this
357 : // AutoEntryMonitor's scope to the following member functions, which the
358 : // embedding is expected to override.
359 : //
360 : // It is important to note that |asyncCause| is owned by the caller and its
361 : // lifetime must outlive the lifetime of the AutoEntryMonitor object. It is
362 : // strongly encouraged that |asyncCause| be a string constant or similar
363 : // statically allocated string.
364 :
365 : // We have begun executing |function|. Note that |function| may not be the
366 : // actual closure we are running, but only the canonical function object to
367 : // which the script refers.
368 : virtual void Entry(JSContext* cx, JSFunction* function,
369 : HandleValue asyncStack,
370 : const char* asyncCause) = 0;
371 :
372 : // Execution has begun at the entry point of |script|, which is not a
373 : // function body. (This is probably being executed by 'eval' or some
374 : // JSAPI equivalent.)
375 : virtual void Entry(JSContext* cx, JSScript* script,
376 : HandleValue asyncStack,
377 : const char* asyncCause) = 0;
378 :
379 : // Execution of the function or script has ended.
380 0 : virtual void Exit(JSContext* cx) { }
381 : };
382 :
383 :
384 :
385 : } // namespace dbg
386 : } // namespace JS
387 :
388 :
389 : #endif /* js_Debug_h */
|