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/Frame-inl.h"
8
9
#include "mozilla/Assertions.h" // for AssertionConditionType
10
#include "mozilla/HashTable.h" // for HashMapEntry
11
#include "mozilla/Maybe.h" // for Maybe
12
#include "mozilla/Move.h" // for std::move
13
#include "mozilla/Range.h" // for Range
14
#include "mozilla/RangedPtr.h" // for RangedPtr
15
#include "mozilla/Result.h" // for Result
16
#include "mozilla/ScopeExit.h" // for MakeScopeExit, ScopeExit
17
#include "mozilla/ThreadLocal.h" // for ThreadLocal
18
#include "mozilla/Vector.h" // for Vector
19
20
#include <stddef.h> // for size_t
21
#include <stdint.h> // for int32_t
22
#include <string.h> // for strlen
23
24
#include "jsapi.h" // for CallArgs, Handle
25
#include "jsfriendapi.h" // for GetErrorMessage
26
#include "jsnum.h" // for Int32ToString
27
28
#include "builtin/Array.h" // for NewDenseCopiedArray
29
#include "builtin/Promise.h" // for PromiseObject
30
#include "debugger/Debugger.h" // for Completion, Debugger
31
#include "debugger/DebugScript.h"
32
#include "debugger/Environment.h" // for DebuggerEnvironment
33
#include "debugger/NoExecute.h" // for LeaveDebuggeeNoExecute
34
#include "debugger/Object.h" // for DebuggerObject
35
#include "debugger/Script.h" // for DebuggerScript
36
#include "frontend/BytecodeCompilation.h" // for CompileEvalScript
37
#include "gc/Barrier.h" // for HeapPtr
38
#include "gc/FreeOp.h" // for JSFreeOp
39
#include "gc/GC.h" // for MemoryUse
40
#include "gc/Marking.h" // for IsAboutToBeFinalized
41
#include "gc/Rooting.h" // for RootedDebuggerFrame
42
#include "gc/Tracer.h" // for TraceCrossCompartmentEdge
43
#include "gc/ZoneAllocator.h" // for AddCellMemory
44
#include "jit/JSJitFrameIter.h" // for InlineFrameIterator
45
#include "jit/RematerializedFrame.h" // for RematerializedFrame
46
#include "js/Proxy.h" // for PrivateValue
47
#include "js/SourceText.h" // for SourceText, SourceOwnership
48
#include "js/StableStringChars.h" // for AutoStableStringChars
49
#include "vm/ArgumentsObject.h" // for ArgumentsObject
50
#include "vm/ArrayObject.h" // for ArrayObject
51
#include "vm/AsyncFunction.h" // for AsyncFunctionGeneratorObject
52
#include "vm/AsyncIteration.h" // for AsyncGeneratorObject
53
#include "vm/BytecodeUtil.h" // for JSDVG_SEARCH_STACK
54
#include "vm/Compartment.h" // for Compartment
55
#include "vm/EnvironmentObject.h" // for IsGlobalLexicalEnvironment
56
#include "vm/GeneratorObject.h" // for AbstractGeneratorObject
57
#include "vm/GlobalObject.h" // for GlobalObject
58
#include "vm/Interpreter.h" // for Call, ExecuteKernel
59
#include "vm/JSAtom.h" // for Atomize
60
#include "vm/JSContext.h" // for JSContext, ReportValueError
61
#include "vm/JSFunction.h" // for JSFunction, NewNativeFunction
62
#include "vm/JSObject.h" // for JSObject, RequireObject
63
#include "vm/JSScript.h" // for JSScript
64
#include "vm/NativeObject.h" // for NativeDefineDataProperty
65
#include "vm/Realm.h" // for AutoRealm
66
#include "vm/Runtime.h" // for JSAtomState
67
#include "vm/Scope.h" // for PositionalFormalParameterIter
68
#include "vm/Stack.h" // for AbstractFramePtr, FrameIter
69
#include "vm/StringType.h" // for PropertyName, JSString
70
#include "wasm/WasmDebug.h" // for DebugState
71
#include "wasm/WasmInstance.h" // for Instance
72
#include "wasm/WasmJS.h" // for WasmInstanceObject
73
#include "wasm/WasmTypes.h" // for DebugFrame
74
75
#include "debugger/Debugger-inl.h" // for Debugger::fromJSObject
76
#include "vm/Compartment-inl.h" // for Compartment::wrap
77
#include "vm/JSContext-inl.h" // for JSContext::check
78
#include "vm/JSObject-inl.h" // for NewObjectWithGivenProto
79
#include "vm/JSScript-inl.h" // for JSScript::ensureHasAnalyzedArgsUsage
80
#include "vm/NativeObject-inl.h" // for NativeObject::global
81
#include "vm/ObjectOperations-inl.h" // for GetProperty
82
#include "vm/Realm-inl.h" // for AutoRealm::AutoRealm
83
#include "vm/Stack-inl.h" // for AbstractFramePtr::script
84
85
namespace js {
86
namespace jit {
87
class JitFrameLayout;
88
} /* namespace jit */
89
} /* namespace js */
90
91
using namespace js;
92
93
using JS::AutoStableStringChars;
94
using JS::CompileOptions;
95
using JS::SourceOwnership;
96
using JS::SourceText;
97
using mozilla::MakeScopeExit;
98
using mozilla::Maybe;
99
100
ScriptedOnStepHandler::ScriptedOnStepHandler(JSObject* object)
101
: object_(object) {
102
MOZ_ASSERT(object_->isCallable());
103
}
104
105
JSObject* ScriptedOnStepHandler::object() const { return object_; }
106
107
void ScriptedOnStepHandler::hold(JSObject* owner) {
108
AddCellMemory(owner, allocSize(), MemoryUse::DebuggerOnStepHandler);
109
}
110
111
void ScriptedOnStepHandler::drop(JSFreeOp* fop, JSObject* owner) {
112
fop->delete_(owner, this, allocSize(), MemoryUse::DebuggerOnStepHandler);
113
}
114
115
void ScriptedOnStepHandler::trace(JSTracer* tracer) {
116
TraceEdge(tracer, &object_, "OnStepHandlerFunction.object");
117
}
118
119
bool ScriptedOnStepHandler::onStep(JSContext* cx, HandleDebuggerFrame frame,
120
ResumeMode& resumeMode,
121
MutableHandleValue vp) {
122
RootedValue fval(cx, ObjectValue(*object_));
123
RootedValue rval(cx);
124
if (!js::Call(cx, fval, frame, &rval)) {
125
return false;
126
}
127
128
return ParseResumptionValue(cx, rval, resumeMode, vp);
129
};
130
131
size_t ScriptedOnStepHandler::allocSize() const { return sizeof(*this); }
132
133
ScriptedOnPopHandler::ScriptedOnPopHandler(JSObject* object) : object_(object) {
134
MOZ_ASSERT(object->isCallable());
135
}
136
137
JSObject* ScriptedOnPopHandler::object() const { return object_; }
138
139
void ScriptedOnPopHandler::hold(JSObject* owner) {
140
AddCellMemory(owner, allocSize(), MemoryUse::DebuggerOnPopHandler);
141
}
142
143
void ScriptedOnPopHandler::drop(JSFreeOp* fop, JSObject* owner) {
144
fop->delete_(owner, this, allocSize(), MemoryUse::DebuggerOnPopHandler);
145
}
146
147
void ScriptedOnPopHandler::trace(JSTracer* tracer) {
148
TraceEdge(tracer, &object_, "OnStepHandlerFunction.object");
149
}
150
151
bool ScriptedOnPopHandler::onPop(JSContext* cx, HandleDebuggerFrame frame,
152
const Completion& completion,
153
ResumeMode& resumeMode,
154
MutableHandleValue vp) {
155
Debugger* dbg = Debugger::fromChildJSObject(frame);
156
157
RootedValue completionValue(cx);
158
if (!completion.buildCompletionValue(cx, dbg, &completionValue)) {
159
return false;
160
}
161
162
RootedValue fval(cx, ObjectValue(*object_));
163
RootedValue rval(cx);
164
if (!js::Call(cx, fval, frame, completionValue, &rval)) {
165
return false;
166
}
167
168
return ParseResumptionValue(cx, rval, resumeMode, vp);
169
};
170
171
size_t ScriptedOnPopHandler::allocSize() const { return sizeof(*this); }
172
173
inline js::Debugger* js::DebuggerFrame::owner() const {
174
JSObject* dbgobj = &getReservedSlot(OWNER_SLOT).toObject();
175
return Debugger::fromJSObject(dbgobj);
176
}
177
178
inline bool js::DebuggerFrame::hasIncrementedStepper() const {
179
return getReservedSlot(HAS_INCREMENTED_STEPPER_SLOT).toBoolean();
180
}
181
inline void js::DebuggerFrame::setHasIncrementedStepper(bool incremented) {
182
setReservedSlot(HAS_INCREMENTED_STEPPER_SLOT, BooleanValue(incremented));
183
}
184
185
const JSClassOps DebuggerFrame::classOps_ = {
186
nullptr, /* addProperty */
187
nullptr, /* delProperty */
188
nullptr, /* enumerate */
189
nullptr, /* newEnumerate */
190
nullptr, /* resolve */
191
nullptr, /* mayResolve */
192
finalize, /* finalize */
193
nullptr, /* call */
194
nullptr, /* hasInstance */
195
nullptr, /* construct */
196
CallTraceMethod<DebuggerFrame>, /* trace */
197
};
198
199
const JSClass DebuggerFrame::class_ = {
200
"Frame",
201
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
202
// We require foreground finalization so we can destruct GeneratorInfo's
203
// HeapPtrs.
204
JSCLASS_FOREGROUND_FINALIZE,
205
&DebuggerFrame::classOps_};
206
207
enum { JSSLOT_DEBUGARGUMENTS_FRAME, JSSLOT_DEBUGARGUMENTS_COUNT };
208
209
const JSClass DebuggerArguments::class_ = {
210
"Arguments", JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT)};
211
212
bool DebuggerFrame::resume(const FrameIter& iter) {
213
FrameIter::Data* data = iter.copyData();
214
if (!data) {
215
return false;
216
}
217
setFrameIterData(data);
218
return true;
219
}
220
221
bool DebuggerFrame::hasAnyHooks() const {
222
return !getReservedSlot(ONSTEP_HANDLER_SLOT).isUndefined() ||
223
!getReservedSlot(ONPOP_HANDLER_SLOT).isUndefined();
224
}
225
226
/* static */
227
NativeObject* DebuggerFrame::initClass(JSContext* cx,
228
Handle<GlobalObject*> global,
229
HandleObject dbgCtor) {
230
return InitClass(cx, dbgCtor, nullptr, &class_, construct, 0, properties_,
231
methods_, nullptr, nullptr);
232
}
233
234
/* static */
235
DebuggerFrame* DebuggerFrame::create(JSContext* cx, HandleObject proto,
236
const FrameIter& iter,
237
HandleNativeObject debugger) {
238
DebuggerFrame* frame = NewObjectWithGivenProto<DebuggerFrame>(cx, proto);
239
if (!frame) {
240
return nullptr;
241
}
242
243
FrameIter::Data* data = iter.copyData();
244
if (!data) {
245
return nullptr;
246
}
247
frame->setFrameIterData(data);
248
249
frame->setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));
250
frame->setReservedSlot(HAS_INCREMENTED_STEPPER_SLOT, BooleanValue(false));
251
252
return frame;
253
}
254
255
/**
256
* Information held by a DebuggerFrame about a generator/async call. A
257
* Debugger.Frame's GENERATOR_INFO_SLOT, if set, holds a PrivateValue pointing
258
* to one of these.
259
*
260
* This is created and attached as soon as a generator object is created for a
261
* debuggee generator/async frame, retained across suspensions and resumptions,
262
* and cleared when the generator call ends permanently.
263
*
264
* It may seem like this information might belong in ordinary reserved slots on
265
* the DebuggerFrame object. But that isn't possible:
266
*
267
* 1) Slots cannot contain cross-compartment references directly.
268
* 2) Ordinary cross-compartment wrappers aren't good enough, because the
269
* debugger must create its own magic entries in the wrapper table for the GC
270
* to get zone collection groups right.
271
* 3) Even if we make debugger wrapper table entries by hand, hiding
272
* cross-compartment edges as PrivateValues doesn't call post-barriers, and
273
* the generational GC won't update our pointer when the generator object
274
* gets tenured.
275
*
276
* Yes, officer, I definitely knew all this in advance and designed it this way
277
* the first time.
278
*
279
* Note that it is not necessary to have a second cross-compartment wrapper
280
* table entry to cover the pointer to the generator's script. The wrapper table
281
* entries play two roles: they help the GC put a debugger zone in the same zone
282
* group as its debuggee, and they serve as roots when collecting the debuggee
283
* zone, but not the debugger zone. Since an AbstractGeneratorObject holds a
284
* strong reference to its callee's script (via the callee), and the AGO and the
285
* script are always in the same compartment, it suffices to add a
286
* cross-compartment wrapper table entry for the Debugger.Frame -> AGO edge.
287
*/
288
class DebuggerFrame::GeneratorInfo {
289
// An unwrapped cross-compartment reference to the generator object.
290
//
291
// Always an object.
292
//
293
// This cannot be GCPtr because we are not always destructed during sweeping;
294
// a Debugger.Frame's generator is also cleared when the generator returns
295
// permanently.
296
const HeapPtr<Value> unwrappedGenerator_;
297
298
// A cross-compartment reference to the generator's script.
299
const HeapPtr<JSScript*> generatorScript_;
300
301
public:
302
GeneratorInfo(Handle<AbstractGeneratorObject*> unwrappedGenerator,
303
HandleScript generatorScript)
304
: unwrappedGenerator_(ObjectValue(*unwrappedGenerator)),
305
generatorScript_(generatorScript) {}
306
307
void trace(JSTracer* tracer, DebuggerFrame& frameObj) {
308
TraceCrossCompartmentEdge(tracer, &frameObj, &unwrappedGenerator_,
309
"Debugger.Frame generator object");
310
TraceCrossCompartmentEdge(tracer, &frameObj, &generatorScript_,
311
"Debugger.Frame generator script");
312
}
313
314
AbstractGeneratorObject& unwrappedGenerator() const {
315
return unwrappedGenerator_.toObject().as<AbstractGeneratorObject>();
316
}
317
318
JSScript* generatorScript() { return generatorScript_; }
319
320
bool isGeneratorScriptAboutToBeFinalized() {
321
return IsAboutToBeFinalized(&generatorScript_);
322
}
323
};
324
325
js::AbstractGeneratorObject& js::DebuggerFrame::unwrappedGenerator() const {
326
return generatorInfo()->unwrappedGenerator();
327
}
328
329
#ifdef DEBUG
330
JSScript* js::DebuggerFrame::generatorScript() const {
331
return generatorInfo()->generatorScript();
332
}
333
#endif
334
335
bool DebuggerFrame::setGenerator(JSContext* cx,
336
Handle<AbstractGeneratorObject*> genObj) {
337
cx->check(this);
338
339
Debugger::GeneratorWeakMap::AddPtr p =
340
owner()->generatorFrames.lookupForAdd(genObj);
341
if (p) {
342
MOZ_ASSERT(p->value() == this);
343
MOZ_ASSERT(&unwrappedGenerator() == genObj);
344
return true;
345
}
346
347
// There are three relations we must establish:
348
//
349
// 1) The DebuggerFrame must point to the AbstractGeneratorObject.
350
//
351
// 2) generatorFrames must map the AbstractGeneratorObject to the
352
// DebuggerFrame.
353
//
354
// 3) The generator's script's observer count must be bumped.
355
RootedScript script(cx, genObj->callee().nonLazyScript());
356
auto* info = cx->new_<GeneratorInfo>(genObj, script);
357
if (!info) {
358
ReportOutOfMemory(cx);
359
return false;
360
}
361
auto infoGuard = MakeScopeExit([&] { js_delete(info); });
362
363
if (!owner()->generatorFrames.relookupOrAdd(p, genObj, this)) {
364
ReportOutOfMemory(cx);
365
return false;
366
}
367
auto generatorFramesGuard =
368
MakeScopeExit([&] { owner()->generatorFrames.remove(genObj); });
369
370
{
371
AutoRealm ar(cx, script);
372
373
// All frames running a debuggee script must themselves be marked as
374
// debuggee frames. Bumping a script's generator observer count makes it a
375
// debuggee, so we need to mark all frames on the stack running it as
376
// debuggees as well, not just this one. This call takes care of all that.
377
if (!Debugger::ensureExecutionObservabilityOfScript(cx, script)) {
378
return false;
379
}
380
381
if (!DebugScript::incrementGeneratorObserverCount(cx, script)) {
382
return false;
383
}
384
}
385
386
InitReservedSlot(this, GENERATOR_INFO_SLOT, info,
387
MemoryUse::DebuggerFrameGeneratorInfo);
388
389
generatorFramesGuard.release();
390
infoGuard.release();
391
392
return true;
393
}
394
395
void DebuggerFrame::clearGenerator(JSFreeOp* fop) {
396
if (!hasGenerator()) {
397
return;
398
}
399
400
GeneratorInfo* info = generatorInfo();
401
402
// 3) The generator's script's observer count must be dropped.
403
//
404
// For ordinary calls, Debugger.Frame objects drop the script's stepper count
405
// when the frame is popped, but for generators, they leave the stepper count
406
// incremented across suspensions. This means that, whereas ordinary calls
407
// never need to drop the stepper count from the D.F finalizer, generator
408
// calls may.
409
if (!info->isGeneratorScriptAboutToBeFinalized()) {
410
JSScript* generatorScript = info->generatorScript();
411
DebugScript::decrementGeneratorObserverCount(fop, generatorScript);
412
maybeDecrementStepperCounter(fop, generatorScript);
413
}
414
415
// 1) The DebuggerFrame must no longer point to the AbstractGeneratorObject.
416
setReservedSlot(GENERATOR_INFO_SLOT, UndefinedValue());
417
fop->delete_(this, info, MemoryUse::DebuggerFrameGeneratorInfo);
418
}
419
420
void DebuggerFrame::clearGenerator(
421
JSFreeOp* fop, Debugger* owner,
422
Debugger::GeneratorWeakMap::Enum* maybeGeneratorFramesEnum) {
423
if (!hasGenerator()) {
424
return;
425
}
426
427
// 2) generatorFrames must no longer map the AbstractGeneratorObject to the
428
// DebuggerFrame.
429
GeneratorInfo* info = generatorInfo();
430
if (maybeGeneratorFramesEnum) {
431
maybeGeneratorFramesEnum->removeFront();
432
} else {
433
owner->generatorFrames.remove(&info->unwrappedGenerator());
434
}
435
436
clearGenerator(fop);
437
}
438
439
/* static */
440
bool DebuggerFrame::getCallee(JSContext* cx, HandleDebuggerFrame frame,
441
MutableHandleDebuggerObject result) {
442
RootedObject callee(cx);
443
if (frame->isOnStack()) {
444
AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
445
if (referent.isFunctionFrame()) {
446
callee = referent.callee();
447
}
448
} else {
449
MOZ_ASSERT(frame->hasGenerator());
450
451
callee = &frame->generatorInfo()->unwrappedGenerator().callee();
452
}
453
454
return frame->owner()->wrapNullableDebuggeeObject(cx, callee, result);
455
}
456
457
/* static */
458
bool DebuggerFrame::getIsConstructing(JSContext* cx, HandleDebuggerFrame frame,
459
bool& result) {
460
MOZ_ASSERT(frame->isOnStack());
461
462
Maybe<FrameIter> maybeIter;
463
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter)) {
464
return false;
465
}
466
FrameIter& iter = *maybeIter;
467
468
result = iter.isFunctionFrame() && iter.isConstructing();
469
return true;
470
}
471
472
static void UpdateFrameIterPc(FrameIter& iter) {
473
if (iter.abstractFramePtr().isWasmDebugFrame()) {
474
// Wasm debug frames don't need their pc updated -- it's null.
475
return;
476
}
477
478
if (iter.abstractFramePtr().isRematerializedFrame()) {
479
#ifdef DEBUG
480
// Rematerialized frames don't need their pc updated. The reason we
481
// need to update pc is because we might get the same Debugger.Frame
482
// object for multiple re-entries into debugger code from debuggee
483
// code. This reentrancy is not possible with rematerialized frames,
484
// because when returning to debuggee code, we would have bailed out
485
// to baseline.
486
//
487
// We walk the stack to assert that it doesn't need updating.
488
jit::RematerializedFrame* frame =
489
iter.abstractFramePtr().asRematerializedFrame();
490
jit::JitFrameLayout* jsFrame = (jit::JitFrameLayout*)frame->top();
491
jit::JitActivation* activation = iter.activation()->asJit();
492
493
JSContext* cx = TlsContext.get();
494
MOZ_ASSERT(cx == activation->cx());
495
496
ActivationIterator activationIter(cx);
497
while (activationIter.activation() != activation) {
498
++activationIter;
499
}
500
501
OnlyJSJitFrameIter jitIter(activationIter);
502
while (!jitIter.frame().isIonJS() || jitIter.frame().jsFrame() != jsFrame) {
503
++jitIter;
504
}
505
506
jit::InlineFrameIterator ionInlineIter(cx, &jitIter.frame());
507
while (ionInlineIter.frameNo() != frame->frameNo()) {
508
++ionInlineIter;
509
}
510
511
MOZ_ASSERT(ionInlineIter.pc() == iter.pc());
512
#endif
513
return;
514
}
515
516
iter.updatePcQuadratic();
517
}
518
519
/* static */
520
bool DebuggerFrame::getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
521
MutableHandleDebuggerEnvironment result) {
522
MOZ_ASSERT(frame->isOnStack());
523
524
Debugger* dbg = frame->owner();
525
526
Maybe<FrameIter> maybeIter;
527
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter)) {
528
return false;
529
}
530
FrameIter& iter = *maybeIter;
531
532
Rooted<Env*> env(cx);
533
{
534
AutoRealm ar(cx, iter.abstractFramePtr().environmentChain());
535
UpdateFrameIterPc(iter);
536
env = GetDebugEnvironmentForFrame(cx, iter.abstractFramePtr(), iter.pc());
537
if (!env) {
538
return false;
539
}
540
}
541
542
return dbg->wrapEnvironment(cx, env, result);
543
}
544
545
/* static */
546
bool DebuggerFrame::getIsGenerator(HandleDebuggerFrame frame) {
547
AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
548
return referent.hasScript() && referent.script()->isGenerator();
549
}
550
551
/* static */
552
bool DebuggerFrame::getOffset(JSContext* cx, HandleDebuggerFrame frame,
553
size_t& result) {
554
if (frame->isOnStack()) {
555
Maybe<FrameIter> maybeIter;
556
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter)) {
557
return false;
558
}
559
FrameIter& iter = *maybeIter;
560
561
AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
562
if (referent.isWasmDebugFrame()) {
563
iter.wasmUpdateBytecodeOffset();
564
result = iter.wasmBytecodeOffset();
565
} else {
566
JSScript* script = iter.script();
567
UpdateFrameIterPc(iter);
568
jsbytecode* pc = iter.pc();
569
result = script->pcToOffset(pc);
570
}
571
} else {
572
MOZ_ASSERT(frame->hasGenerator());
573
574
AbstractGeneratorObject& genObj =
575
frame->generatorInfo()->unwrappedGenerator();
576
JSScript* script = frame->generatorInfo()->generatorScript();
577
result = script->resumeOffsets()[genObj.resumeIndex()];
578
}
579
return true;
580
}
581
582
/* static */
583
bool DebuggerFrame::getOlder(JSContext* cx, HandleDebuggerFrame frame,
584
MutableHandleDebuggerFrame result) {
585
MOZ_ASSERT(frame->isOnStack());
586
587
Debugger* dbg = frame->owner();
588
589
Maybe<FrameIter> maybeIter;
590
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter)) {
591
return false;
592
}
593
FrameIter& iter = *maybeIter;
594
595
for (++iter; !iter.done(); ++iter) {
596
if (dbg->observesFrame(iter)) {
597
if (iter.isIon() && !iter.ensureHasRematerializedFrame(cx)) {
598
return false;
599
}
600
return dbg->getFrame(cx, iter, result);
601
}
602
}
603
604
result.set(nullptr);
605
return true;
606
}
607
608
/* static */
609
bool DebuggerFrame::getAsyncPromise(JSContext* cx, HandleDebuggerFrame frame,
610
MutableHandleDebuggerObject result) {
611
if (!frame->hasGenerator()) {
612
result.set(nullptr);
613
return true;
614
}
615
616
RootedObject resultObject(cx);
617
AbstractGeneratorObject& generator = frame->unwrappedGenerator();
618
if (generator.is<AsyncFunctionGeneratorObject>()) {
619
resultObject = generator.as<AsyncFunctionGeneratorObject>().promise();
620
} else if (generator.is<AsyncGeneratorObject>()) {
621
Rooted<AsyncGeneratorObject*> asyncGen(
622
cx, &generator.as<AsyncGeneratorObject>());
623
// In initial function execution, there is no promise.
624
if (!asyncGen->isQueueEmpty()) {
625
resultObject = AsyncGeneratorObject::peekRequest(asyncGen)->promise();
626
}
627
} else {
628
MOZ_CRASH("Unknown async generator type");
629
}
630
631
return frame->owner()->wrapNullableDebuggeeObject(cx, resultObject, result);
632
}
633
634
/* static */
635
bool DebuggerFrame::getThis(JSContext* cx, HandleDebuggerFrame frame,
636
MutableHandleValue result) {
637
MOZ_ASSERT(frame->isOnStack());
638
639
if (!requireScriptReferent(cx, frame)) {
640
return false;
641
}
642
643
Debugger* dbg = frame->owner();
644
645
Maybe<FrameIter> maybeIter;
646
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter)) {
647
return false;
648
}
649
FrameIter& iter = *maybeIter;
650
651
{
652
AbstractFramePtr frame = iter.abstractFramePtr();
653
AutoRealm ar(cx, frame.environmentChain());
654
655
UpdateFrameIterPc(iter);
656
657
if (!GetThisValueForDebuggerMaybeOptimizedOut(cx, frame, iter.pc(),
658
result)) {
659
return false;
660
}
661
}
662
663
return dbg->wrapDebuggeeValue(cx, result);
664
}
665
666
/* static */
667
DebuggerFrameType DebuggerFrame::getType(HandleDebuggerFrame frame) {
668
AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
669
670
// Indirect eval frames are both isGlobalFrame() and isEvalFrame(), so the
671
// order of checks here is significant.
672
if (referent.isEvalFrame()) {
673
return DebuggerFrameType::Eval;
674
}
675
676
if (referent.isGlobalFrame()) {
677
return DebuggerFrameType::Global;
678
}
679
680
if (referent.isFunctionFrame()) {
681
return DebuggerFrameType::Call;
682
}
683
684
if (referent.isModuleFrame()) {
685
return DebuggerFrameType::Module;
686
}
687
688
if (referent.isWasmDebugFrame()) {
689
return DebuggerFrameType::WasmCall;
690
}
691
692
MOZ_CRASH("Unknown frame type");
693
}
694
695
/* static */
696
DebuggerFrameImplementation DebuggerFrame::getImplementation(
697
HandleDebuggerFrame frame) {
698
AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
699
700
if (referent.isBaselineFrame()) {
701
return DebuggerFrameImplementation::Baseline;
702
}
703
704
if (referent.isRematerializedFrame()) {
705
return DebuggerFrameImplementation::Ion;
706
}
707
708
if (referent.isWasmDebugFrame()) {
709
return DebuggerFrameImplementation::Wasm;
710
}
711
712
return DebuggerFrameImplementation::Interpreter;
713
}
714
715
/*
716
* If succesful, transfers the ownership of the given `handler` to this
717
* Debugger.Frame. Note that on failure, the ownership of `handler` is not
718
* transferred, and the caller is responsible for cleaning it up.
719
*/
720
/* static */
721
bool DebuggerFrame::setOnStepHandler(JSContext* cx, HandleDebuggerFrame frame,
722
OnStepHandler* handler) {
723
OnStepHandler* prior = frame->onStepHandler();
724
if (handler == prior) {
725
return true;
726
}
727
728
JSFreeOp* fop = cx->defaultFreeOp();
729
if (frame->isOnStack()) {
730
AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
731
732
// Adjust execution observability and step counts on whatever code (JS or
733
// Wasm) this frame is running.
734
if (handler) {
735
if (!frame->maybeIncrementStepperCounter(cx, referent)) {
736
return false;
737
}
738
} else {
739
frame->maybeDecrementStepperCounter(cx->runtime()->defaultFreeOp(),
740
referent);
741
}
742
} else if (frame->hasGenerator()) {
743
RootedScript script(cx, frame->generatorInfo()->generatorScript());
744
745
if (handler) {
746
if (!frame->maybeIncrementStepperCounter(cx, script)) {
747
return false;
748
}
749
} else {
750
frame->maybeDecrementStepperCounter(cx->runtime()->defaultFreeOp(),
751
script);
752
}
753
} else {
754
// If the frame is entirely dead, we still allow setting the onStep
755
// handler, but it has no effect.
756
}
757
758
// Now that the stepper counts and observability are set correctly, we can
759
// actually switch the handler.
760
if (prior) {
761
prior->drop(fop, frame);
762
}
763
764
if (handler) {
765
frame->setReservedSlot(ONSTEP_HANDLER_SLOT, PrivateValue(handler));
766
handler->hold(frame);
767
} else {
768
frame->setReservedSlot(ONSTEP_HANDLER_SLOT, UndefinedValue());
769
}
770
771
return true;
772
}
773
774
bool DebuggerFrame::maybeIncrementStepperCounter(JSContext* cx,
775
AbstractFramePtr referent) {
776
if (hasIncrementedStepper()) {
777
return true;
778
}
779
780
if (!referent.isWasmDebugFrame()) {
781
return maybeIncrementStepperCounter(cx, referent.script());
782
}
783
784
wasm::Instance* instance = referent.asWasmDebugFrame()->instance();
785
wasm::DebugFrame* wasmFrame = referent.asWasmDebugFrame();
786
// Single stepping toggled off->on.
787
if (!instance->debug().incrementStepperCount(cx, wasmFrame->funcIndex())) {
788
return false;
789
}
790
791
setHasIncrementedStepper(true);
792
return true;
793
}
794
795
bool DebuggerFrame::maybeIncrementStepperCounter(JSContext* cx,
796
JSScript* script) {
797
if (hasIncrementedStepper()) {
798
return true;
799
}
800
801
// Single stepping toggled off->on.
802
AutoRealm ar(cx, script);
803
// Ensure observability *before* incrementing the step mode count.
804
// Calling this function after calling incrementStepperCount
805
// will make it a no-op.
806
if (!Debugger::ensureExecutionObservabilityOfScript(cx, script)) {
807
return false;
808
}
809
if (!DebugScript::incrementStepperCount(cx, script)) {
810
return false;
811
}
812
813
setHasIncrementedStepper(true);
814
return true;
815
}
816
817
void DebuggerFrame::maybeDecrementStepperCounter(JSFreeOp* fop,
818
AbstractFramePtr referent) {
819
if (!hasIncrementedStepper()) {
820
return;
821
}
822
823
if (!referent.isWasmDebugFrame()) {
824
maybeDecrementStepperCounter(fop, referent.script());
825
return;
826
}
827
828
wasm::Instance* instance = referent.asWasmDebugFrame()->instance();
829
wasm::DebugFrame* wasmFrame = referent.asWasmDebugFrame();
830
// Single stepping toggled on->off.
831
instance->debug().decrementStepperCount(fop, wasmFrame->funcIndex());
832
setHasIncrementedStepper(false);
833
}
834
835
void DebuggerFrame::maybeDecrementStepperCounter(JSFreeOp* fop,
836
JSScript* script) {
837
if (!hasIncrementedStepper()) {
838
return;
839
}
840
841
// Single stepping toggled on->off.
842
DebugScript::decrementStepperCount(fop, script);
843
setHasIncrementedStepper(false);
844
}
845
846
/* static */
847
bool DebuggerFrame::getArguments(JSContext* cx, HandleDebuggerFrame frame,
848
MutableHandleDebuggerArguments result) {
849
Value argumentsv = frame->getReservedSlot(ARGUMENTS_SLOT);
850
if (!argumentsv.isUndefined()) {
851
result.set(argumentsv.isObject()
852
? &argumentsv.toObject().as<DebuggerArguments>()
853
: nullptr);
854
return true;
855
}
856
857
AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
858
859
RootedDebuggerArguments arguments(cx);
860
if (referent.hasArgs()) {
861
Rooted<GlobalObject*> global(cx, &frame->global());
862
RootedObject proto(cx, GlobalObject::getOrCreateArrayPrototype(cx, global));
863
if (!proto) {
864
return false;
865
}
866
arguments = DebuggerArguments::create(cx, proto, frame);
867
if (!arguments) {
868
return false;
869
}
870
} else {
871
arguments = nullptr;
872
}
873
874
result.set(arguments);
875
frame->setReservedSlot(ARGUMENTS_SLOT, ObjectOrNullValue(result));
876
return true;
877
}
878
879
/*
880
* Evaluate |chars[0..length-1]| in the environment |env|, treating that
881
* source as appearing starting at |lineno| in |filename|. Store the return
882
* value in |*rval|. Use |thisv| as the 'this' value.
883
*
884
* If |frame| is non-nullptr, evaluate as for a direct eval in that frame; |env|
885
* must be either |frame|'s DebugScopeObject, or some extension of that
886
* environment; either way, |frame|'s scope is where newly declared variables
887
* go. In this case, |frame| must have a computed 'this' value, equal to
888
* |thisv|.
889
*/
890
static bool EvaluateInEnv(JSContext* cx, Handle<Env*> env,
891
AbstractFramePtr frame,
892
mozilla::Range<const char16_t> chars,
893
const char* filename, unsigned lineno,
894
MutableHandleValue rval) {
895
cx->check(env, frame);
896
897
CompileOptions options(cx);
898
options.setIsRunOnce(true)
899
.setNoScriptRval(false)
900
.setFileAndLine(filename, lineno)
901
.setIntroductionType("debugger eval")
902
/* Do not perform the Javascript filename validation security check for
903
* javascript executions sent through the debugger. Besides making up
904
* a filename for these codepaths, we must allow arbitrary JS execution
905
* for the Browser toolbox to function. */
906
.setSkipFilenameValidation(true);
907
908
if (frame && frame.hasScript() && frame.script()->strict()) {
909
options.setForceStrictMode();
910
}
911
912
SourceText<char16_t> srcBuf;
913
if (!srcBuf.init(cx, chars.begin().get(), chars.length(),
914
SourceOwnership::Borrowed)) {
915
return false;
916
}
917
918
RootedScript callerScript(
919
cx, frame && frame.hasScript() ? frame.script() : nullptr);
920
RootedScript script(cx);
921
922
ScopeKind scopeKind;
923
if (IsGlobalLexicalEnvironment(env)) {
924
scopeKind = ScopeKind::Global;
925
} else {
926
scopeKind = ScopeKind::NonSyntactic;
927
}
928
929
if (frame) {
930
MOZ_ASSERT(scopeKind == ScopeKind::NonSyntactic);
931
RootedScope scope(cx,
932
GlobalScope::createEmpty(cx, ScopeKind::NonSyntactic));
933
if (!scope) {
934
return false;
935
}
936
937
LifoAllocScope allocScope(&cx->tempLifoAlloc());
938
frontend::ParseInfo parseInfo(cx, allocScope);
939
940
frontend::EvalScriptInfo info(cx, parseInfo, options, env, scope);
941
script = frontend::CompileEvalScript(info, srcBuf);
942
if (!script) {
943
return false;
944
}
945
} else {
946
// Do not consider executeInGlobal{WithBindings} as an eval, but instead
947
// as executing a series of statements at the global level. This is to
948
// circumvent the fresh lexical scope that all eval have, so that the
949
// users of executeInGlobal, like the web console, may add new bindings to
950
// the global scope.
951
952
LifoAllocScope allocScope(&cx->tempLifoAlloc());
953
frontend::ParseInfo parseInfo(cx, allocScope);
954
955
frontend::GlobalScriptInfo info(cx, parseInfo, options, scopeKind);
956
script = frontend::CompileGlobalScript(info, srcBuf);
957
if (!script) {
958
return false;
959
}
960
}
961
962
return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address());
963
}
964
965
Result<Completion> js::DebuggerGenericEval(
966
JSContext* cx, const mozilla::Range<const char16_t> chars,
967
HandleObject bindings, const EvalOptions& options, Debugger* dbg,
968
HandleObject envArg, FrameIter* iter) {
969
// Either we're specifying the frame, or a global.
970
MOZ_ASSERT_IF(iter, !envArg);
971
MOZ_ASSERT_IF(!iter, envArg && IsGlobalLexicalEnvironment(envArg));
972
973
// Gather keys and values of bindings, if any. This must be done in the
974
// debugger compartment, since that is where any exceptions must be thrown.
975
RootedIdVector keys(cx);
976
RootedValueVector values(cx);
977
if (bindings) {
978
if (!GetPropertyKeys(cx, bindings, JSITER_OWNONLY, &keys) ||
979
!values.growBy(keys.length())) {
980
return cx->alreadyReportedError();
981
}
982
for (size_t i = 0; i < keys.length(); i++) {
983
MutableHandleValue valp = values[i];
984
if (!GetProperty(cx, bindings, bindings, keys[i], valp) ||
985
!dbg->unwrapDebuggeeValue(cx, valp)) {
986
return cx->alreadyReportedError();
987
}
988
}
989
}
990
991
Maybe<AutoRealm> ar;
992
if (iter) {
993
ar.emplace(cx, iter->environmentChain(cx));
994
} else {
995
ar.emplace(cx, envArg);
996
}
997
998
Rooted<Env*> env(cx);
999
if (iter) {
1000
env = GetDebugEnvironmentForFrame(cx, iter->abstractFramePtr(), iter->pc());
1001
if (!env) {
1002
return cx->alreadyReportedError();
1003
}
1004
} else {
1005
env = envArg;
1006
}
1007
1008
// If evalWithBindings, create the inner environment.
1009
if (bindings) {
1010
RootedPlainObject nenv(cx,
1011
NewObjectWithGivenProto<PlainObject>(cx, nullptr));
1012
if (!nenv) {
1013
return cx->alreadyReportedError();
1014
}
1015
RootedId id(cx);
1016
for (size_t i = 0; i < keys.length(); i++) {
1017
id = keys[i];
1018
cx->markId(id);
1019
MutableHandleValue val = values[i];
1020
if (!cx->compartment()->wrap(cx, val) ||
1021
!NativeDefineDataProperty(cx, nenv, id, val, 0)) {
1022
return cx->alreadyReportedError();
1023
}
1024
}
1025
1026
RootedObjectVector envChain(cx);
1027
if (!envChain.append(nenv)) {
1028
return cx->alreadyReportedError();
1029
}
1030
1031
RootedObject newEnv(cx);
1032
if (!CreateObjectsForEnvironmentChain(cx, envChain, env, &newEnv)) {
1033
return cx->alreadyReportedError();
1034
}
1035
1036
env = newEnv;
1037
}
1038
1039
// Note whether we are in an evaluation that might invoke the OnNativeCall
1040
// hook, so that the JITs will be disabled.
1041
AutoNoteDebuggerEvaluationWithOnNativeCallHook noteEvaluation(
1042
cx, dbg->observesNativeCalls() ? dbg : nullptr);
1043
1044
// Run the code and produce the completion value.
1045
LeaveDebuggeeNoExecute nnx(cx);
1046
RootedValue rval(cx);
1047
AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr();
1048
1049
bool ok = EvaluateInEnv(
1050
cx, env, frame, chars,
1051
options.filename() ? options.filename() : "debugger eval code",
1052
options.lineno(), &rval);
1053
Rooted<Completion> completion(cx, Completion::fromJSResult(cx, ok, rval));
1054
ar.reset();
1055
return completion.get();
1056
}
1057
1058
/* static */
1059
Result<Completion> DebuggerFrame::eval(JSContext* cx, HandleDebuggerFrame frame,
1060
mozilla::Range<const char16_t> chars,
1061
HandleObject bindings,
1062
const EvalOptions& options) {
1063
MOZ_ASSERT(frame->isOnStack());
1064
1065
Debugger* dbg = frame->owner();
1066
1067
Maybe<FrameIter> maybeIter;
1068
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter)) {
1069
return cx->alreadyReportedError();
1070
}
1071
FrameIter& iter = *maybeIter;
1072
1073
UpdateFrameIterPc(iter);
1074
1075
return DebuggerGenericEval(cx, chars, bindings, options, dbg, nullptr, &iter);
1076
}
1077
1078
bool DebuggerFrame::isOnStack() const { return !!getPrivate(); }
1079
1080
bool DebuggerFrame::isOnStackMaybeForwarded() const {
1081
return !!getPrivate(numFixedSlotsMaybeForwarded());
1082
}
1083
1084
OnStepHandler* DebuggerFrame::onStepHandler() const {
1085
Value value = getReservedSlot(ONSTEP_HANDLER_SLOT);
1086
return value.isUndefined() ? nullptr
1087
: static_cast<OnStepHandler*>(value.toPrivate());
1088
}
1089
1090
OnPopHandler* DebuggerFrame::onPopHandler() const {
1091
Value value = getReservedSlot(ONPOP_HANDLER_SLOT);
1092
return value.isUndefined() ? nullptr
1093
: static_cast<OnPopHandler*>(value.toPrivate());
1094
}
1095
1096
void DebuggerFrame::setOnPopHandler(JSContext* cx, OnPopHandler* handler) {
1097
OnPopHandler* prior = onPopHandler();
1098
if (handler == prior) {
1099
return;
1100
}
1101
1102
JSFreeOp* fop = cx->defaultFreeOp();
1103
1104
if (prior) {
1105
prior->drop(fop, this);
1106
}
1107
1108
if (handler) {
1109
setReservedSlot(ONPOP_HANDLER_SLOT, PrivateValue(handler));
1110
handler->hold(this);
1111
} else {
1112
setReservedSlot(ONPOP_HANDLER_SLOT, UndefinedValue());
1113
}
1114
}
1115
1116
FrameIter::Data* DebuggerFrame::frameIterData() const {
1117
return static_cast<FrameIter::Data*>(getPrivate());
1118
}
1119
1120
/* static */
1121
AbstractFramePtr DebuggerFrame::getReferent(HandleDebuggerFrame frame) {
1122
FrameIter iter(*frame->frameIterData());
1123
return iter.abstractFramePtr();
1124
}
1125
1126
/* static */
1127
bool DebuggerFrame::getFrameIter(JSContext* cx, HandleDebuggerFrame frame,
1128
Maybe<FrameIter>& result) {
1129
result.emplace(*frame->frameIterData());
1130
return true;
1131
}
1132
1133
/* static */
1134
bool DebuggerFrame::requireScriptReferent(JSContext* cx,
1135
HandleDebuggerFrame frame) {
1136
AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
1137
if (!referent.hasScript()) {
1138
RootedValue frameobj(cx, ObjectValue(*frame));
1139
ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, frameobj,
1140
nullptr, "a script frame");
1141
return false;
1142
}
1143
return true;
1144
}
1145
1146
void DebuggerFrame::setFrameIterData(FrameIter::Data* data) {
1147
MOZ_ASSERT(data);
1148
MOZ_ASSERT(!frameIterData());
1149
InitObjectPrivate(this, data, MemoryUse::DebuggerFrameIterData);
1150
}
1151
1152
void DebuggerFrame::freeFrameIterData(JSFreeOp* fop) {
1153
if (FrameIter::Data* data = frameIterData()) {
1154
fop->delete_(this, data, MemoryUse::DebuggerFrameIterData);
1155
setPrivate(nullptr);
1156
}
1157
}
1158
1159
/* static */
1160
void DebuggerFrame::finalize(JSFreeOp* fop, JSObject* obj) {
1161
MOZ_ASSERT(fop->onMainThread());
1162
1163
DebuggerFrame& frameobj = obj->as<DebuggerFrame>();
1164
1165
// Connections between dying Debugger.Frames and their
1166
// AbstractGeneratorObjects should have been broken in DebugAPI::sweepAll.
1167
MOZ_ASSERT(!frameobj.hasGenerator());
1168
1169
frameobj.freeFrameIterData(fop);
1170
OnStepHandler* onStepHandler = frameobj.onStepHandler();
1171
if (onStepHandler) {
1172
onStepHandler->drop(fop, &frameobj);
1173
}
1174
OnPopHandler* onPopHandler = frameobj.onPopHandler();
1175
if (onPopHandler) {
1176
onPopHandler->drop(fop, &frameobj);
1177
}
1178
}
1179
1180
void DebuggerFrame::trace(JSTracer* trc) {
1181
OnStepHandler* onStepHandler = this->onStepHandler();
1182
if (onStepHandler) {
1183
onStepHandler->trace(trc);
1184
}
1185
OnPopHandler* onPopHandler = this->onPopHandler();
1186
if (onPopHandler) {
1187
onPopHandler->trace(trc);
1188
}
1189
1190
if (hasGenerator()) {
1191
generatorInfo()->trace(trc, *this);
1192
}
1193
}
1194
1195
/* static */
1196
DebuggerFrame* DebuggerFrame::check(JSContext* cx, HandleValue thisv) {
1197
JSObject* thisobj = RequireObject(cx, thisv);
1198
if (!thisobj) {
1199
return nullptr;
1200
}
1201
if (thisobj->getClass() != &class_) {
1202
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1203
JSMSG_INCOMPATIBLE_PROTO, "Debugger.Frame",
1204
"method", thisobj->getClass()->name);
1205
return nullptr;
1206
}
1207
1208
RootedDebuggerFrame frame(cx, &thisobj->as<DebuggerFrame>());
1209
1210
// Forbid Debugger.Frame.prototype, which is of class DebuggerFrame::class_
1211
// but isn't really a working Debugger.Frame object. The prototype object
1212
// is distinguished by having a nullptr private value. Also, forbid popped
1213
// frames.
1214
if (!frame->getPrivate() &&
1215
frame->getReservedSlot(OWNER_SLOT).isUndefined()) {
1216
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1217
JSMSG_INCOMPATIBLE_PROTO, "Debugger.Frame",
1218
"method", "prototype object");
1219
return nullptr;
1220
}
1221
1222
return frame;
1223
}
1224
1225
struct MOZ_STACK_CLASS DebuggerFrame::CallData {
1226
JSContext* cx;
1227
const CallArgs& args;
1228
1229
HandleDebuggerFrame frame;
1230
1231
CallData(JSContext* cx, const CallArgs& args, HandleDebuggerFrame frame)
1232
: cx(cx), args(args), frame(frame) {}
1233
1234
bool argumentsGetter();
1235
bool calleeGetter();
1236
bool constructingGetter();
1237
bool environmentGetter();
1238
bool generatorGetter();
1239
bool asyncPromiseGetter();
1240
bool liveGetter();
1241
bool onStackGetter();
1242
bool terminatedGetter();
1243
bool offsetGetter();
1244
bool olderGetter();
1245
bool getScript();
1246
bool thisGetter();
1247
bool typeGetter();
1248
bool implementationGetter();
1249
bool onStepGetter();
1250
bool onStepSetter();
1251
bool onPopGetter();
1252
bool onPopSetter();
1253
bool evalMethod();
1254
bool evalWithBindingsMethod();
1255
1256
using Method = bool (CallData::*)();
1257
1258
template <Method MyMethod>
1259
static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
1260
1261
bool ensureOnStack() const;
1262
bool ensureOnStackOrSuspended() const;
1263
};
1264
1265
template <DebuggerFrame::CallData::Method MyMethod>
1266
/* static */
1267
bool DebuggerFrame::CallData::ToNative(JSContext* cx, unsigned argc,
1268
Value* vp) {
1269
CallArgs args = CallArgsFromVp(argc, vp);
1270
1271
RootedDebuggerFrame frame(cx, DebuggerFrame::check(cx, args.thisv()));
1272
if (!frame) {
1273
return false;
1274
}
1275
1276
CallData data(cx, args, frame);
1277
return (data.*MyMethod)();
1278
}
1279
1280
static bool EnsureOnStack(JSContext* cx, HandleDebuggerFrame frame) {
1281
MOZ_ASSERT(frame);
1282
if (!frame->isOnStack()) {
1283
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1284
JSMSG_DEBUG_NOT_ON_STACK, "Debugger.Frame");
1285
return false;
1286
}
1287
1288
return true;
1289
}
1290
static bool EnsureOnStackOrSuspended(JSContext* cx, HandleDebuggerFrame frame) {
1291
MOZ_ASSERT(frame);
1292
if (!frame->isOnStack() && !frame->hasGenerator()) {
1293
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1294
JSMSG_DEBUG_NOT_ON_STACK_OR_SUSPENDED,
1295
"Debugger.Frame");
1296
return false;
1297
}
1298
1299
return true;
1300
}
1301
1302
bool DebuggerFrame::CallData::ensureOnStack() const {
1303
return EnsureOnStack(cx, frame);
1304
}
1305
bool DebuggerFrame::CallData::ensureOnStackOrSuspended() const {
1306
return EnsureOnStackOrSuspended(cx, frame);
1307
}
1308
1309
bool DebuggerFrame::CallData::typeGetter() {
1310
if (!ensureOnStack()) {
1311
return false;
1312
}
1313
1314
DebuggerFrameType type = DebuggerFrame::getType(frame);
1315
1316
JSString* str;
1317
switch (type) {
1318
case DebuggerFrameType::Eval:
1319
str = cx->names().eval;
1320
break;
1321
case DebuggerFrameType::Global:
1322
str = cx->names().global;
1323
break;
1324
case DebuggerFrameType::Call:
1325
str = cx->names().call;
1326
break;
1327
case DebuggerFrameType::Module:
1328
str = cx->names().module;
1329
break;
1330
case DebuggerFrameType::WasmCall:
1331
str = cx->names().wasmcall;
1332
break;
1333
default:
1334
MOZ_CRASH("bad DebuggerFrameType value");
1335
}
1336
1337
args.rval().setString(str);
1338
return true;
1339
}
1340
1341
bool DebuggerFrame::CallData::implementationGetter() {
1342
if (!ensureOnStack()) {
1343
return false;
1344
}
1345
1346
DebuggerFrameImplementation implementation =
1347
DebuggerFrame::getImplementation(frame);
1348
1349
const char* s;
1350
switch (implementation) {
1351
case DebuggerFrameImplementation::Baseline:
1352
s = "baseline";
1353
break;
1354
case DebuggerFrameImplementation::Ion:
1355
s = "ion";
1356
break;
1357
case DebuggerFrameImplementation::Interpreter:
1358
s = "interpreter";
1359
break;
1360
case DebuggerFrameImplementation::Wasm:
1361
s = "wasm";
1362
break;
1363
default:
1364
MOZ_CRASH("bad DebuggerFrameImplementation value");
1365
}
1366
1367
JSAtom* str = Atomize(cx, s, strlen(s));
1368
if (!str) {
1369
return false;
1370
}
1371
1372
args.rval().setString(str);
1373
return true;
1374
}
1375
1376
bool DebuggerFrame::CallData::environmentGetter() {
1377
if (!ensureOnStack()) {
1378
return false;
1379
}
1380
1381
RootedDebuggerEnvironment result(cx);
1382
if (!DebuggerFrame::getEnvironment(cx, frame, &result)) {
1383
return false;
1384
}
1385
1386
args.rval().setObject(*result);
1387
return true;
1388
}
1389
1390
bool DebuggerFrame::CallData::calleeGetter() {
1391
if (!ensureOnStackOrSuspended()) {
1392
return false;
1393
}
1394
1395
RootedDebuggerObject result(cx);
1396
if (!DebuggerFrame::getCallee(cx, frame, &result)) {
1397
return false;
1398
}
1399
1400
args.rval().setObjectOrNull(result);
1401
return true;
1402
}
1403
1404
bool DebuggerFrame::CallData::generatorGetter() {
1405
if (!ensureOnStack()) {
1406
return false;
1407
}
1408
1409
args.rval().setBoolean(DebuggerFrame::getIsGenerator(frame));
1410
return true;
1411
}
1412
1413
bool DebuggerFrame::CallData::constructingGetter() {
1414
if (!ensureOnStack()) {
1415
return false;
1416
}
1417
1418
bool result;
1419
if (!DebuggerFrame::getIsConstructing(cx, frame, result)) {
1420
return false;
1421
}
1422
1423
args.rval().setBoolean(result);
1424
return true;
1425
}
1426
1427
bool DebuggerFrame::CallData::asyncPromiseGetter() {
1428
if (!ensureOnStackOrSuspended()) {
1429
return false;
1430
}
1431
1432
RootedScript script(cx);
1433
if (frame->isOnStack()) {
1434
FrameIter iter(*frame->frameIterData());
1435
AbstractFramePtr framePtr = iter.abstractFramePtr();
1436
1437
if (!framePtr.isWasmDebugFrame()) {
1438
script = framePtr.script();
1439
}
1440
} else {
1441
MOZ_ASSERT(frame->hasGenerator());
1442
script = frame->generatorInfo()->generatorScript();
1443
}
1444
// The async promise value is only provided for async functions and
1445
// async generator functions.
1446
if (!script || !script->isAsync()) {
1447
args.rval().setUndefined();
1448
return true;
1449
}
1450
1451
RootedDebuggerObject result(cx);
1452
if (!DebuggerFrame::getAsyncPromise(cx, frame, &result)) {
1453
return false;
1454
}
1455
1456
args.rval().setObjectOrNull(result);
1457
return true;
1458
}
1459
1460
bool DebuggerFrame::CallData::thisGetter() {
1461
if (!ensureOnStack()) {
1462
return false;
1463
}
1464
1465
return DebuggerFrame::getThis(cx, frame, args.rval());
1466
}
1467
1468
bool DebuggerFrame::CallData::olderGetter() {
1469
if (!ensureOnStack()) {
1470
return false;
1471
}
1472
1473
RootedDebuggerFrame result(cx);
1474
if (!DebuggerFrame::getOlder(cx, frame, &result)) {
1475
return false;
1476
}
1477
1478
args.rval().setObjectOrNull(result);
1479
return true;
1480
}
1481
1482
// The getter used for each element of frame.arguments.
1483
// See DebuggerFrame::getArguments.
1484
static bool DebuggerArguments_getArg(JSContext* cx, unsigned argc, Value* vp) {
1485
CallArgs args = CallArgsFromVp(argc, vp);
1486
int32_t i = args.callee().as<JSFunction>().getExtendedSlot(0).toInt32();
1487
1488
// Check that the this value is an Arguments object.
1489
RootedObject argsobj(cx, RequireObject(cx, args.thisv()));
1490
if (!argsobj) {
1491
return false;
1492
}
1493
if (argsobj->getClass() != &DebuggerArguments::class_) {
1494
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1495
JSMSG_INCOMPATIBLE_PROTO, "Arguments",
1496
"getArgument", argsobj->getClass()->name);
1497
return false;
1498
}
1499
1500
RootedValue framev(cx, argsobj->as<NativeObject>().getReservedSlot(
1501
JSSLOT_DEBUGARGUMENTS_FRAME));
1502
RootedDebuggerFrame thisobj(cx, DebuggerFrame::check(cx, framev));
1503
if (!thisobj || !EnsureOnStack(cx, thisobj)) {
1504
return false;
1505
}
1506
1507
FrameIter frameIter(*thisobj->frameIterData());
1508
AbstractFramePtr frame = frameIter.abstractFramePtr();
1509
1510
// TODO handle wasm frame arguments -- they are not yet reflectable.
1511
MOZ_ASSERT(!frame.isWasmDebugFrame(), "a wasm frame args");
1512
1513
// Since getters can be extracted and applied to other objects,
1514
// there is no guarantee this object has an ith argument.
1515
MOZ_ASSERT(i >= 0);
1516
RootedValue arg(cx);
1517
RootedScript script(cx);
1518
if (unsigned(i) < frame.numActualArgs()) {
1519
script = frame.script();
1520
{
1521
AutoRealm ar(cx, script);
1522
if (!script->ensureHasAnalyzedArgsUsage(cx)) {
1523
return false;
1524
}
1525
}
1526
if (unsigned(i) < frame.numFormalArgs()) {
1527
for (PositionalFormalParameterIter fi(script); fi; fi++) {
1528
if (fi.argumentSlot() == unsigned(i)) {
1529
// We might've been called before the CallObject was
1530
// created.
1531
if (fi.closedOver() && frame.hasInitialEnvironment()) {
1532
arg = frame.callObj().aliasedBinding(fi);
1533
} else {
1534
arg = frame.unaliasedActual(i, DONT_CHECK_ALIASING);
1535
}
1536
break;
1537
}
1538
}
1539
} else if (script->argsObjAliasesFormals() && frame.hasArgsObj()) {
1540
arg = frame.argsObj().arg(i);
1541
} else {
1542
arg = frame.unaliasedActual(i, DONT_CHECK_ALIASING);
1543
}
1544
} else {
1545
arg.setUndefined();
1546
}
1547
1548
if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &arg)) {
1549
return false;
1550
}
1551
args.rval().set(arg);
1552
return true;
1553
}
1554
1555
/* static */
1556
DebuggerArguments* DebuggerArguments::create(JSContext* cx, HandleObject proto,
1557
HandleDebuggerFrame frame) {
1558
AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
1559
1560
Rooted<DebuggerArguments*> obj(
1561
cx, NewObjectWithGivenProto<DebuggerArguments>(cx, proto));
1562
if (!obj) {
1563
return nullptr;
1564
}
1565
1566
SetReservedSlot(obj, FRAME_SLOT, ObjectValue(*frame));
1567
1568
MOZ_ASSERT(referent.numActualArgs() <= 0x7fffffff);
1569
unsigned fargc = referent.numActualArgs();
1570
RootedValue fargcVal(cx, Int32Value(fargc));
1571
if (!NativeDefineDataProperty(cx, obj, cx->names().length, fargcVal,
1572
JSPROP_PERMANENT | JSPROP_READONLY)) {
1573
return nullptr;
1574
}
1575
1576
Rooted<jsid> id(cx);
1577
for (unsigned i = 0; i < fargc; i++) {
1578
RootedFunction getobj(cx);
1579
getobj = NewNativeFunction(cx, DebuggerArguments_getArg, 0, nullptr,
1580
gc::AllocKind::FUNCTION_EXTENDED);
1581
if (!getobj) {
1582
return nullptr;
1583
}
1584
id = INT_TO_JSID(i);
1585
if (!NativeDefineAccessorProperty(cx, obj, id, getobj, nullptr,
1586
JSPROP_ENUMERATE | JSPROP_GETTER)) {
1587
return nullptr;
1588
}
1589
getobj->setExtendedSlot(0, Int32Value(i));
1590
}
1591
1592
return obj;
1593
}
1594
1595
bool DebuggerFrame::CallData::argumentsGetter() {
1596
if (!ensureOnStack()) {
1597
return false;
1598
}
1599
1600
RootedDebuggerArguments result(cx);
1601
if (!DebuggerFrame::getArguments(cx, frame, &result)) {
1602
return false;
1603
}
1604
1605
args.rval().setObjectOrNull(result);
1606
return true;
1607
}
1608
1609
bool DebuggerFrame::CallData::getScript() {
1610
if (!ensureOnStackOrSuspended()) {
1611
return false;
1612
}
1613
1614
RootedDebuggerScript scriptObject(cx);
1615
1616
Debugger* debug = Debugger::fromChildJSObject(frame);
1617
if (frame->isOnStack()) {
1618
FrameIter iter(*frame->frameIterData());
1619
AbstractFramePtr framePtr = iter.abstractFramePtr();
1620
1621
if (framePtr.isWasmDebugFrame()) {
1622
RootedWasmInstanceObject instance(cx, framePtr.wasmInstance()->object());
1623
scriptObject = debug->wrapWasmScript(cx, instance);
1624
} else {
1625
RootedScript script(cx, framePtr.script());
1626
scriptObject = debug->wrapScript(cx, script);
1627
}
1628
} else {
1629
MOZ_ASSERT(frame->hasGenerator());
1630
RootedScript script(cx, frame->generatorInfo()->generatorScript());
1631
scriptObject = debug->wrapScript(cx, script);
1632
}
1633
if (!scriptObject) {
1634
return false;
1635
}
1636
1637
args.rval().setObject(*scriptObject);
1638
return true;
1639
}
1640
1641
bool DebuggerFrame::CallData::offsetGetter() {
1642
if (!ensureOnStackOrSuspended()) {
1643
return false;
1644
}
1645
1646
size_t result;
1647
if (!DebuggerFrame::getOffset(cx, frame, result)) {
1648
return false;
1649
}
1650
1651
args.rval().setNumber(double(result));
1652
return true;
1653
}
1654
1655
bool DebuggerFrame::CallData::liveGetter() {
1656
JS_ReportErrorASCII(
1657
cx, "Debugger.Frame.prototype.live has been renamed to .onStack");
1658
return false;
1659
}
1660
1661
bool DebuggerFrame::CallData::onStackGetter() {
1662
args.rval().setBoolean(frame->isOnStack());
1663
return true;
1664
}
1665
1666
bool DebuggerFrame::CallData::terminatedGetter() {
1667
args.rval().setBoolean(!frame->isOnStack() && !frame->hasGenerator());
1668
return true;
1669
}
1670
1671
static bool IsValidHook(const Value& v) {
1672
return v.isUndefined() || (v.isObject() && v.toObject().isCallable());
1673
}
1674
1675
bool DebuggerFrame::CallData::onStepGetter() {
1676
OnStepHandler* handler = frame->onStepHandler();
1677
RootedValue value(
1678
cx, handler ? ObjectOrNullValue(handler->object()) : UndefinedValue());
1679
MOZ_ASSERT(IsValidHook(value));
1680
args.rval().set(value);
1681
return true;
1682
}
1683
1684
bool DebuggerFrame::CallData::onStepSetter() {
1685
if (!args.requireAtLeast(cx, "Debugger.Frame.set onStep", 1)) {
1686
return false;
1687
}
1688
if (!IsValidHook(args[0])) {
1689
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1690
JSMSG_NOT_CALLABLE_OR_UNDEFINED);
1691
return false;
1692
}
1693
1694
ScriptedOnStepHandler* handler = nullptr;
1695
if (!args[0].isUndefined()) {
1696
handler = cx->new_<ScriptedOnStepHandler>(&args[0].toObject());
1697
if (!handler) {
1698
return false;
1699
}
1700
}
1701
1702
if (!DebuggerFrame::setOnStepHandler(cx, frame, handler)) {
1703
// Handler has never been successfully associated with the frame so just
1704
// delete it rather than calling drop().
1705
js_delete(handler);
1706
return false;
1707
}
1708
1709
args.rval().setUndefined();
1710
return true;
1711
}
1712
1713
bool DebuggerFrame::CallData::onPopGetter() {
1714
OnPopHandler* handler = frame->onPopHandler();
1715
RootedValue value(
1716
cx, handler ? ObjectValue(*handler->object()) : UndefinedValue());
1717
MOZ_ASSERT(IsValidHook(value));
1718
args.rval().set(value);
1719
return true;
1720
}
1721
1722
bool DebuggerFrame::CallData::onPopSetter() {
1723
if (!args.requireAtLeast(cx, "Debugger.Frame.set onPop", 1)) {
1724
return false;
1725
}
1726
if (!IsValidHook(args[0])) {
1727
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1728
JSMSG_NOT_CALLABLE_OR_UNDEFINED);
1729
return false;
1730
}
1731
1732
ScriptedOnPopHandler* handler = nullptr;
1733
if (!args[0].isUndefined()) {
1734
handler = cx->new_<ScriptedOnPopHandler>(&args[0].toObject());
1735
if (!handler) {
1736
return false;
1737
}
1738
}
1739
1740
frame->setOnPopHandler(cx, handler);
1741
1742
args.rval().setUndefined();
1743
return true;
1744
}
1745
1746
bool DebuggerFrame::CallData::evalMethod() {
1747
if (!ensureOnStack()) {
1748
return false;
1749
}
1750
1751
if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.eval", 1)) {
1752
return false;
1753
}
1754
1755
AutoStableStringChars stableChars(cx);
1756
if (!ValueToStableChars(cx, "Debugger.Frame.prototype.eval", args[0],
1757
stableChars)) {
1758
return false;
1759
}
1760
mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
1761
1762
EvalOptions options;
1763
if (!ParseEvalOptions(cx, args.get(1), options)) {
1764
return false;
1765
}
1766
1767
Rooted<Completion> comp(cx);
1768
JS_TRY_VAR_OR_RETURN_FALSE(
1769
cx, comp, DebuggerFrame::eval(cx, frame, chars, nullptr, options));
1770
return comp.get().buildCompletionValue(cx, frame->owner(), args.rval());
1771
}
1772
1773
bool DebuggerFrame::CallData::evalWithBindingsMethod() {
1774
if (!ensureOnStack()) {
1775
return false;
1776
}
1777
1778
if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.evalWithBindings",
1779
2)) {
1780
return false;
1781
}
1782
1783
AutoStableStringChars stableChars(cx);
1784
if (!ValueToStableChars(cx, "Debugger.Frame.prototype.evalWithBindings",
1785
args[0], stableChars)) {
1786
return false;
1787
}
1788
mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
1789
1790
RootedObject bindings(cx, RequireObject(cx, args[1]));
1791
if (!bindings) {
1792
return false;
1793
}
1794
1795
EvalOptions options;
1796
if (!ParseEvalOptions(cx, args.get(2), options)) {
1797
return false;
1798
}
1799
1800
Rooted<Completion> comp(cx);
1801
JS_TRY_VAR_OR_RETURN_FALSE(
1802
cx, comp, DebuggerFrame::eval(cx, frame, chars, bindings, options));