summaryrefslogtreecommitdiffstats
path: root/js/public/Debug.h
blob: 9ebc38d4a4fe8e33742ff6aaacaa2e7e24524285 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Interfaces by which the embedding can interact with the Debugger API.

#ifndef js_Debug_h
#define js_Debug_h

#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"

#include "jsapi.h"
#include "jspubtd.h"

#include "js/GCAPI.h"
#include "js/RootingAPI.h"
#include "js/TypeDecls.h"

namespace js {
class Debugger;
} // namespace js

namespace JS {
namespace dbg {

// Helping embedding code build objects for Debugger
// -------------------------------------------------
//
// Some Debugger API features lean on the embedding application to construct
// their result values. For example, Debugger.Frame.prototype.scriptEntryReason
// calls hooks provided by the embedding to construct values explaining why it
// invoked JavaScript; if F is a frame called from a mouse click event handler,
// F.scriptEntryReason would return an object of the form:
//
//   { eventType: "mousedown", event: <object> }
//
// where <object> is a Debugger.Object whose referent is the event being
// dispatched.
//
// However, Debugger implements a trust boundary. Debuggee code may be
// considered untrusted; debugger code needs to be protected from debuggee
// getters, setters, proxies, Object.watch watchpoints, and any other feature
// that might accidentally cause debugger code to set the debuggee running. The
// Debugger API tries to make it easy to write safe debugger code by only
// offering access to debuggee objects via Debugger.Object instances, which
// ensure that only those operations whose explicit purpose is to invoke
// debuggee code do so. But this protective membrane is only helpful if we
// interpose Debugger.Object instances in all the necessary spots.
//
// SpiderMonkey's compartment system also implements a trust boundary. The
// debuggee and debugger are always in different compartments. Inter-compartment
// work requires carefully tracking which compartment each JSObject or JS::Value
// belongs to, and ensuring that is is correctly wrapped for each operation.
//
// It seems precarious to expect the embedding's hooks to implement these trust
// boundaries. Instead, the JS::dbg::Builder API segregates the code which
// constructs trusted objects from that which deals with untrusted objects.
// Trusted objects have an entirely different C++ type, so code that improperly
// mixes trusted and untrusted objects is caught at compile time.
//
// In the structure shown above, there are two trusted objects, and one
// untrusted object:
//
// - The overall object, with the 'eventType' and 'event' properties, is a
//   trusted object. We're going to return it to D.F.p.scriptEntryReason's
//   caller, which will handle it directly.
//
// - The Debugger.Object instance appearing as the value of the 'event' property
//   is a trusted object. It belongs to the same Debugger instance as the
//   Debugger.Frame instance whose scriptEntryReason accessor was called, and
//   presents a safe reflection-oriented API for inspecting its referent, which
//   is:
//
// - The actual event object, an untrusted object, and the referent of the
//   Debugger.Object above. (Content can do things like replacing accessors on
//   Event.prototype.)
//
// Using JS::dbg::Builder, all objects and values the embedding deals with
// directly are considered untrusted, and are assumed to be debuggee values. The
// only way to construct trusted objects is to use Builder's own methods, which
// return a separate Object type. The only way to set a property on a trusted
// object is through that Object type. The actual trusted object is never
// exposed to the embedding.
//
// So, for example, the embedding might use code like the following to construct
// the object shown above, given a Builder passed to it by Debugger:
//
//    bool
//    MyScriptEntryReason::explain(JSContext* cx,
//                                 Builder& builder,
//                                 Builder::Object& result)
//    {
//        JSObject* eventObject = ... obtain debuggee event object somehow ...;
//        if (!eventObject)
//            return false;
//        result = builder.newObject(cx);
//        return result &&
//               result.defineProperty(cx, "eventType", SafelyFetchType(eventObject)) &&
//               result.defineProperty(cx, "event", eventObject);
//    }
//
//
// Object::defineProperty also accepts an Object as the value to store on the
// property. By its type, we know that the value is trusted, so we set it
// directly as the property's value, without interposing a Debugger.Object
// wrapper. This allows the embedding to builted nested structures of trusted
// objects.
//
// The Builder and Builder::Object methods take care of doing whatever
// compartment switching and wrapping are necessary to construct the trusted
// values in the Debugger's compartment.
//
// The Object type is self-rooting. Construction, assignment, and destruction
// all properly root the referent object.

class BuilderOrigin;

class Builder {
    // The Debugger instance whose client we are building a value for. We build
    // objects in this object's compartment.
    PersistentRootedObject debuggerObject;

