Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "vm/Debugger-inl.h"
8 :
9 : #include "mozilla/DebugOnly.h"
10 : #include "mozilla/ScopeExit.h"
11 : #include "mozilla/Sprintf.h"
12 : #include "mozilla/TypeTraits.h"
13 :
14 : #include <utility>
15 :
16 : #include "jsfriendapi.h"
17 : #include "jsnum.h"
18 :
19 : #include "frontend/BytecodeCompiler.h"
20 : #include "frontend/Parser.h"
21 : #include "gc/FreeOp.h"
22 : #include "gc/HashUtil.h"
23 : #include "gc/Marking.h"
24 : #include "gc/Policy.h"
25 : #include "gc/PublicIterators.h"
26 : #include "jit/BaselineDebugModeOSR.h"
27 : #include "jit/BaselineJIT.h"
28 : #include "js/Date.h"
29 : #include "js/UbiNodeBreadthFirst.h"
30 : #include "js/Vector.h"
31 : #include "js/Wrapper.h"
32 : #include "proxy/ScriptedProxyHandler.h"
33 : #include "util/Text.h"
34 : #include "vm/ArgumentsObject.h"
35 : #include "vm/AsyncFunction.h"
36 : #include "vm/AsyncIteration.h"
37 : #include "vm/DebuggerMemory.h"
38 : #include "vm/GeckoProfiler.h"
39 : #include "vm/GeneratorObject.h"
40 : #include "vm/JSContext.h"
41 : #include "vm/JSObject.h"
42 : #include "vm/Realm.h"
43 : #include "vm/TraceLogging.h"
44 : #include "vm/WrapperObject.h"
45 : #include "wasm/WasmInstance.h"
46 :
47 : #include "gc/GC-inl.h"
48 : #include "vm/BytecodeUtil-inl.h"
49 : #include "vm/Compartment-inl.h"
50 : #include "vm/GeckoProfiler-inl.h"
51 : #include "vm/JSObject-inl.h"
52 : #include "vm/JSScript-inl.h"
53 : #include "vm/NativeObject-inl.h"
54 : #include "vm/Stack-inl.h"
55 :
56 : using namespace js;
57 :
58 : using JS::dbg::AutoEntryMonitor;
59 : using JS::dbg::Builder;
60 : using js::frontend::IsIdentifier;
61 : using mozilla::DebugOnly;
62 : using mozilla::MakeScopeExit;
63 : using mozilla::Maybe;
64 : using mozilla::Some;
65 : using mozilla::Nothing;
66 : using mozilla::AsVariant;
67 : using mozilla::TimeDuration;
68 : using mozilla::TimeStamp;
69 :
70 :
71 : /*** Forward declarations, ClassOps and Classes **************************************************/
72 :
73 : static void DebuggerFrame_finalize(FreeOp* fop, JSObject* obj);
74 : static void DebuggerFrame_trace(JSTracer* trc, JSObject* obj);
75 : static void DebuggerEnv_trace(JSTracer* trc, JSObject* obj);
76 : static void DebuggerObject_trace(JSTracer* trc, JSObject* obj);
77 : static void DebuggerScript_trace(JSTracer* trc, JSObject* obj);
78 : static void DebuggerSource_trace(JSTracer* trc, JSObject* obj);
79 :
80 : enum {
81 : JSSLOT_DEBUGFRAME_OWNER,
82 : JSSLOT_DEBUGFRAME_ARGUMENTS,
83 : JSSLOT_DEBUGFRAME_ONSTEP_HANDLER,
84 : JSSLOT_DEBUGFRAME_ONPOP_HANDLER,
85 : JSSLOT_DEBUGFRAME_COUNT
86 : };
87 :
88 : const ClassOps DebuggerFrame::classOps_ = {
89 : nullptr, /* addProperty */
90 : nullptr, /* delProperty */
91 : nullptr, /* enumerate */
92 : nullptr, /* newEnumerate */
93 : nullptr, /* resolve */
94 : nullptr, /* mayResolve */
95 : DebuggerFrame_finalize,
96 : nullptr, /* call */
97 : nullptr, /* hasInstance */
98 : nullptr, /* construct */
99 : DebuggerFrame_trace
100 : };
101 :
102 : const Class DebuggerFrame::class_ = {
103 : "Frame",
104 : JSCLASS_HAS_PRIVATE |
105 : JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT) |
106 : JSCLASS_BACKGROUND_FINALIZE,
107 : &DebuggerFrame::classOps_
108 : };
109 :
110 : enum {
111 : JSSLOT_DEBUGARGUMENTS_FRAME,
112 : JSSLOT_DEBUGARGUMENTS_COUNT
113 : };
114 :
115 : const Class DebuggerArguments::class_ = {
116 : "Arguments",
117 : JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT)
118 : };
119 :
120 : const ClassOps DebuggerEnvironment::classOps_ = {
121 : nullptr, /* addProperty */
122 : nullptr, /* delProperty */
123 : nullptr, /* enumerate */
124 : nullptr, /* newEnumerate */
125 : nullptr, /* resolve */
126 : nullptr, /* mayResolve */
127 : nullptr, /* finalize */
128 : nullptr, /* call */
129 : nullptr, /* hasInstance */
130 : nullptr, /* construct */
131 : DebuggerEnv_trace
132 : };
133 :
134 : const Class DebuggerEnvironment::class_ = {
135 : "Environment",
136 : JSCLASS_HAS_PRIVATE |
137 : JSCLASS_HAS_RESERVED_SLOTS(DebuggerEnvironment::RESERVED_SLOTS),
138 : &classOps_
139 : };
140 :
141 : enum {
142 : JSSLOT_DEBUGOBJECT_OWNER,
143 : JSSLOT_DEBUGOBJECT_COUNT
144 : };
145 :
146 : const ClassOps DebuggerObject::classOps_ = {
147 : nullptr, /* addProperty */
148 : nullptr, /* delProperty */
149 : nullptr, /* enumerate */
150 : nullptr, /* newEnumerate */
151 : nullptr, /* resolve */
152 : nullptr, /* mayResolve */
153 : nullptr, /* finalize */
154 : nullptr, /* call */
155 : nullptr, /* hasInstance */
156 : nullptr, /* construct */
157 : DebuggerObject_trace
158 : };
159 :
160 : const Class DebuggerObject::class_ = {
161 : "Object",
162 : JSCLASS_HAS_PRIVATE |
163 : JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
164 : &classOps_
165 : };
166 :
167 : enum {
168 : JSSLOT_DEBUGSCRIPT_OWNER,
169 : JSSLOT_DEBUGSCRIPT_COUNT
170 : };
171 :
172 : static const ClassOps DebuggerScript_classOps = {
173 : nullptr, /* addProperty */
174 : nullptr, /* delProperty */
175 : nullptr, /* enumerate */
176 : nullptr, /* newEnumerate */
177 : nullptr, /* resolve */
178 : nullptr, /* mayResolve */
179 : nullptr, /* finalize */
180 : nullptr, /* call */
181 : nullptr, /* hasInstance */
182 : nullptr, /* construct */
183 : DebuggerScript_trace
184 : };
185 :
186 : static const Class DebuggerScript_class = {
187 : "Script",
188 : JSCLASS_HAS_PRIVATE |
189 : JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
190 : &DebuggerScript_classOps
191 : };
192 :
193 : enum {
194 : JSSLOT_DEBUGSOURCE_OWNER,
195 : JSSLOT_DEBUGSOURCE_TEXT,
196 : JSSLOT_DEBUGSOURCE_COUNT
197 : };
198 :
199 : static const ClassOps DebuggerSource_classOps = {
200 : nullptr, /* addProperty */
201 : nullptr, /* delProperty */
202 : nullptr, /* enumerate */
203 : nullptr, /* newEnumerate */
204 : nullptr, /* resolve */
205 : nullptr, /* mayResolve */
206 : nullptr, /* finalize */
207 : nullptr, /* call */
208 : nullptr, /* hasInstance */
209 : nullptr, /* construct */
210 : DebuggerSource_trace
211 : };
212 :
213 : static const Class DebuggerSource_class = {
214 : "Source",
215 : JSCLASS_HAS_PRIVATE |
216 : JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),
217 : &DebuggerSource_classOps
218 : };
219 :
220 :
221 : /*** Utils ***************************************************************************************/
222 :
223 : /*
224 : * If fun is an interpreted function, remove any async function/generator
225 : * wrapper and return the underlying scripted function. Otherwise, return fun
226 : * unchanged.
227 : *
228 : * Async functions are implemented as native functions wrapped around a scripted
229 : * function. JSScripts hold ordinary inner JSFunctions in their object arrays,
230 : * and when we need to actually create a JS-visible function object, we build an
231 : * ordinary JS closure and apply the async wrapper to it. Async generators are
232 : * similar.
233 : *
234 : * This means that JSFunction::isInterpreted returns false for such functions,
235 : * even though their actual code is indeed JavaScript. Debugger should treat
236 : * async functions and generators like any other scripted function, so we must
237 : * carefully check for them whenever we want inspect a function.
238 : */
239 :
240 : static JSFunction*
241 0 : RemoveAsyncWrapper(JSFunction *fun)
242 : {
243 0 : if (js::IsWrappedAsyncFunction(fun))
244 0 : fun = js::GetUnwrappedAsyncFunction(fun);
245 0 : else if (js::IsWrappedAsyncGenerator(fun))
246 0 : fun = js::GetUnwrappedAsyncGenerator(fun);
247 :
248 0 : return fun;
249 : }
250 :
251 : static inline bool
252 0 : EnsureFunctionHasScript(JSContext* cx, HandleFunction fun)
253 : {
254 0 : if (fun->isInterpretedLazy()) {
255 0 : AutoRealm ar(cx, fun);
256 0 : return !!JSFunction::getOrCreateScript(cx, fun);
257 : }
258 : return true;
259 : }
260 :
261 : static inline JSScript*
262 0 : GetOrCreateFunctionScript(JSContext* cx, HandleFunction fun)
263 : {
264 0 : MOZ_ASSERT(fun->isInterpreted());
265 0 : if (!EnsureFunctionHasScript(cx, fun))
266 : return nullptr;
267 0 : return fun->nonLazyScript();
268 : }
269 :
270 : static bool
271 0 : ValueToIdentifier(JSContext* cx, HandleValue v, MutableHandleId id)
272 : {
273 0 : if (!ValueToId<CanGC>(cx, v, id))
274 : return false;
275 0 : if (!JSID_IS_ATOM(id) || !IsIdentifier(JSID_TO_ATOM(id))) {
276 0 : RootedValue val(cx, v);
277 0 : ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, val, nullptr,
278 : "not an identifier");
279 0 : return false;
280 : }
281 : return true;
282 : }
283 :
284 : class js::AutoRestoreRealmDebugMode
285 : {
286 : Realm* realm_;
287 : unsigned bits_;
288 :
289 : public:
290 : explicit AutoRestoreRealmDebugMode(Realm* realm)
291 0 : : realm_(realm), bits_(realm->debugModeBits_)
292 0 : {
293 : MOZ_ASSERT(realm_);
294 0 : }
295 0 :
296 : ~AutoRestoreRealmDebugMode() {
297 0 : if (realm_)
298 0 : realm_->debugModeBits_ = bits_;
299 0 : }
300 :
301 : void release() {
302 : realm_ = nullptr;
303 0 : }
304 : };
305 :
306 : // Given a Debugger instance dbg, if it is enabled, prevents all its debuggee
307 : // compartments from executing scripts. Attempts to run script will throw an
308 : // instance of Debugger.DebuggeeWouldRun from the topmost locked Debugger's
309 : // compartment.
310 : class MOZ_RAII js::EnterDebuggeeNoExecute
311 : {
312 : friend class js::LeaveDebuggeeNoExecute;
313 :
314 : Debugger& dbg_;
315 : EnterDebuggeeNoExecute** stack_;
316 : EnterDebuggeeNoExecute* prev_;
317 :
318 : // Non-nullptr when unlocked temporarily by a LeaveDebuggeeNoExecute.
319 : LeaveDebuggeeNoExecute* unlocked_;
320 :
321 : // When DebuggeeWouldRun is a warning instead of an error, whether we've
322 : // reported a warning already.
323 : bool reported_;
324 :
325 : public:
326 : explicit EnterDebuggeeNoExecute(JSContext* cx, Debugger& dbg)
327 : : dbg_(dbg),
328 0 : unlocked_(nullptr),
329 : reported_(false)
330 0 : {
331 : stack_ = &cx->noExecuteDebuggerTop.ref();
332 0 : prev_ = *stack_;
333 0 : *stack_ = this;
334 0 : }
335 :
336 : ~EnterDebuggeeNoExecute() {
337 0 : MOZ_ASSERT(*stack_ == this);
338 0 : *stack_ = prev_;
339 0 : }
340 0 :
341 : Debugger& debugger() const {
342 : return dbg_;
343 : }
344 :
345 : #ifdef DEBUG
346 : static bool isLockedInStack(JSContext* cx, Debugger& dbg) {
347 0 : for (EnterDebuggeeNoExecute* it = cx->noExecuteDebuggerTop; it; it = it->prev_) {
348 0 : if (&it->debugger() == &dbg)
349 0 : return !it->unlocked_;
350 0 : }
351 : return false;
352 : }
353 : #endif
354 :
355 : // Given a JSContext entered into a debuggee realm, find the lock
356 : // that locks it. Returns nullptr if not found.
357 : static EnterDebuggeeNoExecute* findInStack(JSContext* cx) {
358 0 : Realm* debuggee = cx->realm();
359 0 : for (EnterDebuggeeNoExecute* it = cx->noExecuteDebuggerTop; it; it = it->prev_) {
360 0 : Debugger& dbg = it->debugger();
361 0 : if (!it->unlocked_ && dbg.isEnabled() && dbg.observesGlobal(debuggee->maybeGlobal()))
362 0 : return it;
363 : }
364 : return nullptr;
365 : }
366 :
367 : // Given a JSContext entered into a debuggee compartment, report a
368 : // warning or an error if there is a lock that locks it.
369 : static bool reportIfFoundInStack(JSContext* cx, HandleScript script) {
370 0 : if (EnterDebuggeeNoExecute* nx = findInStack(cx)) {
371 0 : bool warning = !cx->options().throwOnDebuggeeWouldRun();
372 0 : if (!warning || !nx->reported_) {
373 0 : AutoRealm ar(cx, nx->debugger().toJSObject());
374 0 : nx->reported_ = true;
375 0 : if (cx->options().dumpStackOnDebuggeeWouldRun()) {
376 0 : fprintf(stdout, "Dumping stack for DebuggeeWouldRun:\n");
377 0 : DumpBacktrace(cx);
378 0 : }
379 : const char* filename = script->filename() ? script->filename() : "(none)";
380 0 : char linenoStr[15];
381 : SprintfLiteral(linenoStr, "%u", script->lineno());
382 0 : unsigned flags = warning ? JSREPORT_WARNING : JSREPORT_ERROR;
383 0 : // FIXME: filename should be UTF-8 (bug 987069).
384 : return JS_ReportErrorFlagsAndNumberLatin1(cx, flags, GetErrorMessage, nullptr,
385 : JSMSG_DEBUGGEE_WOULD_RUN,
386 : filename, linenoStr);
387 0 : }
388 : }
389 : return true;
390 : }
391 : };
392 :
393 : // Given a JSContext entered into a debuggee compartment, if it is in
394 : // an NX section, unlock the topmost EnterDebuggeeNoExecute instance.
395 : //
396 : // Does nothing if debuggee is not in an NX section. For example, this
397 : // situation arises when invocation functions are called without entering
398 : // Debugger code, e.g., calling D.O.p.executeInGlobal or D.O.p.apply.
399 : class MOZ_RAII js::LeaveDebuggeeNoExecute
400 : {
401 : EnterDebuggeeNoExecute* prevLocked_;
402 :
403 : public:
404 : explicit LeaveDebuggeeNoExecute(JSContext* cx)
405 0 : : prevLocked_(EnterDebuggeeNoExecute::findInStack(cx))
406 0 : {
407 : if (prevLocked_) {
408 0 : MOZ_ASSERT(!prevLocked_->unlocked_);
409 0 : prevLocked_->unlocked_ = this;
410 0 : }
411 : }
412 0 :
413 : ~LeaveDebuggeeNoExecute() {
414 0 : if (prevLocked_) {
415 0 : MOZ_ASSERT(prevLocked_->unlocked_ == this);
416 0 : prevLocked_->unlocked_ = nullptr;
417 0 : }
418 : }
419 0 : };
420 :
421 : /* static */ bool
422 : Debugger::slowPathCheckNoExecute(JSContext* cx, HandleScript script)
423 0 : {
424 : MOZ_ASSERT(cx->realm()->isDebuggee());
425 0 : MOZ_ASSERT(cx->noExecuteDebuggerTop);
426 0 : return EnterDebuggeeNoExecute::reportIfFoundInStack(cx, script);
427 0 : }
428 :
429 : static inline void
430 : NukeDebuggerWrapper(NativeObject *wrapper)
431 : {
432 : // In some OOM failure cases, we need to destroy the edge to the referent,
433 : // to avoid trying to trace it during untimely collections.
434 : wrapper->setPrivate(nullptr);
435 0 : }
436 :
437 : static bool
438 : ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value,
439 0 : AutoStableStringChars& stableChars)
440 : {
441 : if (!value.isString()) {
442 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
443 0 : fnname, "string", InformalValueTypeName(value));
444 0 : return false;
445 0 : }
446 : RootedLinearString linear(cx, value.toString()->ensureLinear(cx));
447 0 : if (!linear)
448 0 : return false;
449 : if (!stableChars.initTwoByte(cx, linear))
450 0 : return false;
451 : return true;
452 0 : }
453 :
454 : bool
455 : EvalOptions::setFilename(JSContext* cx, const char* filename)
456 0 : {
457 : JS::UniqueChars copy;
458 0 : if (filename) {
459 0 : copy = DuplicateString(cx, filename);
460 0 : if (!copy)
461 0 : return false;
462 : }
463 :
464 : filename_ = std::move(copy);
465 0 : return true;
466 : }
467 :
468 : static bool
469 : ParseEvalOptions(JSContext* cx, HandleValue value, EvalOptions& options)
470 0 : {
471 : if (!value.isObject())
472 0 : return true;
473 :
474 : RootedObject opts(cx, &value.toObject());
475 0 :
476 : RootedValue v(cx);
477 0 : if (!JS_GetProperty(cx, opts, "url", &v))
478 0 : return false;
479 : if (!v.isUndefined()) {
480 0 : RootedString url_str(cx, ToString<CanGC>(cx, v));
481 0 : if (!url_str)
482 0 : return false;
483 0 : JSAutoByteString url_bytes(cx, url_str);
484 0 : if (!url_bytes)
485 0 : return false;
486 : if (!options.setFilename(cx, url_bytes.ptr()))
487 0 : return false;
488 : }
489 :
490 : if (!JS_GetProperty(cx, opts, "lineNumber", &v))
491 0 : return false;
492 : if (!v.isUndefined()) {
493 0 : uint32_t lineno;
494 : if (!ToUint32(cx, v, &lineno))
495 0 : return false;
496 0 : options.setLineno(lineno);
497 0 : }
498 :
499 : return true;
500 : }
501 :
502 : static bool
503 : RequireGlobalObject(JSContext* cx, HandleValue dbgobj, HandleObject referent)
504 0 : {
505 : RootedObject obj(cx, referent);
506 0 :
507 : if (!obj->is<GlobalObject>()) {
508 0 : const char* isWrapper = "";
509 0 : const char* isWindowProxy = "";
510 0 :
511 : /* Help the poor programmer by pointing out wrappers around globals... */
512 : if (obj->is<WrapperObject>()) {
513 0 : obj = js::UncheckedUnwrap(obj);
514 0 : isWrapper = "a wrapper around ";
515 0 : }
516 :
517 : /* ... and WindowProxies around Windows. */
518 : if (IsWindowProxy(obj)) {
519 0 : obj = ToWindowIfWindowProxy(obj);
520 0 : isWindowProxy = "a WindowProxy referring to ";
521 0 : }
522 :
523 : if (obj->is<GlobalObject>()) {
524 0 : ReportValueError(cx, JSMSG_DEBUG_WRAPPER_IN_WAY, JSDVG_SEARCH_STACK, dbgobj, nullptr,
525 0 : isWrapper, isWindowProxy);
526 : } else {
527 0 : ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, dbgobj, nullptr,
528 : "a global object");
529 0 : }
530 : return false;
531 0 : }
532 :
533 : return true;
534 : }
535 :
536 :
537 : /*** Breakpoints *********************************************************************************/
538 :
539 : BreakpointSite::BreakpointSite(Type type)
540 : : type_(type), enabledCount(0)
541 : {
542 0 : }
543 0 :
544 : void
545 0 : BreakpointSite::inc(FreeOp* fop)
546 : {
547 : enabledCount++;
548 0 : if (enabledCount == 1)
549 : recompile(fop);
550 0 : }
551 0 :
552 0 : void
553 0 : BreakpointSite::dec(FreeOp* fop)
554 : {
555 : MOZ_ASSERT(enabledCount > 0);
556 0 : enabledCount--;
557 : if (enabledCount == 0)
558 0 : recompile(fop);
559 0 : }
560 0 :
561 0 : bool
562 0 : BreakpointSite::isEmpty() const
563 : {
564 : return breakpoints.isEmpty();
565 0 : }
566 :
567 0 : Breakpoint*
568 : BreakpointSite::firstBreakpoint() const
569 : {
570 : if (isEmpty())
571 0 : return nullptr;
572 : return &(*breakpoints.begin());
573 0 : }
574 :
575 0 : bool
576 : BreakpointSite::hasBreakpoint(Breakpoint* toFind)
577 : {
578 : const BreakpointList::Iterator bp(toFind);
579 0 : for (auto p = breakpoints.begin(); p; p++)
580 : if (p == bp)
581 0 : return true;
582 0 : return false;
583 0 : }
584 :
585 : Breakpoint::Breakpoint(Debugger* debugger, BreakpointSite* site, JSObject* handler)
586 : : debugger(debugger), site(site), handler(handler)
587 : {
588 0 : MOZ_ASSERT(handler->compartment() == debugger->object->compartment());
589 0 : debugger->breakpoints.pushBack(this);
590 : site->breakpoints.pushBack(this);
591 0 : }
592 0 :
593 0 : void
594 0 : Breakpoint::destroy(FreeOp* fop)
595 : {
596 : if (debugger->enabled)
597 0 : site->dec(fop);
598 : debugger->breakpoints.remove(this);
599 0 : site->breakpoints.remove(this);
600 0 : site->destroyIfEmpty(fop);
601 0 : fop->delete_(this);
602 0 : }
603 0 :
604 0 : Breakpoint*
605 0 : Breakpoint::nextInDebugger()
606 : {
607 : return debuggerLink.mNext;
608 0 : }
609 :
610 0 : Breakpoint*
611 : Breakpoint::nextInSite()
612 : {
613 : return siteLink.mNext;
614 0 : }
615 :
616 0 : JSBreakpointSite::JSBreakpointSite(JSScript* script, jsbytecode* pc)
617 : : BreakpointSite(Type::JS),
618 : script(script),
619 0 : pc(pc)
620 : {
621 : MOZ_ASSERT(!script->hasBreakpointsAt(pc));
622 0 : }
623 :
624 0 : void
625 0 : JSBreakpointSite::recompile(FreeOp* fop)
626 : {
627 : if (script->hasBaselineScript())
628 0 : script->baselineScript()->toggleDebugTraps(script, pc);
629 : }
630 0 :
631 0 : void
632 0 : JSBreakpointSite::destroyIfEmpty(FreeOp* fop)
633 : {
634 : if (isEmpty())
635 0 : script->destroyBreakpointSite(fop, pc);
636 : }
637 0 :
638 0 : WasmBreakpointSite::WasmBreakpointSite(wasm::DebugState* debug_, uint32_t offset_)
639 0 : : BreakpointSite(Type::Wasm), debug(debug_), offset(offset_)
640 : {
641 0 : MOZ_ASSERT(debug_);
642 0 : }
643 :
644 0 : void
645 0 : WasmBreakpointSite::recompile(FreeOp* fop)
646 : {
647 : debug->toggleBreakpointTrap(fop->runtime(), offset, isEnabled());
648 0 : }
649 :
650 0 : void
651 0 : WasmBreakpointSite::destroyIfEmpty(FreeOp* fop)
652 : {
653 : if (isEmpty())
654 0 : debug->destroyBreakpointSite(fop, offset);
655 : }
656 0 :
657 0 : /*** Debugger hook dispatch **********************************************************************/
658 0 :
659 : Debugger::Debugger(JSContext* cx, NativeObject* dbg)
660 : : object(dbg),
661 : debuggees(cx->zone()),
662 0 : uncaughtExceptionHook(nullptr),
663 : enabled(true),
664 : allowUnobservedAsmJS(false),
665 : allowWasmBinarySource(false),
666 : collectCoverageInfo(false),
667 : observedGCs(cx->zone()),
668 : allocationsLog(cx),
669 : trackingAllocationSites(false),
670 : allocationSamplingProbability(1.0),
671 : maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH),
672 : allocationsLogOverflowed(false),
673 : frames(cx->zone()),
674 : scripts(cx),
675 : sources(cx),
676 : objects(cx),
677 : environments(cx),
678 : wasmInstanceScripts(cx),
679 : wasmInstanceSources(cx),
680 : #ifdef NIGHTLY_BUILD
681 : traceLoggerLastDrainedSize(0),
682 : traceLoggerLastDrainedIteration(0),
683 : #endif
684 : traceLoggerScriptedCallsLastDrainedSize(0),
685 : traceLoggerScriptedCallsLastDrainedIteration(0)
686 : {
687 : assertSameCompartment(cx, dbg);
688 0 :
689 : #ifdef JS_TRACE_LOGGING
690 0 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
691 : if (logger) {
692 : #ifdef NIGHTLY_BUILD
693 0 : logger->getIterationAndSize(&traceLoggerLastDrainedIteration, &traceLoggerLastDrainedSize);
694 0 : #endif
695 : logger->getIterationAndSize(&traceLoggerScriptedCallsLastDrainedIteration,
696 0 : &traceLoggerScriptedCallsLastDrainedSize);
697 : }
698 0 : #endif
699 : }
700 :
701 : Debugger::~Debugger()
702 0 : {
703 : MOZ_ASSERT_IF(debuggees.initialized(), debuggees.empty());
704 0 : allocationsLog.clear();
705 :
706 0 : /*
707 0 : * We don't have to worry about locking here since Debugger is not
708 : * background finalized.
709 : */
710 : JSContext* cx = TlsContext.get();
711 : if (onNewGlobalObjectWatchersLink.mPrev ||
712 : onNewGlobalObjectWatchersLink.mNext ||
713 0 : cx->runtime()->onNewGlobalObjectWatchers().begin() == JSRuntime::WatchersList::Iterator(this))
714 0 : {
715 0 : cx->runtime()->onNewGlobalObjectWatchers().remove(this);
716 0 : }
717 : }
718 0 :
719 : bool
720 0 : Debugger::init(JSContext* cx)
721 : {
722 : if (!debuggees.init() ||
723 0 : !debuggeeZones.init() ||
724 : !frames.init() ||
725 0 : !scripts.init() ||
726 0 : !sources.init() ||
727 0 : !objects.init() ||
728 0 : !observedGCs.init() ||
729 0 : !environments.init() ||
730 0 : !wasmInstanceScripts.init() ||
731 0 : !wasmInstanceSources.init())
732 0 : {
733 0 : ReportOutOfMemory(cx);
734 0 : return false;
735 : }
736 0 :
737 0 : cx->runtime()->debuggerList().insertBack(this);
738 : return true;
739 : }
740 0 :
741 0 : JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGSCRIPT_OWNER));
742 : JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGSOURCE_OWNER));
743 : JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGOBJECT_OWNER));
744 : JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(DebuggerEnvironment::OWNER_SLOT));
745 :
746 : /* static */ Debugger*
747 : Debugger::fromChildJSObject(JSObject* obj)
748 : {
749 : MOZ_ASSERT(obj->getClass() == &DebuggerFrame::class_ ||
750 0 : obj->getClass() == &DebuggerScript_class ||
751 : obj->getClass() == &DebuggerSource_class ||
752 0 : obj->getClass() == &DebuggerObject::class_ ||
753 : obj->getClass() == &DebuggerEnvironment::class_);
754 : JSObject* dbgobj = &obj->as<NativeObject>().getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER).toObject();
755 : return fromJSObject(dbgobj);
756 : }
757 0 :
758 0 : bool
759 : Debugger::hasMemory() const
760 : {
761 : return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE).isObject();
762 0 : }
763 :
764 0 : DebuggerMemory&
765 : Debugger::memory() const
766 : {
767 : MOZ_ASSERT(hasMemory());
768 0 : return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE).toObject().as<DebuggerMemory>();
769 : }
770 0 :
771 0 : bool
772 : Debugger::getFrame(JSContext* cx, const FrameIter& iter, MutableHandleValue vp)
773 : {
774 : RootedDebuggerFrame result(cx);
775 0 : if (!Debugger::getFrame(cx, iter, &result))
776 : return false;
777 0 : vp.setObject(*result);
778 0 : return true;
779 : }
780 0 :
781 0 : bool
782 : Debugger::getFrame(JSContext* cx, const FrameIter& iter, MutableHandleDebuggerFrame result)
783 : {
784 : AbstractFramePtr referent = iter.abstractFramePtr();
785 0 : MOZ_ASSERT_IF(referent.hasScript(), !referent.script()->selfHosted());
786 :
787 0 : if (referent.hasScript() && !referent.script()->ensureHasAnalyzedArgsUsage(cx))
788 0 : return false;
789 :
790 0 : FrameMap::AddPtr p = frames.lookupForAdd(referent);
791 : if (!p) {
792 : /* Create and populate the Debugger.Frame object. */
793 0 : RootedObject proto(cx, &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject());
794 0 : RootedNativeObject debugger(cx, object);
795 :
796 0 : RootedDebuggerFrame frame(cx, DebuggerFrame::create(cx, proto, iter, debugger));
797 0 : if (!frame)
798 : return false;
799 0 :
800 0 : if (!ensureExecutionObservabilityOfFrame(cx, referent))
801 0 : return false;
802 :
803 0 : if (!frames.add(p, referent, frame)) {
804 : ReportOutOfMemory(cx);
805 : return false;
806 0 : }
807 0 : }
808 0 :
809 : result.set(&p->value()->as<DebuggerFrame>());
810 : return true;
811 : }
812 0 :
813 0 : /* static */ bool
814 : Debugger::hasLiveHook(GlobalObject* global, Hook which)
815 : {
816 : if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
817 0 : for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
818 : Debugger* dbg = *p;
819 0 : if (dbg->enabled && dbg->getHook(which))
820 0 : return true;
821 0 : }
822 0 : }
823 : return false;
824 : }
825 :
826 : JSObject*
827 : Debugger::getHook(Hook hook) const
828 : {
829 : MOZ_ASSERT(hook >= 0 && hook < HookCount);
830 0 : const Value& v = object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + hook);
831 : return v.isUndefined() ? nullptr : &v.toObject();
832 0 : }
833 0 :
834 0 : bool
835 : Debugger::hasAnyLiveHooks(JSRuntime* rt) const
836 : {
837 : if (!enabled)
838 0 : return false;
839 :
840 0 : if (getHook(OnDebuggerStatement) ||
841 : getHook(OnExceptionUnwind) ||
842 : getHook(OnNewScript) ||
843 0 : getHook(OnEnterFrame))
844 0 : {
845 0 : return true;
846 0 : }
847 :
848 : /* If any breakpoints are in live scripts, return true. */
849 : for (Breakpoint* bp = firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
850 : switch (bp->site->type()) {
851 : case BreakpointSite::Type::JS:
852 0 : if (IsMarkedUnbarriered(rt, &bp->site->asJS()->script))
853 0 : return true;
854 : break;
855 0 : case BreakpointSite::Type::Wasm:
856 : if (IsMarkedUnbarriered(rt, &bp->asWasm()->wasmInstance))
857 : return true;
858 : break;
859 0 : }
860 : }
861 :
862 : for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
863 : NativeObject* frameObj = r.front().value();
864 : if (!frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() ||
865 0 : !frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined())
866 0 : return true;
867 0 : }
868 0 :
869 0 : return false;
870 : }
871 :
872 0 : /* static */ ResumeMode
873 : Debugger::slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame)
874 : {
875 : RootedValue rval(cx);
876 0 : ResumeMode resumeMode = dispatchHook(
877 : cx,
878 0 : [frame](Debugger* dbg) -> bool {
879 0 : return dbg->observesFrame(frame) && dbg->observesEnterFrame();
880 : },
881 0 : [&](Debugger* dbg) -> ResumeMode {
882 0 : return dbg->fireEnterFrame(cx, &rval);
883 : });
884 :
885 0 : switch (resumeMode) {
886 0 : case ResumeMode::Continue:
887 : break;
888 0 :
889 : case ResumeMode::Throw:
890 : cx->setPendingException(rval);
891 : break;
892 :
893 0 : case ResumeMode::Terminate:
894 0 : cx->clearPendingException();
895 : break;
896 :
897 0 : case ResumeMode::Return:
898 0 : frame.setReturnValue(rval);
899 : break;
900 :
901 0 : default:
902 0 : MOZ_CRASH("bad Debugger::onEnterFrame resume mode");
903 : }
904 :
905 0 : return resumeMode;
906 : }
907 :
908 0 : static void
909 : DebuggerFrame_maybeDecrementFrameScriptStepModeCount(FreeOp* fop, AbstractFramePtr frame,
910 : NativeObject* frameobj);
911 :
912 : /*
913 : * Handle leaving a frame with debuggers watching. |frameOk| indicates whether
914 : * the frame is exiting normally or abruptly. Set |cx|'s exception and/or
915 : * |cx->fp()|'s return value, and return a new success value.
916 : */
917 : /* static */ bool
918 : Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc, bool frameOk)
919 : {
920 : mozilla::DebugOnly<Handle<GlobalObject*>> debuggeeGlobal = cx->global();
921 0 :
922 : auto frameMapsGuard = MakeScopeExit([&] {
923 0 : // Clean up all Debugger.Frame instances.
924 : removeFromFrameMapsAndClearBreakpointsIn(cx, frame);
925 : });
926 :
927 0 : // The onPop handler and associated clean up logic should not run multiple
928 0 : // times on the same frame. If slowPathOnLeaveFrame has already been
929 : // called, the frame will not be present in the Debugger frame maps.
930 : Rooted<DebuggerFrameVector> frames(cx, DebuggerFrameVector(cx));
931 : if (!getDebuggerFrames(frame, &frames))
932 : return false;
933 0 : if (frames.empty())
934 0 : return frameOk;
935 :
936 0 : /* Save the frame's completion value. */
937 : ResumeMode resumeMode;
938 : RootedValue value(cx);
939 : Debugger::resultToCompletion(cx, frameOk, frame.returnValue(), &resumeMode, &value);
940 :
941 0 : // This path can be hit via unwinding the stack due to over-recursion or
942 0 : // OOM. In those cases, don't fire the frames' onPop handlers, because
943 : // invoking JS will only trigger the same condition. See
944 : // slowPathOnExceptionUnwind.
945 : if (!cx->isThrowingOverRecursed() && !cx->isThrowingOutOfMemory()) {
946 : /* For each Debugger.Frame, fire its onPop handler, if any. */
947 : for (size_t i = 0; i < frames.length(); i++) {
948 0 : HandleDebuggerFrame frameobj = frames[i];
949 : Debugger* dbg = Debugger::fromChildJSObject(frameobj);
950 0 : EnterDebuggeeNoExecute nx(cx, *dbg);
951 0 :
952 0 : if (dbg->enabled && frameobj->onPopHandler())
953 0 : {
954 : OnPopHandler* handler = frameobj->onPopHandler();
955 0 :
956 : Maybe<AutoRealm> ar;
957 0 : ar.emplace(cx, dbg->object);
958 :
959 0 : RootedValue wrappedValue(cx, value);
960 0 : RootedValue completion(cx);
961 : if (!dbg->wrapDebuggeeValue(cx, &wrappedValue))
962 0 : {
963 0 : resumeMode = dbg->reportUncaughtException(ar);
964 0 : break;
965 : }
966 0 :
967 0 : /* Call the onPop handler. */
968 : ResumeMode nextResumeMode = resumeMode;
969 : RootedValue nextValue(cx, wrappedValue);
970 : bool success = handler->onPop(cx, frameobj, nextResumeMode, &nextValue);
971 0 : nextResumeMode = dbg->processParsedHandlerResult(ar, frame, pc, success,
972 0 : nextResumeMode, &nextValue);
973 0 :
974 0 : /*
975 : * At this point, we are back in the debuggee compartment, and any error has
976 : * been wrapped up as a completion value.
977 : */
978 : MOZ_ASSERT(cx->compartment() == debuggeeGlobal->compartment());
979 : MOZ_ASSERT(!cx->isExceptionPending());
980 :
981 0 : /* ResumeMode::Continue means "make no change". */
982 0 : if (nextResumeMode != ResumeMode::Continue) {
983 : resumeMode = nextResumeMode;
984 : value = nextValue;
985 0 : }
986 0 : }
987 : }
988 : }
989 :
990 : /* Establish (resumeMode, value) as our resumption value. */
991 : switch (resumeMode) {
992 : case ResumeMode::Return:
993 : frame.setReturnValue(value);
994 0 : return true;
995 :
996 0 : case ResumeMode::Throw:
997 0 : cx->setPendingException(value);
998 : return false;
999 :
1000 0 : case ResumeMode::Terminate:
1001 0 : MOZ_ASSERT(!cx->isExceptionPending());
1002 : return false;
1003 :
1004 0 : default:
1005 : MOZ_CRASH("bad final onLeaveFrame resume mode");
1006 : }
1007 : }
1008 0 :
1009 : /* static */ ResumeMode
1010 : Debugger::slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame)
1011 : {
1012 : RootedValue rval(cx);
1013 0 : ResumeMode resumeMode = dispatchHook(
1014 : cx,
1015 0 : [](Debugger* dbg) -> bool { return dbg->getHook(OnDebuggerStatement); },
1016 0 : [&](Debugger* dbg) -> ResumeMode {
1017 : return dbg->fireDebuggerStatement(cx, &rval);
1018 0 : });
1019 :
1020 0 : switch (resumeMode) {
1021 0 : case ResumeMode::Continue:
1022 : case ResumeMode::Terminate:
1023 0 : break;
1024 :
1025 : case ResumeMode::Return:
1026 : frame.setReturnValue(rval);
1027 : break;
1028 :
1029 0 : case ResumeMode::Throw:
1030 0 : cx->setPendingException(rval);
1031 : break;
1032 :
1033 0 : default:
1034 0 : MOZ_CRASH("Invalid onDebuggerStatement resume mode");
1035 : }
1036 :
1037 0 : return resumeMode;
1038 : }
1039 :
1040 0 : /* static */ ResumeMode
1041 : Debugger::slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame)
1042 : {
1043 : // Invoking more JS on an over-recursed stack or after OOM is only going
1044 0 : // to result in more of the same error.
1045 : if (cx->isThrowingOverRecursed() || cx->isThrowingOutOfMemory())
1046 : return ResumeMode::Continue;
1047 :
1048 0 : // The Debugger API mustn't muck with frames from self-hosted scripts.
1049 : if (frame.hasScript() && frame.script()->selfHosted())
1050 : return ResumeMode::Continue;
1051 :
1052 0 : RootedValue rval(cx);
1053 : ResumeMode resumeMode = dispatchHook(
1054 : cx,
1055 0 : [](Debugger* dbg) -> bool { return dbg->getHook(OnExceptionUnwind); },
1056 0 : [&](Debugger* dbg) -> ResumeMode {
1057 : return dbg->fireExceptionUnwind(cx, &rval);
1058 0 : });
1059 :
1060 0 : switch (resumeMode) {
1061 0 : case ResumeMode::Continue:
1062 : break;
1063 0 :
1064 : case ResumeMode::Throw:
1065 : cx->setPendingException(rval);
1066 : break;
1067 :
1068 0 : case ResumeMode::Terminate:
1069 0 : cx->clearPendingException();
1070 : break;
1071 :
1072 0 : case ResumeMode::Return:
1073 0 : cx->clearPendingException();
1074 : frame.setReturnValue(rval);
1075 : break;
1076 0 :
1077 0 : default:
1078 0 : MOZ_CRASH("Invalid onExceptionUnwind resume mode");
1079 : }
1080 :
1081 0 : return resumeMode;
1082 : }
1083 :
1084 : // TODO: Remove Remove this function when all properties/methods returning a
1085 : /// DebuggerEnvironment have been given a C++ interface (bug 1271649).
1086 : bool
1087 : Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env, MutableHandleValue rval)
1088 : {
1089 : if (!env) {
1090 0 : rval.setNull();
1091 : return true;
1092 0 : }
1093 0 :
1094 0 : RootedDebuggerEnvironment envobj(cx);
1095 :
1096 : if (!wrapEnvironment(cx, env, &envobj))
1097 0 : return false;
1098 :
1099 0 : rval.setObject(*envobj);
1100 : return true;
1101 : }
1102 0 :
1103 0 : bool
1104 : Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env,
1105 : MutableHandleDebuggerEnvironment result)
1106 : {
1107 0 : MOZ_ASSERT(env);
1108 :
1109 : /*
1110 0 : * DebuggerEnv should only wrap a debug scope chain obtained (transitively)
1111 : * from GetDebugEnvironmentFor(Frame|Function).
1112 : */
1113 : MOZ_ASSERT(!IsSyntacticEnvironment(env));
1114 :
1115 : DependentAddPtr<ObjectWeakMap> p(cx, environments, env);
1116 0 : if (p) {
1117 : result.set(&p->value()->as<DebuggerEnvironment>());
1118 0 : } else {
1119 0 : /* Create a new Debugger.Environment for env. */
1120 0 : RootedObject proto(cx, &object->getReservedSlot(JSSLOT_DEBUG_ENV_PROTO).toObject());
1121 : RootedNativeObject debugger(cx, object);
1122 :
1123 0 : RootedDebuggerEnvironment envobj(cx,
1124 0 : DebuggerEnvironment::create(cx, proto, env, debugger));
1125 : if (!envobj)
1126 : return false;
1127 0 :
1128 0 : if (!p.add(cx, environments, env, envobj)) {
1129 0 : NukeDebuggerWrapper(envobj);
1130 : return false;
1131 0 : }
1132 0 :
1133 0 : CrossCompartmentKey key(object, env, CrossCompartmentKey::DebuggerEnvironment);
1134 : if (!object->compartment()->putWrapper(cx, key, ObjectValue(*envobj))) {
1135 : NukeDebuggerWrapper(envobj);
1136 0 : environments.remove(env);
1137 0 : return false;
1138 0 : }
1139 0 :
1140 0 : result.set(envobj);
1141 : }
1142 :
1143 0 : return true;
1144 : }
1145 :
1146 : bool
1147 : Debugger::wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp)
1148 : {
1149 : assertSameCompartment(cx, object.get());
1150 0 :
1151 : if (vp.isObject()) {
1152 0 : RootedObject obj(cx, &vp.toObject());
1153 : RootedDebuggerObject dobj(cx);
1154 0 :
1155 0 : if (!wrapDebuggeeObject(cx, obj, &dobj))
1156 0 : return false;
1157 :
1158 0 : vp.setObject(*dobj);
1159 0 : } else if (vp.isMagic()) {
1160 : RootedPlainObject optObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
1161 0 : if (!optObj)
1162 0 : return false;
1163 0 :
1164 0 : // We handle three sentinel values: missing arguments (overloading
1165 0 : // JS_OPTIMIZED_ARGUMENTS), optimized out slots (JS_OPTIMIZED_OUT),
1166 : // and uninitialized bindings (JS_UNINITIALIZED_LEXICAL).
1167 : //
1168 : // Other magic values should not have escaped.
1169 : PropertyName* name;
1170 : switch (vp.whyMagic()) {
1171 : case JS_OPTIMIZED_ARGUMENTS: name = cx->names().missingArguments; break;
1172 : case JS_OPTIMIZED_OUT: name = cx->names().optimizedOut; break;
1173 0 : case JS_UNINITIALIZED_LEXICAL: name = cx->names().uninitialized; break;
1174 0 : default: MOZ_CRASH("Unsupported magic value escaped to Debugger");
1175 0 : }
1176 0 :
1177 0 : RootedValue trueVal(cx, BooleanValue(true));
1178 : if (!DefineDataProperty(cx, optObj, name, trueVal))
1179 : return false;
1180 0 :
1181 0 : vp.setObject(*optObj);
1182 0 : } else if (!cx->compartment()->wrap(cx, vp)) {
1183 : vp.setUndefined();
1184 0 : return false;
1185 0 : }
1186 0 :
1187 0 : return true;
1188 : }
1189 :
1190 : bool
1191 : Debugger::wrapDebuggeeObject(JSContext* cx, HandleObject obj,
1192 : MutableHandleDebuggerObject result)
1193 : {
1194 0 : MOZ_ASSERT(obj);
1195 :
1196 : if (obj->is<JSFunction>()) {
1197 0 : MOZ_ASSERT(!IsInternalFunctionObject(*obj));
1198 : RootedFunction fun(cx, &obj->as<JSFunction>());
1199 0 : if (!EnsureFunctionHasScript(cx, fun))
1200 0 : return false;
1201 0 : }
1202 0 :
1203 0 : DependentAddPtr<ObjectWeakMap> p(cx, objects, obj);
1204 : if (p) {
1205 : result.set(&p->value()->as<DebuggerObject>());
1206 0 : } else {
1207 0 : /* Create a new Debugger.Object for obj. */
1208 0 : RootedNativeObject debugger(cx, object);
1209 : RootedObject proto(cx, &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject());
1210 : RootedDebuggerObject dobj(cx, DebuggerObject::create(cx, proto, obj, debugger));
1211 0 : if (!dobj)
1212 0 : return false;
1213 0 :
1214 0 : if (!p.add(cx, objects, obj, dobj)) {
1215 0 : NukeDebuggerWrapper(dobj);
1216 : return false;
1217 0 : }
1218 0 :
1219 0 : if (obj->compartment() != object->compartment()) {
1220 : CrossCompartmentKey key(object, obj, CrossCompartmentKey::DebuggerObject);
1221 : if (!object->compartment()->putWrapper(cx, key, ObjectValue(*dobj))) {
1222 0 : NukeDebuggerWrapper(dobj);
1223 0 : objects.remove(obj);
1224 0 : ReportOutOfMemory(cx);
1225 0 : return false;
1226 0 : }
1227 0 : }
1228 0 :
1229 : result.set(dobj);
1230 : }
1231 :
1232 0 : return true;
1233 : }
1234 :
1235 : static NativeObject*
1236 : ToNativeDebuggerObject(JSContext* cx, MutableHandleObject obj)
1237 : {
1238 : if (obj->getClass() != &DebuggerObject::class_) {
1239 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
1240 : "Debugger", "Debugger.Object", obj->getClass()->name);
1241 0 : return nullptr;
1242 : }
1243 0 :
1244 0 : NativeObject* ndobj = &obj->as<NativeObject>();
1245 :
1246 : Value owner = ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);
1247 0 : if (owner.isUndefined()) {
1248 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1249 0 : JSMSG_DEBUG_PROTO, "Debugger.Object", "Debugger.Object");
1250 0 : return nullptr;
1251 : }
1252 0 :
1253 0 : return ndobj;
1254 : }
1255 :
1256 : bool
1257 : Debugger::unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj)
1258 : {
1259 : NativeObject* ndobj = ToNativeDebuggerObject(cx, obj);
1260 0 : if (!ndobj)
1261 : return false;
1262 0 :
1263 0 : Value owner = ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);
1264 : if (&owner.toObject() != object) {
1265 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1266 0 : JSMSG_DEBUG_WRONG_OWNER, "Debugger.Object");
1267 0 : return false;
1268 : }
1269 0 :
1270 0 : obj.set(static_cast<JSObject*>(ndobj->getPrivate()));
1271 : return true;
1272 : }
1273 0 :
1274 0 : bool
1275 : Debugger::unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp)
1276 : {
1277 : assertSameCompartment(cx, object.get(), vp);
1278 0 : if (vp.isObject()) {
1279 : RootedObject dobj(cx, &vp.toObject());
1280 0 : if (!unwrapDebuggeeObject(cx, &dobj))
1281 0 : return false;
1282 0 : vp.setObject(*dobj);
1283 0 : }
1284 0 : return true;
1285 0 : }
1286 :
1287 : static bool
1288 : CheckArgCompartment(JSContext* cx, JSObject* obj, JSObject* arg,
1289 : const char* methodname, const char* propname)
1290 : {
1291 : if (arg->compartment() != obj->compartment()) {
1292 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_COMPARTMENT_MISMATCH,
1293 : methodname, propname);
1294 0 : return false;
1295 : }
1296 0 : return true;
1297 : }
1298 :
1299 : static bool
1300 : CheckArgCompartment(JSContext* cx, JSObject* obj, HandleValue v,
1301 : const char* methodname, const char* propname)
1302 : {
1303 0 : if (v.isObject())
1304 : return CheckArgCompartment(cx, obj, &v.toObject(), methodname, propname);
1305 : return true;
1306 0 : }
1307 0 :
1308 : bool
1309 : Debugger::unwrapPropertyDescriptor(JSContext* cx, HandleObject obj,
1310 : MutableHandle<PropertyDescriptor> desc)
1311 : {
1312 0 : if (desc.hasValue()) {
1313 : RootedValue value(cx, desc.value());
1314 : if (!unwrapDebuggeeValue(cx, &value) ||
1315 0 : !CheckArgCompartment(cx, obj, value, "defineProperty", "value"))
1316 0 : {
1317 0 : return false;
1318 0 : }
1319 : desc.setValue(value);
1320 0 : }
1321 :
1322 0 : if (desc.hasGetterObject()) {
1323 : RootedObject get(cx, desc.getterObject());
1324 : if (get) {
1325 0 : if (!unwrapDebuggeeObject(cx, &get))
1326 0 : return false;
1327 0 : if (!CheckArgCompartment(cx, obj, get, "defineProperty", "get"))
1328 0 : return false;
1329 0 : }
1330 0 : desc.setGetterObject(get);
1331 : }
1332 :
1333 0 : if (desc.hasSetterObject()) {
1334 : RootedObject set(cx, desc.setterObject());
1335 : if (set) {
1336 0 : if (!unwrapDebuggeeObject(cx, &set))
1337 0 : return false;
1338 0 : if (!CheckArgCompartment(cx, obj, set, "defineProperty", "set"))
1339 0 : return false;
1340 0 : }
1341 0 : desc.setSetterObject(set);
1342 : }
1343 :
1344 0 : return true;
1345 : }
1346 :
1347 : ResumeMode
1348 : Debugger::reportUncaughtException(Maybe<AutoRealm>& ar)
1349 : {
1350 : JSContext* cx = ar->context();
1351 0 :
1352 : // Uncaught exceptions arise from Debugger code, and so we must already be
1353 0 : // in an NX section.
1354 : MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx, *this));
1355 :
1356 : if (cx->isExceptionPending()) {
1357 0 : /*
1358 : * We want to report the pending exception, but we want to let the
1359 0 : * embedding handle it however it wants to. So pretend like we're
1360 : * starting a new script execution on our current compartment (which
1361 : * is the debugger compartment, so reported errors won't get
1362 : * reported to various onerror handlers in debuggees) and as part of
1363 : * that "execution" simply throw our exception so the embedding can
1364 : * deal.
1365 : */
1366 : RootedValue exn(cx);
1367 : if (cx->getPendingException(&exn)) {
1368 : /*
1369 0 : * Clear the exception, because ReportErrorToGlobal will assert that
1370 0 : * we don't have one.
1371 : */
1372 : cx->clearPendingException();
1373 : ReportErrorToGlobal(cx, cx->global(), exn);
1374 : }
1375 0 : /*
1376 0 : * And if not, or if PrepareScriptEnvironmentAndInvoke somehow left
1377 : * an exception on cx (which it totally shouldn't do), just give
1378 : * up.
1379 : */
1380 : cx->clearPendingException();
1381 : }
1382 :
1383 0 : ar.reset();
1384 : return ResumeMode::Terminate;
1385 : }
1386 0 :
1387 0 : ResumeMode
1388 : Debugger::handleUncaughtExceptionHelper(Maybe<AutoRealm>& ar, MutableHandleValue* vp,
1389 : const Maybe<HandleValue>& thisVForCheck,
1390 : AbstractFramePtr frame)
1391 0 : {
1392 : JSContext* cx = ar->context();
1393 :
1394 : // Uncaught exceptions arise from Debugger code, and so we must already be
1395 0 : // in an NX section.
1396 : MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx, *this));
1397 :
1398 : if (cx->isExceptionPending()) {
1399 0 : if (uncaughtExceptionHook) {
1400 : RootedValue exc(cx);
1401 0 : if (!cx->getPendingException(&exc))
1402 0 : return ResumeMode::Terminate;
1403 0 : cx->clearPendingException();
1404 0 :
1405 0 : RootedValue fval(cx, ObjectValue(*uncaughtExceptionHook));
1406 0 : RootedValue rv(cx);
1407 : if (js::Call(cx, fval, object, exc, &rv)) {
1408 0 : if (vp) {
1409 0 : ResumeMode resumeMode = ResumeMode::Continue;
1410 0 : if (processResumptionValue(ar, frame, thisVForCheck, rv, resumeMode, *vp))
1411 0 : return resumeMode;
1412 0 : } else {
1413 0 : return ResumeMode::Continue;
1414 0 : }
1415 : }
1416 : }
1417 :
1418 : return reportUncaughtException(ar);
1419 : }
1420 :
1421 0 : ar.reset();
1422 : return ResumeMode::Terminate;
1423 : }
1424 0 :
1425 0 : ResumeMode
1426 : Debugger::handleUncaughtException(Maybe<AutoRealm>& ar, MutableHandleValue vp,
1427 : const Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame)
1428 : {
1429 0 : return handleUncaughtExceptionHelper(ar, &vp, thisVForCheck, frame);
1430 : }
1431 :
1432 0 : ResumeMode
1433 : Debugger::handleUncaughtException(Maybe<AutoRealm>& ar)
1434 : {
1435 : return handleUncaughtExceptionHelper(ar, nullptr, mozilla::Nothing(), NullFramePtr());
1436 0 : }
1437 :
1438 0 : /* static */ void
1439 : Debugger::resultToCompletion(JSContext* cx, bool ok, const Value& rv,
1440 : ResumeMode* resumeMode, MutableHandleValue value)
1441 : {
1442 0 : MOZ_ASSERT_IF(ok, !cx->isExceptionPending());
1443 :
1444 : if (ok) {
1445 0 : *resumeMode = ResumeMode::Return;
1446 : value.set(rv);
1447 0 : } else if (cx->isExceptionPending()) {
1448 0 : *resumeMode = ResumeMode::Throw;
1449 0 : if (!cx->getPendingException(value))
1450 0 : *resumeMode = ResumeMode::Terminate;
1451 0 : cx->clearPendingException();
1452 0 : } else {
1453 0 : *resumeMode = ResumeMode::Terminate;
1454 0 : value.setUndefined();
1455 : }
1456 0 : }
1457 :
1458 : bool
1459 0 : Debugger::newCompletionValue(JSContext* cx, ResumeMode resumeMode, const Value& value_,
1460 : MutableHandleValue result)
1461 : {
1462 0 : /*
1463 : * We must be in the debugger's compartment, since that's where we want
1464 : * to construct the completion value.
1465 : */
1466 : assertSameCompartment(cx, object.get());
1467 : assertSameCompartment(cx, value_);
1468 :
1469 0 : RootedId key(cx);
1470 0 : RootedValue value(cx, value_);
1471 :
1472 0 : switch (resumeMode) {
1473 0 : case ResumeMode::Return:
1474 : key = NameToId(cx->names().return_);
1475 0 : break;
1476 :
1477 0 : case ResumeMode::Throw:
1478 0 : key = NameToId(cx->names().throw_);
1479 : break;
1480 :
1481 0 : case ResumeMode::Terminate:
1482 0 : result.setNull();
1483 : return true;
1484 :
1485 0 : default:
1486 0 : MOZ_CRASH("bad resume mode passed to Debugger::newCompletionValue");
1487 : }
1488 :
1489 0 : // Common tail for ResumeMode::Return and ResumeMode::Throw.
1490 : RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
1491 : if (!obj ||
1492 : !NativeDefineDataProperty(cx, obj, key, value, JSPROP_ENUMERATE))
1493 0 : {
1494 0 : return false;
1495 0 : }
1496 :
1497 : result.setObject(*obj);
1498 : return true;
1499 : }
1500 0 :
1501 0 : bool
1502 : Debugger::receiveCompletionValue(Maybe<AutoRealm>& ar, bool ok,
1503 : HandleValue val,
1504 : MutableHandleValue vp)
1505 0 : {
1506 : JSContext* cx = ar->context();
1507 :
1508 : ResumeMode resumeMode;
1509 0 : RootedValue value(cx);
1510 : resultToCompletion(cx, ok, val, &resumeMode, &value);
1511 : ar.reset();
1512 0 : return wrapDebuggeeValue(cx, &value) &&
1513 0 : newCompletionValue(cx, resumeMode, value, vp);
1514 0 : }
1515 0 :
1516 0 : static bool
1517 : GetResumptionProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, ResumeMode namedMode,
1518 : ResumeMode& resumeMode, MutableHandleValue vp, int* hits)
1519 : {
1520 0 : bool found;
1521 : if (!HasProperty(cx, obj, name, &found))
1522 : return false;
1523 : if (found) {
1524 0 : ++*hits;
1525 : resumeMode = namedMode;
1526 0 : if (!GetProperty(cx, obj, obj, name, vp))
1527 0 : return false;
1528 0 : }
1529 0 : return true;
1530 : }
1531 :
1532 : static bool
1533 : ParseResumptionValueAsObject(JSContext* cx, HandleValue rv, ResumeMode& resumeMode,
1534 : MutableHandleValue vp)
1535 : {
1536 0 : int hits = 0;
1537 : if (rv.isObject()) {
1538 : RootedObject obj(cx, &rv.toObject());
1539 0 : if (!GetResumptionProperty(cx, obj, cx->names().return_, ResumeMode::Return, resumeMode, vp, &hits))
1540 0 : return false;
1541 0 : if (!GetResumptionProperty(cx, obj, cx->names().throw_, ResumeMode::Throw, resumeMode, vp, &hits))
1542 0 : return false;
1543 0 : }
1544 0 :
1545 : if (hits != 1) {
1546 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_RESUMPTION);
1547 : return false;
1548 0 : }
1549 0 : return true;
1550 0 : }
1551 :
1552 : static bool
1553 : ParseResumptionValue(JSContext* cx, HandleValue rval, ResumeMode& resumeMode, MutableHandleValue vp)
1554 : {
1555 : if (rval.isUndefined()) {
1556 0 : resumeMode = ResumeMode::Continue;
1557 : vp.setUndefined();
1558 0 : return true;
1559 0 : }
1560 0 : if (rval.isNull()) {
1561 0 : resumeMode = ResumeMode::Terminate;
1562 : vp.setUndefined();
1563 0 : return true;
1564 0 : }
1565 0 : return ParseResumptionValueAsObject(cx, rval, resumeMode, vp);
1566 0 : }
1567 :
1568 0 : static bool
1569 : CheckResumptionValue(JSContext* cx, AbstractFramePtr frame, const Maybe<HandleValue>& maybeThisv,
1570 : ResumeMode resumeMode, MutableHandleValue vp)
1571 : {
1572 0 : if (resumeMode == ResumeMode::Return && frame && frame.isFunctionFrame()) {
1573 : // Don't let a { return: ... } resumption value make a generator
1574 : // function violate the iterator protocol. The return value from
1575 0 : // such a frame must have the form { done: <bool>, value: <anything> }.
1576 : RootedFunction callee(cx, frame.callee());
1577 : if (callee->isGenerator()) {
1578 : if (!CheckGeneratorResumptionValue(cx, vp)) {
1579 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD);
1580 0 : return false;
1581 0 : }
1582 0 : }
1583 0 : }
1584 :
1585 : if (maybeThisv.isSome()) {
1586 : const HandleValue& thisv = maybeThisv.ref();
1587 : if (resumeMode == ResumeMode::Return && vp.isPrimitive()) {
1588 0 : if (vp.isUndefined()) {
1589 0 : if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL))
1590 0 : return ThrowUninitializedThis(cx, frame);
1591 0 :
1592 0 : vp.set(thisv);
1593 0 : } else {
1594 : ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, vp, nullptr);
1595 0 : return false;
1596 : }
1597 0 : }
1598 0 : }
1599 : return true;
1600 : }
1601 :
1602 : static bool
1603 : GetThisValueForCheck(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
1604 : MutableHandleValue thisv, Maybe<HandleValue>& maybeThisv)
1605 : {
1606 0 : if (frame.debuggerNeedsCheckPrimitiveReturn()) {
1607 : {
1608 : AutoRealm ar(cx, frame.environmentChain());
1609 0 : if (!GetThisValueForDebuggerMaybeOptimizedOut(cx, frame, pc, thisv))
1610 : return false;
1611 0 : }
1612 0 :
1613 0 : if (!cx->compartment()->wrap(cx, thisv))
1614 : return false;
1615 :
1616 0 : MOZ_ASSERT_IF(thisv.isMagic(), thisv.isMagic(JS_UNINITIALIZED_LEXICAL));
1617 : maybeThisv.emplace(HandleValue(thisv));
1618 : }
1619 0 :
1620 0 : return true;
1621 : }
1622 :
1623 : bool
1624 : Debugger::processResumptionValue(Maybe<AutoRealm>& ar, AbstractFramePtr frame,
1625 : const Maybe<HandleValue>& maybeThisv, HandleValue rval,
1626 : ResumeMode& resumeMode, MutableHandleValue vp)
1627 0 : {
1628 : JSContext* cx = ar->context();
1629 :
1630 : if (!ParseResumptionValue(cx, rval, resumeMode, vp) ||
1631 0 : !unwrapDebuggeeValue(cx, vp) ||
1632 : !CheckResumptionValue(cx, frame, maybeThisv, resumeMode, vp))
1633 0 : {
1634 0 : return false;
1635 0 : }
1636 :
1637 : ar.reset();
1638 : if (!cx->compartment()->wrap(cx, vp)) {
1639 : resumeMode = ResumeMode::Terminate;
1640 0 : vp.setUndefined();
1641 0 : }
1642 0 :
1643 : return true;
1644 : }
1645 :
1646 : ResumeMode
1647 : Debugger::processParsedHandlerResultHelper(Maybe<AutoRealm>& ar, AbstractFramePtr frame,
1648 : const Maybe<HandleValue>& maybeThisv, bool success,
1649 : ResumeMode resumeMode, MutableHandleValue vp)
1650 0 : {
1651 : if (!success)
1652 : return handleUncaughtException(ar, vp, maybeThisv, frame);
1653 :
1654 0 : JSContext* cx = ar->context();
1655 0 :
1656 : if (!unwrapDebuggeeValue(cx, vp) ||
1657 0 : !CheckResumptionValue(cx, frame, maybeThisv, resumeMode, vp))
1658 : {
1659 0 : return handleUncaughtException(ar, vp, maybeThisv, frame);
1660 0 : }
1661 :
1662 0 : ar.reset();
1663 : if (!cx->compartment()->wrap(cx, vp)) {
1664 : resumeMode = ResumeMode::Terminate;
1665 0 : vp.setUndefined();
1666 0 : }
1667 0 :
1668 : return resumeMode;
1669 : }
1670 :
1671 : ResumeMode
1672 : Debugger::processParsedHandlerResult(Maybe<AutoRealm>& ar, AbstractFramePtr frame,
1673 : jsbytecode* pc, bool success, ResumeMode resumeMode,
1674 : MutableHandleValue vp)
1675 0 : {
1676 : JSContext* cx = ar->context();
1677 :
1678 : RootedValue thisv(cx);
1679 0 : Maybe<HandleValue> maybeThisv;
1680 : if (!GetThisValueForCheck(cx, frame, pc, &thisv, maybeThisv)) {
1681 0 : ar.reset();
1682 0 : return ResumeMode::Terminate;
1683 0 : }
1684 0 :
1685 0 : return processParsedHandlerResultHelper(ar, frame, maybeThisv, success, resumeMode, vp);
1686 : }
1687 :
1688 0 : ResumeMode
1689 : Debugger::processHandlerResult(Maybe<AutoRealm>& ar, bool success, const Value& rv,
1690 : AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp)
1691 : {
1692 0 : JSContext* cx = ar->context();
1693 :
1694 : RootedValue thisv(cx);
1695 0 : Maybe<HandleValue> maybeThisv;
1696 : if (!GetThisValueForCheck(cx, frame, pc, &thisv, maybeThisv)) {
1697 0 : ar.reset();
1698 0 : return ResumeMode::Terminate;
1699 0 : }
1700 0 :
1701 0 : if (!success)
1702 : return handleUncaughtException(ar, vp, maybeThisv, frame);
1703 :
1704 0 : RootedValue rootRv(cx, rv);
1705 0 : ResumeMode resumeMode = ResumeMode::Continue;
1706 : success = ParseResumptionValue(cx, rootRv, resumeMode, vp);
1707 0 :
1708 0 : return processParsedHandlerResultHelper(ar, frame, maybeThisv, success, resumeMode, vp);
1709 0 : }
1710 :
1711 0 : static bool
1712 : CallMethodIfPresent(JSContext* cx, HandleObject obj, const char* name, size_t argc, Value* argv,
1713 : MutableHandleValue rval)
1714 : {
1715 0 : rval.setUndefined();
1716 : JSAtom* atom = Atomize(cx, name, strlen(name));
1717 : if (!atom)
1718 0 : return false;
1719 0 :
1720 0 : RootedId id(cx, AtomToId(atom));
1721 : RootedValue fval(cx);
1722 : if (!GetProperty(cx, obj, obj, id, &fval))
1723 0 : return false;
1724 0 :
1725 0 : if (!IsCallable(fval))
1726 : return true;
1727 :
1728 0 : InvokeArgs args(cx);
1729 : if (!args.init(cx, argc))
1730 : return false;
1731 0 :
1732 0 : for (size_t i = 0; i < argc; i++)
1733 : args[i].set(argv[i]);
1734 :
1735 0 : rval.setObject(*obj); // overwritten by successful Call
1736 0 : return js::Call(cx, fval, rval, args, rval);
1737 : }
1738 0 :
1739 0 : ResumeMode
1740 : Debugger::fireDebuggerStatement(JSContext* cx, MutableHandleValue vp)
1741 : {
1742 : RootedObject hook(cx, getHook(OnDebuggerStatement));
1743 0 : MOZ_ASSERT(hook);
1744 : MOZ_ASSERT(hook->isCallable());
1745 0 :
1746 0 : Maybe<AutoRealm> ar;
1747 0 : ar.emplace(cx, object);
1748 :
1749 0 : ScriptFrameIter iter(cx);
1750 0 : RootedValue scriptFrame(cx);
1751 : if (!getFrame(cx, iter, &scriptFrame))
1752 0 : return reportUncaughtException(ar);
1753 0 :
1754 0 : RootedValue fval(cx, ObjectValue(*hook));
1755 0 : RootedValue rv(cx);
1756 : bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
1757 0 : return processHandlerResult(ar, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
1758 0 : }
1759 0 :
1760 0 : ResumeMode
1761 : Debugger::fireExceptionUnwind(JSContext* cx, MutableHandleValue vp)
1762 : {
1763 : RootedObject hook(cx, getHook(OnExceptionUnwind));
1764 0 : MOZ_ASSERT(hook);
1765 : MOZ_ASSERT(hook->isCallable());
1766 0 :
1767 0 : RootedValue exc(cx);
1768 0 : if (!cx->getPendingException(&exc))
1769 : return ResumeMode::Terminate;
1770 0 : cx->clearPendingException();
1771 0 :
1772 : Maybe<AutoRealm> ar;
1773 0 : ar.emplace(cx, object);
1774 :
1775 0 : RootedValue scriptFrame(cx);
1776 0 : RootedValue wrappedExc(cx, exc);
1777 :
1778 0 : FrameIter iter(cx);
1779 0 : if (!getFrame(cx, iter, &scriptFrame) || !wrapDebuggeeValue(cx, &wrappedExc))
1780 : return reportUncaughtException(ar);
1781 0 :
1782 0 : RootedValue fval(cx, ObjectValue(*hook));
1783 0 : RootedValue rv(cx);
1784 : bool ok = js::Call(cx, fval, object, scriptFrame, wrappedExc, &rv);
1785 0 : ResumeMode resumeMode = processHandlerResult(ar, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
1786 0 : if (resumeMode == ResumeMode::Continue)
1787 0 : cx->setPendingException(exc);
1788 0 : return resumeMode;
1789 0 : }
1790 0 :
1791 : ResumeMode
1792 : Debugger::fireEnterFrame(JSContext* cx, MutableHandleValue vp)
1793 : {
1794 : RootedObject hook(cx, getHook(OnEnterFrame));
1795 0 : MOZ_ASSERT(hook);
1796 : MOZ_ASSERT(hook->isCallable());
1797 0 :
1798 0 : Maybe<AutoRealm> ar;
1799 0 : ar.emplace(cx, object);
1800 :
1801 0 : RootedValue scriptFrame(cx);
1802 0 :
1803 : FrameIter iter(cx);
1804 0 : if (!getFrame(cx, iter, &scriptFrame))
1805 : return reportUncaughtException(ar);
1806 0 :
1807 0 : RootedValue fval(cx, ObjectValue(*hook));
1808 0 : RootedValue rv(cx);
1809 : bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
1810 0 :
1811 0 : return processHandlerResult(ar, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
1812 0 : }
1813 :
1814 0 : void
1815 : Debugger::fireNewScript(JSContext* cx, Handle<DebuggerScriptReferent> scriptReferent)
1816 : {
1817 : RootedObject hook(cx, getHook(OnNewScript));
1818 0 : MOZ_ASSERT(hook);
1819 : MOZ_ASSERT(hook->isCallable());
1820 0 :
1821 0 : Maybe<AutoRealm> ar;
1822 0 : ar.emplace(cx, object);
1823 :
1824 0 : JSObject* dsobj = wrapVariantReferent(cx, scriptReferent);
1825 0 : if (!dsobj) {
1826 : reportUncaughtException(ar);
1827 0 : return;
1828 0 : }
1829 0 :
1830 0 : RootedValue fval(cx, ObjectValue(*hook));
1831 : RootedValue dsval(cx, ObjectValue(*dsobj));
1832 : RootedValue rv(cx);
1833 0 : if (!js::Call(cx, fval, object, dsval, &rv))
1834 0 : handleUncaughtException(ar);
1835 0 : }
1836 0 :
1837 0 : void
1838 : Debugger::fireOnGarbageCollectionHook(JSContext* cx,
1839 : const JS::dbg::GarbageCollectionEvent::Ptr& gcData)
1840 : {
1841 0 : MOZ_ASSERT(observedGC(gcData->majorGCNumber()));
1842 : observedGCs.remove(gcData->majorGCNumber());
1843 :
1844 0 : RootedObject hook(cx, getHook(OnGarbageCollection));
1845 0 : MOZ_ASSERT(hook);
1846 : MOZ_ASSERT(hook->isCallable());
1847 0 :
1848 0 : Maybe<AutoRealm> ar;
1849 0 : ar.emplace(cx, object);
1850 :
1851 0 : JSObject* dataObj = gcData->toJSObject(cx);
1852 0 : if (!dataObj) {
1853 : reportUncaughtException(ar);
1854 0 : return;
1855 0 : }
1856 0 :
1857 0 : RootedValue fval(cx, ObjectValue(*hook));
1858 : RootedValue dataVal(cx, ObjectValue(*dataObj));
1859 : RootedValue rv(cx);
1860 0 : if (!js::Call(cx, fval, object, dataVal, &rv))
1861 0 : handleUncaughtException(ar);
1862 0 : }
1863 0 :
1864 0 : template <typename HookIsEnabledFun /* bool (Debugger*) */,
1865 : typename FireHookFun /* ResumeMode (Debugger*) */>
1866 : /* static */ ResumeMode
1867 : Debugger::dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled, FireHookFun fireHook)
1868 : {
1869 : /*
1870 0 : * Determine which debuggers will receive this event, and in what order.
1871 : * Make a copy of the list, since the original is mutable and we will be
1872 : * calling into arbitrary JS.
1873 : *
1874 : * Note: In the general case, 'triggered' contains references to objects in
1875 : * different compartments--every compartment *except* this one.
1876 : */
1877 : AutoValueVector triggered(cx);
1878 : Handle<GlobalObject*> global = cx->global();
1879 : if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
1880 0 : for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
1881 0 : Debugger* dbg = *p;
1882 0 : if (dbg->enabled && hookIsEnabled(dbg)) {
1883 0 : if (!triggered.append(ObjectValue(*dbg->toJSObject())))
1884 0 : return ResumeMode::Terminate;
1885 0 : }
1886 0 : }
1887 : }
1888 :
1889 : /*
1890 : * Deliver the event to each debugger, checking again to make sure it
1891 : * should still be delivered.
1892 : */
1893 : for (Value* p = triggered.begin(); p != triggered.end(); p++) {
1894 : Debugger* dbg = Debugger::fromJSObject(&p->toObject());
1895 : EnterDebuggeeNoExecute nx(cx, *dbg);
1896 0 : if (dbg->debuggees.has(global) && dbg->enabled && hookIsEnabled(dbg)) {
1897 0 : ResumeMode resumeMode = fireHook(dbg);
1898 0 : if (resumeMode != ResumeMode::Continue)
1899 0 : return resumeMode;
1900 0 : }
1901 0 : }
1902 0 : return ResumeMode::Continue;
1903 : }
1904 :
1905 : void
1906 : Debugger::slowPathOnNewScript(JSContext* cx, HandleScript script)
1907 : {
1908 : ResumeMode resumeMode = dispatchHook(
1909 0 : cx,
1910 : [script](Debugger* dbg) -> bool {
1911 0 : return dbg->observesNewScript() && dbg->observesScript(script);
1912 : },
1913 0 : [&](Debugger* dbg) -> ResumeMode {
1914 0 : Rooted<DebuggerScriptReferent> scriptReferent(cx, script.get());
1915 : dbg->fireNewScript(cx, scriptReferent);
1916 0 : return ResumeMode::Continue;
1917 0 : });
1918 0 :
1919 0 : // dispatchHook may fail due to OOM. This OOM is not handlable at the
1920 0 : // callsites of onNewScript in the engine.
1921 : if (resumeMode == ResumeMode::Terminate) {
1922 : cx->clearPendingException();
1923 : return;
1924 0 : }
1925 0 :
1926 0 : MOZ_ASSERT(resumeMode == ResumeMode::Continue);
1927 : }
1928 :
1929 0 : void
1930 : Debugger::slowPathOnNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance)
1931 : {
1932 : ResumeMode resumeMode = dispatchHook(
1933 0 : cx,
1934 : [wasmInstance](Debugger* dbg) -> bool {
1935 0 : return dbg->observesNewScript() && dbg->observesGlobal(&wasmInstance->global());
1936 : },
1937 0 : [&](Debugger* dbg) -> ResumeMode {
1938 0 : Rooted<DebuggerScriptReferent> scriptReferent(cx, wasmInstance.get());
1939 : dbg->fireNewScript(cx, scriptReferent);
1940 0 : return ResumeMode::Continue;
1941 0 : });
1942 0 :
1943 0 : // dispatchHook may fail due to OOM. This OOM is not handlable at the
1944 0 : // callsites of onNewWasmInstance in the engine.
1945 : if (resumeMode == ResumeMode::Terminate) {
1946 : cx->clearPendingException();
1947 : return;
1948 0 : }
1949 0 :
1950 0 : MOZ_ASSERT(resumeMode == ResumeMode::Continue);
1951 : }
1952 :
1953 0 : /* static */ ResumeMode
1954 : Debugger::onTrap(JSContext* cx, MutableHandleValue vp)
1955 : {
1956 : FrameIter iter(cx);
1957 0 : JS::AutoSaveExceptionState savedExc(cx);
1958 : Rooted<GlobalObject*> global(cx);
1959 0 : BreakpointSite* site;
1960 0 : bool isJS; // true when iter.hasScript(), false when iter.isWasm()
1961 0 : jsbytecode* pc; // valid when isJS == true
1962 : uint32_t bytecodeOffset; // valid when isJS == false
1963 : if (iter.hasScript()) {
1964 : RootedScript script(cx, iter.script());
1965 : MOZ_ASSERT(script->isDebuggee());
1966 0 : global.set(&script->global());
1967 0 : isJS = true;
1968 0 : pc = iter.pc();
1969 0 : bytecodeOffset = 0;
1970 0 : site = script->getBreakpointSite(pc);
1971 0 : } else {
1972 0 : MOZ_ASSERT(iter.isWasm());
1973 0 : global.set(&iter.wasmInstance()->object()->global());
1974 : isJS = false;
1975 0 : pc = nullptr;
1976 0 : bytecodeOffset = iter.wasmBytecodeOffset();
1977 0 : site = iter.wasmInstance()->debug().getOrCreateBreakpointSite(cx, bytecodeOffset);
1978 0 : }
1979 0 :
1980 0 : /* Build list of breakpoint handlers. */
1981 : Vector<Breakpoint*> triggered(cx);
1982 : for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
1983 : // Skip a breakpoint that is not set for the current wasm::Instance --
1984 0 : // single wasm::Code can handle breakpoints for mutiple instances.
1985 0 : if (!isJS && &bp->asWasm()->wasmInstance->instance() != iter.wasmInstance())
1986 : continue;
1987 : if (!triggered.append(bp))
1988 0 : return ResumeMode::Terminate;
1989 : }
1990 0 :
1991 0 : for (Breakpoint** p = triggered.begin(); p != triggered.end(); p++) {
1992 : Breakpoint* bp = *p;
1993 :
1994 0 : /* Handlers can clear breakpoints. Check that bp still exists. */
1995 0 : if (!site || !site->hasBreakpoint(bp))
1996 : continue;
1997 :
1998 0 : /*
1999 : * There are two reasons we have to check whether dbg is enabled and
2000 : * debugging global.
2001 : *
2002 : * One is just that one breakpoint handler can disable other Debuggers
2003 : * or remove debuggees.
2004 : *
2005 : * The other has to do with non-compile-and-go scripts, which have no
2006 : * specific global--until they are executed. Only now do we know which
2007 : * global the script is running against.
2008 : */
2009 : Debugger* dbg = bp->debugger;
2010 : bool hasDebuggee = dbg->enabled && dbg->debuggees.has(global);
2011 : if (hasDebuggee) {
2012 0 : Maybe<AutoRealm> ar;
2013 0 : ar.emplace(cx, dbg->object);
2014 0 : EnterDebuggeeNoExecute nx(cx, *dbg);
2015 0 :
2016 0 : RootedValue scriptFrame(cx);
2017 0 : if (!dbg->getFrame(cx, iter, &scriptFrame))
2018 : return dbg->reportUncaughtException(ar);
2019 0 : RootedValue rv(cx);
2020 0 : Rooted<JSObject*> handler(cx, bp->handler);
2021 0 : bool ok = CallMethodIfPresent(cx, handler, "hit", 1, scriptFrame.address(), &rv);
2022 0 : ResumeMode resumeMode = dbg->processHandlerResult(ar, ok, rv, iter.abstractFramePtr(),
2023 0 : iter.pc(), vp);
2024 0 : if (resumeMode != ResumeMode::Continue) {
2025 0 : savedExc.drop();
2026 0 : return resumeMode;
2027 0 : }
2028 0 :
2029 0 : /* Calling JS code invalidates site. Reload it. */
2030 : if (isJS)
2031 : site = iter.script()->getBreakpointSite(pc);
2032 : else
2033 0 : site = iter.wasmInstance()->debug().getOrCreateBreakpointSite(cx, bytecodeOffset);
2034 0 : }
2035 : }
2036 0 :
2037 : // By convention, return the true op to the interpreter in vp, and return
2038 : // undefined in vp to the wasm debug trap.
2039 : if (isJS)
2040 : vp.setInt32(JSOp(*pc));
2041 : else
2042 0 : vp.set(UndefinedValue());
2043 0 : return ResumeMode::Continue;
2044 : }
2045 0 :
2046 : /* static */ ResumeMode
2047 : Debugger::onSingleStep(JSContext* cx, MutableHandleValue vp)
2048 : {
2049 : FrameIter iter(cx);
2050 0 :
2051 : /*
2052 0 : * We may be stepping over a JSOP_EXCEPTION, that pushes the context's
2053 : * pending exception for a 'catch' clause to handle. Don't let the
2054 : * onStep handlers mess with that (other than by returning a resumption
2055 : * value).
2056 : */
2057 : JS::AutoSaveExceptionState savedExc(cx);
2058 :
2059 : /*
2060 0 : * Build list of Debugger.Frame instances referring to this frame with
2061 : * onStep handlers.
2062 : */
2063 : Rooted<DebuggerFrameVector> frames(cx, DebuggerFrameVector(cx));
2064 : if (!getDebuggerFrames(iter.abstractFramePtr(), &frames))
2065 : return ResumeMode::Terminate;
2066 0 :
2067 0 : #ifdef DEBUG
2068 : /*
2069 : * Validate the single-step count on this frame's script, to ensure that
2070 : * we're not receiving traps we didn't ask for. Even when frames is
2071 : * non-empty (and thus we know this trap was requested), do the check
2072 : * anyway, to make sure the count has the correct non-zero value.
2073 : *
2074 : * The converse --- ensuring that we do receive traps when we should --- can
2075 : * be done with unit tests.
2076 : */
2077 : if (iter.hasScript()) {
2078 : uint32_t stepperCount = 0;
2079 : JSScript* trappingScript = iter.script();
2080 0 : GlobalObject* global = cx->global();
2081 0 : if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
2082 0 : for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
2083 0 : Debugger* dbg = *p;
2084 0 : for (FrameMap::Range r = dbg->frames.all(); !r.empty(); r.popFront()) {
2085 0 : AbstractFramePtr frame = r.front().key();
2086 0 : NativeObject* frameobj = r.front().value();
2087 0 : if (frame.isWasmDebugFrame())
2088 0 : continue;
2089 0 : if (frame.script() == trappingScript &&
2090 0 : !frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
2091 0 : {
2092 0 : stepperCount++;
2093 0 : }
2094 : }
2095 0 : }
2096 : }
2097 : MOZ_ASSERT(stepperCount == trappingScript->stepModeCount());
2098 : }
2099 : #endif
2100 0 :
2101 : // Call onStep for frames that have the handler set.
2102 : for (size_t i = 0; i < frames.length(); i++) {
2103 : HandleDebuggerFrame frame = frames[i];
2104 : OnStepHandler* handler = frame->onStepHandler();
2105 0 : if (!handler)
2106 0 : continue;
2107 0 :
2108 0 : Debugger* dbg = Debugger::fromChildJSObject(frame);
2109 0 : EnterDebuggeeNoExecute nx(cx, *dbg);
2110 :
2111 0 : Maybe<AutoRealm> ar;
2112 0 : ar.emplace(cx, dbg->object);
2113 :
2114 0 : ResumeMode resumeMode = ResumeMode::Continue;
2115 0 : bool success = handler->onStep(cx, frame, resumeMode, vp);
2116 : resumeMode = dbg->processParsedHandlerResult(ar, iter.abstractFramePtr(), iter.pc(), success,
2117 0 : resumeMode, vp);
2118 0 : if (resumeMode != ResumeMode::Continue) {
2119 0 : savedExc.drop();
2120 : return resumeMode;
2121 0 : }
2122 0 : }
2123 0 :
2124 : vp.setUndefined();
2125 : return ResumeMode::Continue;
2126 : }
2127 0 :
2128 0 : ResumeMode
2129 : Debugger::fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global, MutableHandleValue vp)
2130 : {
2131 : RootedObject hook(cx, getHook(OnNewGlobalObject));
2132 0 : MOZ_ASSERT(hook);
2133 : MOZ_ASSERT(hook->isCallable());
2134 0 :
2135 0 : Maybe<AutoRealm> ar;
2136 0 : ar.emplace(cx, object);
2137 :
2138 0 : RootedValue wrappedGlobal(cx, ObjectValue(*global));
2139 0 : if (!wrapDebuggeeValue(cx, &wrappedGlobal))
2140 : return reportUncaughtException(ar);
2141 0 :
2142 0 : // onNewGlobalObject is infallible, and thus is only allowed to return
2143 0 : // undefined as a resumption value. If it returns anything else, we throw.
2144 : // And if that happens, or if the hook itself throws, we invoke the
2145 : // uncaughtExceptionHook so that we never leave an exception pending on the
2146 : // cx. This allows JS_NewGlobalObject to avoid handling failures from debugger
2147 : // hooks.
2148 : RootedValue rv(cx);
2149 : RootedValue fval(cx, ObjectValue(*hook));
2150 : bool ok = js::Call(cx, fval, object, wrappedGlobal, &rv);
2151 0 : if (ok && !rv.isUndefined()) {
2152 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2153 0 : JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);
2154 0 : ok = false;
2155 : }
2156 0 : // NB: Even though we don't care about what goes into it, we have to pass vp
2157 0 : // to handleUncaughtException so that it parses resumption values from the
2158 : // uncaughtExceptionHook and tells the caller whether we should execute the
2159 : // rest of the onNewGlobalObject hooks or not.
2160 : ResumeMode resumeMode = ok ? ResumeMode::Continue
2161 : : handleUncaughtException(ar, vp);
2162 : MOZ_ASSERT(!cx->isExceptionPending());
2163 0 : return resumeMode;
2164 0 : }
2165 0 :
2166 : void
2167 : Debugger::slowPathOnNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global)
2168 : {
2169 : MOZ_ASSERT(!cx->runtime()->onNewGlobalObjectWatchers().isEmpty());
2170 0 : if (global->realm()->creationOptions().invisibleToDebugger())
2171 : return;
2172 0 :
2173 0 : /*
2174 0 : * Make a copy of the runtime's onNewGlobalObjectWatchers before running the
2175 : * handlers. Since one Debugger's handler can disable another's, the list
2176 : * can be mutated while we're walking it.
2177 : */
2178 : AutoObjectVector watchers(cx);
2179 : for (auto& dbg : cx->runtime()->onNewGlobalObjectWatchers()) {
2180 : MOZ_ASSERT(dbg.observesNewGlobalObject());
2181 0 : JSObject* obj = dbg.object;
2182 0 : JS::ExposeObjectToActiveJS(obj);
2183 0 : if (!watchers.append(obj)) {
2184 0 : if (cx->isExceptionPending())
2185 0 : cx->clearPendingException();
2186 0 : return;
2187 0 : }
2188 0 : }
2189 0 :
2190 : ResumeMode resumeMode = ResumeMode::Continue;
2191 : RootedValue value(cx);
2192 :
2193 0 : for (size_t i = 0; i < watchers.length(); i++) {
2194 0 : Debugger* dbg = fromJSObject(watchers[i]);
2195 : EnterDebuggeeNoExecute nx(cx, *dbg);
2196 0 :
2197 0 : // We disallow resumption values from onNewGlobalObject hooks, because we
2198 0 : // want the debugger hooks for global object creation to be infallible.
2199 : // But if an onNewGlobalObject hook throws, and the uncaughtExceptionHook
2200 : // decides to raise an error, we want to at least avoid invoking the rest
2201 : // of the onNewGlobalObject handlers in the list (not for any super
2202 : // compelling reason, just because it seems like the right thing to do).
2203 : // So we ignore whatever comes out in |value|, but break out of the loop
2204 : // if a non-success resume mode is returned.
2205 : if (dbg->observesNewGlobalObject()) {
2206 : resumeMode = dbg->fireNewGlobalObject(cx, global, &value);
2207 : if (resumeMode != ResumeMode::Continue && resumeMode != ResumeMode::Return)
2208 0 : break;
2209 0 : }
2210 0 : }
2211 : MOZ_ASSERT(!cx->isExceptionPending());
2212 : }
2213 :
2214 0 : /* static */ bool
2215 : Debugger::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
2216 : mozilla::TimeStamp when, GlobalObject::DebuggerVector& dbgs)
2217 : {
2218 0 : MOZ_ASSERT(!dbgs.empty());
2219 : mozilla::DebugOnly<ReadBarriered<Debugger*>*> begin = dbgs.begin();
2220 :
2221 0 : // Root all the Debuggers while we're iterating over them;
2222 0 : // appendAllocationSite calls Compartment::wrap, and thus can GC.
2223 : //
2224 : // SpiderMonkey protocol is generally for the caller to prove that it has
2225 : // rooted the stuff it's asking you to operate on (i.e. by passing a
2226 : // Handle), but in this case, we're iterating over a global's list of
2227 : // Debuggers, and globals only hold their Debuggers weakly.
2228 : Rooted<GCVector<JSObject*>> activeDebuggers(cx, GCVector<JSObject*>(cx));
2229 : for (auto dbgp = dbgs.begin(); dbgp < dbgs.end(); dbgp++) {
2230 : if (!activeDebuggers.append((*dbgp)->object))
2231 0 : return false;
2232 0 : }
2233 0 :
2234 : for (auto dbgp = dbgs.begin(); dbgp < dbgs.end(); dbgp++) {
2235 : // The set of debuggers had better not change while we're iterating,
2236 : // such that the vector gets reallocated.
2237 0 : MOZ_ASSERT(dbgs.begin() == begin);
2238 :
2239 : if ((*dbgp)->trackingAllocationSites &&
2240 0 : (*dbgp)->enabled &&
2241 : !(*dbgp)->appendAllocationSite(cx, obj, frame, when))
2242 0 : {
2243 0 : return false;
2244 0 : }
2245 : }
2246 :
2247 : return true;
2248 : }
2249 :
2250 : bool
2251 : Debugger::isDebuggeeUnbarriered(const Realm* realm) const
2252 : {
2253 : MOZ_ASSERT(realm);
2254 0 : return realm->isDebuggee() && debuggees.has(realm->unsafeUnbarrieredMaybeGlobal());
2255 : }
2256 0 :
2257 0 : bool
2258 : Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
2259 : mozilla::TimeStamp when)
2260 : {
2261 0 : MOZ_ASSERT(trackingAllocationSites && enabled);
2262 :
2263 : AutoRealm ar(cx, object);
2264 0 : RootedObject wrappedFrame(cx, frame);
2265 : if (!cx->compartment()->wrap(cx, &wrappedFrame))
2266 0 : return false;
2267 0 :
2268 0 : RootedAtom ctorName(cx);
2269 : {
2270 : AutoRealm ar(cx, obj);
2271 0 : if (!JSObject::constructorDisplayAtom(cx, obj, &ctorName))
2272 : return false;
2273 0 : }
2274 0 : if (ctorName)
2275 0 : cx->markAtom(ctorName);
2276 :
2277 0 : auto className = obj->getClass()->name;
2278 0 : auto size = JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf);
2279 : auto inNursery = gc::IsInsideNursery(obj);
2280 0 :
2281 0 : if (!allocationsLog.emplaceBack(wrappedFrame, when, className, ctorName, size, inNursery)) {
2282 0 : ReportOutOfMemory(cx);
2283 : return false;
2284 0 : }
2285 0 :
2286 0 : if (allocationsLog.length() > maxAllocationsLogLength) {
2287 : allocationsLog.popFront();
2288 : MOZ_ASSERT(allocationsLog.length() == maxAllocationsLogLength);
2289 0 : allocationsLogOverflowed = true;
2290 0 : }
2291 0 :
2292 0 : return true;
2293 : }
2294 :
2295 : ResumeMode
2296 : Debugger::firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp)
2297 : {
2298 : MOZ_ASSERT(hook == OnNewPromise || hook == OnPromiseSettled);
2299 0 :
2300 : RootedObject hookObj(cx, getHook(hook));
2301 0 : MOZ_ASSERT(hookObj);
2302 : MOZ_ASSERT(hookObj->isCallable());
2303 0 :
2304 0 : Maybe<AutoRealm> ar;
2305 0 : ar.emplace(cx, object);
2306 :
2307 0 : RootedValue dbgObj(cx, ObjectValue(*promise));
2308 0 : if (!wrapDebuggeeValue(cx, &dbgObj))
2309 : return reportUncaughtException(ar);
2310 0 :
2311 0 : // Like onNewGlobalObject, the Promise hooks are infallible and the comments
2312 0 : // in |Debugger::fireNewGlobalObject| apply here as well.
2313 : RootedValue fval(cx, ObjectValue(*hookObj));
2314 : RootedValue rv(cx);
2315 : bool ok = js::Call(cx, fval, object, dbgObj, &rv);
2316 0 : if (ok && !rv.isUndefined()) {
2317 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2318 0 : JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);
2319 0 : ok = false;
2320 : }
2321 0 :
2322 0 : ResumeMode resumeMode = ok ? ResumeMode::Continue
2323 : : handleUncaughtException(ar, vp);
2324 : MOZ_ASSERT(!cx->isExceptionPending());
2325 0 : return resumeMode;
2326 0 : }
2327 0 :
2328 : /* static */ void
2329 : Debugger::slowPathPromiseHook(JSContext* cx, Hook hook, Handle<PromiseObject*> promise)
2330 : {
2331 : MOZ_ASSERT(hook == OnNewPromise || hook == OnPromiseSettled);
2332 0 :
2333 : Maybe<AutoRealm> ar;
2334 0 : if (hook == OnNewPromise)
2335 : ar.emplace(cx, promise);
2336 0 :
2337 0 : assertSameCompartment(cx, promise);
2338 0 :
2339 : RootedValue rval(cx);
2340 0 : ResumeMode resumeMode = dispatchHook(
2341 : cx,
2342 0 : [hook](Debugger* dbg) -> bool { return dbg->getHook(hook); },
2343 0 : [&](Debugger* dbg) -> ResumeMode {
2344 : (void) dbg->firePromiseHook(cx, hook, promise, &rval);
2345 0 : return ResumeMode::Continue;
2346 0 : });
2347 0 :
2348 0 : if (resumeMode == ResumeMode::Terminate) {
2349 0 : // The dispatch hook function might fail to append into the list of
2350 : // Debuggers which are watching for the hook.
2351 0 : cx->clearPendingException();
2352 : return;
2353 : }
2354 0 :
2355 0 : // Promise hooks are infallible and we ignore errors from uncaught
2356 : // exceptions by design.
2357 : MOZ_ASSERT(resumeMode == ResumeMode::Continue);
2358 : }
2359 :
2360 0 :
2361 : /*** Debugger code invalidation for observing execution ******************************************/
2362 :
2363 : class MOZ_RAII ExecutionObservableRealms : public Debugger::ExecutionObservableSet
2364 : {
2365 : HashSet<Realm*> realms_;
2366 0 : HashSet<Zone*> zones_;
2367 :
2368 : public:
2369 : explicit ExecutionObservableRealms(JSContext* cx
2370 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
2371 : : realms_(cx),
2372 : zones_(cx)
2373 : {
2374 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
2375 0 : }
2376 :
2377 0 : bool init() { return realms_.init() && zones_.init(); }
2378 : bool add(Realm* realm) { return realms_.put(realm) && zones_.put(realm->zone()); }
2379 :
2380 0 : using RealmRange = HashSet<Realm*>::Range;
2381 0 : const HashSet<Realm*>* realms() const { return &realms_; }
2382 :
2383 : const HashSet<Zone*>* zones() const override { return &zones_; }
2384 : bool shouldRecompileOrInvalidate(JSScript* script) const override {
2385 : return script->hasBaselineScript() && realms_.has(script->realm());
2386 0 : }
2387 0 : bool shouldMarkAsDebuggee(FrameIter& iter) const override {
2388 0 : // AbstractFramePtr can't refer to non-remateralized Ion frames or
2389 : // non-debuggee wasm frames, so if iter refers to one such, we know we
2390 0 : // don't match.
2391 : return iter.hasUsableAbstractFramePtr() && realms_.has(iter.realm());
2392 : }
2393 :
2394 0 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
2395 : };
2396 :
2397 : // Given a particular AbstractFramePtr F that has become observable, this
2398 : // represents the stack frames that need to be bailed out or marked as
2399 : // debuggees, and the scripts that need to be recompiled, taking inlining into
2400 : // account.
2401 : class MOZ_RAII ExecutionObservableFrame : public Debugger::ExecutionObservableSet
2402 : {
2403 : AbstractFramePtr frame_;
2404 0 :
2405 : public:
2406 : explicit ExecutionObservableFrame(AbstractFramePtr frame
2407 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
2408 : : frame_(frame)
2409 : {
2410 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
2411 0 : }
2412 :
2413 0 : Zone* singleZone() const override {
2414 : // We never inline across compartments, let alone across zones, so
2415 : // frames_'s script's zone is the only one of interest.
2416 0 : return frame_.script()->compartment()->zone();
2417 : }
2418 :
2419 0 : JSScript* singleScriptForZoneInvalidation() const override {
2420 : MOZ_CRASH("ExecutionObservableFrame shouldn't need zone-wide invalidation.");
2421 : return nullptr;
2422 0 : }
2423 0 :
2424 : bool shouldRecompileOrInvalidate(JSScript* script) const override {
2425 : // Normally, *this represents exactly one script: the one frame_ is
2426 : // running.
2427 0 : //
2428 : // However, debug-mode OSR uses *this for both invalidating Ion frames,
2429 : // and recompiling the Baseline scripts that those Ion frames will bail
2430 : // out into. Suppose frame_ is an inline frame, executing a copy of its
2431 : // JSScript, S_inner, that has been inlined into the IonScript of some
2432 : // other JSScript, S_outer. We must match S_outer, to decide which Ion
2433 : // frame to invalidate; and we must match S_inner, to decide which
2434 : // Baseline script to recompile.
2435 : //
2436 : // Note that this does not, by design, invalidate *all* inliners of
2437 : // frame_.script(), as only frame_ is made observable, not
2438 : // frame_.script().
2439 : if (!script->hasBaselineScript())
2440 : return false;
2441 :
2442 0 : if (frame_.hasScript() && script == frame_.script())
2443 : return true;
2444 :
2445 0 : return frame_.isRematerializedFrame() &&
2446 : script == frame_.asRematerializedFrame()->outerScript();
2447 : }
2448 0 :
2449 0 : bool shouldMarkAsDebuggee(FrameIter& iter) const override {
2450 : // AbstractFramePtr can't refer to non-remateralized Ion frames or
2451 : // non-debuggee wasm frames, so if iter refers to one such, we know we
2452 0 : // don't match.
2453 : //
2454 : // We never use this 'has' overload for frame invalidation, only for
2455 : // frame debuggee marking; so this overload doesn't need a parallel to
2456 : // the just-so inlining logic above.
2457 : return iter.hasUsableAbstractFramePtr() && iter.abstractFramePtr() == frame_;
2458 : }
2459 :
2460 0 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
2461 : };
2462 :
2463 : class MOZ_RAII ExecutionObservableScript : public Debugger::ExecutionObservableSet
2464 : {
2465 : RootedScript script_;
2466 0 :
2467 : public:
2468 : ExecutionObservableScript(JSContext* cx, JSScript* script
2469 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
2470 : : script_(cx, script)
2471 : {
2472 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
2473 0 : }
2474 :
2475 0 : Zone* singleZone() const override { return script_->compartment()->zone(); }
2476 : JSScript* singleScriptForZoneInvalidation() const override { return script_; }
2477 : bool shouldRecompileOrInvalidate(JSScript* script) const override {
2478 0 : return script->hasBaselineScript() && script == script_;
2479 0 : }
2480 0 : bool shouldMarkAsDebuggee(FrameIter& iter) const override {
2481 0 : // AbstractFramePtr can't refer to non-remateralized Ion frames, and
2482 : // while a non-rematerialized Ion frame may indeed be running script_,
2483 0 : // we cannot mark them as debuggees until they bail out.
2484 : //
2485 : // Upon bailing out, any newly constructed Baseline frames that came
2486 : // from Ion frames with scripts that are isDebuggee() is marked as
2487 : // debuggee. This is correct in that the only other way a frame may be
2488 : // marked as debuggee is via Debugger.Frame reflection, which would
2489 : // have rematerialized any Ion frames.
2490 : //
2491 : // Also AbstractFramePtr can't refer to non-debuggee wasm frames, so if
2492 : // iter refers to one such, we know we don't match.
2493 : return iter.hasUsableAbstractFramePtr() && !iter.isWasm() &&
2494 : iter.abstractFramePtr().script() == script_;
2495 : }
2496 0 :
2497 0 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
2498 : };
2499 :
2500 : /* static */ bool
2501 : Debugger::updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObservableSet& obs,
2502 : IsObserving observing)
2503 : {
2504 0 : AutoSuppressProfilerSampling suppressProfilerSampling(cx);
2505 :
2506 : {
2507 0 : jit::JitContext jctx(cx, nullptr);
2508 : if (!jit::RecompileOnStackBaselineScriptsForDebugMode(cx, obs, observing)) {
2509 : ReportOutOfMemory(cx);
2510 0 : return false;
2511 0 : }
2512 0 : }
2513 0 :
2514 : AbstractFramePtr oldestEnabledFrame;
2515 : for (FrameIter iter(cx);
2516 : !iter.done();
2517 0 : ++iter)
2518 0 : {
2519 0 : if (obs.shouldMarkAsDebuggee(iter)) {
2520 : if (observing) {
2521 : if (!iter.abstractFramePtr().isDebuggee()) {
2522 0 : oldestEnabledFrame = iter.abstractFramePtr();
2523 0 : oldestEnabledFrame.setIsDebuggee();
2524 0 : }
2525 0 : if (iter.abstractFramePtr().isWasmDebugFrame())
2526 0 : iter.abstractFramePtr().asWasmDebugFrame()->observe(cx);
2527 : } else {
2528 0 : #ifdef DEBUG
2529 0 : // Debugger.Frame lifetimes are managed by the debug epilogue,
2530 : // so in general it's unsafe to unmark a frame if it has a
2531 : // Debugger.Frame associated with it.
2532 : MOZ_ASSERT(!inFrameMaps(iter.abstractFramePtr()));
2533 : #endif
2534 : iter.abstractFramePtr().unsetIsDebuggee();
2535 0 : }
2536 : }
2537 0 : }
2538 :
2539 : // See comment in unsetPrevUpToDateUntil.
2540 : if (oldestEnabledFrame) {
2541 : AutoRealm ar(cx, oldestEnabledFrame.environmentChain());
2542 : DebugEnvironments::unsetPrevUpToDateUntil(cx, oldestEnabledFrame);
2543 0 : }
2544 0 :
2545 0 : return true;
2546 : }
2547 :
2548 : static inline void
2549 : MarkBaselineScriptActiveIfObservable(JSScript* script, const Debugger::ExecutionObservableSet& obs)
2550 : {
2551 : if (obs.shouldRecompileOrInvalidate(script))
2552 0 : script->baselineScript()->setActive();
2553 : }
2554 0 :
2555 0 : static bool
2556 0 : AppendAndInvalidateScript(JSContext* cx, Zone* zone, JSScript* script, Vector<JSScript*>& scripts)
2557 : {
2558 : // Enter the script's compartment as addPendingRecompile attempts to
2559 0 : // cancel off-thread compilations, whose books are kept on the
2560 : // script's compartment.
2561 : MOZ_ASSERT(script->compartment()->zone() == zone);
2562 : AutoRealm ar(cx, script);
2563 : zone->types.addPendingRecompile(cx, script);
2564 0 : return scripts.append(script);
2565 0 : }
2566 0 :
2567 0 : static bool
2568 : UpdateExecutionObservabilityOfScriptsInZone(JSContext* cx, Zone* zone,
2569 : const Debugger::ExecutionObservableSet& obs,
2570 : Debugger::IsObserving observing)
2571 0 : {
2572 : using namespace js::jit;
2573 :
2574 : AutoSuppressProfilerSampling suppressProfilerSampling(cx);
2575 :
2576 : FreeOp* fop = cx->runtime()->defaultFreeOp();
2577 0 :
2578 : Vector<JSScript*> scripts(cx);
2579 0 :
2580 : // Iterate through observable scripts, invalidating their Ion scripts and
2581 0 : // appending them to a vector for discarding their baseline scripts later.
2582 : {
2583 : AutoEnterAnalysis enter(fop, zone);
2584 : if (JSScript* script = obs.singleScriptForZoneInvalidation()) {
2585 : if (obs.shouldRecompileOrInvalidate(script)) {
2586 0 : if (!AppendAndInvalidateScript(cx, zone, script, scripts))
2587 0 : return false;
2588 0 : }
2589 0 : } else {
2590 0 : for (auto iter = zone->cellIter<JSScript>(); !iter.done(); iter.next()) {
2591 : JSScript* script = iter;
2592 : if (obs.shouldRecompileOrInvalidate(script) &&
2593 0 : !gc::IsAboutToBeFinalizedUnbarriered(&script))
2594 0 : {
2595 0 : if (!AppendAndInvalidateScript(cx, zone, script, scripts))
2596 0 : return false;
2597 : }
2598 0 : }
2599 0 : }
2600 : }
2601 :
2602 : // Code below this point must be infallible to ensure the active bit of
2603 : // BaselineScripts is in a consistent state.
2604 : //
2605 : // Mark active baseline scripts in the observable set so that they don't
2606 : // get discarded. They will be recompiled.
2607 : for (JitActivationIterator actIter(cx); !actIter.done(); ++actIter) {
2608 : if (actIter->compartment()->zone() != zone)
2609 : continue;
2610 0 :
2611 0 : for (OnlyJSJitFrameIter iter(actIter); !iter.done(); ++iter) {
2612 : const jit::JSJitFrameIter& frame = iter.frame();
2613 : switch (frame.type()) {
2614 0 : case JitFrame_BaselineJS:
2615 0 : MarkBaselineScriptActiveIfObservable(frame.script(), obs);
2616 0 : break;
2617 : case JitFrame_IonJS:
2618 0 : MarkBaselineScriptActiveIfObservable(frame.script(), obs);
2619 0 : for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more(); ++inlineIter)
2620 : MarkBaselineScriptActiveIfObservable(inlineIter.script(), obs);
2621 0 : break;
2622 0 : default:;
2623 0 : }
2624 0 : }
2625 : }
2626 :
2627 : // Iterate through the scripts again and finish discarding
2628 : // BaselineScripts. This must be done as a separate phase as we can only
2629 : // discard the BaselineScript on scripts that have no IonScript.
2630 : for (size_t i = 0; i < scripts.length(); i++) {
2631 : MOZ_ASSERT_IF(scripts[i]->isDebuggee(), observing);
2632 : FinishDiscardBaselineScript(fop, scripts[i]);
2633 0 : }
2634 0 :
2635 0 : // Iterate through all wasm instances to find ones that need to be updated.
2636 : for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
2637 : for (wasm::Instance* instance : r->wasm.instances()) {
2638 : if (!instance->debugEnabled())
2639 0 : continue;
2640 0 :
2641 0 : bool enableTrap = observing == Debugger::IsObserving::Observing;
2642 : instance->ensureEnterFrameTrapsState(cx, enableTrap);
2643 : }
2644 0 : }
2645 0 :
2646 : return true;
2647 : }
2648 :
2649 0 : /* static */ bool
2650 : Debugger::updateExecutionObservabilityOfScripts(JSContext* cx, const ExecutionObservableSet& obs,
2651 : IsObserving observing)
2652 : {
2653 0 : if (Zone* zone = obs.singleZone())
2654 : return UpdateExecutionObservabilityOfScriptsInZone(cx, zone, obs, observing);
2655 :
2656 0 : typedef ExecutionObservableSet::ZoneRange ZoneRange;
2657 0 : for (ZoneRange r = obs.zones()->all(); !r.empty(); r.popFront()) {
2658 : if (!UpdateExecutionObservabilityOfScriptsInZone(cx, r.front(), obs, observing))
2659 : return false;
2660 0 : }
2661 0 :
2662 0 : return true;
2663 : }
2664 :
2665 0 : template <typename FrameFn>
2666 : /* static */ void
2667 : Debugger::forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn)
2668 : {
2669 : GlobalObject* global = frame.global();
2670 15048 : if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
2671 : for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
2672 15048 : Debugger* dbg = *p;
2673 15048 : if (FrameMap::Ptr entry = dbg->frames.lookup(frame))
2674 0 : fn(entry->value());
2675 0 : }
2676 0 : }
2677 0 : }
2678 :
2679 : /* static */ bool
2680 0 : Debugger::getDebuggerFrames(AbstractFramePtr frame, MutableHandle<DebuggerFrameVector> frames)
2681 : {
2682 : bool hadOOM = false;
2683 0 : forEachDebuggerFrame(frame, [&](DebuggerFrame* frameobj) {
2684 : if (!hadOOM && !frames.append(frameobj))
2685 0 : hadOOM = true;
2686 0 : });
2687 0 : return !hadOOM;
2688 0 : }
2689 0 :
2690 0 : /* static */ bool
2691 : Debugger::updateExecutionObservability(JSContext* cx, ExecutionObservableSet& obs,
2692 : IsObserving observing)
2693 : {
2694 0 : if (!obs.singleZone() && obs.zones()->empty())
2695 : return true;
2696 :
2697 0 : // Invalidate scripts first so we can set the needsArgsObj flag on scripts
2698 : // before patching frames.
2699 : return updateExecutionObservabilityOfScripts(cx, obs, observing) &&
2700 : updateExecutionObservabilityOfFrames(cx, obs, observing);
2701 : }
2702 0 :
2703 0 : /* static */ bool
2704 : Debugger::ensureExecutionObservabilityOfScript(JSContext* cx, JSScript* script)
2705 : {
2706 : if (script->isDebuggee())
2707 0 : return true;
2708 : ExecutionObservableScript obs(cx, script);
2709 0 : return updateExecutionObservability(cx, obs, Observing);
2710 : }
2711 0 :
2712 0 : /* static */ bool
2713 : Debugger::ensureExecutionObservabilityOfOsrFrame(JSContext* cx, InterpreterFrame* frame)
2714 : {
2715 : MOZ_ASSERT(frame->isDebuggee());
2716 0 : if (frame->script()->hasBaselineScript() &&
2717 : frame->script()->baselineScript()->hasDebugInstrumentation())
2718 0 : {
2719 0 : return true;
2720 0 : }
2721 : ExecutionObservableFrame obs(frame);
2722 : return updateExecutionObservabilityOfFrames(cx, obs, Observing);
2723 : }
2724 0 :
2725 0 : /* static */ bool
2726 : Debugger::ensureExecutionObservabilityOfFrame(JSContext* cx, AbstractFramePtr frame)
2727 : {
2728 : MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(), frame.isDebuggee());
2729 0 : MOZ_ASSERT_IF(frame.isWasmDebugFrame(), frame.wasmInstance()->debugEnabled());
2730 : if (frame.isDebuggee())
2731 0 : return true;
2732 0 : ExecutionObservableFrame obs(frame);
2733 0 : return updateExecutionObservabilityOfFrames(cx, obs, Observing);
2734 : }
2735 0 :
2736 0 : /* static */ bool
2737 : Debugger::ensureExecutionObservabilityOfRealm(JSContext* cx, Realm* realm)
2738 : {
2739 : if (realm->debuggerObservesAllExecution())
2740 0 : return true;
2741 : ExecutionObservableRealms obs(cx);
2742 0 : if (!obs.init() || !obs.add(realm))
2743 : return false;
2744 0 : realm->updateDebuggerObservesAllExecution();
2745 0 : return updateExecutionObservability(cx, obs, Observing);
2746 : }
2747 0 :
2748 0 : /* static */ bool
2749 : Debugger::hookObservesAllExecution(Hook which)
2750 : {
2751 : return which == OnEnterFrame;
2752 0 : }
2753 :
2754 0 : Debugger::IsObserving
2755 : Debugger::observesAllExecution() const
2756 : {
2757 : if (enabled && !!getHook(OnEnterFrame))
2758 0 : return Observing;
2759 : return NotObserving;
2760 0 : }
2761 :
2762 0 : Debugger::IsObserving
2763 : Debugger::observesAsmJS() const
2764 : {
2765 : if (enabled && !allowUnobservedAsmJS)
2766 0 : return Observing;
2767 : return NotObserving;
2768 0 : }
2769 :
2770 0 : Debugger::IsObserving
2771 : Debugger::observesBinarySource() const
2772 : {
2773 : if (enabled && allowWasmBinarySource)
2774 0 : return Observing;
2775 : return NotObserving;
2776 0 : }
2777 :
2778 0 : Debugger::IsObserving
2779 : Debugger::observesCoverage() const
2780 : {
2781 : if (enabled && collectCoverageInfo)
2782 0 : return Observing;
2783 : return NotObserving;
2784 0 : }
2785 :
2786 0 : // Toggle whether this Debugger's debuggees observe all execution. This is
2787 : // called when a hook that observes all execution is set or unset. See
2788 : // hookObservesAllExecution.
2789 : bool
2790 : Debugger::updateObservesAllExecutionOnDebuggees(JSContext* cx, IsObserving observing)
2791 : {
2792 : ExecutionObservableRealms obs(cx);
2793 0 : if (!obs.init())
2794 : return false;
2795 0 :
2796 0 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2797 : GlobalObject* global = r.front();
2798 : JS::Realm* realm = global->realm();
2799 0 :
2800 0 : if (realm->debuggerObservesAllExecution() == observing)
2801 0 : continue;
2802 :
2803 0 : // It's expensive to eagerly invalidate and recompile a realm,
2804 : // so add the realm to the set only if we are observing.
2805 : if (observing && !obs.add(realm))
2806 : return false;
2807 : }
2808 0 :
2809 0 : if (!updateExecutionObservability(cx, obs, observing))
2810 : return false;
2811 :
2812 0 : using RealmRange = ExecutionObservableRealms::RealmRange;
2813 : for (RealmRange r = obs.realms()->all(); !r.empty(); r.popFront())
2814 : r.front()->updateDebuggerObservesAllExecution();
2815 :
2816 0 : return true;
2817 0 : }
2818 :
2819 0 : bool
2820 : Debugger::updateObservesCoverageOnDebuggees(JSContext* cx, IsObserving observing)
2821 : {
2822 : ExecutionObservableRealms obs(cx);
2823 0 : if (!obs.init())
2824 : return false;
2825 0 :
2826 0 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2827 : GlobalObject* global = r.front();
2828 : Realm* realm = global->realm();
2829 0 :
2830 0 : if (realm->debuggerObservesCoverage() == observing)
2831 0 : continue;
2832 :
2833 0 : // Invalidate and recompile a realm to add or remove PCCounts
2834 : // increments. We have to eagerly invalidate, as otherwise we might have
2835 : // dangling pointers to freed PCCounts.
2836 : if (!obs.add(realm))
2837 : return false;
2838 : }
2839 0 :
2840 0 : // If any frame on the stack belongs to the debuggee, then we cannot update
2841 : // the ScriptCounts, because this would imply to invalidate a Debugger.Frame
2842 : // to recompile it with/without ScriptCount support.
2843 : for (FrameIter iter(cx);
2844 : !iter.done();
2845 : ++iter)
2846 0 : {
2847 0 : if (obs.shouldMarkAsDebuggee(iter)) {
2848 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_IDLE);
2849 : return false;
2850 0 : }
2851 0 : }
2852 0 :
2853 : if (!updateExecutionObservability(cx, obs, observing))
2854 : return false;
2855 :
2856 0 : // All realms can safely be toggled, and all scripts will be recompiled.
2857 : // Thus we can update each realm accordingly.
2858 : using RealmRange = ExecutionObservableRealms::RealmRange;
2859 : for (RealmRange r = obs.realms()->all(); !r.empty(); r.popFront())
2860 : r.front()->updateDebuggerObservesCoverage();
2861 :
2862 0 : return true;
2863 0 : }
2864 :
2865 0 : void
2866 : Debugger::updateObservesAsmJSOnDebuggees(IsObserving observing)
2867 : {
2868 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2869 0 : GlobalObject* global = r.front();
2870 : Realm* realm = global->realm();
2871 0 :
2872 0 : if (realm->debuggerObservesAsmJS() == observing)
2873 0 : continue;
2874 :
2875 0 : realm->updateDebuggerObservesAsmJS();
2876 : }
2877 : }
2878 :
2879 : void
2880 0 : Debugger::updateObservesBinarySourceDebuggees(IsObserving observing)
2881 : {
2882 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2883 0 : GlobalObject* global = r.front();
2884 : Realm* realm = global->realm();
2885 0 :
2886 0 : if (realm->debuggerObservesBinarySource() == observing)
2887 0 : continue;
2888 :
2889 0 : realm->updateDebuggerObservesBinarySource();
2890 : }
2891 : }
2892 :
2893 :
2894 0 : /*** Allocations Tracking *************************************************************************/
2895 :
2896 : /* static */ bool
2897 : Debugger::cannotTrackAllocations(const GlobalObject& global)
2898 : {
2899 : auto existingCallback = global.realm()->getAllocationMetadataBuilder();
2900 0 : return existingCallback && existingCallback != &SavedStacks::metadataBuilder;
2901 : }
2902 0 :
2903 0 : /* static */ bool
2904 : Debugger::isObservedByDebuggerTrackingAllocations(const GlobalObject& debuggee)
2905 : {
2906 : if (auto* v = debuggee.getDebuggers()) {
2907 0 : for (auto p = v->begin(); p != v->end(); p++) {
2908 : if ((*p)->trackingAllocationSites && (*p)->enabled) {
2909 0 : return true;
2910 0 : }
2911 0 : }
2912 : }
2913 :
2914 : return false;
2915 : }
2916 :
2917 : /* static */ bool
2918 : Debugger::addAllocationsTracking(JSContext* cx, Handle<GlobalObject*> debuggee)
2919 : {
2920 : // Precondition: the given global object is being observed by at least one
2921 0 : // Debugger that is tracking allocations.
2922 : MOZ_ASSERT(isObservedByDebuggerTrackingAllocations(*debuggee));
2923 :
2924 : if (Debugger::cannotTrackAllocations(*debuggee)) {
2925 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2926 : JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
2927 0 : return false;
2928 : }
2929 0 :
2930 0 : debuggee->realm()->setAllocationMetadataBuilder(&SavedStacks::metadataBuilder);
2931 : debuggee->realm()->chooseAllocationSamplingProbability();
2932 : return true;
2933 0 : }
2934 0 :
2935 0 : /* static */ void
2936 : Debugger::removeAllocationsTracking(GlobalObject& global)
2937 : {
2938 : // If there are still Debuggers that are observing allocations, we cannot
2939 0 : // remove the metadata callback yet. Recompute the sampling probability
2940 : // based on the remaining debuggers' needs.
2941 : if (isObservedByDebuggerTrackingAllocations(global)) {
2942 : global.realm()->chooseAllocationSamplingProbability();
2943 : return;
2944 0 : }
2945 0 :
2946 : global.realm()->forgetAllocationMetadataBuilder();
2947 : }
2948 :
2949 0 : bool
2950 : Debugger::addAllocationsTrackingForAllDebuggees(JSContext* cx)
2951 : {
2952 : MOZ_ASSERT(trackingAllocationSites);
2953 0 :
2954 : // We don't want to end up in a state where we added allocations
2955 0 : // tracking to some of our debuggees, but failed to do so for
2956 : // others. Before attempting to start tracking allocations in *any* of
2957 : // our debuggees, ensure that we will be able to track allocations for
2958 : // *all* of our debuggees.
2959 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2960 : if (Debugger::cannotTrackAllocations(*r.front().get())) {
2961 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2962 0 : JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
2963 0 : return false;
2964 : }
2965 0 : }
2966 0 :
2967 : Rooted<GlobalObject*> g(cx);
2968 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2969 : // This should always succeed, since we already checked for the
2970 0 : // error case above.
2971 0 : g = r.front().get();
2972 : MOZ_ALWAYS_TRUE(Debugger::addAllocationsTracking(cx, g));
2973 : }
2974 0 :
2975 0 : return true;
2976 : }
2977 :
2978 : void
2979 : Debugger::removeAllocationsTrackingForAllDebuggees()
2980 : {
2981 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront())
2982 0 : Debugger::removeAllocationsTracking(*r.front().get());
2983 :
2984 0 : allocationsLog.clear();
2985 0 : }
2986 :
2987 0 :
2988 0 :
2989 : /*** Debugger JSObjects **************************************************************************/
2990 :
2991 : void
2992 : Debugger::traceCrossCompartmentEdges(JSTracer* trc)
2993 : {
2994 : objects.traceCrossCompartmentEdges<DebuggerObject_trace>(trc);
2995 0 : environments.traceCrossCompartmentEdges<DebuggerEnv_trace>(trc);
2996 : scripts.traceCrossCompartmentEdges<DebuggerScript_trace>(trc);
2997 0 : sources.traceCrossCompartmentEdges<DebuggerSource_trace>(trc);
2998 0 : wasmInstanceScripts.traceCrossCompartmentEdges<DebuggerScript_trace>(trc);
2999 0 : wasmInstanceSources.traceCrossCompartmentEdges<DebuggerSource_trace>(trc);
3000 0 : }
3001 0 :
3002 0 : /*
3003 0 : * Ordinarily, WeakMap keys and values are marked because at some point it was
3004 : * discovered that the WeakMap was live; that is, some object containing the
3005 : * WeakMap was marked during mark phase.
3006 : *
3007 : * However, during zone GC, we have to do something about cross-compartment
3008 : * edges in non-GC'd compartments. Since the source may be live, we
3009 : * conservatively assume it is and mark the edge.
3010 : *
3011 : * Each Debugger object keeps four cross-compartment WeakMaps: objects, scripts,
3012 : * script source objects, and environments. They have the property that all
3013 : * their values are in the same compartment as the Debugger object, but we have
3014 : * to mark the keys and the private pointer in the wrapper object.
3015 : *
3016 : * We must scan all Debugger objects regardless of whether they *currently* have
3017 : * any debuggees in a compartment being GC'd, because the WeakMap entries
3018 : * persist even when debuggees are removed.
3019 : *
3020 : * This happens during the initial mark phase, not iterative marking, because
3021 : * all the edges being reported here are strong references.
3022 : *
3023 : * This method is also used during compacting GC to update cross compartment
3024 : * pointers into zones that are being compacted.
3025 : */
3026 : /* static */ void
3027 : Debugger::traceIncomingCrossCompartmentEdges(JSTracer* trc)
3028 : {
3029 : JSRuntime* rt = trc->runtime();
3030 0 : gc::State state = rt->gc.state();
3031 : MOZ_ASSERT(state == gc::State::MarkRoots || state == gc::State::Compact);
3032 0 :
3033 0 : for (Debugger* dbg : rt->debuggerList()) {
3034 0 : Zone* zone = MaybeForwarded(dbg->object.get())->zone();
3035 : if (!zone->isCollecting() || state == gc::State::Compact)
3036 0 : dbg->traceCrossCompartmentEdges(trc);
3037 0 : }
3038 0 : }
3039 0 :
3040 : /*
3041 0 : * This method has two tasks:
3042 : * 1. Mark Debugger objects that are unreachable except for debugger hooks that
3043 : * may yet be called.
3044 : * 2. Mark breakpoint handlers.
3045 : *
3046 : * This happens during the iterative part of the GC mark phase. This method
3047 : * returns true if it has to mark anything; GC calls it repeatedly until it
3048 : * returns false.
3049 : */
3050 : /* static */ bool
3051 : Debugger::markIteratively(GCMarker* marker)
3052 : {
3053 : bool markedAny = false;
3054 0 :
3055 : /*
3056 0 : * Find all Debugger objects in danger of GC. This code is a little
3057 : * convoluted since the easiest way to find them is via their debuggees.
3058 : */
3059 : JSRuntime* rt = marker->runtime();
3060 : for (RealmsIter r(rt); !r.done(); r.next()) {
3061 : if (r->isDebuggee()) {
3062 0 : GlobalObject* global = r->unsafeUnbarrieredMaybeGlobal();
3063 0 : if (!IsMarkedUnbarriered(rt, &global))
3064 0 : continue;
3065 0 :
3066 0 : /*
3067 0 : * Every debuggee has at least one debugger, so in this case
3068 : * getDebuggers can't return nullptr.
3069 : */
3070 : const GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
3071 : MOZ_ASSERT(debuggers);
3072 : for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
3073 0 : Debugger* dbg = *p;
3074 0 :
3075 0 : /*
3076 0 : * dbg is a Debugger with at least one debuggee. Check three things:
3077 : * - dbg is actually in a compartment that is being marked
3078 : * - it isn't already marked
3079 : * - it actually has hooks that might be called
3080 : */
3081 : GCPtrNativeObject& dbgobj = dbg->toJSObjectRef();
3082 : if (!dbgobj->zone()->isGCMarking())
3083 : continue;
3084 0 :
3085 0 : bool dbgMarked = IsMarked(rt, &dbgobj);
3086 : if (!dbgMarked && dbg->hasAnyLiveHooks(rt)) {
3087 : /*
3088 0 : * obj could be reachable only via its live, enabled
3089 0 : * debugger hooks, which may yet be called.
3090 : */
3091 : TraceEdge(marker, &dbgobj, "enabled Debugger");
3092 : markedAny = true;
3093 : dbgMarked = true;
3094 0 : }
3095 0 :
3096 0 : if (dbgMarked) {
3097 : /* Search for breakpoints to mark. */
3098 : for (Breakpoint* bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
3099 0 : switch (bp->site->type()) {
3100 : case BreakpointSite::Type::JS:
3101 0 : if (IsMarkedUnbarriered(rt, &bp->site->asJS()->script)) {
3102 0 : /*
3103 : * The debugger and the script are both live.
3104 0 : * Therefore the breakpoint handler is live.
3105 : */
3106 : if (!IsMarked(rt, &bp->getHandlerRef())) {
3107 : TraceEdge(marker, &bp->getHandlerRef(), "breakpoint handler");
3108 : markedAny = true;
3109 0 : }
3110 0 : }
3111 0 : break;
3112 : case BreakpointSite::Type::Wasm:
3113 : if (IsMarkedUnbarriered(rt, &bp->asWasm()->wasmInstance)) {
3114 : /*
3115 : * The debugger and the wasm instance are both live.
3116 0 : * Therefore the breakpoint handler is live.
3117 : */
3118 : if (!IsMarked(rt, &bp->getHandlerRef())) {
3119 : TraceEdge(marker, &bp->getHandlerRef(), "wasm breakpoint handler");
3120 : markedAny = true;
3121 0 : }
3122 0 : }
3123 0 : break;
3124 : }
3125 : }
3126 : }
3127 : }
3128 : }
3129 : }
3130 : return markedAny;
3131 : }
3132 :
3133 0 : /* static */ void
3134 : Debugger::traceAllForMovingGC(JSTracer* trc)
3135 : {
3136 : JSRuntime* rt = trc->runtime();
3137 0 : for (Debugger* dbg : rt->debuggerList())
3138 : dbg->traceForMovingGC(trc);
3139 4 : }
3140 8 :
3141 0 : /*
3142 4 : * Trace all debugger-owned GC things unconditionally. This is used during
3143 : * compacting GC and in minor GC: the minor GC cannot apply the weak constraints
3144 : * of the full GC because it visits only part of the heap.
3145 : */
3146 : void
3147 : Debugger::traceForMovingGC(JSTracer* trc)
3148 : {
3149 : trace(trc);
3150 0 :
3151 : for (WeakGlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront())
3152 0 : TraceManuallyBarrieredEdge(trc, e.mutableFront().unsafeGet(), "Global Object");
3153 :
3154 0 : for (Breakpoint* bp = firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
3155 0 : switch (bp->site->type()) {
3156 : case BreakpointSite::Type::JS:
3157 0 : TraceManuallyBarrieredEdge(trc, &bp->site->asJS()->script,
3158 0 : "breakpoint script");
3159 : break;
3160 0 : case BreakpointSite::Type::Wasm:
3161 : TraceManuallyBarrieredEdge(trc, &bp->asWasm()->wasmInstance, "breakpoint wasm instance");
3162 : break;
3163 : }
3164 0 : TraceEdge(trc, &bp->getHandlerRef(), "breakpoint handler");
3165 : }
3166 : }
3167 0 :
3168 : /* static */ void
3169 0 : Debugger::traceObject(JSTracer* trc, JSObject* obj)
3170 : {
3171 : if (Debugger* dbg = Debugger::fromJSObject(obj))
3172 0 : dbg->trace(trc);
3173 : }
3174 0 :
3175 0 : void
3176 0 : Debugger::trace(JSTracer* trc)
3177 : {
3178 : TraceEdge(trc, &object, "Debugger Object");
3179 0 :
3180 : TraceNullableEdge(trc, &uncaughtExceptionHook, "hooks");
3181 0 :
3182 : /*
3183 0 : * Mark Debugger.Frame objects. These are all reachable from JS, because the
3184 : * corresponding JS frames are still on the stack.
3185 : *
3186 : * (Once we support generator frames properly, we will need
3187 : * weakly-referenced Debugger.Frame objects as well, for suspended generator
3188 : * frames.)
3189 : */
3190 : if (frames.initialized()) {
3191 : for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
3192 : HeapPtr<DebuggerFrame*>& frameobj = r.front().value();
3193 0 : TraceEdge(trc, &frameobj, "live Debugger.Frame");
3194 0 : MOZ_ASSERT(frameobj->getPrivate(frameobj->numFixedSlotsMaybeForwarded()));
3195 0 : }
3196 0 : }
3197 0 :
3198 : allocationsLog.trace(trc);
3199 :
3200 : /* Trace the weak map from JSScript instances to Debugger.Script objects. */
3201 0 : scripts.trace(trc);
3202 :
3203 : /* Trace the referent -> Debugger.Source weak map */
3204 0 : sources.trace(trc);
3205 :
3206 : /* Trace the referent -> Debugger.Object weak map. */
3207 0 : objects.trace(trc);
3208 :
3209 : /* Trace the referent -> Debugger.Environment weak map. */
3210 0 : environments.trace(trc);
3211 :
3212 : /* Trace the WasmInstanceObject -> synthesized Debugger.Script weak map. */
3213 0 : wasmInstanceScripts.trace(trc);
3214 :
3215 : /* Trace the WasmInstanceObject -> synthesized Debugger.Source weak map. */
3216 0 : wasmInstanceSources.trace(trc);
3217 : }
3218 :
3219 0 : /* static */ void
3220 0 : Debugger::sweepAll(FreeOp* fop)
3221 : {
3222 : JSRuntime* rt = fop->runtime();
3223 0 :
3224 : Debugger* dbg = rt->debuggerList().getFirst();
3225 0 : while (dbg) {
3226 : Debugger* next = dbg->getNext();
3227 0 :
3228 0 : // Detach dying debuggers and debuggees from each other. Since this
3229 0 : // requires access to both objects it must be done before either
3230 : // object is finalized.
3231 : bool debuggerDying = IsAboutToBeFinalized(&dbg->object);
3232 : for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
3233 : GlobalObject* global = e.front().unbarrieredGet();
3234 0 : if (debuggerDying || IsAboutToBeFinalizedUnbarriered(&global))
3235 0 : dbg->removeDebuggeeGlobal(fop, e.front().unbarrieredGet(), &e);
3236 0 : }
3237 0 :
3238 0 : if (debuggerDying)
3239 : fop->delete_(dbg);
3240 :
3241 0 : dbg = next;
3242 0 : }
3243 : }
3244 :
3245 : /* static */ void
3246 0 : Debugger::detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global)
3247 : {
3248 : const GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
3249 0 : MOZ_ASSERT(!debuggers->empty());
3250 : while (!debuggers->empty())
3251 0 : debuggers->back()->removeDebuggeeGlobal(fop, global, nullptr);
3252 0 : }
3253 0 :
3254 0 : /* static */ void
3255 0 : Debugger::findZoneEdges(Zone* zone, js::gc::ZoneComponentFinder& finder)
3256 : {
3257 : JSRuntime* rt = zone->runtimeFromMainThread();
3258 0 : for (Debugger* dbg : rt->debuggerList()) {
3259 : Zone* debuggerZone = dbg->object->zone();
3260 0 : if (!debuggerZone->isGCMarking())
3261 0 : continue;
3262 0 :
3263 0 : if (debuggerZone == zone) {
3264 : /*
3265 : * Add edges to debuggee zones. These are weak references that are
3266 0 : * not in the cross compartment wrapper map.
3267 : */
3268 : for (auto e = dbg->debuggeeZones.all(); !e.empty(); e.popFront()) {
3269 : Zone* debuggeeZone = e.front();
3270 : if (debuggeeZone->isGCMarking())
3271 0 : finder.addEdgeTo(debuggeeZone);
3272 0 : }
3273 0 : } else {
3274 0 : /*
3275 : * For debugger cross compartment wrappers, add edges in the
3276 : * opposite direction to those already added by
3277 : * Compartment::findOutgoingEdges and above. This ensure that
3278 : * debuggers and their debuggees are finalized in the same group.
3279 : */
3280 : if (dbg->debuggeeZones.has(zone) ||
3281 : dbg->scripts.hasKeyInZone(zone) ||
3282 : dbg->sources.hasKeyInZone(zone) ||
3283 0 : dbg->objects.hasKeyInZone(zone) ||
3284 0 : dbg->environments.hasKeyInZone(zone) ||
3285 0 : dbg->wasmInstanceScripts.hasKeyInZone(zone) ||
3286 0 : dbg->wasmInstanceSources.hasKeyInZone(zone))
3287 0 : {
3288 0 : finder.addEdgeTo(debuggerZone);
3289 0 : }
3290 : }
3291 0 : }
3292 : }
3293 :
3294 : const ClassOps Debugger::classOps_ = {
3295 0 : nullptr, /* addProperty */
3296 : nullptr, /* delProperty */
3297 : nullptr, /* enumerate */
3298 : nullptr, /* newEnumerate */
3299 : nullptr, /* resolve */
3300 : nullptr, /* mayResolve */
3301 : nullptr, /* finalize */
3302 : nullptr, /* call */
3303 : nullptr, /* hasInstance */
3304 : nullptr, /* construct */
3305 : Debugger::traceObject
3306 : };
3307 :
3308 : const Class Debugger::class_ = {
3309 : "Debugger",
3310 : JSCLASS_HAS_PRIVATE |
3311 : JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
3312 : &Debugger::classOps_
3313 : };
3314 :
3315 : static Debugger*
3316 : Debugger_fromThisValue(JSContext* cx, const CallArgs& args, const char* fnname)
3317 : {
3318 : JSObject* thisobj = NonNullObject(cx, args.thisv());
3319 0 : if (!thisobj)
3320 : return nullptr;
3321 0 : if (thisobj->getClass() != &Debugger::class_) {
3322 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
3323 : "Debugger", fnname, thisobj->getClass()->name);
3324 0 : return nullptr;
3325 : }
3326 0 :
3327 0 : /*
3328 : * Forbid Debugger.prototype, which is of the Debugger JSClass but isn't
3329 : * really a Debugger object. The prototype object is distinguished by
3330 : * having a nullptr private value.
3331 : */
3332 : Debugger* dbg = Debugger::fromJSObject(thisobj);
3333 : if (!dbg) {
3334 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
3335 0 : "Debugger", fnname, "prototype object");
3336 0 : }
3337 : return dbg;
3338 0 : }
3339 :
3340 : #define THIS_DEBUGGER(cx, argc, vp, fnname, args, dbg) \
3341 : CallArgs args = CallArgsFromVp(argc, vp); \
3342 : Debugger* dbg = Debugger_fromThisValue(cx, args, fnname); \
3343 : if (!dbg) \
3344 : return false
3345 :
3346 : /* static */ bool
3347 : Debugger::getEnabled(JSContext* cx, unsigned argc, Value* vp)
3348 : {
3349 : THIS_DEBUGGER(cx, argc, vp, "get enabled", args, dbg);
3350 0 : args.rval().setBoolean(dbg->enabled);
3351 : return true;
3352 0 : }
3353 0 :
3354 0 : /* static */ bool
3355 : Debugger::setEnabled(JSContext* cx, unsigned argc, Value* vp)
3356 : {
3357 : THIS_DEBUGGER(cx, argc, vp, "set enabled", args, dbg);
3358 0 : if (!args.requireAtLeast(cx, "Debugger.set enabled", 1))
3359 : return false;
3360 0 :
3361 0 : bool wasEnabled = dbg->enabled;
3362 : dbg->enabled = ToBoolean(args[0]);
3363 :
3364 0 : if (wasEnabled != dbg->enabled) {
3365 0 : if (dbg->trackingAllocationSites) {
3366 : if (wasEnabled) {
3367 0 : dbg->removeAllocationsTrackingForAllDebuggees();
3368 0 : } else {
3369 0 : if (!dbg->addAllocationsTrackingForAllDebuggees(cx)) {
3370 0 : dbg->enabled = false;
3371 : return false;
3372 0 : }
3373 0 : }
3374 0 : }
3375 :
3376 : for (Breakpoint* bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
3377 : if (!wasEnabled)
3378 : bp->site->inc(cx->runtime()->defaultFreeOp());
3379 0 : else
3380 0 : bp->site->dec(cx->runtime()->defaultFreeOp());
3381 0 : }
3382 :
3383 0 : /*
3384 : * Add or remove ourselves from the runtime's list of Debuggers
3385 : * that care about new globals.
3386 : */
3387 : if (dbg->getHook(OnNewGlobalObject)) {
3388 : if (!wasEnabled) {
3389 : cx->runtime()->onNewGlobalObjectWatchers().pushBack(dbg);
3390 0 : } else {
3391 0 : cx->runtime()->onNewGlobalObjectWatchers().remove(dbg);
3392 0 : }
3393 : }
3394 0 :
3395 : // Ensure the compartment is observable if we are re-enabling a
3396 : // Debugger with hooks that observe all execution.
3397 : if (!dbg->updateObservesAllExecutionOnDebuggees(cx, dbg->observesAllExecution()))
3398 : return false;
3399 :
3400 0 : // Note: To toogle code coverage, we currently need to have no live
3401 : // stack frame, thus the coverage does not depend on the enabled flag.
3402 :
3403 : dbg->updateObservesAsmJSOnDebuggees(dbg->observesAsmJS());
3404 : dbg->updateObservesBinarySourceDebuggees(dbg->observesBinarySource());
3405 : }
3406 0 :
3407 0 : args.rval().setUndefined();
3408 : return true;
3409 : }
3410 0 :
3411 0 : /* static */ bool
3412 : Debugger::getHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which)
3413 : {
3414 : MOZ_ASSERT(which >= 0 && which < HookCount);
3415 0 : args.rval().set(dbg.object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + which));
3416 : return true;
3417 0 : }
3418 0 :
3419 0 : /* static */ bool
3420 : Debugger::setHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which)
3421 : {
3422 : MOZ_ASSERT(which >= 0 && which < HookCount);
3423 0 : if (!args.requireAtLeast(cx, "Debugger.setHook", 1))
3424 : return false;
3425 0 : if (args[0].isObject()) {
3426 0 : if (!args[0].toObject().isCallable())
3427 : return ReportIsNotFunction(cx, args[0], args.length() - 1);
3428 0 : } else if (!args[0].isUndefined()) {
3429 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
3430 0 : return false;
3431 0 : }
3432 0 : uint32_t slot = JSSLOT_DEBUG_HOOK_START + which;
3433 0 : RootedValue oldHook(cx, dbg.object->getReservedSlot(slot));
3434 : dbg.object->setReservedSlot(slot, args[0]);
3435 0 : if (hookObservesAllExecution(which)) {
3436 0 : if (!dbg.updateObservesAllExecutionOnDebuggees(cx, dbg.observesAllExecution())) {
3437 0 : dbg.object->setReservedSlot(slot, oldHook);
3438 0 : return false;
3439 0 : }
3440 0 : }
3441 0 : args.rval().setUndefined();
3442 : return true;
3443 : }
3444 0 :
3445 0 : /* static */ bool
3446 : Debugger::getOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp)
3447 : {
3448 : THIS_DEBUGGER(cx, argc, vp, "(get onDebuggerStatement)", args, dbg);
3449 0 : return getHookImpl(cx, args, *dbg, OnDebuggerStatement);
3450 : }
3451 0 :
3452 0 : /* static */ bool
3453 : Debugger::setOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp)
3454 : {
3455 : THIS_DEBUGGER(cx, argc, vp, "(set onDebuggerStatement)", args, dbg);
3456 0 : return setHookImpl(cx, args, *dbg, OnDebuggerStatement);
3457 : }
3458 0 :
3459 0 : /* static */ bool
3460 : Debugger::getOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp)
3461 : {
3462 : THIS_DEBUGGER(cx, argc, vp, "(get onExceptionUnwind)", args, dbg);
3463 0 : return getHookImpl(cx, args, *dbg, OnExceptionUnwind);
3464 : }
3465 0 :
3466 0 : /* static */ bool
3467 : Debugger::setOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp)
3468 : {
3469 : THIS_DEBUGGER(cx, argc, vp, "(set onExceptionUnwind)", args, dbg);
3470 0 : return setHookImpl(cx, args, *dbg, OnExceptionUnwind);
3471 : }
3472 0 :
3473 0 : /* static */ bool
3474 : Debugger::getOnNewScript(JSContext* cx, unsigned argc, Value* vp)
3475 : {
3476 : THIS_DEBUGGER(cx, argc, vp, "(get onNewScript)", args, dbg);
3477 0 : return getHookImpl(cx, args, *dbg, OnNewScript);
3478 : }
3479 0 :
3480 0 : /* static */ bool
3481 : Debugger::setOnNewScript(JSContext* cx, unsigned argc, Value* vp)
3482 : {
3483 : THIS_DEBUGGER(cx, argc, vp, "(set onNewScript)", args, dbg);
3484 0 : return setHookImpl(cx, args, *dbg, OnNewScript);
3485 : }
3486 0 :
3487 0 : /* static */ bool
3488 : Debugger::getOnNewPromise(JSContext* cx, unsigned argc, Value* vp)
3489 : {
3490 : THIS_DEBUGGER(cx, argc, vp, "(get onNewPromise)", args, dbg);
3491 0 : return getHookImpl(cx, args, *dbg, OnNewPromise);
3492 : }
3493 0 :
3494 0 : /* static */ bool
3495 : Debugger::setOnNewPromise(JSContext* cx, unsigned argc, Value* vp)
3496 : {
3497 : THIS_DEBUGGER(cx, argc, vp, "(set onNewPromise)", args, dbg);
3498 0 : return setHookImpl(cx, args, *dbg, OnNewPromise);
3499 : }
3500 0 :
3501 0 : /* static */ bool
3502 : Debugger::getOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp)
3503 : {
3504 : THIS_DEBUGGER(cx, argc, vp, "(get onPromiseSettled)", args, dbg);
3505 0 : return getHookImpl(cx, args, *dbg, OnPromiseSettled);
3506 : }
3507 0 :
3508 0 : /* static */ bool
3509 : Debugger::setOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp)
3510 : {
3511 : THIS_DEBUGGER(cx, argc, vp, "(set onPromiseSettled)", args, dbg);
3512 0 : return setHookImpl(cx, args, *dbg, OnPromiseSettled);
3513 : }
3514 0 :
3515 0 : /* static */ bool
3516 : Debugger::getOnEnterFrame(JSContext* cx, unsigned argc, Value* vp)
3517 : {
3518 : THIS_DEBUGGER(cx, argc, vp, "(get onEnterFrame)", args, dbg);
3519 0 : return getHookImpl(cx, args, *dbg, OnEnterFrame);
3520 : }
3521 0 :
3522 0 : /* static */ bool
3523 : Debugger::setOnEnterFrame(JSContext* cx, unsigned argc, Value* vp)
3524 : {
3525 : THIS_DEBUGGER(cx, argc, vp, "(set onEnterFrame)", args, dbg);
3526 0 : return setHookImpl(cx, args, *dbg, OnEnterFrame);
3527 : }
3528 0 :
3529 0 : /* static */ bool
3530 : Debugger::getOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp)
3531 : {
3532 : THIS_DEBUGGER(cx, argc, vp, "(get onNewGlobalObject)", args, dbg);
3533 0 : return getHookImpl(cx, args, *dbg, OnNewGlobalObject);
3534 : }
3535 0 :
3536 0 : /* static */ bool
3537 : Debugger::setOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp)
3538 : {
3539 : THIS_DEBUGGER(cx, argc, vp, "setOnNewGlobalObject", args, dbg);
3540 0 : RootedObject oldHook(cx, dbg->getHook(OnNewGlobalObject));
3541 :
3542 0 : if (!setHookImpl(cx, args, *dbg, OnNewGlobalObject))
3543 0 : return false;
3544 :
3545 0 : /*
3546 : * Add or remove ourselves from the runtime's list of Debuggers that
3547 : * care about new globals.
3548 : */
3549 : if (dbg->enabled) {
3550 : JSObject* newHook = dbg->getHook(OnNewGlobalObject);
3551 : if (!oldHook && newHook) {
3552 0 : cx->runtime()->onNewGlobalObjectWatchers().pushBack(dbg);
3553 0 : } else if (oldHook && !newHook) {
3554 0 : cx->runtime()->onNewGlobalObjectWatchers().remove(dbg);
3555 0 : }
3556 0 : }
3557 0 :
3558 : return true;
3559 : }
3560 :
3561 : /* static */ bool
3562 : Debugger::getUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp)
3563 : {
3564 : THIS_DEBUGGER(cx, argc, vp, "get uncaughtExceptionHook", args, dbg);
3565 0 : args.rval().setObjectOrNull(dbg->uncaughtExceptionHook);
3566 : return true;
3567 0 : }
3568 0 :
3569 : /* static */ bool
3570 : Debugger::setUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp)
3571 : {
3572 : THIS_DEBUGGER(cx, argc, vp, "set uncaughtExceptionHook", args, dbg);
3573 0 : if (!args.requireAtLeast(cx, "Debugger.set uncaughtExceptionHook", 1))
3574 : return false;
3575 0 : if (!args[0].isNull() && (!args[0].isObject() || !args[0].toObject().isCallable())) {
3576 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ASSIGN_FUNCTION_OR_NULL,
3577 : "uncaughtExceptionHook");
3578 0 : return false;
3579 : }
3580 0 : dbg->uncaughtExceptionHook = args[0].toObjectOrNull();
3581 0 : args.rval().setUndefined();
3582 : return true;
3583 0 : }
3584 0 :
3585 0 : /* static */ bool
3586 : Debugger::getAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp)
3587 : {
3588 : THIS_DEBUGGER(cx, argc, vp, "get allowUnobservedAsmJS", args, dbg);
3589 0 : args.rval().setBoolean(dbg->allowUnobservedAsmJS);
3590 : return true;
3591 0 : }
3592 0 :
3593 0 : /* static */ bool
3594 : Debugger::setAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp)
3595 : {
3596 : THIS_DEBUGGER(cx, argc, vp, "set allowUnobservedAsmJS", args, dbg);
3597 0 : if (!args.requireAtLeast(cx, "Debugger.set allowUnobservedAsmJS", 1))
3598 : return false;
3599 0 : dbg->allowUnobservedAsmJS = ToBoolean(args[0]);
3600 0 :
3601 : for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
3602 0 : GlobalObject* global = r.front();
3603 : Realm* realm = global->realm();
3604 0 : realm->updateDebuggerObservesAsmJS();
3605 0 : }
3606 0 :
3607 0 : args.rval().setUndefined();
3608 : return true;
3609 : }
3610 0 :
3611 0 : /* static */ bool
3612 : Debugger::getAllowWasmBinarySource(JSContext* cx, unsigned argc, Value* vp)
3613 : {
3614 : THIS_DEBUGGER(cx, argc, vp, "get allowWasmBinarySource", args, dbg);
3615 0 : args.rval().setBoolean(dbg->allowWasmBinarySource);
3616 : return true;
3617 0 : }
3618 0 :
3619 0 : /* static */ bool
3620 : Debugger::setAllowWasmBinarySource(JSContext* cx, unsigned argc, Value* vp)
3621 : {
3622 : THIS_DEBUGGER(cx, argc, vp, "set allowWasmBinarySource", args, dbg);
3623 0 : if (!args.requireAtLeast(cx, "Debugger.set allowWasmBinarySource", 1))
3624 : return false;
3625 0 : dbg->allowWasmBinarySource = ToBoolean(args[0]);
3626 0 :
3627 : for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
3628 0 : GlobalObject* global = r.front();
3629 : Realm* realm = global->realm();
3630 0 : realm->updateDebuggerObservesBinarySource();
3631 0 : }
3632 0 :
3633 0 : args.rval().setUndefined();
3634 : return true;
3635 : }
3636 0 :
3637 0 : /* static */ bool
3638 : Debugger::getCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp)
3639 : {
3640 : THIS_DEBUGGER(cx, argc, vp, "get collectCoverageInfo", args, dbg);
3641 0 : args.rval().setBoolean(dbg->collectCoverageInfo);
3642 : return true;
3643 0 : }
3644 0 :
3645 0 : /* static */ bool
3646 : Debugger::setCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp)
3647 : {
3648 : THIS_DEBUGGER(cx, argc, vp, "set collectCoverageInfo", args, dbg);
3649 0 : if (!args.requireAtLeast(cx, "Debugger.set collectCoverageInfo", 1))
3650 : return false;
3651 0 : dbg->collectCoverageInfo = ToBoolean(args[0]);
3652 0 :
3653 : IsObserving observing = dbg->collectCoverageInfo ? Observing : NotObserving;
3654 0 : if (!dbg->updateObservesCoverageOnDebuggees(cx, observing))
3655 : return false;
3656 0 :
3657 0 : args.rval().setUndefined();
3658 : return true;
3659 : }
3660 0 :
3661 0 : /* static */ bool
3662 : Debugger::getMemory(JSContext* cx, unsigned argc, Value* vp)
3663 : {
3664 : THIS_DEBUGGER(cx, argc, vp, "get memory", args, dbg);
3665 0 : Value memoryValue = dbg->object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE);
3666 :
3667 0 : if (!memoryValue.isObject()) {
3668 0 : RootedObject memory(cx, DebuggerMemory::create(cx, dbg));
3669 : if (!memory)
3670 0 : return false;
3671 0 : memoryValue = ObjectValue(*memory);
3672 0 : }
3673 0 :
3674 0 : args.rval().set(memoryValue);
3675 : return true;
3676 : }
3677 0 :
3678 0 : /*
3679 : * Given a value used to designate a global (there's quite a variety; see the
3680 : * docs), return the actual designee.
3681 : *
3682 : * Note that this does not check whether the designee is marked "invisible to
3683 : * Debugger" or not; different callers need to handle invisible-to-Debugger
3684 : * globals in different ways.
3685 : */
3686 : GlobalObject*
3687 : Debugger::unwrapDebuggeeArgument(JSContext* cx, const Value& v)
3688 : {
3689 : if (!v.isObject()) {
3690 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
3691 : "argument", "not a global object");
3692 0 : return nullptr;
3693 : }
3694 0 :
3695 0 : RootedObject obj(cx, &v.toObject());
3696 :
3697 : /* If it's a Debugger.Object belonging to this debugger, dereference that. */
3698 0 : if (obj->getClass() == &DebuggerObject::class_) {
3699 : RootedValue rv(cx, v);
3700 : if (!unwrapDebuggeeValue(cx, &rv))
3701 0 : return nullptr;
3702 0 : obj = &rv.toObject();
3703 0 : }
3704 0 :
3705 0 : /* If we have a cross-compartment wrapper, dereference as far as is secure. */
3706 : obj = CheckedUnwrap(obj);
3707 : if (!obj) {
3708 : ReportAccessDenied(cx);
3709 0 : return nullptr;
3710 0 : }
3711 0 :
3712 0 : /* If that produced a WindowProxy, get the Window (global). */
3713 : obj = ToWindowIfWindowProxy(obj);
3714 :
3715 : /* If that didn't produce a global object, it's an error. */
3716 0 : if (!obj->is<GlobalObject>()) {
3717 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
3718 : "argument", "not a global object");
3719 0 : return nullptr;
3720 : }
3721 0 :
3722 0 : return &obj->as<GlobalObject>();
3723 : }
3724 :
3725 0 : /* static */ bool
3726 : Debugger::addDebuggee(JSContext* cx, unsigned argc, Value* vp)
3727 : {
3728 : THIS_DEBUGGER(cx, argc, vp, "addDebuggee", args, dbg);
3729 0 : if (!args.requireAtLeast(cx, "Debugger.addDebuggee", 1))
3730 : return false;
3731 0 : Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
3732 0 : if (!global)
3733 : return false;
3734 0 :
3735 0 : if (!dbg->addDebuggeeGlobal(cx, global))
3736 : return false;
3737 :
3738 0 : RootedValue v(cx, ObjectValue(*global));
3739 : if (!dbg->wrapDebuggeeValue(cx, &v))
3740 : return false;
3741 0 : args.rval().set(v);
3742 0 : return true;
3743 : }
3744 0 :
3745 0 : /* static */ bool
3746 : Debugger::addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp)
3747 : {
3748 : THIS_DEBUGGER(cx, argc, vp, "addAllGlobalsAsDebuggees", args, dbg);
3749 0 : for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
3750 : for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
3751 0 : if (r == dbg->object->realm() || r->creationOptions().invisibleToDebugger())
3752 0 : continue;
3753 0 : r->compartment()->gcState.scheduledForDestruction = false;
3754 0 : GlobalObject* global = r->maybeGlobal();
3755 0 : if (global) {
3756 0 : Rooted<GlobalObject*> rg(cx, global);
3757 0 : if (!dbg->addDebuggeeGlobal(cx, rg))
3758 0 : return false;
3759 0 : }
3760 0 : }
3761 0 : }
3762 :
3763 : args.rval().setUndefined();
3764 : return true;
3765 : }
3766 0 :
3767 0 : /* static */ bool
3768 : Debugger::removeDebuggee(JSContext* cx, unsigned argc, Value* vp)
3769 : {
3770 : THIS_DEBUGGER(cx, argc, vp, "removeDebuggee", args, dbg);
3771 0 :
3772 : if (!args.requireAtLeast(cx, "Debugger.removeDebuggee", 1))
3773 0 : return false;
3774 : Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
3775 0 : if (!global)
3776 : return false;
3777 0 :
3778 0 : ExecutionObservableRealms obs(cx);
3779 : if (!obs.init())
3780 : return false;
3781 0 :
3782 0 : if (dbg->debuggees.has(global)) {
3783 : dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, nullptr);
3784 :
3785 0 : // Only update the realm if there are no Debuggers left, as it's
3786 0 : // expensive to check if no other Debugger has a live script or frame
3787 : // hook on any of the current on-stack debuggee frames.
3788 : if (global->getDebuggers()->empty() && !obs.add(global->realm()))
3789 : return false;
3790 : if (!updateExecutionObservability(cx, obs, NotObserving))
3791 0 : return false;
3792 : }
3793 0 :
3794 : args.rval().setUndefined();
3795 : return true;
3796 : }
3797 0 :
3798 0 : /* static */ bool
3799 : Debugger::removeAllDebuggees(JSContext* cx, unsigned argc, Value* vp)
3800 : {
3801 : THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg);
3802 0 :
3803 : ExecutionObservableRealms obs(cx);
3804 0 : if (!obs.init())
3805 : return false;
3806 0 :
3807 0 : for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
3808 : Rooted<GlobalObject*> global(cx, e.front());
3809 : dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, &e);
3810 0 :
3811 0 : // See note about adding to the observable set in removeDebuggee.
3812 0 : if (global->getDebuggers()->empty() && !obs.add(global->realm()))
3813 : return false;
3814 : }
3815 0 :
3816 0 : if (!updateExecutionObservability(cx, obs, NotObserving))
3817 : return false;
3818 :
3819 0 : args.rval().setUndefined();
3820 : return true;
3821 : }
3822 0 :
3823 0 : /* static */ bool
3824 : Debugger::hasDebuggee(JSContext* cx, unsigned argc, Value* vp)
3825 : {
3826 : THIS_DEBUGGER(cx, argc, vp, "hasDebuggee", args, dbg);
3827 0 : if (!args.requireAtLeast(cx, "Debugger.hasDebuggee", 1))
3828 : return false;
3829 0 : GlobalObject* global = dbg->unwrapDebuggeeArgument(cx, args[0]);
3830 0 : if (!global)
3831 : return false;
3832 0 : args.rval().setBoolean(!!dbg->debuggees.lookup(global));
3833 0 : return true;
3834 : }
3835 0 :
3836 0 : /* static */ bool
3837 : Debugger::getDebuggees(JSContext* cx, unsigned argc, Value* vp)
3838 : {
3839 : THIS_DEBUGGER(cx, argc, vp, "getDebuggees", args, dbg);
3840 0 :
3841 : // Obtain the list of debuggees before wrapping each debuggee, as a GC could
3842 0 : // update the debuggees set while we are iterating it.
3843 : unsigned count = dbg->debuggees.count();
3844 : AutoValueVector debuggees(cx);
3845 : if (!debuggees.resize(count))
3846 0 : return false;
3847 0 : unsigned i = 0;
3848 0 : {
3849 : JS::AutoCheckCannotGC nogc;
3850 0 : for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
3851 : debuggees[i++].setObject(*e.front().get());
3852 0 : }
3853 0 :
3854 0 : RootedArrayObject arrobj(cx, NewDenseFullyAllocatedArray(cx, count));
3855 : if (!arrobj)
3856 : return false;
3857 0 : arrobj->ensureDenseInitializedLength(cx, 0, count);
3858 0 : for (i = 0; i < count; i++) {
3859 : RootedValue v(cx, debuggees[i]);
3860 0 : if (!dbg->wrapDebuggeeValue(cx, &v))
3861 0 : return false;
3862 0 : arrobj->setDenseElement(i, v);
3863 0 : }
3864 0 :
3865 0 : args.rval().setObject(*arrobj);
3866 : return true;
3867 : }
3868 0 :
3869 0 : /* static */ bool
3870 : Debugger::getNewestFrame(JSContext* cx, unsigned argc, Value* vp)
3871 : {
3872 : THIS_DEBUGGER(cx, argc, vp, "getNewestFrame", args, dbg);
3873 0 :
3874 : /* Since there may be multiple contexts, use AllFramesIter. */
3875 0 : for (AllFramesIter i(cx); !i.done(); ++i) {
3876 : if (dbg->observesFrame(i)) {
3877 : // Ensure that Ion frames are rematerialized. Only rematerialized
3878 0 : // Ion frames may be used as AbstractFramePtrs.
3879 0 : if (i.isIon() && !i.ensureHasRematerializedFrame(cx))
3880 : return false;
3881 : AbstractFramePtr frame = i.abstractFramePtr();
3882 0 : FrameIter iter(i.activation()->cx());
3883 : while (!iter.hasUsableAbstractFramePtr() || iter.abstractFramePtr() != frame)
3884 0 : ++iter;
3885 0 : return dbg->getFrame(cx, iter, args.rval());
3886 0 : }
3887 0 : }
3888 0 : args.rval().setNull();
3889 : return true;
3890 : }
3891 0 :
3892 0 : /* static */ bool
3893 : Debugger::clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp)
3894 : {
3895 : THIS_DEBUGGER(cx, argc, vp, "clearAllBreakpoints", args, dbg);
3896 0 : for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront())
3897 : r.front()->realm()->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), dbg, nullptr);
3898 0 : return true;
3899 0 : }
3900 0 :
3901 0 : /* static */ bool
3902 : Debugger::construct(JSContext* cx, unsigned argc, Value* vp)
3903 : {
3904 : CallArgs args = CallArgsFromVp(argc, vp);
3905 0 :
3906 : /* Check that the arguments, if any, are cross-compartment wrappers. */
3907 0 : for (unsigned i = 0; i < args.length(); i++) {
3908 : JSObject* argobj = NonNullObject(cx, args[i]);
3909 : if (!argobj)
3910 0 : return false;
3911 0 : if (!argobj->is<CrossCompartmentWrapperObject>()) {
3912 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_CCW_REQUIRED,
3913 : "Debugger");
3914 0 : return false;
3915 : }
3916 0 : }
3917 0 :
3918 : /* Get Debugger.prototype. */
3919 : RootedValue v(cx);
3920 : RootedObject callee(cx, &args.callee());
3921 : if (!GetProperty(cx, callee, callee, cx->names().prototype, &v))
3922 0 : return false;
3923 0 : RootedNativeObject proto(cx, &v.toObject().as<NativeObject>());
3924 0 : MOZ_ASSERT(proto->getClass() == &Debugger::class_);
3925 : /*
3926 0 : * Make the new Debugger object. Each one has a reference to
3927 0 : * Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The
3928 : * rest of the reserved slots are for hooks; they default to undefined.
3929 : */
3930 : RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &Debugger::class_, proto,
3931 : TenuredObject));
3932 : if (!obj)
3933 0 : return false;
3934 0 : for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++)
3935 0 : obj->setReservedSlot(slot, proto->getReservedSlot(slot));
3936 : obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue());
3937 0 :
3938 0 : Debugger* debugger;
3939 0 : {
3940 : /* Construct the underlying C++ object. */
3941 : auto dbg = cx->make_unique<Debugger>(cx, obj.get());
3942 : if (!dbg || !dbg->init(cx))
3943 : return false;
3944 0 :
3945 0 : debugger = dbg.release();
3946 0 : obj->setPrivate(debugger); // owns the released pointer
3947 : }
3948 0 :
3949 0 : /* Add the initial debuggees, if any. */
3950 : for (unsigned i = 0; i < args.length(); i++) {
3951 : JSObject& wrappedObj = args[i].toObject().as<ProxyObject>().private_().toObject();
3952 : Rooted<GlobalObject*> debuggee(cx, &wrappedObj.deprecatedGlobal());
3953 0 : if (!debugger->addDebuggeeGlobal(cx, debuggee))
3954 : return false;
3955 0 : }
3956 0 :
3957 0 : args.rval().setObject(*obj);
3958 : return true;
3959 : }
3960 0 :
3961 0 : bool
3962 : Debugger::addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> global)
3963 : {
3964 : if (debuggees.has(global))
3965 0 : return true;
3966 :
3967 0 : // Callers should generally be unable to get a reference to a debugger-
3968 : // invisible global in order to pass it to addDebuggee. But this is possible
3969 : // with certain testing aides we expose in the shell, so just make addDebuggee
3970 : // throw in that case.
3971 : Realm* debuggeeRealm = global->realm();
3972 : if (debuggeeRealm->creationOptions().invisibleToDebugger()) {
3973 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_CANT_DEBUG_GLOBAL);
3974 0 : return false;
3975 0 : }
3976 0 :
3977 0 : /*
3978 : * Check for cycles. If global's realm is reachable from this
3979 : * Debugger object's realm by following debuggee-to-debugger links,
3980 : * then adding global would create a cycle. (Typically nobody is debugging
3981 : * the debugger, in which case we zip through this code without looping.)
3982 : */
3983 : Vector<Realm*> visited(cx);
3984 : if (!visited.append(object->realm()))
3985 : return false;
3986 0 : for (size_t i = 0; i < visited.length(); i++) {
3987 0 : Realm* realm = visited[i];
3988 : if (realm == debuggeeRealm) {
3989 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_LOOP);
3990 0 : return false;
3991 0 : }
3992 0 :
3993 0 : /*
3994 : * Find all realms containing debuggers debugging realm's global object.
3995 : * Add those realms to visited.
3996 : */
3997 : if (realm->isDebuggee()) {
3998 : GlobalObject::DebuggerVector* v = realm->maybeGlobal()->getDebuggers();
3999 : for (auto p = v->begin(); p != v->end(); p++) {
4000 0 : Realm* next = (*p)->object->realm();
4001 0 : if (Find(visited, next) == visited.end() && !visited.append(next))
4002 0 : return false;
4003 0 : }
4004 0 : }
4005 0 : }
4006 :
4007 : /*
4008 : * For global to become this js::Debugger's debuggee:
4009 : *
4010 : * 1. this js::Debugger must be in global->getDebuggers(),
4011 : * 2. global must be in this->debuggees,
4012 : * 3. it must be in zone->getDebuggers(),
4013 : * 4. the debuggee's zone must be in this->debuggeeZones,
4014 : * 5. if we are tracking allocations, the SavedStacksMetadataBuilder must be
4015 : * installed for this realm, and
4016 : * 6. Realm::isDebuggee()'s bit must be set.
4017 : *
4018 : * All six indications must be kept consistent.
4019 : */
4020 :
4021 : AutoRealm ar(cx, global);
4022 : Zone* zone = global->zone();
4023 :
4024 0 : // (1)
4025 0 : auto* globalDebuggers = GlobalObject::getOrCreateDebuggers(cx, global);
4026 : if (!globalDebuggers)
4027 : return false;
4028 0 : if (!globalDebuggers->append(this)) {
4029 0 : ReportOutOfMemory(cx);
4030 : return false;
4031 0 : }
4032 0 : auto globalDebuggersGuard = MakeScopeExit([&] {
4033 0 : globalDebuggers->popBack();
4034 : });
4035 :
4036 0 : // (2)
4037 0 : if (!debuggees.put(global)) {
4038 : ReportOutOfMemory(cx);
4039 : return false;
4040 0 : }
4041 0 : auto debuggeesGuard = MakeScopeExit([&] {
4042 0 : debuggees.remove(global);
4043 : });
4044 :
4045 0 : bool addingZoneRelation = !debuggeeZones.has(zone);
4046 0 :
4047 : // (3)
4048 0 : auto* zoneDebuggers = zone->getOrCreateDebuggers(cx);
4049 : if (!zoneDebuggers)
4050 : return false;
4051 0 : if (addingZoneRelation && !zoneDebuggers->append(this)) {
4052 0 : ReportOutOfMemory(cx);
4053 : return false;
4054 0 : }
4055 0 : auto zoneDebuggersGuard = MakeScopeExit([&] {
4056 0 : if (addingZoneRelation)
4057 : zoneDebuggers->popBack();
4058 : });
4059 0 :
4060 0 : // (4)
4061 0 : if (addingZoneRelation && !debuggeeZones.put(zone)) {
4062 : ReportOutOfMemory(cx);
4063 : return false;
4064 0 : }
4065 0 : auto debuggeeZonesGuard = MakeScopeExit([&] {
4066 0 : if (addingZoneRelation)
4067 : debuggeeZones.remove(zone);
4068 : });
4069 0 :
4070 0 : // (5)
4071 0 : if (trackingAllocationSites && enabled && !Debugger::addAllocationsTracking(cx, global))
4072 : return false;
4073 :
4074 0 : auto allocationsTrackingGuard = MakeScopeExit([&] {
4075 : if (trackingAllocationSites && enabled)
4076 : Debugger::removeAllocationsTracking(*global);
4077 0 : });
4078 0 :
4079 0 : // (6)
4080 0 : AutoRestoreRealmDebugMode debugModeGuard(debuggeeRealm);
4081 : debuggeeRealm->setIsDebuggee();
4082 : debuggeeRealm->updateDebuggerObservesAsmJS();
4083 0 : debuggeeRealm->updateDebuggerObservesBinarySource();
4084 0 : debuggeeRealm->updateDebuggerObservesCoverage();
4085 0 : if (observesAllExecution() && !ensureExecutionObservabilityOfRealm(cx, debuggeeRealm))
4086 0 : return false;
4087 0 :
4088 0 : globalDebuggersGuard.release();
4089 : debuggeesGuard.release();
4090 : zoneDebuggersGuard.release();
4091 0 : debuggeeZonesGuard.release();
4092 0 : allocationsTrackingGuard.release();
4093 0 : debugModeGuard.release();
4094 0 : return true;
4095 0 : }
4096 0 :
4097 0 : void
4098 : Debugger::recomputeDebuggeeZoneSet()
4099 : {
4100 : AutoEnterOOMUnsafeRegion oomUnsafe;
4101 0 : debuggeeZones.clear();
4102 : for (auto range = debuggees.all(); !range.empty(); range.popFront()) {
4103 0 : if (!debuggeeZones.put(range.front().unbarrieredGet()->zone()))
4104 0 : oomUnsafe.crash("Debugger::removeDebuggeeGlobal");
4105 0 : }
4106 0 : }
4107 0 :
4108 : template <typename T>
4109 0 : static T*
4110 : findDebuggerInVector(Debugger* dbg, Vector<T, 0, js::SystemAllocPolicy>* vec)
4111 : {
4112 : T* p;
4113 0 : for (p = vec->begin(); p != vec->end(); p++) {
4114 : if (*p == dbg)
4115 : break;
4116 0 : }
4117 0 : MOZ_ASSERT(p != vec->end());
4118 : return p;
4119 : }
4120 0 :
4121 0 : void
4122 : Debugger::removeDebuggeeGlobal(FreeOp* fop, GlobalObject* global,
4123 : WeakGlobalObjectSet::Enum* debugEnum)
4124 : {
4125 0 : /*
4126 : * The caller might have found global by enumerating this->debuggees; if
4127 : * so, use HashSet::Enum::removeFront rather than HashSet::remove below,
4128 : * to avoid invalidating the live enumerator.
4129 : */
4130 : MOZ_ASSERT(debuggees.has(global));
4131 : MOZ_ASSERT(debuggeeZones.has(global->zone()));
4132 : MOZ_ASSERT_IF(debugEnum, debugEnum->front().unbarrieredGet() == global);
4133 0 :
4134 0 : /*
4135 0 : * FIXME Debugger::slowPathOnLeaveFrame needs to kill all Debugger.Frame
4136 : * objects referring to a particular JS stack frame. This is hard if
4137 : * Debugger objects that are no longer debugging the relevant global might
4138 : * have live Frame objects. So we take the easy way out and kill them here.
4139 : * This is a bug, since it's observable and contrary to the spec. One
4140 : * possible fix would be to put such objects into a compartment-wide bag
4141 : * which slowPathOnLeaveFrame would have to examine.
4142 : */
4143 : for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) {
4144 : AbstractFramePtr frame = e.front().key();
4145 : DebuggerFrame* frameobj = e.front().value();
4146 0 : if (frame.global() == global) {
4147 0 : frameobj->freeFrameIterData(fop);
4148 0 : DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj);
4149 0 : e.removeFront();
4150 0 : }
4151 0 : }
4152 :
4153 : auto *globalDebuggersVector = global->getDebuggers();
4154 : auto *zoneDebuggersVector = global->zone()->getDebuggers();
4155 :
4156 0 : /*
4157 0 : * The relation must be removed from up to three places:
4158 : * globalDebuggersVector and debuggees for sure, and possibly the
4159 : * compartment's debuggee set.
4160 : *
4161 : * The debuggee zone set is recomputed on demand. This avoids refcounting
4162 : * and in practice we have relatively few debuggees that tend to all be in
4163 : * the same zone. If after recomputing the debuggee zone set, this global's
4164 : * zone is not in the set, then we must remove ourselves from the zone's
4165 : * vector of observing debuggers.
4166 : */
4167 : globalDebuggersVector->erase(findDebuggerInVector(this, globalDebuggersVector));
4168 :
4169 : if (debugEnum)
4170 0 : debugEnum->removeFront();
4171 : else
4172 0 : debuggees.remove(global);
4173 :
4174 : recomputeDebuggeeZoneSet();
4175 0 :
4176 : if (!debuggeeZones.has(global->zone()))
4177 0 : zoneDebuggersVector->erase(findDebuggerInVector(this, zoneDebuggersVector));
4178 :
4179 0 : /* Remove all breakpoints for the debuggee. */
4180 0 : Breakpoint* nextbp;
4181 : for (Breakpoint* bp = firstBreakpoint(); bp; bp = nextbp) {
4182 : nextbp = bp->nextInDebugger();
4183 : switch (bp->site->type()) {
4184 0 : case BreakpointSite::Type::JS:
4185 0 : if (bp->site->asJS()->script->realm() == global->realm())
4186 0 : bp->destroy(fop);
4187 : break;
4188 0 : case BreakpointSite::Type::Wasm:
4189 0 : if (bp->asWasm()->wasmInstance->realm() == global->realm())
4190 : bp->destroy(fop);
4191 : break;
4192 0 : }
4193 0 : }
4194 : MOZ_ASSERT_IF(debuggees.empty(), !firstBreakpoint());
4195 :
4196 : /*
4197 0 : * If we are tracking allocation sites, we need to remove the object
4198 : * metadata callback from this global's realm.
4199 : */
4200 : if (trackingAllocationSites)
4201 : Debugger::removeAllocationsTracking(*global);
4202 :
4203 0 : if (global->getDebuggers()->empty()) {
4204 0 : global->realm()->unsetIsDebuggee();
4205 : } else {
4206 0 : global->realm()->updateDebuggerObservesAllExecution();
4207 0 : global->realm()->updateDebuggerObservesAsmJS();
4208 : global->realm()->updateDebuggerObservesBinarySource();
4209 0 : global->realm()->updateDebuggerObservesCoverage();
4210 0 : }
4211 0 : }
4212 0 :
4213 :
4214 0 : static inline DebuggerSourceReferent GetSourceReferent(JSObject* obj);
4215 :
4216 : /*
4217 : * A class for parsing 'findScripts' query arguments and searching for
4218 : * scripts that match the criteria they represent.
4219 : */
4220 : class MOZ_STACK_CLASS Debugger::ScriptQuery
4221 : {
4222 : public:
4223 0 : /* Construct a ScriptQuery to use matching scripts for |dbg|. */
4224 : ScriptQuery(JSContext* cx, Debugger* dbg):
4225 : cx(cx),
4226 : debugger(dbg),
4227 0 : iterMarker(&cx->runtime()->gc),
4228 : realms(cx->zone()),
4229 : url(cx),
4230 0 : displayURLString(cx),
4231 : hasSource(false),
4232 : source(cx, AsVariant(static_cast<ScriptSourceObject*>(nullptr))),
4233 : innermostForRealm(cx->zone()),
4234 : vector(cx, ScriptVector(cx)),
4235 0 : wasmInstanceVector(cx, WasmInstanceObjectVector(cx))
4236 : {}
4237 0 :
4238 0 : /*
4239 0 : * Initialize this ScriptQuery. Raise an error and return false if we
4240 : * haven't enough memory.
4241 : */
4242 : bool init() {
4243 : if (!realms.init() ||
4244 : !innermostForRealm.init())
4245 0 : {
4246 0 : ReportOutOfMemory(cx);
4247 0 : return false;
4248 : }
4249 0 :
4250 0 : return true;
4251 : }
4252 :
4253 : /*
4254 : * Parse the query object |query|, and prepare to match only the scripts
4255 : * it specifies.
4256 : */
4257 : bool parseQuery(HandleObject query) {
4258 : /*
4259 : * Check for a 'global' property, which limits the results to those
4260 0 : * scripts scoped to a particular global object.
4261 : */
4262 : RootedValue global(cx);
4263 : if (!GetProperty(cx, query, query, cx->names().global, &global))
4264 : return false;
4265 0 : if (global.isUndefined()) {
4266 0 : if (!matchAllDebuggeeGlobals())
4267 : return false;
4268 0 : } else {
4269 0 : GlobalObject* globalObject = debugger->unwrapDebuggeeArgument(cx, global);
4270 : if (!globalObject)
4271 : return false;
4272 0 :
4273 0 : /*
4274 0 : * If the given global isn't a debuggee, just leave the set of
4275 : * acceptable globals empty; we'll return no scripts.
4276 : */
4277 : if (debugger->debuggees.has(globalObject)) {
4278 : if (!matchSingleGlobal(globalObject))
4279 : return false;
4280 0 : }
4281 0 : }
4282 :
4283 : /* Check for a 'url' property. */
4284 : if (!GetProperty(cx, query, query, cx->names().url, &url))
4285 : return false;
4286 : if (!url.isUndefined() && !url.isString()) {
4287 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
4288 : "query object's 'url' property",
4289 0 : "neither undefined nor a string");
4290 0 : return false;
4291 : }
4292 0 :
4293 0 : /* Check for a 'source' property */
4294 : RootedValue debuggerSource(cx);
4295 : if (!GetProperty(cx, query, query, cx->names().source, &debuggerSource))
4296 : return false;
4297 0 : if (!debuggerSource.isUndefined()) {
4298 0 : if (!debuggerSource.isObject() ||
4299 : debuggerSource.toObject().getClass() != &DebuggerSource_class) {
4300 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
4301 0 : "query object's 'source' property",
4302 0 : "not undefined nor a Debugger.Source object");
4303 0 : return false;
4304 : }
4305 0 :
4306 0 : Value owner = debuggerSource.toObject()
4307 : .as<NativeObject>()
4308 : .getReservedSlot(JSSLOT_DEBUGSOURCE_OWNER);
4309 0 :
4310 0 : /*
4311 0 : * The given source must have an owner. Otherwise, it's a
4312 : * Debugger.Source.prototype, which would match no scripts, and is
4313 : * probably a mistake.
4314 : */
4315 : if (!owner.isObject()) {
4316 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_PROTO,
4317 : "Debugger.Source", "Debugger.Source");
4318 0 : return false;
4319 0 : }
4320 0 :
4321 0 : /*
4322 : * If it does have an owner, it should match the Debugger we're
4323 : * calling findScripts on. It would work fine even if it didn't,
4324 : * but mixing Debugger.Sources is probably a sign of confusion.
4325 : */
4326 : if (&owner.toObject() != debugger->object) {
4327 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_WRONG_OWNER,
4328 : "Debugger.Source");
4329 0 : return false;
4330 0 : }
4331 0 :
4332 0 : hasSource = true;
4333 : source = GetSourceReferent(&debuggerSource.toObject());
4334 : }
4335 0 :
4336 0 : /* Check for a 'displayURL' property. */
4337 : RootedValue displayURL(cx);
4338 : if (!GetProperty(cx, query, query, cx->names().displayURL, &displayURL))
4339 : return false;
4340 0 : if (!displayURL.isUndefined() && !displayURL.isString()) {
4341 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
4342 : "query object's 'displayURL' property",
4343 0 : "neither undefined nor a string");
4344 0 : return false;
4345 : }
4346 0 :
4347 0 : if (displayURL.isString()) {
4348 : displayURLString = displayURL.toString()->ensureLinear(cx);
4349 : if (!displayURLString)
4350 0 : return false;
4351 0 : }
4352 0 :
4353 : /* Check for a 'line' property. */
4354 : RootedValue lineProperty(cx);
4355 : if (!GetProperty(cx, query, query, cx->names().line, &lineProperty))
4356 : return false;
4357 0 : if (lineProperty.isUndefined()) {
4358 0 : hasLine = false;
4359 : } else if (lineProperty.isNumber()) {
4360 0 : if (displayURL.isUndefined() && url.isUndefined() && !hasSource) {
4361 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4362 0 : JSMSG_QUERY_LINE_WITHOUT_URL);
4363 0 : return false;
4364 0 : }
4365 0 : double doubleLine = lineProperty.toNumber();
4366 0 : if (doubleLine <= 0 || (unsigned int) doubleLine != doubleLine) {
4367 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_LINE);
4368 0 : return false;
4369 0 : }
4370 0 : hasLine = true;
4371 0 : line = doubleLine;
4372 : } else {
4373 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
4374 0 : "query object's 'line' property",
4375 : "neither undefined nor an integer");
4376 0 : return false;
4377 : }
4378 0 :
4379 0 : /* Check for an 'innermost' property. */
4380 : PropertyName* innermostName = cx->names().innermost;
4381 : RootedValue innermostProperty(cx);
4382 : if (!GetProperty(cx, query, query, innermostName, &innermostProperty))
4383 0 : return false;
4384 0 : innermost = ToBoolean(innermostProperty);
4385 0 : if (innermost) {
4386 : /* Technically, we need only check hasLine, but this is clearer. */
4387 0 : if ((displayURL.isUndefined() && url.isUndefined() && !hasSource) || !hasLine) {
4388 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4389 : JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL);
4390 0 : return false;
4391 0 : }
4392 0 : }
4393 0 :
4394 : return true;
4395 : }
4396 :
4397 : /* Set up this ScriptQuery appropriately for a missing query argument. */
4398 : bool omittedQuery() {
4399 : url.setUndefined();
4400 : hasLine = false;
4401 0 : innermost = false;
4402 0 : displayURLString = nullptr;
4403 0 : return matchAllDebuggeeGlobals();
4404 0 : }
4405 0 :
4406 0 : /*
4407 : * Search all relevant realms and the stack for scripts matching
4408 : * this query, and append the matching scripts to |vector|.
4409 : */
4410 : bool findScripts() {
4411 : if (!prepareQuery() || !delazifyScripts())
4412 : return false;
4413 0 :
4414 0 : Realm* singletonRealm = nullptr;
4415 : if (realms.count() == 1)
4416 : singletonRealm = realms.all().front();
4417 0 :
4418 0 : /* Search each realm for debuggee scripts. */
4419 0 : MOZ_ASSERT(vector.empty());
4420 : oom = false;
4421 : IterateScripts(cx, singletonRealm, this, considerScript);
4422 0 : if (oom) {
4423 0 : ReportOutOfMemory(cx);
4424 0 : return false;
4425 0 : }
4426 0 :
4427 0 : /* We cannot touch the gray bits while isHeapBusy, so do this now. */
4428 : for (JSScript** i = vector.begin(); i != vector.end(); ++i)
4429 : JS::ExposeScriptToActiveJS(*i);
4430 :
4431 0 : /*
4432 0 : * For most queries, we just accumulate results in 'vector' as we find
4433 : * them. But if this is an 'innermost' query, then we've accumulated the
4434 : * results in the 'innermostForRealm' map. In that case, we now need to
4435 : * walk that map and populate 'vector'.
4436 : */
4437 : if (innermost) {
4438 : for (RealmToScriptMap::Range r = innermostForRealm.all();
4439 : !r.empty();
4440 0 : r.popFront())
4441 0 : {
4442 0 : JS::ExposeScriptToActiveJS(r.front().value());
4443 0 : if (!vector.append(r.front().value())) {
4444 : ReportOutOfMemory(cx);
4445 0 : return false;
4446 0 : }
4447 0 : }
4448 0 : }
4449 :
4450 : // TODOshu: Until such time that wasm modules are real ES6 modules,
4451 : // unconditionally consider all wasm toplevel instance scripts.
4452 : for (WeakGlobalObjectSet::Range r = debugger->allDebuggees(); !r.empty(); r.popFront()) {
4453 : for (wasm::Instance* instance : r.front()->realm()->wasm.instances()) {
4454 : consider(instance->object());
4455 0 : if (oom) {
4456 0 : ReportOutOfMemory(cx);
4457 0 : return false;
4458 0 : }
4459 0 : }
4460 0 : }
4461 :
4462 : return true;
4463 : }
4464 :
4465 0 : Handle<ScriptVector> foundScripts() const {
4466 : return vector;
4467 : }
4468 :
4469 0 : Handle<WasmInstanceObjectVector> foundWasmInstances() const {
4470 : return wasmInstanceVector;
4471 : }
4472 :
4473 0 : private:
4474 : /* The context in which we should do our work. */
4475 : JSContext* cx;
4476 :
4477 : /* The debugger for which we conduct queries. */
4478 : Debugger* debugger;
4479 :
4480 : /* Require the set of realms to stay fixed while the ScriptQuery is alive. */
4481 : gc::AutoEnterIteration iterMarker;
4482 :
4483 : using RealmSet = HashSet<Realm*, DefaultHasher<Realm*>, ZoneAllocPolicy>;
4484 :
4485 : /* A script must be in one of these realms to match the query. */
4486 : RealmSet realms;
4487 :
4488 : /* If this is a string, matching scripts have urls equal to it. */
4489 : RootedValue url;
4490 :
4491 : /* url as a C string. */
4492 : JSAutoByteString urlCString;
4493 :
4494 : /* If this is a string, matching scripts' sources have displayURLs equal to
4495 : * it. */
4496 : RootedLinearString displayURLString;
4497 :
4498 : /*
4499 : * If this is a source referent, matching scripts will have sources equal
4500 : * to this instance. Ideally we'd use a Maybe here, but Maybe interacts
4501 : * very badly with Rooted's LIFO invariant.
4502 : */
4503 : bool hasSource;
4504 : Rooted<DebuggerSourceReferent> source;
4505 :
4506 : /* True if the query contained a 'line' property. */
4507 : bool hasLine;
4508 :
4509 : /* The line matching scripts must cover. */
4510 : unsigned int line;
4511 :
4512 : /* True if the query has an 'innermost' property whose value is true. */
4513 : bool innermost;
4514 :
4515 : using RealmToScriptMap = HashMap<Realm*, JSScript*, DefaultHasher<Realm*>, ZoneAllocPolicy>;
4516 :
4517 : /*
4518 : * For 'innermost' queries, a map from realms to the innermost script
4519 : * we've seen so far in that realm. (Template instantiation code size
4520 : * explosion ho!)
4521 : */
4522 : RealmToScriptMap innermostForRealm;
4523 :
4524 : /*
4525 : * Accumulate the scripts in an Rooted<ScriptVector>, instead of creating
4526 : * the JS array as we go, because we mustn't allocate JS objects or GC
4527 : * while we use the CellIter.
4528 : */
4529 : Rooted<ScriptVector> vector;
4530 :
4531 : /*
4532 : * Like above, but for wasm modules.
4533 : */
4534 : Rooted<WasmInstanceObjectVector> wasmInstanceVector;
4535 :
4536 : /* Indicates whether OOM has occurred while matching. */
4537 : bool oom;
4538 :
4539 : bool addRealm(Realm* realm) {
4540 : return realms.put(realm);
4541 : }
4542 :
4543 0 : /* Arrange for this ScriptQuery to match only scripts that run in |global|. */
4544 : bool matchSingleGlobal(GlobalObject* global) {
4545 : MOZ_ASSERT(realms.count() == 0);
4546 : if (!addRealm(global->realm())) {
4547 0 : ReportOutOfMemory(cx);
4548 0 : return false;
4549 0 : }
4550 0 : return true;
4551 0 : }
4552 :
4553 : /*
4554 : * Arrange for this ScriptQuery to match all scripts running in debuggee
4555 : * globals.
4556 : */
4557 : bool matchAllDebuggeeGlobals() {
4558 : MOZ_ASSERT(realms.count() == 0);
4559 : /* Build our realm set from the debugger's set of debuggee globals. */
4560 0 : for (WeakGlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) {
4561 0 : if (!addRealm(r.front()->realm())) {
4562 : ReportOutOfMemory(cx);
4563 0 : return false;
4564 0 : }
4565 0 : }
4566 0 : return true;
4567 : }
4568 :
4569 0 : /*
4570 : * Given that parseQuery or omittedQuery has been called, prepare to match
4571 : * scripts. Set urlCString and displayURLChars as appropriate.
4572 : */
4573 : bool prepareQuery() {
4574 : /* Compute urlCString and displayURLChars, if a url or displayURL was
4575 : * given respectively. */
4576 0 : if (url.isString()) {
4577 : if (!urlCString.encodeLatin1(cx, url.toString()))
4578 : return false;
4579 0 : }
4580 0 :
4581 : return true;
4582 : }
4583 :
4584 : bool delazifyScripts() {
4585 : // All scripts in debuggee realms must be visible, so delazify
4586 : // everything.
4587 0 : for (auto r = realms.all(); !r.empty(); r.popFront()) {
4588 : Realm* realm = r.front();
4589 : if (!realm->ensureDelazifyScriptsForDebugger(cx))
4590 0 : return false;
4591 0 : }
4592 0 : return true;
4593 0 : }
4594 :
4595 0 : static void considerScript(JSRuntime* rt, void* data, JSScript* script,
4596 : const JS::AutoRequireNoGC& nogc) {
4597 : ScriptQuery* self = static_cast<ScriptQuery*>(data);
4598 0 : self->consider(script, nogc);
4599 : }
4600 0 :
4601 0 : /*
4602 0 : * If |script| matches this query, append it to |vector| or place it in
4603 : * |innermostForRealm|, as appropriate. Set |oom| if an out of memory
4604 : * condition occurred.
4605 : */
4606 : void consider(JSScript* script, const JS::AutoRequireNoGC& nogc) {
4607 : // We check for presence of script->code() because it is possible that
4608 : // the script was created and thus exposed to GC, but *not* fully
4609 0 : // initialized from fullyInit{FromEmitter,Trivial} due to errors.
4610 : if (oom || script->selfHosted() || !script->code())
4611 : return;
4612 : Realm* realm = script->realm();
4613 0 : if (!realms.has(realm))
4614 0 : return;
4615 0 : if (urlCString.ptr()) {
4616 0 : bool gotFilename = false;
4617 : if (script->filename() && strcmp(script->filename(), urlCString.ptr()) == 0)
4618 0 : gotFilename = true;
4619 0 :
4620 0 : bool gotSourceURL = false;
4621 0 : if (!gotFilename && script->scriptSource()->introducerFilename() &&
4622 : strcmp(script->scriptSource()->introducerFilename(), urlCString.ptr()) == 0)
4623 0 : {
4624 0 : gotSourceURL = true;
4625 0 : }
4626 : if (!gotFilename && !gotSourceURL)
4627 0 : return;
4628 : }
4629 0 : if (hasLine) {
4630 : if (line < script->lineno() || script->lineno() + GetScriptLineExtent(script) < line)
4631 : return;
4632 0 : }
4633 0 : if (displayURLString) {
4634 : if (!script->scriptSource() || !script->scriptSource()->hasDisplayURL())
4635 : return;
4636 0 :
4637 0 : const char16_t* s = script->scriptSource()->displayURL();
4638 : if (CompareChars(s, js_strlen(s), displayURLString) != 0)
4639 : return;
4640 0 : }
4641 0 : if (hasSource && !(source.is<ScriptSourceObject*>() &&
4642 : source.as<ScriptSourceObject*>()->source() == script->scriptSource()))
4643 : {
4644 0 : return;
4645 0 : }
4646 :
4647 : if (innermost) {
4648 : /*
4649 : * For 'innermost' queries, we don't place scripts in |vector| right
4650 0 : * away; we may later find another script that is nested inside this
4651 : * one. Instead, we record the innermost script we've found so far
4652 : * for each realm in innermostForRealm, and only populate |vector|
4653 : * at the bottom of findScripts, when we've traversed all the
4654 : * scripts.
4655 : *
4656 : * So: check this script against the innermost one we've found so
4657 : * far (if any), as recorded in innermostForRealm, and replace that
4658 : * if it's better.
4659 : */
4660 : RealmToScriptMap::AddPtr p = innermostForRealm.lookupForAdd(realm);
4661 : if (p) {
4662 : /* Is our newly found script deeper than the last one we found? */
4663 0 : JSScript* incumbent = p->value();
4664 0 : if (script->innermostScope()->chainLength() >
4665 : incumbent->innermostScope()->chainLength())
4666 0 : {
4667 0 : p->value() = script;
4668 0 : }
4669 : } else {
4670 0 : /*
4671 : * This is the first matching script we've encountered for this
4672 : * realm, so it is thus the innermost such script.
4673 : */
4674 : if (!innermostForRealm.add(p, realm, script)) {
4675 : oom = true;
4676 : return;
4677 0 : }
4678 0 : }
4679 0 : } else {
4680 : /* Record this matching script in the results vector. */
4681 : if (!vector.append(script)) {
4682 : oom = true;
4683 : return;
4684 0 : }
4685 0 : }
4686 0 : }
4687 :
4688 : /*
4689 : * If |instanceObject| matches this query, append it to |wasmInstanceVector|.
4690 : * Set |oom| if an out of memory condition occurred.
4691 : */
4692 : void consider(WasmInstanceObject* instanceObject) {
4693 : if (oom)
4694 : return;
4695 0 :
4696 0 : if (hasSource && source != AsVariant(instanceObject))
4697 : return;
4698 :
4699 0 : if (!wasmInstanceVector.append(instanceObject))
4700 : oom = true;
4701 : }
4702 0 : };
4703 0 :
4704 : /* static */ bool
4705 : Debugger::findScripts(JSContext* cx, unsigned argc, Value* vp)
4706 : {
4707 : THIS_DEBUGGER(cx, argc, vp, "findScripts", args, dbg);
4708 0 :
4709 : if (gc::GCRuntime::temporaryAbortIfWasmGc(cx)) {
4710 0 : JS_ReportErrorASCII(cx, "API temporarily unavailable under wasm gc");
4711 : return false;
4712 0 : }
4713 0 :
4714 0 : ScriptQuery query(cx, dbg);
4715 : if (!query.init())
4716 : return false;
4717 0 :
4718 0 : if (args.length() >= 1) {
4719 : RootedObject queryObject(cx, NonNullObject(cx, args[0]));
4720 : if (!queryObject || !query.parseQuery(queryObject))
4721 0 : return false;
4722 0 : } else {
4723 0 : if (!query.omittedQuery())
4724 0 : return false;
4725 : }
4726 0 :
4727 : if (!query.findScripts())
4728 : return false;
4729 :
4730 0 : Handle<ScriptVector> scripts(query.foundScripts());
4731 : Handle<WasmInstanceObjectVector> wasmInstances(query.foundWasmInstances());
4732 :
4733 0 : size_t resultLength = scripts.length() + wasmInstances.length();
4734 0 : RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, resultLength));
4735 : if (!result)
4736 0 : return false;
4737 0 :
4738 0 : result->ensureDenseInitializedLength(cx, 0, resultLength);
4739 :
4740 : for (size_t i = 0; i < scripts.length(); i++) {
4741 0 : JSObject* scriptObject = dbg->wrapScript(cx, scripts[i]);
4742 : if (!scriptObject)
4743 0 : return false;
4744 0 : result->setDenseElement(i, ObjectValue(*scriptObject));
4745 0 : }
4746 :
4747 0 : size_t wasmStart = scripts.length();
4748 : for (size_t i = 0; i < wasmInstances.length(); i++) {
4749 : JSObject* scriptObject = dbg->wrapWasmScript(cx, wasmInstances[i]);
4750 : if (!scriptObject)
4751 0 : return false;
4752 0 : result->setDenseElement(wasmStart + i, ObjectValue(*scriptObject));
4753 0 : }
4754 :
4755 0 : args.rval().setObject(*result);
4756 : return true;
4757 : }
4758 0 :
4759 0 : /*
4760 : * A class for parsing 'findObjects' query arguments and searching for objects
4761 : * that match the criteria they represent.
4762 : */
4763 : class MOZ_STACK_CLASS Debugger::ObjectQuery
4764 : {
4765 : public:
4766 0 : /* Construct an ObjectQuery to use matching scripts for |dbg|. */
4767 : ObjectQuery(JSContext* cx, Debugger* dbg) :
4768 : objects(cx), cx(cx), dbg(dbg), className(cx)
4769 : { }
4770 0 :
4771 0 : /* The vector that we are accumulating results in. */
4772 0 : AutoObjectVector objects;
4773 :
4774 : /* The set of debuggee compartments. */
4775 : JS::CompartmentSet debuggeeCompartments;
4776 :
4777 : /*
4778 : * Parse the query object |query|, and prepare to match only the objects it
4779 : * specifies.
4780 : */
4781 : bool parseQuery(HandleObject query) {
4782 : /* Check for the 'class' property */
4783 : RootedValue cls(cx);
4784 0 : if (!GetProperty(cx, query, query, cx->names().class_, &cls))
4785 : return false;
4786 0 : if (!cls.isUndefined()) {
4787 0 : if (!cls.isString()) {
4788 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
4789 0 : "query object's 'class' property",
4790 0 : "neither undefined nor a string");
4791 0 : return false;
4792 : }
4793 0 : className = cls;
4794 0 : }
4795 : return true;
4796 0 : }
4797 :
4798 : /* Set up this ObjectQuery appropriately for a missing query argument. */
4799 : void omittedQuery() {
4800 : className.setUndefined();
4801 : }
4802 :
4803 0 : /*
4804 : * Traverse the heap to find all relevant objects and add them to the
4805 : * provided vector.
4806 : */
4807 : bool findObjects() {
4808 : if (!prepareQuery())
4809 : return false;
4810 0 :
4811 0 : if (!debuggeeCompartments.init()) {
4812 : ReportOutOfMemory(cx);
4813 : return false;
4814 0 : }
4815 0 :
4816 0 : for (WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
4817 : if (!debuggeeCompartments.put(r.front()->compartment())) {
4818 : ReportOutOfMemory(cx);
4819 0 : return false;
4820 0 : }
4821 0 : }
4822 0 :
4823 : {
4824 : /*
4825 : * We can't tolerate the GC moving things around while we're
4826 : * searching the heap. Check that nothing we do causes a GC.
4827 : */
4828 : Maybe<JS::AutoCheckCannotGC> maybeNoGC;
4829 : RootedObject dbgObj(cx, dbg->object);
4830 : JS::ubi::RootList rootList(cx, maybeNoGC);
4831 0 : if (!rootList.init(dbgObj)) {
4832 0 : ReportOutOfMemory(cx);
4833 0 : return false;
4834 0 : }
4835 0 :
4836 0 : Traversal traversal(cx, *this, maybeNoGC.ref());
4837 : if (!traversal.init()) {
4838 : ReportOutOfMemory(cx);
4839 0 : return false;
4840 0 : }
4841 0 : traversal.wantNames = false;
4842 0 :
4843 : return traversal.addStart(JS::ubi::Node(&rootList)) &&
4844 0 : traversal.traverse();
4845 : }
4846 0 : }
4847 0 :
4848 : /*
4849 : * |ubi::Node::BreadthFirst| interface.
4850 : */
4851 : class NodeData {};
4852 : typedef JS::ubi::BreadthFirst<ObjectQuery> Traversal;
4853 : bool operator() (Traversal& traversal, JS::ubi::Node origin, const JS::ubi::Edge& edge,
4854 : NodeData*, bool first)
4855 : {
4856 0 : if (!first)
4857 : return true;
4858 :
4859 0 : JS::ubi::Node referent = edge.referent;
4860 : /*
4861 : * Only follow edges within our set of debuggee compartments; we don't
4862 0 : * care about the heap's subgraphs outside of our debuggee compartments,
4863 : * so we abandon the referent. Either (1) there is not a path from this
4864 : * non-debuggee node back to a node in our debuggee compartments, and we
4865 : * don't need to follow edges to or from this node, or (2) there does
4866 : * exist some path from this non-debuggee node back to a node in our
4867 : * debuggee compartments. However, if that were true, then the incoming
4868 : * cross compartment edge back into a debuggee compartment is already
4869 : * listed as an edge in the RootList we started traversal with, and
4870 : * therefore we don't need to follow edges to or from this non-debuggee
4871 : * node.
4872 : */
4873 : JS::Compartment* comp = referent.compartment();
4874 : if (comp && !debuggeeCompartments.has(comp)) {
4875 : traversal.abandonReferent();
4876 0 : return true;
4877 0 : }
4878 0 :
4879 0 : /*
4880 : * If the referent has an associated realm and it's not a debuggee
4881 : * realm, skip it. Don't abandonReferent() here like above: realms
4882 : * within a compartment can reference each other without going through
4883 : * cross-compartment wrappers.
4884 : */
4885 : Realm* realm = referent.realm();
4886 : if (realm && !dbg->isDebuggeeUnbarriered(realm))
4887 : return true;
4888 0 :
4889 0 : /*
4890 : * If the referent is an object and matches our query's restrictions,
4891 : * add it to the vector accumulating results. Skip objects that should
4892 : * never be exposed to JS, like EnvironmentObjects and internal
4893 : * functions.
4894 : */
4895 :
4896 : if (!referent.is<JSObject>() || referent.exposeToJS().isUndefined())
4897 : return true;
4898 :
4899 0 : JSObject* obj = referent.as<JSObject>();
4900 :
4901 : if (!className.isUndefined()) {
4902 0 : const char* objClassName = obj->getClass()->name;
4903 : if (strcmp(objClassName, classNameCString.ptr()) != 0)
4904 0 : return true;
4905 0 : }
4906 0 :
4907 : return objects.append(obj);
4908 : }
4909 :
4910 0 : private:
4911 : /* The context in which we should do our work. */
4912 : JSContext* cx;
4913 :
4914 : /* The debugger for which we conduct queries. */
4915 : Debugger* dbg;
4916 :
4917 : /*
4918 : * If this is non-null, matching objects will have a class whose name is
4919 : * this property.
4920 : */
4921 : RootedValue className;
4922 :
4923 : /* The className member, as a C string. */
4924 : JSAutoByteString classNameCString;
4925 :
4926 : /*
4927 : * Given that either omittedQuery or parseQuery has been called, prepare the
4928 : * query for matching objects.
4929 : */
4930 : bool prepareQuery() {
4931 : if (className.isString()) {
4932 : if (!classNameCString.encodeLatin1(cx, className.toString()))
4933 0 : return false;
4934 0 : }
4935 0 :
4936 : return true;
4937 : }
4938 : };
4939 :
4940 : bool
4941 : Debugger::findObjects(JSContext* cx, unsigned argc, Value* vp)
4942 : {
4943 : THIS_DEBUGGER(cx, argc, vp, "findObjects", args, dbg);
4944 0 :
4945 : ObjectQuery query(cx, dbg);
4946 0 :
4947 : if (args.length() >= 1) {
4948 0 : RootedObject queryObject(cx, NonNullObject(cx, args[0]));
4949 : if (!queryObject || !query.parseQuery(queryObject))
4950 0 : return false;
4951 0 : } else {
4952 0 : query.omittedQuery();
4953 0 : }
4954 :
4955 : if (!query.findObjects())
4956 : return false;
4957 :
4958 0 : size_t length = query.objects.length();
4959 : RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
4960 : if (!result)
4961 0 : return false;
4962 0 :
4963 0 : result->ensureDenseInitializedLength(cx, 0, length);
4964 :
4965 : for (size_t i = 0; i < length; i++) {
4966 0 : RootedValue debuggeeVal(cx, ObjectValue(*query.objects[i]));
4967 : if (!dbg->wrapDebuggeeValue(cx, &debuggeeVal))
4968 0 : return false;
4969 0 : result->setDenseElement(i, debuggeeVal);
4970 0 : }
4971 0 :
4972 0 : args.rval().setObject(*result);
4973 : return true;
4974 : }
4975 0 :
4976 0 : /* static */ bool
4977 : Debugger::findAllGlobals(JSContext* cx, unsigned argc, Value* vp)
4978 : {
4979 : THIS_DEBUGGER(cx, argc, vp, "findAllGlobals", args, dbg);
4980 0 :
4981 : AutoObjectVector globals(cx);
4982 0 :
4983 : {
4984 0 : // Accumulate the list of globals before wrapping them, because
4985 : // wrapping can GC and collect realms from under us, while iterating.
4986 : JS::AutoCheckCannotGC nogc;
4987 :
4988 : for (RealmsIter r(cx->runtime()); !r.done(); r.next()) {
4989 0 : if (r->creationOptions().invisibleToDebugger())
4990 : continue;
4991 0 :
4992 0 : r->compartment()->gcState.scheduledForDestruction = false;
4993 0 :
4994 : GlobalObject* global = r->maybeGlobal();
4995 0 :
4996 : if (cx->runtime()->isSelfHostingGlobal(global))
4997 0 : continue;
4998 :
4999 0 : if (global) {
5000 : /*
5001 : * We pulled |global| out of nowhere, so it's possible that it was
5002 0 : * marked gray by XPConnect. Since we're now exposing it to JS code,
5003 : * we need to mark it black.
5004 : */
5005 : JS::ExposeObjectToActiveJS(global);
5006 : if (!globals.append(global))
5007 : return false;
5008 0 : }
5009 0 : }
5010 0 : }
5011 :
5012 : RootedObject result(cx, NewDenseEmptyArray(cx));
5013 : if (!result)
5014 : return false;
5015 0 :
5016 0 : for (size_t i = 0; i < globals.length(); i++) {
5017 : RootedValue globalValue(cx, ObjectValue(*globals[i]));
5018 : if (!dbg->wrapDebuggeeValue(cx, &globalValue))
5019 0 : return false;
5020 0 : if (!NewbornArrayPush(cx, result, globalValue))
5021 0 : return false;
5022 0 : }
5023 0 :
5024 : args.rval().setObject(*result);
5025 : return true;
5026 : }
5027 0 :
5028 0 : /* static */ bool
5029 : Debugger::makeGlobalObjectReference(JSContext* cx, unsigned argc, Value* vp)
5030 : {
5031 : THIS_DEBUGGER(cx, argc, vp, "makeGlobalObjectReference", args, dbg);
5032 0 : if (!args.requireAtLeast(cx, "Debugger.makeGlobalObjectReference", 1))
5033 : return false;
5034 0 :
5035 0 : Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
5036 : if (!global)
5037 : return false;
5038 0 :
5039 0 : // If we create a D.O referring to a global in an invisible realm,
5040 : // then from it we can reach function objects, scripts, environments, etc.,
5041 : // none of which we're ever supposed to see.
5042 : if (global->realm()->creationOptions().invisibleToDebugger()) {
5043 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5044 : JSMSG_DEBUG_INVISIBLE_COMPARTMENT);
5045 0 : return false;
5046 : }
5047 0 :
5048 0 : args.rval().setObject(*global);
5049 : return dbg->wrapDebuggeeValue(cx, args.rval());
5050 : }
5051 0 :
5052 0 : bool
5053 : Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp)
5054 : {
5055 : CallArgs args = CallArgsFromVp(argc, vp);
5056 0 :
5057 : if (!args.requireAtLeast(cx, "Debugger.isCompilableUnit", 1))
5058 0 : return false;
5059 :
5060 0 : if (!args[0].isString()) {
5061 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
5062 : "Debugger.isCompilableUnit", "string",
5063 0 : InformalValueTypeName(args[0]));
5064 0 : return false;
5065 : }
5066 0 :
5067 0 : JSString* str = args[0].toString();
5068 : size_t length = GetStringLength(str);
5069 :
5070 0 : AutoStableStringChars chars(cx);
5071 0 : if (!chars.initTwoByte(cx, str))
5072 : return false;
5073 0 :
5074 0 : bool result = true;
5075 :
5076 : CompileOptions options(cx);
5077 0 : frontend::UsedNameTracker usedNames(cx);
5078 : if (!usedNames.init())
5079 0 : return false;
5080 0 :
5081 0 : RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
5082 : Nothing()));
5083 : if (!sourceObject)
5084 0 : return false;
5085 0 :
5086 0 : frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
5087 : options, chars.twoByteChars(),
5088 : length,
5089 : /* foldConstants = */ true,
5090 : usedNames, nullptr, nullptr,
5091 : sourceObject,
5092 : frontend::ParseGoal::Script);
5093 : JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
5094 : if (!parser.checkOptions() || !parser.parse()) {
5095 0 : // We ran into an error. If it was because we ran out of memory we report
5096 0 : // it in the usual way.
5097 0 : if (cx->isThrowingOutOfMemory()) {
5098 : JS::SetWarningReporter(cx, older);
5099 : return false;
5100 0 : }
5101 0 :
5102 0 : // If it was because we ran out of source, we return false so our caller
5103 : // knows to try to collect more [source].
5104 : if (parser.isUnexpectedEOF())
5105 : result = false;
5106 :
5107 0 : cx->clearPendingException();
5108 0 : }
5109 : JS::SetWarningReporter(cx, older);
5110 0 : args.rval().setBoolean(result);
5111 : return true;
5112 0 : }
5113 0 :
5114 0 : bool
5115 : Debugger::adoptDebuggeeValue(JSContext* cx, unsigned argc, Value* vp)
5116 : {
5117 : THIS_DEBUGGER(cx, argc, vp, "adoptDebuggeeValue", args, dbg);
5118 0 : if (!args.requireAtLeast(cx, "Debugger.adoptDebuggeeValue", 1))
5119 : return false;
5120 0 :
5121 0 : RootedValue v(cx, args[0]);
5122 : if (v.isObject()) {
5123 : RootedObject obj(cx, &v.toObject());
5124 0 : NativeObject* ndobj = ToNativeDebuggerObject(cx, &obj);
5125 0 : if (!ndobj) {
5126 0 : return false;
5127 0 : }
5128 0 :
5129 0 : obj.set(static_cast<JSObject*>(ndobj->getPrivate()));
5130 : v = ObjectValue(*obj);
5131 :
5132 0 : if (!dbg->wrapDebuggeeValue(cx, &v)) {
5133 0 : return false;
5134 : }
5135 0 : }
5136 :
5137 : args.rval().set(v);
5138 : return true;
5139 : }
5140 0 :
5141 0 : const JSPropertySpec Debugger::properties[] = {
5142 : JS_PSGS("enabled", Debugger::getEnabled, Debugger::setEnabled, 0),
5143 : JS_PSGS("onDebuggerStatement", Debugger::getOnDebuggerStatement,
5144 : Debugger::setOnDebuggerStatement, 0),
5145 : JS_PSGS("onExceptionUnwind", Debugger::getOnExceptionUnwind,
5146 : Debugger::setOnExceptionUnwind, 0),
5147 : JS_PSGS("onNewScript", Debugger::getOnNewScript, Debugger::setOnNewScript, 0),
5148 : JS_PSGS("onNewPromise", Debugger::getOnNewPromise, Debugger::setOnNewPromise, 0),
5149 : JS_PSGS("onPromiseSettled", Debugger::getOnPromiseSettled, Debugger::setOnPromiseSettled, 0),
5150 : JS_PSGS("onEnterFrame", Debugger::getOnEnterFrame, Debugger::setOnEnterFrame, 0),
5151 : JS_PSGS("onNewGlobalObject", Debugger::getOnNewGlobalObject, Debugger::setOnNewGlobalObject, 0),
5152 : JS_PSGS("uncaughtExceptionHook", Debugger::getUncaughtExceptionHook,
5153 : Debugger::setUncaughtExceptionHook, 0),
5154 : JS_PSGS("allowUnobservedAsmJS", Debugger::getAllowUnobservedAsmJS,
5155 : Debugger::setAllowUnobservedAsmJS, 0),
5156 : JS_PSGS("allowWasmBinarySource", Debugger::getAllowWasmBinarySource,
5157 : Debugger::setAllowWasmBinarySource, 0),
5158 : JS_PSGS("collectCoverageInfo", Debugger::getCollectCoverageInfo,
5159 : Debugger::setCollectCoverageInfo, 0),
5160 : JS_PSG("memory", Debugger::getMemory, 0),
5161 : JS_PS_END
5162 : };
5163 :
5164 : const JSFunctionSpec Debugger::methods[] = {
5165 : JS_FN("addDebuggee", Debugger::addDebuggee, 1, 0),
5166 : JS_FN("addAllGlobalsAsDebuggees", Debugger::addAllGlobalsAsDebuggees, 0, 0),
5167 : JS_FN("removeDebuggee", Debugger::removeDebuggee, 1, 0),
5168 : JS_FN("removeAllDebuggees", Debugger::removeAllDebuggees, 0, 0),
5169 : JS_FN("hasDebuggee", Debugger::hasDebuggee, 1, 0),
5170 : JS_FN("getDebuggees", Debugger::getDebuggees, 0, 0),
5171 : JS_FN("getNewestFrame", Debugger::getNewestFrame, 0, 0),
5172 : JS_FN("clearAllBreakpoints", Debugger::clearAllBreakpoints, 0, 0),
5173 : JS_FN("findScripts", Debugger::findScripts, 1, 0),
5174 : JS_FN("findObjects", Debugger::findObjects, 1, 0),
5175 : JS_FN("findAllGlobals", Debugger::findAllGlobals, 0, 0),
5176 : JS_FN("makeGlobalObjectReference", Debugger::makeGlobalObjectReference, 1, 0),
5177 : JS_FN("adoptDebuggeeValue", Debugger::adoptDebuggeeValue, 1, 0),
5178 : JS_FS_END
5179 : };
5180 :
5181 : const JSFunctionSpec Debugger::static_methods[] {
5182 : JS_FN("isCompilableUnit", Debugger::isCompilableUnit, 1, 0),
5183 : JS_FS_END
5184 : };
5185 :
5186 : /*** Debugger.Script *****************************************************************************/
5187 :
5188 : // Get the Debugger.Script referent as bare Cell. This should only be used for
5189 : // GC operations like tracing. Please use GetScriptReferent below.
5190 : static inline gc::Cell*
5191 : GetScriptReferentCell(JSObject* obj)
5192 : {
5193 : MOZ_ASSERT(obj->getClass() == &DebuggerScript_class);
5194 0 : return static_cast<gc::Cell*>(obj->as<NativeObject>().getPrivate());
5195 : }
5196 0 :
5197 0 : static inline DebuggerScriptReferent
5198 : GetScriptReferent(JSObject* obj)
5199 : {
5200 : MOZ_ASSERT(obj->getClass() == &DebuggerScript_class);
5201 0 : if (gc::Cell* cell = GetScriptReferentCell(obj)) {
5202 : if (cell->is<JSScript>())
5203 0 : return AsVariant(cell->as<JSScript>());
5204 0 : MOZ_ASSERT(cell->is<JSObject>());
5205 0 : return AsVariant(&static_cast<NativeObject*>(cell)->as<WasmInstanceObject>());
5206 0 : }
5207 0 : return AsVariant(static_cast<JSScript*>(nullptr));
5208 0 : }
5209 :
5210 0 : void
5211 : DebuggerScript_trace(JSTracer* trc, JSObject* obj)
5212 : {
5213 : /* This comes from a private pointer, so no barrier needed. */
5214 0 : gc::Cell* cell = GetScriptReferentCell(obj);
5215 : if (cell) {
5216 : if (cell->is<JSScript>()) {
5217 0 : JSScript* script = cell->as<JSScript>();
5218 0 : TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &script,
5219 0 : "Debugger.Script script referent");
5220 0 : obj->as<NativeObject>().setPrivateUnbarriered(script);
5221 : } else {
5222 0 : JSObject* wasm = cell->as<JSObject>();
5223 0 : TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &wasm,
5224 : "Debugger.Script wasm referent");
5225 0 : MOZ_ASSERT(wasm->is<WasmInstanceObject>());
5226 : obj->as<NativeObject>().setPrivateUnbarriered(wasm);
5227 0 : }
5228 0 : }
5229 0 : }
5230 :
5231 : class DebuggerScriptSetPrivateMatcher
5232 0 : {
5233 : NativeObject* obj_;
5234 : public:
5235 : explicit DebuggerScriptSetPrivateMatcher(NativeObject* obj) : obj_(obj) { }
5236 : using ReturnType = void;
5237 : ReturnType match(HandleScript script) { obj_->setPrivateGCThing(script); }
5238 0 : ReturnType match(Handle<WasmInstanceObject*> instance) { obj_->setPrivateGCThing(instance); }
5239 : };
5240 0 :
5241 0 : NativeObject*
5242 : Debugger::newDebuggerScript(JSContext* cx, Handle<DebuggerScriptReferent> referent)
5243 : {
5244 : assertSameCompartment(cx, object.get());
5245 0 :
5246 : RootedObject proto(cx, &object->getReservedSlot(JSSLOT_DEBUG_SCRIPT_PROTO).toObject());
5247 0 : MOZ_ASSERT(proto);
5248 : NativeObject* scriptobj = NewNativeObjectWithGivenProto(cx, &DebuggerScript_class,
5249 0 : proto, TenuredObject);
5250 0 : if (!scriptobj)
5251 0 : return nullptr;
5252 0 : scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_OWNER, ObjectValue(*object));
5253 0 : DebuggerScriptSetPrivateMatcher matcher(scriptobj);
5254 : referent.match(matcher);
5255 0 :
5256 0 : return scriptobj;
5257 0 : }
5258 :
5259 0 : template <typename ReferentVariant, typename Referent, typename Map>
5260 : JSObject*
5261 : Debugger::wrapVariantReferent(JSContext* cx, Map& map, Handle<CrossCompartmentKey> key,
5262 : Handle<ReferentVariant> referent)
5263 : {
5264 0 : assertSameCompartment(cx, object);
5265 :
5266 : Handle<Referent> untaggedReferent = referent.template as<Referent>();
5267 0 : MOZ_ASSERT(cx->compartment() != untaggedReferent->compartment());
5268 :
5269 0 : DependentAddPtr<Map> p(cx, map, untaggedReferent);
5270 0 : if (!p) {
5271 : NativeObject* wrapper = newVariantWrapper(cx, referent);
5272 0 : if (!wrapper)
5273 0 : return nullptr;
5274 0 :
5275 0 : if (!p.add(cx, map, untaggedReferent, wrapper)) {
5276 0 : NukeDebuggerWrapper(wrapper);
5277 : return nullptr;
5278 0 : }
5279 0 :
5280 0 : if (!object->compartment()->putWrapper(cx, key, ObjectValue(*wrapper))) {
5281 : NukeDebuggerWrapper(wrapper);
5282 : map.remove(untaggedReferent);
5283 0 : ReportOutOfMemory(cx);
5284 0 : return nullptr;
5285 0 : }
5286 0 :
5287 0 : }
5288 :
5289 : return p->value();
5290 : }
5291 :
5292 0 : JSObject*
5293 : Debugger::wrapVariantReferent(JSContext* cx, Handle<DebuggerScriptReferent> referent)
5294 : {
5295 : JSObject* obj;
5296 0 : if (referent.is<JSScript*>()) {
5297 : Handle<JSScript*> untaggedReferent = referent.template as<JSScript*>();
5298 : Rooted<CrossCompartmentKey> key(cx, CrossCompartmentKey(object, untaggedReferent));
5299 0 : obj = wrapVariantReferent<DebuggerScriptReferent, JSScript*, ScriptWeakMap>(
5300 0 : cx, scripts, key, referent);
5301 0 : } else {
5302 0 : Handle<WasmInstanceObject*> untaggedReferent = referent.template as<WasmInstanceObject*>();
5303 0 : Rooted<CrossCompartmentKey> key(cx, CrossCompartmentKey(object, untaggedReferent,
5304 : CrossCompartmentKey::DebuggerObjectKind::DebuggerWasmScript));
5305 0 : obj = wrapVariantReferent<DebuggerScriptReferent, WasmInstanceObject*, WasmInstanceWeakMap>(
5306 0 : cx, wasmInstanceScripts, key, referent);
5307 0 : }
5308 0 : MOZ_ASSERT_IF(obj, GetScriptReferent(obj) == referent);
5309 0 : return obj;
5310 : }
5311 0 :
5312 0 : JSObject*
5313 : Debugger::wrapScript(JSContext* cx, HandleScript script)
5314 : {
5315 : Rooted<DebuggerScriptReferent> referent(cx, script.get());
5316 0 : return wrapVariantReferent(cx, referent);
5317 : }
5318 0 :
5319 0 : JSObject*
5320 : Debugger::wrapWasmScript(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance)
5321 : {
5322 : Rooted<DebuggerScriptReferent> referent(cx, wasmInstance.get());
5323 0 : return wrapVariantReferent(cx, referent);
5324 : }
5325 0 :
5326 0 : static JSObject*
5327 : DebuggerScript_check(JSContext* cx, const Value& v, const char* fnname)
5328 : {
5329 : JSObject* thisobj = NonNullObject(cx, v);
5330 0 : if (!thisobj)
5331 : return nullptr;
5332 0 : if (thisobj->getClass() != &DebuggerScript_class) {
5333 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
5334 : "Debugger.Script", fnname, thisobj->getClass()->name);
5335 0 : return nullptr;
5336 : }
5337 0 :
5338 0 : /*
5339 : * Check for Debugger.Script.prototype, which is of class DebuggerScript_class
5340 : * but whose script is null.
5341 : */
5342 : if (!GetScriptReferentCell(thisobj)) {
5343 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
5344 : "Debugger.Script", fnname, "prototype object");
5345 0 : return nullptr;
5346 : }
5347 0 :
5348 0 : return thisobj;
5349 : }
5350 :
5351 : template <typename ReferentT>
5352 : static JSObject*
5353 : DebuggerScript_checkThis(JSContext* cx, const CallArgs& args, const char* fnname,
5354 : const char* refname)
5355 : {
5356 0 : JSObject* thisobj = DebuggerScript_check(cx, args.thisv(), fnname);
5357 : if (!thisobj)
5358 : return nullptr;
5359 0 :
5360 0 : if (!GetScriptReferent(thisobj).is<ReferentT>()) {
5361 : ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, args.thisv(), nullptr,
5362 : refname);
5363 0 : return nullptr;
5364 0 : }
5365 :
5366 : return thisobj;
5367 0 : }
5368 :
5369 : #define THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, fnname, args, obj, referent) \
5370 : CallArgs args = CallArgsFromVp(argc, vp); \
5371 : RootedObject obj(cx, DebuggerScript_check(cx, args.thisv(), fnname)); \
5372 : if (!obj) \
5373 : return false; \
5374 : Rooted<DebuggerScriptReferent> referent(cx, GetScriptReferent(obj))
5375 :
5376 : #define THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, fnname, args, obj, script) \
5377 : CallArgs args = CallArgsFromVp(argc, vp); \
5378 : RootedObject obj(cx, DebuggerScript_checkThis<JSScript*>(cx, args, fnname, \
5379 : "a JS script")); \
5380 : if (!obj) \
5381 : return false; \
5382 : RootedScript script(cx, GetScriptReferent(obj).as<JSScript*>())
5383 :
5384 : static bool
5385 : DebuggerScript_getIsGeneratorFunction(JSContext* cx, unsigned argc, Value* vp)
5386 : {
5387 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get isGeneratorFunction)", args, obj, script);
5388 : args.rval().setBoolean(script->isGenerator());
5389 0 : return true;
5390 : }
5391 0 :
5392 0 : static bool
5393 : DebuggerScript_getIsAsyncFunction(JSContext* cx, unsigned argc, Value* vp)
5394 : {
5395 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get isAsyncFunction)", args, obj, script);
5396 : args.rval().setBoolean(script->isAsync());
5397 0 : return true;
5398 : }
5399 0 :
5400 0 : static bool
5401 : DebuggerScript_getDisplayName(JSContext* cx, unsigned argc, Value* vp)
5402 : {
5403 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get displayName)", args, obj, script);
5404 : Debugger* dbg = Debugger::fromChildJSObject(obj);
5405 0 :
5406 : JSFunction* func = script->functionNonDelazifying();
5407 0 : JSString* name = func ? func->displayAtom() : nullptr;
5408 0 : if (!name) {
5409 : args.rval().setUndefined();
5410 0 : return true;
5411 0 : }
5412 0 :
5413 0 : RootedValue namev(cx, StringValue(name));
5414 0 : if (!dbg->wrapDebuggeeValue(cx, &namev))
5415 : return false;
5416 : args.rval().set(namev);
5417 0 : return true;
5418 0 : }
5419 :
5420 0 : static bool
5421 0 : DebuggerScript_getUrl(JSContext* cx, unsigned argc, Value* vp)
5422 : {
5423 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get url)", args, obj, script);
5424 :
5425 0 : if (script->filename()) {
5426 : JSString* str;
5427 0 : if (script->scriptSource()->introducerFilename())
5428 : str = NewStringCopyZ<CanGC>(cx, script->scriptSource()->introducerFilename());
5429 0 : else
5430 : str = NewStringCopyZ<CanGC>(cx, script->filename());
5431 0 : if (!str)
5432 0 : return false;
5433 : args.rval().setString(str);
5434 0 : } else {
5435 0 : args.rval().setNull();
5436 : }
5437 0 : return true;
5438 : }
5439 0 :
5440 : struct DebuggerScriptGetStartLineMatcher
5441 : {
5442 : using ReturnType = uint32_t;
5443 :
5444 : ReturnType match(HandleScript script) {
5445 : return script->lineno();
5446 : }
5447 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
5448 : return 1;
5449 0 : }
5450 : };
5451 :
5452 : static bool
5453 : DebuggerScript_getStartLine(JSContext* cx, unsigned argc, Value* vp)
5454 : {
5455 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get startLine)", args, obj, referent);
5456 : DebuggerScriptGetStartLineMatcher matcher;
5457 0 : args.rval().setNumber(referent.match(matcher));
5458 : return true;
5459 0 : }
5460 :
5461 0 : struct DebuggerScriptGetLineCountMatcher
5462 : {
5463 : JSContext* cx_;
5464 : double totalLines;
5465 :
5466 : explicit DebuggerScriptGetLineCountMatcher(JSContext* cx) : cx_(cx) {}
5467 : using ReturnType = bool;
5468 :
5469 : ReturnType match(HandleScript script) {
5470 0 : totalLines = double(GetScriptLineExtent(script));
5471 : return true;
5472 : }
5473 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
5474 0 : uint32_t result;
5475 : if (!wasmInstance->instance().debug().totalSourceLines(cx_, &result))
5476 : return false;
5477 0 : totalLines = double(result);
5478 : return true;
5479 0 : }
5480 : };
5481 0 :
5482 0 : static bool
5483 : DebuggerScript_getLineCount(JSContext* cx, unsigned argc, Value* vp)
5484 : {
5485 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get lineCount)", args, obj, referent);
5486 : DebuggerScriptGetLineCountMatcher matcher(cx);
5487 0 : if (!referent.match(matcher))
5488 : return false;
5489 0 : args.rval().setNumber(matcher.totalLines);
5490 0 : return true;
5491 0 : }
5492 :
5493 0 : class DebuggerScriptGetSourceMatcher
5494 0 : {
5495 : JSContext* cx_;
5496 : Debugger* dbg_;
5497 :
5498 : public:
5499 : DebuggerScriptGetSourceMatcher(JSContext* cx, Debugger* dbg)
5500 : : cx_(cx), dbg_(dbg)
5501 : { }
5502 :
5503 : using ReturnType = JSObject*;
5504 0 :
5505 : ReturnType match(HandleScript script) {
5506 : RootedScriptSourceObject source(cx_,
5507 : &UncheckedUnwrap(script->sourceObject())->as<ScriptSourceObject>());
5508 : return dbg_->wrapSource(cx_, source);
5509 0 : }
5510 :
5511 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
5512 0 : return dbg_->wrapWasmSource(cx_, wasmInstance);
5513 : }
5514 : };
5515 :
5516 0 : static bool
5517 : DebuggerScript_getSource(JSContext* cx, unsigned argc, Value* vp)
5518 : {
5519 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get source)", args, obj, referent);
5520 : Debugger* dbg = Debugger::fromChildJSObject(obj);
5521 0 :
5522 : DebuggerScriptGetSourceMatcher matcher(cx, dbg);
5523 0 : RootedObject sourceObject(cx, referent.match(matcher));
5524 0 : if (!sourceObject)
5525 : return false;
5526 0 :
5527 0 : args.rval().setObject(*sourceObject);
5528 0 : return true;
5529 : }
5530 :
5531 0 : static bool
5532 0 : DebuggerScript_getSourceStart(JSContext* cx, unsigned argc, Value* vp)
5533 : {
5534 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get sourceStart)", args, obj, script);
5535 : args.rval().setNumber(uint32_t(script->sourceStart()));
5536 0 : return true;
5537 : }
5538 0 :
5539 0 : static bool
5540 : DebuggerScript_getSourceLength(JSContext* cx, unsigned argc, Value* vp)
5541 : {
5542 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get sourceEnd)", args, obj, script);
5543 : args.rval().setNumber(uint32_t(script->sourceEnd() - script->sourceStart()));
5544 0 : return true;
5545 : }
5546 0 :
5547 0 : static bool
5548 : DebuggerScript_getGlobal(JSContext* cx, unsigned argc, Value* vp)
5549 : {
5550 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get global)", args, obj, script);
5551 : Debugger* dbg = Debugger::fromChildJSObject(obj);
5552 0 :
5553 : RootedValue v(cx, ObjectValue(script->global()));
5554 0 : if (!dbg->wrapDebuggeeValue(cx, &v))
5555 0 : return false;
5556 : args.rval().set(v);
5557 0 : return true;
5558 0 : }
5559 :
5560 0 : class DebuggerScriptGetFormatMatcher
5561 0 : {
5562 : const JSAtomState& names_;
5563 : public:
5564 : explicit DebuggerScriptGetFormatMatcher(const JSAtomState& names) : names_(names) { }
5565 : using ReturnType = JSAtom*;
5566 : ReturnType match(HandleScript script) { return names_.js; }
5567 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return names_.wasm; }
5568 0 : };
5569 :
5570 0 : static bool
5571 0 : DebuggerScript_getFormat(JSContext* cx, unsigned argc, Value* vp)
5572 : {
5573 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get format)", args, obj, referent);
5574 : DebuggerScriptGetFormatMatcher matcher(cx->names());
5575 0 : args.rval().setString(referent.match(matcher));
5576 : return true;
5577 0 : }
5578 0 :
5579 0 : static bool
5580 : DebuggerScript_getChildScripts(JSContext* cx, unsigned argc, Value* vp)
5581 : {
5582 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getChildScripts", args, obj, script);
5583 : Debugger* dbg = Debugger::fromChildJSObject(obj);
5584 0 :
5585 : RootedObject result(cx, NewDenseEmptyArray(cx));
5586 0 : if (!result)
5587 0 : return false;
5588 : if (script->hasObjects()) {
5589 0 : /*
5590 0 : * script->savedCallerFun indicates that this is a direct eval script
5591 : * and the calling function is stored as script->objects()->vector[0].
5592 0 : * It is not really a child script of this script, so skip it using
5593 : * innerObjectsStart().
5594 : */
5595 : ObjectArray* objects = script->objects();
5596 : RootedFunction fun(cx);
5597 : RootedScript funScript(cx);
5598 : RootedObject obj(cx), s(cx);
5599 0 : for (uint32_t i = 0; i < objects->length; i++) {
5600 0 : obj = objects->vector[i];
5601 0 : if (obj->is<JSFunction>()) {
5602 0 : fun = &obj->as<JSFunction>();
5603 0 : // The inner function could be a wasm native.
5604 0 : if (fun->isNative())
5605 0 : continue;
5606 0 : funScript = GetOrCreateFunctionScript(cx, fun);
5607 : if (!funScript)
5608 0 : return false;
5609 : s = dbg->wrapScript(cx, funScript);
5610 0 : if (!s || !NewbornArrayPush(cx, result, ObjectValue(*s)))
5611 0 : return false;
5612 0 : }
5613 0 : }
5614 0 : }
5615 : args.rval().setObject(*result);
5616 : return true;
5617 : }
5618 :
5619 0 : static bool
5620 0 : ScriptOffset(JSContext* cx, const Value& v, size_t* offsetp)
5621 : {
5622 : double d;
5623 : size_t off;
5624 0 :
5625 : bool ok = v.isNumber();
5626 : if (ok) {
5627 : d = v.toNumber();
5628 : off = size_t(d);
5629 0 : }
5630 0 : if (!ok || off != d) {
5631 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
5632 0 : return false;
5633 : }
5634 0 : *offsetp = off;
5635 0 : return true;
5636 0 : }
5637 :
5638 0 : static bool
5639 0 : EnsureScriptOffsetIsValid(JSContext* cx, JSScript* script, size_t offset)
5640 : {
5641 : if (IsValidBytecodeOffset(cx, script, offset))
5642 : return true;
5643 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
5644 : return false;
5645 0 : }
5646 :
5647 0 : namespace {
5648 0 :
5649 : class BytecodeRangeWithPosition : private BytecodeRange
5650 : {
5651 : public:
5652 : using BytecodeRange::empty;
5653 0 : using BytecodeRange::frontPC;
5654 : using BytecodeRange::frontOpcode;
5655 : using BytecodeRange::frontOffset;
5656 :
5657 : BytecodeRangeWithPosition(JSContext* cx, JSScript* script)
5658 : : BytecodeRange(cx, script), lineno(script->lineno()), column(0),
5659 : sn(script->notes()), snpc(script->code()), isEntryPoint(false),
5660 : wasArtifactEntryPoint(false)
5661 0 : {
5662 0 : if (!SN_IS_TERMINATOR(sn))
5663 0 : snpc += SN_DELTA(sn);
5664 0 : updatePosition();
5665 : while (frontPC() != script->main())
5666 0 : popFront();
5667 0 :
5668 0 : if (frontOpcode() != JSOP_JUMPTARGET)
5669 0 : isEntryPoint = true;
5670 0 : else
5671 : wasArtifactEntryPoint = true;
5672 0 : }
5673 0 :
5674 : void popFront() {
5675 0 : BytecodeRange::popFront();
5676 0 : if (empty())
5677 : isEntryPoint = false;
5678 0 : else
5679 0 : updatePosition();
5680 0 :
5681 0 : // The following conditions are handling artifacts introduced by the
5682 : // bytecode emitter, such that we do not add breakpoints on empty
5683 0 : // statements of the source code of the user.
5684 : if (wasArtifactEntryPoint) {
5685 : wasArtifactEntryPoint = false;
5686 : isEntryPoint = true;
5687 : }
5688 0 :
5689 0 : if (isEntryPoint && frontOpcode() == JSOP_JUMPTARGET) {
5690 0 : wasArtifactEntryPoint = isEntryPoint;
5691 : isEntryPoint = false;
5692 : }
5693 0 : }
5694 0 :
5695 0 : size_t frontLineNumber() const { return lineno; }
5696 : size_t frontColumnNumber() const { return column; }
5697 0 :
5698 : // Entry points are restricted to bytecode offsets that have an
5699 : // explicit mention in the line table. This restriction avoids a
5700 : // number of failing cases caused by some instructions not having
5701 : // sensible (to the user) line numbers, and it is one way to
5702 : // implement the idea that the bytecode emitter should tell the
5703 : // debugger exactly which offsets represent "interesting" (to the
5704 : // user) places to stop.
5705 : bool frontIsEntryPoint() const { return isEntryPoint; }
5706 :
5707 : private:
5708 : void updatePosition() {
5709 : /*
5710 : * Determine the current line number by reading all source notes up to
5711 : * and including the current offset.
5712 0 : */
5713 : jsbytecode *lastLinePC = nullptr;
5714 : while (!SN_IS_TERMINATOR(sn) && snpc <= frontPC()) {
5715 : SrcNoteType type = SN_TYPE(sn);
5716 : if (type == SRC_COLSPAN) {
5717 0 : ptrdiff_t colspan = SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, 0));
5718 0 : MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
5719 0 : column += colspan;
5720 0 : lastLinePC = snpc;
5721 0 : } else if (type == SRC_SETLINE) {
5722 0 : lineno = size_t(GetSrcNoteOffset(sn, 0));
5723 0 : column = 0;
5724 0 : lastLinePC = snpc;
5725 0 : } else if (type == SRC_NEWLINE) {
5726 0 : lineno++;
5727 0 : column = 0;
5728 0 : lastLinePC = snpc;
5729 0 : }
5730 0 :
5731 0 : sn = SN_NEXT(sn);
5732 0 : snpc += SN_DELTA(sn);
5733 : }
5734 : isEntryPoint = lastLinePC == frontPC();
5735 0 : }
5736 0 :
5737 : size_t lineno;
5738 0 : size_t column;
5739 0 : jssrcnote* sn;
5740 : jsbytecode* snpc;
5741 : bool isEntryPoint;
5742 : bool wasArtifactEntryPoint;
5743 : };
5744 :
5745 : /*
5746 : * FlowGraphSummary::populate(cx, script) computes a summary of script's
5747 : * control flow graph used by DebuggerScript_{getAllOffsets,getLineOffsets}.
5748 : *
5749 : * An instruction on a given line is an entry point for that line if it can be
5750 : * reached from (an instruction on) a different line. We distinguish between the
5751 : * following cases:
5752 : * - hasNoEdges:
5753 : * The instruction cannot be reached, so the instruction is not an entry
5754 : * point for the line it is on.
5755 : * - hasSingleEdge:
5756 : * The instruction can be reached from a single line. If this line is
5757 : * different from the line the instruction is on, the instruction is an
5758 : * entry point for that line.
5759 : *
5760 : * Similarly, an instruction on a given position (line/column pair) is an
5761 : * entry point for that position if it can be reached from (an instruction on) a
5762 : * different position. Again, we distinguish between the following cases:
5763 : * - hasNoEdges:
5764 : * The instruction cannot be reached, so the instruction is not an entry
5765 : * point for the position it is on.
5766 : * - hasSingleEdge:
5767 : * The instruction can be reached from a single position. If this line is
5768 : * different from the position the instruction is on, the instruction is
5769 : * an entry point for that position.
5770 : */
5771 : class FlowGraphSummary {
5772 : public:
5773 : class Entry {
5774 : public:
5775 0 : static Entry createWithSingleEdge(size_t lineno, size_t column) {
5776 : return Entry(lineno, column);
5777 : }
5778 :
5779 : static Entry createWithMultipleEdgesFromSingleLine(size_t lineno) {
5780 0 : return Entry(lineno, SIZE_MAX);
5781 : }
5782 :
5783 : static Entry createWithMultipleEdgesFromMultipleLines() {
5784 0 : return Entry(SIZE_MAX, SIZE_MAX);
5785 : }
5786 :
5787 : Entry() : lineno_(SIZE_MAX), column_(0) {}
5788 0 :
5789 : bool hasNoEdges() const {
5790 : return lineno_ == SIZE_MAX && column_ != SIZE_MAX;
5791 0 : }
5792 :
5793 : bool hasSingleEdge() const {
5794 0 : return lineno_ != SIZE_MAX && column_ != SIZE_MAX;
5795 : }
5796 :
5797 : size_t lineno() const {
5798 0 : return lineno_;
5799 : }
5800 :
5801 : size_t column() const {
5802 : return column_;
5803 : }
5804 :
5805 : private:
5806 : Entry(size_t lineno, size_t column) : lineno_(lineno), column_(column) {}
5807 :
5808 : size_t lineno_;
5809 : size_t column_;
5810 : };
5811 :
5812 : explicit FlowGraphSummary(JSContext* cx) : entries_(cx) {}
5813 :
5814 : Entry& operator[](size_t index) {
5815 : return entries_[index];
5816 0 : }
5817 :
5818 : bool populate(JSContext* cx, JSScript* script) {
5819 0 : if (!entries_.growBy(script->length()))
5820 : return false;
5821 : unsigned mainOffset = script->pcToOffset(script->main());
5822 0 : entries_[mainOffset] = Entry::createWithMultipleEdgesFromMultipleLines();
5823 0 :
5824 : size_t prevLineno = script->lineno();
5825 0 : size_t prevColumn = 0;
5826 0 : JSOp prevOp = JSOP_NOP;
5827 : for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) {
5828 0 : size_t lineno = prevLineno;
5829 0 : size_t column = prevColumn;
5830 0 : JSOp op = r.frontOpcode();
5831 0 :
5832 0 : if (FlowsIntoNext(prevOp))
5833 0 : addEdge(prevLineno, prevColumn, r.frontOffset());
5834 0 :
5835 : // If we visit the branch target before we visit the
5836 0 : // branch op itself, just reuse the previous location.
5837 0 : // This is reasonable for the time being because this
5838 : // situation can currently only arise from loop heads,
5839 : // where this assumption holds.
5840 : if (BytecodeIsJumpTarget(op) && !entries_[r.frontOffset()].hasNoEdges()) {
5841 : lineno = entries_[r.frontOffset()].lineno();
5842 : column = entries_[r.frontOffset()].column();
5843 : }
5844 0 :
5845 0 : if (r.frontIsEntryPoint()) {
5846 0 : lineno = r.frontLineNumber();
5847 : column = r.frontColumnNumber();
5848 : }
5849 0 :
5850 0 : if (CodeSpec[op].type() == JOF_JUMP) {
5851 0 : addEdge(lineno, column, r.frontOffset() + GET_JUMP_OFFSET(r.frontPC()));
5852 : } else if (op == JSOP_TABLESWITCH) {
5853 : jsbytecode* pc = r.frontPC();
5854 0 : size_t offset = r.frontOffset();
5855 0 : ptrdiff_t step = JUMP_OFFSET_LEN;
5856 0 : size_t defaultOffset = offset + GET_JUMP_OFFSET(pc);
5857 0 : pc += step;
5858 0 : addEdge(lineno, column, defaultOffset);
5859 0 :
5860 0 : int32_t low = GET_JUMP_OFFSET(pc);
5861 0 : pc += JUMP_OFFSET_LEN;
5862 0 : int ncases = GET_JUMP_OFFSET(pc) - low + 1;
5863 : pc += JUMP_OFFSET_LEN;
5864 0 :
5865 0 : for (int i = 0; i < ncases; i++) {
5866 0 : size_t target = offset + GET_JUMP_OFFSET(pc);
5867 0 : addEdge(lineno, column, target);
5868 : pc += step;
5869 0 : }
5870 0 : } else if (op == JSOP_TRY) {
5871 0 : // As there is no literal incoming edge into the catch block, we
5872 0 : // make a fake one by copying the JSOP_TRY location, as-if this
5873 : // was an incoming edge of the catch block. This is needed
5874 0 : // because we only report offsets of entry points which have
5875 : // valid incoming edges.
5876 : JSTryNote* tn = script->trynotes()->vector;
5877 : JSTryNote* tnlimit = tn + script->trynotes()->length;
5878 : for (; tn < tnlimit; tn++) {
5879 : uint32_t startOffset = script->mainOffset() + tn->start;
5880 0 : if (startOffset == r.frontOffset() + 1) {
5881 0 : uint32_t catchOffset = startOffset + tn->length;
5882 0 : if (tn->kind == JSTRY_CATCH || tn->kind == JSTRY_FINALLY)
5883 0 : addEdge(lineno, column, catchOffset);
5884 0 : }
5885 0 : }
5886 0 : }
5887 0 :
5888 : prevLineno = lineno;
5889 : prevColumn = column;
5890 : prevOp = op;
5891 : }
5892 0 :
5893 0 : return true;
5894 0 : }
5895 :
5896 : private:
5897 0 : void addEdge(size_t sourceLineno, size_t sourceColumn, size_t targetOffset) {
5898 : if (entries_[targetOffset].hasNoEdges())
5899 : entries_[targetOffset] = Entry::createWithSingleEdge(sourceLineno, sourceColumn);
5900 : else if (entries_[targetOffset].lineno() != sourceLineno)
5901 0 : entries_[targetOffset] = Entry::createWithMultipleEdgesFromMultipleLines();
5902 0 : else if (entries_[targetOffset].column() != sourceColumn)
5903 0 : entries_[targetOffset] = Entry::createWithMultipleEdgesFromSingleLine(sourceLineno);
5904 0 : }
5905 0 :
5906 0 : Vector<Entry> entries_;
5907 0 : };
5908 0 :
5909 : } /* anonymous namespace */
5910 :
5911 : class DebuggerScriptGetOffsetLocationMatcher
5912 : {
5913 : JSContext* cx_;
5914 : size_t offset_;
5915 : MutableHandlePlainObject result_;
5916 :
5917 : public:
5918 : explicit DebuggerScriptGetOffsetLocationMatcher(JSContext* cx, size_t offset,
5919 : MutableHandlePlainObject result)
5920 : : cx_(cx), offset_(offset), result_(result) { }
5921 : using ReturnType = bool;
5922 : ReturnType match(HandleScript script) {
5923 : if (!EnsureScriptOffsetIsValid(cx_, script, offset_))
5924 0 : return false;
5925 :
5926 0 : FlowGraphSummary flowData(cx_);
5927 0 : if (!flowData.populate(cx_, script))
5928 : return false;
5929 :
5930 0 : result_.set(NewBuiltinClassInstance<PlainObject>(cx_));
5931 0 : if (!result_)
5932 : return false;
5933 :
5934 0 : BytecodeRangeWithPosition r(cx_, script);
5935 0 : while (!r.empty() && r.frontOffset() < offset_)
5936 : r.popFront();
5937 :
5938 0 : size_t offset = r.frontOffset();
5939 0 : bool isEntryPoint = r.frontIsEntryPoint();
5940 0 :
5941 : // Line numbers are only correctly defined on entry points. Thus looks
5942 0 : // either for the next valid offset in the flowData, being the last entry
5943 0 : // point flowing into the current offset, or for the next valid entry point.
5944 : while (!r.frontIsEntryPoint() && !flowData[r.frontOffset()].hasSingleEdge()) {
5945 : r.popFront();
5946 : MOZ_ASSERT(!r.empty());
5947 : }
5948 0 :
5949 0 : // If this is an entry point, take the line number associated with the entry
5950 0 : // point, otherwise settle on the next instruction and take the incoming
5951 : // edge position.
5952 : size_t lineno;
5953 : size_t column;
5954 : if (r.frontIsEntryPoint()) {
5955 : lineno = r.frontLineNumber();
5956 : column = r.frontColumnNumber();
5957 : } else {
5958 0 : MOZ_ASSERT(flowData[r.frontOffset()].hasSingleEdge());
5959 0 : lineno = flowData[r.frontOffset()].lineno();
5960 0 : column = flowData[r.frontOffset()].column();
5961 : }
5962 0 :
5963 0 : RootedId id(cx_, NameToId(cx_->names().lineNumber));
5964 0 : RootedValue value(cx_, NumberValue(lineno));
5965 : if (!DefineDataProperty(cx_, result_, id, value))
5966 : return false;
5967 0 :
5968 0 : value = NumberValue(column);
5969 0 : if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value))
5970 : return false;
5971 :
5972 0 : // The same entry point test that is used by getAllColumnOffsets.
5973 0 : isEntryPoint = (isEntryPoint &&
5974 : !flowData[offset].hasNoEdges() &&
5975 : (flowData[offset].lineno() != r.frontLineNumber() ||
5976 : flowData[offset].column() != r.frontColumnNumber()));
5977 0 : value.setBoolean(isEntryPoint);
5978 0 : if (!DefineDataProperty(cx_, result_, cx_->names().isEntryPoint, value))
5979 0 : return false;
5980 0 :
5981 0 : return true;
5982 0 : }
5983 :
5984 : ReturnType match(Handle<WasmInstanceObject*> instance) {
5985 0 : size_t lineno;
5986 : size_t column;
5987 : bool found;
5988 0 : if (!instance->instance().debug().getOffsetLocation(cx_, offset_, &found, &lineno, &column))
5989 : return false;
5990 :
5991 : if (!found) {
5992 0 : JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
5993 : return false;
5994 : }
5995 0 :
5996 0 : result_.set(NewBuiltinClassInstance<PlainObject>(cx_));
5997 0 : if (!result_)
5998 : return false;
5999 :
6000 0 : RootedId id(cx_, NameToId(cx_->names().lineNumber));
6001 0 : RootedValue value(cx_, NumberValue(lineno));
6002 : if (!DefineDataProperty(cx_, result_, id, value))
6003 : return false;
6004 0 :
6005 0 : value = NumberValue(column);
6006 0 : if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value))
6007 : return false;
6008 :
6009 0 : value.setBoolean(true);
6010 0 : if (!DefineDataProperty(cx_, result_, cx_->names().isEntryPoint, value))
6011 : return false;
6012 :
6013 0 : return true;
6014 0 : }
6015 : };
6016 :
6017 0 : static bool
6018 : DebuggerScript_getOffsetLocation(JSContext* cx, unsigned argc, Value* vp)
6019 : {
6020 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getOffsetLocation", args, obj, referent);
6021 : if (!args.requireAtLeast(cx, "Debugger.Script.getOffsetLocation", 1))
6022 0 : return false;
6023 : size_t offset;
6024 0 : if (!ScriptOffset(cx, args[0], &offset))
6025 0 : return false;
6026 :
6027 : RootedPlainObject result(cx);
6028 0 : DebuggerScriptGetOffsetLocationMatcher matcher(cx, offset, &result);
6029 : if (!referent.match(matcher))
6030 : return false;
6031 0 :
6032 0 : args.rval().setObject(*result);
6033 0 : return true;
6034 : }
6035 :
6036 0 : static bool
6037 0 : DebuggerScript_getAllOffsets(JSContext* cx, unsigned argc, Value* vp)
6038 : {
6039 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getAllOffsets", args, obj, script);
6040 :
6041 0 : /*
6042 : * First pass: determine which offsets in this script are jump targets and
6043 0 : * which line numbers jump to them.
6044 : */
6045 : FlowGraphSummary flowData(cx);
6046 : if (!flowData.populate(cx, script))
6047 : return false;
6048 :
6049 0 : /* Second pass: build the result array. */
6050 0 : RootedObject result(cx, NewDenseEmptyArray(cx));
6051 : if (!result)
6052 : return false;
6053 : for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) {
6054 0 : if (!r.frontIsEntryPoint())
6055 0 : continue;
6056 :
6057 0 : size_t offset = r.frontOffset();
6058 0 : size_t lineno = r.frontLineNumber();
6059 :
6060 : /* Make a note, if the current instruction is an entry point for the current line. */
6061 0 : if (!flowData[offset].hasNoEdges() && flowData[offset].lineno() != lineno) {
6062 0 : /* Get the offsets array for this line. */
6063 : RootedObject offsets(cx);
6064 : RootedValue offsetsv(cx);
6065 0 :
6066 : RootedId id(cx, INT_TO_JSID(lineno));
6067 0 :
6068 0 : bool found;
6069 : if (!HasOwnProperty(cx, result, id, &found))
6070 0 : return false;
6071 : if (found && !GetProperty(cx, result, result, id, &offsetsv))
6072 : return false;
6073 0 :
6074 0 : if (offsetsv.isObject()) {
6075 0 : offsets = &offsetsv.toObject();
6076 : } else {
6077 : MOZ_ASSERT(offsetsv.isUndefined());
6078 0 :
6079 0 : /*
6080 : * Create an empty offsets array for this line.
6081 0 : * Store it in the result array.
6082 : */
6083 : RootedId id(cx);
6084 : RootedValue v(cx, NumberValue(lineno));
6085 : offsets = NewDenseEmptyArray(cx);
6086 : if (!offsets ||
6087 0 : !ValueToId<CanGC>(cx, v, &id))
6088 0 : {
6089 0 : return false;
6090 0 : }
6091 0 :
6092 : RootedValue value(cx, ObjectValue(*offsets));
6093 0 : if (!DefineDataProperty(cx, result, id, value))
6094 : return false;
6095 : }
6096 0 :
6097 0 : /* Append the current offset to the offsets array. */
6098 0 : if (!NewbornArrayPush(cx, offsets, NumberValue(offset)))
6099 : return false;
6100 : }
6101 : }
6102 0 :
6103 : args.rval().setObject(*result);
6104 : return true;
6105 : }
6106 :
6107 0 : class DebuggerScriptGetAllColumnOffsetsMatcher
6108 0 : {
6109 : JSContext* cx_;
6110 : MutableHandleObject result_;
6111 :
6112 : bool appendColumnOffsetEntry(size_t lineno, size_t column, size_t offset) {
6113 : RootedPlainObject entry(cx_, NewBuiltinClassInstance<PlainObject>(cx_));
6114 : if (!entry)
6115 : return false;
6116 0 :
6117 0 : RootedId id(cx_, NameToId(cx_->names().lineNumber));
6118 0 : RootedValue value(cx_, NumberValue(lineno));
6119 : if (!DefineDataProperty(cx_, entry, id, value))
6120 : return false;
6121 0 :
6122 0 : value = NumberValue(column);
6123 0 : if (!DefineDataProperty(cx_, entry, cx_->names().columnNumber, value))
6124 : return false;
6125 :
6126 0 : id = NameToId(cx_->names().offset);
6127 0 : value = NumberValue(offset);
6128 : if (!DefineDataProperty(cx_, entry, id, value))
6129 : return false;
6130 0 :
6131 0 : return NewbornArrayPush(cx_, result_, ObjectValue(*entry));
6132 0 : }
6133 :
6134 : public:
6135 0 : explicit DebuggerScriptGetAllColumnOffsetsMatcher(JSContext* cx, MutableHandleObject result)
6136 : : cx_(cx), result_(result) { }
6137 : using ReturnType = bool;
6138 : ReturnType match(HandleScript script) {
6139 : /*
6140 0 : * First pass: determine which offsets in this script are jump targets
6141 : * and which positions jump to them.
6142 0 : */
6143 : FlowGraphSummary flowData(cx_);
6144 : if (!flowData.populate(cx_, script))
6145 : return false;
6146 :
6147 0 : /* Second pass: build the result array. */
6148 0 : result_.set(NewDenseEmptyArray(cx_));
6149 : if (!result_)
6150 : return false;
6151 :
6152 0 : for (BytecodeRangeWithPosition r(cx_, script); !r.empty(); r.popFront()) {
6153 0 : size_t lineno = r.frontLineNumber();
6154 : size_t column = r.frontColumnNumber();
6155 : size_t offset = r.frontOffset();
6156 0 :
6157 0 : /*
6158 0 : * Make a note, if the current instruction is an entry point for
6159 0 : * the current position.
6160 : */
6161 : if (r.frontIsEntryPoint() &&
6162 : !flowData[offset].hasNoEdges() &&
6163 : (flowData[offset].lineno() != lineno ||
6164 : flowData[offset].column() != column)) {
6165 0 : if (!appendColumnOffsetEntry(lineno, column, offset))
6166 0 : return false;
6167 0 : }
6168 0 : }
6169 0 : return true;
6170 0 : }
6171 :
6172 : ReturnType match(Handle<WasmInstanceObject*> instance) {
6173 0 : Vector<wasm::ExprLoc> offsets(cx_);
6174 : if (!instance->instance().debug().getAllColumnOffsets(cx_, &offsets))
6175 : return false;
6176 0 :
6177 0 : result_.set(NewDenseEmptyArray(cx_));
6178 0 : if (!result_)
6179 : return false;
6180 :
6181 0 : for (uint32_t i = 0; i < offsets.length(); i++) {
6182 0 : size_t lineno = offsets[i].lineno;
6183 : size_t column = offsets[i].column;
6184 : size_t offset = offsets[i].offset;
6185 0 : if (!appendColumnOffsetEntry(lineno, column, offset))
6186 0 : return false;
6187 0 : }
6188 0 : return true;
6189 0 : }
6190 : };
6191 :
6192 : static bool
6193 : DebuggerScript_getAllColumnOffsets(JSContext* cx, unsigned argc, Value* vp)
6194 : {
6195 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getAllColumnOffsets", args, obj, referent);
6196 :
6197 0 : RootedObject result(cx);
6198 : DebuggerScriptGetAllColumnOffsetsMatcher matcher(cx, &result);
6199 0 : if (!referent.match(matcher))
6200 : return false;
6201 0 :
6202 0 : args.rval().setObject(*result);
6203 0 : return true;
6204 : }
6205 :
6206 0 : class DebuggerScriptGetLineOffsetsMatcher
6207 0 : {
6208 : JSContext* cx_;
6209 : size_t lineno_;
6210 : MutableHandleObject result_;
6211 :
6212 : public:
6213 : explicit DebuggerScriptGetLineOffsetsMatcher(JSContext* cx, size_t lineno, MutableHandleObject result)
6214 : : cx_(cx), lineno_(lineno), result_(result) { }
6215 : using ReturnType = bool;
6216 : ReturnType match(HandleScript script) {
6217 : /*
6218 0 : * First pass: determine which offsets in this script are jump targets and
6219 : * which line numbers jump to them.
6220 0 : */
6221 : FlowGraphSummary flowData(cx_);
6222 : if (!flowData.populate(cx_, script))
6223 : return false;
6224 :
6225 0 : result_.set(NewDenseEmptyArray(cx_));
6226 0 : if (!result_)
6227 : return false;
6228 :
6229 0 : /* Second pass: build the result array. */
6230 0 : for (BytecodeRangeWithPosition r(cx_, script); !r.empty(); r.popFront()) {
6231 : if (!r.frontIsEntryPoint())
6232 : continue;
6233 :
6234 0 : size_t offset = r.frontOffset();
6235 0 :
6236 : /* If the op at offset is an entry point, append offset to result. */
6237 : if (r.frontLineNumber() == lineno_ &&
6238 0 : !flowData[offset].hasNoEdges() &&
6239 : flowData[offset].lineno() != lineno_)
6240 : {
6241 0 : if (!NewbornArrayPush(cx_, result_, NumberValue(offset)))
6242 0 : return false;
6243 0 : }
6244 : }
6245 0 :
6246 0 : return true;
6247 : }
6248 :
6249 : ReturnType match(Handle<WasmInstanceObject*> instance) {
6250 0 : Vector<uint32_t> offsets(cx_);
6251 : if (!instance->instance().debug().getLineOffsets(cx_, lineno_, &offsets))
6252 : return false;
6253 0 :
6254 0 : result_.set(NewDenseEmptyArray(cx_));
6255 0 : if (!result_)
6256 : return false;
6257 :
6258 0 : for (uint32_t i = 0; i < offsets.length(); i++) {
6259 0 : if (!NewbornArrayPush(cx_, result_, NumberValue(offsets[i])))
6260 : return false;
6261 : }
6262 0 : return true;
6263 0 : }
6264 : };
6265 :
6266 : static bool
6267 : DebuggerScript_getLineOffsets(JSContext* cx, unsigned argc, Value* vp)
6268 : {
6269 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getLineOffsets", args, obj, referent);
6270 : if (!args.requireAtLeast(cx, "Debugger.Script.getLineOffsets", 1))
6271 0 : return false;
6272 :
6273 0 : /* Parse lineno argument. */
6274 0 : RootedValue linenoValue(cx, args[0]);
6275 : size_t lineno;
6276 : if (!ToNumber(cx, &linenoValue))
6277 : return false;
6278 0 : {
6279 : double d = linenoValue.toNumber();
6280 0 : lineno = size_t(d);
6281 : if (lineno != d) {
6282 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_LINE);
6283 0 : return false;
6284 0 : }
6285 0 : }
6286 0 :
6287 0 : RootedObject result(cx);
6288 : DebuggerScriptGetLineOffsetsMatcher matcher(cx, lineno, &result);
6289 : if (!referent.match(matcher))
6290 : return false;
6291 0 :
6292 0 : args.rval().setObject(*result);
6293 0 : return true;
6294 : }
6295 :
6296 0 : bool
6297 0 : Debugger::observesFrame(AbstractFramePtr frame) const
6298 : {
6299 : if (frame.isWasmDebugFrame())
6300 : return observesWasm(frame.wasmInstance());
6301 0 :
6302 : return observesScript(frame.script());
6303 0 : }
6304 0 :
6305 : bool
6306 0 : Debugger::observesFrame(const FrameIter& iter) const
6307 : {
6308 : // Skip frames not yet fully initialized during their prologue.
6309 : if (iter.isInterp() && iter.isFunctionFrame()) {
6310 0 : const Value& thisVal = iter.interpFrame()->thisArgument();
6311 : if (thisVal.isMagic() && thisVal.whyMagic() == JS_IS_CONSTRUCTING)
6312 : return false;
6313 0 : }
6314 0 : if (iter.isWasm()) {
6315 0 : // Skip frame of wasm instances we cannot observe.
6316 : if (!iter.wasmDebugEnabled())
6317 : return false;
6318 0 : return observesWasm(iter.wasmInstance());
6319 : }
6320 0 : return observesScript(iter.script());
6321 : }
6322 0 :
6323 : bool
6324 0 : Debugger::observesScript(JSScript* script) const
6325 : {
6326 : if (!enabled)
6327 : return false;
6328 0 : // Don't ever observe self-hosted scripts: the Debugger API can break
6329 : // self-hosted invariants.
6330 0 : return observesGlobal(&script->global()) && !script->selfHosted();
6331 : }
6332 :
6333 : bool
6334 0 : Debugger::observesWasm(wasm::Instance* instance) const
6335 : {
6336 : if (!enabled || !instance->debugEnabled())
6337 : return false;
6338 0 : return observesGlobal(&instance->object()->global());
6339 : }
6340 0 :
6341 : /* static */ bool
6342 0 : Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to,
6343 : ScriptFrameIter& iter)
6344 : {
6345 : auto removeFromDebuggerFramesOnExit = MakeScopeExit([&] {
6346 0 : // Remove any remaining old entries on exit, as the 'from' frame will
6347 : // be gone. This is only done in the failure case. On failure, the
6348 : // removeToDebuggerFramesOnExit lambda below will rollback any frames
6349 0 : // that were replaced, resulting in !frameMaps(to). On success, the
6350 : // range will be empty, as all from Frame.Debugger instances will have
6351 : // been removed.
6352 : MOZ_ASSERT_IF(inFrameMaps(to), !inFrameMaps(from));
6353 : removeFromFrameMapsAndClearBreakpointsIn(cx, from);
6354 :
6355 : // Rekey missingScopes to maintain Debugger.Environment identity and
6356 0 : // forward liveScopes to point to the new frame.
6357 0 : DebugEnvironments::forwardLiveFrame(cx, from, to);
6358 : });
6359 :
6360 : // Forward live Debugger.Frame objects.
6361 0 : Rooted<DebuggerFrameVector> frames(cx, DebuggerFrameVector(cx));
6362 0 : if (!getDebuggerFrames(from, &frames)) {
6363 : // An OOM here means that all Debuggers' frame maps still contain
6364 : // entries for 'from' and no entries for 'to'. Since the 'from' frame
6365 0 : // will be gone, they are removed by removeFromDebuggerFramesOnExit
6366 0 : // above.
6367 : return false;
6368 : }
6369 :
6370 : // If during the loop below we hit an OOM, we must also rollback any of
6371 : // the frames that were successfully replaced. For OSR frames, OOM here
6372 : // means those frames will pop from the OSR trampoline, which does not
6373 : // call Debugger::onLeaveFrame.
6374 : auto removeToDebuggerFramesOnExit = MakeScopeExit([&] {
6375 : removeFromFrameMapsAndClearBreakpointsIn(cx, to);
6376 : });
6377 :
6378 : for (size_t i = 0; i < frames.length(); i++) {
6379 0 : HandleDebuggerFrame frameobj = frames[i];
6380 0 : Debugger* dbg = Debugger::fromChildJSObject(frameobj);
6381 :
6382 0 : // Update frame object's ScriptFrameIter::data pointer.
6383 0 : frameobj->freeFrameIterData(cx->runtime()->defaultFreeOp());
6384 0 : ScriptFrameIter::Data* data = iter.copyData();
6385 : if (!data) {
6386 : // An OOM here means that some Debuggers' frame maps may still
6387 0 : // contain entries for 'from' and some Debuggers' frame maps may
6388 0 : // also contain entries for 'to'. Thus both
6389 0 : // removeFromDebuggerFramesOnExit and
6390 : // removeToDebuggerFramesOnExit must both run.
6391 : //
6392 : // The current frameobj in question is still in its Debugger's
6393 : // frame map keyed by 'from', so it will be covered by
6394 : // removeFromDebuggerFramesOnExit.
6395 : return false;
6396 : }
6397 : frameobj->setPrivate(data);
6398 :
6399 0 : // Remove old frame.
6400 : dbg->frames.remove(from);
6401 0 :
6402 : // Add the frame object with |to| as key.
6403 : if (!dbg->frames.putNew(to, frameobj)) {
6404 0 : // This OOM is subtle. At this point, both
6405 : // removeFromDebuggerFramesOnExit and removeToDebuggerFramesOnExit
6406 : // must both run for the same reason given above.
6407 0 : //
6408 : // The difference is that the current frameobj is no longer in its
6409 : // Debugger's frame map, so it will not be cleaned up by neither
6410 : // lambda. Manually clean it up here.
6411 : FreeOp* fop = cx->runtime()->defaultFreeOp();
6412 : frameobj->freeFrameIterData(fop);
6413 : DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, to, frameobj);
6414 :
6415 0 : ReportOutOfMemory(cx);
6416 0 : return false;
6417 0 : }
6418 : }
6419 0 :
6420 0 : // All frames successfuly replaced, cancel the rollback.
6421 : removeToDebuggerFramesOnExit.release();
6422 :
6423 : return true;
6424 : }
6425 0 :
6426 : /* static */ bool
6427 0 : Debugger::inFrameMaps(AbstractFramePtr frame)
6428 : {
6429 : bool foundAny = false;
6430 : forEachDebuggerFrame(frame, [&](DebuggerFrame* frameobj) { foundAny = true; });
6431 0 : return foundAny;
6432 : }
6433 15048 :
6434 15048 : /* static */ void
6435 0 : Debugger::removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame)
6436 : {
6437 : forEachDebuggerFrame(frame, [&](DebuggerFrame* frameobj) {
6438 : Debugger* dbg = Debugger::fromChildJSObject(frameobj);
6439 0 :
6440 : FreeOp* fop = cx->runtime()->defaultFreeOp();
6441 0 : frameobj->freeFrameIterData(fop);
6442 0 : DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj);
6443 :
6444 0 : dbg->frames.remove(frame);
6445 0 : });
6446 0 :
6447 : /*
6448 0 : * If this is an eval frame, then from the debugger's perspective the
6449 0 : * script is about to be destroyed. Remove any breakpoints in it.
6450 : */
6451 : if (frame.isEvalFrame()) {
6452 : RootedScript script(cx, frame.script());
6453 : script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), nullptr, nullptr);
6454 : }
6455 0 : }
6456 0 :
6457 0 : /* static */ bool
6458 : Debugger::handleBaselineOsr(JSContext* cx, InterpreterFrame* from, jit::BaselineFrame* to)
6459 0 : {
6460 : ScriptFrameIter iter(cx);
6461 : MOZ_ASSERT(iter.abstractFramePtr() == to);
6462 0 : return replaceFrameGuts(cx, from, to, iter);
6463 : }
6464 0 :
6465 0 : /* static */ bool
6466 0 : Debugger::handleIonBailout(JSContext* cx, jit::RematerializedFrame* from, jit::BaselineFrame* to)
6467 : {
6468 : // When we return to a bailed-out Ion real frame, we must update all
6469 : // Debugger.Frames that refer to its inline frames. However, since we
6470 0 : // can't pop individual inline frames off the stack (we can only pop the
6471 : // real frame that contains them all, as a unit), we cannot assume that
6472 : // the frame we're dealing with is the top frame. Advance the iterator
6473 : // across any inlined frames younger than |to|, the baseline frame
6474 : // reconstructed during bailout from the Ion frame corresponding to
6475 : // |from|.
6476 : ScriptFrameIter iter(cx);
6477 : while (iter.abstractFramePtr() != to)
6478 : ++iter;
6479 : return replaceFrameGuts(cx, from, to, iter);
6480 0 : }
6481 0 :
6482 : /* static */ void
6483 0 : Debugger::handleUnrecoverableIonBailoutError(JSContext* cx, jit::RematerializedFrame* frame)
6484 : {
6485 : // Ion bailout can fail due to overrecursion. In such cases we cannot
6486 : // honor any further Debugger hooks on the frame, and need to ensure that
6487 0 : // its Debugger.Frame entry is cleaned up.
6488 : removeFromFrameMapsAndClearBreakpointsIn(cx, frame);
6489 : }
6490 :
6491 : /* static */ void
6492 0 : Debugger::propagateForcedReturn(JSContext* cx, AbstractFramePtr frame, HandleValue rval)
6493 0 : {
6494 : // Invoking the interrupt handler is considered a step and invokes the
6495 : // youngest frame's onStep handler, if any. However, we cannot handle
6496 0 : // { return: ... } resumption values straightforwardly from the interrupt
6497 : // handler. Instead, we set the intended return value in the frame's rval
6498 : // slot and set the propagating-forced-return flag on the JSContext.
6499 : //
6500 : // The interrupt handler then returns false with no exception set,
6501 : // signaling an uncatchable exception. In the exception handlers, we then
6502 : // check for the special propagating-forced-return flag.
6503 : MOZ_ASSERT(!cx->isExceptionPending());
6504 : cx->setPropagatingForcedReturn();
6505 : frame.setReturnValue(rval);
6506 : }
6507 0 :
6508 0 : struct DebuggerScriptSetBreakpointMatcher
6509 0 : {
6510 0 : JSContext* cx_;
6511 : Debugger* dbg_;
6512 0 : size_t offset_;
6513 : RootedObject handler_;
6514 :
6515 : public:
6516 : explicit DebuggerScriptSetBreakpointMatcher(JSContext* cx, Debugger* dbg, size_t offset, HandleObject handler)
6517 : : cx_(cx), dbg_(dbg), offset_(offset), handler_(cx, handler)
6518 : { }
6519 :
6520 : using ReturnType = bool;
6521 0 :
6522 : ReturnType match(HandleScript script) {
6523 : if (!dbg_->observesScript(script)) {
6524 : JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_DEBUGGING);
6525 : return false;
6526 0 : }
6527 0 :
6528 0 : if (!EnsureScriptOffsetIsValid(cx_, script, offset_))
6529 0 : return false;
6530 :
6531 : // Ensure observability *before* setting the breakpoint. If the script is
6532 0 : // not already a debuggee, trying to ensure observability after setting
6533 : // the breakpoint (and thus marking the script as a debuggee) will skip
6534 : // actually ensuring observability.
6535 : if (!dbg_->ensureExecutionObservabilityOfScript(cx_, script))
6536 : return false;
6537 :
6538 : jsbytecode* pc = script->offsetToPC(offset_);
6539 0 : BreakpointSite* site = script->getOrCreateBreakpointSite(cx_, pc);
6540 : if (!site)
6541 : return false;
6542 0 : site->inc(cx_->runtime()->defaultFreeOp());
6543 0 : if (cx_->zone()->new_<Breakpoint>(dbg_, site, handler_))
6544 0 : return true;
6545 : site->dec(cx_->runtime()->defaultFreeOp());
6546 0 : site->destroyIfEmpty(cx_->runtime()->defaultFreeOp());
6547 0 : return false;
6548 : }
6549 0 :
6550 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
6551 0 : wasm::Instance& instance = wasmInstance->instance();
6552 : if (!instance.debug().hasBreakpointTrapAtOffset(offset_)) {
6553 : JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
6554 0 : return false;
6555 0 : }
6556 0 : WasmBreakpointSite* site = instance.debug().getOrCreateBreakpointSite(cx_, offset_);
6557 0 : if (!site)
6558 0 : return false;
6559 : site->inc(cx_->runtime()->defaultFreeOp());
6560 0 : if (cx_->zone()->new_<WasmBreakpoint>(dbg_, site, handler_, instance.object()))
6561 0 : return true;
6562 : site->dec(cx_->runtime()->defaultFreeOp());
6563 0 : site->destroyIfEmpty(cx_->runtime()->defaultFreeOp());
6564 0 : return false;
6565 : }
6566 0 : };
6567 0 :
6568 0 : static bool
6569 : DebuggerScript_setBreakpoint(JSContext* cx, unsigned argc, Value* vp)
6570 : {
6571 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "setBreakpoint", args, obj, referent);
6572 : if (!args.requireAtLeast(cx, "Debugger.Script.setBreakpoint", 2))
6573 0 : return false;
6574 : Debugger* dbg = Debugger::fromChildJSObject(obj);
6575 0 :
6576 0 : size_t offset;
6577 : if (!ScriptOffset(cx, args[0], &offset))
6578 0 : return false;
6579 :
6580 : RootedObject handler(cx, NonNullObject(cx, args[1]));
6581 0 : if (!handler)
6582 : return false;
6583 :
6584 0 : DebuggerScriptSetBreakpointMatcher matcher(cx, dbg, offset, handler);
6585 0 : if (!referent.match(matcher))
6586 : return false;
6587 : args.rval().setUndefined();
6588 0 : return true;
6589 0 : }
6590 :
6591 0 : static bool
6592 0 : DebuggerScript_getBreakpoints(JSContext* cx, unsigned argc, Value* vp)
6593 : {
6594 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getBreakpoints", args, obj, script);
6595 : Debugger* dbg = Debugger::fromChildJSObject(obj);
6596 0 :
6597 : jsbytecode* pc;
6598 0 : if (args.length() > 0) {
6599 0 : size_t offset;
6600 : if (!ScriptOffset(cx, args[0], &offset) || !EnsureScriptOffsetIsValid(cx, script, offset))
6601 : return false;
6602 0 : pc = script->offsetToPC(offset);
6603 : } else {
6604 0 : pc = nullptr;
6605 0 : }
6606 0 :
6607 : RootedObject arr(cx, NewDenseEmptyArray(cx));
6608 : if (!arr)
6609 : return false;
6610 :
6611 0 : for (unsigned i = 0; i < script->length(); i++) {
6612 0 : BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
6613 : if (!site)
6614 : continue;
6615 0 : MOZ_ASSERT(site->type() == BreakpointSite::Type::JS);
6616 0 : if (!pc || site->asJS()->pc == pc) {
6617 0 : for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
6618 : if (bp->debugger == dbg &&
6619 0 : !NewbornArrayPush(cx, arr, ObjectValue(*bp->getHandler())))
6620 0 : {
6621 0 : return false;
6622 0 : }
6623 0 : }
6624 : }
6625 : }
6626 : args.rval().setObject(*arr);
6627 : return true;
6628 : }
6629 :
6630 0 : class DebuggerScriptClearBreakpointMatcher
6631 0 : {
6632 : JSContext* cx_;
6633 : Debugger* dbg_;
6634 : JSObject* handler_;
6635 :
6636 : public:
6637 : explicit DebuggerScriptClearBreakpointMatcher(JSContext* cx, Debugger* dbg, JSObject* handler) : cx_(cx), dbg_(dbg), handler_(handler) { }
6638 : using ReturnType = bool;
6639 :
6640 : ReturnType match(HandleScript script) {
6641 0 : script->clearBreakpointsIn(cx_->runtime()->defaultFreeOp(), dbg_, handler_);
6642 : return true;
6643 : }
6644 0 :
6645 0 : ReturnType match(Handle<WasmInstanceObject*> instance) {
6646 0 : return instance->instance().debug().clearBreakpointsIn(cx_, instance, dbg_, handler_);
6647 : }
6648 : };
6649 0 :
6650 0 :
6651 : static bool
6652 : DebuggerScript_clearBreakpoint(JSContext* cx, unsigned argc, Value* vp)
6653 : {
6654 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "clearBreakpoint", args, obj, referent);
6655 : if (!args.requireAtLeast(cx, "Debugger.Script.clearBreakpoint", 1))
6656 0 : return false;
6657 : Debugger* dbg = Debugger::fromChildJSObject(obj);
6658 0 :
6659 0 : JSObject* handler = NonNullObject(cx, args[0]);
6660 : if (!handler)
6661 0 : return false;
6662 :
6663 0 : DebuggerScriptClearBreakpointMatcher matcher(cx, dbg, handler);
6664 0 : if (!referent.match(matcher))
6665 : return false;
6666 :
6667 0 : args.rval().setUndefined();
6668 0 : return true;
6669 : }
6670 :
6671 0 : static bool
6672 0 : DebuggerScript_clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp)
6673 : {
6674 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "clearAllBreakpoints", args, obj, referent);
6675 : Debugger* dbg = Debugger::fromChildJSObject(obj);
6676 0 : DebuggerScriptClearBreakpointMatcher matcher(cx, dbg, nullptr);
6677 : if (!referent.match(matcher))
6678 0 : return false;
6679 0 : args.rval().setUndefined();
6680 0 : return true;
6681 0 : }
6682 :
6683 0 : class DebuggerScriptIsInCatchScopeMatcher
6684 0 : {
6685 : JSContext* cx_;
6686 : size_t offset_;
6687 : bool isInCatch_;
6688 :
6689 : public:
6690 : explicit DebuggerScriptIsInCatchScopeMatcher(JSContext* cx, size_t offset) : cx_(cx), offset_(offset) { }
6691 : using ReturnType = bool;
6692 :
6693 : inline bool isInCatch() const { return isInCatch_; }
6694 0 :
6695 : ReturnType match(HandleScript script) {
6696 : if (!EnsureScriptOffsetIsValid(cx_, script, offset_))
6697 : return false;
6698 :
6699 0 : /*
6700 0 : * Try note ranges are relative to the mainOffset of the script, so adjust
6701 : * offset accordingly.
6702 : */
6703 : size_t offset = offset_ - script->mainOffset();
6704 :
6705 : if (script->hasTrynotes()) {
6706 : JSTryNote* tnBegin = script->trynotes()->vector;
6707 0 : JSTryNote* tnEnd = tnBegin + script->trynotes()->length;
6708 : while (tnBegin != tnEnd) {
6709 0 : if (tnBegin->start <= offset &&
6710 0 : offset <= tnBegin->start + tnBegin->length &&
6711 0 : tnBegin->kind == JSTRY_CATCH)
6712 0 : {
6713 0 : isInCatch_ = true;
6714 0 : return true;
6715 0 : }
6716 : ++tnBegin;
6717 0 : }
6718 0 : }
6719 : isInCatch_ = false;
6720 0 : return true;
6721 : }
6722 :
6723 0 : ReturnType match(Handle<WasmInstanceObject*> instance) {
6724 0 : isInCatch_ = false;
6725 : return true;
6726 : }
6727 : };
6728 0 :
6729 : static bool
6730 : DebuggerScript_isInCatchScope(JSContext* cx, unsigned argc, Value* vp)
6731 : {
6732 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "isInCatchScope", args, obj, referent);
6733 : if (!args.requireAtLeast(cx, "Debugger.Script.isInCatchScope", 1))
6734 0 : return false;
6735 :
6736 0 : size_t offset;
6737 0 : if (!ScriptOffset(cx, args[0], &offset))
6738 : return false;
6739 :
6740 : DebuggerScriptIsInCatchScopeMatcher matcher(cx, offset);
6741 0 : if (!referent.match(matcher))
6742 : return false;
6743 : args.rval().setBoolean(matcher.isInCatch());
6744 0 : return true;
6745 0 : }
6746 :
6747 0 : static bool
6748 0 : DebuggerScript_getOffsetsCoverage(JSContext* cx, unsigned argc, Value* vp)
6749 : {
6750 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getOffsetsCoverage", args, obj, script);
6751 :
6752 0 : // If the script has no coverage information, then skip this and return null
6753 : // instead.
6754 0 : if (!script->hasScriptCounts()) {
6755 : args.rval().setNull();
6756 : return true;
6757 : }
6758 0 :
6759 0 : ScriptCounts* sc = &script->getScriptCounts();
6760 0 :
6761 : // If the main ever got visited, then assume that any code before main got
6762 : // visited once.
6763 0 : uint64_t hits = 0;
6764 : const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(script->main()));
6765 : if (counts->numExec())
6766 : hits = 1;
6767 0 :
6768 0 : // Build an array of objects which are composed of 4 properties:
6769 0 : // - offset PC offset of the current opcode.
6770 0 : // - lineNumber Line of the current opcode.
6771 : // - columnNumber Column of the current opcode.
6772 : // - count Number of times the instruction got executed.
6773 : RootedObject result(cx, NewDenseEmptyArray(cx));
6774 : if (!result)
6775 : return false;
6776 :
6777 0 : RootedId offsetId(cx, NameToId(cx->names().offset));
6778 0 : RootedId lineNumberId(cx, NameToId(cx->names().lineNumber));
6779 : RootedId columnNumberId(cx, NameToId(cx->names().columnNumber));
6780 : RootedId countId(cx, NameToId(cx->names().count));
6781 0 :
6782 0 : RootedObject item(cx);
6783 0 : RootedValue offsetValue(cx);
6784 0 : RootedValue lineNumberValue(cx);
6785 : RootedValue columnNumberValue(cx);
6786 0 : RootedValue countValue(cx);
6787 0 :
6788 0 : // Iterate linearly over the bytecode.
6789 0 : for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) {
6790 0 : size_t offset = r.frontOffset();
6791 :
6792 : // The beginning of each non-branching sequences of instruction set the
6793 0 : // number of execution of the current instruction and any following
6794 0 : // instruction.
6795 : counts = sc->maybeGetPCCounts(offset);
6796 : if (counts)
6797 : hits = counts->numExec();
6798 :
6799 0 : offsetValue.setNumber(double(offset));
6800 0 : lineNumberValue.setNumber(double(r.frontLineNumber()));
6801 0 : columnNumberValue.setNumber(double(r.frontColumnNumber()));
6802 : countValue.setNumber(double(hits));
6803 0 :
6804 0 : // Create a new object with the offset, line number, column number, the
6805 0 : // number of hit counts, and append it to the array.
6806 0 : item = NewObjectWithGivenProto<PlainObject>(cx, nullptr);
6807 : if (!item ||
6808 : !DefineDataProperty(cx, item, offsetId, offsetValue) ||
6809 : !DefineDataProperty(cx, item, lineNumberId, lineNumberValue) ||
6810 0 : !DefineDataProperty(cx, item, columnNumberId, columnNumberValue) ||
6811 0 : !DefineDataProperty(cx, item, countId, countValue) ||
6812 0 : !NewbornArrayPush(cx, result, ObjectValue(*item)))
6813 0 : {
6814 0 : return false;
6815 0 : }
6816 0 :
6817 : // If the current instruction has thrown, then decrement the hit counts
6818 0 : // with the number of throws.
6819 : counts = sc->maybeGetThrowCounts(offset);
6820 : if (counts)
6821 : hits -= counts->numExec();
6822 : }
6823 0 :
6824 0 : args.rval().setObject(*result);
6825 0 : return true;
6826 : }
6827 :
6828 0 : static bool
6829 0 : DebuggerScript_construct(JSContext* cx, unsigned argc, Value* vp)
6830 : {
6831 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
6832 : "Debugger.Script");
6833 0 : return false;
6834 : }
6835 :
6836 0 : static const JSPropertySpec DebuggerScript_properties[] = {
6837 0 : JS_PSG("isGeneratorFunction", DebuggerScript_getIsGeneratorFunction, 0),
6838 : JS_PSG("isAsyncFunction", DebuggerScript_getIsAsyncFunction, 0),
6839 : JS_PSG("displayName", DebuggerScript_getDisplayName, 0),
6840 : JS_PSG("url", DebuggerScript_getUrl, 0),
6841 : JS_PSG("startLine", DebuggerScript_getStartLine, 0),
6842 : JS_PSG("lineCount", DebuggerScript_getLineCount, 0),
6843 : JS_PSG("source", DebuggerScript_getSource, 0),
6844 : JS_PSG("sourceStart", DebuggerScript_getSourceStart, 0),
6845 : JS_PSG("sourceLength", DebuggerScript_getSourceLength, 0),
6846 : JS_PSG("global", DebuggerScript_getGlobal, 0),
6847 : JS_PSG("format", DebuggerScript_getFormat, 0),
6848 : JS_PS_END
6849 : };
6850 :
6851 : static const JSFunctionSpec DebuggerScript_methods[] = {
6852 : JS_FN("getChildScripts", DebuggerScript_getChildScripts, 0, 0),
6853 : JS_FN("getAllOffsets", DebuggerScript_getAllOffsets, 0, 0),
6854 : JS_FN("getAllColumnOffsets", DebuggerScript_getAllColumnOffsets, 0, 0),
6855 : JS_FN("getLineOffsets", DebuggerScript_getLineOffsets, 1, 0),
6856 : JS_FN("getOffsetLocation", DebuggerScript_getOffsetLocation, 0, 0),
6857 : JS_FN("setBreakpoint", DebuggerScript_setBreakpoint, 2, 0),
6858 : JS_FN("getBreakpoints", DebuggerScript_getBreakpoints, 1, 0),
6859 : JS_FN("clearBreakpoint", DebuggerScript_clearBreakpoint, 1, 0),
6860 : JS_FN("clearAllBreakpoints", DebuggerScript_clearAllBreakpoints, 0, 0),
6861 : JS_FN("isInCatchScope", DebuggerScript_isInCatchScope, 1, 0),
6862 : JS_FN("getOffsetsCoverage", DebuggerScript_getOffsetsCoverage, 0, 0),
6863 : JS_FS_END
6864 : };
6865 :
6866 :
6867 : /*** Debugger.Source *****************************************************************************/
6868 :
6869 : // For internal use only.
6870 : static inline NativeObject*
6871 : GetSourceReferentRawObject(JSObject* obj)
6872 : {
6873 : MOZ_ASSERT(obj->getClass() == &DebuggerSource_class);
6874 : return static_cast<NativeObject*>(obj->as<NativeObject>().getPrivate());
6875 0 : }
6876 :
6877 0 : static inline DebuggerSourceReferent
6878 0 : GetSourceReferent(JSObject* obj)
6879 : {
6880 : if (NativeObject* referent = GetSourceReferentRawObject(obj)) {
6881 : if (referent->is<ScriptSourceObject>())
6882 0 : return AsVariant(&referent->as<ScriptSourceObject>());
6883 : return AsVariant(&referent->as<WasmInstanceObject>());
6884 0 : }
6885 0 : return AsVariant(static_cast<ScriptSourceObject*>(nullptr));
6886 0 : }
6887 0 :
6888 : void
6889 0 : DebuggerSource_trace(JSTracer* trc, JSObject* obj)
6890 : {
6891 : /*
6892 : * There is a barrier on private pointers, so the Unbarriered marking
6893 0 : * is okay.
6894 : */
6895 : if (JSObject *referent = GetSourceReferentRawObject(obj)) {
6896 : TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &referent,
6897 : "Debugger.Source referent");
6898 : obj->as<NativeObject>().setPrivateUnbarriered(referent);
6899 0 : }
6900 : }
6901 0 :
6902 0 : class SetDebuggerSourcePrivateMatcher
6903 : {
6904 0 : NativeObject* obj_;
6905 : public:
6906 : explicit SetDebuggerSourcePrivateMatcher(NativeObject* obj) : obj_(obj) { }
6907 : using ReturnType = void;
6908 : ReturnType match(HandleScriptSourceObject source) { obj_->setPrivateGCThing(source); }
6909 : ReturnType match(Handle<WasmInstanceObject*> instance) { obj_->setPrivateGCThing(instance); }
6910 0 : };
6911 :
6912 0 : NativeObject*
6913 0 : Debugger::newDebuggerSource(JSContext* cx, Handle<DebuggerSourceReferent> referent)
6914 : {
6915 : assertSameCompartment(cx, object.get());
6916 :
6917 0 : RootedObject proto(cx, &object->getReservedSlot(JSSLOT_DEBUG_SOURCE_PROTO).toObject());
6918 : MOZ_ASSERT(proto);
6919 0 : NativeObject* sourceobj = NewNativeObjectWithGivenProto(cx, &DebuggerSource_class,
6920 : proto, TenuredObject);
6921 0 : if (!sourceobj)
6922 0 : return nullptr;
6923 0 : sourceobj->setReservedSlot(JSSLOT_DEBUGSOURCE_OWNER, ObjectValue(*object));
6924 0 : SetDebuggerSourcePrivateMatcher matcher(sourceobj);
6925 0 : referent.match(matcher);
6926 :
6927 0 : return sourceobj;
6928 0 : }
6929 0 :
6930 : JSObject*
6931 0 : Debugger::wrapVariantReferent(JSContext* cx, Handle<DebuggerSourceReferent> referent)
6932 : {
6933 : JSObject* obj;
6934 : if (referent.is<ScriptSourceObject*>()) {
6935 0 : Handle<ScriptSourceObject*> untaggedReferent = referent.template as<ScriptSourceObject*>();
6936 : Rooted<CrossCompartmentKey> key(cx, CrossCompartmentKey(object, untaggedReferent,
6937 : CrossCompartmentKey::DebuggerObjectKind::DebuggerSource));
6938 0 : obj = wrapVariantReferent<DebuggerSourceReferent, ScriptSourceObject*, SourceWeakMap>(
6939 0 : cx, sources, key, referent);
6940 0 : } else {
6941 0 : Handle<WasmInstanceObject*> untaggedReferent = referent.template as<WasmInstanceObject*>();
6942 0 : Rooted<CrossCompartmentKey> key(cx, CrossCompartmentKey(object, untaggedReferent,
6943 0 : CrossCompartmentKey::DebuggerObjectKind::DebuggerWasmSource));
6944 : obj = wrapVariantReferent<DebuggerSourceReferent, WasmInstanceObject*, WasmInstanceWeakMap>(
6945 0 : cx, wasmInstanceSources, key, referent);
6946 0 : }
6947 0 : MOZ_ASSERT_IF(obj, GetSourceReferent(obj) == referent);
6948 0 : return obj;
6949 0 : }
6950 :
6951 0 : JSObject*
6952 0 : Debugger::wrapSource(JSContext* cx, HandleScriptSourceObject source)
6953 : {
6954 : Rooted<DebuggerSourceReferent> referent(cx, source.get());
6955 : return wrapVariantReferent(cx, referent);
6956 0 : }
6957 :
6958 0 : JSObject*
6959 0 : Debugger::wrapWasmSource(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance)
6960 : {
6961 : Rooted<DebuggerSourceReferent> referent(cx, wasmInstance.get());
6962 : return wrapVariantReferent(cx, referent);
6963 0 : }
6964 :
6965 0 : static bool
6966 0 : DebuggerSource_construct(JSContext* cx, unsigned argc, Value* vp)
6967 : {
6968 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
6969 : "Debugger.Source");
6970 0 : return false;
6971 : }
6972 :
6973 0 : static NativeObject*
6974 0 : DebuggerSource_check(JSContext* cx, HandleValue thisv, const char* fnname)
6975 : {
6976 : JSObject* thisobj = NonNullObject(cx, thisv);
6977 : if (!thisobj)
6978 0 : return nullptr;
6979 : if (thisobj->getClass() != &DebuggerSource_class) {
6980 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
6981 0 : "Debugger.Source", fnname, thisobj->getClass()->name);
6982 : return nullptr;
6983 0 : }
6984 :
6985 0 : NativeObject* nthisobj = &thisobj->as<NativeObject>();
6986 0 :
6987 : if (!GetSourceReferentRawObject(thisobj)) {
6988 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
6989 0 : "Debugger.Source", fnname, "prototype object");
6990 : return nullptr;
6991 0 : }
6992 :
6993 0 : return nthisobj;
6994 0 : }
6995 :
6996 : template <typename ReferentT>
6997 : static NativeObject*
6998 : DebuggerSource_checkThis(JSContext* cx, const CallArgs& args, const char* fnname,
6999 : const char* refname)
7000 : {
7001 : NativeObject* thisobj = DebuggerSource_check(cx, args.thisv(), fnname);
7002 0 : if (!thisobj)
7003 : return nullptr;
7004 :
7005 0 : if (!GetSourceReferent(thisobj).is<ReferentT>()) {
7006 0 : ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, args.thisv(), nullptr,
7007 : refname);
7008 : return nullptr;
7009 0 : }
7010 0 :
7011 : return thisobj;
7012 : }
7013 0 :
7014 : #define THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, fnname, args, obj, referent) \
7015 : CallArgs args = CallArgsFromVp(argc, vp); \
7016 : RootedNativeObject obj(cx, DebuggerSource_check(cx, args.thisv(), fnname)); \
7017 : if (!obj) \
7018 : return false; \
7019 : Rooted<DebuggerSourceReferent> referent(cx, GetSourceReferent(obj))
7020 :
7021 : #define THIS_DEBUGSOURCE_SOURCE(cx, argc, vp, fnname, args, obj, sourceObject) \
7022 : CallArgs args = CallArgsFromVp(argc, vp); \
7023 : RootedNativeObject obj(cx, \
7024 : DebuggerSource_checkThis<ScriptSourceObject*>(cx, args, fnname, \
7025 : "a JS source")); \
7026 : if (!obj) \
7027 : return false; \
7028 : RootedScriptSourceObject sourceObject(cx, GetSourceReferent(obj).as<ScriptSourceObject*>())
7029 :
7030 : class DebuggerSourceGetTextMatcher
7031 : {
7032 : JSContext* cx_;
7033 :
7034 : public:
7035 : explicit DebuggerSourceGetTextMatcher(JSContext* cx) : cx_(cx) { }
7036 :
7037 : using ReturnType = JSString*;
7038 :
7039 : ReturnType match(HandleScriptSourceObject sourceObject) {
7040 0 : ScriptSource* ss = sourceObject->source();
7041 : bool hasSourceData = ss->hasSourceData();
7042 : if (!ss->hasSourceData() && !JSScript::loadSource(cx_, ss, &hasSourceData))
7043 : return nullptr;
7044 0 : if (!hasSourceData)
7045 0 : return NewStringCopyZ<CanGC>(cx_, "[no source]");
7046 0 :
7047 0 : if (ss->isFunctionBody())
7048 : return ss->functionBodyString(cx_);
7049 0 :
7050 0 : return ss->substring(cx_, 0, ss->length());
7051 : }
7052 0 :
7053 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7054 : if (wasmInstance->instance().debug().maybeBytecode() &&
7055 0 : wasmInstance->instance().debug().binarySource())
7056 : {
7057 : return NewStringCopyZ<CanGC>(cx_, "[wasm]");
7058 0 : }
7059 0 : return wasmInstance->instance().debug().createText(cx_);
7060 0 : }
7061 : };
7062 0 :
7063 : static bool
7064 0 : DebuggerSource_getText(JSContext* cx, unsigned argc, Value* vp)
7065 : {
7066 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get text)", args, obj, referent);
7067 : Value textv = obj->getReservedSlot(JSSLOT_DEBUGSOURCE_TEXT);
7068 : if (!textv.isUndefined()) {
7069 0 : MOZ_ASSERT(textv.isString());
7070 : args.rval().set(textv);
7071 0 : return true;
7072 0 : }
7073 0 :
7074 0 : DebuggerSourceGetTextMatcher matcher(cx);
7075 0 : JSString* str = referent.match(matcher);
7076 0 : if (!str)
7077 : return false;
7078 :
7079 0 : args.rval().setString(str);
7080 0 : obj->setReservedSlot(JSSLOT_DEBUGSOURCE_TEXT, args.rval());
7081 0 : return true;
7082 : }
7083 :
7084 0 : static bool
7085 0 : DebuggerSource_getBinary(JSContext* cx, unsigned argc, Value* vp)
7086 0 : {
7087 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get binary)", args, obj, referent);
7088 :
7089 : if (!referent.is<WasmInstanceObject*>()) {
7090 0 : ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, args.thisv(), nullptr,
7091 : "a wasm source");
7092 0 : return false;
7093 : }
7094 0 :
7095 0 : RootedWasmInstanceObject wasmInstance(cx, referent.as<WasmInstanceObject*>());
7096 : if (!wasmInstance->instance().debug().binarySource()) {
7097 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
7098 0 : JSMSG_DEBUG_NO_BINARY_SOURCE);
7099 : return false;
7100 : }
7101 0 :
7102 0 : auto bytecode = wasmInstance->instance().debug().maybeBytecode();
7103 : size_t arrLength = bytecode ? bytecode->length() : 0;
7104 0 : RootedObject arr(cx, JS_NewUint8Array(cx, arrLength));
7105 0 : if (!arr)
7106 : return false;
7107 : if (bytecode)
7108 0 : memcpy(arr->as<TypedArrayObject>().viewDataUnshared(), bytecode->begin(), arrLength);
7109 0 :
7110 0 : args.rval().setObject(*arr);
7111 0 : return true;
7112 : }
7113 0 :
7114 0 : class DebuggerSourceGetURLMatcher
7115 : {
7116 0 : JSContext* cx_;
7117 0 :
7118 : public:
7119 : explicit DebuggerSourceGetURLMatcher(JSContext* cx) : cx_(cx) { }
7120 :
7121 : using ReturnType = Maybe<JSString*>;
7122 :
7123 : ReturnType match(HandleScriptSourceObject sourceObject) {
7124 : ScriptSource* ss = sourceObject->source();
7125 0 : MOZ_ASSERT(ss);
7126 : if (ss->filename()) {
7127 : JSString* str = NewStringCopyZ<CanGC>(cx_, ss->filename());
7128 : return Some(str);
7129 0 : }
7130 0 : return Nothing();
7131 0 : }
7132 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7133 0 : if (wasmInstance->instance().metadata().filenameIsURL) {
7134 0 : JSString* str = NewStringCopyZ<CanGC>(cx_, wasmInstance->instance().metadata().filename.get());
7135 : if (!str)
7136 : return Nothing();
7137 : return Some(str);
7138 0 : }
7139 0 : if (JSString* str = wasmInstance->instance().debug().debugDisplayURL(cx_))
7140 0 : return Some(str);
7141 0 : return Nothing();
7142 : }
7143 : };
7144 :
7145 0 : static bool
7146 0 : DebuggerSource_getURL(JSContext* cx, unsigned argc, Value* vp)
7147 : {
7148 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get url)", args, obj, referent);
7149 :
7150 : DebuggerSourceGetURLMatcher matcher(cx);
7151 : Maybe<JSString*> str = referent.match(matcher);
7152 0 : if (str.isSome()) {
7153 : if (!*str)
7154 0 : return false;
7155 : args.rval().setString(*str);
7156 0 : } else {
7157 0 : args.rval().setNull();
7158 0 : }
7159 0 : return true;
7160 : }
7161 0 :
7162 : struct DebuggerSourceGetDisplayURLMatcher
7163 0 : {
7164 : using ReturnType = const char16_t*;
7165 : ReturnType match(HandleScriptSourceObject sourceObject) {
7166 : ScriptSource* ss = sourceObject->source();
7167 : MOZ_ASSERT(ss);
7168 : return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
7169 : }
7170 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7171 0 : return wasmInstance->instance().metadata().displayURL();
7172 0 : }
7173 0 : };
7174 0 :
7175 : static bool
7176 0 : DebuggerSource_getDisplayURL(JSContext* cx, unsigned argc, Value* vp)
7177 0 : {
7178 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get url)", args, obj, referent);
7179 :
7180 : DebuggerSourceGetDisplayURLMatcher matcher;
7181 : if (const char16_t* displayURL = referent.match(matcher)) {
7182 0 : JSString* str = JS_NewUCStringCopyZ(cx, displayURL);
7183 : if (!str)
7184 0 : return false;
7185 : args.rval().setString(str);
7186 : } else {
7187 0 : args.rval().setNull();
7188 0 : }
7189 0 : return true;
7190 : }
7191 0 :
7192 : struct DebuggerSourceGetElementMatcher
7193 0 : {
7194 : using ReturnType = JSObject*;
7195 : ReturnType match(HandleScriptSourceObject sourceObject) {
7196 : return sourceObject->element();
7197 : }
7198 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7199 : return nullptr;
7200 : }
7201 0 : };
7202 0 :
7203 : static bool
7204 : DebuggerSource_getElement(JSContext* cx, unsigned argc, Value* vp)
7205 : {
7206 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get element)", args, obj, referent);
7207 :
7208 : DebuggerSourceGetElementMatcher matcher;
7209 : if (JSObject* element = referent.match(matcher)) {
7210 0 : args.rval().setObjectOrNull(element);
7211 : if (!Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval()))
7212 0 : return false;
7213 : } else {
7214 : args.rval().setUndefined();
7215 0 : }
7216 0 : return true;
7217 0 : }
7218 :
7219 : struct DebuggerSourceGetElementPropertyMatcher
7220 0 : {
7221 : using ReturnType = Value;
7222 : ReturnType match(HandleScriptSourceObject sourceObject) {
7223 : return sourceObject->elementAttributeName();
7224 : }
7225 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7226 : return UndefinedValue();
7227 : }
7228 : };
7229 0 :
7230 : static bool
7231 : DebuggerSource_getElementProperty(JSContext* cx, unsigned argc, Value* vp)
7232 : {
7233 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get elementAttributeName)", args, obj, referent);
7234 : DebuggerSourceGetElementPropertyMatcher matcher;
7235 : args.rval().set(referent.match(matcher));
7236 : return Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval());
7237 0 : }
7238 :
7239 0 : class DebuggerSourceGetIntroductionScriptMatcher
7240 : {
7241 0 : JSContext* cx_;
7242 0 : Debugger* dbg_;
7243 : MutableHandleValue rval_;
7244 :
7245 : public:
7246 : DebuggerSourceGetIntroductionScriptMatcher(JSContext* cx, Debugger* dbg,
7247 : MutableHandleValue rval)
7248 : : cx_(cx),
7249 : dbg_(dbg),
7250 : rval_(rval)
7251 : { }
7252 :
7253 : using ReturnType = bool;
7254 0 :
7255 : ReturnType match(HandleScriptSourceObject sourceObject) {
7256 0 : RootedScript script(cx_, sourceObject->introductionScript());
7257 : if (script) {
7258 : RootedObject scriptDO(cx_, dbg_->wrapScript(cx_, script));
7259 : if (!scriptDO)
7260 : return false;
7261 0 : rval_.setObject(*scriptDO);
7262 0 : } else {
7263 0 : rval_.setUndefined();
7264 0 : }
7265 0 : return true;
7266 0 : }
7267 0 :
7268 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7269 0 : RootedObject ds(cx_, dbg_->wrapWasmScript(cx_, wasmInstance));
7270 : if (!ds)
7271 : return false;
7272 : rval_.setObject(*ds);
7273 : return true;
7274 0 : }
7275 0 : };
7276 0 :
7277 : static bool
7278 0 : DebuggerSource_getIntroductionScript(JSContext* cx, unsigned argc, Value* vp)
7279 0 : {
7280 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionScript)", args, obj, referent);
7281 : Debugger* dbg = Debugger::fromChildJSObject(obj);
7282 : DebuggerSourceGetIntroductionScriptMatcher matcher(cx, dbg, args.rval());
7283 : return referent.match(matcher);
7284 0 : }
7285 :
7286 0 : struct DebuggerGetIntroductionOffsetMatcher
7287 0 : {
7288 0 : using ReturnType = Value;
7289 0 : ReturnType match(HandleScriptSourceObject sourceObject) {
7290 : // Regardless of what's recorded in the ScriptSourceObject and
7291 : // ScriptSource, only hand out the introduction offset if we also have
7292 : // the script within which it applies.
7293 : ScriptSource* ss = sourceObject->source();
7294 : if (ss->hasIntroductionOffset() && sourceObject->introductionScript())
7295 0 : return Int32Value(ss->introductionOffset());
7296 : return UndefinedValue();
7297 : }
7298 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7299 0 : return UndefinedValue();
7300 0 : }
7301 0 : };
7302 0 :
7303 : static bool
7304 : DebuggerSource_getIntroductionOffset(JSContext* cx, unsigned argc, Value* vp)
7305 : {
7306 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionOffset)", args, obj, referent);
7307 : DebuggerGetIntroductionOffsetMatcher matcher;
7308 : args.rval().set(referent.match(matcher));
7309 : return true;
7310 0 : }
7311 :
7312 0 : struct DebuggerSourceGetIntroductionTypeMatcher
7313 : {
7314 0 : using ReturnType = const char*;
7315 : ReturnType match(HandleScriptSourceObject sourceObject) {
7316 : ScriptSource* ss = sourceObject->source();
7317 : MOZ_ASSERT(ss);
7318 : return ss->hasIntroductionType() ? ss->introductionType() : nullptr;
7319 : }
7320 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7321 0 : return "wasm";
7322 0 : }
7323 0 : };
7324 0 :
7325 : static bool
7326 : DebuggerSource_getIntroductionType(JSContext* cx, unsigned argc, Value* vp)
7327 : {
7328 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionType)", args, obj, referent);
7329 :
7330 : DebuggerSourceGetIntroductionTypeMatcher matcher;
7331 : if (const char* introductionType = referent.match(matcher)) {
7332 0 : JSString* str = NewStringCopyZ<CanGC>(cx, introductionType);
7333 : if (!str)
7334 0 : return false;
7335 : args.rval().setString(str);
7336 : } else {
7337 0 : args.rval().setUndefined();
7338 0 : }
7339 0 :
7340 : return true;
7341 0 : }
7342 :
7343 0 : static bool
7344 : DebuggerSource_setSourceMapURL(JSContext* cx, unsigned argc, Value* vp)
7345 : {
7346 : THIS_DEBUGSOURCE_SOURCE(cx, argc, vp, "set sourceMapURL", args, obj, sourceObject);
7347 : ScriptSource* ss = sourceObject->source();
7348 : MOZ_ASSERT(ss);
7349 : if (!args.requireAtLeast(cx, "set sourceMapURL", 1))
7350 0 : return false;
7351 :
7352 0 : JSString* str = ToString<CanGC>(cx, args[0]);
7353 0 : if (!str)
7354 0 : return false;
7355 0 :
7356 : AutoStableStringChars stableChars(cx);
7357 : if (!stableChars.initTwoByte(cx, str))
7358 0 : return false;
7359 0 :
7360 : if (!ss->setSourceMapURL(cx, stableChars.twoByteChars()))
7361 : return false;
7362 0 :
7363 0 : args.rval().setUndefined();
7364 : return true;
7365 : }
7366 0 :
7367 : class DebuggerSourceGetSourceMapURLMatcher
7368 : {
7369 0 : JSContext* cx_;
7370 0 : MutableHandleString result_;
7371 :
7372 : public:
7373 : explicit DebuggerSourceGetSourceMapURLMatcher(JSContext* cx, MutableHandleString result)
7374 : : cx_(cx),
7375 : result_(result)
7376 : { }
7377 :
7378 : using ReturnType = bool;
7379 : ReturnType match(HandleScriptSourceObject sourceObject) {
7380 0 : ScriptSource* ss = sourceObject->source();
7381 0 : MOZ_ASSERT(ss);
7382 : if (!ss->hasSourceMapURL()) {
7383 : result_.set(nullptr);
7384 : return true;
7385 0 : }
7386 0 : JSString* str = JS_NewUCStringCopyZ(cx_, ss->sourceMapURL());
7387 0 : if (!str)
7388 0 : return false;
7389 0 : result_.set(str);
7390 0 : return true;
7391 : }
7392 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7393 0 : // sourceMapURL is not available if debugger was not in
7394 : // allowWasmBinarySource mode.
7395 0 : if (!wasmInstance->instance().debug().binarySource()) {
7396 0 : result_.set(nullptr);
7397 : return true;
7398 0 : }
7399 : RootedString str(cx_);
7400 : if (!wasmInstance->instance().debug().getSourceMappingURL(cx_, &str))
7401 0 : return false;
7402 0 : result_.set(str);
7403 0 : return true;
7404 : }
7405 0 : };
7406 0 :
7407 : static bool
7408 0 : DebuggerSource_getSourceMapURL(JSContext* cx, unsigned argc, Value* vp)
7409 0 : {
7410 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get sourceMapURL)", args, obj, referent);
7411 :
7412 : RootedString result(cx);
7413 : DebuggerSourceGetSourceMapURLMatcher matcher(cx, &result);
7414 0 : if (!referent.match(matcher))
7415 : return false;
7416 0 : if (result)
7417 : args.rval().setString(result);
7418 0 : else
7419 0 : args.rval().setNull();
7420 0 : return true;
7421 : }
7422 0 :
7423 0 : static const JSPropertySpec DebuggerSource_properties[] = {
7424 : JS_PSG("text", DebuggerSource_getText, 0),
7425 0 : JS_PSG("binary", DebuggerSource_getBinary, 0),
7426 : JS_PSG("url", DebuggerSource_getURL, 0),
7427 : JS_PSG("element", DebuggerSource_getElement, 0),
7428 : JS_PSG("displayURL", DebuggerSource_getDisplayURL, 0),
7429 : JS_PSG("introductionScript", DebuggerSource_getIntroductionScript, 0),
7430 : JS_PSG("introductionOffset", DebuggerSource_getIntroductionOffset, 0),
7431 : JS_PSG("introductionType", DebuggerSource_getIntroductionType, 0),
7432 : JS_PSG("elementAttributeName", DebuggerSource_getElementProperty, 0),
7433 : JS_PSGS("sourceMapURL", DebuggerSource_getSourceMapURL, DebuggerSource_setSourceMapURL, 0),
7434 : JS_PS_END
7435 : };
7436 :
7437 : static const JSFunctionSpec DebuggerSource_methods[] = {
7438 : JS_FS_END
7439 : };
7440 :
7441 :
7442 : /*** Debugger.Frame ******************************************************************************/
7443 :
7444 : ScriptedOnStepHandler::ScriptedOnStepHandler(JSObject* object)
7445 : : object_(object)
7446 : {
7447 : MOZ_ASSERT(object_->isCallable());
7448 : }
7449 :
7450 0 : JSObject*
7451 0 : ScriptedOnStepHandler::object() const
7452 : {
7453 0 : return object_;
7454 0 : }
7455 :
7456 : void
7457 0 : ScriptedOnStepHandler::drop()
7458 : {
7459 0 : this->~ScriptedOnStepHandler();
7460 : js_free(this);
7461 : }
7462 :
7463 0 : void
7464 : ScriptedOnStepHandler::trace(JSTracer* tracer)
7465 0 : {
7466 0 : TraceEdge(tracer, &object_, "OnStepHandlerFunction.object");
7467 0 : }
7468 :
7469 : bool
7470 0 : ScriptedOnStepHandler::onStep(JSContext* cx, HandleDebuggerFrame frame, ResumeMode& resumeMode,
7471 : MutableHandleValue vp)
7472 0 : {
7473 0 : RootedValue fval(cx, ObjectValue(*object_));
7474 : RootedValue rval(cx);
7475 : if (!js::Call(cx, fval, frame, &rval))
7476 0 : return false;
7477 :
7478 : return ParseResumptionValue(cx, rval, resumeMode, vp);
7479 0 : };
7480 0 :
7481 0 : ScriptedOnPopHandler::ScriptedOnPopHandler(JSObject* object)
7482 : : object_(object)
7483 : {
7484 0 : MOZ_ASSERT(object->isCallable());
7485 : }
7486 :
7487 0 : JSObject*
7488 0 : ScriptedOnPopHandler::object() const
7489 : {
7490 0 : return object_;
7491 0 : }
7492 :
7493 : void
7494 0 : ScriptedOnPopHandler::drop()
7495 : {
7496 0 : this->~ScriptedOnPopHandler();
7497 : js_free(this);
7498 : }
7499 :
7500 0 : void
7501 : ScriptedOnPopHandler::trace(JSTracer* tracer)
7502 0 : {
7503 0 : TraceEdge(tracer, &object_, "OnStepHandlerFunction.object");
7504 0 : }
7505 :
7506 : bool
7507 0 : ScriptedOnPopHandler::onPop(JSContext* cx, HandleDebuggerFrame frame, ResumeMode& resumeMode,
7508 : MutableHandleValue vp)
7509 0 : {
7510 0 : Debugger *dbg = frame->owner();
7511 :
7512 : RootedValue completion(cx);
7513 0 : if (!dbg->newCompletionValue(cx, resumeMode, vp, &completion))
7514 : return false;
7515 :
7516 0 : RootedValue fval(cx, ObjectValue(*object_));
7517 : RootedValue rval(cx);
7518 0 : if (!js::Call(cx, fval, frame, completion, &rval))
7519 0 : return false;
7520 :
7521 : return ParseResumptionValue(cx, rval, resumeMode, vp);
7522 0 : };
7523 0 :
7524 0 : /* static */ NativeObject*
7525 : DebuggerFrame::initClass(JSContext* cx, HandleObject dbgCtor, Handle<GlobalObject*> global)
7526 : {
7527 0 : RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
7528 :
7529 : return InitClass(cx, dbgCtor, objProto, &class_, construct, 0, properties_,
7530 : methods_, nullptr, nullptr);
7531 0 : }
7532 :
7533 0 : /* static */ DebuggerFrame*
7534 : DebuggerFrame::create(JSContext* cx, HandleObject proto, const FrameIter& iter,
7535 0 : HandleNativeObject debugger)
7536 0 : {
7537 : DebuggerFrame* frame = NewObjectWithGivenProto<DebuggerFrame>(cx, proto);
7538 : if (!frame)
7539 : return nullptr;
7540 0 :
7541 : FrameIter::Data* data = iter.copyData();
7542 : if (!data)
7543 0 : return nullptr;
7544 0 : frame->setPrivate(data);
7545 :
7546 : frame->setReservedSlot(JSSLOT_DEBUGFRAME_OWNER, ObjectValue(*debugger));
7547 0 :
7548 0 : return frame;
7549 : }
7550 0 :
7551 : /* static */ bool
7552 0 : DebuggerFrame::getCallee(JSContext* cx, HandleDebuggerFrame frame,
7553 : MutableHandleDebuggerObject result)
7554 0 : {
7555 : MOZ_ASSERT(frame->isLive());
7556 :
7557 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
7558 0 : if (!referent.isFunctionFrame()) {
7559 : result.set(nullptr);
7560 : return true;
7561 0 : }
7562 :
7563 0 : Debugger* dbg = frame->owner();
7564 0 :
7565 0 : RootedObject callee(cx, referent.callee());
7566 0 : return dbg->wrapDebuggeeObject(cx, callee, result);
7567 : }
7568 :
7569 0 : /* static */ bool
7570 : DebuggerFrame::getIsConstructing(JSContext* cx, HandleDebuggerFrame frame, bool& result)
7571 0 : {
7572 0 : MOZ_ASSERT(frame->isLive());
7573 :
7574 : Maybe<FrameIter> maybeIter;
7575 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
7576 0 : return false;
7577 : FrameIter& iter = *maybeIter;
7578 0 :
7579 : result = iter.isFunctionFrame() && iter.isConstructing();
7580 0 : return true;
7581 0 : }
7582 :
7583 0 : static void
7584 : UpdateFrameIterPc(FrameIter& iter)
7585 0 : {
7586 0 : if (iter.abstractFramePtr().isWasmDebugFrame()) {
7587 : // Wasm debug frames don't need their pc updated -- it's null.
7588 : return;
7589 : }
7590 0 :
7591 : if (iter.abstractFramePtr().isRematerializedFrame()) {
7592 0 : #ifdef DEBUG
7593 : // Rematerialized frames don't need their pc updated. The reason we
7594 : // need to update pc is because we might get the same Debugger.Frame
7595 : // object for multiple re-entries into debugger code from debuggee
7596 : // code. This reentrancy is not possible with rematerialized frames,
7597 0 : // because when returning to debuggee code, we would have bailed out
7598 : // to baseline.
7599 : //
7600 : // We walk the stack to assert that it doesn't need updating.
7601 : jit::RematerializedFrame* frame = iter.abstractFramePtr().asRematerializedFrame();
7602 : jit::JitFrameLayout* jsFrame = (jit::JitFrameLayout*)frame->top();
7603 : jit::JitActivation* activation = iter.activation()->asJit();
7604 :
7605 : JSContext* cx = TlsContext.get();
7606 : MOZ_ASSERT(cx == activation->cx());
7607 0 :
7608 0 : ActivationIterator activationIter(cx);
7609 0 : while (activationIter.activation() != activation)
7610 : ++activationIter;
7611 0 :
7612 0 : OnlyJSJitFrameIter jitIter(activationIter);
7613 : while (!jitIter.frame().isIonJS() || jitIter.frame().jsFrame() != jsFrame)
7614 0 : ++jitIter;
7615 0 :
7616 0 : jit::InlineFrameIterator ionInlineIter(cx, &jitIter.frame());
7617 : while (ionInlineIter.frameNo() != frame->frameNo())
7618 0 : ++ionInlineIter;
7619 0 :
7620 : MOZ_ASSERT(ionInlineIter.pc() == iter.pc());
7621 : #endif
7622 0 : return;
7623 0 : }
7624 :
7625 : iter.updatePcQuadratic();
7626 0 : }
7627 :
7628 : /* static */ bool
7629 : DebuggerFrame::getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
7630 : MutableHandleDebuggerEnvironment result)
7631 0 : {
7632 : MOZ_ASSERT(frame->isLive());
7633 :
7634 : Debugger* dbg = frame->owner();
7635 0 :
7636 : Maybe<FrameIter> maybeIter;
7637 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
7638 0 : return false;
7639 : FrameIter& iter = *maybeIter;
7640 0 :
7641 : Rooted<Env*> env(cx);
7642 0 : {
7643 0 : AutoRealm ar(cx, iter.abstractFramePtr().environmentChain());
7644 : UpdateFrameIterPc(iter);
7645 0 : env = GetDebugEnvironmentForFrame(cx, iter.abstractFramePtr(), iter.pc());
7646 : if (!env)
7647 0 : return false;
7648 : }
7649 0 :
7650 0 : return dbg->wrapEnvironment(cx, env, result);
7651 0 : }
7652 0 :
7653 0 : /* static */ bool
7654 : DebuggerFrame::getIsGenerator(HandleDebuggerFrame frame)
7655 : {
7656 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
7657 : return referent.hasScript() && referent.script()->isGenerator();
7658 : }
7659 :
7660 0 : /* static */ bool
7661 : DebuggerFrame::getOffset(JSContext* cx, HandleDebuggerFrame frame, size_t& result)
7662 0 : {
7663 0 : MOZ_ASSERT(frame->isLive());
7664 :
7665 : Maybe<FrameIter> maybeIter;
7666 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
7667 0 : return false;
7668 : FrameIter& iter = *maybeIter;
7669 0 :
7670 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
7671 0 : if (referent.isWasmDebugFrame()) {
7672 0 : iter.wasmUpdateBytecodeOffset();
7673 : result = iter.wasmBytecodeOffset();
7674 0 : } else {
7675 : JSScript* script = iter.script();
7676 0 : UpdateFrameIterPc(iter);
7677 0 : jsbytecode* pc = iter.pc();
7678 0 : result = script->pcToOffset(pc);
7679 0 : }
7680 : return true;
7681 0 : }
7682 0 :
7683 0 : /* static */ bool
7684 0 : DebuggerFrame::getOlder(JSContext* cx, HandleDebuggerFrame frame,
7685 : MutableHandleDebuggerFrame result)
7686 : {
7687 : MOZ_ASSERT(frame->isLive());
7688 :
7689 : Debugger* dbg = frame->owner();
7690 0 :
7691 : Maybe<FrameIter> maybeIter;
7692 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
7693 0 : return false;
7694 : FrameIter& iter = *maybeIter;
7695 0 :
7696 : for (++iter; !iter.done(); ++iter) {
7697 0 : if (dbg->observesFrame(iter)) {
7698 0 : if (iter.isIon() && !iter.ensureHasRematerializedFrame(cx))
7699 : return false;
7700 0 : return dbg->getFrame(cx, iter, result);
7701 : }
7702 0 : }
7703 0 :
7704 0 : result.set(nullptr);
7705 : return true;
7706 0 : }
7707 :
7708 : /* static */ bool
7709 : DebuggerFrame::getThis(JSContext* cx, HandleDebuggerFrame frame, MutableHandleValue result)
7710 0 : {
7711 0 : MOZ_ASSERT(frame->isLive());
7712 :
7713 : if (!requireScriptReferent(cx, frame))
7714 : return false;
7715 0 :
7716 : Debugger* dbg = frame->owner();
7717 0 :
7718 : Maybe<FrameIter> maybeIter;
7719 0 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
7720 : return false;
7721 : FrameIter& iter = *maybeIter;
7722 0 :
7723 : {
7724 0 : AbstractFramePtr frame = iter.abstractFramePtr();
7725 0 : AutoRealm ar(cx, frame.environmentChain());
7726 :
7727 0 : UpdateFrameIterPc(iter);
7728 :
7729 : if (!GetThisValueForDebuggerMaybeOptimizedOut(cx, frame, iter.pc(), result))
7730 0 : return false;
7731 0 : }
7732 :
7733 0 : return dbg->wrapDebuggeeValue(cx, result);
7734 : }
7735 0 :
7736 0 : /* static */ DebuggerFrameType
7737 : DebuggerFrame::getType(HandleDebuggerFrame frame)
7738 : {
7739 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
7740 :
7741 : /*
7742 : * Indirect eval frames are both isGlobalFrame() and isEvalFrame(), so the
7743 0 : * order of checks here is significant.
7744 : */
7745 0 : if (referent.isEvalFrame())
7746 : return DebuggerFrameType::Eval;
7747 : else if (referent.isGlobalFrame())
7748 : return DebuggerFrameType::Global;
7749 : else if (referent.isFunctionFrame())
7750 : return DebuggerFrameType::Call;
7751 0 : else if (referent.isModuleFrame())
7752 : return DebuggerFrameType::Module;
7753 0 : else if (referent.isWasmDebugFrame())
7754 : return DebuggerFrameType::WasmCall;
7755 0 : MOZ_CRASH("Unknown frame type");
7756 : }
7757 0 :
7758 : /* static */ DebuggerFrameImplementation
7759 0 : DebuggerFrame::getImplementation(HandleDebuggerFrame frame)
7760 : {
7761 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
7762 :
7763 : if (referent.isBaselineFrame())
7764 : return DebuggerFrameImplementation::Baseline;
7765 0 : else if (referent.isRematerializedFrame())
7766 : return DebuggerFrameImplementation::Ion;
7767 0 : else if (referent.isWasmDebugFrame())
7768 : return DebuggerFrameImplementation::Wasm;
7769 0 : return DebuggerFrameImplementation::Interpreter;
7770 : }
7771 0 :
7772 : /*
7773 0 : * If succesful, transfers the ownership of the given `handler` to this
7774 : * Debugger.Frame. Note that on failure, the ownership of `handler` is not
7775 0 : * transferred, and the caller is responsible for cleaning it up.
7776 : */
7777 : /* static */ bool
7778 : DebuggerFrame::setOnStepHandler(JSContext* cx, HandleDebuggerFrame frame, OnStepHandler* handler)
7779 : {
7780 : MOZ_ASSERT(frame->isLive());
7781 :
7782 : OnStepHandler* prior = frame->onStepHandler();
7783 : if (prior && handler != prior) {
7784 0 : prior->drop();
7785 : }
7786 0 :
7787 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
7788 0 : if (referent.isWasmDebugFrame()) {
7789 0 : wasm::Instance* instance = referent.asWasmDebugFrame()->instance();
7790 0 : wasm::DebugFrame* wasmFrame = referent.asWasmDebugFrame();
7791 : if (handler && !prior) {
7792 : // Single stepping toggled off->on.
7793 0 : if (!instance->debug().incrementStepModeCount(cx, wasmFrame->funcIndex()))
7794 0 : return false;
7795 0 : } else if (!handler && prior) {
7796 0 : // Single stepping toggled on->off.
7797 0 : FreeOp* fop = cx->runtime()->defaultFreeOp();
7798 : if (!instance->debug().decrementStepModeCount(fop, wasmFrame->funcIndex()))
7799 0 : return false;
7800 : }
7801 0 : } else {
7802 : if (handler && !prior) {
7803 0 : // Single stepping toggled off->on.
7804 0 : AutoRealm ar(cx, referent.environmentChain());
7805 : // Ensure observability *before* incrementing the step mode count.
7806 : // Calling this function after calling incrementStepModeCount
7807 : // will make it a no-op.
7808 0 : Debugger* dbg = frame->owner();
7809 : if (!dbg->ensureExecutionObservabilityOfScript(cx, referent.script()))
7810 0 : return false;
7811 : if (!referent.script()->incrementStepModeCount(cx))
7812 : return false;
7813 : } else if (!handler && prior) {
7814 0 : // Single stepping toggled on->off.
7815 0 : referent.script()->decrementStepModeCount(cx->runtime()->defaultFreeOp());
7816 0 : }
7817 0 : }
7818 :
7819 0 : /* Now that the step mode switch has succeeded, we can install the handler. */
7820 : frame->setReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER,
7821 0 : handler ? PrivateValue(handler) : UndefinedValue());
7822 : return true;
7823 : }
7824 :
7825 : /* static */ bool
7826 0 : DebuggerFrame::getArguments(JSContext *cx, HandleDebuggerFrame frame,
7827 0 : MutableHandleDebuggerArguments result)
7828 0 : {
7829 : Value argumentsv = frame->getReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS);
7830 : if (!argumentsv.isUndefined()) {
7831 : result.set(argumentsv.isObject() ? &argumentsv.toObject().as<DebuggerArguments>() : nullptr);
7832 0 : return true;
7833 : }
7834 :
7835 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
7836 0 :
7837 0 : RootedDebuggerArguments arguments(cx);
7838 0 : if (referent.hasArgs()) {
7839 : Rooted<GlobalObject*> global(cx, &frame->global());
7840 : RootedObject proto(cx, GlobalObject::getOrCreateArrayPrototype(cx, global));
7841 0 : if (!proto)
7842 : return false;
7843 0 : arguments = DebuggerArguments::create(cx, proto, frame);
7844 0 : if (!arguments)
7845 0 : return false;
7846 0 : } else {
7847 0 : arguments = nullptr;
7848 0 : }
7849 0 :
7850 0 : result.set(arguments);
7851 : frame->setReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS, ObjectOrNullValue(result));
7852 : return true;
7853 0 : }
7854 :
7855 : /*
7856 0 : * Evaluate |chars[0..length-1]| in the environment |env|, treating that
7857 0 : * source as appearing starting at |lineno| in |filename|. Store the return
7858 0 : * value in |*rval|. Use |thisv| as the 'this' value.
7859 : *
7860 : * If |frame| is non-nullptr, evaluate as for a direct eval in that frame; |env|
7861 : * must be either |frame|'s DebugScopeObject, or some extension of that
7862 : * environment; either way, |frame|'s scope is where newly declared variables
7863 : * go. In this case, |frame| must have a computed 'this' value, equal to |thisv|.
7864 : */
7865 : static bool
7866 : EvaluateInEnv(JSContext* cx, Handle<Env*> env, AbstractFramePtr frame,
7867 : mozilla::Range<const char16_t> chars, const char* filename,
7868 : unsigned lineno, MutableHandleValue rval)
7869 : {
7870 : assertSameCompartment(cx, env, frame);
7871 :
7872 0 : CompileOptions options(cx);
7873 : options.setIsRunOnce(true)
7874 : .setNoScriptRval(false)
7875 : .setFileAndLine(filename, lineno)
7876 0 : .setCanLazilyParse(false)
7877 : .setIntroductionType("debugger eval")
7878 0 : .maybeMakeStrictMode(frame && frame.hasScript() ? frame.script()->strict() : false);
7879 0 : RootedScript callerScript(cx, frame && frame.hasScript() ? frame.script() : nullptr);
7880 0 : SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), SourceBufferHolder::NoOwnership);
7881 0 : RootedScript script(cx);
7882 0 :
7883 0 : ScopeKind scopeKind;
7884 0 : if (IsGlobalLexicalEnvironment(env))
7885 0 : scopeKind = ScopeKind::Global;
7886 0 : else
7887 0 : scopeKind = ScopeKind::NonSyntactic;
7888 :
7889 : if (frame) {
7890 0 : MOZ_ASSERT(scopeKind == ScopeKind::NonSyntactic);
7891 : RootedScope scope(cx, GlobalScope::createEmpty(cx, ScopeKind::NonSyntactic));
7892 : if (!scope)
7893 0 : return false;
7894 : script = frontend::CompileEvalScript(cx, cx->tempLifoAlloc(), env, scope,
7895 0 : options, srcBuf);
7896 0 : if (script)
7897 0 : script->setActiveEval();
7898 0 : } else {
7899 0 : // Do not consider executeInGlobal{WithBindings} as an eval, but instead
7900 0 : // as executing a series of statements at the global level. This is to
7901 0 : // circumvent the fresh lexical scope that all eval have, so that the
7902 0 : // users of executeInGlobal, like the web console, may add new bindings to
7903 0 : // the global scope.
7904 : script = frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(), scopeKind, options,
7905 : srcBuf);
7906 : }
7907 :
7908 : if (!script)
7909 : return false;
7910 0 :
7911 0 : return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address());
7912 : }
7913 :
7914 0 : static bool
7915 : DebuggerGenericEval(JSContext* cx, const mozilla::Range<const char16_t> chars,
7916 : HandleObject bindings, const EvalOptions& options,
7917 0 : ResumeMode& resumeMode, MutableHandleValue value,
7918 : Debugger* dbg, HandleObject envArg, FrameIter* iter)
7919 : {
7920 : /* Either we're specifying the frame, or a global. */
7921 0 : MOZ_ASSERT_IF(iter, !envArg);
7922 : MOZ_ASSERT_IF(!iter, envArg && IsGlobalLexicalEnvironment(envArg));
7923 :
7924 : /*
7925 : * Gather keys and values of bindings, if any. This must be done in the
7926 : * debugger compartment, since that is where any exceptions must be
7927 0 : * thrown.
7928 0 : */
7929 : AutoIdVector keys(cx);
7930 : AutoValueVector values(cx);
7931 : if (bindings) {
7932 : if (!GetPropertyKeys(cx, bindings, JSITER_OWNONLY, &keys) ||
7933 : !values.growBy(keys.length()))
7934 : {
7935 0 : return false;
7936 0 : }
7937 0 : for (size_t i = 0; i < keys.length(); i++) {
7938 0 : MutableHandleValue valp = values[i];
7939 0 : if (!GetProperty(cx, bindings, bindings, keys[i], valp) ||
7940 : !dbg->unwrapDebuggeeValue(cx, valp))
7941 : {
7942 : return false;
7943 0 : }
7944 0 : }
7945 0 : }
7946 0 :
7947 : Maybe<AutoRealm> ar;
7948 0 : if (iter)
7949 : ar.emplace(cx, iter->environmentChain(cx));
7950 : else
7951 : ar.emplace(cx, envArg);
7952 :
7953 0 : Rooted<Env*> env(cx);
7954 0 : if (iter) {
7955 0 : env = GetDebugEnvironmentForFrame(cx, iter->abstractFramePtr(), iter->pc());
7956 : if (!env)
7957 0 : return false;
7958 : } else {
7959 0 : env = envArg;
7960 0 : }
7961 0 :
7962 0 : /* If evalWithBindings, create the inner environment. */
7963 : if (bindings) {
7964 : RootedPlainObject nenv(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
7965 0 : if (!nenv)
7966 : return false;
7967 : RootedId id(cx);
7968 : for (size_t i = 0; i < keys.length(); i++) {
7969 0 : id = keys[i];
7970 0 : cx->markId(id);
7971 0 : MutableHandleValue val = values[i];
7972 0 : if (!cx->compartment()->wrap(cx, val) ||
7973 0 : !NativeDefineDataProperty(cx, nenv, id, val, 0))
7974 0 : {
7975 0 : return false;
7976 0 : }
7977 0 : }
7978 0 :
7979 0 : AutoObjectVector envChain(cx);
7980 : if (!envChain.append(nenv))
7981 : return false;
7982 :
7983 : RootedObject newEnv(cx);
7984 : if (!CreateObjectsForEnvironmentChain(cx, envChain, env, &newEnv))
7985 0 : return false;
7986 0 :
7987 : env = newEnv;
7988 : }
7989 0 :
7990 0 : /* Run the code and produce the completion value. */
7991 0 : LeaveDebuggeeNoExecute nnx(cx);
7992 : RootedValue rval(cx);
7993 0 : AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr();
7994 :
7995 : bool ok = EvaluateInEnv(cx, env, frame, chars,
7996 : options.filename() ? options.filename() : "debugger eval code",
7997 0 : options.lineno(), &rval);
7998 0 : Debugger::resultToCompletion(cx, ok, rval, &resumeMode, value);
7999 0 : ar.reset();
8000 : return dbg->wrapDebuggeeValue(cx, value);
8001 0 : }
8002 0 :
8003 0 : /* static */ bool
8004 0 : DebuggerFrame::eval(JSContext* cx, HandleDebuggerFrame frame, mozilla::Range<const char16_t> chars,
8005 0 : HandleObject bindings, const EvalOptions& options, ResumeMode& resumeMode,
8006 0 : MutableHandleValue value)
8007 : {
8008 : MOZ_ASSERT(frame->isLive());
8009 :
8010 0 : Debugger* dbg = frame->owner();
8011 :
8012 : Maybe<FrameIter> maybeIter;
8013 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
8014 0 : return false;
8015 : FrameIter& iter = *maybeIter;
8016 0 :
8017 : UpdateFrameIterPc(iter);
8018 0 :
8019 0 : return DebuggerGenericEval(cx, chars, bindings, options, resumeMode, value, dbg, nullptr, &iter);
8020 : }
8021 0 :
8022 : /* static */ bool
8023 0 : DebuggerFrame::isLive() const
8024 : {
8025 0 : return !!getPrivate();
8026 : }
8027 :
8028 : OnStepHandler*
8029 0 : DebuggerFrame::onStepHandler() const
8030 : {
8031 0 : Value value = getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);
8032 : return value.isUndefined() ? nullptr : static_cast<OnStepHandler*>(value.toPrivate());
8033 : }
8034 :
8035 0 : OnPopHandler*
8036 : DebuggerFrame::onPopHandler() const
8037 0 : {
8038 0 : Value value = getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER);
8039 : return value.isUndefined() ? nullptr : static_cast<OnPopHandler*>(value.toPrivate());
8040 : }
8041 :
8042 0 : void
8043 : DebuggerFrame::setOnPopHandler(OnPopHandler* handler)
8044 0 : {
8045 0 : MOZ_ASSERT(isLive());
8046 :
8047 : OnPopHandler* prior = onPopHandler();
8048 : if (prior && prior != handler)
8049 0 : prior->drop();
8050 :
8051 0 : setReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER,
8052 : handler ? PrivateValue(handler) : UndefinedValue());
8053 0 : }
8054 0 :
8055 0 : static bool
8056 : DebuggerFrame_requireLive(JSContext* cx, HandleDebuggerFrame frame)
8057 : {
8058 0 : if (!frame->isLive()) {
8059 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
8060 : "Debugger.Frame");
8061 : return false;
8062 0 : }
8063 :
8064 0 : return true;
8065 : }
8066 0 :
8067 0 : FrameIter::Data*
8068 : DebuggerFrame::frameIterData() const
8069 : {
8070 : return static_cast<FrameIter::Data*>(getPrivate());
8071 : }
8072 :
8073 : /* static */ AbstractFramePtr
8074 0 : DebuggerFrame::getReferent(HandleDebuggerFrame frame)
8075 : {
8076 0 : FrameIter iter(*frame->frameIterData());
8077 : return iter.abstractFramePtr();
8078 : }
8079 :
8080 0 : /* static */ bool
8081 : DebuggerFrame::getFrameIter(JSContext* cx, HandleDebuggerFrame frame,
8082 0 : Maybe<FrameIter>& result)
8083 0 : {
8084 : result.emplace(*frame->frameIterData());
8085 : return true;
8086 : }
8087 0 :
8088 : /* static */ bool
8089 : DebuggerFrame::requireScriptReferent(JSContext* cx, HandleDebuggerFrame frame)
8090 0 : {
8091 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
8092 : if (!referent.hasScript()) {
8093 : RootedValue frameobj(cx, ObjectValue(*frame));
8094 : ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, frameobj, nullptr,
8095 0 : "a script frame");
8096 : return false;
8097 0 : }
8098 0 : return true;
8099 0 : }
8100 0 :
8101 : void
8102 0 : DebuggerFrame::freeFrameIterData(FreeOp* fop)
8103 : {
8104 : if (FrameIter::Data* data = frameIterData()) {
8105 : fop->delete_(data);
8106 : setPrivate(nullptr);
8107 : }
8108 : }
8109 0 :
8110 : static void
8111 0 : DebuggerFrame_maybeDecrementFrameScriptStepModeCount(FreeOp* fop, AbstractFramePtr frame,
8112 0 : NativeObject* frameobj)
8113 0 : {
8114 : /* If this frame has an onStep handler, decrement the script's count. */
8115 0 : if (frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
8116 : return;
8117 : if (frame.isWasmDebugFrame()) {
8118 0 : wasm::Instance* instance = frame.wasmInstance();
8119 : instance->debug().decrementStepModeCount(fop, frame.asWasmDebugFrame()->funcIndex());
8120 : } else {
8121 : frame.script()->decrementStepModeCount(fop);
8122 0 : }
8123 : }
8124 0 :
8125 0 : static void
8126 0 : DebuggerFrame_finalize(FreeOp* fop, JSObject* obj)
8127 : {
8128 0 : MOZ_ASSERT(fop->maybeOnHelperThread());
8129 : DebuggerFrame& frameobj = obj->as<DebuggerFrame>();
8130 : frameobj.freeFrameIterData(fop);
8131 : OnStepHandler* onStepHandler = frameobj.onStepHandler();
8132 : if (onStepHandler)
8133 0 : onStepHandler->drop();
8134 : OnPopHandler* onPopHandler = frameobj.onPopHandler();
8135 0 : if (onPopHandler)
8136 0 : onPopHandler->drop();
8137 0 : }
8138 0 :
8139 0 : static void
8140 0 : DebuggerFrame_trace(JSTracer* trc, JSObject* obj)
8141 0 : {
8142 0 : OnStepHandler* onStepHandler = obj->as<DebuggerFrame>().onStepHandler();
8143 0 : if (onStepHandler)
8144 0 : onStepHandler->trace(trc);
8145 : OnPopHandler* onPopHandler = obj->as<DebuggerFrame>().onPopHandler();
8146 : if (onPopHandler)
8147 0 : onPopHandler->trace(trc);
8148 : }
8149 0 :
8150 0 : static DebuggerFrame*
8151 0 : DebuggerFrame_checkThis(JSContext* cx, const CallArgs& args, const char* fnname, bool checkLive)
8152 0 : {
8153 0 : JSObject* thisobj = NonNullObject(cx, args.thisv());
8154 0 : if (!thisobj)
8155 0 : return nullptr;
8156 : if (thisobj->getClass() != &DebuggerFrame::class_) {
8157 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
8158 0 : "Debugger.Frame", fnname, thisobj->getClass()->name);
8159 : return nullptr;
8160 0 : }
8161 0 :
8162 : RootedDebuggerFrame frame(cx, &thisobj->as<DebuggerFrame>());
8163 0 :
8164 : /*
8165 0 : * Forbid Debugger.Frame.prototype, which is of class DebuggerFrame::class_
8166 0 : * but isn't really a working Debugger.Frame object. The prototype object
8167 : * is distinguished by having a nullptr private value. Also, forbid popped
8168 : * frames.
8169 0 : */
8170 : if (!frame->getPrivate() &&
8171 : frame->getReservedSlot(JSSLOT_DEBUGFRAME_OWNER).isUndefined())
8172 : {
8173 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
8174 : "Debugger.Frame", fnname, "prototype object");
8175 : return nullptr;
8176 : }
8177 0 :
8178 0 : if (checkLive) {
8179 : if (!DebuggerFrame_requireLive(cx, frame))
8180 : return nullptr;
8181 0 : }
8182 0 :
8183 : return frame;
8184 : }
8185 0 :
8186 0 : /*
8187 : * Methods can use THIS_DEBUGGER_FRAME to check that `this` is a Debugger.Frame object
8188 : * and get it in a local Rooted.
8189 : *
8190 0 : * Methods that need the AbstractFramePtr should use THIS_FRAME.
8191 : */
8192 : #define THIS_DEBUGGER_FRAME(cx, argc, vp, fnname, args, frame) \
8193 : CallArgs args = CallArgsFromVp(argc, vp); \
8194 : RootedDebuggerFrame frame(cx, DebuggerFrame_checkThis(cx, args, fnname, true)); \
8195 : if (!frame) \
8196 : return false;
8197 :
8198 : #define THIS_FRAME(cx, argc, vp, fnname, args, thisobj, iter, frame) \
8199 : THIS_DEBUGGER_FRAME(cx, argc, vp, fnname, args, thisobj); \
8200 : FrameIter iter(*thisobj->frameIterData()); \
8201 : AbstractFramePtr frame = iter.abstractFramePtr()
8202 :
8203 : /* static */ bool
8204 : DebuggerFrame::typeGetter(JSContext* cx, unsigned argc, Value* vp)
8205 : {
8206 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get type", args, frame);
8207 :
8208 : DebuggerFrameType type = DebuggerFrame::getType(frame);
8209 :
8210 : JSString* str;
8211 0 : switch (type) {
8212 : case DebuggerFrameType::Eval:
8213 0 : str = cx->names().eval;
8214 : break;
8215 0 : case DebuggerFrameType::Global:
8216 : str = cx->names().global;
8217 : break;
8218 0 : case DebuggerFrameType::Call:
8219 : str = cx->names().call;
8220 0 : break;
8221 0 : case DebuggerFrameType::Module:
8222 : str = cx->names().module;
8223 0 : break;
8224 0 : case DebuggerFrameType::WasmCall:
8225 : str = cx->names().wasmcall;
8226 0 : break;
8227 0 : default:
8228 : MOZ_CRASH("bad DebuggerFrameType value");
8229 0 : }
8230 0 :
8231 : args.rval().setString(str);
8232 0 : return true;
8233 0 : }
8234 :
8235 0 : /* static */ bool
8236 : DebuggerFrame::implementationGetter(JSContext* cx, unsigned argc, Value* vp)
8237 : {
8238 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get implementation", args, frame);
8239 0 :
8240 : DebuggerFrameImplementation implementation = DebuggerFrame::getImplementation(frame);
8241 :
8242 : const char* s;
8243 0 : switch (implementation) {
8244 : case DebuggerFrameImplementation::Baseline:
8245 0 : s = "baseline";
8246 : break;
8247 0 : case DebuggerFrameImplementation::Ion:
8248 : s = "ion";
8249 : break;
8250 0 : case DebuggerFrameImplementation::Interpreter:
8251 : s = "interpreter";
8252 : break;
8253 : case DebuggerFrameImplementation::Wasm:
8254 : s = "wasm";
8255 0 : break;
8256 0 : default:
8257 : MOZ_CRASH("bad DebuggerFrameImplementation value");
8258 0 : }
8259 0 :
8260 : JSAtom* str = Atomize(cx, s, strlen(s));
8261 0 : if (!str)
8262 0 : return false;
8263 :
8264 0 : args.rval().setString(str);
8265 : return true;
8266 : }
8267 0 :
8268 0 : /* static */ bool
8269 : DebuggerFrame::environmentGetter(JSContext* cx, unsigned argc, Value* vp)
8270 : {
8271 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get environment", args, frame);
8272 0 :
8273 : RootedDebuggerEnvironment result(cx);
8274 : if (!DebuggerFrame::getEnvironment(cx, frame, &result))
8275 : return false;
8276 0 :
8277 : args.rval().setObject(*result);
8278 0 : return true;
8279 : }
8280 0 :
8281 0 : /* static */ bool
8282 : DebuggerFrame::calleeGetter(JSContext* cx, unsigned argc, Value* vp)
8283 : {
8284 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get callee", args, frame);
8285 0 :
8286 : RootedDebuggerObject result(cx);
8287 : if (!DebuggerFrame::getCallee(cx, frame, &result))
8288 : return false;
8289 0 :
8290 : args.rval().setObjectOrNull(result);
8291 0 : return true;
8292 : }
8293 0 :
8294 0 : /* static */ bool
8295 : DebuggerFrame::generatorGetter(JSContext* cx, unsigned argc, Value* vp)
8296 : {
8297 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get callee", args, frame);
8298 :
8299 : args.rval().setBoolean(DebuggerFrame::getIsGenerator(frame));
8300 : return true;
8301 : }
8302 0 :
8303 : /* static */ bool
8304 0 : DebuggerFrame::constructingGetter(JSContext* cx, unsigned argc, Value* vp)
8305 : {
8306 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get callee", args, frame);
8307 0 :
8308 : bool result;
8309 : if (!DebuggerFrame::getIsConstructing(cx, frame, result))
8310 : return false;
8311 0 :
8312 : args.rval().setBoolean(result);
8313 0 : return true;
8314 : }
8315 :
8316 0 : /* static */ bool
8317 : DebuggerFrame::thisGetter(JSContext* cx, unsigned argc, Value* vp)
8318 : {
8319 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get this", args, frame);
8320 0 :
8321 : return DebuggerFrame::getThis(cx, frame, args.rval());
8322 : }
8323 :
8324 0 : /* static */ bool
8325 : DebuggerFrame::olderGetter(JSContext* cx, unsigned argc, Value* vp)
8326 0 : {
8327 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get older", args, frame);
8328 0 :
8329 : RootedDebuggerFrame result(cx);
8330 : if (!DebuggerFrame::getOlder(cx, frame, &result))
8331 : return false;
8332 0 :
8333 : args.rval().setObjectOrNull(result);
8334 0 : return true;
8335 : }
8336 0 :
8337 0 : /* The getter used for each element of frame.arguments. See DebuggerFrame_getArguments. */
8338 : static bool
8339 : DebuggerArguments_getArg(JSContext* cx, unsigned argc, Value* vp)
8340 0 : {
8341 : CallArgs args = CallArgsFromVp(argc, vp);
8342 : int32_t i = args.callee().as<JSFunction>().getExtendedSlot(0).toInt32();
8343 :
8344 : /* Check that the this value is an Arguments object. */
8345 : RootedObject argsobj(cx, NonNullObject(cx, args.thisv()));
8346 0 : if (!argsobj)
8347 : return false;
8348 0 : if (argsobj->getClass() != &DebuggerArguments::class_) {
8349 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
8350 : "Arguments", "getArgument", argsobj->getClass()->name);
8351 : return false;
8352 0 : }
8353 0 :
8354 : /*
8355 0 : * Put the Debugger.Frame into the this-value slot, then use THIS_FRAME
8356 : * to check that it is still live and get the fp.
8357 0 : */
8358 0 : args.setThis(argsobj->as<NativeObject>().getReservedSlot(JSSLOT_DEBUGARGUMENTS_FRAME));
8359 : THIS_FRAME(cx, argc, vp, "get argument", ca2, thisobj, frameIter, frame);
8360 :
8361 : // TODO handle wasm frame arguments -- they are not yet reflectable.
8362 : MOZ_ASSERT(!frame.isWasmDebugFrame(), "a wasm frame args");
8363 :
8364 : /*
8365 0 : * Since getters can be extracted and applied to other objects,
8366 0 : * there is no guarantee this object has an ith argument.
8367 : */
8368 : MOZ_ASSERT(i >= 0);
8369 0 : RootedValue arg(cx);
8370 : RootedScript script(cx);
8371 : if (unsigned(i) < frame.numActualArgs()) {
8372 : script = frame.script();
8373 : {
8374 : AutoRealm ar(cx, script);
8375 0 : if (!script->ensureHasAnalyzedArgsUsage(cx))
8376 0 : return false;
8377 0 : }
8378 0 : if (unsigned(i) < frame.numFormalArgs()) {
8379 0 : for (PositionalFormalParameterIter fi(script); fi; fi++) {
8380 : if (fi.argumentSlot() == unsigned(i)) {
8381 0 : // We might've been called before the CallObject was
8382 0 : // created.
8383 0 : if (fi.closedOver() && frame.hasInitialEnvironment())
8384 : arg = frame.callObj().aliasedBinding(fi);
8385 0 : else
8386 0 : arg = frame.unaliasedActual(i, DONT_CHECK_ALIASING);
8387 0 : break;
8388 : }
8389 : }
8390 0 : } else if (script->argsObjAliasesFormals() && frame.hasArgsObj()) {
8391 0 : arg = frame.argsObj().arg(i);
8392 : } else {
8393 0 : arg = frame.unaliasedActual(i, DONT_CHECK_ALIASING);
8394 : }
8395 : } else {
8396 : arg.setUndefined();
8397 0 : }
8398 0 :
8399 : if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &arg))
8400 0 : return false;
8401 : args.rval().set(arg);
8402 : return true;
8403 : }
8404 :
8405 : /* static */ DebuggerArguments*
8406 0 : DebuggerArguments::create(JSContext* cx, HandleObject proto, HandleDebuggerFrame frame)
8407 : {
8408 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
8409 0 :
8410 : Rooted<DebuggerArguments*> obj(cx, NewObjectWithGivenProto<DebuggerArguments>(cx, proto));
8411 : if (!obj)
8412 : return nullptr;
8413 0 :
8414 : SetReservedSlot(obj, FRAME_SLOT, ObjectValue(*frame));
8415 0 :
8416 : MOZ_ASSERT(referent.numActualArgs() <= 0x7fffffff);
8417 0 : unsigned fargc = referent.numActualArgs();
8418 0 : RootedValue fargcVal(cx, Int32Value(fargc));
8419 : if (!NativeDefineDataProperty(cx, obj, cx->names().length, fargcVal,
8420 : JSPROP_PERMANENT | JSPROP_READONLY))
8421 0 : {
8422 : return nullptr;
8423 0 : }
8424 0 :
8425 0 : Rooted<jsid> id(cx);
8426 0 : for (unsigned i = 0; i < fargc; i++) {
8427 : RootedFunction getobj(cx);
8428 : getobj = NewNativeFunction(cx, DebuggerArguments_getArg, 0, nullptr,
8429 : gc::AllocKind::FUNCTION_EXTENDED);
8430 : if (!getobj)
8431 : return nullptr;
8432 0 : id = INT_TO_JSID(i);
8433 0 : if (!getobj ||
8434 0 : !NativeDefineAccessorProperty(cx, obj, id,
8435 0 : JS_DATA_TO_FUNC_PTR(GetterOp, getobj.get()), nullptr,
8436 0 : JSPROP_ENUMERATE | JSPROP_GETTER))
8437 0 : {
8438 0 : return nullptr;
8439 0 : }
8440 0 : getobj->setExtendedSlot(0, Int32Value(i));
8441 0 : }
8442 0 :
8443 : return obj;
8444 : }
8445 :
8446 : /* static */ bool
8447 0 : DebuggerFrame::argumentsGetter(JSContext* cx, unsigned argc, Value* vp)
8448 : {
8449 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get arguments", args, frame);
8450 0 :
8451 : RootedDebuggerArguments result(cx);
8452 : if (!DebuggerFrame::getArguments(cx, frame, &result))
8453 : return false;
8454 0 :
8455 : args.rval().setObjectOrNull(result);
8456 0 : return true;
8457 : }
8458 0 :
8459 0 : static bool
8460 : DebuggerFrame_getScript(JSContext* cx, unsigned argc, Value* vp)
8461 : {
8462 0 : THIS_FRAME(cx, argc, vp, "get script", args, thisobj, frameIter, frame);
8463 : Debugger* debug = Debugger::fromChildJSObject(thisobj);
8464 :
8465 : RootedObject scriptObject(cx);
8466 : if (frame.isWasmDebugFrame()) {
8467 0 : RootedWasmInstanceObject instance(cx, frame.wasmInstance()->object());
8468 : scriptObject = debug->wrapWasmScript(cx, instance);
8469 0 : if (!scriptObject)
8470 0 : return false;
8471 : } else {
8472 0 : RootedScript script(cx, frame.script());
8473 0 : scriptObject = debug->wrapScript(cx, script);
8474 0 : if (!scriptObject)
8475 0 : return false;
8476 0 : }
8477 0 :
8478 : MOZ_ASSERT(scriptObject);
8479 0 : args.rval().setObject(*scriptObject);
8480 0 : return true;
8481 0 : }
8482 0 :
8483 : /* static */ bool
8484 : DebuggerFrame::offsetGetter(JSContext* cx, unsigned argc, Value* vp)
8485 0 : {
8486 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get offset", args, frame);
8487 0 :
8488 : size_t result;
8489 : if (!DebuggerFrame::getOffset(cx, frame, result))
8490 : return false;
8491 0 :
8492 : args.rval().setNumber(double(result));
8493 0 : return true;
8494 : }
8495 :
8496 0 : /* static */ bool
8497 : DebuggerFrame::liveGetter(JSContext* cx, unsigned argc, Value* vp)
8498 : {
8499 0 : CallArgs args = CallArgsFromVp(argc, vp);
8500 0 : RootedDebuggerFrame frame(cx, DebuggerFrame_checkThis(cx, args, "get live", false));
8501 : if (!frame)
8502 : return false;
8503 :
8504 0 : args.rval().setBoolean(frame->isLive());
8505 : return true;
8506 0 : }
8507 0 :
8508 0 : static bool
8509 : IsValidHook(const Value& v)
8510 : {
8511 0 : return v.isUndefined() || (v.isObject() && v.toObject().isCallable());
8512 0 : }
8513 :
8514 : /* static */ bool
8515 : DebuggerFrame::onStepGetter(JSContext* cx, unsigned argc, Value* vp)
8516 0 : {
8517 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get onStep", args, frame);
8518 0 :
8519 : OnStepHandler* handler = frame->onStepHandler();
8520 : RootedValue value(cx, handler ? ObjectOrNullValue(handler->object()) : UndefinedValue());
8521 : MOZ_ASSERT(IsValidHook(value));
8522 0 : args.rval().set(value);
8523 : return true;
8524 0 : }
8525 :
8526 0 : /* static */ bool
8527 0 : DebuggerFrame::onStepSetter(JSContext* cx, unsigned argc, Value* vp)
8528 0 : {
8529 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "set onStep", args, frame);
8530 : if (!args.requireAtLeast(cx, "Debugger.Frame.set onStep", 1))
8531 : return false;
8532 : if (!IsValidHook(args[0])) {
8533 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
8534 0 : return false;
8535 : }
8536 0 :
8537 0 : ScriptedOnStepHandler* handler = nullptr;
8538 : if (!args[0].isUndefined()) {
8539 0 : handler = cx->new_<ScriptedOnStepHandler>(&args[0].toObject());
8540 0 : if (!handler)
8541 0 : return false;
8542 : }
8543 :
8544 0 : if (!DebuggerFrame::setOnStepHandler(cx, frame, handler)) {
8545 0 : handler->drop();
8546 0 : return false;
8547 0 : }
8548 :
8549 : args.rval().setUndefined();
8550 : return true;
8551 0 : }
8552 0 :
8553 0 : /* static */ bool
8554 : DebuggerFrame::onPopGetter(JSContext* cx, unsigned argc, Value* vp)
8555 : {
8556 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get onPop", args, frame);
8557 0 :
8558 : OnPopHandler* handler = frame->onPopHandler();
8559 : RootedValue value(cx, handler ? ObjectValue(*handler->object()) : UndefinedValue());
8560 : MOZ_ASSERT(IsValidHook(value));
8561 0 : args.rval().set(value);
8562 : return true;
8563 0 : }
8564 :
8565 0 : /* static */ bool
8566 0 : DebuggerFrame::onPopSetter(JSContext* cx, unsigned argc, Value* vp)
8567 0 : {
8568 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "set onPop", args, frame);
8569 : if (!args.requireAtLeast(cx, "Debugger.Frame.set onPop", 1))
8570 : return false;
8571 : if (!IsValidHook(args[0])) {
8572 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
8573 0 : return false;
8574 : }
8575 0 :
8576 0 : ScriptedOnPopHandler* handler = nullptr;
8577 : if (!args[0].isUndefined()) {
8578 0 : handler = cx->new_<ScriptedOnPopHandler>(&args[0].toObject());
8579 0 : if (!handler)
8580 0 : return false;
8581 : }
8582 :
8583 0 : frame->setOnPopHandler(handler);
8584 0 :
8585 0 : args.rval().setUndefined();
8586 0 : return true;
8587 : }
8588 :
8589 : /* static */ bool
8590 0 : DebuggerFrame::evalMethod(JSContext* cx, unsigned argc, Value* vp)
8591 : {
8592 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "eval", args, frame);
8593 0 : if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.eval", 1))
8594 : return false;
8595 :
8596 : AutoStableStringChars stableChars(cx);
8597 0 : if (!ValueToStableChars(cx, "Debugger.Frame.prototype.eval", args[0], stableChars))
8598 : return false;
8599 0 : mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
8600 0 :
8601 : EvalOptions options;
8602 : if (!ParseEvalOptions(cx, args.get(1), options))
8603 0 : return false;
8604 0 :
8605 : ResumeMode resumeMode;
8606 0 : RootedValue value(cx);
8607 : if (!DebuggerFrame::eval(cx, frame, chars, nullptr, options, resumeMode, &value))
8608 0 : return false;
8609 0 :
8610 : return frame->owner()->newCompletionValue(cx, resumeMode, value, args.rval());
8611 : }
8612 :
8613 0 : /* static */ bool
8614 0 : DebuggerFrame::evalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp)
8615 : {
8616 : THIS_DEBUGGER_FRAME(cx, argc, vp, "evalWithBindings", args, frame);
8617 0 : if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.evalWithBindings", 2))
8618 : return false;
8619 :
8620 : AutoStableStringChars stableChars(cx);
8621 0 : if (!ValueToStableChars(cx, "Debugger.Frame.prototype.evalWithBindings", args[0],
8622 : stableChars))
8623 0 : {
8624 0 : return false;
8625 : }
8626 : mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
8627 0 :
8628 0 : RootedObject bindings(cx, NonNullObject(cx, args[1]));
8629 : if (!bindings)
8630 : return false;
8631 :
8632 : EvalOptions options;
8633 0 : if (!ParseEvalOptions(cx, args.get(2), options))
8634 : return false;
8635 0 :
8636 0 : ResumeMode resumeMode;
8637 : RootedValue value(cx);
8638 : if (!DebuggerFrame::eval(cx, frame, chars, bindings, options, resumeMode, &value))
8639 0 : return false;
8640 0 :
8641 : return frame->owner()->newCompletionValue(cx, resumeMode, value, args.rval());
8642 : }
8643 :
8644 0 : /* static */ bool
8645 0 : DebuggerFrame::construct(JSContext* cx, unsigned argc, Value* vp)
8646 : {
8647 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
8648 0 : "Debugger.Frame");
8649 : return false;
8650 : }
8651 :
8652 0 : const JSPropertySpec DebuggerFrame::properties_[] = {
8653 : JS_PSG("arguments", DebuggerFrame::argumentsGetter, 0),
8654 : JS_PSG("callee", DebuggerFrame::calleeGetter, 0),
8655 0 : JS_PSG("constructing", DebuggerFrame::constructingGetter, 0),
8656 0 : JS_PSG("environment", DebuggerFrame::environmentGetter, 0),
8657 : JS_PSG("generator", DebuggerFrame::generatorGetter, 0),
8658 : JS_PSG("live", DebuggerFrame::liveGetter, 0),
8659 : JS_PSG("offset", DebuggerFrame::offsetGetter, 0),
8660 : JS_PSG("older", DebuggerFrame::olderGetter, 0),
8661 : JS_PSG("script", DebuggerFrame_getScript, 0),
8662 : JS_PSG("this", DebuggerFrame::thisGetter, 0),
8663 : JS_PSG("type", DebuggerFrame::typeGetter, 0),
8664 : JS_PSG("implementation", DebuggerFrame::implementationGetter, 0),
8665 : JS_PSGS("onStep", DebuggerFrame::onStepGetter, DebuggerFrame::onStepSetter, 0),
8666 : JS_PSGS("onPop", DebuggerFrame::onPopGetter, DebuggerFrame::onPopSetter, 0),
8667 : JS_PS_END
8668 : };
8669 :
8670 : const JSFunctionSpec DebuggerFrame::methods_[] = {
8671 : JS_FN("eval", DebuggerFrame::evalMethod, 1, 0),
8672 : JS_FN("evalWithBindings", DebuggerFrame::evalWithBindingsMethod, 1, 0),
8673 : JS_FS_END
8674 : };
8675 :
8676 :
8677 : /*** Debugger.Object *****************************************************************************/
8678 :
8679 : void
8680 : DebuggerObject_trace(JSTracer* trc, JSObject* obj)
8681 : {
8682 : /*
8683 : * There is a barrier on private pointers, so the Unbarriered marking
8684 : * is okay.
8685 : */
8686 : if (JSObject* referent = (JSObject*) obj->as<NativeObject>().getPrivate()) {
8687 0 : TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &referent,
8688 : "Debugger.Object referent");
8689 : obj->as<NativeObject>().setPrivateUnbarriered(referent);
8690 : }
8691 : }
8692 :
8693 0 : static DebuggerObject*
8694 : DebuggerObject_checkThis(JSContext* cx, const CallArgs& args, const char* fnname)
8695 0 : {
8696 0 : JSObject* thisobj = NonNullObject(cx, args.thisv());
8697 : if (!thisobj)
8698 0 : return nullptr;
8699 : if (thisobj->getClass() != &DebuggerObject::class_) {
8700 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
8701 0 : "Debugger.Object", fnname, thisobj->getClass()->name);
8702 : return nullptr;
8703 0 : }
8704 0 :
8705 : /*
8706 0 : * Forbid Debugger.Object.prototype, which is of class DebuggerObject::class_
8707 : * but isn't a real working Debugger.Object. The prototype object is
8708 0 : * distinguished by having no referent.
8709 0 : */
8710 : DebuggerObject* nthisobj = &thisobj->as<DebuggerObject>();
8711 : if (!nthisobj->getPrivate()) {
8712 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
8713 : "Debugger.Object", fnname, "prototype object");
8714 : return nullptr;
8715 : }
8716 : return nthisobj;
8717 0 : }
8718 0 :
8719 : #define THIS_DEBUGOBJECT(cx, argc, vp, fnname, args, object) \
8720 0 : CallArgs args = CallArgsFromVp(argc, vp); \
8721 0 : RootedDebuggerObject object(cx, DebuggerObject_checkThis(cx, args, fnname)); \
8722 : if (!object) \
8723 : return false; \
8724 :
8725 : #define THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj) \
8726 : CallArgs args = CallArgsFromVp(argc, vp); \
8727 : RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname)); \
8728 : if (!obj) \
8729 : return false; \
8730 : obj = (JSObject*) obj->as<NativeObject>().getPrivate(); \
8731 : MOZ_ASSERT(obj)
8732 :
8733 : #define THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj) \
8734 : CallArgs args = CallArgsFromVp(argc, vp); \
8735 : RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname)); \
8736 : if (!obj) \
8737 : return false; \
8738 : Debugger* dbg = Debugger::fromChildJSObject(obj); \
8739 : obj = (JSObject*) obj->as<NativeObject>().getPrivate(); \
8740 : MOZ_ASSERT(obj)
8741 :
8742 : #define THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, fnname, args, obj) \
8743 : THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj); \
8744 : obj = CheckedUnwrap(obj); \
8745 : if (!obj) { \
8746 : ReportAccessDenied(cx); \
8747 : return false; \
8748 : } \
8749 : if (!obj->is<PromiseObject>()) { \
8750 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,\
8751 : "Debugger", "Promise", obj->getClass()->name); \
8752 : return false; \
8753 : } \
8754 : Rooted<PromiseObject*> promise(cx, &obj->as<PromiseObject>());
8755 :
8756 : #define THIS_DEBUGOBJECT_OWNER_PROMISE(cx, argc, vp, fnname, args, dbg, obj) \
8757 : THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj); \
8758 : obj = CheckedUnwrap(obj); \
8759 : if (!obj) { \
8760 : ReportAccessDenied(cx); \
8761 : return false; \
8762 : } \
8763 : if (!obj->is<PromiseObject>()) { \
8764 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,\
8765 : "Debugger", "Promise", obj->getClass()->name); \
8766 : return false; \
8767 : } \
8768 : Rooted<PromiseObject*> promise(cx, &obj->as<PromiseObject>());
8769 :
8770 : /* static */ bool
8771 : DebuggerObject::construct(JSContext* cx, unsigned argc, Value* vp)
8772 : {
8773 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
8774 : "Debugger.Object");
8775 : return false;
8776 : }
8777 :
8778 0 : /* static */ bool
8779 : DebuggerObject::callableGetter(JSContext* cx, unsigned argc, Value* vp)
8780 : {
8781 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get callable", args, object)
8782 0 :
8783 : args.rval().setBoolean(object->isCallable());
8784 : return true;
8785 : }
8786 0 :
8787 : /* static */ bool
8788 0 : DebuggerObject::isBoundFunctionGetter(JSContext* cx, unsigned argc, Value* vp)
8789 : {
8790 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get isBoundFunction", args, object)
8791 0 :
8792 : if (!object->isDebuggeeFunction()) {
8793 : args.rval().setUndefined();
8794 : return true;
8795 0 : }
8796 :
8797 0 : args.rval().setBoolean(object->isBoundFunction());
8798 : return true;
8799 0 : }
8800 0 :
8801 0 : /* static */ bool
8802 : DebuggerObject::isArrowFunctionGetter(JSContext* cx, unsigned argc, Value* vp)
8803 : {
8804 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get isArrowFunction", args, object)
8805 0 :
8806 : if (!object->isDebuggeeFunction()) {
8807 : args.rval().setUndefined();
8808 : return true;
8809 0 : }
8810 :
8811 0 : args.rval().setBoolean(object->isArrowFunction());
8812 : return true;
8813 0 : }
8814 0 :
8815 0 : /* static */ bool
8816 : DebuggerObject::isAsyncFunctionGetter(JSContext* cx, unsigned argc, Value* vp)
8817 : {
8818 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get isAsyncFunction", args, object)
8819 0 :
8820 : if (!object->isDebuggeeFunction()) {
8821 : args.rval().setUndefined();
8822 : return true;
8823 0 : }
8824 :
8825 0 : args.rval().setBoolean(object->isAsyncFunction());
8826 : return true;
8827 0 : }
8828 0 :
8829 0 : /* static */ bool
8830 : DebuggerObject::isGeneratorFunctionGetter(JSContext* cx, unsigned argc, Value* vp)
8831 : {
8832 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get isGeneratorFunction", args, object)
8833 0 :
8834 : if (!object->isDebuggeeFunction()) {
8835 : args.rval().setUndefined();
8836 : return true;
8837 0 : }
8838 :
8839 0 : args.rval().setBoolean(object->isGeneratorFunction());
8840 : return true;
8841 0 : }
8842 0 :
8843 0 : /* static */ bool
8844 : DebuggerObject::protoGetter(JSContext* cx, unsigned argc, Value* vp)
8845 : {
8846 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get proto", args, object)
8847 0 :
8848 : RootedDebuggerObject result(cx);
8849 : if (!DebuggerObject::getPrototypeOf(cx, object, &result))
8850 : return false;
8851 0 :
8852 : args.rval().setObjectOrNull(result);
8853 0 : return true;
8854 : }
8855 0 :
8856 0 : /* static */ bool
8857 : DebuggerObject::classGetter(JSContext* cx, unsigned argc, Value* vp)
8858 : {
8859 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get class", args, object)
8860 :
8861 : RootedString result(cx);
8862 : if (!DebuggerObject::getClassName(cx, object, &result))
8863 : return false;
8864 0 :
8865 : args.rval().setString(result);
8866 0 : return true;
8867 : }
8868 0 :
8869 0 : /* static */ bool
8870 : DebuggerObject::nameGetter(JSContext* cx, unsigned argc, Value* vp)
8871 : {
8872 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get name", args, object)
8873 0 :
8874 : if (!object->isFunction()) {
8875 : args.rval().setUndefined();
8876 : return true;
8877 0 : }
8878 :
8879 0 : RootedString result(cx, object->name(cx));
8880 : if (result)
8881 0 : args.rval().setString(result);
8882 0 : else
8883 0 : args.rval().setUndefined();
8884 : return true;
8885 : }
8886 0 :
8887 0 : /* static */ bool
8888 0 : DebuggerObject::displayNameGetter(JSContext* cx, unsigned argc, Value* vp)
8889 : {
8890 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get displayName", args, object)
8891 :
8892 : if (!object->isFunction()) {
8893 : args.rval().setUndefined();
8894 : return true;
8895 0 : }
8896 :
8897 0 : RootedString result(cx, object->displayName(cx));
8898 : if (result)
8899 0 : args.rval().setString(result);
8900 0 : else
8901 0 : args.rval().setUndefined();
8902 : return true;
8903 : }
8904 0 :
8905 0 : /* static */ bool
8906 0 : DebuggerObject::parameterNamesGetter(JSContext* cx, unsigned argc, Value* vp)
8907 : {
8908 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get parameterNames", args, object)
8909 :
8910 : if (!object->isDebuggeeFunction()) {
8911 : args.rval().setUndefined();
8912 : return true;
8913 0 : }
8914 :
8915 0 : Rooted<StringVector> names(cx, StringVector(cx));
8916 : if (!DebuggerObject::getParameterNames(cx, object, &names))
8917 0 : return false;
8918 0 :
8919 0 : RootedArrayObject obj(cx, NewDenseFullyAllocatedArray(cx, names.length()));
8920 : if (!obj)
8921 : return false;
8922 0 :
8923 0 : obj->ensureDenseInitializedLength(cx, 0, names.length());
8924 : for (size_t i = 0; i < names.length(); ++i) {
8925 : Value v;
8926 0 : if (names[i])
8927 0 : v = StringValue(names[i]);
8928 : else
8929 : v = UndefinedValue();
8930 0 : obj->setDenseElement(i, v);
8931 0 : }
8932 0 :
8933 0 : args.rval().setObject(*obj);
8934 0 : return true;
8935 : }
8936 0 :
8937 0 : /* static */ bool
8938 : DebuggerObject::scriptGetter(JSContext* cx, unsigned argc, Value* vp)
8939 : {
8940 0 : THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get script", args, dbg, obj);
8941 0 :
8942 : if (!obj->is<JSFunction>()) {
8943 : args.rval().setUndefined();
8944 : return true;
8945 0 : }
8946 :
8947 0 : RootedFunction fun(cx, RemoveAsyncWrapper(&obj->as<JSFunction>()));
8948 : if (!fun->isInterpreted()) {
8949 0 : args.rval().setUndefined();
8950 0 : return true;
8951 0 : }
8952 :
8953 : RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
8954 0 : if (!script)
8955 0 : return false;
8956 0 :
8957 0 : /* Only hand out debuggee scripts. */
8958 : if (!dbg->observesScript(script)) {
8959 : args.rval().setNull();
8960 0 : return true;
8961 0 : }
8962 :
8963 : RootedObject scriptObject(cx, dbg->wrapScript(cx, script));
8964 : if (!scriptObject)
8965 0 : return false;
8966 0 :
8967 0 : args.rval().setObject(*scriptObject);
8968 : return true;
8969 : }
8970 0 :
8971 0 : /* static */ bool
8972 : DebuggerObject::environmentGetter(JSContext* cx, unsigned argc, Value* vp)
8973 : {
8974 0 : THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get environment", args, dbg, obj);
8975 0 :
8976 : /* Don't bother switching compartments just to check obj's type and get its env. */
8977 : if (!obj->is<JSFunction>()) {
8978 : args.rval().setUndefined();
8979 0 : return true;
8980 : }
8981 0 :
8982 : RootedFunction fun(cx, RemoveAsyncWrapper(&obj->as<JSFunction>()));
8983 : if (!fun->isInterpreted()) {
8984 0 : args.rval().setUndefined();
8985 0 : return true;
8986 0 : }
8987 :
8988 : /* Only hand out environments of debuggee functions. */
8989 0 : if (!dbg->observesGlobal(&fun->global())) {
8990 0 : args.rval().setNull();
8991 0 : return true;
8992 0 : }
8993 :
8994 : Rooted<Env*> env(cx);
8995 : {
8996 0 : AutoRealm ar(cx, fun);
8997 0 : env = GetDebugEnvironmentForFunction(cx, fun);
8998 0 : if (!env)
8999 : return false;
9000 : }
9001 0 :
9002 : return dbg->wrapEnvironment(cx, env, args.rval());
9003 0 : }
9004 0 :
9005 0 : /* static */ bool
9006 0 : DebuggerObject::boundTargetFunctionGetter(JSContext* cx, unsigned argc, Value* vp)
9007 : {
9008 : THIS_DEBUGOBJECT(cx, argc, vp, "get boundTargetFunction", args, object)
9009 0 :
9010 : if (!object->isDebuggeeFunction() || !object->isBoundFunction()) {
9011 : args.rval().setUndefined();
9012 : return true;
9013 0 : }
9014 :
9015 0 : RootedDebuggerObject result(cx);
9016 : if (!DebuggerObject::getBoundTargetFunction(cx, object, &result))
9017 0 : return false;
9018 0 :
9019 0 : args.rval().setObject(*result);
9020 : return true;
9021 : }
9022 0 :
9023 0 : /* static */ bool
9024 : DebuggerObject::boundThisGetter(JSContext* cx, unsigned argc, Value* vp)
9025 : {
9026 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get boundThis", args, object)
9027 0 :
9028 : if (!object->isDebuggeeFunction() || !object->isBoundFunction()) {
9029 : args.rval().setUndefined();
9030 : return true;
9031 0 : }
9032 :
9033 0 : return DebuggerObject::getBoundThis(cx, object, args.rval());
9034 : }
9035 0 :
9036 0 : /* static */ bool
9037 0 : DebuggerObject::boundArgumentsGetter(JSContext* cx, unsigned argc, Value* vp)
9038 : {
9039 : THIS_DEBUGOBJECT(cx, argc, vp, "get boundArguments", args, object)
9040 0 :
9041 : if (!object->isDebuggeeFunction() || !object->isBoundFunction()) {
9042 : args.rval().setUndefined();
9043 : return true;
9044 0 : }
9045 :
9046 0 : Rooted<ValueVector> result(cx, ValueVector(cx));
9047 : if (!DebuggerObject::getBoundArguments(cx, object, &result))
9048 0 : return false;
9049 0 :
9050 0 : RootedObject obj(cx, NewDenseCopiedArray(cx, result.length(), result.begin()));
9051 : if (!obj)
9052 : return false;
9053 0 :
9054 0 : args.rval().setObject(*obj);
9055 : return true;
9056 : }
9057 0 :
9058 0 : /* static */ bool
9059 : DebuggerObject::globalGetter(JSContext* cx, unsigned argc, Value* vp)
9060 : {
9061 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get global", args, object)
9062 0 :
9063 : RootedDebuggerObject result(cx);
9064 : if (!DebuggerObject::getGlobal(cx, object, &result))
9065 : return false;
9066 0 :
9067 : args.rval().setObject(*result);
9068 0 : return true;
9069 : }
9070 0 :
9071 0 : /* static */ bool
9072 : DebuggerObject::allocationSiteGetter(JSContext* cx, unsigned argc, Value* vp)
9073 : {
9074 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get allocationSite", args, object)
9075 0 :
9076 : RootedObject result(cx);
9077 : if (!DebuggerObject::getAllocationSite(cx, object, &result))
9078 : return false;
9079 0 :
9080 : args.rval().setObjectOrNull(result);
9081 0 : return true;
9082 : }
9083 0 :
9084 0 : // Returns the "name" field (see js.msg), which may be used as a unique
9085 : // identifier, for any error object with a JSErrorReport or undefined
9086 : // if the object has no JSErrorReport.
9087 0 : /* static */ bool
9088 : DebuggerObject::errorMessageNameGetter(JSContext *cx, unsigned argc, Value* vp)
9089 : {
9090 : THIS_DEBUGOBJECT(cx, argc, vp, "get errorMessageName", args, object)
9091 :
9092 : RootedString result(cx);
9093 : if (!DebuggerObject::getErrorMessageName(cx, object, &result))
9094 : return false;
9095 0 :
9096 : if (result)
9097 0 : args.rval().setString(result);
9098 : else
9099 0 : args.rval().setUndefined();
9100 0 : return true;
9101 : }
9102 :
9103 0 : /* static */ bool
9104 0 : DebuggerObject::errorNotesGetter(JSContext *cx, unsigned argc, Value* vp)
9105 : {
9106 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get errorNotes", args, object)
9107 :
9108 : return DebuggerObject::getErrorNotes(cx, object, args.rval());
9109 : }
9110 :
9111 0 : /* static */ bool
9112 : DebuggerObject::errorLineNumberGetter(JSContext *cx, unsigned argc, Value* vp)
9113 0 : {
9114 : THIS_DEBUGOBJECT(cx, argc, vp, "get errorLineNumber", args, object)
9115 0 :
9116 : return DebuggerObject::getErrorLineNumber(cx, object, args.rval());
9117 : }
9118 :
9119 0 : /* static */ bool
9120 : DebuggerObject::errorColumnNumberGetter(JSContext *cx, unsigned argc, Value* vp)
9121 0 : {
9122 : THIS_DEBUGOBJECT(cx, argc, vp, "get errorColumnNumber", args, object)
9123 0 :
9124 : return DebuggerObject::getErrorColumnNumber(cx, object, args.rval());
9125 : }
9126 :
9127 0 : /* static */ bool
9128 : DebuggerObject::isProxyGetter(JSContext* cx, unsigned argc, Value* vp)
9129 0 : {
9130 : THIS_DEBUGOBJECT(cx, argc, vp, "get isProxy", args, object)
9131 0 :
9132 : args.rval().setBoolean(object->isScriptedProxy());
9133 : return true;
9134 : }
9135 0 :
9136 : /* static */ bool
9137 0 : DebuggerObject::proxyTargetGetter(JSContext* cx, unsigned argc, Value* vp)
9138 : {
9139 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get proxyTarget", args, object)
9140 0 :
9141 : if (!object->isScriptedProxy()) {
9142 : args.rval().setUndefined();
9143 : return true;
9144 0 : }
9145 :
9146 0 : Rooted<DebuggerObject*> result(cx);
9147 : if (!DebuggerObject::getScriptedProxyTarget(cx, object, &result))
9148 0 : return false;
9149 0 :
9150 0 : args.rval().setObjectOrNull(result);
9151 : return true;
9152 : }
9153 0 :
9154 0 : /* static */ bool
9155 : DebuggerObject::proxyHandlerGetter(JSContext* cx, unsigned argc, Value* vp)
9156 : {
9157 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get proxyHandler", args, object)
9158 :
9159 : if (!object->isScriptedProxy()) {
9160 : args.rval().setUndefined();
9161 : return true;
9162 0 : }
9163 : Rooted<DebuggerObject*> result(cx);
9164 0 : if (!DebuggerObject::getScriptedProxyHandler(cx, object, &result))
9165 : return false;
9166 0 :
9167 0 : args.rval().setObjectOrNull(result);
9168 0 : return true;
9169 : }
9170 0 :
9171 0 : /* static */ bool
9172 : DebuggerObject::isPromiseGetter(JSContext* cx, unsigned argc, Value* vp)
9173 : {
9174 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get isPromise", args, object)
9175 :
9176 : args.rval().setBoolean(object->isPromise());
9177 : return true;
9178 : }
9179 0 :
9180 : /* static */ bool
9181 0 : DebuggerObject::promiseStateGetter(JSContext* cx, unsigned argc, Value* vp)
9182 : {
9183 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get promiseState", args, object);
9184 0 :
9185 : if (!DebuggerObject::requirePromise(cx, object))
9186 : return false;
9187 :
9188 0 : RootedValue result(cx);
9189 : switch (object->promiseState()) {
9190 0 : case JS::PromiseState::Pending:
9191 : result.setString(cx->names().pending);
9192 0 : break;
9193 : case JS::PromiseState::Fulfilled:
9194 : result.setString(cx->names().fulfilled);
9195 0 : break;
9196 0 : case JS::PromiseState::Rejected:
9197 : result.setString(cx->names().rejected);
9198 0 : break;
9199 : }
9200 :
9201 0 : args.rval().set(result);
9202 : return true;
9203 : }
9204 0 :
9205 : /* static */ bool
9206 : DebuggerObject::promiseValueGetter(JSContext* cx, unsigned argc, Value* vp)
9207 : {
9208 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get promiseValue", args, object);
9209 :
9210 : if (!DebuggerObject::requirePromise(cx, object))
9211 : return false;
9212 :
9213 0 : if (object->promiseState() != JS::PromiseState::Fulfilled) {
9214 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_PROMISE_NOT_FULFILLED);
9215 0 : return false;
9216 : }
9217 0 :
9218 : return DebuggerObject::getPromiseValue(cx, object, args.rval());;
9219 : }
9220 0 :
9221 0 : /* static */ bool
9222 0 : DebuggerObject::promiseReasonGetter(JSContext* cx, unsigned argc, Value* vp)
9223 : {
9224 : THIS_DEBUGOBJECT(cx, argc, vp, "get promiseReason", args, object);
9225 0 :
9226 : if (!DebuggerObject::requirePromise(cx, object))
9227 : return false;
9228 :
9229 0 : if (object->promiseState() != JS::PromiseState::Rejected) {
9230 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_PROMISE_NOT_REJECTED);
9231 0 : return false;
9232 : }
9233 0 :
9234 : return DebuggerObject::getPromiseReason(cx, object, args.rval());;
9235 : }
9236 0 :
9237 0 : /* static */ bool
9238 0 : DebuggerObject::promiseLifetimeGetter(JSContext* cx, unsigned argc, Value* vp)
9239 : {
9240 : THIS_DEBUGOBJECT(cx, argc, vp, "get promiseLifetime", args, object);
9241 0 :
9242 : if (!DebuggerObject::requirePromise(cx, object))
9243 : return false;
9244 :
9245 0 : args.rval().setNumber(object->promiseLifetime());
9246 : return true;
9247 0 : }
9248 :
9249 0 : /* static */ bool
9250 : DebuggerObject::promiseTimeToResolutionGetter(JSContext* cx, unsigned argc, Value* vp)
9251 : {
9252 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get promiseTimeToResolution", args, object);
9253 0 :
9254 : if (!DebuggerObject::requirePromise(cx, object))
9255 : return false;
9256 :
9257 0 : if (object->promiseState() == JS::PromiseState::Pending) {
9258 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_PROMISE_NOT_RESOLVED);
9259 0 : return false;
9260 : }
9261 0 :
9262 : args.rval().setNumber(object->promiseTimeToResolution());
9263 : return true;
9264 0 : }
9265 0 :
9266 0 : /* static */ bool
9267 : DebuggerObject::promiseAllocationSiteGetter(JSContext* cx, unsigned argc, Value* vp)
9268 : {
9269 0 : THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, "get promiseAllocationSite", args, refobj);
9270 0 :
9271 : RootedObject allocSite(cx, promise->allocationSite());
9272 : if (!allocSite) {
9273 : args.rval().setNull();
9274 0 : return true;
9275 : }
9276 0 :
9277 : if (!cx->compartment()->wrap(cx, &allocSite))
9278 0 : return false;
9279 0 : args.rval().set(ObjectValue(*allocSite));
9280 0 : return true;
9281 0 : }
9282 :
9283 : /* static */ bool
9284 0 : DebuggerObject::promiseResolutionSiteGetter(JSContext* cx, unsigned argc, Value* vp)
9285 : {
9286 0 : THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, "get promiseResolutionSite", args, refobj);
9287 0 :
9288 : if (promise->state() == JS::PromiseState::Pending) {
9289 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_PROMISE_NOT_RESOLVED);
9290 : return false;
9291 0 : }
9292 :
9293 0 : RootedObject resolutionSite(cx, promise->resolutionSite());
9294 : if (!resolutionSite) {
9295 0 : args.rval().setNull();
9296 0 : return true;
9297 0 : }
9298 :
9299 : if (!cx->compartment()->wrap(cx, &resolutionSite))
9300 0 : return false;
9301 0 : args.rval().set(ObjectValue(*resolutionSite));
9302 0 : return true;
9303 0 : }
9304 :
9305 : /* static */ bool
9306 0 : DebuggerObject::promiseIDGetter(JSContext* cx, unsigned argc, Value* vp)
9307 : {
9308 0 : THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, "get promiseID", args, refobj);
9309 0 :
9310 : args.rval().setNumber(double(promise->getID()));
9311 : return true;
9312 : }
9313 0 :
9314 : /* static */ bool
9315 0 : DebuggerObject::promiseDependentPromisesGetter(JSContext* cx, unsigned argc, Value* vp)
9316 : {
9317 0 : THIS_DEBUGOBJECT_OWNER_PROMISE(cx, argc, vp, "get promiseDependentPromises", args, dbg, refobj);
9318 :
9319 : Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
9320 : {
9321 : JSAutoRealm ar(cx, promise);
9322 0 : if (!promise->dependentPromises(cx, &values))
9323 : return false;
9324 0 : }
9325 : for (size_t i = 0; i < values.length(); i++) {
9326 0 : if (!dbg->wrapDebuggeeValue(cx, values[i]))
9327 : return false;
9328 0 : }
9329 0 : RootedArrayObject promises(cx);
9330 0 : if (values.length() == 0)
9331 : promises = NewDenseEmptyArray(cx);
9332 0 : else
9333 0 : promises = NewDenseCopiedArray(cx, values.length(), values[0].address());
9334 : if (!promises)
9335 : return false;
9336 0 : args.rval().setObject(*promises);
9337 0 : return true;
9338 0 : }
9339 :
9340 0 : /* static */ bool
9341 0 : DebuggerObject::isExtensibleMethod(JSContext* cx, unsigned argc, Value* vp)
9342 : {
9343 0 : THIS_DEBUGOBJECT(cx, argc, vp, "isExtensible", args, object)
9344 0 :
9345 : bool result;
9346 : if (!DebuggerObject::isExtensible(cx, object, result))
9347 : return false;
9348 0 :
9349 : args.rval().setBoolean(result);
9350 0 : return true;
9351 : }
9352 :
9353 0 : /* static */ bool
9354 : DebuggerObject::isSealedMethod(JSContext* cx, unsigned argc, Value* vp)
9355 : {
9356 0 : THIS_DEBUGOBJECT(cx, argc, vp, "isSealed", args, object)
9357 0 :
9358 : bool result;
9359 : if (!DebuggerObject::isSealed(cx, object, result))
9360 : return false;
9361 0 :
9362 : args.rval().setBoolean(result);
9363 0 : return true;
9364 : }
9365 :
9366 0 : /* static */ bool
9367 : DebuggerObject::isFrozenMethod(JSContext* cx, unsigned argc, Value* vp)
9368 : {
9369 0 : THIS_DEBUGOBJECT(cx, argc, vp, "isFrozen", args, object)
9370 0 :
9371 : bool result;
9372 : if (!DebuggerObject::isFrozen(cx, object, result))
9373 : return false;
9374 0 :
9375 : args.rval().setBoolean(result);
9376 0 : return true;
9377 : }
9378 :
9379 0 : static JSObject*
9380 : IdVectorToArray(JSContext* cx, Handle<IdVector> ids)
9381 : {
9382 0 : Rooted<ValueVector> vals(cx, ValueVector(cx));
9383 0 : if (!vals.growBy(ids.length()))
9384 : return nullptr;
9385 :
9386 : for (size_t i = 0, len = ids.length(); i < len; i++) {
9387 0 : jsid id = ids[i];
9388 : if (JSID_IS_INT(id)) {
9389 0 : JSString* str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
9390 0 : if (!str)
9391 : return nullptr;
9392 : vals[i].setString(str);
9393 0 : } else if (JSID_IS_ATOM(id)) {
9394 0 : vals[i].setString(JSID_TO_STRING(id));
9395 0 : } else if (JSID_IS_SYMBOL(id)) {
9396 0 : vals[i].setSymbol(JSID_TO_SYMBOL(id));
9397 0 : } else {
9398 0 : MOZ_ASSERT_UNREACHABLE("IdVector must contain only string, int, and Symbol jsids");
9399 0 : }
9400 0 : }
9401 0 :
9402 0 : return NewDenseCopiedArray(cx, vals.length(), vals.begin());
9403 0 : }
9404 :
9405 0 : /* static */ bool
9406 : DebuggerObject::getOwnPropertyNamesMethod(JSContext* cx, unsigned argc, Value* vp)
9407 : {
9408 : THIS_DEBUGOBJECT(cx, argc, vp, "getOwnPropertyNames", args, object)
9409 0 :
9410 : Rooted<IdVector> ids(cx, IdVector(cx));
9411 : if (!DebuggerObject::getOwnPropertyNames(cx, object, &ids))
9412 : return false;
9413 0 :
9414 : RootedObject obj(cx, IdVectorToArray(cx, ids));
9415 0 : if (!obj)
9416 : return false;
9417 0 :
9418 0 : args.rval().setObject(*obj);
9419 : return true;
9420 : }
9421 0 :
9422 0 : /* static */ bool
9423 : DebuggerObject::getOwnPropertySymbolsMethod(JSContext* cx, unsigned argc, Value* vp)
9424 : {
9425 0 : THIS_DEBUGOBJECT(cx, argc, vp, "getOwnPropertySymbols", args, object)
9426 0 :
9427 : Rooted<IdVector> ids(cx, IdVector(cx));
9428 : if (!DebuggerObject::getOwnPropertySymbols(cx, object, &ids))
9429 : return false;
9430 0 :
9431 : RootedObject obj(cx, IdVectorToArray(cx, ids));
9432 0 : if (!obj)
9433 : return false;
9434 0 :
9435 0 : args.rval().setObject(*obj);
9436 : return true;
9437 : }
9438 0 :
9439 0 : /* static */ bool
9440 : DebuggerObject::getOwnPropertyDescriptorMethod(JSContext* cx, unsigned argc, Value* vp)
9441 : {
9442 0 : THIS_DEBUGOBJECT(cx, argc, vp, "getOwnPropertyDescriptor", args, object)
9443 0 :
9444 : RootedId id(cx);
9445 : if (!ValueToId<CanGC>(cx, args.get(0), &id))
9446 : return false;
9447 0 :
9448 : Rooted<PropertyDescriptor> desc(cx);
9449 0 : if (!DebuggerObject::getOwnPropertyDescriptor(cx, object, id, &desc))
9450 : return false;
9451 0 :
9452 0 : return JS::FromPropertyDescriptor(cx, desc, args.rval());
9453 : }
9454 :
9455 0 : /* static */ bool
9456 0 : DebuggerObject::preventExtensionsMethod(JSContext* cx, unsigned argc, Value* vp)
9457 : {
9458 : THIS_DEBUGOBJECT(cx, argc, vp, "preventExtensions", args, object)
9459 0 :
9460 : if (!DebuggerObject::preventExtensions(cx, object))
9461 : return false;
9462 :
9463 0 : args.rval().setUndefined();
9464 : return true;
9465 0 : }
9466 :
9467 0 : /* static */ bool
9468 : DebuggerObject::sealMethod(JSContext* cx, unsigned argc, Value* vp)
9469 : {
9470 0 : THIS_DEBUGOBJECT(cx, argc, vp, "seal", args, object)
9471 0 :
9472 : if (!DebuggerObject::seal(cx, object))
9473 : return false;
9474 :
9475 0 : args.rval().setUndefined();
9476 : return true;
9477 0 : }
9478 :
9479 0 : /* static */ bool
9480 : DebuggerObject::freezeMethod(JSContext* cx, unsigned argc, Value* vp)
9481 : {
9482 0 : THIS_DEBUGOBJECT(cx, argc, vp, "freeze", args, object)
9483 0 :
9484 : if (!DebuggerObject::freeze(cx, object))
9485 : return false;
9486 :
9487 0 : args.rval().setUndefined();
9488 : return true;
9489 0 : }
9490 :
9491 0 : /* static */ bool
9492 : DebuggerObject::definePropertyMethod(JSContext* cx, unsigned argc, Value* vp)
9493 : {
9494 0 : THIS_DEBUGOBJECT(cx, argc, vp, "defineProperty", args, object)
9495 0 : if (!args.requireAtLeast(cx, "Debugger.Object.defineProperty", 2))
9496 : return false;
9497 :
9498 : RootedId id(cx);
9499 0 : if (!ValueToId<CanGC>(cx, args[0], &id))
9500 : return false;
9501 0 :
9502 0 : Rooted<PropertyDescriptor> desc(cx);
9503 : if (!ToPropertyDescriptor(cx, args[1], false, &desc))
9504 : return false;
9505 0 :
9506 0 : if (!DebuggerObject::defineProperty(cx, object, id, desc))
9507 : return false;
9508 :
9509 0 : args.rval().setUndefined();
9510 0 : return true;
9511 : }
9512 :
9513 0 : /* static */ bool
9514 : DebuggerObject::definePropertiesMethod(JSContext* cx, unsigned argc, Value* vp)
9515 : {
9516 0 : THIS_DEBUGOBJECT(cx, argc, vp, "defineProperties", args, object);
9517 0 : if (!args.requireAtLeast(cx, "Debugger.Object.defineProperties", 1))
9518 : return false;
9519 :
9520 : RootedValue arg(cx, args[0]);
9521 0 : RootedObject props(cx, ToObject(cx, arg));
9522 : if (!props)
9523 0 : return false;
9524 0 : AutoIdVector ids(cx);
9525 : Rooted<PropertyDescriptorVector> descs(cx, PropertyDescriptorVector(cx));
9526 : if (!ReadPropertyDescriptors(cx, props, false, &ids, &descs))
9527 0 : return false;
9528 0 : Rooted<IdVector> ids2(cx, IdVector(cx));
9529 0 : if (!ids2.append(ids.begin(), ids.end()))
9530 : return false;
9531 0 :
9532 0 : if (!DebuggerObject::defineProperties(cx, object, ids2, descs))
9533 0 : return false;
9534 :
9535 0 : args.rval().setUndefined();
9536 0 : return true;
9537 : }
9538 :
9539 0 : /*
9540 : * This does a non-strict delete, as a matter of API design. The case where the
9541 : * property is non-configurable isn't necessarily exceptional here.
9542 0 : */
9543 0 : /* static */ bool
9544 : DebuggerObject::deletePropertyMethod(JSContext* cx, unsigned argc, Value* vp)
9545 : {
9546 : THIS_DEBUGOBJECT(cx, argc, vp, "deleteProperty", args, object)
9547 :
9548 : RootedId id(cx);
9549 : if (!ValueToId<CanGC>(cx, args.get(0), &id))
9550 : return false;
9551 0 :
9552 : ObjectOpResult result;
9553 0 : if (!DebuggerObject::deleteProperty(cx, object, id, result))
9554 : return false;
9555 0 :
9556 0 : args.rval().setBoolean(result.ok());
9557 : return true;
9558 : }
9559 0 :
9560 0 : /* static */ bool
9561 : DebuggerObject::callMethod(JSContext* cx, unsigned argc, Value* vp)
9562 : {
9563 0 : THIS_DEBUGOBJECT(cx, argc, vp, "call", callArgs, object);
9564 0 :
9565 : RootedValue thisv(cx, callArgs.get(0));
9566 :
9567 : Rooted<ValueVector> args(cx, ValueVector(cx));
9568 0 : if (callArgs.length() >= 2) {
9569 : if (!args.growBy(callArgs.length() - 1))
9570 0 : return false;
9571 : for (size_t i = 1; i < callArgs.length(); ++i)
9572 0 : args[i - 1].set(callArgs[i]);
9573 : }
9574 0 :
9575 0 : return object->call(cx, object, thisv, args, callArgs.rval());
9576 0 : }
9577 :
9578 0 : /* static */ bool
9579 0 : DebuggerObject::applyMethod(JSContext* cx, unsigned argc, Value* vp)
9580 : {
9581 : THIS_DEBUGOBJECT(cx, argc, vp, "apply", callArgs, object);
9582 0 :
9583 : RootedValue thisv(cx, callArgs.get(0));
9584 :
9585 : Rooted<ValueVector> args(cx, ValueVector(cx));
9586 0 : if (callArgs.length() >= 2 && !callArgs[1].isNullOrUndefined()) {
9587 : if (!callArgs[1].isObject()) {
9588 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_APPLY_ARGS,
9589 : js_apply_str);
9590 0 : return false;
9591 : }
9592 0 :
9593 0 : RootedObject argsobj(cx, &callArgs[1].toObject());
9594 0 :
9595 : unsigned argc = 0;
9596 0 : if (!GetLengthProperty(cx, argsobj, &argc))
9597 0 : return false;
9598 : argc = unsigned(Min(argc, ARGS_LENGTH_MAX));
9599 :
9600 0 : if (!args.growBy(argc) || !GetElements(cx, argsobj, argc, args.begin()))
9601 : return false;
9602 0 : }
9603 0 :
9604 0 : return object->call(cx, object, thisv, args, callArgs.rval());
9605 0 : }
9606 :
9607 0 : /* static */ bool
9608 : DebuggerObject::asEnvironmentMethod(JSContext* cx, unsigned argc, Value* vp)
9609 : {
9610 : THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "asEnvironment", args, dbg, referent);
9611 0 : if (!RequireGlobalObject(cx, args.thisv(), referent))
9612 : return false;
9613 :
9614 : Rooted<Env*> env(cx);
9615 0 : {
9616 : AutoRealm ar(cx, referent);
9617 0 : env = GetDebugEnvironmentForGlobalLexicalEnvironment(cx);
9618 0 : if (!env)
9619 : return false;
9620 : }
9621 0 :
9622 : return dbg->wrapEnvironment(cx, env, args.rval());
9623 0 : }
9624 0 :
9625 0 : // Lookup a binding on the referent's global scope and change it to undefined
9626 0 : // if it is an uninitialized lexical, otherwise do nothing. The method's
9627 : // JavaScript return value is true _only_ when an uninitialized lexical has been
9628 : // altered, otherwise it is false.
9629 0 : /* static */ bool
9630 : DebuggerObject::forceLexicalInitializationByNameMethod(JSContext *cx, unsigned argc, Value* vp)
9631 : {
9632 : THIS_DEBUGOBJECT(cx, argc, vp, "forceLexicalInitializationByName", args, object)
9633 : if (!args.requireAtLeast(cx, "Debugger.Object.prototype.forceLexicalInitializationByName", 1))
9634 : return false;
9635 :
9636 : if (!DebuggerObject::requireGlobal(cx, object))
9637 0 : return false;
9638 :
9639 0 : RootedId id(cx);
9640 0 : if (!ValueToIdentifier(cx, args[0], &id))
9641 : return false;
9642 :
9643 0 : bool result;
9644 : if (!DebuggerObject::forceLexicalInitializationByName(cx, object, id, result))
9645 : return false;
9646 0 :
9647 0 : args.rval().setBoolean(result);
9648 : return true;
9649 : }
9650 :
9651 0 : /* static */ bool
9652 : DebuggerObject::executeInGlobalMethod(JSContext* cx, unsigned argc, Value* vp)
9653 : {
9654 0 : THIS_DEBUGOBJECT(cx, argc, vp, "executeInGlobal", args, object);
9655 0 : if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobal", 1))
9656 : return false;
9657 :
9658 : if (!DebuggerObject::requireGlobal(cx, object))
9659 0 : return false;
9660 :
9661 0 : AutoStableStringChars stableChars(cx);
9662 0 : if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobal", args[0],
9663 : stableChars))
9664 : {
9665 0 : return false;
9666 : }
9667 : mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
9668 0 :
9669 0 : EvalOptions options;
9670 : if (!ParseEvalOptions(cx, args.get(1), options))
9671 : return false;
9672 :
9673 : ResumeMode resumeMode;
9674 0 : RootedValue value(cx);
9675 : if (!DebuggerObject::executeInGlobal(cx, object, chars, nullptr, options, resumeMode, &value))
9676 0 : return false;
9677 0 :
9678 : return object->owner()->newCompletionValue(cx, resumeMode, value, args.rval());
9679 : }
9680 :
9681 0 : /* static */ bool
9682 0 : DebuggerObject::executeInGlobalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp)
9683 : {
9684 : THIS_DEBUGOBJECT(cx, argc, vp, "executeInGlobalWithBindings", args, object);
9685 0 : if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", 2))
9686 : return false;
9687 :
9688 : if (!DebuggerObject::requireGlobal(cx, object))
9689 0 : return false;
9690 :
9691 0 : AutoStableStringChars stableChars(cx);
9692 0 : if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", args[0],
9693 : stableChars))
9694 : {
9695 0 : return false;
9696 : }
9697 : mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
9698 0 :
9699 0 : RootedObject bindings(cx, NonNullObject(cx, args[1]));
9700 : if (!bindings)
9701 : return false;
9702 :
9703 : EvalOptions options;
9704 0 : if (!ParseEvalOptions(cx, args.get(2), options))
9705 : return false;
9706 0 :
9707 0 : ResumeMode resumeMode;
9708 : RootedValue value(cx);
9709 : if (!DebuggerObject::executeInGlobal(cx, object, chars, bindings, options, resumeMode, &value))
9710 0 : return false;
9711 0 :
9712 : return object->owner()->newCompletionValue(cx, resumeMode, value, args.rval());
9713 : }
9714 :
9715 0 : /* static */ bool
9716 0 : DebuggerObject::makeDebuggeeValueMethod(JSContext* cx, unsigned argc, Value* vp)
9717 : {
9718 : THIS_DEBUGOBJECT(cx, argc, vp, "makeDebuggeeValue", args, object);
9719 0 : if (!args.requireAtLeast(cx, "Debugger.Object.prototype.makeDebuggeeValue", 1))
9720 : return false;
9721 :
9722 : return DebuggerObject::makeDebuggeeValue(cx, object, args[0], args.rval());
9723 0 : }
9724 :
9725 0 : /* static */ bool
9726 0 : DebuggerObject::unsafeDereferenceMethod(JSContext* cx, unsigned argc, Value* vp)
9727 : {
9728 : THIS_DEBUGOBJECT(cx, argc, vp, "unsafeDereference", args, object);
9729 0 :
9730 : RootedObject result(cx);
9731 : if (!DebuggerObject::unsafeDereference(cx, object, &result))
9732 : return false;
9733 0 :
9734 : args.rval().setObject(*result);
9735 0 : return true;
9736 : }
9737 0 :
9738 0 : /* static */ bool
9739 : DebuggerObject::unwrapMethod(JSContext* cx, unsigned argc, Value* vp)
9740 : {
9741 0 : THIS_DEBUGOBJECT(cx, argc, vp, "unwrap", args, object);
9742 0 :
9743 : RootedDebuggerObject result(cx);
9744 : if (!DebuggerObject::unwrap(cx, object, &result))
9745 : return false;
9746 0 :
9747 : args.rval().setObjectOrNull(result);
9748 0 : return true;
9749 : }
9750 0 :
9751 0 : const JSPropertySpec DebuggerObject::properties_[] = {
9752 : JS_PSG("callable", DebuggerObject::callableGetter, 0),
9753 : JS_PSG("isBoundFunction", DebuggerObject::isBoundFunctionGetter, 0),
9754 0 : JS_PSG("isArrowFunction", DebuggerObject::isArrowFunctionGetter, 0),
9755 : JS_PSG("isGeneratorFunction", DebuggerObject::isGeneratorFunctionGetter, 0),
9756 : JS_PSG("isAsyncFunction", DebuggerObject::isAsyncFunctionGetter, 0),
9757 : JS_PSG("proto", DebuggerObject::protoGetter, 0),
9758 : JS_PSG("class", DebuggerObject::classGetter, 0),
9759 : JS_PSG("name", DebuggerObject::nameGetter, 0),
9760 : JS_PSG("displayName", DebuggerObject::displayNameGetter, 0),
9761 : JS_PSG("parameterNames", DebuggerObject::parameterNamesGetter, 0),
9762 : JS_PSG("script", DebuggerObject::scriptGetter, 0),
9763 : JS_PSG("environment", DebuggerObject::environmentGetter, 0),
9764 : JS_PSG("boundTargetFunction", DebuggerObject::boundTargetFunctionGetter, 0),
9765 : JS_PSG("boundThis", DebuggerObject::boundThisGetter, 0),
9766 : JS_PSG("boundArguments", DebuggerObject::boundArgumentsGetter, 0),
9767 : JS_PSG("global", DebuggerObject::globalGetter, 0),
9768 : JS_PSG("allocationSite", DebuggerObject::allocationSiteGetter, 0),
9769 : JS_PSG("errorMessageName", DebuggerObject::errorMessageNameGetter, 0),
9770 : JS_PSG("errorNotes", DebuggerObject::errorNotesGetter, 0),
9771 : JS_PSG("errorLineNumber", DebuggerObject::errorLineNumberGetter, 0),
9772 : JS_PSG("errorColumnNumber", DebuggerObject::errorColumnNumberGetter, 0),
9773 : JS_PSG("isProxy", DebuggerObject::isProxyGetter, 0),
9774 : JS_PSG("proxyTarget", DebuggerObject::proxyTargetGetter, 0),
9775 : JS_PSG("proxyHandler", DebuggerObject::proxyHandlerGetter, 0),
9776 : JS_PS_END
9777 : };
9778 :
9779 : const JSPropertySpec DebuggerObject::promiseProperties_[] = {
9780 : JS_PSG("isPromise", DebuggerObject::isPromiseGetter, 0),
9781 : JS_PSG("promiseState", DebuggerObject::promiseStateGetter, 0),
9782 : JS_PSG("promiseValue", DebuggerObject::promiseValueGetter, 0),
9783 : JS_PSG("promiseReason", DebuggerObject::promiseReasonGetter, 0),
9784 : JS_PSG("promiseLifetime", DebuggerObject::promiseLifetimeGetter, 0),
9785 : JS_PSG("promiseTimeToResolution", DebuggerObject::promiseTimeToResolutionGetter, 0),
9786 : JS_PSG("promiseAllocationSite", DebuggerObject::promiseAllocationSiteGetter, 0),
9787 : JS_PSG("promiseResolutionSite", DebuggerObject::promiseResolutionSiteGetter, 0),
9788 : JS_PSG("promiseID", DebuggerObject::promiseIDGetter, 0),
9789 : JS_PSG("promiseDependentPromises", DebuggerObject::promiseDependentPromisesGetter, 0),
9790 : JS_PS_END
9791 : };
9792 :
9793 : const JSFunctionSpec DebuggerObject::methods_[] = {
9794 : JS_FN("isExtensible", DebuggerObject::isExtensibleMethod, 0, 0),
9795 : JS_FN("isSealed", DebuggerObject::isSealedMethod, 0, 0),
9796 : JS_FN("isFrozen", DebuggerObject::isFrozenMethod, 0, 0),
9797 : JS_FN("getOwnPropertyNames", DebuggerObject::getOwnPropertyNamesMethod, 0, 0),
9798 : JS_FN("getOwnPropertySymbols", DebuggerObject::getOwnPropertySymbolsMethod, 0, 0),
9799 : JS_FN("getOwnPropertyDescriptor", DebuggerObject::getOwnPropertyDescriptorMethod, 1, 0),
9800 : JS_FN("preventExtensions", DebuggerObject::preventExtensionsMethod, 0, 0),
9801 : JS_FN("seal", DebuggerObject::sealMethod, 0, 0),
9802 : JS_FN("freeze", DebuggerObject::freezeMethod, 0, 0),
9803 : JS_FN("defineProperty", DebuggerObject::definePropertyMethod, 2, 0),
9804 : JS_FN("defineProperties", DebuggerObject::definePropertiesMethod, 1, 0),
9805 : JS_FN("deleteProperty", DebuggerObject::deletePropertyMethod, 1, 0),
9806 : JS_FN("call", DebuggerObject::callMethod, 0, 0),
9807 : JS_FN("apply", DebuggerObject::applyMethod, 0, 0),
9808 : JS_FN("asEnvironment", DebuggerObject::asEnvironmentMethod, 0, 0),
9809 : JS_FN("forceLexicalInitializationByName", DebuggerObject::forceLexicalInitializationByNameMethod, 1, 0),
9810 : JS_FN("executeInGlobal", DebuggerObject::executeInGlobalMethod, 1, 0),
9811 : JS_FN("executeInGlobalWithBindings", DebuggerObject::executeInGlobalWithBindingsMethod, 2, 0),
9812 : JS_FN("makeDebuggeeValue", DebuggerObject::makeDebuggeeValueMethod, 1, 0),
9813 : JS_FN("unsafeDereference", DebuggerObject::unsafeDereferenceMethod, 0, 0),
9814 : JS_FN("unwrap", DebuggerObject::unwrapMethod, 0, 0),
9815 : JS_FS_END
9816 : };
9817 :
9818 : /* static */ NativeObject*
9819 : DebuggerObject::initClass(JSContext* cx, Handle<GlobalObject*> global, HandleObject debugCtor)
9820 : {
9821 : RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
9822 :
9823 : RootedNativeObject objectProto(cx, InitClass(cx, debugCtor, objProto, &class_,
9824 : construct, 0, properties_,
9825 : methods_, nullptr, nullptr));
9826 0 :
9827 : if (!objectProto)
9828 0 : return nullptr;
9829 :
9830 0 : if (!DefinePropertiesAndFunctions(cx, objectProto, promiseProperties_, nullptr))
9831 : return nullptr;
9832 0 :
9833 : return objectProto;
9834 0 : }
9835 :
9836 : /* static */ DebuggerObject*
9837 0 : DebuggerObject::create(JSContext* cx, HandleObject proto, HandleObject referent,
9838 : HandleNativeObject debugger)
9839 : {
9840 0 : NewObjectKind newKind = IsInsideNursery(referent) ? GenericObject : TenuredObject;
9841 : DebuggerObject* obj = NewObjectWithGivenProto<DebuggerObject>(cx, proto, newKind);
9842 : if (!obj)
9843 : return nullptr;
9844 0 :
9845 : obj->setPrivateGCThing(referent);
9846 : obj->setReservedSlot(JSSLOT_DEBUGOBJECT_OWNER, ObjectValue(*debugger));
9847 0 :
9848 0 : return obj;
9849 0 : }
9850 :
9851 : bool
9852 0 : DebuggerObject::isCallable() const
9853 0 : {
9854 : return referent()->isCallable();
9855 0 : }
9856 :
9857 : bool
9858 : DebuggerObject::isFunction() const
9859 0 : {
9860 : return referent()->is<JSFunction>();
9861 0 : }
9862 :
9863 : bool
9864 : DebuggerObject::isDebuggeeFunction() const
9865 0 : {
9866 : return referent()->is<JSFunction>() &&
9867 0 : owner()->observesGlobal(&referent()->as<JSFunction>().global());
9868 : }
9869 :
9870 : bool
9871 0 : DebuggerObject::isBoundFunction() const
9872 : {
9873 0 : MOZ_ASSERT(isDebuggeeFunction());
9874 0 :
9875 : return referent()->isBoundFunction();
9876 : }
9877 :
9878 0 : bool
9879 : DebuggerObject::isArrowFunction() const
9880 0 : {
9881 : MOZ_ASSERT(isDebuggeeFunction());
9882 0 :
9883 : return RemoveAsyncWrapper(&referent()->as<JSFunction>())->isArrow();
9884 : }
9885 :
9886 0 : bool
9887 : DebuggerObject::isAsyncFunction() const
9888 0 : {
9889 : MOZ_ASSERT(isDebuggeeFunction());
9890 0 :
9891 : return RemoveAsyncWrapper(&referent()->as<JSFunction>())->isAsync();
9892 : }
9893 :
9894 0 : bool
9895 : DebuggerObject::isGeneratorFunction() const
9896 0 : {
9897 : MOZ_ASSERT(isDebuggeeFunction());
9898 0 :
9899 : JSFunction* fun = RemoveAsyncWrapper(&referent()->as<JSFunction>());
9900 : return fun->isGenerator();
9901 : }
9902 0 :
9903 : bool
9904 0 : DebuggerObject::isGlobal() const
9905 : {
9906 0 : return referent()->is<GlobalObject>();
9907 0 : }
9908 :
9909 : bool
9910 : DebuggerObject::isScriptedProxy() const
9911 0 : {
9912 : return js::IsScriptedProxy(referent());
9913 0 : }
9914 :
9915 : bool
9916 : DebuggerObject::isPromise() const
9917 0 : {
9918 : JSObject* referent = this->referent();
9919 0 :
9920 : if (IsCrossCompartmentWrapper(referent)) {
9921 : referent = CheckedUnwrap(referent);
9922 : if (!referent)
9923 0 : return false;
9924 : }
9925 0 :
9926 : return referent->is<PromiseObject>();
9927 0 : }
9928 0 :
9929 0 : /* static */ bool
9930 : DebuggerObject::getClassName(JSContext* cx, HandleDebuggerObject object,
9931 : MutableHandleString result)
9932 : {
9933 0 : RootedObject referent(cx, object->referent());
9934 :
9935 : const char* className;
9936 : {
9937 0 : AutoRealm ar(cx, referent);
9938 : className = GetObjectClassName(cx, referent);
9939 : }
9940 0 :
9941 : JSAtom* str = Atomize(cx, className, strlen(className));
9942 : if (!str)
9943 : return false;
9944 0 :
9945 0 : result.set(str);
9946 : return true;
9947 : }
9948 0 :
9949 0 : /* static */ bool
9950 : DebuggerObject::getGlobal(JSContext* cx, HandleDebuggerObject object,
9951 : MutableHandleDebuggerObject result)
9952 0 : {
9953 0 : RootedObject referent(cx, object->referent());
9954 : Debugger* dbg = object->owner();
9955 :
9956 : RootedObject global(cx, &referent->deprecatedGlobal());
9957 0 : return dbg->wrapDebuggeeObject(cx, global, result);
9958 : }
9959 :
9960 0 : JSAtom*
9961 0 : DebuggerObject::name(JSContext* cx) const
9962 : {
9963 0 : MOZ_ASSERT(isFunction());
9964 0 :
9965 : JSAtom* atom = referent()->as<JSFunction>().explicitName();
9966 : if (atom)
9967 : cx->markAtom(atom);
9968 0 : return atom;
9969 : }
9970 0 :
9971 : JSAtom*
9972 0 : DebuggerObject::displayName(JSContext* cx) const
9973 0 : {
9974 0 : MOZ_ASSERT(isFunction());
9975 0 :
9976 : JSAtom* atom = referent()->as<JSFunction>().displayAtom();
9977 : if (atom)
9978 : cx->markAtom(atom);
9979 0 : return atom;
9980 : }
9981 0 :
9982 : JS::PromiseState
9983 0 : DebuggerObject::promiseState() const
9984 0 : {
9985 0 : return promise()->state();
9986 0 : }
9987 :
9988 : double
9989 : DebuggerObject::promiseLifetime() const
9990 0 : {
9991 : return promise()->lifetime();
9992 0 : }
9993 :
9994 : double
9995 : DebuggerObject::promiseTimeToResolution() const
9996 0 : {
9997 : MOZ_ASSERT(promiseState() != JS::PromiseState::Pending);
9998 0 :
9999 : return promise()->timeToResolution();
10000 : }
10001 :
10002 0 : /* static */ bool
10003 : DebuggerObject::getParameterNames(JSContext* cx, HandleDebuggerObject object,
10004 0 : MutableHandle<StringVector> result)
10005 : {
10006 0 : MOZ_ASSERT(object->isDebuggeeFunction());
10007 :
10008 : RootedFunction referent(cx, RemoveAsyncWrapper(&object->referent()->as<JSFunction>()));
10009 :
10010 0 : if (!result.growBy(referent->nargs()))
10011 : return false;
10012 : if (referent->isInterpreted()) {
10013 0 : RootedScript script(cx, GetOrCreateFunctionScript(cx, referent));
10014 : if (!script)
10015 0 : return false;
10016 :
10017 0 : MOZ_ASSERT(referent->nargs() == script->numArgs());
10018 :
10019 0 : if (referent->nargs() > 0) {
10020 0 : PositionalFormalParameterIter fi(script);
10021 0 : for (size_t i = 0; i < referent->nargs(); i++, fi++) {
10022 0 : MOZ_ASSERT(fi.argumentSlot() == i);
10023 : JSAtom* atom = fi.name();
10024 0 : if (atom)
10025 : cx->markAtom(atom);
10026 0 : result[i].set(atom);
10027 0 : }
10028 0 : }
10029 0 : } else {
10030 0 : for (size_t i = 0; i < referent->nargs(); i++)
10031 0 : result[i].set(nullptr);
10032 0 : }
10033 0 :
10034 : return true;
10035 : }
10036 :
10037 0 : /* static */ bool
10038 0 : DebuggerObject::getBoundTargetFunction(JSContext* cx, HandleDebuggerObject object,
10039 : MutableHandleDebuggerObject result)
10040 : {
10041 : MOZ_ASSERT(object->isBoundFunction());
10042 :
10043 : RootedFunction referent(cx, &object->referent()->as<JSFunction>());
10044 : Debugger* dbg = object->owner();
10045 0 :
10046 : RootedObject target(cx, referent->getBoundFunctionTarget());
10047 : return dbg->wrapDebuggeeObject(cx, target, result);
10048 0 : }
10049 :
10050 0 : /* static */ bool
10051 0 : DebuggerObject::getBoundThis(JSContext* cx, HandleDebuggerObject object,
10052 : MutableHandleValue result)
10053 0 : {
10054 0 : MOZ_ASSERT(object->isBoundFunction());
10055 :
10056 : RootedFunction referent(cx, &object->referent()->as<JSFunction>());
10057 : Debugger* dbg = object->owner();
10058 0 :
10059 : result.set(referent->getBoundFunctionThis());
10060 : return dbg->wrapDebuggeeValue(cx, result);
10061 0 : }
10062 :
10063 0 : /* static */ bool
10064 0 : DebuggerObject::getBoundArguments(JSContext* cx, HandleDebuggerObject object,
10065 : MutableHandle<ValueVector> result)
10066 0 : {
10067 0 : MOZ_ASSERT(object->isBoundFunction());
10068 :
10069 : RootedFunction referent(cx, &object->referent()->as<JSFunction>());
10070 : Debugger* dbg = object->owner();
10071 0 :
10072 : size_t length = referent->getBoundFunctionArgumentCount();
10073 : if (!result.resize(length))
10074 0 : return false;
10075 : for (size_t i = 0; i < length; i++) {
10076 0 : result[i].set(referent->getBoundFunctionArgument(i));
10077 0 : if (!dbg->wrapDebuggeeValue(cx, result[i]))
10078 : return false;
10079 0 : }
10080 0 : return true;
10081 : }
10082 0 :
10083 0 : /* static */ SavedFrame*
10084 0 : Debugger::getObjectAllocationSite(JSObject& obj)
10085 : {
10086 : JSObject* metadata = GetAllocationMetadata(&obj);
10087 : if (!metadata)
10088 : return nullptr;
10089 :
10090 : MOZ_ASSERT(!metadata->is<WrapperObject>());
10091 0 : return SavedFrame::isSavedFrameAndNotProto(*metadata)
10092 : ? &metadata->as<SavedFrame>()
10093 0 : : nullptr;
10094 0 : }
10095 :
10096 : /* static */ bool
10097 0 : DebuggerObject::getAllocationSite(JSContext* cx, HandleDebuggerObject object,
10098 0 : MutableHandleObject result)
10099 0 : {
10100 : RootedObject referent(cx, object->referent());
10101 :
10102 : RootedObject allocSite(cx, Debugger::getObjectAllocationSite(*referent));
10103 : if (!cx->compartment()->wrap(cx, &allocSite))
10104 0 : return false;
10105 :
10106 : result.set(allocSite);
10107 0 : return true;
10108 : }
10109 0 :
10110 0 : /* static */ bool
10111 : DebuggerObject::getErrorReport(JSContext* cx, HandleObject maybeError, JSErrorReport*& report)
10112 : {
10113 0 : JSObject* obj = maybeError;
10114 0 : if (IsCrossCompartmentWrapper(obj))
10115 : obj = CheckedUnwrap(obj);
10116 :
10117 : if (!obj) {
10118 0 : ReportAccessDenied(cx);
10119 : return false;
10120 0 : }
10121 0 :
10122 0 : if (!obj->is<ErrorObject>()) {
10123 : report = nullptr;
10124 0 : return true;
10125 0 : }
10126 0 :
10127 : report = obj->as<ErrorObject>().getErrorReport();
10128 : return true;
10129 0 : }
10130 0 :
10131 0 : /* static */ bool
10132 : DebuggerObject::getErrorMessageName(JSContext* cx, HandleDebuggerObject object,
10133 : MutableHandleString result)
10134 0 : {
10135 0 : RootedObject referent(cx, object->referent());
10136 : JSErrorReport* report;
10137 : if (!getErrorReport(cx, referent, report))
10138 : return false;
10139 0 :
10140 : if (!report) {
10141 : result.set(nullptr);
10142 0 : return true;
10143 : }
10144 0 :
10145 : const JSErrorFormatString* efs = GetErrorMessage(nullptr, report->errorNumber);
10146 : if (!efs) {
10147 0 : result.set(nullptr);
10148 0 : return true;
10149 0 : }
10150 :
10151 : RootedString str(cx, JS_NewStringCopyZ(cx, efs->name));
10152 0 : if (!cx->compartment()->wrap(cx, &str))
10153 0 : return false;
10154 0 :
10155 0 : result.set(str);
10156 : return true;
10157 : }
10158 0 :
10159 0 : /* static */ bool
10160 : DebuggerObject::getErrorNotes(JSContext* cx, HandleDebuggerObject object,
10161 : MutableHandleValue result)
10162 0 : {
10163 0 : RootedObject referent(cx, object->referent());
10164 : JSErrorReport* report;
10165 : if (!getErrorReport(cx, referent, report))
10166 : return false;
10167 0 :
10168 : if (!report) {
10169 : result.setUndefined();
10170 0 : return true;
10171 : }
10172 0 :
10173 : RootedObject errorNotesArray(cx, CreateErrorNotesArray(cx, report));
10174 : if (!errorNotesArray)
10175 0 : return false;
10176 0 :
10177 0 : if (!cx->compartment()->wrap(cx, &errorNotesArray))
10178 : return false;
10179 : result.setObject(*errorNotesArray);
10180 0 : return true;
10181 0 : }
10182 :
10183 : /* static */ bool
10184 0 : DebuggerObject::getErrorLineNumber(JSContext* cx, HandleDebuggerObject object,
10185 : MutableHandleValue result)
10186 0 : {
10187 0 : RootedObject referent(cx, object->referent());
10188 : JSErrorReport* report;
10189 : if (!getErrorReport(cx, referent, report))
10190 : return false;
10191 0 :
10192 : if (!report) {
10193 : result.setUndefined();
10194 0 : return true;
10195 : }
10196 0 :
10197 : result.setNumber(report->lineno);
10198 : return true;
10199 0 : }
10200 0 :
10201 0 : /* static */ bool
10202 : DebuggerObject::getErrorColumnNumber(JSContext* cx, HandleDebuggerObject object,
10203 : MutableHandleValue result)
10204 0 : {
10205 : RootedObject referent(cx, object->referent());
10206 : JSErrorReport* report;
10207 : if (!getErrorReport(cx, referent, report))
10208 : return false;
10209 0 :
10210 : if (!report) {
10211 : result.setUndefined();
10212 0 : return true;
10213 : }
10214 0 :
10215 : result.setNumber(report->column);
10216 : return true;
10217 0 : }
10218 0 :
10219 0 : /* static */ bool
10220 : DebuggerObject::getPromiseValue(JSContext* cx, HandleDebuggerObject object,
10221 : MutableHandleValue result)
10222 0 : {
10223 : MOZ_ASSERT(object->promiseState() == JS::PromiseState::Fulfilled);
10224 :
10225 : result.set(object->promise()->value());
10226 : return object->owner()->wrapDebuggeeValue(cx, result);
10227 0 : }
10228 :
10229 : /* static */ bool
10230 0 : DebuggerObject::getPromiseReason(JSContext* cx, HandleDebuggerObject object,
10231 : MutableHandleValue result)
10232 0 : {
10233 0 : MOZ_ASSERT(object->promiseState() == JS::PromiseState::Rejected);
10234 :
10235 : result.set(object->promise()->reason());
10236 : return object->owner()->wrapDebuggeeValue(cx, result);
10237 0 : }
10238 :
10239 : /* static */ bool
10240 0 : DebuggerObject::isExtensible(JSContext* cx, HandleDebuggerObject object, bool& result)
10241 : {
10242 0 : RootedObject referent(cx, object->referent());
10243 0 :
10244 : Maybe<AutoRealm> ar;
10245 : ar.emplace(cx, referent);
10246 : ErrorCopier ec(ar);
10247 0 : return IsExtensible(cx, referent, &result);
10248 : }
10249 0 :
10250 : /* static */ bool
10251 0 : DebuggerObject::isSealed(JSContext* cx, HandleDebuggerObject object, bool& result)
10252 0 : {
10253 0 : RootedObject referent(cx, object->referent());
10254 0 :
10255 : Maybe<AutoRealm> ar;
10256 : ar.emplace(cx, referent);
10257 :
10258 0 : ErrorCopier ec(ar);
10259 : return TestIntegrityLevel(cx, referent, IntegrityLevel::Sealed, &result);
10260 0 : }
10261 :
10262 0 : /* static */ bool
10263 0 : DebuggerObject::isFrozen(JSContext* cx, HandleDebuggerObject object, bool& result)
10264 : {
10265 0 : RootedObject referent(cx, object->referent());
10266 0 :
10267 : Maybe<AutoRealm> ar;
10268 : ar.emplace(cx, referent);
10269 :
10270 0 : ErrorCopier ec(ar);
10271 : return TestIntegrityLevel(cx, referent, IntegrityLevel::Frozen, &result);
10272 0 : }
10273 :
10274 0 : /* static */ bool
10275 0 : DebuggerObject::getPrototypeOf(JSContext* cx, HandleDebuggerObject object,
10276 : MutableHandleDebuggerObject result)
10277 0 : {
10278 0 : RootedObject referent(cx, object->referent());
10279 : Debugger* dbg = object->owner();
10280 :
10281 : RootedObject proto(cx);
10282 0 : {
10283 : AutoRealm ar(cx, referent);
10284 : if (!GetPrototype(cx, referent, &proto))
10285 0 : return false;
10286 0 : }
10287 :
10288 0 : if (!proto) {
10289 : result.set(nullptr);
10290 0 : return true;
10291 0 : }
10292 0 :
10293 : return dbg->wrapDebuggeeObject(cx, proto, result);
10294 : }
10295 0 :
10296 0 : /* static */ bool
10297 0 : DebuggerObject::getOwnPropertyNames(JSContext* cx, HandleDebuggerObject object,
10298 : MutableHandle<IdVector> result)
10299 : {
10300 0 : RootedObject referent(cx, object->referent());
10301 :
10302 : AutoIdVector ids(cx);
10303 : {
10304 0 : Maybe<AutoRealm> ar;
10305 : ar.emplace(cx, referent);
10306 :
10307 0 : ErrorCopier ec(ar);
10308 : if (!GetPropertyKeys(cx, referent, JSITER_OWNONLY | JSITER_HIDDEN, &ids))
10309 0 : return false;
10310 : }
10311 0 :
10312 0 : for (size_t i = 0; i < ids.length(); i++)
10313 : cx->markId(ids[i]);
10314 0 :
10315 0 : return result.append(ids.begin(), ids.end());
10316 0 : }
10317 :
10318 : /* static */ bool
10319 0 : DebuggerObject::getOwnPropertySymbols(JSContext* cx, HandleDebuggerObject object,
10320 0 : MutableHandle<IdVector> result)
10321 : {
10322 0 : RootedObject referent(cx, object->referent());
10323 :
10324 : AutoIdVector ids(cx);
10325 : {
10326 0 : Maybe<AutoRealm> ar;
10327 : ar.emplace(cx, referent);
10328 :
10329 0 : ErrorCopier ec(ar);
10330 : if (!GetPropertyKeys(cx, referent,
10331 0 : JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY,
10332 : &ids))
10333 0 : return false;
10334 0 : }
10335 :
10336 0 : for (size_t i = 0; i < ids.length(); i++)
10337 0 : cx->markId(ids[i]);
10338 :
10339 : return result.append(ids.begin(), ids.end());
10340 0 : }
10341 :
10342 : /* static */ bool
10343 0 : DebuggerObject::getOwnPropertyDescriptor(JSContext* cx, HandleDebuggerObject object,
10344 0 : HandleId id, MutableHandle<PropertyDescriptor> desc)
10345 : {
10346 0 : RootedObject referent(cx, object->referent());
10347 : Debugger* dbg = object->owner();
10348 :
10349 : /* Bug: This can cause the debuggee to run! */
10350 0 : {
10351 : Maybe<AutoRealm> ar;
10352 : ar.emplace(cx, referent);
10353 0 : cx->markId(id);
10354 0 :
10355 : ErrorCopier ec(ar);
10356 : if (!GetOwnPropertyDescriptor(cx, referent, id, desc))
10357 : return false;
10358 0 : }
10359 0 :
10360 0 : if (desc.object()) {
10361 : /* Rewrap the debuggee values in desc for the debugger. */
10362 0 : if (!dbg->wrapDebuggeeValue(cx, desc.value()))
10363 0 : return false;
10364 0 :
10365 : if (desc.hasGetterObject()) {
10366 : RootedValue get(cx, ObjectOrNullValue(desc.getterObject()));
10367 0 : if (!dbg->wrapDebuggeeValue(cx, &get))
10368 : return false;
10369 0 : desc.setGetterObject(get.toObjectOrNull());
10370 : }
10371 : if (desc.hasSetterObject()) {
10372 0 : RootedValue set(cx, ObjectOrNullValue(desc.setterObject()));
10373 0 : if (!dbg->wrapDebuggeeValue(cx, &set))
10374 0 : return false;
10375 0 : desc.setSetterObject(set.toObjectOrNull());
10376 0 : }
10377 :
10378 0 : // Avoid tripping same-compartment assertions in JS::FromPropertyDescriptor().
10379 0 : desc.object().set(object);
10380 0 : }
10381 0 :
10382 0 : return true;
10383 : }
10384 :
10385 : /* static */ bool
10386 0 : DebuggerObject::preventExtensions(JSContext* cx, HandleDebuggerObject object)
10387 : {
10388 : RootedObject referent(cx, object->referent());
10389 :
10390 : Maybe<AutoRealm> ar;
10391 : ar.emplace(cx, referent);
10392 :
10393 0 : ErrorCopier ec(ar);
10394 : return PreventExtensions(cx, referent);
10395 0 : }
10396 :
10397 0 : /* static */ bool
10398 0 : DebuggerObject::seal(JSContext* cx, HandleDebuggerObject object)
10399 : {
10400 0 : RootedObject referent(cx, object->referent());
10401 0 :
10402 : Maybe<AutoRealm> ar;
10403 : ar.emplace(cx, referent);
10404 :
10405 0 : ErrorCopier ec(ar);
10406 : return SetIntegrityLevel(cx, referent, IntegrityLevel::Sealed);
10407 0 : }
10408 :
10409 0 : /* static */ bool
10410 0 : DebuggerObject::freeze(JSContext* cx, HandleDebuggerObject object)
10411 : {
10412 0 : RootedObject referent(cx, object->referent());
10413 0 :
10414 : Maybe<AutoRealm> ar;
10415 : ar.emplace(cx, referent);
10416 :
10417 0 : ErrorCopier ec(ar);
10418 : return SetIntegrityLevel(cx, referent, IntegrityLevel::Frozen);
10419 0 : }
10420 :
10421 0 : /* static */ bool
10422 0 : DebuggerObject::defineProperty(JSContext* cx, HandleDebuggerObject object, HandleId id,
10423 : Handle<PropertyDescriptor> desc_)
10424 0 : {
10425 0 : RootedObject referent(cx, object->referent());
10426 : Debugger* dbg = object->owner();
10427 :
10428 : Rooted<PropertyDescriptor> desc(cx, desc_);
10429 0 : if (!dbg->unwrapPropertyDescriptor(cx, referent, &desc))
10430 : return false;
10431 : JS_TRY_OR_RETURN_FALSE(cx, CheckPropertyDescriptorAccessors(cx, desc));
10432 0 :
10433 0 : Maybe<AutoRealm> ar;
10434 : ar.emplace(cx, referent);
10435 0 : if (!cx->compartment()->wrap(cx, &desc))
10436 0 : return false;
10437 : cx->markId(id);
10438 0 :
10439 : ErrorCopier ec(ar);
10440 0 : if (!DefineProperty(cx, referent, id, desc))
10441 0 : return false;
10442 0 :
10443 : return true;
10444 0 : }
10445 :
10446 0 : /* static */ bool
10447 0 : DebuggerObject::defineProperties(JSContext* cx, HandleDebuggerObject object,
10448 : Handle<IdVector> ids,
10449 : Handle<PropertyDescriptorVector> descs_)
10450 0 : {
10451 : RootedObject referent(cx, object->referent());
10452 : Debugger* dbg = object->owner();
10453 :
10454 0 : Rooted<PropertyDescriptorVector> descs(cx, PropertyDescriptorVector(cx));
10455 : if (!descs.append(descs_.begin(), descs_.end()))
10456 : return false;
10457 : for (size_t i = 0; i < descs.length(); i++) {
10458 0 : if (!dbg->unwrapPropertyDescriptor(cx, referent, descs[i]))
10459 0 : return false;
10460 : JS_TRY_OR_RETURN_FALSE(cx, CheckPropertyDescriptorAccessors(cx, descs[i]));
10461 0 : }
10462 0 :
10463 : Maybe<AutoRealm> ar;
10464 0 : ar.emplace(cx, referent);
10465 0 : for (size_t i = 0; i < descs.length(); i++) {
10466 : if (!cx->compartment()->wrap(cx, descs[i]))
10467 0 : return false;
10468 : cx->markId(ids[i]);
10469 : }
10470 0 :
10471 0 : ErrorCopier ec(ar);
10472 0 : for (size_t i = 0; i < descs.length(); i++) {
10473 0 : if (!DefineProperty(cx, referent, ids[i], descs[i]))
10474 : return false;
10475 0 : }
10476 :
10477 : return true;
10478 0 : }
10479 0 :
10480 0 : /* static */ bool
10481 : DebuggerObject::deleteProperty(JSContext* cx, HandleDebuggerObject object, HandleId id,
10482 : ObjectOpResult& result)
10483 : {
10484 : RootedObject referent(cx, object->referent());
10485 :
10486 : Maybe<AutoRealm> ar;
10487 : ar.emplace(cx, referent);
10488 0 :
10489 : cx->markId(id);
10490 :
10491 0 : ErrorCopier ec(ar);
10492 : return DeleteProperty(cx, referent, id, result);
10493 0 : }
10494 0 :
10495 : /* static */ bool
10496 0 : DebuggerObject::call(JSContext* cx, HandleDebuggerObject object, HandleValue thisv_,
10497 : Handle<ValueVector> args, MutableHandleValue result)
10498 0 : {
10499 0 : RootedObject referent(cx, object->referent());
10500 : Debugger* dbg = object->owner();
10501 :
10502 : if (!referent->isCallable()) {
10503 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
10504 : "Debugger.Object", "call", referent->getClass()->name);
10505 : return false;
10506 0 : }
10507 0 :
10508 : RootedValue calleev(cx, ObjectValue(*referent));
10509 0 :
10510 : /*
10511 0 : * Unwrap Debugger.Objects. This happens in the debugger's compartment since
10512 0 : * that is where any exceptions must be reported.
10513 : */
10514 : RootedValue thisv(cx, thisv_);
10515 0 : if (!dbg->unwrapDebuggeeValue(cx, &thisv))
10516 : return false;
10517 : Rooted<ValueVector> args2(cx, ValueVector(cx));
10518 : if (!args2.append(args.begin(), args.end()))
10519 : return false;
10520 : for (unsigned i = 0; i < args2.length(); ++i) {
10521 0 : if (!dbg->unwrapDebuggeeValue(cx, args2[i]))
10522 0 : return false;
10523 : }
10524 0 :
10525 0 : /*
10526 : * Enter the debuggee compartment and rewrap all input value for that compartment.
10527 0 : * (Rewrapping always takes place in the destination compartment.)
10528 0 : */
10529 : Maybe<AutoRealm> ar;
10530 : ar.emplace(cx, referent);
10531 : if (!cx->compartment()->wrap(cx, &calleev) || !cx->compartment()->wrap(cx, &thisv))
10532 : return false;
10533 : for (unsigned i = 0; i < args2.length(); ++i) {
10534 : if (!cx->compartment()->wrap(cx, args2[i]))
10535 : return false;
10536 0 : }
10537 0 :
10538 0 : /*
10539 : * Call the function. Use receiveCompletionValue to return to the debugger
10540 0 : * compartment and populate args.rval().
10541 0 : */
10542 : LeaveDebuggeeNoExecute nnx(cx);
10543 :
10544 : bool ok;
10545 : {
10546 : InvokeArgs invokeArgs(cx);
10547 :
10548 : ok = invokeArgs.init(cx, args2.length());
10549 0 : if (ok) {
10550 : for (size_t i = 0; i < args2.length(); ++i)
10551 : invokeArgs[i].set(args2[i]);
10552 :
10553 0 : ok = js::Call(cx, calleev, thisv, invokeArgs, result);
10554 : }
10555 0 : }
10556 0 :
10557 0 : return dbg->receiveCompletionValue(ar, ok, result, result);
10558 0 : }
10559 :
10560 0 : /* static */ bool
10561 : DebuggerObject::forceLexicalInitializationByName(JSContext* cx, HandleDebuggerObject object,
10562 : HandleId id, bool& result)
10563 : {
10564 0 : if (!JSID_IS_STRING(id)) {
10565 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
10566 : "Debugger.Object.prototype.forceLexicalInitializationByName",
10567 : "string", InformalValueTypeName(IdToValue(id)));
10568 0 : return false;
10569 : }
10570 :
10571 0 : MOZ_ASSERT(object->isGlobal());
10572 0 :
10573 : Rooted<GlobalObject*> referent(cx, &object->referent()->as<GlobalObject>());
10574 0 :
10575 0 : RootedObject globalLexical(cx, &referent->lexicalEnvironment());
10576 : RootedObject pobj(cx);
10577 : Rooted<PropertyResult> prop(cx);
10578 0 : if (!LookupProperty(cx, globalLexical, id, &pobj, &prop))
10579 : return false;
10580 0 :
10581 : result = false;
10582 0 : if (prop) {
10583 0 : MOZ_ASSERT(prop.isNativeProperty());
10584 0 : Shape* shape = prop.shape();
10585 0 : Value v = globalLexical->as<NativeObject>().getSlot(shape->slot());
10586 : if (shape->isDataProperty() && v.isMagic() && v.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
10587 : globalLexical->as<NativeObject>().setSlot(shape->slot(), UndefinedValue());
10588 0 : result = true;
10589 0 : }
10590 0 : }
10591 0 :
10592 0 : return true;
10593 0 : }
10594 0 :
10595 0 : /* static */ bool
10596 : DebuggerObject::executeInGlobal(JSContext* cx, HandleDebuggerObject object,
10597 : mozilla::Range<const char16_t> chars, HandleObject bindings,
10598 : const EvalOptions& options, ResumeMode& resumeMode,
10599 : MutableHandleValue value)
10600 : {
10601 : MOZ_ASSERT(object->isGlobal());
10602 :
10603 0 : Rooted<GlobalObject*> referent(cx, &object->referent()->as<GlobalObject>());
10604 : Debugger* dbg = object->owner();
10605 :
10606 : RootedObject globalLexical(cx, &referent->lexicalEnvironment());
10607 : return DebuggerGenericEval(cx, chars, bindings, options, resumeMode, value, dbg, globalLexical,
10608 0 : nullptr);
10609 : }
10610 0 :
10611 0 : /* static */ bool
10612 : DebuggerObject::makeDebuggeeValue(JSContext* cx, HandleDebuggerObject object,
10613 0 : HandleValue value_, MutableHandleValue result)
10614 0 : {
10615 0 : RootedObject referent(cx, object->referent());
10616 : Debugger* dbg = object->owner();
10617 :
10618 : RootedValue value(cx, value_);
10619 0 :
10620 : /* Non-objects are already debuggee values. */
10621 : if (value.isObject()) {
10622 0 : // Enter this Debugger.Object's referent's compartment, and wrap the
10623 0 : // argument as appropriate for references from there.
10624 : {
10625 0 : AutoRealm ar(cx, referent);
10626 : if (!cx->compartment()->wrap(cx, &value))
10627 : return false;
10628 0 : }
10629 :
10630 : // Back in the debugger's compartment, produce a new Debugger.Object
10631 : // instance referring to the wrapped argument.
10632 0 : if (!dbg->wrapDebuggeeValue(cx, &value))
10633 0 : return false;
10634 0 : }
10635 :
10636 : result.set(value);
10637 : return true;
10638 : }
10639 0 :
10640 : /* static */ bool
10641 : DebuggerObject::unsafeDereference(JSContext* cx, HandleDebuggerObject object,
10642 : MutableHandleObject result)
10643 0 : {
10644 0 : RootedObject referent(cx, object->referent());
10645 :
10646 : if (!cx->compartment()->wrap(cx, &referent))
10647 : return false;
10648 0 :
10649 : // Wrapping should return the WindowProxy.
10650 : MOZ_ASSERT(!IsWindow(referent));
10651 0 :
10652 : result.set(referent);
10653 0 : return true;
10654 : }
10655 :
10656 : /* static */ bool
10657 0 : DebuggerObject::unwrap(JSContext* cx, HandleDebuggerObject object,
10658 : MutableHandleDebuggerObject result)
10659 0 : {
10660 0 : RootedObject referent(cx, object->referent());
10661 : Debugger* dbg = object->owner();
10662 :
10663 : RootedObject unwrapped(cx, UnwrapOneChecked(referent));
10664 0 : if (!unwrapped) {
10665 : result.set(nullptr);
10666 : return true;
10667 0 : }
10668 0 :
10669 : // Don't allow unwrapping to create a D.O whose referent is in an
10670 0 : // invisible-to-Debugger global. (If our referent is a *wrapper* to such,
10671 0 : // and the wrapper is in a visible realm, that's fine.)
10672 0 : if (unwrapped->realm()->creationOptions().invisibleToDebugger()) {
10673 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_INVISIBLE_COMPARTMENT);
10674 : return false;
10675 : }
10676 :
10677 : return dbg->wrapDebuggeeObject(cx, unwrapped, result);
10678 : }
10679 0 :
10680 0 : /* static */ bool
10681 0 : DebuggerObject::requireGlobal(JSContext* cx, HandleDebuggerObject object)
10682 : {
10683 : if (!object->isGlobal()) {
10684 0 : RootedObject referent(cx, object->referent());
10685 :
10686 : const char* isWrapper = "";
10687 : const char* isWindowProxy = "";
10688 0 :
10689 : /* Help the poor programmer by pointing out wrappers around globals... */
10690 0 : if (referent->is<WrapperObject>()) {
10691 0 : referent = js::UncheckedUnwrap(referent);
10692 : isWrapper = "a wrapper around ";
10693 0 : }
10694 0 :
10695 : /* ... and WindowProxies around Windows. */
10696 : if (IsWindowProxy(referent)) {
10697 0 : referent = ToWindowIfWindowProxy(referent);
10698 0 : isWindowProxy = "a WindowProxy referring to ";
10699 0 : }
10700 :
10701 : RootedValue dbgobj(cx, ObjectValue(*object));
10702 : if (referent->is<GlobalObject>()) {
10703 0 : ReportValueError(cx, JSMSG_DEBUG_WRAPPER_IN_WAY, JSDVG_SEARCH_STACK, dbgobj, nullptr,
10704 0 : isWrapper, isWindowProxy);
10705 0 : } else {
10706 : ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, dbgobj, nullptr,
10707 : "a global object");
10708 0 : }
10709 0 : return false;
10710 0 : }
10711 :
10712 0 : return true;
10713 : }
10714 0 :
10715 : /* static */ bool
10716 0 : DebuggerObject::requirePromise(JSContext* cx, HandleDebuggerObject object)
10717 : {
10718 : RootedObject referent(cx, object->referent());
10719 :
10720 : if (IsCrossCompartmentWrapper(referent)) {
10721 : referent = CheckedUnwrap(referent);
10722 : if (!referent) {
10723 : ReportAccessDenied(cx);
10724 : return false;
10725 0 : }
10726 : }
10727 0 :
10728 : if (!referent->is<PromiseObject>()) {
10729 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
10730 0 : "Debugger", "Promise", object->getClass()->name);
10731 0 : return false;
10732 0 : }
10733 0 :
10734 : return true;
10735 : }
10736 :
10737 0 : /* static */ bool
10738 : DebuggerObject::getScriptedProxyTarget(JSContext* cx, HandleDebuggerObject object,
10739 0 : MutableHandleDebuggerObject result)
10740 0 : {
10741 : MOZ_ASSERT(object->isScriptedProxy());
10742 : RootedObject referent(cx, object->referent());
10743 : Debugger* dbg = object->owner();
10744 : RootedObject unwrapped(cx, js::GetProxyTargetObject(referent));
10745 : if(!unwrapped) {
10746 : result.set(nullptr);
10747 0 : return true;
10748 : }
10749 : return dbg->wrapDebuggeeObject(cx, unwrapped, result);
10750 0 : }
10751 0 :
10752 0 : /* static */ bool
10753 0 : DebuggerObject::getScriptedProxyHandler(JSContext* cx, HandleDebuggerObject object,
10754 0 : MutableHandleDebuggerObject result)
10755 0 : {
10756 0 : MOZ_ASSERT(object->isScriptedProxy());
10757 : RootedObject referent(cx, object->referent());
10758 0 : Debugger* dbg = object->owner();
10759 : RootedObject unwrapped(cx, ScriptedProxyHandler::handlerObject(referent));
10760 : if(!unwrapped) {
10761 : result.set(nullptr);
10762 0 : return true;
10763 : }
10764 : return dbg->wrapDebuggeeObject(cx, unwrapped, result);
10765 0 : }
10766 0 :
10767 0 :
10768 0 : /*** Debugger.Environment ************************************************************************/
10769 0 :
10770 0 : void
10771 0 : DebuggerEnv_trace(JSTracer* trc, JSObject* obj)
10772 : {
10773 0 : /*
10774 : * There is a barrier on private pointers, so the Unbarriered marking
10775 : * is okay.
10776 : */
10777 : if (Env* referent = (JSObject*) obj->as<NativeObject>().getPrivate()) {
10778 : TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &referent,
10779 : "Debugger.Environment referent");
10780 0 : obj->as<NativeObject>().setPrivateUnbarriered(referent);
10781 : }
10782 : }
10783 :
10784 : static DebuggerEnvironment*
10785 : DebuggerEnvironment_checkThis(JSContext* cx, const CallArgs& args, const char* fnname,
10786 0 : bool requireDebuggee)
10787 : {
10788 0 : JSObject* thisobj = NonNullObject(cx, args.thisv());
10789 0 : if (!thisobj)
10790 : return nullptr;
10791 0 : if (thisobj->getClass() != &DebuggerEnvironment::class_) {
10792 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
10793 : "Debugger.Environment", fnname, thisobj->getClass()->name);
10794 0 : return nullptr;
10795 : }
10796 :
10797 0 : /*
10798 0 : * Forbid Debugger.Environment.prototype, which is of class DebuggerEnvironment::class_
10799 : * but isn't a real working Debugger.Environment. The prototype object is
10800 0 : * distinguished by having no referent.
10801 : */
10802 0 : DebuggerEnvironment* nthisobj = &thisobj->as<DebuggerEnvironment>();
10803 0 : if (!nthisobj->getPrivate()) {
10804 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
10805 : "Debugger.Environment", fnname, "prototype object");
10806 : return nullptr;
10807 : }
10808 :
10809 : /*
10810 : * Forbid access to Debugger.Environment objects that are not debuggee
10811 0 : * environments.
10812 0 : */
10813 : if (requireDebuggee) {
10814 0 : Rooted<Env*> env(cx, static_cast<Env*>(nthisobj->getPrivate()));
10815 0 : if (!Debugger::fromChildJSObject(nthisobj)->observesGlobal(&env->nonCCWGlobal())) {
10816 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_DEBUGGEE,
10817 : "Debugger.Environment", "environment");
10818 : return nullptr;
10819 : }
10820 : }
10821 :
10822 0 : return nthisobj;
10823 0 : }
10824 0 :
10825 : #define THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, fnname, args, environment) \
10826 0 : CallArgs args = CallArgsFromVp(argc, vp); \
10827 0 : Rooted<DebuggerEnvironment*> environment(cx, DebuggerEnvironment_checkThis(cx, args, fnname, false)); \
10828 : if (!environment) \
10829 : return false; \
10830 :
10831 : /* static */ bool
10832 : DebuggerEnvironment::construct(JSContext* cx, unsigned argc, Value* vp)
10833 : {
10834 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
10835 : "Debugger.Environment");
10836 : return false;
10837 : }
10838 :
10839 : static bool
10840 : IsDeclarative(Env* env)
10841 0 : {
10842 : return env->is<DebugEnvironmentProxy>() &&
10843 : env->as<DebugEnvironmentProxy>().isForDeclarative();
10844 0 : }
10845 0 :
10846 : template <typename T>
10847 : static bool
10848 : IsDebugEnvironmentWrapper(Env* env)
10849 0 : {
10850 : return env->is<DebugEnvironmentProxy>() &&
10851 0 : env->as<DebugEnvironmentProxy>().environment().is<T>();
10852 0 : }
10853 :
10854 : bool
10855 : DebuggerEnvironment::typeGetter(JSContext* cx, unsigned argc, Value* vp)
10856 : {
10857 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get type", args, environment);
10858 :
10859 0 : if (!environment->requireDebuggee(cx))
10860 0 : return false;
10861 :
10862 : DebuggerEnvironmentType type = environment->type();
10863 :
10864 0 : const char* s;
10865 : switch (type) {
10866 0 : case DebuggerEnvironmentType::Declarative:
10867 : s = "declarative";
10868 0 : break;
10869 : case DebuggerEnvironmentType::With:
10870 : s = "with";
10871 0 : break;
10872 : case DebuggerEnvironmentType::Object:
10873 : s = "object";
10874 0 : break;
10875 : }
10876 0 :
10877 0 : JSAtom* str = Atomize(cx, s, strlen(s), PinAtom);
10878 : if (!str)
10879 0 : return false;
10880 0 :
10881 : args.rval().setString(str);
10882 0 : return true;
10883 0 : }
10884 :
10885 : /* static */ bool
10886 0 : DebuggerEnvironment::parentGetter(JSContext* cx, unsigned argc, Value* vp)
10887 0 : {
10888 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get type", args, environment);
10889 :
10890 0 : if (!environment->requireDebuggee(cx))
10891 0 : return false;
10892 :
10893 : RootedDebuggerEnvironment result(cx);
10894 : if (!environment->getParent(cx, &result))
10895 0 : return false;
10896 :
10897 0 : args.rval().setObjectOrNull(result);
10898 : return true;
10899 0 : }
10900 :
10901 : /* static */ bool
10902 0 : DebuggerEnvironment::objectGetter(JSContext* cx, unsigned argc, Value* vp)
10903 0 : {
10904 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get type", args, environment);
10905 :
10906 0 : if (!environment->requireDebuggee(cx))
10907 : return false;
10908 :
10909 : if (environment->type() == DebuggerEnvironmentType::Declarative) {
10910 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NO_ENV_OBJECT);
10911 0 : return false;
10912 : }
10913 0 :
10914 : RootedDebuggerObject result(cx);
10915 0 : if (!environment->getObject(cx, &result))
10916 : return false;
10917 :
10918 0 : args.rval().setObject(*result);
10919 0 : return true;
10920 0 : }
10921 :
10922 : /* static */ bool
10923 0 : DebuggerEnvironment::calleeGetter(JSContext* cx, unsigned argc, Value* vp)
10924 0 : {
10925 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get callee", args, environment);
10926 :
10927 0 : if (!environment->requireDebuggee(cx))
10928 0 : return false;
10929 :
10930 : RootedDebuggerObject result(cx);
10931 : if (!environment->getCallee(cx, &result))
10932 0 : return false;
10933 :
10934 0 : args.rval().setObjectOrNull(result);
10935 : return true;
10936 0 : }
10937 :
10938 : /* static */ bool
10939 0 : DebuggerEnvironment::inspectableGetter(JSContext* cx, unsigned argc, Value* vp)
10940 0 : {
10941 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get inspectable", args, environment);
10942 :
10943 0 : args.rval().setBoolean(environment->isDebuggee());
10944 : return true;
10945 : }
10946 :
10947 : /* static */ bool
10948 0 : DebuggerEnvironment::optimizedOutGetter(JSContext* cx, unsigned argc, Value* vp)
10949 : {
10950 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get optimizedOut", args, environment);
10951 :
10952 0 : args.rval().setBoolean(environment->isOptimized());
10953 0 : return true;
10954 : }
10955 :
10956 : /* static */ bool
10957 0 : DebuggerEnvironment::namesMethod(JSContext* cx, unsigned argc, Value* vp)
10958 : {
10959 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "names", args, environment);
10960 :
10961 0 : if (!environment->requireDebuggee(cx))
10962 0 : return false;
10963 :
10964 : Rooted<IdVector> ids(cx, IdVector(cx));
10965 : if (!DebuggerEnvironment::getNames(cx, environment, &ids))
10966 0 : return false;
10967 :
10968 0 : RootedObject obj(cx, IdVectorToArray(cx, ids));
10969 : if (!obj)
10970 0 : return false;
10971 :
10972 : args.rval().setObject(*obj);
10973 0 : return true;
10974 0 : }
10975 :
10976 : /* static */ bool
10977 0 : DebuggerEnvironment::findMethod(JSContext* cx, unsigned argc, Value* vp)
10978 0 : {
10979 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "find", args, environment);
10980 : if (!args.requireAtLeast(cx, "Debugger.Environment.find", 1))
10981 0 : return false;
10982 0 :
10983 : if (!environment->requireDebuggee(cx))
10984 : return false;
10985 :
10986 0 : RootedId id(cx);
10987 : if (!ValueToIdentifier(cx, args[0], &id))
10988 0 : return false;
10989 0 :
10990 : RootedDebuggerEnvironment result(cx);
10991 : if (!DebuggerEnvironment::find(cx, environment, id, &result))
10992 0 : return false;
10993 :
10994 : args.rval().setObjectOrNull(result);
10995 0 : return true;
10996 0 : }
10997 :
10998 : /* static */ bool
10999 0 : DebuggerEnvironment::getVariableMethod(JSContext* cx, unsigned argc, Value* vp)
11000 0 : {
11001 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "getVariable", args, environment);
11002 : if (!args.requireAtLeast(cx, "Debugger.Environment.getVariable", 1))
11003 0 : return false;
11004 :
11005 : if (!environment->requireDebuggee(cx))
11006 : return false;
11007 :
11008 0 : RootedId id(cx);
11009 : if (!ValueToIdentifier(cx, args[0], &id))
11010 0 : return false;
11011 0 :
11012 : return DebuggerEnvironment::getVariable(cx, environment, id, args.rval());
11013 : }
11014 0 :
11015 : /* static */ bool
11016 : DebuggerEnvironment::setVariableMethod(JSContext* cx, unsigned argc, Value* vp)
11017 0 : {
11018 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "setVariable", args, environment);
11019 : if (!args.requireAtLeast(cx, "Debugger.Environment.setVariable", 2))
11020 : return false;
11021 0 :
11022 : if (!environment->requireDebuggee(cx))
11023 : return false;
11024 :
11025 0 : RootedId id(cx);
11026 : if (!ValueToIdentifier(cx, args[0], &id))
11027 0 : return false;
11028 0 :
11029 : if (!DebuggerEnvironment::setVariable(cx, environment, id, args[1]))
11030 : return false;
11031 0 :
11032 : args.rval().setUndefined();
11033 : return true;
11034 0 : }
11035 0 :
11036 : bool
11037 : DebuggerEnvironment::requireDebuggee(JSContext* cx) const
11038 0 : {
11039 : if (!isDebuggee()) {
11040 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_DEBUGGEE,
11041 0 : "Debugger.Environment", "environment");
11042 0 :
11043 : return false;
11044 : }
11045 :
11046 0 : return true;
11047 : }
11048 0 :
11049 : const JSPropertySpec DebuggerEnvironment::properties_[] = {
11050 0 : JS_PSG("type", DebuggerEnvironment::typeGetter, 0),
11051 : JS_PSG("parent", DebuggerEnvironment::parentGetter, 0),
11052 0 : JS_PSG("object", DebuggerEnvironment::objectGetter, 0),
11053 : JS_PSG("callee", DebuggerEnvironment::calleeGetter, 0),
11054 : JS_PSG("inspectable", DebuggerEnvironment::inspectableGetter, 0),
11055 : JS_PSG("optimizedOut", DebuggerEnvironment::optimizedOutGetter, 0),
11056 : JS_PS_END
11057 : };
11058 :
11059 : const JSFunctionSpec DebuggerEnvironment::methods_[] = {
11060 : JS_FN("names", DebuggerEnvironment::namesMethod, 0, 0),
11061 : JS_FN("find", DebuggerEnvironment::findMethod, 1, 0),
11062 : JS_FN("getVariable", DebuggerEnvironment::getVariableMethod, 1, 0),
11063 : JS_FN("setVariable", DebuggerEnvironment::setVariableMethod, 2, 0),
11064 : JS_FS_END
11065 : };
11066 :
11067 : /* static */ NativeObject*
11068 : DebuggerEnvironment::initClass(JSContext* cx, HandleObject dbgCtor, Handle<GlobalObject*> global)
11069 : {
11070 : RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
11071 :
11072 : return InitClass(cx, dbgCtor, objProto, &DebuggerEnvironment::class_, construct, 0,
11073 : properties_, methods_, nullptr, nullptr);
11074 : }
11075 :
11076 : /* static */ DebuggerEnvironment*
11077 0 : DebuggerEnvironment::create(JSContext* cx, HandleObject proto, HandleObject referent,
11078 : HandleNativeObject debugger)
11079 0 : {
11080 : NewObjectKind newKind = IsInsideNursery(referent) ? GenericObject : TenuredObject;
11081 0 : DebuggerEnvironment* obj = NewObjectWithGivenProto<DebuggerEnvironment>(cx, proto, newKind);
11082 0 : if (!obj)
11083 : return nullptr;
11084 :
11085 : obj->setPrivateGCThing(referent);
11086 0 : obj->setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));
11087 :
11088 : return obj;
11089 0 : }
11090 0 :
11091 0 : /* static */ DebuggerEnvironmentType
11092 : DebuggerEnvironment::type() const
11093 : {
11094 0 : /* Don't bother switching compartments just to check env's type. */
11095 0 : if (IsDeclarative(referent()))
11096 : return DebuggerEnvironmentType::Declarative;
11097 0 : if (IsDebugEnvironmentWrapper<WithEnvironmentObject>(referent()))
11098 : return DebuggerEnvironmentType::With;
11099 : return DebuggerEnvironmentType::Object;
11100 : }
11101 0 :
11102 : bool
11103 : DebuggerEnvironment::getParent(JSContext* cx, MutableHandleDebuggerEnvironment result) const
11104 0 : {
11105 : /* Don't bother switching compartments just to get env's parent. */
11106 0 : Rooted<Env*> parent(cx, referent()->enclosingEnvironment());
11107 : if (!parent) {
11108 0 : result.set(nullptr);
11109 : return true;
11110 : }
11111 :
11112 0 : return owner()->wrapEnvironment(cx, parent, result);
11113 : }
11114 :
11115 0 : bool
11116 0 : DebuggerEnvironment::getObject(JSContext* cx, MutableHandleDebuggerObject result) const
11117 0 : {
11118 0 : MOZ_ASSERT(type() != DebuggerEnvironmentType::Declarative);
11119 :
11120 : /* Don't bother switching compartments just to get env's object. */
11121 0 : RootedObject object(cx);
11122 : if (IsDebugEnvironmentWrapper<WithEnvironmentObject>(referent())) {
11123 : object.set(&referent()->as<DebugEnvironmentProxy>()
11124 : .environment().as<WithEnvironmentObject>().object());
11125 0 : } else if (IsDebugEnvironmentWrapper<NonSyntacticVariablesObject>(referent())) {
11126 : object.set(&referent()->as<DebugEnvironmentProxy>()
11127 0 : .environment().as<NonSyntacticVariablesObject>());
11128 : } else {
11129 : object.set(referent());
11130 0 : MOZ_ASSERT(!object->is<DebugEnvironmentProxy>());
11131 0 : }
11132 0 :
11133 0 : return owner()->wrapDebuggeeObject(cx, object, result);
11134 0 : }
11135 0 :
11136 0 : bool
11137 : DebuggerEnvironment::getCallee(JSContext* cx, MutableHandleDebuggerObject result) const
11138 0 : {
11139 0 : if (!referent()->is<DebugEnvironmentProxy>()) {
11140 : result.set(nullptr);
11141 : return true;
11142 0 : }
11143 :
11144 : JSObject& scope = referent()->as<DebugEnvironmentProxy>().environment();
11145 : if (!scope.is<CallObject>()) {
11146 0 : result.set(nullptr);
11147 : return true;
11148 0 : }
11149 0 :
11150 0 : RootedObject callee(cx, &scope.as<CallObject>().callee());
11151 : if (IsInternalFunctionObject(*callee)) {
11152 : result.set(nullptr);
11153 0 : return true;
11154 0 : }
11155 0 :
11156 0 : return owner()->wrapDebuggeeObject(cx, callee, result);
11157 : }
11158 :
11159 0 : bool
11160 0 : DebuggerEnvironment::isDebuggee() const
11161 0 : {
11162 0 : MOZ_ASSERT(referent());
11163 : MOZ_ASSERT(!referent()->is<EnvironmentObject>());
11164 :
11165 0 : return owner()->observesGlobal(&referent()->nonCCWGlobal());
11166 : }
11167 :
11168 : bool
11169 0 : DebuggerEnvironment::isOptimized() const
11170 : {
11171 0 : return referent()->is<DebugEnvironmentProxy>() &&
11172 0 : referent()->as<DebugEnvironmentProxy>().isOptimizedOut();
11173 : }
11174 0 :
11175 : /* static */ bool
11176 : DebuggerEnvironment::getNames(JSContext* cx, HandleDebuggerEnvironment environment,
11177 : MutableHandle<IdVector> result)
11178 0 : {
11179 : MOZ_ASSERT(environment->isDebuggee());
11180 0 :
11181 0 : Rooted<Env*> referent(cx, environment->referent());
11182 :
11183 : AutoIdVector ids(cx);
11184 : {
11185 0 : Maybe<AutoRealm> ar;
11186 : ar.emplace(cx, referent);
11187 :
11188 0 : ErrorCopier ec(ar);
11189 : if (!GetPropertyKeys(cx, referent, JSITER_HIDDEN, &ids))
11190 0 : return false;
11191 : }
11192 0 :
11193 : for (size_t i = 0; i < ids.length(); ++i) {
11194 0 : jsid id = ids[i];
11195 0 : if (JSID_IS_ATOM(id) && IsIdentifier(JSID_TO_ATOM(id))) {
11196 : cx->markId(id);
11197 0 : if (!result.append(id))
11198 0 : return false;
11199 0 : }
11200 : }
11201 :
11202 0 : return true;
11203 0 : }
11204 0 :
11205 0 : /* static */ bool
11206 0 : DebuggerEnvironment::find(JSContext* cx, HandleDebuggerEnvironment environment, HandleId id,
11207 0 : MutableHandleDebuggerEnvironment result)
11208 : {
11209 : MOZ_ASSERT(environment->isDebuggee());
11210 :
11211 : Rooted<Env*> env(cx, environment->referent());
11212 : Debugger* dbg = environment->owner();
11213 :
11214 : {
11215 0 : Maybe<AutoRealm> ar;
11216 : ar.emplace(cx, env);
11217 :
11218 0 : cx->markId(id);
11219 :
11220 0 : /* This can trigger resolve hooks. */
11221 0 : ErrorCopier ec(ar);
11222 : for (; env; env = env->enclosingEnvironment()) {
11223 : bool found;
11224 0 : if (!HasProperty(cx, env, id, &found))
11225 0 : return false;
11226 : if (found)
11227 0 : break;
11228 : }
11229 : }
11230 0 :
11231 0 : if (!env) {
11232 : result.set(nullptr);
11233 0 : return true;
11234 0 : }
11235 0 :
11236 : return dbg->wrapEnvironment(cx, env, result);
11237 : }
11238 :
11239 : /* static */ bool
11240 0 : DebuggerEnvironment::getVariable(JSContext* cx, HandleDebuggerEnvironment environment,
11241 0 : HandleId id, MutableHandleValue result)
11242 0 : {
11243 : MOZ_ASSERT(environment->isDebuggee());
11244 :
11245 0 : Rooted<Env*> referent(cx, environment->referent());
11246 : Debugger* dbg = environment->owner();
11247 :
11248 : {
11249 0 : Maybe<AutoRealm> ar;
11250 : ar.emplace(cx, referent);
11251 :
11252 0 : cx->markId(id);
11253 :
11254 0 : /* This can trigger getters. */
11255 0 : ErrorCopier ec(ar);
11256 :
11257 : bool found;
11258 0 : if (!HasProperty(cx, referent, id, &found))
11259 0 : return false;
11260 : if (!found) {
11261 0 : result.setUndefined();
11262 : return true;
11263 : }
11264 0 :
11265 : // For DebugEnvironmentProxys, we get sentinel values for optimized out
11266 : // slots and arguments instead of throwing (the default behavior).
11267 0 : //
11268 0 : // See wrapDebuggeeValue for how the sentinel values are wrapped.
11269 0 : if (referent->is<DebugEnvironmentProxy>()) {
11270 0 : Rooted<DebugEnvironmentProxy*> env(cx, &referent->as<DebugEnvironmentProxy>());
11271 0 : if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, env, id, result))
11272 : return false;
11273 : } else {
11274 : if (!GetProperty(cx, referent, referent, id, result))
11275 : return false;
11276 : }
11277 : }
11278 0 :
11279 0 : // When we've faked up scope chain objects for optimized-out scopes,
11280 0 : // declarative environments may contain internal JSFunction objects, which
11281 0 : // we shouldn't expose to the user.
11282 : if (result.isObject()) {
11283 0 : RootedObject obj(cx, &result.toObject());
11284 : if (obj->is<JSFunction>() &&
11285 : IsInternalFunctionObject(obj->as<JSFunction>()))
11286 : result.setMagic(JS_OPTIMIZED_OUT);
11287 : }
11288 :
11289 : return dbg->wrapDebuggeeValue(cx, result);
11290 : }
11291 0 :
11292 0 : /* static */ bool
11293 0 : DebuggerEnvironment::setVariable(JSContext* cx, HandleDebuggerEnvironment environment,
11294 0 : HandleId id, HandleValue value_)
11295 : {
11296 : MOZ_ASSERT(environment->isDebuggee());
11297 :
11298 0 : Rooted<Env*> referent(cx, environment->referent());
11299 : Debugger* dbg = environment->owner();
11300 :
11301 : RootedValue value(cx, value_);
11302 0 : if (!dbg->unwrapDebuggeeValue(cx, &value))
11303 : return false;
11304 :
11305 0 : {
11306 : Maybe<AutoRealm> ar;
11307 0 : ar.emplace(cx, referent);
11308 0 : if (!cx->compartment()->wrap(cx, &value))
11309 : return false;
11310 0 : cx->markId(id);
11311 0 :
11312 : /* This can trigger setters. */
11313 : ErrorCopier ec(ar);
11314 :
11315 0 : /* Make sure the environment actually has the specified binding. */
11316 0 : bool found;
11317 0 : if (!HasProperty(cx, referent, id, &found))
11318 0 : return false;
11319 0 : if (!found) {
11320 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_VARIABLE_NOT_FOUND);
11321 : return false;
11322 0 : }
11323 :
11324 : /* Just set the property. */
11325 : if (!SetProperty(cx, referent, id, value))
11326 0 : return false;
11327 0 : }
11328 0 :
11329 0 : return true;
11330 0 : }
11331 :
11332 :
11333 : /*** JS::dbg::Builder ****************************************************************************/
11334 0 :
11335 : Builder::Builder(JSContext* cx, js::Debugger* debugger)
11336 : : debuggerObject(cx, debugger->toJSObject().get()),
11337 : debugger(debugger)
11338 0 : { }
11339 :
11340 :
11341 : #if DEBUG
11342 : void
11343 : Builder::assertBuilt(JSObject* obj)
11344 0 : {
11345 0 : // We can't use assertSameCompartment here, because that is always keyed to
11346 0 : // some JSContext's current compartment, whereas BuiltThings can be
11347 0 : // constructed and assigned to without respect to any particular context;
11348 : // the only constraint is that they should be in their debugger's compartment.
11349 : MOZ_ASSERT_IF(obj, debuggerObject->compartment() == obj->compartment());
11350 : }
11351 : #endif
11352 0 :
11353 : bool
11354 : Builder::Object::definePropertyToTrusted(JSContext* cx, const char* name,
11355 : JS::MutableHandleValue trusted)
11356 : {
11357 : // We should have checked for false Objects before calling this.
11358 0 : MOZ_ASSERT(value);
11359 0 :
11360 : JSAtom* atom = Atomize(cx, name, strlen(name));
11361 : if (!atom)
11362 : return false;
11363 0 : RootedId id(cx, AtomToId(atom));
11364 :
11365 : return DefineDataProperty(cx, value, id, trusted);
11366 : }
11367 0 :
11368 : bool
11369 0 : Builder::Object::defineProperty(JSContext* cx, const char* name, JS::HandleValue propval_)
11370 0 : {
11371 : AutoRealm ar(cx, debuggerObject());
11372 0 :
11373 : RootedValue propval(cx, propval_);
11374 0 : if (!debugger()->wrapDebuggeeValue(cx, &propval))
11375 : return false;
11376 :
11377 : return definePropertyToTrusted(cx, name, &propval);
11378 0 : }
11379 :
11380 0 : bool
11381 : Builder::Object::defineProperty(JSContext* cx, const char* name, JS::HandleObject propval_)
11382 0 : {
11383 0 : RootedValue propval(cx, ObjectOrNullValue(propval_));
11384 : return defineProperty(cx, name, propval);
11385 : }
11386 0 :
11387 : bool
11388 : Builder::Object::defineProperty(JSContext* cx, const char* name, Builder::Object& propval_)
11389 : {
11390 0 : AutoRealm ar(cx, debuggerObject());
11391 :
11392 0 : RootedValue propval(cx, ObjectOrNullValue(propval_.value));
11393 0 : return definePropertyToTrusted(cx, name, &propval);
11394 : }
11395 :
11396 : Builder::Object
11397 0 : Builder::newObject(JSContext* cx)
11398 : {
11399 0 : AutoRealm ar(cx, debuggerObject);
11400 :
11401 0 : RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
11402 0 :
11403 : // If the allocation failed, this will return a false Object, as the spec promises.
11404 : return Object(cx, *this, obj);
11405 : }
11406 0 :
11407 :
11408 0 : /*** JS::dbg::AutoEntryMonitor ******************************************************************/
11409 :
11410 0 : AutoEntryMonitor::AutoEntryMonitor(JSContext* cx)
11411 : : cx_(cx),
11412 : savedMonitor_(cx->entryMonitor)
11413 0 : {
11414 : cx->entryMonitor = this;
11415 : }
11416 :
11417 : AutoEntryMonitor::~AutoEntryMonitor()
11418 : {
11419 0 : cx_->entryMonitor = savedMonitor_;
11420 : }
11421 0 :
11422 :
11423 0 : /*** Glue ****************************************************************************************/
11424 0 :
11425 : extern JS_PUBLIC_API(bool)
11426 0 : JS_DefineDebuggerObject(JSContext* cx, HandleObject obj)
11427 : {
11428 0 : RootedNativeObject
11429 0 : objProto(cx),
11430 : debugCtor(cx),
11431 : debugProto(cx),
11432 : frameProto(cx),
11433 : scriptProto(cx),
11434 : sourceProto(cx),
11435 0 : objectProto(cx),
11436 : envProto(cx),
11437 : memoryProto(cx);
11438 0 : RootedObject debuggeeWouldRunProto(cx);
11439 0 : RootedValue debuggeeWouldRunCtor(cx);
11440 0 : Handle<GlobalObject*> global = obj.as<GlobalObject>();
11441 0 :
11442 0 : objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
11443 0 : if (!objProto)
11444 0 : return false;
11445 0 : debugProto = InitClass(cx, global,
11446 0 : objProto, &Debugger::class_, Debugger::construct,
11447 0 : 1, Debugger::properties, Debugger::methods, nullptr,
11448 0 : Debugger::static_methods, debugCtor.address());
11449 0 : if (!debugProto)
11450 : return false;
11451 0 :
11452 0 : frameProto = DebuggerFrame::initClass(cx, debugCtor, global);
11453 : if (!frameProto)
11454 0 : return false;
11455 :
11456 : scriptProto = InitClass(cx, debugCtor, objProto, &DebuggerScript_class,
11457 0 : DebuggerScript_construct, 0,
11458 0 : DebuggerScript_properties, DebuggerScript_methods,
11459 : nullptr, nullptr);
11460 : if (!scriptProto)
11461 0 : return false;
11462 0 :
11463 : sourceProto = InitClass(cx, debugCtor, sourceProto, &DebuggerSource_class,
11464 : DebuggerSource_construct, 0,
11465 0 : DebuggerSource_properties, DebuggerSource_methods,
11466 : nullptr, nullptr);
11467 : if (!sourceProto)
11468 0 : return false;
11469 0 :
11470 : objectProto = DebuggerObject::initClass(cx, global, debugCtor);
11471 : if (!objectProto)
11472 0 : return false;
11473 :
11474 : envProto = DebuggerEnvironment::initClass(cx, debugCtor, global);
11475 0 : if (!envProto)
11476 0 : return false;
11477 :
11478 : memoryProto = InitClass(cx, debugCtor, objProto, &DebuggerMemory::class_,
11479 0 : DebuggerMemory::construct, 0, DebuggerMemory::properties,
11480 0 : DebuggerMemory::methods, nullptr, nullptr);
11481 : if (!memoryProto)
11482 : return false;
11483 0 :
11484 0 : debuggeeWouldRunProto =
11485 : GlobalObject::getOrCreateCustomErrorPrototype(cx, global, JSEXN_DEBUGGEEWOULDRUN);
11486 : if (!debuggeeWouldRunProto)
11487 0 : return false;
11488 : debuggeeWouldRunCtor = global->getConstructor(JSProto_DebuggeeWouldRun);
11489 0 : RootedId debuggeeWouldRunId(cx, NameToId(ClassName(JSProto_DebuggeeWouldRun, cx)));
11490 0 : if (!DefineDataProperty(cx, debugCtor, debuggeeWouldRunId, debuggeeWouldRunCtor, 0))
11491 : return false;
11492 :
11493 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_FRAME_PROTO, ObjectValue(*frameProto));
11494 0 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_OBJECT_PROTO, ObjectValue(*objectProto));
11495 0 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SCRIPT_PROTO, ObjectValue(*scriptProto));
11496 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SOURCE_PROTO, ObjectValue(*sourceProto));
11497 0 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_ENV_PROTO, ObjectValue(*envProto));
11498 0 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_MEMORY_PROTO, ObjectValue(*memoryProto));
11499 0 : return true;
11500 : }
11501 :
11502 0 : JS_PUBLIC_API(bool)
11503 0 : JS::dbg::IsDebugger(JSObject& obj)
11504 0 : {
11505 0 : JSObject* unwrapped = CheckedUnwrap(&obj);
11506 0 : return unwrapped &&
11507 0 : unwrapped->getClass() == &Debugger::class_ &&
11508 0 : js::Debugger::fromJSObject(unwrapped) != nullptr;
11509 : }
11510 :
11511 : JS_PUBLIC_API(bool)
11512 0 : JS::dbg::GetDebuggeeGlobals(JSContext* cx, JSObject& dbgObj, AutoObjectVector& vector)
11513 : {
11514 0 : MOZ_ASSERT(IsDebugger(dbgObj));
11515 0 : js::Debugger* dbg = js::Debugger::fromJSObject(CheckedUnwrap(&dbgObj));
11516 0 :
11517 0 : if (!vector.reserve(vector.length() + dbg->debuggees.count())) {
11518 : JS_ReportOutOfMemory(cx);
11519 : return false;
11520 : }
11521 0 :
11522 : for (WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront())
11523 0 : vector.infallibleAppend(static_cast<JSObject*>(r.front()));
11524 0 :
11525 : return true;
11526 0 : }
11527 0 :
11528 0 : #ifdef DEBUG
11529 : /* static */ bool
11530 : Debugger::isDebuggerCrossCompartmentEdge(JSObject* obj, const gc::Cell* target)
11531 0 : {
11532 0 : MOZ_ASSERT(target);
11533 :
11534 0 : auto cls = obj->getClass();
11535 : const gc::Cell* referent = nullptr;
11536 : if (cls == &DebuggerScript_class) {
11537 : referent = GetScriptReferentCell(obj);
11538 : } else if (cls == &DebuggerSource_class) {
11539 0 : referent = GetSourceReferentRawObject(obj);
11540 : } else if (obj->is<DebuggerObject>()) {
11541 0 : referent = static_cast<gc::Cell*>(obj->as<DebuggerObject>().getPrivate());
11542 : } else if (obj->is<DebuggerEnvironment>()) {
11543 0 : referent = static_cast<gc::Cell*>(obj->as<DebuggerEnvironment>().getPrivate());
11544 0 : }
11545 0 :
11546 0 : return referent == target;
11547 0 : }
11548 0 : #endif
11549 0 :
11550 0 :
11551 0 : /*** JS::dbg::GarbageCollectionEvent **************************************************************/
11552 0 :
11553 : namespace JS {
11554 : namespace dbg {
11555 0 :
11556 : /* static */ GarbageCollectionEvent::Ptr
11557 : GarbageCollectionEvent::Create(JSRuntime* rt, ::js::gcstats::Statistics& stats, uint64_t gcNumber)
11558 : {
11559 : auto data = MakeUnique<GarbageCollectionEvent>(gcNumber);
11560 : if (!data)
11561 : return nullptr;
11562 :
11563 : data->nonincrementalReason = stats.nonincrementalReason();
11564 :
11565 : for (auto& slice : stats.slices()) {
11566 0 : if (!data->reason) {
11567 : // There is only one GC reason for the whole cycle, but for legacy
11568 0 : // reasons this data is stored and replicated on each slice. Each
11569 0 : // slice used to have its own GCReason, but now they are all the
11570 : // same.
11571 : data->reason = gcreason::ExplainReason(slice.reason);
11572 0 : MOZ_ASSERT(data->reason);
11573 : }
11574 0 :
11575 0 : if (!data->collections.growBy(1))
11576 : return nullptr;
11577 :
11578 : data->collections.back().startTimestamp = slice.start;
11579 : data->collections.back().endTimestamp = slice.end;
11580 0 : }
11581 0 :
11582 : return data;
11583 : }
11584 0 :
11585 : static bool
11586 : DefineStringProperty(JSContext* cx, HandleObject obj, PropertyName* propName, const char* strVal)
11587 0 : {
11588 0 : RootedValue val(cx, UndefinedValue());
11589 : if (strVal) {
11590 : JSAtom* atomized = Atomize(cx, strVal, strlen(strVal));
11591 : if (!atomized)
11592 : return false;
11593 : val = StringValue(atomized);
11594 : }
11595 0 : return DefineDataProperty(cx, obj, propName, val);
11596 : }
11597 0 :
11598 0 : JSObject*
11599 0 : GarbageCollectionEvent::toJSObject(JSContext* cx) const
11600 0 : {
11601 : RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
11602 0 : RootedValue gcCycleNumberVal(cx, NumberValue(majorGCNumber_));
11603 : if (!obj ||
11604 0 : !DefineStringProperty(cx, obj, cx->names().nonincrementalReason, nonincrementalReason) ||
11605 : !DefineStringProperty(cx, obj, cx->names().reason, reason) ||
11606 : !DefineDataProperty(cx, obj, cx->names().gcCycleNumber, gcCycleNumberVal))
11607 : {
11608 0 : return nullptr;
11609 : }
11610 0 :
11611 0 : RootedArrayObject slicesArray(cx, NewDenseEmptyArray(cx));
11612 0 : if (!slicesArray)
11613 0 : return nullptr;
11614 0 :
11615 0 : TimeStamp originTime = TimeStamp::ProcessCreation();
11616 :
11617 : size_t idx = 0;
11618 : for (auto range = collections.all(); !range.empty(); range.popFront()) {
11619 : RootedPlainObject collectionObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
11620 0 : if (!collectionObj)
11621 0 : return nullptr;
11622 :
11623 : RootedValue start(cx), end(cx);
11624 0 : start = NumberValue((range.front().startTimestamp - originTime).ToMilliseconds());
11625 : end = NumberValue((range.front().endTimestamp - originTime).ToMilliseconds());
11626 0 : if (!DefineDataProperty(cx, collectionObj, cx->names().startTimestamp, start) ||
11627 0 : !DefineDataProperty(cx, collectionObj, cx->names().endTimestamp, end))
11628 0 : {
11629 0 : return nullptr;
11630 0 : }
11631 :
11632 0 : RootedValue collectionVal(cx, ObjectValue(*collectionObj));
11633 0 : if (!DefineDataElement(cx, slicesArray, idx++, collectionVal))
11634 0 : return nullptr;
11635 0 : }
11636 0 :
11637 : RootedValue slicesValue(cx, ObjectValue(*slicesArray));
11638 0 : if (!DefineDataProperty(cx, obj, cx->names().collections, slicesValue))
11639 : return nullptr;
11640 :
11641 0 : return obj;
11642 0 : }
11643 0 :
11644 : JS_PUBLIC_API(bool)
11645 : FireOnGarbageCollectionHookRequired(JSContext* cx)
11646 0 : {
11647 0 : AutoCheckCannotGC noGC;
11648 :
11649 : for (Debugger* dbg : cx->runtime()->debuggerList()) {
11650 0 : if (dbg->enabled &&
11651 : dbg->observedGC(cx->runtime()->gc.majorGCCount()) &&
11652 : dbg->getHook(Debugger::OnGarbageCollection))
11653 : {
11654 0 : return true;
11655 : }
11656 0 : }
11657 :
11658 0 : return false;
11659 0 : }
11660 0 :
11661 0 : JS_PUBLIC_API(bool)
11662 : FireOnGarbageCollectionHook(JSContext* cx, JS::dbg::GarbageCollectionEvent::Ptr&& data)
11663 : {
11664 : AutoObjectVector triggered(cx);
11665 :
11666 : {
11667 : // We had better not GC (and potentially get a dangling Debugger
11668 : // pointer) while finding all Debuggers observing a debuggee that
11669 : // participated in this GC.
11670 : AutoCheckCannotGC noGC;
11671 0 :
11672 : for (Debugger* dbg : cx->runtime()->debuggerList()) {
11673 0 : if (dbg->enabled &&
11674 : dbg->observedGC(data->majorGCNumber()) &&
11675 : dbg->getHook(Debugger::OnGarbageCollection))
11676 : {
11677 : if (!triggered.append(dbg->object)) {
11678 : JS_ReportOutOfMemory(cx);
11679 0 : return false;
11680 : }
11681 0 : }
11682 0 : }
11683 0 : }
11684 0 :
11685 : for ( ; !triggered.empty(); triggered.popBack()) {
11686 0 : Debugger* dbg = Debugger::fromJSObject(triggered.back());
11687 0 : dbg->fireOnGarbageCollectionHook(cx, data);
11688 0 : MOZ_ASSERT(!cx->isExceptionPending());
11689 : }
11690 :
11691 : return true;
11692 : }
11693 :
11694 0 : } // namespace dbg
11695 0 : } // namespace JS
|