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
#include "debugger/Debugger-inl.h"
8
9
#include "mozilla/Attributes.h" // for MOZ_STACK_CLASS, MOZ_RAII
10
#include "mozilla/DebugOnly.h" // for DebugOnly
11
#include "mozilla/DoublyLinkedList.h" // for DoublyLinkedList<>::Iterator
12
#include "mozilla/GuardObjects.h" // for MOZ_GUARD_OBJECT_NOTIFIER_PARAM
13
#include "mozilla/HashTable.h" // for HashSet<>::Range, HashMapEntry
14
#include "mozilla/Maybe.h" // for Maybe, Nothing, Some
15
#include "mozilla/Move.h" // for std::move
16
#include "mozilla/RecordReplay.h" // for IsMiddleman
17
#include "mozilla/ScopeExit.h" // for MakeScopeExit, ScopeExit
18
#include "mozilla/ThreadLocal.h" // for ThreadLocal
19
#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
20
#include "mozilla/TypeTraits.h" // for RemoveConst<>::Type
21
#include "mozilla/UniquePtr.h" // for UniquePtr
22
#include "mozilla/Variant.h" // for AsVariant, AsVariantTemporary
23
#include "mozilla/Vector.h" // for Vector, Vector<>::ConstRange
24
25
#include <algorithm> // for std::find, std::max
26
#include <functional> // for function
27
#include <stddef.h> // for size_t
28
#include <stdint.h> // for uint32_t, uint64_t, int32_t
29
#include <string.h> // for strlen, strcmp
30
31
#include "jsapi.h" // for CallArgs, CallArgsFromVp
32
#include "jsfriendapi.h" // for GetErrorMessage
33
#include "jstypes.h" // for JS_PUBLIC_API
34
35
#include "builtin/Array.h" // for NewDenseFullyAllocatedArray
36
#include "builtin/Promise.h" // for PromiseObject
37
#include "debugger/DebugAPI.h" // for ResumeMode, DebugAPI
38
#include "debugger/DebuggerMemory.h" // for DebuggerMemory
39
#include "debugger/DebugScript.h" // for DebugScript
40
#include "debugger/Environment.h" // for DebuggerEnvironment
41
#include "debugger/Frame.h" // for DebuggerFrame
42
#include "debugger/NoExecute.h" // for EnterDebuggeeNoExecute
43
#include "debugger/Object.h" // for DebuggerObject
44
#include "debugger/Script.h" // for DebuggerScript
45
#include "debugger/Source.h" // for DebuggerSource
46
#include "frontend/BytecodeCompiler.h" // for CreateScriptSourceObject
47
#include "frontend/NameAnalysisTypes.h" // for ParseGoal, ParseGoal::Script
48
#include "frontend/ParseContext.h" // for UsedNameTracker
49
#include "frontend/Parser.h" // for Parser
50
#include "gc/Barrier.h" // for GCPtrNativeObject
51
#include "gc/FreeOp.h" // for JSFreeOp
52
#include "gc/GC.h" // for IterateLazyScripts
53
#include "gc/GCMarker.h" // for GCMarker
54
#include "gc/GCRuntime.h" // for GCRuntime, AutoEnterIteration
55
#include "gc/HashUtil.h" // for DependentAddPtr
56
#include "gc/Marking.h" // for IsMarkedUnbarriered, IsMarked
57
#include "gc/PublicIterators.h" // for RealmsIter, CompartmentsIter
58
#include "gc/Rooting.h" // for RootedNativeObject
59
#include "gc/Statistics.h" // for Statistics::SliceData
60
#include "gc/Tracer.h" // for TraceEdge
61
#include "gc/Zone.h" // for Zone
62
#include "gc/ZoneAllocator.h" // for ZoneAllocPolicy
63
#include "jit/BaselineDebugModeOSR.h" // for RecompileOnStackBaselineScriptsForDebugMode
64
#include "jit/BaselineJIT.h" // for FinishDiscardBaselineScript
65
#include "jit/Ion.h" // for JitContext
66
#include "jit/JitScript.h" // for JitScript
67
#include "jit/JSJitFrameIter.h" // for InlineFrameIterator
68
#include "jit/RematerializedFrame.h" // for RematerializedFrame
69
#include "js/Conversions.h" // for ToBoolean, ToUint32
70
#include "js/Debug.h" // for Builder::Object, Builder
71
#include "js/GCAPI.h" // for GarbageCollectionEvent
72
#include "js/HeapAPI.h" // for ExposeObjectToActiveJS
73
#include "js/Promise.h" // for AutoDebuggerJobQueueInterruption
74
#include "js/Proxy.h" // for PropertyDescriptor
75
#include "js/SourceText.h" // for SourceOwnership, SourceText
76
#include "js/StableStringChars.h" // for AutoStableStringChars
77
#include "js/UbiNode.h" // for Node, RootList, Edge
78
#include "js/UbiNodeBreadthFirst.h" // for BreadthFirst
79
#include "js/Warnings.h" // for AutoSuppressWarningReporter
80
#include "js/Wrapper.h" // for CheckedUnwrapStatic
81
#include "util/Text.h" // for DuplicateString, js_strlen
82
#include "vm/ArrayObject.h" // for ArrayObject
83
#include "vm/AsyncFunction.h" // for AsyncFunctionGeneratorObject
84
#include "vm/AsyncIteration.h" // for AsyncGeneratorObject
85
#include "vm/BytecodeUtil.h" // for JSDVG_IGNORE_STACK
86
#include "vm/Compartment.h" // for CrossCompartmentKey
87
#include "vm/EnvironmentObject.h" // for IsSyntacticEnvironment
88
#include "vm/ErrorReporting.h" // for ReportErrorToGlobal
89
#include "vm/GeneratorObject.h" // for AbstractGeneratorObject
90
#include "vm/GlobalObject.h" // for GlobalObject
91
#include "vm/Interpreter.h" // for Call, ReportIsNotFunction
92
#include "vm/Iteration.h" // for CreateIterResultObject
93
#include "vm/JSAtom.h" // for Atomize, ClassName
94
#include "vm/JSContext.h" // for JSContext
95
#include "vm/JSFunction.h" // for JSFunction
96
#include "vm/JSObject.h" // for JSObject, RequireObject
97
#include "vm/ObjectGroup.h" // for TenuredObject
98
#include "vm/ObjectOperations.h" // for DefineDataProperty
99
#include "vm/ProxyObject.h" // for ProxyObject, JSObject::is
100
#include "vm/Realm.h" // for AutoRealm, Realm
101
#include "vm/Runtime.h" // for ReportOutOfMemory, JSRuntime
102
#include "vm/SavedFrame.h" // for SavedFrame
103
#include "vm/SavedStacks.h" // for SavedStacks
104
#include "vm/Scope.h" // for Scope
105
#include "vm/StringType.h" // for JSString, PropertyName
106
#include "vm/TraceLogging.h" // for TraceLoggerForCurrentThread
107
#include "vm/TypeInference.h" // for TypeZone
108
#include "vm/WrapperObject.h" // for CrossCompartmentWrapperObject
109
#include "wasm/WasmDebug.h" // for DebugState
110
#include "wasm/WasmInstance.h" // for Instance
111
#include "wasm/WasmJS.h" // for WasmInstanceObject
112
#include "wasm/WasmRealm.h" // for Realm
113
#include "wasm/WasmTypes.h" // for WasmInstanceObjectVector
114
115
#include "debugger/DebugAPI-inl.h"
116
#include "debugger/Frame-inl.h" // for DebuggerFrame::hasGenerator
117
#include "debugger/Script-inl.h" // for DebuggerScript::getReferent
118
#include "gc/GC-inl.h" // for ZoneCellIter
119
#include "gc/Marking-inl.h" // for MaybeForwarded
120
#include "gc/WeakMap-inl.h" // for DebuggerWeakMap::trace
121
#include "vm/Compartment-inl.h" // for Compartment::wrap
122
#include "vm/GeckoProfiler-inl.h" // for AutoSuppressProfilerSampling
123
#include "vm/JSAtom-inl.h" // for AtomToId, ValueToId
124
#include "vm/JSContext-inl.h" // for JSContext::check
125
#include "vm/JSObject-inl.h" // for JSObject::isCallable
126
#include "vm/JSScript-inl.h" // for JSScript::isDebuggee, JSScript
127
#include "vm/NativeObject-inl.h" // for NativeObject::ensureDenseInitializedLength
128
#include "vm/ObjectOperations-inl.h" // for GetProperty, HasProperty
129
#include "vm/Realm-inl.h" // for AutoRealm::AutoRealm
130
#include "vm/Stack-inl.h" // for AbstractFramePtr::script
131
#include "vm/TypeInference-inl.h" // for AutoEnterAnalysis
132
133
namespace js {
134
135
namespace frontend {
136
class FullParseHandler;
137
}
138
139
namespace gc {
140
struct Cell;
141
}
142
143
namespace jit {
144
class BaselineFrame;
145
}
146
147
} /* namespace js */
148
149
using namespace js;
150
151
using JS::AutoStableStringChars;
152
using JS::CompileOptions;
153
using JS::SourceOwnership;
154
using JS::SourceText;
155
using JS::dbg::AutoEntryMonitor;
156
using JS::dbg::Builder;
157
using js::frontend::IsIdentifier;
158
using mozilla::AsVariant;
159
using mozilla::DebugOnly;
160
using mozilla::MakeScopeExit;
161
using mozilla::Maybe;
162
using mozilla::Nothing;
163
using mozilla::Some;
164
using mozilla::TimeDuration;
165
using mozilla::TimeStamp;
166
167
/*** Utils ******************************************************************/
168
169
bool js::IsInterpretedNonSelfHostedFunction(JSFunction* fun) {
170
return fun->isInterpreted() && !fun->isSelfHostedBuiltin();
171
}
172
173
bool js::EnsureFunctionHasScript(JSContext* cx, HandleFunction fun) {
174
if (fun->isInterpretedLazy()) {
175
AutoRealm ar(cx, fun);
176
return !!JSFunction::getOrCreateScript(cx, fun);
177
}
178
return true;
179
}
180
181
JSScript* js::GetOrCreateFunctionScript(JSContext* cx, HandleFunction fun) {
182
MOZ_ASSERT(IsInterpretedNonSelfHostedFunction(fun));
183
if (!EnsureFunctionHasScript(cx, fun)) {
184
return nullptr;
185
}
186
return fun->nonLazyScript();
187
}
188
189
bool js::ValueToIdentifier(JSContext* cx, HandleValue v, MutableHandleId id) {
190
if (!ValueToId<CanGC>(cx, v, id)) {
191
return false;
192
}
193
if (!JSID_IS_ATOM(id) || !IsIdentifier(JSID_TO_ATOM(id))) {
194
RootedValue val(cx, v);
195
ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, val,
196
nullptr, "not an identifier");
197
return false;
198
}
199
return true;
200
}
201
202
class js::AutoRestoreRealmDebugMode {
203
Realm* realm_;
204
unsigned bits_;
205
206
public:
207
explicit AutoRestoreRealmDebugMode(Realm* realm)
208
: realm_(realm), bits_(realm->debugModeBits_) {
209
MOZ_ASSERT(realm_);
210
}
211
212
~AutoRestoreRealmDebugMode() {
213
if (realm_) {
214
realm_->debugModeBits_ = bits_;
215
}
216
}
217
218
void release() { realm_ = nullptr; }
219
};
220
221
/* static */
222
bool DebugAPI::slowPathCheckNoExecute(JSContext* cx, HandleScript script) {
223
MOZ_ASSERT(cx->realm()->isDebuggee());
224
MOZ_ASSERT(cx->noExecuteDebuggerTop);
225
return EnterDebuggeeNoExecute::reportIfFoundInStack(cx, script);
226
}
227
228
static inline void NukeDebuggerWrapper(NativeObject* wrapper) {
229
// In some OOM failure cases, we need to destroy the edge to the referent,
230
// to avoid trying to trace it during untimely collections.
231
wrapper->setPrivate(nullptr);
232
}
233
234
bool js::ValueToStableChars(JSContext* cx, const char* fnname,
235
HandleValue value,
236
AutoStableStringChars& stableChars) {
237
if (!value.isString()) {
238
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
239
JSMSG_NOT_EXPECTED_TYPE, fnname, "string",
240
InformalValueTypeName(value));
241
return false;
242
}
243
RootedLinearString linear(cx, value.toString()->ensureLinear(cx));
244
if (!linear) {
245
return false;
246
}
247
if (!stableChars.initTwoByte(cx, linear)) {
248
return false;
249
}
250
return true;
251
}
252
253
bool EvalOptions::setFilename(JSContext* cx, const char* filename) {
254
JS::UniqueChars copy;
255
if (filename) {
256
copy = DuplicateString(cx, filename);
257
if (!copy) {
258
return false;
259
}
260
}
261
262
filename_ = std::move(copy);
263
return true;
264
}
265
266
bool js::ParseEvalOptions(JSContext* cx, HandleValue value,
267
EvalOptions& options) {
268
if (!value.isObject()) {
269
return true;
270
}
271
272
RootedObject opts(cx, &value.toObject());
273
274
RootedValue v(cx);
275
if (!JS_GetProperty(cx, opts, "url", &v)) {
276
return false;
277
}
278
if (!v.isUndefined()) {
279
RootedString url_str(cx, ToString<CanGC>(cx, v));
280
if (!url_str) {
281
return false;
282
}
283
UniqueChars url_bytes = JS_EncodeStringToLatin1(cx, url_str);
284
if (!url_bytes) {
285
return false;
286
}
287
if (!options.setFilename(cx, url_bytes.get())) {
288
return false;
289
}
290
}
291
292
if (!JS_GetProperty(cx, opts, "lineNumber", &v)) {
293
return false;
294
}
295
if (!v.isUndefined()) {
296
uint32_t lineno;
297
if (!ToUint32(cx, v, &lineno)) {
298
return false;
299
}
300
options.setLineno(lineno);
301
}
302
303
return true;
304
}
305
306
/*** Breakpoints ************************************************************/
307
308
bool BreakpointSite::isEmpty() const { return breakpoints.isEmpty(); }
309
310
void BreakpointSite::trace(JSTracer* trc) {
311
for (auto p = breakpoints.begin(); p; p++) {
312
p->trace(trc);
313
}
314
}
315
316
void BreakpointSite::finalize(JSFreeOp* fop) {
317
while (!breakpoints.isEmpty()) {
318
breakpoints.begin()->delete_(fop);
319
}
320
}
321
322
Breakpoint* BreakpointSite::firstBreakpoint() const {
323
if (isEmpty()) {
324
return nullptr;
325
}
326
return &(*breakpoints.begin());
327
}
328
329
bool BreakpointSite::hasBreakpoint(Breakpoint* toFind) {
330
const BreakpointList::Iterator bp(toFind);
331
for (auto p = breakpoints.begin(); p; p++) {
332
if (p == bp) {
333
return true;
334
}
335
}
336
return false;
337
}
338
339
Breakpoint::Breakpoint(Debugger* debugger, HandleObject wrappedDebugger,
340
BreakpointSite* site, HandleObject handler)
341
: debugger(debugger),
342
wrappedDebugger(wrappedDebugger),
343
site(site),
344
handler(handler) {
345
MOZ_ASSERT(UncheckedUnwrap(wrappedDebugger) == debugger->object);
346
MOZ_ASSERT(handler->compartment() == wrappedDebugger->compartment());
347
348
debugger->breakpoints.pushBack(this);
349
site->breakpoints.pushBack(this);
350
}
351
352
void Breakpoint::trace(JSTracer* trc) {
353
TraceEdge(trc, &wrappedDebugger, "breakpoint owner");
354
TraceEdge(trc, &handler, "breakpoint handler");
355
}
356
357
void Breakpoint::delete_(JSFreeOp* fop) {
358
debugger->breakpoints.remove(this);
359
site->breakpoints.remove(this);
360
gc::Cell* cell = site->owningCell();
361
fop->delete_(cell, this, MemoryUse::Breakpoint);
362
}
363
364
void Breakpoint::remove(JSFreeOp* fop) {
365
BreakpointSite* savedSite = site;
366
delete_(fop);
367
368
savedSite->destroyIfEmpty(fop);
369
}
370
371
Breakpoint* Breakpoint::nextInDebugger() { return debuggerLink.mNext; }
372
373
Breakpoint* Breakpoint::nextInSite() { return siteLink.mNext; }
374
375
JSBreakpointSite::JSBreakpointSite(JSScript* script, jsbytecode* pc)
376
: script(script), pc(pc) {
377
MOZ_ASSERT(!DebugAPI::hasBreakpointsAt(script, pc));
378
}
379
380
void JSBreakpointSite::remove(JSFreeOp* fop) {
381
DebugScript::destroyBreakpointSite(fop, script, pc);
382
}
383
384
void JSBreakpointSite::trace(JSTracer* trc) {
385
BreakpointSite::trace(trc);
386
TraceEdge(trc, &script, "breakpoint script");
387
}
388
389
void JSBreakpointSite::delete_(JSFreeOp* fop) {
390
BreakpointSite::finalize(fop);
391
392
fop->delete_(script, this, MemoryUse::BreakpointSite);
393
}
394
395
gc::Cell* JSBreakpointSite::owningCell() { return script; }
396
397
Realm* JSBreakpointSite::realm() const { return script->realm(); }
398
399
WasmBreakpointSite::WasmBreakpointSite(WasmInstanceObject* instanceObject_,
400
uint32_t offset_)
401
: instanceObject(instanceObject_), offset(offset_) {
402
MOZ_ASSERT(instanceObject_);
403
MOZ_ASSERT(instanceObject_->instance().debugEnabled());
404
}
405
406
void WasmBreakpointSite::trace(JSTracer* trc) {
407
BreakpointSite::trace(trc);
408
TraceEdge(trc, &instanceObject, "breakpoint Wasm instance");
409
}
410
411
void WasmBreakpointSite::remove(JSFreeOp* fop) {
412
instanceObject->instance().destroyBreakpointSite(fop, offset);
413
}
414
415
void WasmBreakpointSite::delete_(JSFreeOp* fop) {
416
BreakpointSite::finalize(fop);
417
418
fop->delete_(instanceObject, this, MemoryUse::BreakpointSite);
419
}
420
421
gc::Cell* WasmBreakpointSite::owningCell() { return instanceObject; }
422
423
Realm* WasmBreakpointSite::realm() const { return instanceObject->realm(); }
424
425
/*** Debugger hook dispatch *************************************************/
426
427
Debugger::Debugger(JSContext* cx, NativeObject* dbg)
428
: object(dbg),
429
debuggees(cx->zone()),
430
uncaughtExceptionHook(nullptr),
431
allowUnobservedAsmJS(false),
432
collectCoverageInfo(false),
433
observedGCs(cx->zone()),
434
allocationsLog(cx),
435
trackingAllocationSites(false),
436
allocationSamplingProbability(1.0),
437
maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH),
438
allocationsLogOverflowed(false),
439
frames(cx->zone()),
440
generatorFrames(cx),
441
scripts(cx),
442
lazyScripts(cx),
443
sources(cx),
444
objects(cx),
445
environments(cx),
446
wasmInstanceScripts(cx),
447
wasmInstanceSources(cx),
448
#ifdef NIGHTLY_BUILD
449
traceLoggerLastDrainedSize(0),
450
traceLoggerLastDrainedIteration(0),
451
#endif
452
traceLoggerScriptedCallsLastDrainedSize(0),
453
traceLoggerScriptedCallsLastDrainedIteration(0) {
454
cx->check(dbg);
455
456
#ifdef JS_TRACE_LOGGING
457
TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
458
if (logger) {
459
# ifdef NIGHTLY_BUILD
460
logger->getIterationAndSize(&traceLoggerLastDrainedIteration,
461
&traceLoggerLastDrainedSize);
462
# endif
463
logger->getIterationAndSize(&traceLoggerScriptedCallsLastDrainedIteration,
464
&traceLoggerScriptedCallsLastDrainedSize);
465
}
466
#endif
467
468
cx->runtime()->debuggerList().insertBack(this);
469
}
470
471
Debugger::~Debugger() {
472
MOZ_ASSERT(debuggees.empty());
473
allocationsLog.clear();
474
475
// Breakpoints should hold us alive, so any breakpoints remaining must be set
476
// in dying JSScripts. We should clean them up, but this never asserts. I'm
477
// not sure why.
478
MOZ_ASSERT(breakpoints.isEmpty());
479
480
// We don't have to worry about locking here since Debugger is not
481
// background finalized.
482
JSContext* cx = TlsContext.get();
483
if (onNewGlobalObjectWatchersLink.mPrev ||
484
onNewGlobalObjectWatchersLink.mNext ||
485
cx->runtime()->onNewGlobalObjectWatchers().begin() ==
486
JSRuntime::WatchersList::Iterator(this)) {
487
cx->runtime()->onNewGlobalObjectWatchers().remove(this);
488
}
489
}
490
491
JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
492
unsigned(DebuggerScript::OWNER_SLOT));
493
JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
494
unsigned(DebuggerSource::OWNER_SLOT));
495
JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
496
unsigned(JSSLOT_DEBUGOBJECT_OWNER));
497
JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
498
unsigned(DebuggerEnvironment::OWNER_SLOT));
499
500
#ifdef DEBUG
501
/* static */
502
bool Debugger::isChildJSObject(JSObject* obj) {
503
return obj->getClass() == &DebuggerFrame::class_ ||
504
obj->getClass() == &DebuggerScript::class_ ||
505
obj->getClass() == &DebuggerSource::class_ ||
506
obj->getClass() == &DebuggerObject::class_ ||
507
obj->getClass() == &DebuggerEnvironment::class_;
508
}
509
#endif
510
511
/* static */
512
Debugger* Debugger::fromChildJSObject(JSObject* obj) {
513
MOZ_ASSERT(isChildJSObject(obj));
514
JSObject* dbgobj = &obj->as<NativeObject>()
515
.getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER)
516
.toObject();
517
return fromJSObject(dbgobj);
518
}
519
520
bool Debugger::hasMemory() const {
521
return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE).isObject();
522
}
523
524
DebuggerMemory& Debugger::memory() const {
525
MOZ_ASSERT(hasMemory());
526
return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE)
527
.toObject()
528
.as<DebuggerMemory>();
529
}
530
531
/*** Debugger accessors *******************************************************/
532
533
bool Debugger::getFrame(JSContext* cx, const FrameIter& iter,
534
MutableHandleValue vp) {
535
RootedDebuggerFrame result(cx);
536
if (!Debugger::getFrame(cx, iter, &result)) {
537
return false;
538
}
539
vp.setObject(*result);
540
return true;
541
}
542
543
bool Debugger::getFrame(JSContext* cx, const FrameIter& iter,
544
MutableHandleDebuggerFrame result) {
545
AbstractFramePtr referent = iter.abstractFramePtr();
546
MOZ_ASSERT_IF(referent.hasScript(), !referent.script()->selfHosted());
547
548
if (referent.hasScript() &&
549
!referent.script()->ensureHasAnalyzedArgsUsage(cx)) {
550
return false;
551
}
552
553
FrameMap::AddPtr p = frames.lookupForAdd(referent);
554
if (!p) {
555
RootedDebuggerFrame frame(cx);
556
557
// If this is a generator frame, there may be an existing
558
// Debugger.Frame object that isn't in `frames` because the generator
559
// was suspended, popping the stack frame, and later resumed (and we
560
// were not stepping, so did not pass through slowPathOnResumeFrame).
561
Rooted<AbstractGeneratorObject*> genObj(cx);
562
GeneratorWeakMap::AddPtr gp;
563
if (referent.isGeneratorFrame()) {
564
{
565
AutoRealm ar(cx, referent.callee());
566
genObj = GetGeneratorObjectForFrame(cx, referent);
567
}
568
if (genObj) {
569
gp = generatorFrames.lookupForAdd(genObj);
570
if (gp) {
571
frame = &gp->value()->as<DebuggerFrame>();
572
MOZ_ASSERT(&frame->unwrappedGenerator() == genObj);
573
574
// We have found an existing Debugger.Frame object. But
575
// since it was previously popped (see comment above), it
576
// is not currently "live". We must revive it.
577
if (!frame->resume(iter)) {
578
return false;
579
}
580
if (!ensureExecutionObservabilityOfFrame(cx, referent)) {
581
return false;
582
}
583
}
584
}
585
586
// If no AbstractGeneratorObject exists yet, we create a Debugger.Frame
587
// below anyway, and Debugger::onNewGenerator() will associate it
588
// with the AbstractGeneratorObject later when we hit JSOP_GENERATOR.
589
}
590
591
if (!frame) {
592
// Create and populate the Debugger.Frame object.
593
RootedObject proto(
594
cx, &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject());
595
RootedNativeObject debugger(cx, object);
596
597
frame = DebuggerFrame::create(cx, proto, iter, debugger);
598
if (!frame) {
599
return false;
600
}
601
602
if (!ensureExecutionObservabilityOfFrame(cx, referent)) {
603
return false;
604
}
605
606
if (genObj) {
607
if (!frame->setGenerator(cx, genObj)) {
608
return false;
609
}
610
}
611
}
612
613
if (!frames.add(p, referent, frame)) {
614
frame->freeFrameIterData(cx->defaultFreeOp());
615
frame->clearGenerator(cx->runtime()->defaultFreeOp(), this);
616
ReportOutOfMemory(cx);
617
return false;
618
}
619
}
620
621
result.set(&p->value()->as<DebuggerFrame>());
622
return true;
623
}
624
625
static bool DebuggerExists(
626
GlobalObject* global, const std::function<bool(Debugger* dbg)>& predicate) {
627
// The GC analysis can't determine that the predicate can't GC, so let it know
628
// explicitly.
629
JS::AutoSuppressGCAnalysis nogc;
630
631
for (Realm::DebuggerVectorEntry& entry : global->getDebuggers()) {
632
// Callbacks should not create new references to the debugger, so don't
633
// use a barrier. This allows this method to be called during GC.
634
if (predicate(entry.dbg.unbarrieredGet())) {
635
return true;
636
}
637
}
638
return false;
639
}
640
641
/* static */
642
bool Debugger::hasLiveHook(GlobalObject* global, Hook which) {
643
return DebuggerExists(global,
644
[=](Debugger* dbg) { return dbg->getHook(which); });
645
}
646
647
/* static */
648
bool DebugAPI::debuggerObservesAllExecution(GlobalObject* global) {
649
return DebuggerExists(
650
global, [=](Debugger* dbg) { return dbg->observesAllExecution(); });
651
}
652
653
/* static */
654
bool DebugAPI::debuggerObservesCoverage(GlobalObject* global) {
655
return DebuggerExists(global,
656
[=](Debugger* dbg) { return dbg->observesCoverage(); });
657
}
658
659
/* static */
660
bool DebugAPI::debuggerObservesAsmJS(GlobalObject* global) {
661
return DebuggerExists(global,
662
[=](Debugger* dbg) { return dbg->observesAsmJS(); });
663
}
664
665
/* static */
666
bool DebugAPI::hasExceptionUnwindHook(GlobalObject* global) {
667
return Debugger::hasLiveHook(global, Debugger::OnExceptionUnwind);
668
}
669
670
/* static */
671
bool DebugAPI::hasDebuggerStatementHook(GlobalObject* global) {
672
return Debugger::hasLiveHook(global, Debugger::OnDebuggerStatement);
673
}
674
675
JSObject* Debugger::getHook(Hook hook) const {
676
MOZ_ASSERT(hook >= 0 && hook < HookCount);
677
const Value& v = object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + hook);
678
return v.isUndefined() ? nullptr : &v.toObject();
679
}
680
681
bool Debugger::hasAnyLiveHooks() const {
682
// A onNewGlobalObject hook does not hold its Debugger live, so its behavior
683
// is nondeterministic. This behavior is not satisfying, but it is at least
684
// documented.
685
if (getHook(OnDebuggerStatement) || getHook(OnExceptionUnwind) ||
686
getHook(OnNewScript) || getHook(OnEnterFrame)) {
687
return true;
688
}
689
690
return false;
691
}
692
693
/* static */
694
ResumeMode DebugAPI::slowPathOnEnterFrame(JSContext* cx,
695
AbstractFramePtr frame) {
696
RootedValue rval(cx);
697
ResumeMode resumeMode = Debugger::dispatchHook(
698
cx,
699
[frame](Debugger* dbg) -> bool {
700
return dbg->observesFrame(frame) && dbg->observesEnterFrame();
701
},
702
[&](Debugger* dbg) -> ResumeMode {
703
return dbg->fireEnterFrame(cx, &rval);
704
});
705
706
switch (resumeMode) {
707
case ResumeMode::Continue:
708
break;
709
710
case ResumeMode::Throw:
711
cx->setPendingExceptionAndCaptureStack(rval);
712
break;
713
714
case ResumeMode::Terminate:
715
cx->clearPendingException();
716
break;
717
718
case ResumeMode::Return:
719
frame.setReturnValue(rval);
720
break;
721
722
default:
723
MOZ_CRASH("bad Debugger::onEnterFrame resume mode");
724
}
725
726
return resumeMode;
727
}
728
729
/* static */
730
ResumeMode DebugAPI::slowPathOnResumeFrame(JSContext* cx,
731
AbstractFramePtr frame) {
732
// Don't count on this method to be called every time a generator is
733
// resumed! This is called only if the frame's debuggee bit is set,
734
// i.e. the script has breakpoints or the frame is stepping.
735
MOZ_ASSERT(frame.isGeneratorFrame());
736
MOZ_ASSERT(frame.isDebuggee());
737
738
Rooted<AbstractGeneratorObject*> genObj(
739
cx, GetGeneratorObjectForFrame(cx, frame));
740
MOZ_ASSERT(genObj);
741
742
// For each debugger, if there is an existing Debugger.Frame object for the
743
// resumed `frame`, update it with the new frame pointer and make sure the
744
// frame is observable.
745
for (Realm::DebuggerVectorEntry& entry : frame.global()->getDebuggers()) {
746
Debugger* dbg = entry.dbg;
747
if (Debugger::GeneratorWeakMap::Ptr generatorEntry =
748
dbg->generatorFrames.lookup(genObj)) {
749
DebuggerFrame* frameObj = &generatorEntry->value()->as<DebuggerFrame>();
750
MOZ_ASSERT(&frameObj->unwrappedGenerator() == genObj);
751
if (!dbg->frames.putNew(frame, frameObj)) {
752
ReportOutOfMemory(cx);
753
return ResumeMode::Throw;
754
}
755
756
FrameIter iter(cx);
757
MOZ_ASSERT(iter.abstractFramePtr() == frame);
758
if (!frameObj->resume(iter)) {
759
return ResumeMode::Throw;
760
}
761
if (!Debugger::ensureExecutionObservabilityOfFrame(cx, frame)) {
762
return ResumeMode::Throw;
763
}
764
}
765
}
766
767
return slowPathOnEnterFrame(cx, frame);
768
}
769
770
/* static */
771
ResumeMode DebugAPI::slowPathOnNativeCall(JSContext* cx, const CallArgs& args,
772
CallReason reason) {
773
RootedValue rval(cx);
774
ResumeMode resumeMode = Debugger::dispatchHook(
775
cx,
776
[cx](Debugger* dbg) -> bool {
777
return dbg == cx->insideDebuggerEvaluationWithOnNativeCallHook &&
778
dbg->getHook(Debugger::OnNativeCall);
779
},
780
[&](Debugger* dbg) -> ResumeMode {
781
return dbg->fireNativeCall(cx, args, reason, &rval);
782
});
783
784
switch (resumeMode) {
785
case ResumeMode::Continue:
786
break;
787
788
case ResumeMode::Throw:
789
cx->setPendingExceptionAndCaptureStack(rval);
790
break;
791
792
case ResumeMode::Terminate:
793
cx->clearPendingException();
794
break;
795
796
case ResumeMode::Return:
797
args.rval().set(rval);
798
break;
799
}
800
801
return resumeMode;
802
}
803
804
/*
805
* RAII class to mark a generator as "running" temporarily while running
806
* debugger code.
807
*
808
* When Debugger::slowPathOnLeaveFrame is called for a frame that is yielding
809
* or awaiting, its generator is in the "suspended" state. Letting script
810
* observe this state, with the generator on stack yet also reenterable, would
811
* be bad, so we mark it running while we fire events.
812
*/
813
class MOZ_RAII AutoSetGeneratorRunning {
814
int32_t resumeIndex_;
815
AsyncGeneratorObject::State asyncGenState_;
816
Rooted<AbstractGeneratorObject*> genObj_;
817
818
public:
819
AutoSetGeneratorRunning(JSContext* cx,
820
Handle<AbstractGeneratorObject*> genObj)
821
: resumeIndex_(0),
822
asyncGenState_(static_cast<AsyncGeneratorObject::State>(0)),
823
genObj_(cx, genObj) {
824
if (genObj) {
825
if (!genObj->isClosed() && !genObj->isBeforeInitialYield() &&
826
genObj->isSuspended()) {
827
// Yielding or awaiting.
828
resumeIndex_ = genObj->resumeIndex();
829
genObj->setRunning();
830
831
// Async generators have additionally bookkeeping which must be
832
// adjusted when switching over to the running state.
833
if (genObj->is<AsyncGeneratorObject>()) {
834
auto* asyncGenObj = &genObj->as<AsyncGeneratorObject>();
835
asyncGenState_ = asyncGenObj->state();
836
asyncGenObj->setExecuting();
837
}
838
} else {
839
// Returning or throwing. The generator is already closed, if
840
// it was ever exposed at all.
841
genObj_ = nullptr;
842
}
843
}
844
}
845
846
~AutoSetGeneratorRunning() {
847
if (genObj_) {
848
MOZ_ASSERT(genObj_->isRunning());
849
genObj_->setResumeIndex(resumeIndex_);
850
if (genObj_->is<AsyncGeneratorObject>()) {
851
genObj_->as<AsyncGeneratorObject>().setState(asyncGenState_);
852
}
853
}
854
}
855
};
856
857
/*
858
* Handle leaving a frame with debuggers watching. |frameOk| indicates whether
859
* the frame is exiting normally or abruptly. Set |cx|'s exception and/or
860
* |cx->fp()|'s return value, and return a new success value.
861
*/
862
/* static */
863
bool DebugAPI::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame,
864
jsbytecode* pc, bool frameOk) {
865
MOZ_ASSERT_IF(!frame.isWasmDebugFrame(), pc);
866
867
mozilla::DebugOnly<Handle<GlobalObject*>> debuggeeGlobal = cx->global();
868
869
// These are updated below, but consulted by the cleanup code we register now,
870
// so declare them here, initialized to quiescent values.
871
Rooted<Completion> completion(cx);
872
bool success = false;
873
874
auto frameMapsGuard = MakeScopeExit([&] {
875
// Clean up all Debugger.Frame instances on exit. On suspending, pass the
876
// flag that says to leave those frames `.live`. Note that if the completion
877
// is a suspension but success is false, the generator gets closed, not
878
// suspended.
879
Debugger::removeFromFrameMapsAndClearBreakpointsIn(
880
cx, frame, success && completion.get().suspending());
881
});
882
883
// The onPop handler and associated clean up logic should not run multiple
884
// times on the same frame. If slowPathOnLeaveFrame has already been
885
// called, the frame will not be present in the Debugger frame maps.
886
Rooted<Debugger::DebuggerFrameVector> frames(
887
cx, Debugger::DebuggerFrameVector(cx));
888
if (!Debugger::getDebuggerFrames(frame, &frames)) {
889
return false;
890
}
891
if (frames.empty()) {
892
return frameOk;
893
}
894
895
completion = Completion::fromJSFramePop(cx, frame, pc, frameOk);
896
Rooted<AbstractGeneratorObject*> genObj(
897
cx, completion.get().maybeGeneratorObject());
898
899
// Preserve the debuggee's microtask event queue while we run the hooks, so
900
// the debugger's microtask checkpoints don't run from the debuggee's
901
// microtasks, and vice versa.
902
JS::AutoDebuggerJobQueueInterruption adjqi;
903
if (!adjqi.init(cx)) {
904
return false;
905
}
906
907
// This path can be hit via unwinding the stack due to over-recursion or
908
// OOM. In those cases, don't fire the frames' onPop handlers, because
909
// invoking JS will only trigger the same condition. See
910
// slowPathOnExceptionUnwind.
911
if (!cx->isThrowingOverRecursed() && !cx->isThrowingOutOfMemory()) {
912
// For each Debugger.Frame, fire its onPop handler, if any.
913
for (size_t i = 0; i < frames.length(); i++) {
914
HandleDebuggerFrame frameobj = frames[i];
915
Debugger* dbg = Debugger::fromChildJSObject(frameobj);
916
EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
917
918
if (frameobj->isOnStack() && frameobj->onPopHandler()) {
919
OnPopHandler* handler = frameobj->onPopHandler();
920
921
Maybe<AutoRealm> ar;
922
ar.emplace(cx, dbg->object);
923
924
// The resumption requested by the onPop handler we're about to call.
925
ResumeMode nextResumeMode;
926
RootedValue nextValue(cx);
927
928
// Call the onPop handler.
929
bool success;
930
{
931
// Mark the generator as running, to prevent reentrance.
932
//
933
// At certain points in a generator's lifetime,
934
// GetGeneratorObjectForFrame can return null even when the generator
935
// exists, but at those points the generator has not yet been exposed
936
// to JavaScript, so reentrance isn't possible anyway. So there's no
937
// harm done if this has no effect in that case.
938
AutoSetGeneratorRunning asgr(cx, genObj);
939
success = handler->onPop(cx, frameobj, completion, nextResumeMode,
940
&nextValue);
941
}
942
nextResumeMode = dbg->processParsedHandlerResult(
943
ar, frame, pc, success, nextResumeMode, &nextValue);
944
adjqi.runJobs();
945
946
// At this point, we are back in the debuggee compartment, and
947
// any error has been wrapped up as a completion value.
948
MOZ_ASSERT(cx->compartment() == debuggeeGlobal->compartment());
949
MOZ_ASSERT(!cx->isExceptionPending());
950
951
completion.get().updateForNextHandler(nextResumeMode, nextValue);
952
}
953
}
954
}
955
956
// Now that we've run all the handlers, extract the final resumption mode. */
957
ResumeMode resumeMode;
958
RootedValue value(cx);
959
RootedSavedFrame exnStack(cx);
960
completion.get().toResumeMode(resumeMode, &value, &exnStack);
961
962
switch (resumeMode) {
963
case ResumeMode::Return:
964
frame.setReturnValue(value);
965
success = true;
966
return true;
967
968
case ResumeMode::Throw:
969
// If we have a stack from the original throw, use it instead of
970
// associating the throw with the current execution point.
971
if (exnStack) {
972
cx->setPendingException(value, exnStack);
973
} else {
974
cx->setPendingExceptionAndCaptureStack(value);
975
}
976
return false;
977
978
case ResumeMode::Terminate:
979
MOZ_ASSERT(!cx->isExceptionPending());
980
return false;
981
982
default:
983
MOZ_CRASH("bad final onLeaveFrame resume mode");
984
}
985
}
986
987
/* static */
988
bool DebugAPI::slowPathOnNewGenerator(JSContext* cx, AbstractFramePtr frame,
989
Handle<AbstractGeneratorObject*> genObj) {
990
// This is called from JSOP_GENERATOR, after default parameter expressions
991
// are evaluated and well after onEnterFrame, so Debugger.Frame objects for
992
// `frame` may already have been exposed to debugger code. The
993
// AbstractGeneratorObject for this generator call, though, has just been
994
// created. It must be associated with any existing Debugger.Frames.
995
bool ok = true;
996
Debugger::forEachDebuggerFrame(frame, [&](DebuggerFrame* frameObjPtr) {
997
if (!ok) {
998
return;
999
}
1000
1001
RootedDebuggerFrame frameObj(cx, frameObjPtr);
1002
{
1003
AutoRealm ar(cx, frameObj);
1004
1005
if (!frameObj->setGenerator(cx, genObj)) {
1006
ReportOutOfMemory(cx);
1007
1008
// This leaves `genObj` and `frameObj` unassociated. It's OK
1009
// because we won't pause again with this generator on the stack:
1010
// the caller will immediately discard `genObj` and unwind `frame`.
1011
ok = false;
1012
}
1013
}
1014
});
1015
return ok;
1016
}
1017
1018
/* static */
1019
ResumeMode DebugAPI::slowPathOnDebuggerStatement(JSContext* cx,
1020
AbstractFramePtr frame) {
1021
RootedValue rval(cx);
1022
ResumeMode resumeMode = Debugger::dispatchHook(
1023
cx,
1024
[](Debugger* dbg) -> bool {
1025
return dbg->getHook(Debugger::OnDebuggerStatement);
1026
},
1027
[&](Debugger* dbg) -> ResumeMode {
1028
return dbg->fireDebuggerStatement(cx, &rval);
1029
});
1030
1031
switch (resumeMode) {
1032
case ResumeMode::Continue:
1033
case ResumeMode::Terminate:
1034
break;
1035
1036
case ResumeMode::Return:
1037
frame.setReturnValue(rval);
1038
break;
1039
1040
case ResumeMode::Throw:
1041
cx->setPendingExceptionAndCaptureStack(rval);
1042
break;
1043
1044
default:
1045
MOZ_CRASH("Invalid onDebuggerStatement resume mode");
1046
}
1047
1048
return resumeMode;
1049
}
1050
1051
/* static */
1052
ResumeMode DebugAPI::slowPathOnExceptionUnwind(JSContext* cx,
1053
AbstractFramePtr frame) {
1054
// Invoking more JS on an over-recursed stack or after OOM is only going
1055
// to result in more of the same error.
1056
if (cx->isThrowingOverRecursed() || cx->isThrowingOutOfMemory()) {
1057
return ResumeMode::Continue;
1058
}
1059
1060
// The Debugger API mustn't muck with frames from self-hosted scripts.
1061
if (frame.hasScript() && frame.script()->selfHosted()) {
1062
return ResumeMode::Continue;
1063
}
1064
1065
RootedValue rval(cx);
1066
ResumeMode resumeMode = Debugger::dispatchHook(
1067
cx,
1068
[](Debugger* dbg) -> bool {
1069
return dbg->getHook(Debugger::OnExceptionUnwind);
1070
},
1071
[&](Debugger* dbg) -> ResumeMode {
1072
return dbg->fireExceptionUnwind(cx, &rval);
1073
});
1074
1075
switch (resumeMode) {
1076
case ResumeMode::Continue:
1077
break;
1078
1079
case ResumeMode::Throw:
1080
cx->setPendingExceptionAndCaptureStack(rval);
1081
break;
1082
1083
case ResumeMode::Terminate:
1084
cx->clearPendingException();
1085
break;
1086
1087
case ResumeMode::Return:
1088
cx->clearPendingException();
1089
frame.setReturnValue(rval);
1090
break;
1091
1092
default:
1093
MOZ_CRASH("Invalid onExceptionUnwind resume mode");
1094
}
1095
1096
return resumeMode;
1097
}
1098
1099
// TODO: Remove Remove this function when all properties/methods returning a
1100
/// DebuggerEnvironment have been given a C++ interface (bug 1271649).
1101
bool Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env,
1102
MutableHandleValue rval) {
1103
if (!env) {
1104
rval.setNull();
1105
return true;
1106
}
1107
1108
RootedDebuggerEnvironment envobj(cx);
1109
1110
if (!wrapEnvironment(cx, env, &envobj)) {
1111
return false;
1112
}
1113
1114
rval.setObject(*envobj);
1115
return true;
1116
}
1117
1118
bool Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env,
1119
MutableHandleDebuggerEnvironment result) {
1120
MOZ_ASSERT(env);
1121
1122
// DebuggerEnv should only wrap a debug scope chain obtained (transitively)
1123
// from GetDebugEnvironmentFor(Frame|Function).
1124
MOZ_ASSERT(!IsSyntacticEnvironment(env));
1125
1126
DependentAddPtr<EnvironmentWeakMap> p(cx, environments, env);
1127
if (p) {
1128
result.set(&p->value()->as<DebuggerEnvironment>());
1129
} else {
1130
// Create a new Debugger.Environment for env.
1131
RootedObject proto(
1132
cx, &object->getReservedSlot(JSSLOT_DEBUG_ENV_PROTO).toObject());
1133
RootedNativeObject debugger(cx, object);
1134
1135
RootedDebuggerEnvironment envobj(
1136
cx, DebuggerEnvironment::create(cx, proto, env, debugger));
1137
if (!envobj) {
1138
return false;
1139
}
1140
1141
if (!p.add(cx, environments, env, envobj)) {
1142
NukeDebuggerWrapper(envobj);
1143
return false;
1144
}
1145
1146
result.set(envobj);
1147
}
1148
1149
return true;
1150
}
1151
1152
bool Debugger::wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp) {
1153
cx->check(object.get());
1154
1155
if (vp.isObject()) {
1156
RootedObject obj(cx, &vp.toObject());
1157
RootedDebuggerObject dobj(cx);
1158
1159
if (!wrapDebuggeeObject(cx, obj, &dobj)) {
1160
return false;
1161
}
1162
1163
vp.setObject(*dobj);
1164
} else if (vp.isMagic()) {
1165
RootedPlainObject optObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
1166
if (!optObj) {
1167
return false;
1168
}
1169
1170
// We handle three sentinel values: missing arguments (overloading
1171
// JS_OPTIMIZED_ARGUMENTS), optimized out slots (JS_OPTIMIZED_OUT),
1172
// and uninitialized bindings (JS_UNINITIALIZED_LEXICAL).
1173
//
1174
// Other magic values should not have escaped.
1175
PropertyName* name;
1176
switch (vp.whyMagic()) {
1177
case JS_OPTIMIZED_ARGUMENTS:
1178
name = cx->names().missingArguments;
1179
break;
1180
case JS_OPTIMIZED_OUT:
1181
name = cx->names().optimizedOut;
1182
break;
1183
case JS_UNINITIALIZED_LEXICAL:
1184
name = cx->names().uninitialized;
1185
break;
1186
default:
1187
MOZ_CRASH("Unsupported magic value escaped to Debugger");
1188
}
1189
1190
RootedValue trueVal(cx, BooleanValue(true));
1191
if (!DefineDataProperty(cx, optObj, name, trueVal)) {
1192
return false;
1193
}
1194
1195
vp.setObject(*optObj);
1196
} else if (!cx->compartment()->wrap(cx, vp)) {
1197
vp.setUndefined();
1198
return false;
1199
}
1200
1201
return true;
1202
}
1203
1204
bool Debugger::wrapNullableDebuggeeObject(JSContext* cx, HandleObject obj,
1205
MutableHandleDebuggerObject result) {
1206
if (!obj) {
1207
result.set(nullptr);
1208
return true;
1209
}
1210
1211
return wrapDebuggeeObject(cx, obj, result);
1212
}
1213
1214
bool Debugger::wrapDebuggeeObject(JSContext* cx, HandleObject obj,
1215
MutableHandleDebuggerObject result) {
1216
MOZ_ASSERT(obj);
1217
1218
DependentAddPtr<ObjectWeakMap> p(cx, objects, obj);
1219
if (p) {
1220
result.set(&p->value()->as<DebuggerObject>());
1221
} else {
1222
// Create a new Debugger.Object for obj.
1223
RootedNativeObject debugger(cx, object);
1224
RootedObject proto(
1225
cx, &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject());
1226
RootedDebuggerObject dobj(cx,
1227
DebuggerObject::create(cx, proto, obj, debugger));
1228
if (!dobj) {
1229
return false;
1230
}
1231
1232
if (!p.add(cx, objects, obj, dobj)) {
1233
NukeDebuggerWrapper(dobj);
1234
return false;
1235
}
1236
1237
result.set(dobj);
1238
}
1239
1240
return true;
1241
}
1242
1243
static NativeObject* ToNativeDebuggerObject(JSContext* cx,
1244
MutableHandleObject obj) {
1245
if (obj->getClass() != &DebuggerObject::class_) {
1246
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1247
JSMSG_NOT_EXPECTED_TYPE, "Debugger",
1248
"Debugger.Object", obj->getClass()->name);
1249
return nullptr;
1250
}
1251
1252
NativeObject* ndobj = &obj->as<NativeObject>();
1253
1254
Value owner = ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);
1255
if (owner.isUndefined()) {
1256
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_PROTO,
1257
"Debugger.Object", "Debugger.Object");
1258
return nullptr;
1259
}
1260
1261
return ndobj;
1262
}
1263
1264
bool Debugger::unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj) {
1265
NativeObject* ndobj = ToNativeDebuggerObject(cx, obj);
1266
if (!ndobj) {
1267
return false;
1268
}
1269
1270
Value owner = ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);
1271
if (&owner.toObject() != object) {
1272
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1273
JSMSG_DEBUG_WRONG_OWNER, "Debugger.Object");
1274
return false;
1275
}
1276
1277
obj.set(static_cast<JSObject*>(ndobj->getPrivate()));
1278
return true;
1279
}
1280
1281
bool Debugger::unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp) {
1282
cx->check(object.get(), vp);
1283
if (vp.isObject()) {
1284
RootedObject dobj(cx, &vp.toObject());
1285
if (!unwrapDebuggeeObject(cx, &dobj)) {
1286
return false;
1287
}
1288
vp.setObject(*dobj);
1289
}
1290
return true;
1291
}
1292
1293
static bool CheckArgCompartment(JSContext* cx, JSObject* obj, JSObject* arg,
1294
const char* methodname, const char* propname) {
1295
if (arg->compartment() != obj->compartment()) {
1296
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1297
JSMSG_DEBUG_COMPARTMENT_MISMATCH, methodname,
1298
propname);
1299
return false;
1300
}
1301
return true;
1302
}
1303
1304
static bool CheckArgCompartment(JSContext* cx, JSObject* obj, HandleValue v,
1305
const char* methodname, const char* propname) {
1306
if (v.isObject()) {
1307
return CheckArgCompartment(cx, obj, &v.toObject(), methodname, propname);
1308
}
1309
return true;
1310
}
1311
1312
bool Debugger::unwrapPropertyDescriptor(
1313
JSContext* cx, HandleObject obj, MutableHandle<PropertyDescriptor> desc) {
1314
if (desc.hasValue()) {
1315
RootedValue value(cx, desc.value());
1316
if (!unwrapDebuggeeValue(cx, &value) ||
1317
!CheckArgCompartment(cx, obj, value, "defineProperty", "value")) {
1318
return false;
1319
}
1320
desc.setValue(value);
1321
}
1322
1323
if (desc.hasGetterObject()) {
1324
RootedObject get(cx, desc.getterObject());
1325
if (get) {
1326
if (!unwrapDebuggeeObject(cx, &get)) {
1327
return false;
1328
}
1329
if (!CheckArgCompartment(cx, obj, get, "defineProperty", "get")) {
1330
return false;
1331
}
1332
}
1333
desc.setGetterObject(get);
1334
}
1335
1336
if (desc.hasSetterObject()) {
1337
RootedObject set(cx, desc.setterObject());
1338
if (set) {
1339
if (!unwrapDebuggeeObject(cx, &set)) {
1340
return false;
1341
}
1342
if (!CheckArgCompartment(cx, obj, set, "defineProperty", "set")) {
1343
return false;
1344
}
1345
}
1346
desc.setSetterObject(set);
1347
}
1348
1349
return true;
1350
}
1351
1352
/*** Debuggee resumption values and debugger error handling *****************/
1353
1354
static bool GetResumptionProperty(JSContext* cx, HandleObject obj,
1355
HandlePropertyName name, ResumeMode namedMode,
1356
ResumeMode& resumeMode, MutableHandleValue vp,
1357
int* hits) {
1358
bool found;
1359
if (!HasProperty(cx, obj, name, &found)) {
1360
return false;
1361
}
1362
if (found) {
1363
++*hits;
1364
resumeMode = namedMode;
1365
if (!GetProperty(cx, obj, obj, name, vp)) {
1366
return false;
1367
}
1368
}
1369
return true;
1370
}
1371
1372
bool js::ParseResumptionValue(JSContext* cx, HandleValue rval,
1373
ResumeMode& resumeMode, MutableHandleValue vp) {
1374
if (rval.isUndefined()) {
1375
resumeMode = ResumeMode::Continue;
1376
vp.setUndefined();
1377
return true;
1378
}
1379
if (rval.isNull()) {
1380
resumeMode = ResumeMode::Terminate;
1381
vp.setUndefined();
1382
return true;
1383
}
1384
1385
int hits = 0;
1386
if (rval.isObject()) {
1387
RootedObject obj(cx, &rval.toObject());
1388
if (!GetResumptionProperty(cx, obj, cx->names().return_, ResumeMode::Return,
1389
resumeMode, vp, &hits)) {
1390
return false;
1391
}
1392
if (!GetResumptionProperty(cx, obj, cx->names().throw_, ResumeMode::Throw,
1393
resumeMode, vp, &hits)) {
1394
return false;
1395
}
1396
}
1397
1398
if (hits != 1) {
1399
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1400
JSMSG_DEBUG_BAD_RESUMPTION);
1401
return false;
1402
}
1403
return true;
1404
}
1405
1406
static bool GetThisValueForCheck(JSContext* cx, AbstractFramePtr frame,
1407
jsbytecode* pc, MutableHandleValue thisv,
1408
Maybe<HandleValue>& maybeThisv) {
1409
if (frame.debuggerNeedsCheckPrimitiveReturn()) {
1410
{
1411
AutoRealm ar(cx, frame.environmentChain());
1412
if (!GetThisValueForDebuggerFrameMaybeOptimizedOut(cx, frame, pc,
1413
thisv)) {
1414
return false;
1415
}
1416
}
1417
1418
if (!cx->compartment()->wrap(cx, thisv)) {
1419
return false;
1420
}
1421
1422
MOZ_ASSERT_IF(thisv.isMagic(), thisv.isMagic(JS_UNINITIALIZED_LEXICAL));
1423
maybeThisv.emplace(HandleValue(thisv));
1424
}
1425
1426
return true;
1427
}
1428
1429
static bool CheckResumptionValue(JSContext* cx, AbstractFramePtr frame,
1430
const Maybe<HandleValue>& maybeThisv,
1431
ResumeMode resumeMode, MutableHandleValue vp) {
1432
if (maybeThisv.isSome()) {
1433
const HandleValue& thisv = maybeThisv.ref();
1434
if (resumeMode == ResumeMode::Return && vp.isPrimitive()) {
1435
// Forcing return from a class constructor. There are rules.
1436
if (vp.isUndefined()) {
1437
if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL)) {
1438
return ThrowUninitializedThis(cx, frame);
1439
}
1440
1441
vp.set(thisv);
1442
} else {
1443
ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, vp,
1444
nullptr);
1445
return false;
1446
}
1447
}
1448
}
1449
1450
// Check for forcing return from a generator before the initial yield. This
1451
// is not supported because some engine-internal code assumes a call to a
1452
// generator will return a GeneratorObject; see bug 1477084.
1453
if (resumeMode == ResumeMode::Return && frame && frame.isFunctionFrame() &&
1454
frame.callee()->isGenerator()) {
1455
Rooted<AbstractGeneratorObject*> genObj(cx);
1456
{
1457
AutoRealm ar(cx, frame.callee());
1458
genObj = GetGeneratorObjectForFrame(cx, frame);
1459
}
1460
1461
if (!genObj || genObj->isBeforeInitialYield()) {
1462
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1463
JSMSG_DEBUG_FORCED_RETURN_DISALLOWED);
1464
return false;
1465
}
1466
}
1467
1468
return true;
1469
}
1470
1471
// Last-minute sanity adjustments to resumption.
1472
//
1473
// This is called last, as we leave the debugger. It must happen outside the
1474
// control of the uncaughtExceptionHook, because this code assumes we won't
1475
// change our minds and continue execution--we must not close the generator
1476
// object unless we're really going to force-return.
1477
static void AdjustGeneratorResumptionValue(JSContext* cx,
1478
AbstractFramePtr frame,
1479
ResumeMode& resumeMode,
1480
MutableHandleValue vp) {
1481
if (resumeMode != ResumeMode::Return && resumeMode != ResumeMode::Throw) {
1482
return;
1483
}
1484
if (!frame || !frame.isFunctionFrame()) {
1485
return;
1486
}
1487
1488
// To propagate out of memory in debuggee code.
1489
auto getAndClearExceptionThenThrow = [&]() {
1490
MOZ_ALWAYS_TRUE(cx->getPendingException(vp));
1491
cx->clearPendingException();
1492
resumeMode = ResumeMode::Throw;
1493
};
1494
1495
// Treat `{return: <value>}` like a `return` statement. Simulate what the
1496
// debuggee would do for an ordinary `return` statement, using a few bytecode
1497
// instructions. It's simpler to do the work manually than to count on that
1498
// bytecode sequence existing in the debuggee, somehow jump to it, and then
1499
// avoid re-entering the debugger from it.
1500
//
1501
// Similarly treat `{throw: <value>}` like a `throw` statement.
1502
if (frame.callee()->isGenerator()) {
1503
// Throw doesn't require any special processing for (async) generators.
1504
if (resumeMode == ResumeMode::Throw) {
1505
return;
1506
}
1507
1508
// Forcing return from a (possibly async) generator.
1509
Rooted<AbstractGeneratorObject*> genObj(
1510
cx, GetGeneratorObjectForFrame(cx, frame));
1511
1512
// We already went through CheckResumptionValue, which would have replaced
1513
// this invalid resumption value with an error if we were trying to force
1514
// return before the initial yield.
1515
MOZ_RELEASE_ASSERT(genObj && !genObj->isBeforeInitialYield());
1516
1517
// 1. `return <value>` creates and returns a new object,
1518
// `{value: <value>, done: true}`.
1519
//
1520
// For non-async generators, the iterator result object is created in
1521
// bytecode, so we have to simulate that here. For async generators, our
1522
// C++ implementation of AsyncGeneratorResolve will do this. So don't do it
1523
// twice:
1524
if (!genObj->is<AsyncGeneratorObject>()) {
1525
JSObject* pair = CreateIterResultObject(cx, vp, true);
1526
if (!pair) {
1527
getAndClearExceptionThenThrow();
1528
return;
1529
}
1530
vp.setObject(*pair);
1531
}
1532
1533
// 2. The generator must be closed.
1534
genObj->setClosed();
1535
1536
// Async generators have additionally bookkeeping which must be adjusted
1537
// when switching over to the closed state.
1538
if (genObj->is<AsyncGeneratorObject>()) {
1539
genObj->as<AsyncGeneratorObject>().setCompleted();
1540
}
1541
} else if (frame.callee()->isAsync()) {
1542
if (AbstractGeneratorObject* genObj =
1543
GetGeneratorObjectForFrame(cx, frame)) {
1544
// Throw doesn't require any special processing for async functions when
1545
// the internal generator object is already present.
1546
if (resumeMode == ResumeMode::Throw) {
1547
return;
1548
}
1549
1550
Rooted<AsyncFunctionGeneratorObject*> asyncGenObj(
1551
cx, &genObj->as<AsyncFunctionGeneratorObject>());
1552
1553
// 1. `return <value>` fulfills and returns the async function's promise.
1554
Rooted<PromiseObject*> promise(cx, asyncGenObj->promise());
1555
if (promise->state() == JS::PromiseState::Pending) {
1556
if (!AsyncFunctionResolve(cx, asyncGenObj, vp,
1557
AsyncFunctionResolveKind::Fulfill)) {
1558
getAndClearExceptionThenThrow();
1559
return;
1560
}
1561
}
1562
vp.setObject(*promise);
1563
1564
// 2. The generator must be closed.
1565
asyncGenObj->setClosed();
1566
} else {
1567
// We're before entering the actual function code.
1568
1569
// 1. `throw <value>` creates a promise rejected with the value *vp.
1570
// 1. `return <value>` creates a promise resolved with the value *vp.
1571
JSObject* promise = resumeMode == ResumeMode::Throw
1572
? PromiseObject::unforgeableReject(cx, vp)
1573
: PromiseObject::unforgeableResolve(cx, vp);
1574
if (!promise) {
1575
getAndClearExceptionThenThrow();
1576
return;
1577
}
1578
vp.setObject(*promise);
1579
1580
// 2. Return normally in both cases.
1581
resumeMode = ResumeMode::Return;
1582
}
1583
}
1584
}
1585
1586
ResumeMode Debugger::reportUncaughtException(Maybe<AutoRealm>& ar) {
1587
JSContext* cx = ar->context();
1588
1589
// Uncaught exceptions arise from Debugger code, and so we must already be
1590
// in an NX section.
1591
MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx, *this));
1592
1593
if (cx->isExceptionPending()) {
1594
// We want to report the pending exception, but we want to let the
1595
// embedding handle it however it wants to. So pretend like we're
1596
// starting a new script execution on our current compartment (which
1597
// is the debugger compartment, so reported errors won't get
1598
// reported to various onerror handlers in debuggees) and as part of
1599
// that "execution" simply throw our exception so the embedding can
1600
// deal.
1601
RootedValue exn(cx);
1602
if (cx->getPendingException(&exn)) {
1603
// Clear the exception, because ReportErrorToGlobal will assert that
1604
// we don't have one.
1605
cx->clearPendingException();
1606
ReportErrorToGlobal(cx, cx->global(), exn);
1607
}
1608
1609
// And if not, or if PrepareScriptEnvironmentAndInvoke somehow left an
1610
// exception on cx (which it totally shouldn't do), just give up.
1611
cx->clearPendingException();
1612
}
1613
1614
ar.reset();
1615
return ResumeMode::Terminate;
1616
}
1617
1618
ResumeMode Debugger::handleUncaughtExceptionHelper(
1619
Maybe<AutoRealm>& ar, MutableHandleValue* vp,
1620
const Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame) {
1621
JSContext* cx = ar->context();
1622
1623
// Uncaught exceptions arise from Debugger code, and so we must already be in
1624
// an NX section. This also establishes that we are already within the scope
1625
// of an AutoDebuggerJobQueueInterruption object.
1626
MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx, *this));
1627
1628
if (cx->isExceptionPending()) {
1629
if (uncaughtExceptionHook) {
1630
RootedValue exc(cx);
1631
if (!cx->getPendingException(&exc)) {
1632
return ResumeMode::Terminate;
1633
}
1634
cx->clearPendingException();
1635
1636
RootedValue fval(cx, ObjectValue(*uncaughtExceptionHook));
1637
RootedValue rv(cx);
1638
if (js::Call(cx, fval, object, exc, &rv)) {
1639
if (vp) {
1640
ResumeMode resumeMode = ResumeMode::Continue;
1641
if (!ParseResumptionValue(cx, rv, resumeMode, *vp)) {
1642
return reportUncaughtException(ar);
1643
}
1644
return leaveDebugger(ar, frame, thisVForCheck,
1645
CallUncaughtExceptionHook::No, resumeMode, *vp);
1646
} else {
1647
// Caller is something like onGarbageCollectionHook that
1648
// doesn't allow Debugger to control debuggee resumption.
1649
// The return value from uncaughtExceptionHook is ignored.
1650
return ResumeMode::Continue;
1651
}
1652
}
1653
}
1654
1655
return reportUncaughtException(ar);
1656
}
1657
1658
ar.reset();
1659
return ResumeMode::Terminate;
1660
}
1661
1662
ResumeMode Debugger::handleUncaughtException(
1663
Maybe<AutoRealm>& ar, MutableHandleValue vp,
1664
const Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame) {
1665
return handleUncaughtExceptionHelper(ar, &vp, thisVForCheck, frame);
1666
}
1667
1668
ResumeMode Debugger::handleUncaughtException(Maybe<AutoRealm>& ar) {
1669
return handleUncaughtExceptionHelper(ar, nullptr, mozilla::Nothing(),
1670
NullFramePtr());
1671
}
1672
1673
ResumeMode Debugger::leaveDebugger(Maybe<AutoRealm>& ar, AbstractFramePtr frame,
1674
const Maybe<HandleValue>& maybeThisv,
1675
CallUncaughtExceptionHook callHook,
1676
ResumeMode resumeMode,
1677
MutableHandleValue vp) {
1678
JSContext* cx = ar->context();
1679
if (!unwrapDebuggeeValue(cx, vp) ||
1680
!CheckResumptionValue(cx, frame, maybeThisv, resumeMode, vp)) {
1681
if (callHook == CallUncaughtExceptionHook::Yes) {
1682
return handleUncaughtException(ar, vp, maybeThisv, frame);
1683
}
1684
return reportUncaughtException(ar);
1685
}
1686
1687
ar.reset();
1688
if (!cx->compartment()->wrap(cx, vp)) {
1689
resumeMode = ResumeMode::Terminate;
1690
vp.setUndefined();
1691
}
1692
AdjustGeneratorResumptionValue(cx, frame, resumeMode, vp);
1693
1694
return resumeMode;
1695
}
1696
1697
ResumeMode Debugger::processParsedHandlerResult(Maybe<AutoRealm>& ar,
1698
AbstractFramePtr frame,
1699
jsbytecode* pc, bool success,
1700
ResumeMode resumeMode,
1701
MutableHandleValue vp) {
1702
JSContext* cx = ar->context();
1703
1704
RootedValue thisv(cx);
1705
Maybe<HandleValue> maybeThisv;
1706
if (!GetThisValueForCheck(cx, frame, pc, &thisv, maybeThisv)) {
1707
ar.reset();
1708
return ResumeMode::Terminate;
1709
}
1710
1711
if (!success) {
1712
return handleUncaughtException(ar, vp, maybeThisv, frame);
1713
}
1714
1715
return leaveDebugger(ar, frame, maybeThisv, CallUncaughtExceptionHook::Yes,
1716
resumeMode, vp);
1717
}
1718
1719
ResumeMode Debugger::processHandlerResult(Maybe<AutoRealm>& ar, bool success,
1720
const Value& rv,
1721
AbstractFramePtr frame,
1722
jsbytecode* pc,
1723
MutableHandleValue vp) {
1724
JSContext* cx = ar->context();
1725
1726
RootedValue thisv(cx);
1727
Maybe<HandleValue> maybeThisv;
1728
if (frame && !GetThisValueForCheck(cx, frame, pc, &