    // debuggerObject's Debugger structure, for convenience.
    js::Debugger* debugger;

    // Check that |thing| is in the same compartment as our debuggerObject. Used
    // for assertions when constructing BuiltThings. We can overload this as we
    // add more instantiations of BuiltThing.
#if DEBUG
    void assertBuilt(JSObject* obj);
#else
    void assertBuilt(JSObject* obj) { }
#endif

  protected:
    // A reference to a trusted object or value. At the moment, we only use it
    // with JSObject*.
    template<typename T>
    class BuiltThing {
        friend class BuilderOrigin;

      protected:
        // The Builder to which this trusted thing belongs.
        Builder& owner;

        // A rooted reference to our value.
        PersistentRooted<T> value;

        BuiltThing(JSContext* cx, Builder& owner_, T value_ = GCPolicy<T>::initial())
          : owner(owner_), value(cx, value_)
        {
            owner.assertBuilt(value_);
        }

        // Forward some things from our owner, for convenience.
        js::Debugger* debugger() const { return owner.debugger; }
        JSObject* debuggerObject() const { return owner.debuggerObject; }

      public:
        BuiltThing(const BuiltThing& rhs) : owner(rhs.owner), value(rhs.value) { }
        BuiltThing& operator=(const BuiltThing& rhs) {
            MOZ_ASSERT(&owner == &rhs.owner);
            owner.assertBuilt(rhs.value);
            value = rhs.value;
            return *this;
        }

        explicit operator bool() const {
            // If we ever instantiate BuiltThing<Value>, this might not suffice.
            return value;
        }

      private:
        BuiltThing() = delete;
    };

  public:
    // A reference to a trusted object, possibly null. Instances of Object are
    // always properly rooted. They can be copied and assigned, as if they were
    // pointers.
    class Object: private BuiltThing<JSObject*> {
        friend class Builder;           // for construction
        friend class BuilderOrigin;     // for unwrapping

        typedef BuiltThing<JSObject*> Base;

        // This is private, because only Builders can create Objects that
        // actually point to something (hence the 'friend' declaration).
        Object(JSContext* cx, Builder& owner_, HandleObject obj) : Base(cx, owner_, obj.get()) { }

        bool definePropertyToTrusted(JSContext* cx, const char* name,
                                     JS::MutableHandleValue value);

      public:
        Object(JSContext* cx, Builder& owner_) : Base(cx, owner_, nullptr) { }
        Object(const Object& rhs) : Base(rhs) { }

        // Our automatically-generated assignment operator can see our base
        // class's assignment operator, so we don't need to write one out here.

        // Set the property named |name| on this object to |value|.
        //
        // If |value| is a string or primitive, re-wrap it for the debugger's
        // compartment.
        //
        // If |value| is an object, assume it is a debuggee object and make a
        // Debugger.Object instance referring to it. Set that as the propery's
        // value.
        //
        // If |value| is another trusted object, store it directly as the
        // property's value.
        //
        // On error, report the problem on cx and return false.
        bool defineProperty(JSContext* cx, const char* name, JS::HandleValue value);
        bool defineProperty(JSContext* cx, const char* name, JS::HandleObject value);
        bool defineProperty(JSContext* cx, const char* name, Object& value);

        using Base::operator bool;
    };

    // Build an empty object for direct use by debugger code, owned by this
    // Builder. If an error occurs, report it on cx and return a false Object.
    Object newObject(JSContext* cx);

  protected:
    Builder(JSContext* cx, js::Debugger* debugger);
};

// Debugger itself instantiates this subclass of Builder, which can unwrap
// BuiltThings that belong to it.
class BuilderOrigin : public Builder {
    template<typename T>
    T unwrapAny(const BuiltThing<T>& thing) {
        MOZ_ASSERT(&thing.owner == this);
        return thing.value.get();
    }

  public:
    BuilderOrigin(JSContext* cx, js::Debugger* debugger_)
      : Builder(cx, debugger_)
    { }

