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 : #ifndef vm_Debugger_h
8 : #define vm_Debugger_h
9 :
10 : #include "mozilla/DoublyLinkedList.h"
11 : #include "mozilla/GuardObjects.h"
12 : #include "mozilla/LinkedList.h"
13 : #include "mozilla/Range.h"
14 : #include "mozilla/TimeStamp.h"
15 : #include "mozilla/Vector.h"
16 :
17 : #include "builtin/Promise.h"
18 : #include "ds/TraceableFifo.h"
19 : #include "gc/Barrier.h"
20 : #include "gc/WeakMap.h"
21 : #include "js/Debug.h"
22 : #include "js/GCVariant.h"
23 : #include "js/HashTable.h"
24 : #include "js/Utility.h"
25 : #include "js/Wrapper.h"
26 : #include "vm/GlobalObject.h"
27 : #include "vm/JSContext.h"
28 : #include "vm/Realm.h"
29 : #include "vm/SavedStacks.h"
30 : #include "vm/Stack.h"
31 : #include "wasm/WasmJS.h"
32 :
33 : namespace js {
34 :
35 : /**
36 : * Tells how the JS engine should resume debuggee execution after firing a
37 : * debugger hook. Most debugger hooks get to choose how the debuggee proceeds;
38 : * see js/src/doc/Debugger/Conventions.md under "Resumption Values".
39 : *
40 : * Debugger::processHandlerResult() translates between JavaScript values and
41 : * this enum.
42 : *
43 : * The values `ResumeMode::Throw` and `ResumeMode::Return` are always
44 : * associated with a value (the exception value or return value). Sometimes
45 : * this is represented as an explicit `JS::Value` variable or parameter,
46 : * declared alongside the `ResumeMode`. In other cases, especially when
47 : * ResumeMode is used as a return type (as in Debugger::onEnterFrame), the
48 : * value is stashed in `cx`'s pending exception slot or the topmost frame's
49 : * return value slot.
50 : */
51 : enum class ResumeMode {
52 : /**
53 : * The debuggee should continue unchanged.
54 : *
55 : * This corresponds to a resumption value of `undefined`.
56 : */
57 : Continue,
58 :
59 : /**
60 : * Throw an exception in the debuggee.
61 : *
62 : * This corresponds to a resumption value of `{throw: <value>}`.
63 : */
64 : Throw,
65 :
66 : /**
67 : * Terminate the debuggee, as if it had been cancelled via the "slow
68 : * script" ribbon.
69 : *
70 : * This corresponds to a resumption value of `null`.
71 : */
72 : Terminate,
73 :
74 : /**
75 : * Force the debuggee to return from the current frame.
76 : *
77 : * This corresponds to a resumption value of `{return: <value>}`.
78 : */
79 : Return,
80 : };
81 :
82 : class Breakpoint;
83 : class DebuggerMemory;
84 : class ScriptedOnStepHandler;
85 : class ScriptedOnPopHandler;
86 : class WasmInstanceObject;
87 :
88 : typedef HashSet<ReadBarrieredGlobalObject,
89 : MovableCellHasher<ReadBarrieredGlobalObject>,
90 : ZoneAllocPolicy> WeakGlobalObjectSet;
91 :
92 : /*
93 : * A weakmap from GC thing keys to JSObject values that supports the keys being
94 : * in different compartments to the values. All values must be in the same
95 : * compartment.
96 : *
97 : * The purpose of this is to allow the garbage collector to easily find edges
98 : * from debuggee object compartments to debugger compartments when calculating
99 : * the compartment groups. Note that these edges are the inverse of the edges
100 : * stored in the cross compartment map.
101 : *
102 : * The current implementation results in all debuggee object compartments being
103 : * swept in the same group as the debugger. This is a conservative approach,
104 : * and compartments may be unnecessarily grouped, however it results in a
105 : * simpler and faster implementation.
106 : *
107 : * If InvisibleKeysOk is true, then the map can have keys in invisible-to-
108 : * debugger compartments. If it is false, we assert that such entries are never
109 : * created.
110 : *
111 : * Also note that keys in these weakmaps can be in any compartment, debuggee or
112 : * not, because they cannot be deleted when a compartment is no longer a
113 : * debuggee: the values need to maintain object identity across add/remove/add
114 : * transitions.
115 : */
116 : template <class UnbarrieredKey, bool InvisibleKeysOk=false>
117 0 : class DebuggerWeakMap : private WeakMap<HeapPtr<UnbarrieredKey>, HeapPtr<JSObject*>,
118 : MovableCellHasher<HeapPtr<UnbarrieredKey>>>
119 : {
120 : private:
121 : typedef HeapPtr<UnbarrieredKey> Key;
122 : typedef HeapPtr<JSObject*> Value;
123 :
124 : typedef HashMap<JS::Zone*,
125 : uintptr_t,
126 : DefaultHasher<JS::Zone*>,
127 : ZoneAllocPolicy> CountMap;
128 :
129 : CountMap zoneCounts;
130 : JS::Compartment* compartment;
131 :
132 : public:
133 : typedef WeakMap<Key, Value, MovableCellHasher<Key>> Base;
134 :
135 0 : explicit DebuggerWeakMap(JSContext* cx)
136 : : Base(cx),
137 : zoneCounts(cx->zone()),
138 0 : compartment(cx->compartment())
139 0 : { }
140 :
141 : public:
142 : /* Expose those parts of HashMap public interface that are used by Debugger methods. */
143 :
144 : typedef typename Base::Entry Entry;
145 : typedef typename Base::Ptr Ptr;
146 : typedef typename Base::AddPtr AddPtr;
147 : typedef typename Base::Range Range;
148 : typedef typename Base::Enum Enum;
149 : typedef typename Base::Lookup Lookup;
150 :
151 : /* Expose WeakMap public interface */
152 :
153 : using Base::lookupForAdd;
154 : using Base::all;
155 : using Base::trace;
156 :
157 0 : MOZ_MUST_USE bool init(uint32_t len = 16) {
158 0 : return Base::init(len) && zoneCounts.init();
159 : }
160 :
161 : template<typename KeyInput, typename ValueInput>
162 0 : bool relookupOrAdd(AddPtr& p, const KeyInput& k, const ValueInput& v) {
163 0 : MOZ_ASSERT(v->compartment() == this->compartment);
164 0 : MOZ_ASSERT(!k->realm()->creationOptions().mergeable());
165 0 : MOZ_ASSERT_IF(!InvisibleKeysOk,
166 : !k->realm()->creationOptions().invisibleToDebugger());
167 0 : MOZ_ASSERT(!Base::has(k));
168 0 : if (!incZoneCount(k->zone()))
169 : return false;
170 0 : bool ok = Base::relookupOrAdd(p, k, v);
171 0 : if (!ok)
172 0 : decZoneCount(k->zone());
173 : return ok;
174 : }
175 :
176 0 : void remove(const Lookup& l) {
177 0 : MOZ_ASSERT(Base::has(l));
178 0 : Base::remove(l);
179 0 : decZoneCount(l->zone());
180 0 : }
181 :
182 : public:
183 : template <void (traceValueEdges)(JSTracer*, JSObject*)>
184 0 : void traceCrossCompartmentEdges(JSTracer* tracer) {
185 0 : for (Enum e(*static_cast<Base*>(this)); !e.empty(); e.popFront()) {
186 0 : traceValueEdges(tracer, e.front().value());
187 0 : Key key = e.front().key();
188 0 : TraceEdge(tracer, &key, "Debugger WeakMap key");
189 0 : if (key != e.front().key())
190 : e.rekeyFront(key);
191 0 : key.unsafeSet(nullptr);
192 : }
193 0 : }
194 :
195 0 : bool hasKeyInZone(JS::Zone* zone) {
196 0 : CountMap::Ptr p = zoneCounts.lookup(zone);
197 0 : MOZ_ASSERT_IF(p.found(), p->value() > 0);
198 0 : return p.found();
199 : }
200 :
201 : private:
202 : /* Override sweep method to also update our edge cache. */
203 0 : void sweep() override {
204 0 : MOZ_ASSERT(CurrentThreadIsPerformingGC());
205 0 : for (Enum e(*static_cast<Base*>(this)); !e.empty(); e.popFront()) {
206 0 : if (gc::IsAboutToBeFinalized(&e.front().mutableKey())) {
207 0 : decZoneCount(e.front().key()->zoneFromAnyThread());
208 : e.removeFront();
209 : }
210 : }
211 0 : Base::assertEntriesNotAboutToBeFinalized();
212 0 : }
213 :
214 0 : MOZ_MUST_USE bool incZoneCount(JS::Zone* zone) {
215 0 : CountMap::Ptr p = zoneCounts.lookupWithDefault(zone, 0);
216 0 : if (!p)
217 : return false;
218 0 : ++p->value();
219 0 : return true;
220 : }
221 :
222 0 : void decZoneCount(JS::Zone* zone) {
223 0 : CountMap::Ptr p = zoneCounts.lookup(zone);
224 0 : MOZ_ASSERT(p);
225 0 : MOZ_ASSERT(p->value() > 0);
226 0 : --p->value();
227 0 : if (p->value() == 0)
228 0 : zoneCounts.remove(zone);
229 0 : }
230 : };
231 :
232 : class LeaveDebuggeeNoExecute;
233 :
234 : // Suppresses all debuggee NX checks, i.e., allow all execution. Used to allow
235 : // certain whitelisted operations to execute code.
236 : //
237 : // WARNING
238 : // WARNING Do not use this unless you know what you are doing!
239 : // WARNING
240 : class AutoSuppressDebuggeeNoExecuteChecks
241 : {
242 : EnterDebuggeeNoExecute** stack_;
243 : EnterDebuggeeNoExecute* prev_;
244 :
245 : public:
246 0 : explicit AutoSuppressDebuggeeNoExecuteChecks(JSContext* cx) {
247 0 : stack_ = &cx->noExecuteDebuggerTop.ref();
248 0 : prev_ = *stack_;
249 644 : *stack_ = nullptr;
250 : }
251 :
252 0 : ~AutoSuppressDebuggeeNoExecuteChecks() {
253 0 : MOZ_ASSERT(!*stack_);
254 0 : *stack_ = prev_;
255 644 : }
256 : };
257 :
258 : class MOZ_RAII EvalOptions {
259 : JS::UniqueChars filename_;
260 : unsigned lineno_ = 1;
261 :
262 : public:
263 0 : EvalOptions() = default;
264 0 : ~EvalOptions() = default;
265 0 : const char* filename() const { return filename_.get(); }
266 : unsigned lineno() const { return lineno_; }
267 : MOZ_MUST_USE bool setFilename(JSContext* cx, const char* filename);
268 0 : void setLineno(unsigned lineno) { lineno_ = lineno; }
269 : };
270 :
271 : /*
272 : * Env is the type of what ES5 calls "lexical environments" (runtime activations
273 : * of lexical scopes). This is currently just JSObject, and is implemented by
274 : * CallObject, LexicalEnvironmentObject, and WithEnvironmentObject, among
275 : * others--but environments and objects are really two different concepts.
276 : */
277 : typedef JSObject Env;
278 :
279 : // Either a real JSScript or synthesized.
280 : //
281 : // If synthesized, the referent is one of the following:
282 : //
283 : // 1. A WasmInstanceObject, denoting a synthesized toplevel wasm module
284 : // script.
285 : // 2. A wasm JSFunction, denoting a synthesized wasm function script.
286 : // NYI!
287 : typedef mozilla::Variant<JSScript*, WasmInstanceObject*> DebuggerScriptReferent;
288 :
289 : // Either a ScriptSourceObject, for ordinary JS, or a WasmInstanceObject,
290 : // denoting the synthesized source of a wasm module.
291 : typedef mozilla::Variant<ScriptSourceObject*, WasmInstanceObject*> DebuggerSourceReferent;
292 :
293 : class Debugger : private mozilla::LinkedListElement<Debugger>
294 : {
295 : friend class Breakpoint;
296 : friend class DebuggerMemory;
297 : friend struct JSRuntime::GlobalObjectWatchersLinkAccess<Debugger>;
298 : friend class SavedStacks;
299 : friend class ScriptedOnStepHandler;
300 : friend class ScriptedOnPopHandler;
301 : friend class mozilla::LinkedListElement<Debugger>;
302 : friend class mozilla::LinkedList<Debugger>;
303 : friend bool (::JS_DefineDebuggerObject)(JSContext* cx, JS::HandleObject obj);
304 : friend bool (::JS::dbg::IsDebugger)(JSObject&);
305 : friend bool (::JS::dbg::GetDebuggeeGlobals)(JSContext*, JSObject&, AutoObjectVector&);
306 : friend bool JS::dbg::FireOnGarbageCollectionHookRequired(JSContext* cx);
307 : friend bool JS::dbg::FireOnGarbageCollectionHook(JSContext* cx,
308 : JS::dbg::GarbageCollectionEvent::Ptr&& data);
309 :
310 : public:
311 : enum Hook {
312 : OnDebuggerStatement,
313 : OnExceptionUnwind,
314 : OnNewScript,
315 : OnEnterFrame,
316 : OnNewGlobalObject,
317 : OnNewPromise,
318 : OnPromiseSettled,
319 : OnGarbageCollection,
320 : HookCount
321 : };
322 : enum {
323 : JSSLOT_DEBUG_PROTO_START,
324 : JSSLOT_DEBUG_FRAME_PROTO = JSSLOT_DEBUG_PROTO_START,
325 : JSSLOT_DEBUG_ENV_PROTO,
326 : JSSLOT_DEBUG_OBJECT_PROTO,
327 : JSSLOT_DEBUG_SCRIPT_PROTO,
328 : JSSLOT_DEBUG_SOURCE_PROTO,
329 : JSSLOT_DEBUG_MEMORY_PROTO,
330 : JSSLOT_DEBUG_PROTO_STOP,
331 : JSSLOT_DEBUG_HOOK_START = JSSLOT_DEBUG_PROTO_STOP,
332 : JSSLOT_DEBUG_HOOK_STOP = JSSLOT_DEBUG_HOOK_START + HookCount,
333 : JSSLOT_DEBUG_MEMORY_INSTANCE = JSSLOT_DEBUG_HOOK_STOP,
334 : JSSLOT_DEBUG_COUNT
335 : };
336 :
337 : class ExecutionObservableSet
338 : {
339 : public:
340 : typedef HashSet<Zone*>::Range ZoneRange;
341 :
342 0 : virtual Zone* singleZone() const { return nullptr; }
343 0 : virtual JSScript* singleScriptForZoneInvalidation() const { return nullptr; }
344 0 : virtual const HashSet<Zone*>* zones() const { return nullptr; }
345 :
346 : virtual bool shouldRecompileOrInvalidate(JSScript* script) const = 0;
347 : virtual bool shouldMarkAsDebuggee(FrameIter& iter) const = 0;
348 : };
349 :
350 : // This enum is converted to and compare with bool values; NotObserving
351 : // must be 0 and Observing must be 1.
352 : enum IsObserving {
353 : NotObserving = 0,
354 : Observing = 1
355 : };
356 :
357 : // Return true if the given realm is a debuggee of this debugger,
358 : // false otherwise.
359 : bool isDebuggeeUnbarriered(const Realm* realm) const;
360 :
361 : // Return true if this Debugger observed a debuggee that participated in the
362 : // GC identified by the given GC number. Return false otherwise.
363 : // May return false negatives if we have hit OOM.
364 : bool observedGC(uint64_t majorGCNumber) const {
365 0 : return observedGCs.has(majorGCNumber);
366 : }
367 :
368 : // Notify this Debugger that one or more of its debuggees is participating
369 : // in the GC identified by the given GC number.
370 : bool debuggeeIsBeingCollected(uint64_t majorGCNumber) {
371 0 : return observedGCs.put(majorGCNumber);
372 : }
373 :
374 : bool isEnabled() const {
375 : return enabled;
376 : }
377 :
378 : static SavedFrame* getObjectAllocationSite(JSObject& obj);
379 :
380 0 : struct AllocationsLogEntry
381 : {
382 0 : AllocationsLogEntry(HandleObject frame, mozilla::TimeStamp when, const char* className,
383 : HandleAtom ctorName, size_t size, bool inNursery)
384 0 : : frame(frame),
385 : when(when),
386 : className(className),
387 : ctorName(ctorName),
388 : size(size),
389 0 : inNursery(inNursery)
390 : {
391 0 : MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>());
392 0 : };
393 :
394 : HeapPtr<JSObject*> frame;
395 : mozilla::TimeStamp when;
396 : const char* className;
397 : HeapPtr<JSAtom*> ctorName;
398 : size_t size;
399 : bool inNursery;
400 :
401 0 : void trace(JSTracer* trc) {
402 0 : TraceNullableEdge(trc, &frame, "Debugger::AllocationsLogEntry::frame");
403 0 : TraceNullableEdge(trc, &ctorName, "Debugger::AllocationsLogEntry::ctorName");
404 0 : }
405 : };
406 :
407 : // Barrier methods so we can have ReadBarriered<Debugger*>.
408 : static void readBarrier(Debugger* dbg) {
409 0 : InternalBarrierMethods<JSObject*>::readBarrier(dbg->object);
410 : }
411 : static void writeBarrierPost(Debugger** vp, Debugger* prev, Debugger* next) {}
412 : #ifdef DEBUG
413 : static bool thingIsNotGray(Debugger* dbg) { return true; }
414 : #endif
415 :
416 : private:
417 : GCPtrNativeObject object; /* The Debugger object. Strong reference. */
418 : WeakGlobalObjectSet debuggees; /* Debuggee globals. Cross-compartment weak references. */
419 : JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */
420 : js::GCPtrObject uncaughtExceptionHook; /* Strong reference. */
421 : bool enabled;
422 : bool allowUnobservedAsmJS;
423 : bool allowWasmBinarySource;
424 :
425 : // Whether to enable code coverage on the Debuggee.
426 : bool collectCoverageInfo;
427 :
428 : template <typename T>
429 : struct DebuggerLinkAccess {
430 : static mozilla::DoublyLinkedListElement<T>& Get(T* aThis) {
431 : return aThis->debuggerLink;
432 : }
433 : };
434 :
435 : // List of all js::Breakpoints in this debugger.
436 : using BreakpointList =
437 : mozilla::DoublyLinkedList<js::Breakpoint,
438 : DebuggerLinkAccess<js::Breakpoint>>;
439 : BreakpointList breakpoints;
440 :
441 : // The set of GC numbers for which one or more of this Debugger's observed
442 : // debuggees participated in.
443 : using GCNumberSet = HashSet<uint64_t, DefaultHasher<uint64_t>, ZoneAllocPolicy>;
444 : GCNumberSet observedGCs;
445 :
446 : using AllocationsLog = js::TraceableFifo<AllocationsLogEntry>;
447 :
448 : AllocationsLog allocationsLog;
449 : bool trackingAllocationSites;
450 : double allocationSamplingProbability;
451 : size_t maxAllocationsLogLength;
452 : bool allocationsLogOverflowed;
453 :
454 : static const size_t DEFAULT_MAX_LOG_LENGTH = 5000;
455 :
456 : MOZ_MUST_USE bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
457 : mozilla::TimeStamp when);
458 :
459 : /*
460 : * Recompute the set of debuggee zones based on the set of debuggee globals.
461 : */
462 : void recomputeDebuggeeZoneSet();
463 :
464 : /*
465 : * Return true if there is an existing object metadata callback for the
466 : * given global's compartment that will prevent our instrumentation of
467 : * allocations.
468 : */
469 : static bool cannotTrackAllocations(const GlobalObject& global);
470 :
471 : /*
472 : * Return true if the given global is being observed by at least one
473 : * Debugger that is tracking allocations.
474 : */
475 : static bool isObservedByDebuggerTrackingAllocations(const GlobalObject& global);
476 :
477 : /*
478 : * Add allocations tracking for objects allocated within the given
479 : * debuggee's compartment. The given debuggee global must be observed by at
480 : * least one Debugger that is enabled and tracking allocations.
481 : */
482 : static MOZ_MUST_USE bool addAllocationsTracking(JSContext* cx, Handle<GlobalObject*> debuggee);
483 :
484 : /*
485 : * Remove allocations tracking for objects allocated within the given
486 : * global's compartment. This is a no-op if there are still Debuggers
487 : * observing this global and who are tracking allocations.
488 : */
489 : static void removeAllocationsTracking(GlobalObject& global);
490 :
491 : /*
492 : * Add or remove allocations tracking for all debuggees.
493 : */
494 : MOZ_MUST_USE bool addAllocationsTrackingForAllDebuggees(JSContext* cx);
495 : void removeAllocationsTrackingForAllDebuggees();
496 :
497 : /*
498 : * If this Debugger is enabled, and has a onNewGlobalObject handler, then
499 : * this link is inserted into the list headed by
500 : * JSRuntime::onNewGlobalObjectWatchers.
501 : */
502 : mozilla::DoublyLinkedListElement<Debugger> onNewGlobalObjectWatchersLink;
503 :
504 : /*
505 : * Map from stack frames that are currently on the stack to Debugger.Frame
506 : * instances.
507 : *
508 : * The keys are always live stack frames. We drop them from this map as
509 : * soon as they leave the stack (see slowPathOnLeaveFrame) and in
510 : * removeDebuggee.
511 : *
512 : * We don't trace the keys of this map (the frames are on the stack and
513 : * thus necessarily live), but we do trace the values. It's like a WeakMap
514 : * that way, but since stack frames are not gc-things, the implementation
515 : * has to be different.
516 : */
517 : typedef HashMap<AbstractFramePtr,
518 : HeapPtr<DebuggerFrame*>,
519 : DefaultHasher<AbstractFramePtr>,
520 : ZoneAllocPolicy> FrameMap;
521 : FrameMap frames;
522 :
523 : /* An ephemeral map from JSScript* to Debugger.Script instances. */
524 : typedef DebuggerWeakMap<JSScript*> ScriptWeakMap;
525 : ScriptWeakMap scripts;
526 :
527 : /* The map from debuggee source script objects to their Debugger.Source instances. */
528 : typedef DebuggerWeakMap<JSObject*, true> SourceWeakMap;
529 : SourceWeakMap sources;
530 :
531 : /* The map from debuggee objects to their Debugger.Object instances. */
532 : typedef DebuggerWeakMap<JSObject*> ObjectWeakMap;
533 : ObjectWeakMap objects;
534 :
535 : /* The map from debuggee Envs to Debugger.Environment instances. */
536 : ObjectWeakMap environments;
537 :
538 : /* The map from WasmInstanceObjects to synthesized Debugger.Script instances. */
539 : typedef DebuggerWeakMap<WasmInstanceObject*> WasmInstanceWeakMap;
540 : WasmInstanceWeakMap wasmInstanceScripts;
541 :
542 : /* The map from WasmInstanceObjects to synthesized Debugger.Source instances. */
543 : WasmInstanceWeakMap wasmInstanceSources;
544 :
545 : /*
546 : * Keep track of tracelogger last drained identifiers to know if there are
547 : * lost events.
548 : */
549 : #ifdef NIGHTLY_BUILD
550 : uint32_t traceLoggerLastDrainedSize;
551 : uint32_t traceLoggerLastDrainedIteration;
552 : #endif
553 : uint32_t traceLoggerScriptedCallsLastDrainedSize;
554 : uint32_t traceLoggerScriptedCallsLastDrainedIteration;
555 :
556 : class ScriptQuery;
557 : class ObjectQuery;
558 :
559 : MOZ_MUST_USE bool addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> obj);
560 : void removeDebuggeeGlobal(FreeOp* fop, GlobalObject* global,
561 : WeakGlobalObjectSet::Enum* debugEnum);
562 :
563 : /*
564 : * Report and clear the pending exception on ar.context, if any, and return
565 : * ResumeMode::Terminate.
566 : */
567 : ResumeMode reportUncaughtException(mozilla::Maybe<AutoRealm>& ar);
568 :
569 : /*
570 : * Cope with an error or exception in a debugger hook.
571 : *
572 : * If callHook is true, then call the uncaughtExceptionHook, if any. If, in
573 : * addition, vp is given, then parse the value returned by
574 : * uncaughtExceptionHook as a resumption value.
575 : *
576 : * If there is no uncaughtExceptionHook, or if it fails, report and clear
577 : * the pending exception on ar.context and return ResumeMode::Terminate.
578 : *
579 : * This always calls ar.leave(); ar is a parameter because this method must
580 : * do some things in the debugger realm and some things in the debuggee
581 : * realm.
582 : */
583 : ResumeMode handleUncaughtException(mozilla::Maybe<AutoRealm>& ar);
584 : ResumeMode handleUncaughtException(mozilla::Maybe<AutoRealm>& ar,
585 : MutableHandleValue vp,
586 : const mozilla::Maybe<HandleValue>& thisVForCheck = mozilla::Nothing(),
587 : AbstractFramePtr frame = NullFramePtr());
588 :
589 : ResumeMode handleUncaughtExceptionHelper(mozilla::Maybe<AutoRealm>& ar,
590 : MutableHandleValue* vp,
591 : const mozilla::Maybe<HandleValue>& thisVForCheck,
592 : AbstractFramePtr frame);
593 :
594 : /*
595 : * Handle the result of a hook that is expected to return a resumption
596 : * value <https://wiki.mozilla.org/Debugger#Resumption_Values>. This is
597 : * called when we return from a debugging hook to debuggee code. The
598 : * interpreter wants a (ResumeMode, Value) pair telling it how to proceed.
599 : *
600 : * Precondition: ar is entered. We are in the debugger compartment.
601 : *
602 : * Postcondition: This called ar.leave(). See handleUncaughtException.
603 : *
604 : * If `success` is false, the hook failed. If an exception is pending in
605 : * ar.context(), return handleUncaughtException(ar, vp, callhook).
606 : * Otherwise just return ResumeMode::Terminate.
607 : *
608 : * If `success` is true, there must be no exception pending in ar.context().
609 : * `rv` may be:
610 : *
611 : * undefined - Return `ResumeMode::Continue` to continue execution
612 : * normally.
613 : *
614 : * {return: value} or {throw: value} - Call unwrapDebuggeeValue to
615 : * unwrap `value`. Store the result in `*vp` and return
616 : * `ResumeMode::Return` or `ResumeMode::Throw`. The interpreter
617 : * will force the current frame to return or throw an exception.
618 : *
619 : * null - Return `ResumeMode::Terminate` to terminate the debuggee with
620 : * an uncatchable error.
621 : *
622 : * anything else - Make a new TypeError the pending exception and
623 : * return handleUncaughtException(ar, vp, callHook).
624 : */
625 : ResumeMode processHandlerResult(mozilla::Maybe<AutoRealm>& ar, bool success, const Value& rv,
626 : AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp);
627 :
628 : ResumeMode processParsedHandlerResult(mozilla::Maybe<AutoRealm>& ar,
629 : AbstractFramePtr frame, jsbytecode* pc,
630 : bool success, ResumeMode resumeMode,
631 : MutableHandleValue vp);
632 :
633 : ResumeMode processParsedHandlerResultHelper(mozilla::Maybe<AutoRealm>& ar,
634 : AbstractFramePtr frame,
635 : const mozilla::Maybe<HandleValue>& maybeThisv,
636 : bool success, ResumeMode resumeMode,
637 : MutableHandleValue vp);
638 :
639 : bool processResumptionValue(mozilla::Maybe<AutoRealm>& ar, AbstractFramePtr frame,
640 : const mozilla::Maybe<HandleValue>& maybeThis, HandleValue rval,
641 : ResumeMode& resumeMode, MutableHandleValue vp);
642 :
643 : GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
644 :
645 : static void traceObject(JSTracer* trc, JSObject* obj);
646 :
647 : void trace(JSTracer* trc);
648 : friend struct js::GCManagedDeletePolicy<Debugger>;
649 :
650 : void traceForMovingGC(JSTracer* trc);
651 : void traceCrossCompartmentEdges(JSTracer* tracer);
652 :
653 : static const ClassOps classOps_;
654 :
655 : public:
656 : static const Class class_;
657 :
658 : private:
659 : static MOZ_MUST_USE bool getHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
660 : static MOZ_MUST_USE bool setHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
661 :
662 : static bool getEnabled(JSContext* cx, unsigned argc, Value* vp);
663 : static bool setEnabled(JSContext* cx, unsigned argc, Value* vp);
664 : static bool getOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
665 : static bool setOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
666 : static bool getOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
667 : static bool setOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
668 : static bool getOnNewScript(JSContext* cx, unsigned argc, Value* vp);
669 : static bool setOnNewScript(JSContext* cx, unsigned argc, Value* vp);
670 : static bool getOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
671 : static bool setOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
672 : static bool getOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
673 : static bool setOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
674 : static bool getOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
675 : static bool setOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
676 : static bool getOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
677 : static bool setOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
678 : static bool getUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
679 : static bool setUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
680 : static bool getAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
681 : static bool setAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
682 : static bool getAllowWasmBinarySource(JSContext* cx, unsigned argc, Value* vp);
683 : static bool setAllowWasmBinarySource(JSContext* cx, unsigned argc, Value* vp);
684 : static bool getCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
685 : static bool setCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
686 : static bool getMemory(JSContext* cx, unsigned argc, Value* vp);
687 : static bool addDebuggee(JSContext* cx, unsigned argc, Value* vp);
688 : static bool addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp);
689 : static bool removeDebuggee(JSContext* cx, unsigned argc, Value* vp);
690 : static bool removeAllDebuggees(JSContext* cx, unsigned argc, Value* vp);
691 : static bool hasDebuggee(JSContext* cx, unsigned argc, Value* vp);
692 : static bool getDebuggees(JSContext* cx, unsigned argc, Value* vp);
693 : static bool getNewestFrame(JSContext* cx, unsigned argc, Value* vp);
694 : static bool clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp);
695 : static bool findScripts(JSContext* cx, unsigned argc, Value* vp);
696 : static bool findObjects(JSContext* cx, unsigned argc, Value* vp);
697 : static bool findAllGlobals(JSContext* cx, unsigned argc, Value* vp);
698 : static bool makeGlobalObjectReference(JSContext* cx, unsigned argc, Value* vp);
699 : static bool setupTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp);
700 : static bool drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp);
701 : static bool startTraceLogger(JSContext* cx, unsigned argc, Value* vp);
702 : static bool endTraceLogger(JSContext* cx, unsigned argc, Value* vp);
703 : static bool isCompilableUnit(JSContext* cx, unsigned argc, Value* vp);
704 : #ifdef NIGHTLY_BUILD
705 : static bool setupTraceLogger(JSContext* cx, unsigned argc, Value* vp);
706 : static bool drainTraceLogger(JSContext* cx, unsigned argc, Value* vp);
707 : #endif
708 : static bool adoptDebuggeeValue(JSContext* cx, unsigned argc, Value* vp);
709 : static bool construct(JSContext* cx, unsigned argc, Value* vp);
710 : static const JSPropertySpec properties[];
711 : static const JSFunctionSpec methods[];
712 : static const JSFunctionSpec static_methods[];
713 :
714 : static void removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame);
715 : static bool updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObservableSet& obs,
716 : IsObserving observing);
717 : static bool updateExecutionObservabilityOfScripts(JSContext* cx, const ExecutionObservableSet& obs,
718 : IsObserving observing);
719 : static bool updateExecutionObservability(JSContext* cx, ExecutionObservableSet& obs,
720 : IsObserving observing);
721 :
722 : template <typename FrameFn /* void (DebuggerFrame*) */>
723 : static void forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn);
724 :
725 : /*
726 : * Return a vector containing all Debugger.Frame instances referring to
727 : * |frame|. |global| is |frame|'s global object; if nullptr or omitted, we
728 : * compute it ourselves from |frame|.
729 : */
730 : using DebuggerFrameVector = GCVector<DebuggerFrame*>;
731 : static MOZ_MUST_USE bool getDebuggerFrames(AbstractFramePtr frame,
732 : MutableHandle<DebuggerFrameVector> frames);
733 :
734 : public:
735 : static MOZ_MUST_USE bool ensureExecutionObservabilityOfOsrFrame(JSContext* cx,
736 : InterpreterFrame* frame);
737 :
738 : // Public for DebuggerScript_setBreakpoint.
739 : static MOZ_MUST_USE bool ensureExecutionObservabilityOfScript(JSContext* cx, JSScript* script);
740 :
741 : // Whether the Debugger instance needs to observe all non-AOT JS
742 : // execution of its debugees.
743 : IsObserving observesAllExecution() const;
744 :
745 : // Whether the Debugger instance needs to observe AOT-compiled asm.js
746 : // execution of its debuggees.
747 : IsObserving observesAsmJS() const;
748 :
749 : // Whether the Debugger instance needs to observe coverage of any JavaScript
750 : // execution.
751 : IsObserving observesCoverage() const;
752 :
753 : IsObserving observesBinarySource() const;
754 :
755 : private:
756 : static MOZ_MUST_USE bool ensureExecutionObservabilityOfFrame(JSContext* cx,
757 : AbstractFramePtr frame);
758 : static MOZ_MUST_USE bool ensureExecutionObservabilityOfRealm(JSContext* cx,
759 : JS::Realm* realm);
760 :
761 : static bool hookObservesAllExecution(Hook which);
762 :
763 : MOZ_MUST_USE bool updateObservesAllExecutionOnDebuggees(JSContext* cx, IsObserving observing);
764 : MOZ_MUST_USE bool updateObservesCoverageOnDebuggees(JSContext* cx, IsObserving observing);
765 : void updateObservesAsmJSOnDebuggees(IsObserving observing);
766 : void updateObservesBinarySourceDebuggees(IsObserving observing);
767 :
768 : JSObject* getHook(Hook hook) const;
769 : bool hasAnyLiveHooks(JSRuntime* rt) const;
770 :
771 : static MOZ_MUST_USE bool slowPathCheckNoExecute(JSContext* cx, HandleScript script);
772 : static ResumeMode slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
773 : static MOZ_MUST_USE bool slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame,
774 : jsbytecode* pc, bool ok);
775 : static ResumeMode slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
776 : static ResumeMode slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
777 : static void slowPathOnNewScript(JSContext* cx, HandleScript script);
778 : static void slowPathOnNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
779 : static void slowPathOnNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
780 : static MOZ_MUST_USE bool slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj,
781 : HandleSavedFrame frame,
782 : mozilla::TimeStamp when,
783 : GlobalObject::DebuggerVector& dbgs);
784 : static void slowPathPromiseHook(JSContext* cx, Hook hook, Handle<PromiseObject*> promise);
785 :
786 : template <typename HookIsEnabledFun /* bool (Debugger*) */,
787 : typename FireHookFun /* ResumeMode (Debugger*) */>
788 : static ResumeMode dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
789 : FireHookFun fireHook);
790 :
791 : ResumeMode fireDebuggerStatement(JSContext* cx, MutableHandleValue vp);
792 : ResumeMode fireExceptionUnwind(JSContext* cx, MutableHandleValue vp);
793 : ResumeMode fireEnterFrame(JSContext* cx, MutableHandleValue vp);
794 : ResumeMode fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global, MutableHandleValue vp);
795 : ResumeMode firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp);
796 :
797 : NativeObject* newVariantWrapper(JSContext* cx, Handle<DebuggerScriptReferent> referent) {
798 0 : return newDebuggerScript(cx, referent);
799 : }
800 : NativeObject* newVariantWrapper(JSContext* cx, Handle<DebuggerSourceReferent> referent) {
801 0 : return newDebuggerSource(cx, referent);
802 : }
803 :
804 : /*
805 : * Helper function to help wrap Debugger objects whose referents may be
806 : * variants. Currently Debugger.Script and Debugger.Source referents may
807 : * be variants.
808 : *
809 : * Prefer using wrapScript, wrapWasmScript, wrapSource, and wrapWasmSource
810 : * whenever possible.
811 : */
812 : template <typename ReferentVariant, typename Referent, typename Map>
813 : JSObject* wrapVariantReferent(JSContext* cx, Map& map, Handle<CrossCompartmentKey> key,
814 : Handle<ReferentVariant> referent);
815 : JSObject* wrapVariantReferent(JSContext* cx, Handle<DebuggerScriptReferent> referent);
816 : JSObject* wrapVariantReferent(JSContext* cx, Handle<DebuggerSourceReferent> referent);
817 :
818 : /*
819 : * Allocate and initialize a Debugger.Script instance whose referent is
820 : * |referent|.
821 : */
822 : NativeObject* newDebuggerScript(JSContext* cx, Handle<DebuggerScriptReferent> referent);
823 :
824 : /*
825 : * Allocate and initialize a Debugger.Source instance whose referent is
826 : * |referent|.
827 : */
828 : NativeObject* newDebuggerSource(JSContext* cx, Handle<DebuggerSourceReferent> referent);
829 :
830 : /*
831 : * Receive a "new script" event from the engine. A new script was compiled
832 : * or deserialized.
833 : */
834 : void fireNewScript(JSContext* cx, Handle<DebuggerScriptReferent> scriptReferent);
835 :
836 : /*
837 : * Receive a "garbage collection" event from the engine. A GC cycle with the
838 : * given data was recently completed.
839 : */
840 : void fireOnGarbageCollectionHook(JSContext* cx,
841 : const JS::dbg::GarbageCollectionEvent::Ptr& gcData);
842 :
843 : inline Breakpoint* firstBreakpoint() const;
844 :
845 : static MOZ_MUST_USE bool replaceFrameGuts(JSContext* cx, AbstractFramePtr from,
846 : AbstractFramePtr to,
847 : ScriptFrameIter& iter);
848 :
849 : public:
850 : Debugger(JSContext* cx, NativeObject* dbg);
851 : ~Debugger();
852 :
853 : MOZ_MUST_USE bool init(JSContext* cx);
854 : inline const js::GCPtrNativeObject& toJSObject() const;
855 : inline js::GCPtrNativeObject& toJSObjectRef();
856 : static inline Debugger* fromJSObject(const JSObject* obj);
857 : static Debugger* fromChildJSObject(JSObject* obj);
858 :
859 : Zone* zone() const { return toJSObject()->zone(); }
860 :
861 : bool hasMemory() const;
862 : DebuggerMemory& memory() const;
863 :
864 0 : WeakGlobalObjectSet::Range allDebuggees() const { return debuggees.all(); }
865 :
866 : /*********************************** Methods for interaction with the GC. */
867 :
868 : /*
869 : * A Debugger object is live if:
870 : * * the Debugger JSObject is live (Debugger::trace handles this case); OR
871 : * * it is in the middle of dispatching an event (the event dispatching
872 : * code roots it in this case); OR
873 : * * it is enabled, and it is debugging at least one live compartment,
874 : * and at least one of the following is true:
875 : * - it has a debugger hook installed
876 : * - it has a breakpoint set on a live script
877 : * - it has a watchpoint set on a live object.
878 : *
879 : * Debugger::markIteratively handles the last case. If it finds any Debugger
880 : * objects that are definitely live but not yet marked, it marks them and
881 : * returns true. If not, it returns false.
882 : */
883 : static void traceIncomingCrossCompartmentEdges(JSTracer* tracer);
884 : static MOZ_MUST_USE bool markIteratively(GCMarker* marker);
885 : static void traceAllForMovingGC(JSTracer* trc);
886 : static void sweepAll(FreeOp* fop);
887 : static void detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global);
888 : static void findZoneEdges(JS::Zone* v, gc::ZoneComponentFinder& finder);
889 : #ifdef DEBUG
890 : static bool isDebuggerCrossCompartmentEdge(JSObject* obj, const js::gc::Cell* cell);
891 : #endif
892 :
893 : // Checks it the current compartment is allowed to execute code.
894 : static inline MOZ_MUST_USE bool checkNoExecute(JSContext* cx, HandleScript script);
895 :
896 : /*
897 : * Announce to the debugger that the context has entered a new JavaScript
898 : * frame, |frame|. Call whatever hooks have been registered to observe new
899 : * frames.
900 : */
901 : static inline ResumeMode onEnterFrame(JSContext* cx, AbstractFramePtr frame);
902 :
903 : /*
904 : * Announce to the debugger a |debugger;| statement on has been
905 : * encountered on the youngest JS frame on |cx|. Call whatever hooks have
906 : * been registered to observe this.
907 : *
908 : * Note that this method is called for all |debugger;| statements,
909 : * regardless of the frame's debuggee-ness.
910 : */
911 : static inline ResumeMode onDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
912 :
913 : /*
914 : * Announce to the debugger that an exception has been thrown and propagated
915 : * to |frame|. Call whatever hooks have been registered to observe this.
916 : */
917 : static inline ResumeMode onExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
918 :
919 : /*
920 : * Announce to the debugger that the thread has exited a JavaScript frame, |frame|.
921 : * If |ok| is true, the frame is returning normally; if |ok| is false, the frame
922 : * is throwing an exception or terminating.
923 : *
924 : * Change cx's current exception and |frame|'s return value to reflect the changes
925 : * in behavior the hooks request, if any. Return the new error/success value.
926 : *
927 : * This function may be called twice for the same outgoing frame; only the
928 : * first call has any effect. (Permitting double calls simplifies some
929 : * cases where an onPop handler's resumption value changes a return to a
930 : * throw, or vice versa: we can redirect to a complete copy of the
931 : * alternative path, containing its own call to onLeaveFrame.)
932 : */
933 : static inline MOZ_MUST_USE bool onLeaveFrame(JSContext* cx, AbstractFramePtr frame,
934 : jsbytecode* pc, bool ok);
935 :
936 : static inline void onNewScript(JSContext* cx, HandleScript script);
937 : static inline void onNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
938 : static inline void onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
939 : static inline MOZ_MUST_USE bool onLogAllocationSite(JSContext* cx, JSObject* obj,
940 : HandleSavedFrame frame, mozilla::TimeStamp when);
941 : static ResumeMode onTrap(JSContext* cx, MutableHandleValue vp);
942 : static ResumeMode onSingleStep(JSContext* cx, MutableHandleValue vp);
943 : static MOZ_MUST_USE bool handleBaselineOsr(JSContext* cx, InterpreterFrame* from,
944 : jit::BaselineFrame* to);
945 : static MOZ_MUST_USE bool handleIonBailout(JSContext* cx, jit::RematerializedFrame* from,
946 : jit::BaselineFrame* to);
947 : static void handleUnrecoverableIonBailoutError(JSContext* cx, jit::RematerializedFrame* frame);
948 : static void propagateForcedReturn(JSContext* cx, AbstractFramePtr frame, HandleValue rval);
949 : static bool hasLiveHook(GlobalObject* global, Hook which);
950 : static bool inFrameMaps(AbstractFramePtr frame);
951 :
952 : // Notify any Debugger instances observing this promise's global that a new
953 : // promise was allocated.
954 : static inline void onNewPromise(JSContext* cx, Handle<PromiseObject*> promise);
955 :
956 : // Notify any Debugger instances observing this promise's global that the
957 : // promise has settled (ie, it has either been fulfilled or rejected). Note that
958 : // this is *not* equivalent to the promise resolution (ie, the promise's fate
959 : // getting locked in) because you can resolve a promise with another pending
960 : // promise, in which case neither promise has settled yet.
961 : //
962 : // This should never be called on the same promise more than once, because a
963 : // promise can only make the transition from unsettled to settled once.
964 : static inline void onPromiseSettled(JSContext* cx, Handle<PromiseObject*> promise);
965 :
966 : /************************************* Functions for use by Debugger.cpp. */
967 :
968 : inline bool observesEnterFrame() const;
969 : inline bool observesNewScript() const;
970 : inline bool observesNewGlobalObject() const;
971 : inline bool observesGlobal(GlobalObject* global) const;
972 : bool observesFrame(AbstractFramePtr frame) const;
973 : bool observesFrame(const FrameIter& iter) const;
974 : bool observesScript(JSScript* script) const;
975 : bool observesWasm(wasm::Instance* instance) const;
976 :
977 : /*
978 : * If env is nullptr, call vp->setNull() and return true. Otherwise, find
979 : * or create a Debugger.Environment object for the given Env. On success,
980 : * store the Environment object in *vp and return true.
981 : */
982 : MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env, MutableHandleValue vp);
983 : MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env,
984 : MutableHandleDebuggerEnvironment result);
985 :
986 : /*
987 : * Like cx->compartment()->wrap(cx, vp), but for the debugger realm.
988 : *
989 : * Preconditions: *vp is a value from a debuggee realm; cx is in the
990 : * debugger's compartment.
991 : *
992 : * If *vp is an object, this produces a (new or existing) Debugger.Object
993 : * wrapper for it. Otherwise this is the same as Compartment::wrap.
994 : *
995 : * If *vp is a magic JS_OPTIMIZED_OUT value, this produces a plain object
996 : * of the form { optimizedOut: true }.
997 : *
998 : * If *vp is a magic JS_OPTIMIZED_ARGUMENTS value signifying missing
999 : * arguments, this produces a plain object of the form { missingArguments:
1000 : * true }.
1001 : *
1002 : * If *vp is a magic JS_UNINITIALIZED_LEXICAL value signifying an
1003 : * unaccessible uninitialized binding, this produces a plain object of the
1004 : * form { uninitialized: true }.
1005 : */
1006 : MOZ_MUST_USE bool wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
1007 : MOZ_MUST_USE bool wrapDebuggeeObject(JSContext* cx, HandleObject obj,
1008 : MutableHandleDebuggerObject result);
1009 :
1010 : /*
1011 : * Unwrap a Debug.Object, without rewrapping it for any particular debuggee
1012 : * compartment.
1013 : *
1014 : * Preconditions: cx is in the debugger compartment. *vp is a value in that
1015 : * compartment. (*vp should be a "debuggee value", meaning it is the
1016 : * debugger's reflection of a value in the debuggee.)
1017 : *
1018 : * If *vp is a Debugger.Object, store the referent in *vp. Otherwise, if *vp
1019 : * is an object, throw a TypeError, because it is not a debuggee
1020 : * value. Otherwise *vp is a primitive, so leave it alone.
1021 : *
1022 : * When passing values from the debuggee to the debugger:
1023 : * enter debugger compartment;
1024 : * call wrapDebuggeeValue; // compartment- and debugger-wrapping
1025 : *
1026 : * When passing values from the debugger to the debuggee:
1027 : * call unwrapDebuggeeValue; // debugger-unwrapping
1028 : * enter debuggee realm;
1029 : * call cx->compartment()->wrap; // compartment-rewrapping
1030 : *
1031 : * (Extreme nerd sidebar: Unwrapping happens in two steps because there are
1032 : * two different kinds of symmetry at work: regardless of which direction
1033 : * we're going, we want any exceptions to be created and thrown in the
1034 : * debugger compartment--mirror symmetry. But compartment wrapping always
1035 : * happens in the target compartment--rotational symmetry.)
1036 : */
1037 : MOZ_MUST_USE bool unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
1038 : MOZ_MUST_USE bool unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj);
1039 : MOZ_MUST_USE bool unwrapPropertyDescriptor(JSContext* cx, HandleObject obj,
1040 : MutableHandle<PropertyDescriptor> desc);
1041 :
1042 : /*
1043 : * Store the Debugger.Frame object for iter in *vp/result.
1044 : *
1045 : * If this Debugger does not already have a Frame object for the frame
1046 : * `iter` points to, a new Frame object is created, and `iter`'s private
1047 : * data is copied into it.
1048 : */
1049 : MOZ_MUST_USE bool getFrame(JSContext* cx, const FrameIter& iter,
1050 : MutableHandleValue vp);
1051 : MOZ_MUST_USE bool getFrame(JSContext* cx, const FrameIter& iter,
1052 : MutableHandleDebuggerFrame result);
1053 :
1054 : /*
1055 : * Set |*resumeMode| and |*value| to a (ResumeMode, Value) pair reflecting a
1056 : * standard SpiderMonkey call state: a boolean success value |ok|, a return
1057 : * value |rv|, and a context |cx| that may or may not have an exception set.
1058 : * If an exception was pending on |cx|, it is cleared (and |ok| is asserted
1059 : * to be false).
1060 : */
1061 : static void resultToCompletion(JSContext* cx, bool ok, const Value& rv,
1062 : ResumeMode* resumeMode, MutableHandleValue value);
1063 :
1064 : /*
1065 : * Set |*result| to a JavaScript completion value corresponding to |resumeMode|
1066 : * and |value|. |value| should be the return value or exception value, not
1067 : * wrapped as a debuggee value. |cx| must be in the debugger compartment.
1068 : */
1069 : MOZ_MUST_USE bool newCompletionValue(JSContext* cx, ResumeMode resumeMode, const Value& value,
1070 : MutableHandleValue result);
1071 :
1072 : /*
1073 : * Precondition: we are in the debuggee realm (ar is entered) and ok is true
1074 : * if the operation in the debuggee realm succeeded, false on error or
1075 : * exception.
1076 : *
1077 : * Postcondition: we are in the debugger realm, having called ar.leave()
1078 : * even if an error occurred.
1079 : *
1080 : * On success, a completion value is in vp and ar.context does not have a
1081 : * pending exception. (This ordinarily returns true even if the ok argument
1082 : * is false.)
1083 : */
1084 : MOZ_MUST_USE bool receiveCompletionValue(mozilla::Maybe<AutoRealm>& ar, bool ok,
1085 : HandleValue val,
1086 : MutableHandleValue vp);
1087 :
1088 : /*
1089 : * Return the Debugger.Script object for |script|, or create a new one if
1090 : * needed. The context |cx| must be in the debugger realm; |script| must be
1091 : * a script in a debuggee realm.
1092 : */
1093 : JSObject* wrapScript(JSContext* cx, HandleScript script);
1094 :
1095 : /*
1096 : * Return the Debugger.Script object for |wasmInstance| (the toplevel
1097 : * script), synthesizing a new one if needed. The context |cx| must be in
1098 : * the debugger compartment; |wasmInstance| must be a WasmInstanceObject in
1099 : * the debuggee realm.
1100 : */
1101 : JSObject* wrapWasmScript(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
1102 :
1103 : /*
1104 : * Return the Debugger.Source object for |source|, or create a new one if
1105 : * needed. The context |cx| must be in the debugger compartment; |source|
1106 : * must be a script source object in a debuggee realm.
1107 : */
1108 : JSObject* wrapSource(JSContext* cx, js::HandleScriptSourceObject source);
1109 :
1110 : /*
1111 : * Return the Debugger.Source object for |wasmInstance| (the entire module),
1112 : * synthesizing a new one if needed. The context |cx| must be in the
1113 : * debugger compartment; |wasmInstance| must be a WasmInstanceObject in the
1114 : * debuggee realm.
1115 : */
1116 : JSObject* wrapWasmSource(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
1117 :
1118 : private:
1119 : Debugger(const Debugger&) = delete;
1120 : Debugger & operator=(const Debugger&) = delete;
1121 : };
1122 :
1123 : enum class DebuggerEnvironmentType {
1124 : Declarative,
1125 : With,
1126 : Object
1127 : };
1128 :
1129 : class DebuggerEnvironment : public NativeObject
1130 : {
1131 : public:
1132 : enum {
1133 : OWNER_SLOT
1134 : };
1135 :
1136 : static const unsigned RESERVED_SLOTS = 1;
1137 :
1138 : static const Class class_;
1139 :
1140 : static NativeObject* initClass(JSContext* cx, HandleObject dbgCtor,
1141 : Handle<GlobalObject*> global);
1142 : static DebuggerEnvironment* create(JSContext* cx, HandleObject proto, HandleObject referent,
1143 : HandleNativeObject debugger);
1144 :
1145 : DebuggerEnvironmentType type() const;
1146 : MOZ_MUST_USE bool getParent(JSContext* cx, MutableHandleDebuggerEnvironment result) const;
1147 : MOZ_MUST_USE bool getObject(JSContext* cx, MutableHandleDebuggerObject result) const;
1148 : MOZ_MUST_USE bool getCallee(JSContext* cx, MutableHandleDebuggerObject result) const;
1149 : bool isDebuggee() const;
1150 : bool isOptimized() const;
1151 :
1152 : static MOZ_MUST_USE bool getNames(JSContext* cx, HandleDebuggerEnvironment environment,
1153 : MutableHandle<IdVector> result);
1154 : static MOZ_MUST_USE bool find(JSContext* cx, HandleDebuggerEnvironment environment,
1155 : HandleId id, MutableHandleDebuggerEnvironment result);
1156 : static MOZ_MUST_USE bool getVariable(JSContext* cx, HandleDebuggerEnvironment environment,
1157 : HandleId id, MutableHandleValue result);
1158 : static MOZ_MUST_USE bool setVariable(JSContext* cx, HandleDebuggerEnvironment environment,
1159 : HandleId id, HandleValue value);
1160 :
1161 : private:
1162 : static const ClassOps classOps_;
1163 :
1164 : static const JSPropertySpec properties_[];
1165 : static const JSFunctionSpec methods_[];
1166 :
1167 0 : Env* referent() const {
1168 0 : Env* env = static_cast<Env*>(getPrivate());
1169 0 : MOZ_ASSERT(env);
1170 0 : return env;
1171 : }
1172 :
1173 : Debugger* owner() const;
1174 :
1175 : bool requireDebuggee(JSContext* cx) const;
1176 :
1177 : static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
1178 :
1179 : static MOZ_MUST_USE bool typeGetter(JSContext* cx, unsigned argc, Value* vp);
1180 : static MOZ_MUST_USE bool parentGetter(JSContext* cx, unsigned argc, Value* vp);
1181 : static MOZ_MUST_USE bool objectGetter(JSContext* cx, unsigned argc, Value* vp);
1182 : static MOZ_MUST_USE bool calleeGetter(JSContext* cx, unsigned argc, Value* vp);
1183 : static MOZ_MUST_USE bool inspectableGetter(JSContext* cx, unsigned argc, Value* vp);
1184 : static MOZ_MUST_USE bool optimizedOutGetter(JSContext* cx, unsigned argc, Value* vp);
1185 :
1186 : static MOZ_MUST_USE bool namesMethod(JSContext* cx, unsigned argc, Value* vp);
1187 : static MOZ_MUST_USE bool findMethod(JSContext* cx, unsigned argc, Value* vp);
1188 : static MOZ_MUST_USE bool getVariableMethod(JSContext* cx, unsigned argc, Value* vp);
1189 : static MOZ_MUST_USE bool setVariableMethod(JSContext* cx, unsigned argc, Value* vp);
1190 : };
1191 :
1192 : enum class DebuggerFrameType {
1193 : Eval,
1194 : Global,
1195 : Call,
1196 : Module,
1197 : WasmCall
1198 : };
1199 :
1200 : enum class DebuggerFrameImplementation {
1201 : Interpreter,
1202 : Baseline,
1203 : Ion,
1204 : Wasm
1205 : };
1206 :
1207 : /*
1208 : * A Handler represents a reference to a handler function. These handler
1209 : * functions are called by the Debugger API to notify the user of certain
1210 : * events. For each event type, we define a separate subclass of Handler. This
1211 : * allows users to define a single reference to an object that implements
1212 : * multiple handlers, by inheriting from the appropriate subclasses.
1213 : *
1214 : * A Handler can be stored on a reflection object, in which case the reflection
1215 : * object becomes responsible for managing the lifetime of the Handler. To aid
1216 : * with this, the Handler base class defines several methods, which are to be
1217 : * called by the reflection object at the appropriate time (see below).
1218 : */
1219 : struct Handler {
1220 : virtual ~Handler() {}
1221 :
1222 : /*
1223 : * If the Handler is a reference to a callable JSObject, this method returns
1224 : * the latter. This allows the Handler to be used from JS. Otherwise, this
1225 : * method returns nullptr.
1226 : */
1227 : virtual JSObject* object() const = 0;
1228 :
1229 : /*
1230 : * Drops the reference to the handler. This method will be called by the
1231 : * reflection object on which the reference is stored when the former is
1232 : * finalized, or the latter replaced.
1233 : */
1234 : virtual void drop() = 0;
1235 :
1236 : /*
1237 : * Traces the reference to the handler. This method will be called
1238 : * by the reflection object on which the reference is stored whenever the
1239 : * former is traced.
1240 : */
1241 : virtual void trace(JSTracer* tracer) = 0;
1242 : };
1243 :
1244 : class DebuggerArguments : public NativeObject {
1245 : public:
1246 : static const Class class_;
1247 :
1248 : static DebuggerArguments* create(JSContext* cx, HandleObject proto, HandleDebuggerFrame frame);
1249 :
1250 : private:
1251 : enum {
1252 : FRAME_SLOT
1253 : };
1254 :
1255 : static const unsigned RESERVED_SLOTS = 1;
1256 : };
1257 :
1258 : /*
1259 : * An OnStepHandler represents a handler function that is called when a small
1260 : * amount of progress is made in a frame.
1261 : */
1262 0 : struct OnStepHandler : Handler {
1263 : /*
1264 : * If we have made a small amount of progress in a frame, this method is
1265 : * called with the frame as argument. If succesful, this method should
1266 : * return true, with `resumeMode` and `vp` set to a resumption value
1267 : * specifiying how execution should continue.
1268 : */
1269 : virtual bool onStep(JSContext* cx, HandleDebuggerFrame frame, ResumeMode& resumeMode,
1270 : MutableHandleValue vp) = 0;
1271 : };
1272 :
1273 0 : class ScriptedOnStepHandler final : public OnStepHandler {
1274 : public:
1275 : explicit ScriptedOnStepHandler(JSObject* object);
1276 : virtual JSObject* object() const override;
1277 : virtual void drop() override;
1278 : virtual void trace(JSTracer* tracer) override;
1279 : virtual bool onStep(JSContext* cx, HandleDebuggerFrame frame, ResumeMode& resumeMode,
1280 : MutableHandleValue vp) override;
1281 :
1282 : private:
1283 : HeapPtr<JSObject*> object_;
1284 : };
1285 :
1286 : /*
1287 : * An OnPopHandler represents a handler function that is called just before a
1288 : * frame is popped.
1289 : */
1290 0 : struct OnPopHandler : Handler {
1291 : /*
1292 : * If a frame is about the be popped, this method is called with the frame
1293 : * as argument, and `resumeMode` and `vp` set to a completion value specifying
1294 : * how this frame's execution completed. If successful, this method should
1295 : * return true, with `resumeMode` and `vp` set to a resumption value specifying
1296 : * how execution should continue.
1297 : */
1298 : virtual bool onPop(JSContext* cx, HandleDebuggerFrame frame, ResumeMode& resumeMode,
1299 : MutableHandleValue vp) = 0;
1300 : };
1301 :
1302 0 : class ScriptedOnPopHandler final : public OnPopHandler {
1303 : public:
1304 : explicit ScriptedOnPopHandler(JSObject* object);
1305 : virtual JSObject* object() const override;
1306 : virtual void drop() override;
1307 : virtual void trace(JSTracer* tracer) override;
1308 : virtual bool onPop(JSContext* cx, HandleDebuggerFrame frame, ResumeMode& resumeMode,
1309 : MutableHandleValue vp) override;
1310 :
1311 : private:
1312 : HeapPtr<JSObject*> object_;
1313 : };
1314 :
1315 : class DebuggerFrame : public NativeObject
1316 : {
1317 : friend class DebuggerArguments;
1318 : friend class ScriptedOnStepHandler;
1319 : friend class ScriptedOnPopHandler;
1320 :
1321 : public:
1322 : enum {
1323 : OWNER_SLOT
1324 : };
1325 :
1326 : static const unsigned RESERVED_SLOTS = 1;
1327 :
1328 : static const Class class_;
1329 :
1330 : static NativeObject* initClass(JSContext* cx, HandleObject dbgCtor,
1331 : Handle<GlobalObject*> global);
1332 : static DebuggerFrame* create(JSContext* cx, HandleObject proto, const FrameIter& iter,
1333 : HandleNativeObject debugger);
1334 : void freeFrameIterData(FreeOp* fop);
1335 :
1336 : static MOZ_MUST_USE bool getArguments(JSContext* cx, HandleDebuggerFrame frame,
1337 : MutableHandleDebuggerArguments result);
1338 : static MOZ_MUST_USE bool getCallee(JSContext* cx, HandleDebuggerFrame frame,
1339 : MutableHandleDebuggerObject result);
1340 : static MOZ_MUST_USE bool getIsConstructing(JSContext* cx, HandleDebuggerFrame frame,
1341 : bool& result);
1342 : static MOZ_MUST_USE bool getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
1343 : MutableHandleDebuggerEnvironment result);
1344 : static bool getIsGenerator(HandleDebuggerFrame frame);
1345 : static MOZ_MUST_USE bool getOffset(JSContext* cx, HandleDebuggerFrame frame, size_t& result);
1346 : static MOZ_MUST_USE bool getOlder(JSContext* cx, HandleDebuggerFrame frame,
1347 : MutableHandleDebuggerFrame result);
1348 : static MOZ_MUST_USE bool getThis(JSContext* cx, HandleDebuggerFrame frame,
1349 : MutableHandleValue result);
1350 : static DebuggerFrameType getType(HandleDebuggerFrame frame);
1351 : static DebuggerFrameImplementation getImplementation(HandleDebuggerFrame frame);
1352 : static MOZ_MUST_USE bool setOnStepHandler(JSContext* cx, HandleDebuggerFrame frame,
1353 : OnStepHandler* handler);
1354 :
1355 : static MOZ_MUST_USE bool eval(JSContext* cx, HandleDebuggerFrame frame,
1356 : mozilla::Range<const char16_t> chars, HandleObject bindings,
1357 : const EvalOptions& options, ResumeMode& resumeMode,
1358 : MutableHandleValue value);
1359 :
1360 : bool isLive() const;
1361 : OnStepHandler* onStepHandler() const;
1362 : OnPopHandler* onPopHandler() const;
1363 : void setOnPopHandler(OnPopHandler* handler);
1364 :
1365 : private:
1366 : static const ClassOps classOps_;
1367 :
1368 : static const JSPropertySpec properties_[];
1369 : static const JSFunctionSpec methods_[];
1370 :
1371 : static AbstractFramePtr getReferent(HandleDebuggerFrame frame);
1372 : static MOZ_MUST_USE bool getFrameIter(JSContext* cx, HandleDebuggerFrame frame,
1373 : mozilla::Maybe<FrameIter>& result);
1374 : static MOZ_MUST_USE bool requireScriptReferent(JSContext* cx, HandleDebuggerFrame frame);
1375 :
1376 : static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
1377 :
1378 : static MOZ_MUST_USE bool argumentsGetter(JSContext* cx, unsigned argc, Value* vp);
1379 : static MOZ_MUST_USE bool calleeGetter(JSContext* cx, unsigned argc, Value* vp);
1380 : static MOZ_MUST_USE bool constructingGetter(JSContext* cx, unsigned argc, Value* vp);
1381 : static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc, Value* vp);
1382 : static MOZ_MUST_USE bool generatorGetter(JSContext* cx, unsigned argc, Value* vp);
1383 : static MOZ_MUST_USE bool liveGetter(JSContext* cx, unsigned argc, Value* vp);
1384 : static MOZ_MUST_USE bool offsetGetter(JSContext* cx, unsigned argc, Value* vp);
1385 : static MOZ_MUST_USE bool olderGetter(JSContext* cx, unsigned argc, Value* vp);
1386 : static MOZ_MUST_USE bool thisGetter(JSContext* cx, unsigned argc, Value* vp);
1387 : static MOZ_MUST_USE bool typeGetter(JSContext* cx, unsigned argc, Value* vp);
1388 : static MOZ_MUST_USE bool implementationGetter(JSContext* cx, unsigned argc, Value* vp);
1389 : static MOZ_MUST_USE bool onStepGetter(JSContext* cx, unsigned argc, Value* vp);
1390 : static MOZ_MUST_USE bool onStepSetter(JSContext* cx, unsigned argc, Value* vp);
1391 : static MOZ_MUST_USE bool onPopGetter(JSContext* cx, unsigned argc, Value* vp);
1392 : static MOZ_MUST_USE bool onPopSetter(JSContext* cx, unsigned argc, Value* vp);
1393 :
1394 : static MOZ_MUST_USE bool evalMethod(JSContext* cx, unsigned argc, Value* vp);
1395 : static MOZ_MUST_USE bool evalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp);
1396 :
1397 : Debugger* owner() const;
1398 : public:
1399 : FrameIter::Data* frameIterData() const;
1400 : };
1401 :
1402 : class DebuggerObject : public NativeObject
1403 : {
1404 : public:
1405 : static const Class class_;
1406 :
1407 : static NativeObject* initClass(JSContext* cx, Handle<GlobalObject*> global,
1408 : HandleObject debugCtor);
1409 : static DebuggerObject* create(JSContext* cx, HandleObject proto, HandleObject obj,
1410 : HandleNativeObject debugger);
1411 :
1412 : // Properties
1413 : static MOZ_MUST_USE bool getClassName(JSContext* cx, HandleDebuggerObject object,
1414 : MutableHandleString result);
1415 : static MOZ_MUST_USE bool getGlobal(JSContext* cx, HandleDebuggerObject object,
1416 : MutableHandleDebuggerObject result);
1417 : static MOZ_MUST_USE bool getParameterNames(JSContext* cx, HandleDebuggerObject object,
1418 : MutableHandle<StringVector> result);
1419 : static MOZ_MUST_USE bool getBoundTargetFunction(JSContext* cx, HandleDebuggerObject object,
1420 : MutableHandleDebuggerObject result);
1421 : static MOZ_MUST_USE bool getBoundThis(JSContext* cx, HandleDebuggerObject object,
1422 : MutableHandleValue result);
1423 : static MOZ_MUST_USE bool getBoundArguments(JSContext* cx, HandleDebuggerObject object,
1424 : MutableHandle<ValueVector> result);
1425 : static MOZ_MUST_USE bool getAllocationSite(JSContext* cx, HandleDebuggerObject object,
1426 : MutableHandleObject result);
1427 : static MOZ_MUST_USE bool getErrorMessageName(JSContext* cx, HandleDebuggerObject object,
1428 : MutableHandleString result);
1429 : static MOZ_MUST_USE bool getErrorNotes(JSContext* cx, HandleDebuggerObject object,
1430 : MutableHandleValue result);
1431 : static MOZ_MUST_USE bool getErrorLineNumber(JSContext* cx, HandleDebuggerObject object,
1432 : MutableHandleValue result);
1433 : static MOZ_MUST_USE bool getErrorColumnNumber(JSContext* cx, HandleDebuggerObject object,
1434 : MutableHandleValue result);
1435 : static MOZ_MUST_USE bool getScriptedProxyTarget(JSContext* cx, HandleDebuggerObject object,
1436 : MutableHandleDebuggerObject result);
1437 : static MOZ_MUST_USE bool getScriptedProxyHandler(JSContext* cx, HandleDebuggerObject object,
1438 : MutableHandleDebuggerObject result);
1439 : static MOZ_MUST_USE bool getPromiseValue(JSContext* cx, HandleDebuggerObject object,
1440 : MutableHandleValue result);
1441 : static MOZ_MUST_USE bool getPromiseReason(JSContext* cx, HandleDebuggerObject object,
1442 : MutableHandleValue result);
1443 :
1444 : // Methods
1445 : static MOZ_MUST_USE bool isExtensible(JSContext* cx, HandleDebuggerObject object,
1446 : bool& result);
1447 : static MOZ_MUST_USE bool isSealed(JSContext* cx, HandleDebuggerObject object, bool& result);
1448 : static MOZ_MUST_USE bool isFrozen(JSContext* cx, HandleDebuggerObject object, bool& result);
1449 : static MOZ_MUST_USE bool getPrototypeOf(JSContext* cx, HandleDebuggerObject object,
1450 : MutableHandleDebuggerObject result);
1451 : static MOZ_MUST_USE bool getOwnPropertyNames(JSContext* cx, HandleDebuggerObject object,
1452 : MutableHandle<IdVector> result);
1453 : static MOZ_MUST_USE bool getOwnPropertySymbols(JSContext* cx, HandleDebuggerObject object,
1454 : MutableHandle<IdVector> result);
1455 : static MOZ_MUST_USE bool getOwnPropertyDescriptor(JSContext* cx, HandleDebuggerObject object,
1456 : HandleId id,
1457 : MutableHandle<PropertyDescriptor> desc);
1458 : static MOZ_MUST_USE bool preventExtensions(JSContext* cx, HandleDebuggerObject object);
1459 : static MOZ_MUST_USE bool seal(JSContext* cx, HandleDebuggerObject object);
1460 : static MOZ_MUST_USE bool freeze(JSContext* cx, HandleDebuggerObject object);
1461 : static MOZ_MUST_USE bool defineProperty(JSContext* cx, HandleDebuggerObject object,
1462 : HandleId id, Handle<PropertyDescriptor> desc);
1463 : static MOZ_MUST_USE bool defineProperties(JSContext* cx, HandleDebuggerObject object,
1464 : Handle<IdVector> ids,
1465 : Handle<PropertyDescriptorVector> descs);
1466 : static MOZ_MUST_USE bool deleteProperty(JSContext* cx, HandleDebuggerObject object,
1467 : HandleId id, ObjectOpResult& result);
1468 : static MOZ_MUST_USE bool call(JSContext* cx, HandleDebuggerObject object, HandleValue thisv,
1469 : Handle<ValueVector> args, MutableHandleValue result);
1470 : static MOZ_MUST_USE bool forceLexicalInitializationByName(JSContext* cx,
1471 : HandleDebuggerObject object,
1472 : HandleId id, bool& result);
1473 : static MOZ_MUST_USE bool executeInGlobal(JSContext* cx, HandleDebuggerObject object,
1474 : mozilla::Range<const char16_t> chars,
1475 : HandleObject bindings, const EvalOptions& options,
1476 : ResumeMode& resumeMode, MutableHandleValue value);
1477 : static MOZ_MUST_USE bool makeDebuggeeValue(JSContext* cx, HandleDebuggerObject object,
1478 : HandleValue value, MutableHandleValue result);
1479 : static MOZ_MUST_USE bool unsafeDereference(JSContext* cx, HandleDebuggerObject object,
1480 : MutableHandleObject result);
1481 : static MOZ_MUST_USE bool unwrap(JSContext* cx, HandleDebuggerObject object,
1482 : MutableHandleDebuggerObject result);
1483 :
1484 : // Infallible properties
1485 : bool isCallable() const;
1486 : bool isFunction() const;
1487 : bool isDebuggeeFunction() const;
1488 : bool isBoundFunction() const;
1489 : bool isArrowFunction() const;
1490 : bool isAsyncFunction() const;
1491 : bool isGeneratorFunction() const;
1492 : bool isGlobal() const;
1493 : bool isScriptedProxy() const;
1494 : bool isPromise() const;
1495 : JSAtom* name(JSContext* cx) const;
1496 : JSAtom* displayName(JSContext* cx) const;
1497 : JS::PromiseState promiseState() const;
1498 : double promiseLifetime() const;
1499 : double promiseTimeToResolution() const;
1500 :
1501 : private:
1502 : enum {
1503 : OWNER_SLOT
1504 : };
1505 :
1506 : static const unsigned RESERVED_SLOTS = 1;
1507 :
1508 : static const ClassOps classOps_;
1509 :
1510 : static const JSPropertySpec properties_[];
1511 : static const JSPropertySpec promiseProperties_[];
1512 : static const JSFunctionSpec methods_[];
1513 :
1514 0 : JSObject* referent() const {
1515 0 : JSObject* obj = (JSObject*) getPrivate();
1516 0 : MOZ_ASSERT(obj);
1517 0 : return obj;
1518 : }
1519 :
1520 : Debugger* owner() const;
1521 : PromiseObject* promise() const;
1522 :
1523 : static MOZ_MUST_USE bool requireGlobal(JSContext* cx, HandleDebuggerObject object);
1524 : static MOZ_MUST_USE bool requirePromise(JSContext* cx, HandleDebuggerObject object);
1525 : static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
1526 :
1527 : // JSNative properties
1528 : static MOZ_MUST_USE bool callableGetter(JSContext* cx, unsigned argc, Value* vp);
1529 : static MOZ_MUST_USE bool isBoundFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
1530 : static MOZ_MUST_USE bool isArrowFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
1531 : static MOZ_MUST_USE bool isAsyncFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
1532 : static MOZ_MUST_USE bool isGeneratorFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
1533 : static MOZ_MUST_USE bool protoGetter(JSContext* cx, unsigned argc, Value* vp);
1534 : static MOZ_MUST_USE bool classGetter(JSContext* cx, unsigned argc, Value* vp);
1535 : static MOZ_MUST_USE bool nameGetter(JSContext* cx, unsigned argc, Value* vp);
1536 : static MOZ_MUST_USE bool displayNameGetter(JSContext* cx, unsigned argc, Value* vp);
1537 : static MOZ_MUST_USE bool parameterNamesGetter(JSContext* cx, unsigned argc, Value* vp);
1538 : static MOZ_MUST_USE bool scriptGetter(JSContext* cx, unsigned argc, Value* vp);
1539 : static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc, Value* vp);
1540 : static MOZ_MUST_USE bool boundTargetFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
1541 : static MOZ_MUST_USE bool boundThisGetter(JSContext* cx, unsigned argc, Value* vp);
1542 : static MOZ_MUST_USE bool boundArgumentsGetter(JSContext* cx, unsigned argc, Value* vp);
1543 : static MOZ_MUST_USE bool globalGetter(JSContext* cx, unsigned argc, Value* vp);
1544 : static MOZ_MUST_USE bool allocationSiteGetter(JSContext* cx, unsigned argc, Value* vp);
1545 : static MOZ_MUST_USE bool errorMessageNameGetter(JSContext* cx, unsigned argc, Value* vp);
1546 : static MOZ_MUST_USE bool errorNotesGetter(JSContext* cx, unsigned argc, Value* vp);
1547 : static MOZ_MUST_USE bool errorLineNumberGetter(JSContext* cx, unsigned argc, Value* vp);
1548 : static MOZ_MUST_USE bool errorColumnNumberGetter(JSContext* cx, unsigned argc, Value* vp);
1549 : static MOZ_MUST_USE bool isProxyGetter(JSContext* cx, unsigned argc, Value* vp);
1550 : static MOZ_MUST_USE bool proxyTargetGetter(JSContext* cx, unsigned argc, Value* vp);
1551 : static MOZ_MUST_USE bool proxyHandlerGetter(JSContext* cx, unsigned argc, Value* vp);
1552 : static MOZ_MUST_USE bool isPromiseGetter(JSContext* cx, unsigned argc, Value* vp);
1553 : static MOZ_MUST_USE bool promiseStateGetter(JSContext* cx, unsigned argc, Value* vp);
1554 : static MOZ_MUST_USE bool promiseValueGetter(JSContext* cx, unsigned argc, Value* vp);
1555 : static MOZ_MUST_USE bool promiseReasonGetter(JSContext* cx, unsigned argc, Value* vp);
1556 : static MOZ_MUST_USE bool promiseLifetimeGetter(JSContext* cx, unsigned argc, Value* vp);
1557 : static MOZ_MUST_USE bool promiseTimeToResolutionGetter(JSContext* cx, unsigned argc, Value* vp);
1558 : static MOZ_MUST_USE bool promiseAllocationSiteGetter(JSContext* cx, unsigned argc, Value* vp);
1559 : static MOZ_MUST_USE bool promiseResolutionSiteGetter(JSContext* cx, unsigned argc, Value* vp);
1560 : static MOZ_MUST_USE bool promiseIDGetter(JSContext* cx, unsigned argc, Value* vp);
1561 : static MOZ_MUST_USE bool promiseDependentPromisesGetter(JSContext* cx, unsigned argc, Value* vp);
1562 :
1563 : // JSNative methods
1564 : static MOZ_MUST_USE bool isExtensibleMethod(JSContext* cx, unsigned argc, Value* vp);
1565 : static MOZ_MUST_USE bool isSealedMethod(JSContext* cx, unsigned argc, Value* vp);
1566 : static MOZ_MUST_USE bool isFrozenMethod(JSContext* cx, unsigned argc, Value* vp);
1567 : static MOZ_MUST_USE bool getOwnPropertyNamesMethod(JSContext* cx, unsigned argc, Value* vp);
1568 : static MOZ_MUST_USE bool getOwnPropertySymbolsMethod(JSContext* cx, unsigned argc, Value* vp);
1569 : static MOZ_MUST_USE bool getOwnPropertyDescriptorMethod(JSContext* cx, unsigned argc, Value* vp);
1570 : static MOZ_MUST_USE bool preventExtensionsMethod(JSContext* cx, unsigned argc, Value* vp);
1571 : static MOZ_MUST_USE bool sealMethod(JSContext* cx, unsigned argc, Value* vp);
1572 : static MOZ_MUST_USE bool freezeMethod(JSContext* cx, unsigned argc, Value* vp);
1573 : static MOZ_MUST_USE bool definePropertyMethod(JSContext* cx, unsigned argc, Value* vp);
1574 : static MOZ_MUST_USE bool definePropertiesMethod(JSContext* cx, unsigned argc, Value* vp);
1575 : static MOZ_MUST_USE bool deletePropertyMethod(JSContext* cx, unsigned argc, Value* vp);
1576 : static MOZ_MUST_USE bool callMethod(JSContext* cx, unsigned argc, Value* vp);
1577 : static MOZ_MUST_USE bool applyMethod(JSContext* cx, unsigned argc, Value* vp);
1578 : static MOZ_MUST_USE bool asEnvironmentMethod(JSContext* cx, unsigned argc, Value* vp);
1579 : static MOZ_MUST_USE bool forceLexicalInitializationByNameMethod(JSContext* cx, unsigned argc, Value* vp);
1580 : static MOZ_MUST_USE bool executeInGlobalMethod(JSContext* cx, unsigned argc, Value* vp);
1581 : static MOZ_MUST_USE bool executeInGlobalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp);
1582 : static MOZ_MUST_USE bool makeDebuggeeValueMethod(JSContext* cx, unsigned argc, Value* vp);
1583 : static MOZ_MUST_USE bool unsafeDereferenceMethod(JSContext* cx, unsigned argc, Value* vp);
1584 : static MOZ_MUST_USE bool unwrapMethod(JSContext* cx, unsigned argc, Value* vp);
1585 : static MOZ_MUST_USE bool getErrorReport(JSContext* cx, HandleObject maybeError,
1586 : JSErrorReport*& report);
1587 : };
1588 :
1589 : class JSBreakpointSite;
1590 : class WasmBreakpoint;
1591 : class WasmBreakpointSite;
1592 :
1593 : class BreakpointSite {
1594 : friend class Breakpoint;
1595 : friend class ::JSScript;
1596 : friend class Debugger;
1597 :
1598 : public:
1599 : enum class Type { JS, Wasm };
1600 :
1601 : private:
1602 : Type type_;
1603 :
1604 : template <typename T>
1605 : struct SiteLinkAccess {
1606 : static mozilla::DoublyLinkedListElement<T>& Get(T* aThis) {
1607 : return aThis->siteLink;
1608 : }
1609 : };
1610 :
1611 : // List of all js::Breakpoints at this instruction.
1612 : using BreakpointList =
1613 : mozilla::DoublyLinkedList<js::Breakpoint,
1614 : SiteLinkAccess<js::Breakpoint>>;
1615 : BreakpointList breakpoints;
1616 : size_t enabledCount; /* number of breakpoints in the list that are enabled */
1617 :
1618 : protected:
1619 : virtual void recompile(FreeOp* fop) = 0;
1620 : bool isEmpty() const;
1621 0 : inline bool isEnabled() const { return enabledCount > 0; }
1622 :
1623 : public:
1624 : BreakpointSite(Type type);
1625 : Breakpoint* firstBreakpoint() const;
1626 : virtual ~BreakpointSite() {}
1627 : bool hasBreakpoint(Breakpoint* bp);
1628 : inline Type type() const { return type_; }
1629 :
1630 : void inc(FreeOp* fop);
1631 : void dec(FreeOp* fop);
1632 : virtual void destroyIfEmpty(FreeOp* fop) = 0;
1633 :
1634 : inline JSBreakpointSite* asJS();
1635 : inline WasmBreakpointSite* asWasm();
1636 : };
1637 :
1638 : /*
1639 : * Each Breakpoint is a member of two linked lists: its debugger's list and its
1640 : * site's list.
1641 : *
1642 : * GC rules:
1643 : * - script is live, breakpoint exists, and debugger is enabled
1644 : * ==> debugger is live
1645 : * - script is live, breakpoint exists, and debugger is live
1646 : * ==> retain the breakpoint and the handler object is live
1647 : *
1648 : * Debugger::markIteratively implements these two rules. It uses
1649 : * Debugger::hasAnyLiveHooks to check for rule 1.
1650 : *
1651 : * Nothing else causes a breakpoint to be retained, so if its script or
1652 : * debugger is collected, the breakpoint is destroyed during GC sweep phase,
1653 : * even if the debugger compartment isn't being GC'd. This is implemented in
1654 : * Zone::sweepBreakpoints.
1655 : */
1656 0 : class Breakpoint {
1657 : friend class Debugger;
1658 : friend class BreakpointSite;
1659 :
1660 : public:
1661 : Debugger * const debugger;
1662 : BreakpointSite * const site;
1663 : private:
1664 : /* |handler| is marked unconditionally during minor GC. */
1665 : js::PreBarrieredObject handler;
1666 :
1667 : /**
1668 : * Link elements for each list this breakpoint can be in.
1669 : */
1670 : mozilla::DoublyLinkedListElement<Breakpoint> debuggerLink;
1671 : mozilla::DoublyLinkedListElement<Breakpoint> siteLink;
1672 :
1673 : public:
1674 : Breakpoint(Debugger* debugger, BreakpointSite* site, JSObject* handler);
1675 : void destroy(FreeOp* fop);
1676 : Breakpoint* nextInDebugger();
1677 : Breakpoint* nextInSite();
1678 : const PreBarrieredObject& getHandler() const { return handler; }
1679 : PreBarrieredObject& getHandlerRef() { return handler; }
1680 :
1681 : inline WasmBreakpoint* asWasm();
1682 : };
1683 :
1684 0 : class JSBreakpointSite : public BreakpointSite
1685 : {
1686 : public:
1687 : JSScript* script;
1688 : jsbytecode * const pc;
1689 :
1690 : protected:
1691 : void recompile(FreeOp* fop) override;
1692 :
1693 : public:
1694 : JSBreakpointSite(JSScript* script, jsbytecode* pc);
1695 :
1696 : void destroyIfEmpty(FreeOp* fop) override;
1697 : };
1698 :
1699 : inline JSBreakpointSite*
1700 0 : BreakpointSite::asJS()
1701 : {
1702 0 : MOZ_ASSERT(type() == Type::JS);
1703 0 : return static_cast<JSBreakpointSite*>(this);
1704 : }
1705 :
1706 0 : class WasmBreakpointSite : public BreakpointSite
1707 : {
1708 : public:
1709 : wasm::DebugState* debug;
1710 : uint32_t offset;
1711 :
1712 : protected:
1713 : void recompile(FreeOp* fop) override;
1714 :
1715 : public:
1716 : WasmBreakpointSite(wasm::DebugState* debug, uint32_t offset);
1717 :
1718 : void destroyIfEmpty(FreeOp* fop) override;
1719 : };
1720 :
1721 : inline WasmBreakpointSite*
1722 : BreakpointSite::asWasm()
1723 : {
1724 : MOZ_ASSERT(type() == Type::Wasm);
1725 : return static_cast<WasmBreakpointSite*>(this);
1726 : }
1727 :
1728 : class WasmBreakpoint : public Breakpoint
1729 : {
1730 : public:
1731 : WasmInstanceObject* wasmInstance;
1732 :
1733 : WasmBreakpoint(Debugger* debugger, WasmBreakpointSite* site, JSObject* handler,
1734 : WasmInstanceObject* wasmInstance_)
1735 0 : : Breakpoint(debugger, site, handler),
1736 0 : wasmInstance(wasmInstance_)
1737 : {}
1738 : };
1739 :
1740 : inline WasmBreakpoint*
1741 0 : Breakpoint::asWasm()
1742 : {
1743 0 : MOZ_ASSERT(site && site->type() == BreakpointSite::Type::Wasm);
1744 0 : return static_cast<WasmBreakpoint*>(this);
1745 : }
1746 :
1747 :
1748 : Breakpoint*
1749 : Debugger::firstBreakpoint() const
1750 : {
1751 0 : if (breakpoints.isEmpty())
1752 : return nullptr;
1753 0 : return &(*breakpoints.begin());
1754 : }
1755 :
1756 : const js::GCPtrNativeObject&
1757 0 : Debugger::toJSObject() const
1758 : {
1759 0 : MOZ_ASSERT(object);
1760 0 : return object;
1761 : }
1762 :
1763 : js::GCPtrNativeObject&
1764 0 : Debugger::toJSObjectRef()
1765 : {
1766 0 : MOZ_ASSERT(object);
1767 0 : return object;
1768 : }
1769 :
1770 : bool
1771 : Debugger::observesEnterFrame() const
1772 : {
1773 0 : return enabled && getHook(OnEnterFrame);
1774 : }
1775 :
1776 : bool
1777 : Debugger::observesNewScript() const
1778 : {
1779 0 : return enabled && getHook(OnNewScript);
1780 : }
1781 :
1782 : bool
1783 : Debugger::observesNewGlobalObject() const
1784 : {
1785 0 : return enabled && getHook(OnNewGlobalObject);
1786 : }
1787 :
1788 : bool
1789 0 : Debugger::observesGlobal(GlobalObject* global) const
1790 : {
1791 0 : ReadBarriered<GlobalObject*> debuggee(global);
1792 0 : return debuggees.has(debuggee);
1793 : }
1794 :
1795 : /* static */ void
1796 2226 : Debugger::onNewScript(JSContext* cx, HandleScript script)
1797 : {
1798 : // We early return in slowPathOnNewScript for self-hosted scripts, so we can
1799 : // ignore those in our assertion here.
1800 6352 : MOZ_ASSERT_IF(!script->realm()->creationOptions().invisibleToDebugger() &&
1801 : !script->selfHosted(),
1802 : script->realm()->firedOnNewGlobalObject);
1803 :
1804 : // The script may not be ready to be interrogated by the debugger.
1805 4452 : if (script->hideScriptFromDebugger())
1806 : return;
1807 :
1808 4452 : if (script->realm()->isDebuggee())
1809 0 : slowPathOnNewScript(cx, script);
1810 : }
1811 :
1812 : /* static */ void
1813 42 : Debugger::onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global)
1814 : {
1815 84 : MOZ_ASSERT(!global->realm()->firedOnNewGlobalObject);
1816 : #ifdef DEBUG
1817 84 : global->realm()->firedOnNewGlobalObject = true;
1818 : #endif
1819 84 : if (!cx->runtime()->onNewGlobalObjectWatchers().isEmpty())
1820 0 : Debugger::slowPathOnNewGlobalObject(cx, global);
1821 0 : }
1822 :
1823 : /* static */ bool
1824 0 : Debugger::onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame, mozilla::TimeStamp when)
1825 : {
1826 0 : GlobalObject::DebuggerVector* dbgs = cx->global()->getDebuggers();
1827 0 : if (!dbgs || dbgs->empty())
1828 : return true;
1829 0 : RootedObject hobj(cx, obj);
1830 0 : return Debugger::slowPathOnLogAllocationSite(cx, hobj, frame, when, *dbgs);
1831 : }
1832 :
1833 : MOZ_MUST_USE bool ReportObjectRequired(JSContext* cx);
1834 :
1835 : } /* namespace js */
1836 :
1837 : namespace JS {
1838 :
1839 : template <>
1840 : struct DeletePolicy<js::Debugger> : public js::GCManagedDeletePolicy<js::Debugger>
1841 : {};
1842 :
1843 : } /* namespace JS */
1844 :
1845 : #endif /* vm_Debugger_h */
|