Source code

Revision control

Other Tools

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_Debugger_h
8
#define debugger_Debugger_h
9
10
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER1
11
#include "mozilla/Attributes.h" // for MOZ_MUST_USE, MOZ_RAII
12
#include "mozilla/DoublyLinkedList.h" // for DoublyLinkedListElement
13
#include "mozilla/HashTable.h" // for HashSet, DefaultHasher (ptr only)
14
#include "mozilla/LinkedList.h" // for LinkedList (ptr only)
15
#include "mozilla/Maybe.h" // for Maybe, Nothing
16
#include "mozilla/Move.h" // for std::move
17
#include "mozilla/Range.h" // for Range
18
#include "mozilla/Result.h" // for Result
19
#include "mozilla/TimeStamp.h" // for TimeStamp
20
#include "mozilla/Variant.h" // for Variant
21
22
#include <stddef.h> // for size_t
23
#include <stdint.h> // for uint32_t, uint64_t, uintptr_t
24
25
#include "jsapi.h" // for Handle, UnsafeTraceRoot
26
#include "jstypes.h" // for JS_GC_ZEAL
27
28
#include "NamespaceImports.h" // for Value, HandleObject
29
#include "debugger/DebugAPI.h" // for DebugAPI
30
#include "debugger/Object.h" // for DebuggerObject
31
#include "ds/TraceableFifo.h" // for TraceableFifo
32
#include "gc/Barrier.h" // for WeakHeapPtrGlobalObject, HeapPtr
33
#include "gc/Marking.h" // for IsAboutToBeFinalized, ToMarkable
34
#include "gc/Rooting.h" // for HandleSavedFrame, HandleAtom
35
#include "gc/Tracer.h" // for TraceNullableEdge, TraceEdge
36
#include "gc/WeakMap.h" // for WeakMap
37
#include "gc/ZoneAllocator.h" // for ZoneAllocPolicy
38
#include "js/GCAPI.h" // for GarbageCollectionEvent
39
#include "js/Proxy.h" // for PropertyDescriptor
40
#include "js/Wrapper.h" // for UncheckedUnwrap
41
#include "proxy/DeadObjectProxy.h" // for IsDeadProxyObject
42
#include "vm/GeneratorObject.h" // for AbstractGeneratorObject
43
#include "vm/GlobalObject.h" // for GlobalObject
44
#include "vm/JSContext.h" // for JSContext
45
#include "vm/JSObject.h" // for JSObject
46
#include "vm/JSScript.h" // for JSScript, ScriptSourceObject
47
#include "vm/NativeObject.h" // for NativeObject
48
#include "vm/Runtime.h" // for JSRuntime
49
#include "vm/SavedFrame.h" // for SavedFrame
50
#include "vm/Stack.h" // for AbstractFramePtr, FrameIter
51
#include "vm/StringType.h" // for JSAtom
52
#include "wasm/WasmJS.h" // for WasmInstanceObject
53
54
class JS_PUBLIC_API JSFunction;
55
56
namespace JS {
57
class AutoStableStringChars;
58
class JS_PUBLIC_API Compartment;
59
class JS_PUBLIC_API Realm;
60
class JS_PUBLIC_API Zone;
61
} /* namespace JS */
62
63
namespace js {
64
class AutoRealm;
65
class CrossCompartmentKey;
66
class Debugger;
67
class DebuggerEnvironment;
68
class PromiseObject;
69
namespace gc {
70
struct Cell;
71
}
72
namespace wasm {
73
class Instance;
74
}
75
template <typename T>
76
struct GCManagedDeletePolicy;
77
} /* namespace js */
78
79
/*
80
* Windows 3.x used a cooperative multitasking model, with a Yield macro that
81
* let you relinquish control to other cooperative threads. Microsoft replaced
82
* it with an empty macro long ago. We should be free to use it in our code.
83
*/
84
#undef Yield
85
86
namespace js {
87
88
class Breakpoint;
89
class DebuggerFrame;
90
class DebuggerScript;
91
class DebuggerSource;
92
class DebuggerMemory;
93
class ScriptedOnStepHandler;
94
class ScriptedOnPopHandler;
95
class DebuggerDebuggeeLink;
96
97
/**
98
* A completion value, describing how some sort of JavaScript evaluation
99
* completed. This is used to tell an onPop handler what's going on with the
100
* frame, and to report the outcome of call, apply, setProperty, and getProperty
101
* operations.
102
*
103
* Local variables of type Completion should be held in Rooted locations,
104
* and passed using Handle and MutableHandle.
105
*/
106
class Completion {
107
public:
108
struct Return {
109
explicit Return(const Value& value) : value(value) {}
110
Value value;
111
112
void trace(JSTracer* trc) {
113
JS::UnsafeTraceRoot(trc, &value, "js::Completion::Return::value");
114
}
115
};
116
117
struct Throw {
118
Throw(const Value& exception, SavedFrame* stack)
119
: exception(exception), stack(stack) {}
120
Value exception;
121
SavedFrame* stack;
122
123
void trace(JSTracer* trc) {
124
JS::UnsafeTraceRoot(trc, &exception, "js::Completion::Throw::exception");
125
JS::UnsafeTraceRoot(trc, &stack, "js::Completion::Throw::stack");
126
}
127
};
128
129
struct Terminate {
130
void trace(JSTracer* trc) {}
131
};
132
133
struct InitialYield {
134
explicit InitialYield(AbstractGeneratorObject* generatorObject)
135
: generatorObject(generatorObject) {}
136
AbstractGeneratorObject* generatorObject;
137
138
void trace(JSTracer* trc) {
139
JS::UnsafeTraceRoot(trc, &generatorObject,
140
"js::Completion::InitialYield::generatorObject");
141
}
142
};
143
144
struct Yield {
145
Yield(AbstractGeneratorObject* generatorObject, const Value& iteratorResult)
146
: generatorObject(generatorObject), iteratorResult(iteratorResult) {}
147
AbstractGeneratorObject* generatorObject;
148
Value iteratorResult;
149
150
void trace(JSTracer* trc) {
151
JS::UnsafeTraceRoot(trc, &generatorObject,
152
"js::Completion::Yield::generatorObject");
153
JS::UnsafeTraceRoot(trc, &iteratorResult,
154
"js::Completion::Yield::iteratorResult");
155
}
156
};
157
158
struct Await {
159
Await(AbstractGeneratorObject* generatorObject, const Value& awaitee)
160
: generatorObject(generatorObject), awaitee(awaitee) {}
161
AbstractGeneratorObject* generatorObject;
162
Value awaitee;
163
164
void trace(JSTracer* trc) {
165
JS::UnsafeTraceRoot(trc, &generatorObject,
166
"js::Completion::Await::generatorObject");
167
JS::UnsafeTraceRoot(trc, &awaitee, "js::Completion::Await::awaitee");
168
}
169
};
170
171
// The JS::Result macros want to assign to an existing variable, so having a
172
// default constructor is handy.
173
Completion() : variant(Terminate()) {}
174
175
// Construct a completion from a specific variant.
176
//
177
// Unfortunately, using a template here would prevent the implicit definitions
178
// of the copy and move constructor and assignment operators, which is icky.
179
explicit Completion(Return&& variant)
180
: variant(std::forward<Return>(variant)) {}
181
explicit Completion(Throw&& variant)
182
: variant(std::forward<Throw>(variant)) {}
183
explicit Completion(Terminate&& variant)
184
: variant(std::forward<Terminate>(variant)) {}
185
explicit Completion(InitialYield&& variant)
186
: variant(std::forward<InitialYield>(variant)) {}
187
explicit Completion(Yield&& variant)
188
: variant(std::forward<Yield>(variant)) {}
189
explicit Completion(Await&& variant)
190
: variant(std::forward<Await>(variant)) {}
191
192
// Capture a JavaScript operation result as a Completion value. This clears
193
// any exception and stack from cx, taking ownership of them itself.
194
static Completion fromJSResult(JSContext* cx, bool ok, const Value& rv);
195
196
// Construct a completion given an AbstractFramePtr that is being popped. This
197
// clears any exception and stack from cx, taking ownership of them itself.
198
static Completion fromJSFramePop(JSContext* cx, AbstractFramePtr frame,
199
const jsbytecode* pc, bool ok);
200
201
template <typename V>
202
bool is() const {
203
return variant.template is<V>();
204
}
205
206
template <typename V>
207
V& as() {
208
return variant.template as<V>();
209
}
210
211
template <typename V>
212
const V& as() const {
213
return variant.template as<V>();
214
}
215
216
void trace(JSTracer* trc);
217
218
/* True if this completion is a suspension of a generator or async call. */
219
bool suspending() const {
220
return variant.is<InitialYield>() || variant.is<Yield>() ||
221
variant.is<Await>();
222
}
223
224
/*
225
* If this completion is a suspension of a generator or async call, return the
226
* call's generator object, nullptr otherwise.
227
*/
228
AbstractGeneratorObject* maybeGeneratorObject() const;
229
230
/* Set `result` to a Debugger API completion value describing this completion.
231
*/
232
bool buildCompletionValue(JSContext* cx, Debugger* dbg,
233
MutableHandleValue result) const;
234
235
/*
236
* Set `resumeMode`, `value`, and `exnStack` to values describing this
237
* completion.
238
*/
239
void toResumeMode(ResumeMode& resumeMode, MutableHandleValue value,
240
MutableHandleSavedFrame exnStack) const;
241
/*
242
* Given a `ResumeMode` and value (typically derived from a resumption value
243
* returned by a Debugger hook), update this completion as requested.
244
*/
245
void updateForNextHandler(ResumeMode resumeMode, HandleValue value);
246
247
private:
248
using Variant =
249
mozilla::Variant<Return, Throw, Terminate, InitialYield, Yield, Await>;
250
struct BuildValueMatcher;
251
struct ToResumeModeMatcher;
252
253
Variant variant;
254
};
255
256
typedef HashSet<WeakHeapPtrGlobalObject,
257
MovableCellHasher<WeakHeapPtrGlobalObject>, ZoneAllocPolicy>
258
WeakGlobalObjectSet;
259
260
#ifdef DEBUG
261
extern void CheckDebuggeeThing(JSScript* script, bool invisibleOk);
262
263
extern void CheckDebuggeeThing(LazyScript* script, bool invisibleOk);
264
265
extern void CheckDebuggeeThing(JSObject* obj, bool invisibleOk);
266
#endif
267
268
/*
269
* [SMDOC] Cross-compartment weakmap entries for Debugger API objects
270
*
271
* The Debugger API creates objects like Debugger.Object, Debugger.Script,
272
* Debugger.Environment, etc. to refer to things in the debuggee. Each Debugger
273
* gets at most one Debugger.Mumble for each referent: Debugger.Mumbles are
274
* unique per referent per Debugger. This is accomplished by storing the
275
* debugger objects in a DebuggerWeakMap, using the debuggee thing as the key.
276
*
277
* Since a Debugger and its debuggee must be in different compartments, a
278
* Debugger.Mumble's pointer to its referent is a cross-compartment edge, from
279
* the debugger's compartment into the debuggee compartment. Like any other sort
280
* of cross-compartment edge, the GC needs to be able to find all of these edges
281
* readily. The GC therefore consults the debugger's weakmap tables as
282
* necessary. This allows the garbage collector to easily find edges between
283
* debuggee object compartments and debugger compartments when calculating the
284
* zone sweep groups.
285
*
286
* The current implementation results in all debuggee object compartments being
287
* swept in the same group as the debugger. This is a conservative approach, and
288
* compartments may be unnecessarily grouped. However this results in a simpler
289
* and faster implementation.
290
*/
291
292
/*
293
* A weakmap from GC thing keys to JSObject values that supports the keys being
294
* in different compartments to the values. All values must be in the same
295
* compartment.
296
*
297
* If InvisibleKeysOk is true, then the map can have keys in invisible-to-
298
* debugger compartments. If it is false, we assert that such entries are never
299
* created.
300
*
301
* Note that keys in these weakmaps can be in any compartment, debuggee or not,
302
* because they are not deleted when a compartment is no longer a debuggee: the
303
* values need to maintain object identity across add/remove/add
304
* transitions. (Frames are an exception to the rule. Existing Debugger.Frame
305
* objects are killed if their realm is removed as a debugger; if the realm
306
* beacomes a debuggee again later, new Frame objects are created.)
307
*/
308
template <class Referent, class Wrapper, bool InvisibleKeysOk = false>
309
class DebuggerWeakMap : private WeakMap<HeapPtr<Referent*>, HeapPtr<Wrapper*>> {
310
private:
311
typedef HeapPtr<Referent*> Key;
312
typedef HeapPtr<Wrapper*> Value;
313
314
JS::Compartment* compartment;
315
316
public:
317
typedef WeakMap<Key, Value> Base;
318
using ReferentType = Referent;
319
using WrapperType = Wrapper;
320
321
explicit DebuggerWeakMap(JSContext* cx)
322
: Base(cx), compartment(cx->compartment()) {}
323
324
public:
325
// Expose those parts of HashMap public interface that are used by Debugger
326
// methods.
327
328
using Entry = typename Base::Entry;
329
using Ptr = typename Base::Ptr;
330
using AddPtr = typename Base::AddPtr;
331
using Range = typename Base::Range;
332
using Lookup = typename Base::Lookup;
333
334
// Expose WeakMap public interface.
335
336
using Base::all;
337
using Base::has;
338
using Base::lookup;
339
using Base::lookupForAdd;
340
using Base::lookupUnbarriered;
341
using Base::remove;
342
using Base::trace;
343
using Base::zone;
344
#ifdef DEBUG
345
using Base::hasEntry;
346
#endif
347
348
class Enum : public Base::Enum {
349
public:
350
explicit Enum(DebuggerWeakMap& map) : Base::Enum(map) {}
351
};
352
353
template <typename KeyInput, typename ValueInput>
354
bool relookupOrAdd(AddPtr& p, const KeyInput& k, const ValueInput& v) {
355
MOZ_ASSERT(v->compartment() == this->compartment);
356
#ifdef DEBUG
357
CheckDebuggeeThing(k, InvisibleKeysOk);
358
#endif
359
MOZ_ASSERT(!Base::has(k));
360
bool ok = Base::relookupOrAdd(p, k, v);
361
return ok;
362
}
363
364
public:
365
void traceCrossCompartmentEdges(JSTracer* tracer) {
366
for (Enum e(*this); !e.empty(); e.popFront()) {
367
e.front().value()->trace(tracer);
368
Key key = e.front().key();
369
TraceEdge(tracer, &key, "Debugger WeakMap key");
370
if (key != e.front().key()) {
371
e.rekeyFront(key);
372
}
373
key.unsafeSet(nullptr);
374
}
375
}
376
377
bool findSweepGroupEdges() override;
378
379
private:
380
#ifdef JS_GC_ZEAL
381
// Let the weak map marking verifier know that this map can
382
// contain keys in other zones.
383
virtual bool allowKeysInOtherZones() const override { return true; }
384
#endif
385
};
386
387
class LeaveDebuggeeNoExecute;
388
389
class MOZ_RAII EvalOptions {
390
JS::UniqueChars filename_;
391
unsigned lineno_ = 1;
392
393
public:
394
EvalOptions() = default;
395
~EvalOptions() = default;
396
const char* filename() const { return filename_.get(); }
397
unsigned lineno() const { return lineno_; }
398
MOZ_MUST_USE bool setFilename(JSContext* cx, const char* filename);
399
void setLineno(unsigned lineno) { lineno_ = lineno; }
400
};
401
402
/*
403
* Env is the type of what ES5 calls "lexical environments" (runtime activations
404
* of lexical scopes). This is currently just JSObject, and is implemented by
405
* CallObject, LexicalEnvironmentObject, and WithEnvironmentObject, among
406
* others--but environments and objects are really two different concepts.
407
*/
408
typedef JSObject Env;
409
410
// The referent of a Debugger.Script.
411
//
412
// - For most scripts, we point at their LazyScript, because that address
413
// doesn't change as the script is lazified/delazified.
414
//
415
// - For scripts that cannot be lazified, and thus have no LazyScript, we point
416
// directly to their JSScript.
417
//
418
// - For Web Assembly instances for which we are presenting a script-like
419
// interface, we point at their WasmInstanceObject.
420
//
421
// The DebuggerScript object itself simply stores a Cell* in its private
422
// pointer, but when we're working with that pointer in C++ code, we'd rather
423
// not pass around a Cell* and be constantly asserting that, yes, this really
424
// does point to something okay. Instead, we immediately build an instance of
425
// this type from the Cell* and use that instead, so we can benefit from
426
// Variant's static checks.
427
typedef mozilla::Variant<JSScript*, LazyScript*, WasmInstanceObject*>
428
DebuggerScriptReferent;
429
430
// The referent of a Debugger.Source.
431
//
432
// - For most sources, this is a ScriptSourceObject.
433
//
434
// - For Web Assembly instances for which we are presenting a source-like
435
// interface, we point at their WasmInstanceObject.
436
//
437
// The DebuggerSource object actually simply stores a Cell* in its private
438
// pointer. See the comments for DebuggerScriptReferent for the rationale for
439
// this type.
440
typedef mozilla::Variant<ScriptSourceObject*, WasmInstanceObject*>
441
DebuggerSourceReferent;
442
443
class Debugger : private mozilla::LinkedListElement<Debugger> {
444
friend class DebugAPI;
445
friend class Breakpoint;
446
friend class DebuggerFrame;
447
friend class DebuggerMemory;
448
friend struct JSRuntime::GlobalObjectWatchersLinkAccess<Debugger>;
449
friend class SavedStacks;
450
friend class ScriptedOnStepHandler;
451
friend class ScriptedOnPopHandler;
452
friend class mozilla::LinkedListElement<Debugger>;
453
friend class mozilla::LinkedList<Debugger>;
454
friend bool(::JS_DefineDebuggerObject)(JSContext* cx, JS::HandleObject obj);
455
friend bool(::JS::dbg::IsDebugger)(JSObject&);
456
friend bool(::JS::dbg::GetDebuggeeGlobals)(JSContext*, JSObject&,
457
MutableHandleObjectVector);
458
friend bool JS::dbg::FireOnGarbageCollectionHookRequired(JSContext* cx);
459
friend bool JS::dbg::FireOnGarbageCollectionHook(
460
JSContext* cx, JS::dbg::GarbageCollectionEvent::Ptr&& data);
461
462
public:
463
enum Hook {
464
OnDebuggerStatement,
465
OnExceptionUnwind,
466
OnNewScript,
467
OnEnterFrame,
468
OnNativeCall,
469
OnNewGlobalObject,
470
OnNewPromise,
471
OnPromiseSettled,
472
OnGarbageCollection,
473
HookCount
474
};
475
enum {
476
JSSLOT_DEBUG_PROTO_START,
477
JSSLOT_DEBUG_FRAME_PROTO = JSSLOT_DEBUG_PROTO_START,
478
JSSLOT_DEBUG_ENV_PROTO,
479
JSSLOT_DEBUG_OBJECT_PROTO,
480
JSSLOT_DEBUG_SCRIPT_PROTO,
481
JSSLOT_DEBUG_SOURCE_PROTO,
482
JSSLOT_DEBUG_MEMORY_PROTO,
483
JSSLOT_DEBUG_PROTO_STOP,
484
JSSLOT_DEBUG_HOOK_START = JSSLOT_DEBUG_PROTO_STOP,
485
JSSLOT_DEBUG_HOOK_STOP = JSSLOT_DEBUG_HOOK_START + HookCount,
486
JSSLOT_DEBUG_MEMORY_INSTANCE = JSSLOT_DEBUG_HOOK_STOP,
487
JSSLOT_DEBUG_DEBUGGEE_LINK,
488
JSSLOT_DEBUG_COUNT
489
};
490
491
// Bring DebugAPI::IsObserving into the Debugger namespace.
492
using IsObserving = DebugAPI::IsObserving;
493
static const IsObserving Observing = DebugAPI::Observing;
494
static const IsObserving NotObserving = DebugAPI::NotObserving;
495
496
// Return true if the given realm is a debuggee of this debugger,
497
// false otherwise.
498
bool isDebuggeeUnbarriered(const Realm* realm) const;
499
500
// Return true if this Debugger observed a debuggee that participated in the
501
// GC identified by the given GC number. Return false otherwise.
502
// May return false negatives if we have hit OOM.
503
bool observedGC(uint64_t majorGCNumber) const {
504
return observedGCs.has(majorGCNumber);
505
}
506
507
// Notify this Debugger that one or more of its debuggees is participating
508
// in the GC identified by the given GC number.
509
bool debuggeeIsBeingCollected(uint64_t majorGCNumber) {
510
return observedGCs.put(majorGCNumber);
511
}
512
513
static SavedFrame* getObjectAllocationSite(JSObject& obj);
514
515
struct AllocationsLogEntry {
516
AllocationsLogEntry(HandleObject frame, mozilla::TimeStamp when,
517
const char* className, HandleAtom ctorName, size_t size,
518
bool inNursery)
519
: frame(frame),
520
when(when),
521
className(className),
522
ctorName(ctorName),
523
size(size),
524
inNursery(inNursery) {
525
MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>() ||
526
IsDeadProxyObject(frame));
527
}
528
529
HeapPtr<JSObject*> frame;
530
mozilla::TimeStamp when;
531
const char* className;
532
HeapPtr<JSAtom*> ctorName;
533
size_t size;
534
bool inNursery;
535
536
void trace(JSTracer* trc) {
537
TraceNullableEdge(trc, &frame, "Debugger::AllocationsLogEntry::frame");
538
TraceNullableEdge(trc, &ctorName,
539
"Debugger::AllocationsLogEntry::ctorName");
540
}
541
};
542
543
// Barrier methods so we can have WeakHeapPtr<Debugger*>.
544
static void readBarrier(Debugger* dbg) {
545
InternalBarrierMethods<JSObject*>::readBarrier(dbg->object);
546
}
547
static void writeBarrierPost(Debugger** vp, Debugger* prev, Debugger* next) {}
548
#ifdef DEBUG
549
static void assertThingIsNotGray(Debugger* dbg) { return; }
550
#endif
551
552
private:
553
GCPtrNativeObject object; /* The Debugger object. Strong reference. */
554
WeakGlobalObjectSet
555
debuggees; /* Debuggee globals. Cross-compartment weak references. */
556
JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */
557
js::GCPtrObject uncaughtExceptionHook; /* Strong reference. */
558
bool allowUnobservedAsmJS;
559
560
// Whether to enable code coverage on the Debuggee.
561
bool collectCoverageInfo;
562
563
template <typename T>
564
struct DebuggerLinkAccess {
565
static mozilla::DoublyLinkedListElement<T>& Get(T* aThis) {
566
return aThis->debuggerLink;
567
}
568
};
569
570
// List of all js::Breakpoints in this debugger.
571
using BreakpointList =
572
mozilla::DoublyLinkedList<js::Breakpoint,
573
DebuggerLinkAccess<js::Breakpoint>>;
574
BreakpointList breakpoints;
575
576
// The set of GC numbers for which one or more of this Debugger's observed
577
// debuggees participated in.
578
using GCNumberSet =
579
HashSet<uint64_t, DefaultHasher<uint64_t>, ZoneAllocPolicy>;
580
GCNumberSet observedGCs;
581
582
using AllocationsLog = js::TraceableFifo<AllocationsLogEntry>;
583
584
AllocationsLog allocationsLog;
585
bool trackingAllocationSites;
586
double allocationSamplingProbability;
587
size_t maxAllocationsLogLength;
588
bool allocationsLogOverflowed;
589
590
static const size_t DEFAULT_MAX_LOG_LENGTH = 5000;
591
592
MOZ_MUST_USE bool appendAllocationSite(JSContext* cx, HandleObject obj,
593
HandleSavedFrame frame,
594
mozilla::TimeStamp when);
595
596
/*
597
* Recompute the set of debuggee zones based on the set of debuggee globals.
598
*/
599
void recomputeDebuggeeZoneSet();
600
601
/*
602
* Return true if there is an existing object metadata callback for the
603
* given global's compartment that will prevent our instrumentation of
604
* allocations.
605
*/
606
static bool cannotTrackAllocations(const GlobalObject& global);
607
608
/*
609
* Add allocations tracking for objects allocated within the given
610
* debuggee's compartment. The given debuggee global must be observed by at
611
* least one Debugger that is tracking allocations.
612
*/
613
static MOZ_MUST_USE bool addAllocationsTracking(
614
JSContext* cx, Handle<GlobalObject*> debuggee);
615
616
/*
617
* Remove allocations tracking for objects allocated within the given
618
* global's compartment. This is a no-op if there are still Debuggers
619
* observing this global and who are tracking allocations.
620
*/
621
static void removeAllocationsTracking(GlobalObject& global);
622
623
/*
624
* Add or remove allocations tracking for all debuggees.
625
*/
626
MOZ_MUST_USE bool addAllocationsTrackingForAllDebuggees(JSContext* cx);
627
void removeAllocationsTrackingForAllDebuggees();
628
629
/*
630
* If this Debugger has a onNewGlobalObject handler, then
631
* this link is inserted into the list headed by
632
* JSRuntime::onNewGlobalObjectWatchers.
633
*/
634
mozilla::DoublyLinkedListElement<Debugger> onNewGlobalObjectWatchersLink;
635
636
/*
637
* Map from stack frames that are currently on the stack to Debugger.Frame
638
* instances.
639
*
640
* The keys are always live stack frames. We drop them from this map as
641
* soon as they leave the stack (see slowPathOnLeaveFrame) and in
642
* removeDebuggee.
643
*
644
* We don't trace the keys of this map (the frames are on the stack and
645
* thus necessarily live), but we do trace the values. It's like a WeakMap
646
* that way, but since stack frames are not gc-things, the implementation
647
* has to be different.
648
*/
649
typedef HashMap<AbstractFramePtr, HeapPtr<DebuggerFrame*>,
650
DefaultHasher<AbstractFramePtr>, ZoneAllocPolicy>
651
FrameMap;
652
FrameMap frames;
653
654
/*
655
* Map from generator objects to their Debugger.Frame instances.
656
*
657
* When a Debugger.Frame is created for a generator frame, it is added to
658
* this map and remains there for the lifetime of the generator, whether
659
* that frame is on the stack at the moment or not. This is in addition to
660
* the entry in `frames` that exists as long as the generator frame is on
661
* the stack.
662
*
663
* We need to keep the Debugger.Frame object alive to deliver it to the
664
* onEnterFrame handler on resume, and to retain onStep and onPop hooks.
665
*
666
* An entry is present in this table when:
667
* - both the debuggee generator object and the Debugger.Frame object exists
668
* - the debuggee generator object belongs to a relam that is a debuggee of
669
* the Debugger.Frame's owner.
670
*
671
* regardless of whether the frame is currently suspended. (This list is
672
* meant to explain why we update the table in the particular places where
673
* we do so.)
674
*
675
* An entry in this table exists if and only if the Debugger.Frame's
676
* GENERATOR_INFO_SLOT is set.
677
*/
678
typedef DebuggerWeakMap<AbstractGeneratorObject, DebuggerFrame>
679
GeneratorWeakMap;
680
GeneratorWeakMap generatorFrames;
681
682
/* An ephemeral map from JSScript* to Debugger.Script instances. */
683
typedef DebuggerWeakMap<JSScript, DebuggerScript> ScriptWeakMap;
684
ScriptWeakMap scripts;
685
686
using LazyScriptWeakMap = DebuggerWeakMap<LazyScript, DebuggerScript>;
687
LazyScriptWeakMap lazyScripts;
688
689
using LazyScriptVector = JS::GCVector<LazyScript*>;
690
691
// The map from debuggee source script objects to their Debugger.Source
692
// instances.
693
typedef DebuggerWeakMap<ScriptSourceObject, DebuggerSource, true>
694
SourceWeakMap;
695
SourceWeakMap sources;
696
697
// The map from debuggee objects to their Debugger.Object instances.
698
typedef DebuggerWeakMap<JSObject, DebuggerObject> ObjectWeakMap;
699
ObjectWeakMap objects;
700
701
// The map from debuggee Envs to Debugger.Environment instances.
702
typedef DebuggerWeakMap<JSObject, DebuggerEnvironment> EnvironmentWeakMap;
703
EnvironmentWeakMap environments;
704
705
// The map from WasmInstanceObjects to synthesized Debugger.Script
706
// instances.
707
typedef DebuggerWeakMap<WasmInstanceObject, DebuggerScript>
708
WasmInstanceScriptWeakMap;
709
WasmInstanceScriptWeakMap wasmInstanceScripts;
710
711
// The map from WasmInstanceObjects to synthesized Debugger.Source
712
// instances.
713
typedef DebuggerWeakMap<WasmInstanceObject, DebuggerSource>
714
WasmInstanceSourceWeakMap;
715
WasmInstanceSourceWeakMap wasmInstanceSources;
716
717
// Keep track of tracelogger last drained identifiers to know if there are
718
// lost events.
719
#ifdef NIGHTLY_BUILD
720
uint32_t traceLoggerLastDrainedSize;
721
uint32_t traceLoggerLastDrainedIteration;
722
#endif
723
uint32_t traceLoggerScriptedCallsLastDrainedSize;
724
uint32_t traceLoggerScriptedCallsLastDrainedIteration;
725
726
class QueryBase;
727
class ScriptQuery;
728
class SourceQuery;
729
class ObjectQuery;
730
731
enum class FromSweep { No, Yes };
732
733
MOZ_MUST_USE bool addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> obj);
734
void removeDebuggeeGlobal(JSFreeOp* fop, GlobalObject* global,
735
WeakGlobalObjectSet::Enum* debugEnum,
736
FromSweep fromSweep);
737
738
enum class CallUncaughtExceptionHook { No, Yes };
739
740
/*
741
* Apply the resumption information in (resumeMode, vp) to `frame` in
742
* anticipation of returning to the debuggee.
743
*
744
* This is the usual path for returning from the debugger to the debuggee
745
* when we have a resumption value to apply. This does final checks on the
746
* result value and exits the debugger's realm by calling `ar.reset()`.
747
* Some hooks don't call this because they don't allow the debugger to
748
* control resumption; those just call `ar.reset()` and return.
749
*/
750
ResumeMode leaveDebugger(mozilla::Maybe<AutoRealm>& ar,
751
AbstractFramePtr frame,
752
const mozilla::Maybe<HandleValue>& maybeThisv,
753
CallUncaughtExceptionHook callHook,
754
ResumeMode resumeMode, MutableHandleValue vp);
755
756
/*
757
* Report and clear the pending exception on ar.context, if any, and return
758
* ResumeMode::Terminate.
759
*/
760
ResumeMode reportUncaughtException(mozilla::Maybe<AutoRealm>& ar);
761
762
/*
763
* Cope with an error or exception in a debugger hook.
764
*
765
* If callHook is true, then call the uncaughtExceptionHook, if any. If, in
766
* addition, vp is given, then parse the value returned by
767
* uncaughtExceptionHook as a resumption value.
768
*
769
* If there is no uncaughtExceptionHook, or if it fails, report and clear
770
* the pending exception on ar.context and return ResumeMode::Terminate.
771
*
772
* This always calls `ar.reset()`; ar is a parameter because this method
773
* must do some things in the debugger realm and some things in the
774
* debuggee realm.
775
*/
776
ResumeMode handleUncaughtException(mozilla::Maybe<AutoRealm>& ar);
777
ResumeMode handleUncaughtException(
778
mozilla::Maybe<AutoRealm>& ar, MutableHandleValue vp,
779
const mozilla::Maybe<HandleValue>& thisVForCheck = mozilla::Nothing(),
780
AbstractFramePtr frame = NullFramePtr());
781
782
ResumeMode handleUncaughtExceptionHelper(
783
mozilla::Maybe<AutoRealm>& ar, MutableHandleValue* vp,
784
const mozilla::Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame);
785
786
/*
787
* Handle the result of a hook that is expected to return a resumption
789
* called when we return from a debugging hook to debuggee code. The
790
* interpreter wants a (ResumeMode, Value) pair telling it how to proceed.
791
*
792
* Precondition: ar is entered. We are in the debugger compartment.
793
*
794
* Postcondition: This called ar.reset(). See handleUncaughtException.
795
*
796
* If `success` is false, the hook failed. If an exception is pending in
797
* ar.context(), return handleUncaughtException(ar, vp, callhook).
798
* Otherwise just return ResumeMode::Terminate.
799
*
800
* If `success` is true, there must be no exception pending in ar.context().
801
* `rv` may be:
802
*
803
* undefined - Return `ResumeMode::Continue` to continue execution
804
* normally.
805
*
806
* {return: value} or {throw: value} - Call unwrapDebuggeeValue to
807
* unwrap `value`. Store the result in `*vp` and return
808
* `ResumeMode::Return` or `ResumeMode::Throw`. The interpreter
809
* will force the current frame to return or throw an exception.
810
*
811
* null - Return `ResumeMode::Terminate` to terminate the debuggee with
812
* an uncatchable error.
813
*
814
* anything else - Make a new TypeError the pending exception and
815
* return handleUncaughtException(ar, vp, callHook).
816
*/
817
ResumeMode processHandlerResult(mozilla::Maybe<AutoRealm>& ar, bool success,
818
const Value& rv, AbstractFramePtr frame,
819
jsbytecode* pc, MutableHandleValue vp);
820
821
ResumeMode processParsedHandlerResult(mozilla::Maybe<AutoRealm>& ar,
822
AbstractFramePtr frame, jsbytecode* pc,
823
bool success, ResumeMode resumeMode,
824
MutableHandleValue vp);
825
826
GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
827
828
static void traceObject(JSTracer* trc, JSObject* obj);
829
830
void trace(JSTracer* trc);
831
friend struct js::GCManagedDeletePolicy<Debugger>;
832
833
void traceForMovingGC(JSTracer* trc);
834
void traceCrossCompartmentEdges(JSTracer* tracer);
835
836
static const JSClassOps classOps_;
837
838
public:
839
static const JSClass class_;
840
841
private:
842
template <typename F>
843
void forEachWeakMap(const F& f);
844
845
static MOZ_MUST_USE bool getHookImpl(JSContext* cx, const CallArgs& args,
846
Debugger& dbg, Hook which);
847
static MOZ_MUST_USE bool setHookImpl(JSContext* cx, const CallArgs& args,
848
Debugger& dbg, Hook which);
849
850
static bool isCompilableUnit(JSContext* cx, unsigned argc, Value* vp);
851
static bool recordReplayProcessKind(JSContext* cx, unsigned argc, Value* vp);
852
static bool construct(JSContext* cx, unsigned argc, Value* vp);
853
854
struct CallData;
855
856
static const JSPropertySpec properties[];
857
static const JSFunctionSpec methods[];
858
static const JSFunctionSpec static_methods[];
859
860
static void removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx,
861
AbstractFramePtr frame,
862
bool suspending = false);
863
static bool updateExecutionObservabilityOfFrames(
864
JSContext* cx, const DebugAPI::ExecutionObservableSet& obs,
865
IsObserving observing);
866
static bool updateExecutionObservabilityOfScripts(
867
JSContext* cx, const DebugAPI::ExecutionObservableSet& obs,
868
IsObserving observing);
869
static bool updateExecutionObservability(
870
JSContext* cx, DebugAPI::ExecutionObservableSet& obs,
871
IsObserving observing);
872
873
template <typename FrameFn /* void (DebuggerFrame*) */>
874
static void forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn);
875
876
/*
877
* Return a vector containing all Debugger.Frame instances referring to
878
* |frame|. |global| is |frame|'s global object; if nullptr or omitted, we
879
* compute it ourselves from |frame|.
880
*/
881
using DebuggerFrameVector = GCVector<DebuggerFrame*>;
882
static MOZ_MUST_USE bool getDebuggerFrames(
883
AbstractFramePtr frame, MutableHandle<DebuggerFrameVector> frames);
884
885
public:
886
// Public for DebuggerScript::setBreakpoint.
887
static MOZ_MUST_USE bool ensureExecutionObservabilityOfScript(
888
JSContext* cx, JSScript* script);
889
890
// Whether the Debugger instance needs to observe all non-AOT JS
891
// execution of its debugees.
892
IsObserving observesAllExecution() const;
893
894
// Whether the Debugger instance needs to observe AOT-compiled asm.js
895
// execution of its debuggees.
896
IsObserving observesAsmJS() const;
897
898
// Whether the Debugger instance needs to observe coverage of any JavaScript
899
// execution.
900
IsObserving observesCoverage() const;
901
902
// Whether the Debugger instance needs to observe native call invocations.
903
IsObserving observesNativeCalls() const;
904
905
private:
906
static MOZ_MUST_USE bool ensureExecutionObservabilityOfFrame(
907
JSContext* cx, AbstractFramePtr frame);
908
static MOZ_MUST_USE bool ensureExecutionObservabilityOfRealm(
909
JSContext* cx, JS::Realm* realm);
910
911
static bool hookObservesAllExecution(Hook which);
912
913
MOZ_MUST_USE bool updateObservesAllExecutionOnDebuggees(
914
JSContext* cx, IsObserving observing);
915
MOZ_MUST_USE bool updateObservesCoverageOnDebuggees(JSContext* cx,
916
IsObserving observing);
917
void updateObservesAsmJSOnDebuggees(IsObserving observing);
918
919
JSObject* getHook(Hook hook) const;
920
bool hasAnyLiveHooks() const;
921
922
static void slowPathPromiseHook(JSContext* cx, Hook hook,
923
Handle<PromiseObject*> promise);
924
925
template <typename HookIsEnabledFun /* bool (Debugger*) */,
926
typename FireHookFun /* ResumeMode (Debugger*) */>
927
static ResumeMode dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
928
FireHookFun fireHook);
929
930
ResumeMode fireDebuggerStatement(JSContext* cx, MutableHandleValue vp);
931
ResumeMode fireExceptionUnwind(JSContext* cx, MutableHandleValue vp);
932
ResumeMode fireEnterFrame(JSContext* cx, MutableHandleValue vp);
933
ResumeMode fireNativeCall(JSContext* cx, const CallArgs& args,
934
CallReason reason, MutableHandleValue vp);
935
ResumeMode fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global,
936
MutableHandleValue vp);
937
ResumeMode firePromiseHook(JSContext* cx, Hook hook, HandleObject promise,
938
MutableHandleValue vp);
939
940
DebuggerScript* newVariantWrapper(JSContext* cx,
941
Handle<DebuggerScriptReferent> referent) {
942
return newDebuggerScript(cx, referent);
943
}
944
DebuggerSource* newVariantWrapper(JSContext* cx,
945
Handle<DebuggerSourceReferent> referent) {
946
return newDebuggerSource(cx, referent);
947
}
948
949
/*
950
* Helper function to help wrap Debugger objects whose referents may be
951
* variants. Currently Debugger.Script and Debugger.Source referents may
952
* be variants.
953
*
954
* Prefer using wrapScript, wrapWasmScript, wrapSource, and wrapWasmSource
955
* whenever possible.
956
*/
957
template <typename Map>
958
typename Map::WrapperType* wrapVariantReferent(
959
JSContext* cx, Map& map,
960
Handle<typename Map::WrapperType::ReferentVariant> referent);
961
DebuggerScript* wrapVariantReferent(JSContext* cx,
962
Handle<DebuggerScriptReferent> referent);
963
DebuggerSource* wrapVariantReferent(JSContext* cx,
964
Handle<DebuggerSourceReferent> referent);
965
966
/*
967
* Allocate and initialize a Debugger.Script instance whose referent is
968
* |referent|.
969
*/
970
DebuggerScript* newDebuggerScript(JSContext* cx,
971
Handle<DebuggerScriptReferent> referent);
972
973
/*
974
* Allocate and initialize a Debugger.Source instance whose referent is
975
* |referent|.
976
*/
977
DebuggerSource* newDebuggerSource(JSContext* cx,
978
Handle<DebuggerSourceReferent> referent);
979
980
/*
981
* Receive a "new script" event from the engine. A new script was compiled
982
* or deserialized.
983
*/
984
void fireNewScript(JSContext* cx,
985
Handle<DebuggerScriptReferent> scriptReferent);
986
987
/*
988
* Receive a "garbage collection" event from the engine. A GC cycle with the
989
* given data was recently completed.
990
*/
991
void fireOnGarbageCollectionHook(
992
JSContext* cx, const JS::dbg::GarbageCollectionEvent::Ptr& gcData);
993
994
inline Breakpoint* firstBreakpoint() const;
995
996
static MOZ_MUST_USE bool replaceFrameGuts(JSContext* cx,
997
AbstractFramePtr from,
998
AbstractFramePtr to,
999
ScriptFrameIter& iter);
1000
1001
public:
1002
Debugger(JSContext* cx, NativeObject* dbg);
1003
~Debugger();
1004
1005
inline const js::GCPtrNativeObject& toJSObject() const;
1006
inline js::GCPtrNativeObject& toJSObjectRef();
1007
static inline Debugger* fromJSObject(const JSObject* obj);
1008
1009
#ifdef DEBUG
1010
static bool isChildJSObject(JSObject* obj);
1011
#endif
1012
static Debugger* fromChildJSObject(JSObject* obj);
1013
1014
Zone* zone() const { return toJSObject()->zone(); }
1015
1016
bool hasMemory() const;
1017
DebuggerMemory& memory() const;
1018
1019
WeakGlobalObjectSet::Range allDebuggees() const { return debuggees.all(); }
1020
1021
static void detachAllDebuggersFromGlobal(JSFreeOp* fop, GlobalObject* global);
1022
#ifdef DEBUG
1023
static bool isDebuggerCrossCompartmentEdge(JSObject* obj,
1024
const js::gc::Cell* cell);
1025
#endif
1026
1027
static bool hasLiveHook(GlobalObject* global, Hook which);
1028
1029
/*** Functions for use by Debugger.cpp. *********************************/
1030
1031
inline bool observesEnterFrame() const;
1032
inline bool observesNewScript() const;
1033
inline bool observesNewGlobalObject() const;
1034
inline bool observesGlobal(GlobalObject* global) const;
1035
bool observesFrame(AbstractFramePtr frame) const;
1036
bool observesFrame(const FrameIter& iter) const;
1037
bool observesScript(JSScript* script) const;
1038
bool observesWasm(wasm::Instance* instance) const;
1039
1040
/*
1041
* If env is nullptr, call vp->setNull() and return true. Otherwise, find
1042
* or create a Debugger.Environment object for the given Env. On success,
1043
* store the Environment object in *vp and return true.
1044
*/
1045
MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env,
1046
MutableHandleValue vp);
1047
MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env,
1048
MutableHandleDebuggerEnvironment result);
1049
1050
/*
1051
* Like cx->compartment()->wrap(cx, vp), but for the debugger realm.
1052
*
1053
* Preconditions: *vp is a value from a debuggee realm; cx is in the
1054
* debugger's compartment.
1055
*
1056
* If *vp is an object, this produces a (new or existing) Debugger.Object
1057
* wrapper for it. Otherwise this is the same as Compartment::wrap.
1058
*
1059
* If *vp is a magic JS_OPTIMIZED_OUT value, this produces a plain object
1060
* of the form { optimizedOut: true }.
1061
*
1062
* If *vp is a magic JS_OPTIMIZED_ARGUMENTS value signifying missing
1063
* arguments, this produces a plain object of the form { missingArguments:
1064
* true }.
1065
*
1066
* If *vp is a magic JS_UNINITIALIZED_LEXICAL value signifying an
1067
* unaccessible uninitialized binding, this produces a plain object of the
1068
* form { uninitialized: true }.
1069
*/
1070
MOZ_MUST_USE bool wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
1071
MOZ_MUST_USE bool wrapDebuggeeObject(JSContext* cx, HandleObject obj,
1072
MutableHandleDebuggerObject result);
1073
MOZ_MUST_USE bool wrapNullableDebuggeeObject(
1074
JSContext* cx, HandleObject obj, MutableHandleDebuggerObject result);
1075
1076
/*
1077
* Unwrap a Debug.Object, without rewrapping it for any particular debuggee
1078
* compartment.
1079
*
1080
* Preconditions: cx is in the debugger compartment. *vp is a value in that
1081
* compartment. (*vp should be a "debuggee value", meaning it is the
1082
* debugger's reflection of a value in the debuggee.)
1083
*
1084
* If *vp is a Debugger.Object, store the referent in *vp. Otherwise, if *vp
1085
* is an object, throw a TypeError, because it is not a debuggee
1086
* value. Otherwise *vp is a primitive, so leave it alone.
1087
*
1088
* When passing values from the debuggee to the debugger:
1089
* enter debugger compartment;
1090
* call wrapDebuggeeValue; // compartment- and debugger-wrapping
1091
*
1092
* When passing values from the debugger to the debuggee:
1093
* call unwrapDebuggeeValue; // debugger-unwrapping
1094
* enter debuggee realm;
1095
* call cx->compartment()->wrap; // compartment-rewrapping
1096
*
1097
* (Extreme nerd sidebar: Unwrapping happens in two steps because there are
1098
* two different kinds of symmetry at work: regardless of which direction
1099
* we're going, we want any exceptions to be created and thrown in the
1100
* debugger compartment--mirror symmetry. But compartment wrapping always
1101
* happens in the target compartment--rotational symmetry.)
1102
*/
1103
MOZ_MUST_USE bool unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
1104
MOZ_MUST_USE bool unwrapDebuggeeObject(JSContext* cx,
1105
MutableHandleObject obj);
1106
MOZ_MUST_USE bool unwrapPropertyDescriptor(
1107
JSContext* cx, HandleObject obj, MutableHandle<PropertyDescriptor> desc);
1108
1109
/*
1110
* Store the Debugger.Frame object for iter in *vp/result.
1111
*
1112
* If this Debugger does not already have a Frame object for the frame
1113
* `iter` points to, a new Frame object is created, and `iter`'s private
1114
* data is copied into it.
1115
*/
1116
MOZ_MUST_USE bool getFrame(JSContext* cx, const FrameIter& iter,
1117
MutableHandleValue vp);
1118
MOZ_MUST_USE bool getFrame(JSContext* cx, const FrameIter& iter,
1119
MutableHandleDebuggerFrame result);
1120
1121
/*
1122
* Return the Debugger.Script object for |script|, or create a new one if
1123
* needed. The context |cx| must be in the debugger realm; |script| must be
1124
* a script in a debuggee realm.
1125
*/
1126
DebuggerScript* wrapScript(JSContext* cx, HandleScript script);
1127
1128
DebuggerScript* wrapLazyScript(JSContext* cx, Handle<LazyScript*> script);
1129
1130
/*
1131
* Return the Debugger.Script object for |wasmInstance| (the toplevel
1132
* script), synthesizing a new one if needed. The context |cx| must be in
1133
* the debugger compartment; |wasmInstance| must be a WasmInstanceObject in
1134
* the debuggee realm.
1135
*/
1136
DebuggerScript* wrapWasmScript(JSContext* cx,
1137
Handle<WasmInstanceObject*> wasmInstance);
1138
1139
/*
1140
* Return the Debugger.Source object for |source|, or create a new one if
1141
* needed. The context |cx| must be in the debugger compartment; |source|
1142
* must be a script source object in a debuggee realm.
1143
*/
1144
DebuggerSource* wrapSource(JSContext* cx,
1145
js::HandleScriptSourceObject source);
1146
1147
/*
1148
* Return the Debugger.Source object for |wasmInstance| (the entire module),
1149
* synthesizing a new one if needed. The context |cx| must be in the
1150
* debugger compartment; |wasmInstance| must be a WasmInstanceObject in the
1151
* debuggee realm.
1152
*/
1153
DebuggerSource* wrapWasmSource(JSContext* cx,
1154
Handle<WasmInstanceObject*> wasmInstance);
1155
1156
DebuggerDebuggeeLink* getDebuggeeLink();
1157
1158
private:
1159
Debugger(const Debugger&) = delete;
1160
Debugger& operator=(const Debugger&) = delete;
1161
};
1162
1163
/**
1164
* This class exists for one specific reason. If a given Debugger object is in
1165
* a state where:
1166
*
1167
* a) nothing in the system has a reference to the object
1168
* b) the debugger is currently attached to a live debuggee
1169
* c) the debugger has hooks like 'onEnterFrame'
1170
*
1171
* then we don't want the GC to delete the Debugger, because the system could
1172
* still call the hooks. This means we need to ensure that, whenever the global
1173
* gets marked, the Debugger will get marked as well. Critically, we _only_
1174
* want that to happen if the debugger has hooks. If it doesn't, then GCing
1175
* the debugger is the right think to do.
1176
*
1177
* Note that there are _other_ cases where the debugger may be held live, but
1178
* those are not addressed by this case.
1179
*
1180
* To accomplish this, we use a bit of roundabout link approach. Both the
1181
* Debugger and the debuggees can reach the link object:
1182
*
1183
* Debugger -> DebuggerDebuggeeLink <- CCW <- Debuggee Global #1
1184
* | | ^ ^---<- CCW <- Debuggee Global #2
1185
* \--<<-optional-<<--/ \------<- CCW <- Debuggee Global #3
1186
*
1187
* and critically, the Debugger is able to conditionally add or remove the link
1188
* going from the DebuggerDebuggeeLink _back_ to the Debugger. When this link
1189
* exists, the GC can trace all the way from the global to the Debugger,
1190
* meaning that any Debugger with this link will be kept alive as long as any
1191
* of its debuggees are alive.
1192
*/
1193
class DebuggerDebuggeeLink : public NativeObject {
1194
private:
1195
enum {
1196
DEBUGGER_LINK_SLOT,
1197
RESERVED_SLOTS,
1198
};
1199
1200
public:
1201
static const JSClass class_;
1202
1203
void setLinkSlot(Debugger& dbg);
1204
void clearLinkSlot();
1205
};
1206
1207
/*
1208
* A Handler represents a Debugger API reflection object's handler function,
1209
* like a Debugger.Frame's onStep handler. These handler functions are called by
1210
* the Debugger API to notify the user of certain events. For each event type,
1211
* we define a separate subclass of Handler.
1212
*
1213
* When a reflection object accepts a Handler, it calls its 'hold' method; and
1214
* if the Handler is replaced by another, or the reflection object is finalized,
1215
* the reflection object calls the Handler's 'drop' method. The reflection
1216
* object does not otherwise manage the Handler's lifetime, say, by calling its
1217
* destructor or freeing its memory. A simple Handler implementation might have
1218
* an empty 'hold' method, and have its 'drop' method delete the Handler. A more
1219
* complex Handler might process many kinds of events, and thus inherit from
1220
* many Handler subclasses and be held by many reflection objects
1221
* simultaneously; a handler like this could use 'hold' and 'drop' to manage a
1222
* reference count.
1223
*
1224
* To support SpiderMonkey's memory use tracking, 'hold' and 'drop' also require
1225
* a pointer to the owning reflection object, so that the Holder implementation
1226
* can properly report changes in ownership to functions using the
1227
* js::gc::MemoryUse categories.
1228
*/
1229
struct Handler {
1230
virtual ~Handler() {}
1231
1232
/*
1233
* If this Handler is a reference to a callable JSObject, return that
1234
* JSObject. Otherwise, this method returns nullptr.
1235
*
1236
* The JavaScript getters for handler properties on reflection objects use
1237
* this method to obtain the callable the handler represents. When a Handler's
1238
* 'object' method returns nullptr, that handler is simply not visible to
1239
* JavaScript.
1240
*/
1241
virtual JSObject* object() const = 0;
1242
1243
/* Report that this Handler is now held by owner. See comment above. */
1244
virtual void hold(JSObject* owner) = 0;
1245
1246
/* Report that this Handler is no longer held by owner. See comment above. */
1247
virtual void drop(JSFreeOp* fop, JSObject* owner) = 0;
1248
1249
/*
1250
* Trace the reference to the handler. This method will be called by the
1251
* reflection object holding this Handler whenever the former is traced.
1252
*/
1253
virtual void trace(JSTracer* tracer) = 0;
1254
1255
/* Allocation size in bytes for memory accounting purposes. */
1256
virtual size_t allocSize() const = 0;
1257
};
1258
1259
class JSBreakpointSite;
1260
class WasmBreakpointSite;
1261
1262
/**
1263
* Breakpoint GC rules:
1264
*
1265
* BreakpointSites and Breakpoints are owned by the code in which they are set.
1266
* Tracing a JSScript or WasmInstance traces all BreakpointSites set in it,
1267
* which traces all Breakpoints; and if the code is garbage collected, the
1268
* BreakpointSite and the Breakpoints set at it are freed as well. Doing so is
1269
* not observable to JS, since the handlers would never fire, and there is no
1270
* way to enumerate all breakpoints without specifying a specific script, in
1271
* which case it must not have been GC'd.
1272
*
1273
* Although BreakpointSites and Breakpoints are not GC things, they should be
1274
* treated as belonging to the code's compartment. This means that the
1275
* BreakpointSite concrete subclasses' pointers to the code are not
1276
* cross-compartment references, but a Breakpoint's pointers to its handler and
1277
* owning Debugger are cross-compartment references, and go through
1278
* cross-compartment wrappers.
1279
*/
1280
1281
/**
1282
* A location in a JSScript or WasmInstance at which we have breakpoints. A
1283
* BreakpointSite owns a linked list of all the breakpoints set at its location.
1284
* In general, this list contains breakpoints set by multiple Debuggers in
1285
* various compartments.
1286
*
1287
* BreakpointSites are created only as needed, for locations at which
1288
* breakpoints are currently set. When the last breakpoint is removed from a
1289
* location, the BreakpointSite is removed as well.
1290
*
1291
* This is an abstract base class, with subclasses specialized for the different
1292
* sorts of code a breakpoint might be set in. JSBreakpointSite manages sites in
1293
* JSScripts, and WasmBreakpointSite manages sites in WasmInstances.
1294
*/
1295
class BreakpointSite {
1296
friend class DebugAPI;
1297
friend class Breakpoint;
1298
friend class Debugger;
1299
1300
private:
1301
template <typename T>
1302
struct SiteLinkAccess {
1303
static mozilla::DoublyLinkedListElement<T>& Get(T* aThis) {
1304
return aThis->siteLink;
1305
}
1306
};
1307
1308
// List of all js::Breakpoints at this instruction.
1309
using BreakpointList =
1310
mozilla::DoublyLinkedList<js::Breakpoint, SiteLinkAccess<js::Breakpoint>>;
1311
BreakpointList breakpoints;
1312
1313
protected:
1314
BreakpointSite(){};
1315
virtual ~BreakpointSite() {}
1316
void finalize(JSFreeOp* fop);
1317
virtual gc::Cell* owningCell() = 0;
1318
1319
public:
1320
Breakpoint* firstBreakpoint() const;
1321
bool hasBreakpoint(Breakpoint* bp);
1322
1323
bool isEmpty() const;
1324
virtual void trace(JSTracer* trc);
1325
virtual void remove(JSFreeOp* fop) = 0;
1326
void destroyIfEmpty(JSFreeOp* fop) {
1327
if (isEmpty()) {
1328
remove(fop);
1329
}
1330
}
1331
virtual Realm* realm() const = 0;
1332
};
1333
1334
/*
1335
* A breakpoint set at a given BreakpointSite, indicating the owning debugger
1336
* and the handler object. A Breakpoint is a member of two linked lists: its
1337
* owning debugger's list and its site's list.
1338
*/
1339
class Breakpoint {
1340
friend class DebugAPI;
1341
friend class Debugger;
1342
friend class BreakpointSite;
1343
1344
public:
1345
/* Our owning debugger. */
1346
Debugger* const debugger;
1347
1348
/**
1349
* A cross-compartment wrapper for our owning debugger's object, a CCW in the
1350
* code's compartment to the Debugger object in its own compartment. Holding
1351
* this lets the GC know about the effective cross-compartment reference from
1352
* the code to the debugger; see "Breakpoint GC Rules", above.
1353
*
1354
* This is almost redundant with the `debugger` field, except that we need
1355
* access to our owning `Debugger` regardless of the relative privilege levels
1356
* of debugger and debuggee, regardless of whether we're in the midst of a GC,
1357
* and so on - unwrapping is just too entangled.
1358
*/
1359
const HeapPtr<JSObject*> wrappedDebugger;
1360
1361
/* The site at which we're inserted. */
1362
BreakpointSite* const site;
1363
1364
private:
1365
/**
1366
* The breakpoint handler object, via a cross-compartment wrapper in the
1367
* code's compartment.
1368
*
1369
* Although eventually we would like this to be a `js::Handler` instance, for
1370
* now it is just cross-compartment wrapper for the JS object supplied to
1371
* `setBreakpoint`, hopefully with a callable `hit` property.
1372
*/
1373
const HeapPtr<JSObject*> handler;
1374
1375
/**
1376
* Link elements for each list this breakpoint can be in.
1377
*/
1378
mozilla::DoublyLinkedListElement<Breakpoint> debuggerLink;
1379
mozilla::DoublyLinkedListElement<Breakpoint> siteLink;
1380
1381
void trace(JSTracer* trc);
1382
1383
public:
1384
Breakpoint(Debugger* debugger, HandleObject wrappedDebugger,
1385
BreakpointSite* site, HandleObject handler);
1386
1387
enum MayDestroySite { False, True };
1388
1389
/**
1390
* Unlink this breakpoint from its Debugger's and and BreakpointSite's lists,
1391
* and free its memory.
1392
*
1393
* This is the low-level primitive shared by breakpoint removal and script
1394
* finalization code. It is only concerned with cleaning up this Breakpoint;
1395
* it does not check for now-empty BreakpointSites, unneeded DebugScripts, or
1396
* the like.
1397
*/
1398
void delete_(JSFreeOp* fop);
1399
1400
/**
1401
* Remove this breakpoint. Unlink it from its Debugger's and BreakpointSite's
1402
* lists, and if the BreakpointSite is now empty, clean that up and update JIT
1403
* code as necessary.
1404
*/
1405
void remove(JSFreeOp* fop);
1406
1407
Breakpoint* nextInDebugger();
1408
Breakpoint* nextInSite();
1409
JSObject* getHandler() const { return handler; }
1410
};
1411
1412
class JSBreakpointSite : public BreakpointSite {
1413
public:
1414
const HeapPtr<JSScript*> script;
1415
jsbytecode* const pc;
1416
1417
public:
1418
JSBreakpointSite(JSScript* script, jsbytecode* pc);
1419
1420
void trace(JSTracer* trc) override;
1421
void delete_(JSFreeOp* fop);
1422
void remove(JSFreeOp* fop) override;
1423
Realm* realm() const override;
1424
1425
private:
1426
gc::Cell* owningCell() override;
1427
};
1428
1429
class WasmBreakpointSite : public BreakpointSite {
1430
public:
1431
const HeapPtr<WasmInstanceObject*> instanceObject;
1432
uint32_t offset;
1433
1434
public:
1435
WasmBreakpointSite(WasmInstanceObject* instanceObject, uint32_t offset);
1436
1437
void trace(JSTracer* trc) override;
1438
void delete_(JSFreeOp* fop);
1439
void remove(JSFreeOp* fop) override;
1440
Realm* realm() const override;
1441
1442
private:
1443
gc::Cell* owningCell() override;
1444
};
1445
1446
Breakpoint* Debugger::firstBreakpoint() const {
1447
if (breakpoints.isEmpty()) {
1448
return nullptr;
1449
}
1450
return &(*breakpoints.begin());
1451
}
1452
1453
const js::GCPtrNativeObject& Debugger::toJSObject() const {
1454
MOZ_ASSERT(object);
1455
return object;
1456
}
1457
1458
js::GCPtrNativeObject& Debugger::toJSObjectRef() {
1459
MOZ_ASSERT(object);
1460
return object;
1461
}
1462
1463
bool Debugger::observesEnterFrame() const { return getHook(OnEnterFrame); }
1464
1465
bool Debugger::observesNewScript() const { return getHook(OnNewScript); }
1466
1467
bool Debugger::observesNewGlobalObject() const {
1468
return getHook(OnNewGlobalObject);
1469
}
1470
1471
bool Debugger::observesGlobal(GlobalObject* global) const {
1472
WeakHeapPtr<GlobalObject*> debuggee(global);
1473
return debuggees.has(debuggee);
1474
}
1475
1476
MOZ_MUST_USE bool ReportObjectRequired(JSContext* cx);
1477
1478
JSObject* IdVectorToArray(JSContext* cx, Handle<IdVector> ids);
1479
bool IsInterpretedNonSelfHostedFunction(JSFunction* fun);
1480
bool EnsureFunctionHasScript(JSContext* cx, HandleFunction fun);
1481
JSScript* GetOrCreateFunctionScript(JSContext* cx, HandleFunction fun);
1482
bool ValueToIdentifier(JSContext* cx, HandleValue v, MutableHandleId id);
1483
bool ValueToStableChars(JSContext* cx, const char* fnname, HandleValue value,
1484
JS::AutoStableStringChars& stableChars);
1485
bool ParseEvalOptions(JSContext* cx, HandleValue value, EvalOptions& options);
1486
1487
Result<Completion> DebuggerGenericEval(
1488
JSContext* cx, const mozilla::Range<const char16_t> chars,
1489
HandleObject bindings, const EvalOptions& options, Debugger* dbg,
1490
HandleObject envArg, FrameIter* iter);
1491
1492
bool ParseResumptionValue(JSContext* cx, HandleValue rval,
1493
ResumeMode& resumeMode, MutableHandleValue vp);
1494
1495
#define JS_DEBUG_PSG(Name, Getter) \
1496
JS_PSG(Name, CallData::ToNative<&CallData::Getter>, 0)
1497
1498
#define JS_DEBUG_PSGS(Name, Getter, Setter) \
1499
JS_PSGS(Name, CallData::ToNative<&CallData::Getter>, \
1500
CallData::ToNative<&CallData::Setter>, 0)
1501
1502
#define JS_DEBUG_FN(Name, Method, NumArgs) \
1503
JS_FN(Name, CallData::ToNative<&CallData::Method>, NumArgs, 0)
1504
1505
} /* namespace js */
1506
1507
namespace JS {
1508
1509
template <>
1510
struct DeletePolicy<js::Debugger>
1511
: public js::GCManagedDeletePolicy<js::Debugger> {};
1512
1513
} /* namespace JS */
1514
1515
#endif /* debugger_Debugger_h */