    JSObject* unwrap(Object& object) { return unwrapAny(object); }
};



// Finding the size of blocks allocated with malloc
// ------------------------------------------------
//
// Debugger.Memory wants to be able to report how many bytes items in memory are
// consuming. To do this, it needs a function that accepts a pointer to a block,
// and returns the number of bytes allocated to that block. SpiderMonkey itself
// doesn't know which function is appropriate to use, but the embedding does.

// Tell Debuggers in |cx| to use |mallocSizeOf| to find the size of
// malloc'd blocks.
JS_PUBLIC_API(void)
SetDebuggerMallocSizeOf(JSContext* cx, mozilla::MallocSizeOf mallocSizeOf);

// Get the MallocSizeOf function that the given context is using to find the
// size of malloc'd blocks.
JS_PUBLIC_API(mozilla::MallocSizeOf)
GetDebuggerMallocSizeOf(JSContext* cx);



// Debugger and Garbage Collection Events
// --------------------------------------
//
// The Debugger wants to report about its debuggees' GC cycles, however entering
// JS after a GC is troublesome since SpiderMonkey will often do something like
// force a GC and then rely on the nursery being empty. If we call into some
// Debugger's hook after the GC, then JS runs and the nursery won't be
// empty. Instead, we rely on embedders to call back into SpiderMonkey after a
// GC and notify Debuggers to call their onGarbageCollection hook.


// For each Debugger that observed a debuggee involved in the given GC event,
// call its `onGarbageCollection` hook.
JS_PUBLIC_API(bool)
FireOnGarbageCollectionHook(JSContext* cx, GarbageCollectionEvent::Ptr&& data);



// Handlers for observing Promises
// -------------------------------
//
// The Debugger wants to observe behavior of promises, which are implemented by
// Gecko with webidl and which SpiderMonkey knows nothing about. On the other
// hand, Gecko knows nothing about which (if any) debuggers are observing a
// promise's global. The compromise is that Gecko is responsible for calling
// these handlers at the appropriate times, and SpiderMonkey will handle
// notifying any Debugger instances that are observing the given promise's
// global.

// Notify any Debugger instances observing this promise's global that a new
// promise was allocated.
JS_PUBLIC_API(void)
onNewPromise(JSContext* cx, HandleObject promise);

// Notify any Debugger instances observing this promise's global that the
// promise has settled (ie, it has either been fulfilled or rejected). Note that
// this is *not* equivalent to the promise resolution (ie, the promise's fate
// getting locked in) because you can resolve a promise with another pending
// promise, in which case neither promise has settled yet.
//
// It is Gecko's responsibility to ensure that this is never called on the same
// promise more than once (because a promise can only make the transition from
// unsettled to settled once).
JS_PUBLIC_API(void)
onPromiseSettled(JSContext* cx, HandleObject promise);



// Return true if the given value is a Debugger object, false otherwise.
JS_PUBLIC_API(bool)
IsDebugger(JSObject& obj);

// Append each of the debuggee global objects observed by the Debugger object
// |dbgObj| to |vector|. Returns true on success, false on failure.
JS_PUBLIC_API(bool)
GetDebuggeeGlobals(JSContext* cx, JSObject& dbgObj, AutoObjectVector& vector);


// Hooks for reporting where JavaScript execution began.
//
// Our performance tools would like to be able to label blocks of JavaScript
// execution with the function name and source location where execution began:
// the event handler, the callback, etc.
//
// Construct an instance of this class on the stack, providing a JSContext
// belonging to the runtime in which execution will occur. Each time we enter
// JavaScript --- specifically, each time we push a JavaScript stack frame that
// has no older JS frames younger than this AutoEntryMonitor --- we will
// call the appropriate |Entry| member function to indicate where we've begun
// execution.

class MOZ_STACK_CLASS JS_PUBLIC_API(AutoEntryMonitor) {
    JSRuntime* runtime_;
    AutoEntryMonitor* savedMonitor_;

  public:
    explicit AutoEntryMonitor(JSContext* cx);
    ~AutoEntryMonitor();

    // SpiderMonkey reports the JavaScript entry points occuring within this
    // AutoEntryMonitor's scope to the following member functions, which the
    // embedding is expected to override.
    //
    // It is important to note that |asyncCause| is owned by the caller and its
    // lifetime must outlive the lifetime of the AutoEntryMonitor object. It is
    // strongly encouraged that |asyncCause| be a string constant or similar
    // statically allocated string.

    // We have begun executing |function|. Note that |function| may not be the
    // actual closure we are running, but only the canonical function object to
    // which the script refers.
    virtual void Entry(JSContext* cx, JSFunction* function,
                       HandleValue asyncStack,
                       const char* asyncCause) = 0;

    // Execution has begun at the entry point of |script|, which is not a
    // function body. (This is probably being executed by 'eval' or some
    // JSAPI equivalent.)
    virtual void Entry(JSContext* cx, JSScript* script,
                       HandleValue asyncStack,
                       const char* asyncCause) = 0;

    // Execution of the function or script has ended.
    virtual void Exit(JSContext* cx) { }
};



} // namespace dbg
} // namespace JS


#endif /* js_Debug_h */