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 "jscntxt.h"
15 : #include "jscompartment.h"
16 : #include "jsfriendapi.h"
17 : #include "jshashutil.h"
18 : #include "jsnum.h"
19 : #include "jsobj.h"
20 : #include "jsprf.h"
21 : #include "jswrapper.h"
22 :
23 : #include "frontend/BytecodeCompiler.h"
24 : #include "frontend/Parser.h"
25 : #include "gc/Marking.h"
26 : #include "gc/Policy.h"
27 : #include "jit/BaselineDebugModeOSR.h"
28 : #include "jit/BaselineJIT.h"
29 : #include "js/Date.h"
30 : #include "js/GCAPI.h"
31 : #include "js/UbiNodeBreadthFirst.h"
32 : #include "js/Vector.h"
33 : #include "proxy/ScriptedProxyHandler.h"
34 : #include "vm/ArgumentsObject.h"
35 : #include "vm/DebuggerMemory.h"
36 : #include "vm/GeckoProfiler.h"
37 : #include "vm/GeneratorObject.h"
38 : #include "vm/TraceLogging.h"
39 : #include "vm/WrapperObject.h"
40 : #include "wasm/WasmInstance.h"
41 :
42 : #include "jsgcinlines.h"
43 : #include "jsobjinlines.h"
44 : #include "jsopcodeinlines.h"
45 : #include "jsscriptinlines.h"
46 :
47 : #include "vm/GeckoProfiler-inl.h"
48 : #include "vm/NativeObject-inl.h"
49 : #include "vm/Stack-inl.h"
50 :
51 : using namespace js;
52 :
53 : using JS::dbg::AutoEntryMonitor;
54 : using JS::dbg::Builder;
55 : using js::frontend::IsIdentifier;
56 : using mozilla::ArrayLength;
57 : using mozilla::DebugOnly;
58 : using mozilla::MakeScopeExit;
59 : using mozilla::Maybe;
60 : using mozilla::Some;
61 : using mozilla::Nothing;
62 : using mozilla::Variant;
63 : using mozilla::AsVariant;
64 : using mozilla::TimeDuration;
65 : using mozilla::TimeStamp;
66 :
67 :
68 : /*** Forward declarations, ClassOps and Classes **************************************************/
69 :
70 : static void DebuggerFrame_finalize(FreeOp* fop, JSObject* obj);
71 : static void DebuggerFrame_trace(JSTracer* trc, JSObject* obj);
72 : static void DebuggerEnv_trace(JSTracer* trc, JSObject* obj);
73 : static void DebuggerObject_trace(JSTracer* trc, JSObject* obj);
74 : static void DebuggerScript_trace(JSTracer* trc, JSObject* obj);
75 : static void DebuggerSource_trace(JSTracer* trc, JSObject* obj);
76 :
77 : enum {
78 : JSSLOT_DEBUGFRAME_OWNER,
79 : JSSLOT_DEBUGFRAME_ARGUMENTS,
80 : JSSLOT_DEBUGFRAME_ONSTEP_HANDLER,
81 : JSSLOT_DEBUGFRAME_ONPOP_HANDLER,
82 : JSSLOT_DEBUGFRAME_COUNT
83 : };
84 :
85 : const ClassOps DebuggerFrame::classOps_ = {
86 : nullptr, /* addProperty */
87 : nullptr, /* delProperty */
88 : nullptr, /* getProperty */
89 : nullptr, /* setProperty */
90 : nullptr, /* enumerate */
91 : nullptr, /* newEnumerate */
92 : nullptr, /* resolve */
93 : nullptr, /* mayResolve */
94 : DebuggerFrame_finalize,
95 : nullptr, /* call */
96 : nullptr, /* hasInstance */
97 : nullptr, /* construct */
98 : DebuggerFrame_trace
99 : };
100 :
101 : const Class DebuggerFrame::class_ = {
102 : "Frame",
103 : JSCLASS_HAS_PRIVATE |
104 : JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT) |
105 : JSCLASS_BACKGROUND_FINALIZE,
106 : &DebuggerFrame::classOps_
107 : };
108 :
109 : enum {
110 : JSSLOT_DEBUGARGUMENTS_FRAME,
111 : JSSLOT_DEBUGARGUMENTS_COUNT
112 : };
113 :
114 : const Class DebuggerArguments::class_ = {
115 : "Arguments",
116 : JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT)
117 : };
118 :
119 : const ClassOps DebuggerEnvironment::classOps_ = {
120 : nullptr, /* addProperty */
121 : nullptr, /* delProperty */
122 : nullptr, /* getProperty */
123 : nullptr, /* setProperty */
124 : nullptr, /* enumerate */
125 : nullptr, /* newEnumerate */
126 : nullptr, /* resolve */
127 : nullptr, /* mayResolve */
128 : nullptr, /* finalize */
129 : nullptr, /* call */
130 : nullptr, /* hasInstance */
131 : nullptr, /* construct */
132 : DebuggerEnv_trace
133 : };
134 :
135 : const Class DebuggerEnvironment::class_ = {
136 : "Environment",
137 : JSCLASS_HAS_PRIVATE |
138 : JSCLASS_HAS_RESERVED_SLOTS(DebuggerEnvironment::RESERVED_SLOTS),
139 : &classOps_
140 : };
141 :
142 : enum {
143 : JSSLOT_DEBUGOBJECT_OWNER,
144 : JSSLOT_DEBUGOBJECT_COUNT
145 : };
146 :
147 : const ClassOps DebuggerObject::classOps_ = {
148 : nullptr, /* addProperty */
149 : nullptr, /* delProperty */
150 : nullptr, /* getProperty */
151 : nullptr, /* setProperty */
152 : nullptr, /* enumerate */
153 : nullptr, /* newEnumerate */
154 : nullptr, /* resolve */
155 : nullptr, /* mayResolve */
156 : nullptr, /* finalize */
157 : nullptr, /* call */
158 : nullptr, /* hasInstance */
159 : nullptr, /* construct */
160 : DebuggerObject_trace
161 : };
162 :
163 : const Class DebuggerObject::class_ = {
164 : "Object",
165 : JSCLASS_HAS_PRIVATE |
166 : JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
167 : &classOps_
168 : };
169 :
170 : enum {
171 : JSSLOT_DEBUGSCRIPT_OWNER,
172 : JSSLOT_DEBUGSCRIPT_COUNT
173 : };
174 :
175 : static const ClassOps DebuggerScript_classOps = {
176 : nullptr, /* addProperty */
177 : nullptr, /* delProperty */
178 : nullptr, /* getProperty */
179 : nullptr, /* setProperty */
180 : nullptr, /* enumerate */
181 : nullptr, /* newEnumerate */
182 : nullptr, /* resolve */
183 : nullptr, /* mayResolve */
184 : nullptr, /* finalize */
185 : nullptr, /* call */
186 : nullptr, /* hasInstance */
187 : nullptr, /* construct */
188 : DebuggerScript_trace
189 : };
190 :
191 : static const Class DebuggerScript_class = {
192 : "Script",
193 : JSCLASS_HAS_PRIVATE |
194 : JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
195 : &DebuggerScript_classOps
196 : };
197 :
198 : enum {
199 : JSSLOT_DEBUGSOURCE_OWNER,
200 : JSSLOT_DEBUGSOURCE_TEXT,
201 : JSSLOT_DEBUGSOURCE_COUNT
202 : };
203 :
204 : static const ClassOps DebuggerSource_classOps = {
205 : nullptr, /* addProperty */
206 : nullptr, /* delProperty */
207 : nullptr, /* getProperty */
208 : nullptr, /* setProperty */
209 : nullptr, /* enumerate */
210 : nullptr, /* newEnumerate */
211 : nullptr, /* resolve */
212 : nullptr, /* mayResolve */
213 : nullptr, /* finalize */
214 : nullptr, /* call */
215 : nullptr, /* hasInstance */
216 : nullptr, /* construct */
217 : DebuggerSource_trace
218 : };
219 :
220 : static const Class DebuggerSource_class = {
221 : "Source",
222 : JSCLASS_HAS_PRIVATE |
223 : JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),
224 : &DebuggerSource_classOps
225 : };
226 :
227 :
228 : /*** Utils ***************************************************************************************/
229 :
230 : static inline bool
231 0 : EnsureFunctionHasScript(JSContext* cx, HandleFunction fun)
232 : {
233 0 : if (fun->isInterpretedLazy()) {
234 0 : AutoCompartment ac(cx, fun);
235 0 : return !!JSFunction::getOrCreateScript(cx, fun);
236 : }
237 0 : return true;
238 : }
239 :
240 : static inline JSScript*
241 0 : GetOrCreateFunctionScript(JSContext* cx, HandleFunction fun)
242 : {
243 0 : MOZ_ASSERT(fun->isInterpreted());
244 0 : if (!EnsureFunctionHasScript(cx, fun))
245 0 : return nullptr;
246 0 : return fun->nonLazyScript();
247 : }
248 :
249 : static bool
250 0 : ValueToIdentifier(JSContext* cx, HandleValue v, MutableHandleId id)
251 : {
252 0 : if (!ValueToId<CanGC>(cx, v, id))
253 0 : return false;
254 0 : if (!JSID_IS_ATOM(id) || !IsIdentifier(JSID_TO_ATOM(id))) {
255 0 : RootedValue val(cx, v);
256 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
257 : JSDVG_SEARCH_STACK, val, nullptr, "not an identifier",
258 0 : nullptr);
259 0 : return false;
260 : }
261 0 : return true;
262 : }
263 :
264 : class AutoRestoreCompartmentDebugMode
265 : {
266 : JSCompartment* comp_;
267 : unsigned bits_;
268 :
269 : public:
270 0 : explicit AutoRestoreCompartmentDebugMode(JSCompartment* comp)
271 0 : : comp_(comp), bits_(comp->debugModeBits)
272 : {
273 0 : MOZ_ASSERT(comp_);
274 0 : }
275 :
276 0 : ~AutoRestoreCompartmentDebugMode() {
277 0 : if (comp_)
278 0 : comp_->debugModeBits = bits_;
279 0 : }
280 :
281 0 : void release() {
282 0 : comp_ = nullptr;
283 0 : }
284 : };
285 :
286 : // Given a Debugger instance dbg, if it is enabled, prevents all its debuggee
287 : // compartments from executing scripts. Attempts to run script will throw an
288 : // instance of Debugger.DebuggeeWouldRun from the topmost locked Debugger's
289 : // compartment.
290 : class MOZ_RAII js::EnterDebuggeeNoExecute
291 : {
292 : friend class js::LeaveDebuggeeNoExecute;
293 :
294 : Debugger& dbg_;
295 : EnterDebuggeeNoExecute** stack_;
296 : EnterDebuggeeNoExecute* prev_;
297 :
298 : // Non-nullptr when unlocked temporarily by a LeaveDebuggeeNoExecute.
299 : LeaveDebuggeeNoExecute* unlocked_;
300 :
301 : // When DebuggeeWouldRun is a warning instead of an error, whether we've
302 : // reported a warning already.
303 : bool reported_;
304 :
305 : public:
306 0 : explicit EnterDebuggeeNoExecute(JSContext* cx, Debugger& dbg)
307 0 : : dbg_(dbg),
308 : unlocked_(nullptr),
309 0 : reported_(false)
310 : {
311 0 : stack_ = &cx->noExecuteDebuggerTop.ref();
312 0 : prev_ = *stack_;
313 0 : *stack_ = this;
314 0 : }
315 :
316 0 : ~EnterDebuggeeNoExecute() {
317 0 : MOZ_ASSERT(*stack_ == this);
318 0 : *stack_ = prev_;
319 0 : }
320 :
321 0 : Debugger& debugger() const {
322 0 : return dbg_;
323 : }
324 :
325 : #ifdef DEBUG
326 0 : static bool isLockedInStack(JSContext* cx, Debugger& dbg) {
327 0 : for (EnterDebuggeeNoExecute* it = cx->noExecuteDebuggerTop; it; it = it->prev_) {
328 0 : if (&it->debugger() == &dbg)
329 0 : return !it->unlocked_;
330 : }
331 0 : return false;
332 : }
333 : #endif
334 :
335 : // Given a JSContext entered into a debuggee compartment, find the lock
336 : // that locks it. Returns nullptr if not found.
337 0 : static EnterDebuggeeNoExecute* findInStack(JSContext* cx) {
338 0 : JSCompartment* debuggee = cx->compartment();
339 0 : for (EnterDebuggeeNoExecute* it = cx->noExecuteDebuggerTop; it; it = it->prev_) {
340 0 : Debugger& dbg = it->debugger();
341 0 : if (!it->unlocked_ && dbg.isEnabled() && dbg.observesGlobal(debuggee->maybeGlobal()))
342 0 : return it;
343 : }
344 0 : return nullptr;
345 : }
346 :
347 : // Given a JSContext entered into a debuggee compartment, report a
348 : // warning or an error if there is a lock that locks it.
349 0 : static bool reportIfFoundInStack(JSContext* cx, HandleScript script) {
350 0 : if (EnterDebuggeeNoExecute* nx = findInStack(cx)) {
351 0 : bool warning = !cx->options().throwOnDebuggeeWouldRun();
352 0 : if (!warning || !nx->reported_) {
353 0 : AutoCompartment ac(cx, nx->debugger().toJSObject());
354 0 : nx->reported_ = true;
355 0 : if (cx->options().dumpStackOnDebuggeeWouldRun()) {
356 0 : fprintf(stdout, "Dumping stack for DebuggeeWouldRun:\n");
357 0 : DumpBacktrace(cx);
358 : }
359 0 : const char* filename = script->filename() ? script->filename() : "(none)";
360 : char linenoStr[15];
361 0 : SprintfLiteral(linenoStr, "%" PRIuSIZE, script->lineno());
362 0 : unsigned flags = warning ? JSREPORT_WARNING : JSREPORT_ERROR;
363 : // FIXME: filename should be UTF-8 (bug 987069).
364 : return JS_ReportErrorFlagsAndNumberLatin1(cx, flags, GetErrorMessage, nullptr,
365 : JSMSG_DEBUGGEE_WOULD_RUN,
366 0 : filename, linenoStr);
367 : }
368 : }
369 0 : return true;
370 : }
371 : };
372 :
373 : // Given a JSContext entered into a debuggee compartment, if it is in
374 : // an NX section, unlock the topmost EnterDebuggeeNoExecute instance.
375 : //
376 : // Does nothing if debuggee is not in an NX section. For example, this
377 : // situation arises when invocation functions are called without entering
378 : // Debugger code, e.g., calling D.O.p.executeInGlobal or D.O.p.apply.
379 : class MOZ_RAII js::LeaveDebuggeeNoExecute
380 : {
381 : EnterDebuggeeNoExecute* prevLocked_;
382 :
383 : public:
384 0 : explicit LeaveDebuggeeNoExecute(JSContext* cx)
385 0 : : prevLocked_(EnterDebuggeeNoExecute::findInStack(cx))
386 : {
387 0 : if (prevLocked_) {
388 0 : MOZ_ASSERT(!prevLocked_->unlocked_);
389 0 : prevLocked_->unlocked_ = this;
390 : }
391 0 : }
392 :
393 0 : ~LeaveDebuggeeNoExecute() {
394 0 : if (prevLocked_) {
395 0 : MOZ_ASSERT(prevLocked_->unlocked_ == this);
396 0 : prevLocked_->unlocked_ = nullptr;
397 : }
398 0 : }
399 : };
400 :
401 : /* static */ bool
402 0 : Debugger::slowPathCheckNoExecute(JSContext* cx, HandleScript script)
403 : {
404 0 : MOZ_ASSERT(cx->compartment()->isDebuggee());
405 0 : MOZ_ASSERT(cx->noExecuteDebuggerTop);
406 0 : return EnterDebuggeeNoExecute::reportIfFoundInStack(cx, script);
407 : }
408 :
409 : static inline void
410 0 : NukeDebuggerWrapper(NativeObject *wrapper)
411 : {
412 : // In some OOM failure cases, we need to destroy the edge to the referent,
413 : // to avoid trying to trace it during untimely collections.
414 0 : wrapper->setPrivate(nullptr);
415 0 : }
416 :
417 : static bool
418 0 : ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value,
419 : AutoStableStringChars& stableChars)
420 : {
421 0 : if (!value.isString()) {
422 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
423 0 : fnname, "string", InformalValueTypeName(value));
424 0 : return false;
425 : }
426 0 : RootedLinearString linear(cx, value.toString()->ensureLinear(cx));
427 0 : if (!linear)
428 0 : return false;
429 0 : if (!stableChars.initTwoByte(cx, linear))
430 0 : return false;
431 0 : return true;
432 : }
433 :
434 0 : EvalOptions::~EvalOptions()
435 : {
436 0 : js_free(const_cast<char*>(filename_));
437 0 : }
438 :
439 : bool
440 0 : EvalOptions::setFilename(JSContext* cx, const char* filename)
441 : {
442 0 : char* copy = nullptr;
443 0 : if (filename) {
444 0 : copy = JS_strdup(cx, filename);
445 0 : if (!copy)
446 0 : return false;
447 : }
448 :
449 : // EvalOptions always owns filename_, so this cast is okay.
450 0 : js_free(const_cast<char*>(filename_));
451 :
452 0 : filename_ = copy;
453 0 : return true;
454 : }
455 :
456 : static bool
457 0 : ParseEvalOptions(JSContext* cx, HandleValue value, EvalOptions& options)
458 : {
459 0 : if (!value.isObject())
460 0 : return true;
461 :
462 0 : RootedObject opts(cx, &value.toObject());
463 :
464 0 : RootedValue v(cx);
465 0 : if (!JS_GetProperty(cx, opts, "url", &v))
466 0 : return false;
467 0 : if (!v.isUndefined()) {
468 0 : RootedString url_str(cx, ToString<CanGC>(cx, v));
469 0 : if (!url_str)
470 0 : return false;
471 0 : JSAutoByteString url_bytes(cx, url_str);
472 0 : if (!url_bytes)
473 0 : return false;
474 0 : if (!options.setFilename(cx, url_bytes.ptr()))
475 0 : return false;
476 : }
477 :
478 0 : if (!JS_GetProperty(cx, opts, "lineNumber", &v))
479 0 : return false;
480 0 : if (!v.isUndefined()) {
481 : uint32_t lineno;
482 0 : if (!ToUint32(cx, v, &lineno))
483 0 : return false;
484 0 : options.setLineno(lineno);
485 : }
486 :
487 0 : return true;
488 : }
489 :
490 : static bool
491 0 : RequireGlobalObject(JSContext* cx, HandleValue dbgobj, HandleObject referent)
492 : {
493 0 : RootedObject obj(cx, referent);
494 :
495 0 : if (!obj->is<GlobalObject>()) {
496 0 : const char* isWrapper = "";
497 0 : const char* isWindowProxy = "";
498 :
499 : /* Help the poor programmer by pointing out wrappers around globals... */
500 0 : if (obj->is<WrapperObject>()) {
501 0 : obj = js::UncheckedUnwrap(obj);
502 0 : isWrapper = "a wrapper around ";
503 : }
504 :
505 : /* ... and WindowProxies around Windows. */
506 0 : if (IsWindowProxy(obj)) {
507 0 : obj = ToWindowIfWindowProxy(obj);
508 0 : isWindowProxy = "a WindowProxy referring to ";
509 : }
510 :
511 0 : if (obj->is<GlobalObject>()) {
512 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_WRAPPER_IN_WAY,
513 : JSDVG_SEARCH_STACK, dbgobj, nullptr,
514 0 : isWrapper, isWindowProxy);
515 : } else {
516 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT,
517 : JSDVG_SEARCH_STACK, dbgobj, nullptr,
518 0 : "a global object", nullptr);
519 : }
520 0 : return false;
521 : }
522 :
523 0 : return true;
524 : }
525 :
526 :
527 : /*** Breakpoints *********************************************************************************/
528 :
529 0 : BreakpointSite::BreakpointSite(Type type)
530 0 : : type_(type), enabledCount(0)
531 : {
532 0 : }
533 :
534 : void
535 0 : BreakpointSite::inc(FreeOp* fop)
536 : {
537 0 : enabledCount++;
538 0 : if (enabledCount == 1)
539 0 : recompile(fop);
540 0 : }
541 :
542 : void
543 0 : BreakpointSite::dec(FreeOp* fop)
544 : {
545 0 : MOZ_ASSERT(enabledCount > 0);
546 0 : enabledCount--;
547 0 : if (enabledCount == 0)
548 0 : recompile(fop);
549 0 : }
550 :
551 : bool
552 0 : BreakpointSite::isEmpty() const
553 : {
554 0 : return breakpoints.isEmpty();
555 : }
556 :
557 : Breakpoint*
558 0 : BreakpointSite::firstBreakpoint() const
559 : {
560 0 : if (isEmpty())
561 0 : return nullptr;
562 0 : return &(*breakpoints.begin());
563 : }
564 :
565 : bool
566 0 : BreakpointSite::hasBreakpoint(Breakpoint* toFind)
567 : {
568 0 : const BreakpointList::Iterator bp(toFind);
569 0 : for (auto p = breakpoints.begin(); p; p++)
570 0 : if (p == bp)
571 0 : return true;
572 0 : return false;
573 : }
574 :
575 0 : Breakpoint::Breakpoint(Debugger* debugger, BreakpointSite* site, JSObject* handler)
576 0 : : debugger(debugger), site(site), handler(handler)
577 : {
578 0 : MOZ_ASSERT(handler->compartment() == debugger->object->compartment());
579 0 : debugger->breakpoints.pushBack(this);
580 0 : site->breakpoints.pushBack(this);
581 0 : }
582 :
583 : void
584 0 : Breakpoint::destroy(FreeOp* fop)
585 : {
586 0 : if (debugger->enabled)
587 0 : site->dec(fop);
588 0 : debugger->breakpoints.remove(this);
589 0 : site->breakpoints.remove(this);
590 0 : site->destroyIfEmpty(fop);
591 0 : fop->delete_(this);
592 0 : }
593 :
594 : Breakpoint*
595 0 : Breakpoint::nextInDebugger()
596 : {
597 0 : return debuggerLink.mNext;
598 : }
599 :
600 : Breakpoint*
601 0 : Breakpoint::nextInSite()
602 : {
603 0 : return siteLink.mNext;
604 : }
605 :
606 0 : JSBreakpointSite::JSBreakpointSite(JSScript* script, jsbytecode* pc)
607 : : BreakpointSite(Type::JS),
608 : script(script),
609 0 : pc(pc)
610 : {
611 0 : MOZ_ASSERT(!script->hasBreakpointsAt(pc));
612 0 : }
613 :
614 : void
615 0 : JSBreakpointSite::recompile(FreeOp* fop)
616 : {
617 0 : if (script->hasBaselineScript())
618 0 : script->baselineScript()->toggleDebugTraps(script, pc);
619 0 : }
620 :
621 : void
622 0 : JSBreakpointSite::destroyIfEmpty(FreeOp* fop)
623 : {
624 0 : if (isEmpty())
625 0 : script->destroyBreakpointSite(fop, pc);
626 0 : }
627 :
628 0 : WasmBreakpointSite::WasmBreakpointSite(wasm::DebugState* debug_, uint32_t offset_)
629 0 : : BreakpointSite(Type::Wasm), debug(debug_), offset(offset_)
630 : {
631 0 : MOZ_ASSERT(debug_);
632 0 : }
633 :
634 : void
635 0 : WasmBreakpointSite::recompile(FreeOp* fop)
636 : {
637 0 : debug->toggleBreakpointTrap(fop->runtime(), offset, isEnabled());
638 0 : }
639 :
640 : void
641 0 : WasmBreakpointSite::destroyIfEmpty(FreeOp* fop)
642 : {
643 0 : if (isEmpty())
644 0 : debug->destroyBreakpointSite(fop, offset);
645 0 : }
646 :
647 : /*** Debugger hook dispatch **********************************************************************/
648 :
649 0 : Debugger::Debugger(JSContext* cx, NativeObject* dbg)
650 : : object(dbg),
651 : debuggees(cx->runtime()),
652 : uncaughtExceptionHook(nullptr),
653 : enabled(true),
654 : allowUnobservedAsmJS(false),
655 : allowWasmBinarySource(false),
656 : collectCoverageInfo(false),
657 : observedGCs(cx->runtime()),
658 : allocationsLog(cx),
659 : trackingAllocationSites(false),
660 : allocationSamplingProbability(1.0),
661 : maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH),
662 : allocationsLogOverflowed(false),
663 : frames(cx->runtime()),
664 : scripts(cx),
665 : sources(cx),
666 : objects(cx),
667 : environments(cx),
668 : wasmInstanceScripts(cx),
669 : wasmInstanceSources(cx),
670 : #ifdef NIGHTLY_BUILD
671 : traceLoggerLastDrainedSize(0),
672 : traceLoggerLastDrainedIteration(0),
673 : #endif
674 : traceLoggerScriptedCallsLastDrainedSize(0),
675 0 : traceLoggerScriptedCallsLastDrainedIteration(0)
676 : {
677 0 : assertSameCompartment(cx, dbg);
678 :
679 : #ifdef JS_TRACE_LOGGING
680 0 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
681 0 : if (logger) {
682 : #ifdef NIGHTLY_BUILD
683 0 : logger->getIterationAndSize(&traceLoggerLastDrainedIteration, &traceLoggerLastDrainedSize);
684 : #endif
685 0 : logger->getIterationAndSize(&traceLoggerScriptedCallsLastDrainedIteration,
686 0 : &traceLoggerScriptedCallsLastDrainedSize);
687 : }
688 : #endif
689 0 : }
690 :
691 0 : Debugger::~Debugger()
692 : {
693 0 : MOZ_ASSERT_IF(debuggees.initialized(), debuggees.empty());
694 0 : allocationsLog.clear();
695 :
696 : /*
697 : * We don't have to worry about locking here since Debugger is not
698 : * background finalized.
699 : */
700 0 : JSContext* cx = TlsContext.get();
701 0 : if (onNewGlobalObjectWatchersLink.mPrev ||
702 0 : onNewGlobalObjectWatchersLink.mNext ||
703 0 : cx->runtime()->onNewGlobalObjectWatchers().begin() == JSRuntime::WatchersList::Iterator(this))
704 0 : cx->runtime()->onNewGlobalObjectWatchers().remove(this);
705 :
706 0 : cx->runtime()->endSingleThreadedExecution(cx);
707 0 : }
708 :
709 : bool
710 0 : Debugger::init(JSContext* cx)
711 : {
712 0 : if (!debuggees.init() ||
713 0 : !debuggeeZones.init() ||
714 0 : !frames.init() ||
715 0 : !scripts.init() ||
716 0 : !sources.init() ||
717 0 : !objects.init() ||
718 0 : !observedGCs.init() ||
719 0 : !environments.init() ||
720 0 : !wasmInstanceScripts.init() ||
721 0 : !wasmInstanceSources.init())
722 : {
723 0 : ReportOutOfMemory(cx);
724 0 : return false;
725 : }
726 :
727 0 : cx->zone()->group()->debuggerList().insertBack(this);
728 0 : return true;
729 : }
730 :
731 : JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGSCRIPT_OWNER));
732 : JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGSOURCE_OWNER));
733 : JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGOBJECT_OWNER));
734 : JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(DebuggerEnvironment::OWNER_SLOT));
735 :
736 : /* static */ Debugger*
737 0 : Debugger::fromChildJSObject(JSObject* obj)
738 : {
739 0 : MOZ_ASSERT(obj->getClass() == &DebuggerFrame::class_ ||
740 : obj->getClass() == &DebuggerScript_class ||
741 : obj->getClass() == &DebuggerSource_class ||
742 : obj->getClass() == &DebuggerObject::class_ ||
743 : obj->getClass() == &DebuggerEnvironment::class_);
744 0 : JSObject* dbgobj = &obj->as<NativeObject>().getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER).toObject();
745 0 : return fromJSObject(dbgobj);
746 : }
747 :
748 : bool
749 0 : Debugger::hasMemory() const
750 : {
751 0 : return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE).isObject();
752 : }
753 :
754 : DebuggerMemory&
755 0 : Debugger::memory() const
756 : {
757 0 : MOZ_ASSERT(hasMemory());
758 0 : return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE).toObject().as<DebuggerMemory>();
759 : }
760 :
761 : bool
762 0 : Debugger::getScriptFrameWithIter(JSContext* cx, AbstractFramePtr referent,
763 : const FrameIter* maybeIter, MutableHandleValue vp)
764 : {
765 0 : RootedDebuggerFrame result(cx);
766 0 : if (!Debugger::getScriptFrameWithIter(cx, referent, maybeIter, &result))
767 0 : return false;
768 :
769 0 : vp.setObject(*result);
770 0 : return true;
771 : }
772 :
773 : bool
774 0 : Debugger::getScriptFrameWithIter(JSContext* cx, AbstractFramePtr referent,
775 : const FrameIter* maybeIter,
776 : MutableHandleDebuggerFrame result)
777 : {
778 0 : MOZ_ASSERT_IF(maybeIter, maybeIter->abstractFramePtr() == referent);
779 0 : MOZ_ASSERT_IF(referent.hasScript(), !referent.script()->selfHosted());
780 :
781 0 : if (referent.hasScript() && !referent.script()->ensureHasAnalyzedArgsUsage(cx))
782 0 : return false;
783 :
784 0 : FrameMap::AddPtr p = frames.lookupForAdd(referent);
785 0 : if (!p) {
786 : /* Create and populate the Debugger.Frame object. */
787 0 : RootedObject proto(cx, &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject());
788 0 : RootedNativeObject debugger(cx, object);
789 :
790 0 : RootedDebuggerFrame frame(cx, DebuggerFrame::create(cx, proto, referent, maybeIter,
791 0 : debugger));
792 0 : if (!frame)
793 0 : return false;
794 :
795 0 : if (!ensureExecutionObservabilityOfFrame(cx, referent))
796 0 : return false;
797 :
798 0 : if (!frames.add(p, referent, frame)) {
799 0 : ReportOutOfMemory(cx);
800 0 : return false;
801 : }
802 : }
803 :
804 0 : result.set(&p->value()->as<DebuggerFrame>());
805 0 : return true;
806 : }
807 :
808 : /* static */ bool
809 0 : Debugger::hasLiveHook(GlobalObject* global, Hook which)
810 : {
811 0 : if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
812 0 : for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
813 0 : Debugger* dbg = *p;
814 0 : if (dbg->enabled && dbg->getHook(which))
815 0 : return true;
816 : }
817 : }
818 0 : return false;
819 : }
820 :
821 : JSObject*
822 0 : Debugger::getHook(Hook hook) const
823 : {
824 0 : MOZ_ASSERT(hook >= 0 && hook < HookCount);
825 0 : const Value& v = object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + hook);
826 0 : return v.isUndefined() ? nullptr : &v.toObject();
827 : }
828 :
829 : bool
830 0 : Debugger::hasAnyLiveHooks(JSRuntime* rt) const
831 : {
832 0 : if (!enabled)
833 0 : return false;
834 :
835 0 : if (getHook(OnDebuggerStatement) ||
836 0 : getHook(OnExceptionUnwind) ||
837 0 : getHook(OnNewScript) ||
838 0 : getHook(OnEnterFrame))
839 : {
840 0 : return true;
841 : }
842 :
843 : /* If any breakpoints are in live scripts, return true. */
844 0 : for (Breakpoint* bp = firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
845 0 : switch (bp->site->type()) {
846 : case BreakpointSite::Type::JS:
847 0 : if (IsMarkedUnbarriered(rt, &bp->site->asJS()->script))
848 0 : return true;
849 0 : break;
850 : case BreakpointSite::Type::Wasm:
851 0 : if (IsMarkedUnbarriered(rt, &bp->asWasm()->wasmInstance))
852 0 : return true;
853 0 : break;
854 : }
855 : }
856 :
857 0 : for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
858 0 : NativeObject* frameObj = r.front().value();
859 0 : if (!frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() ||
860 0 : !frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined())
861 0 : return true;
862 : }
863 :
864 0 : return false;
865 : }
866 :
867 : /* static */ JSTrapStatus
868 0 : Debugger::slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame)
869 : {
870 0 : RootedValue rval(cx);
871 0 : JSTrapStatus status = dispatchHook(
872 : cx,
873 0 : [frame](Debugger* dbg) -> bool {
874 0 : return dbg->observesFrame(frame) && dbg->observesEnterFrame();
875 : },
876 0 : [&](Debugger* dbg) -> JSTrapStatus {
877 0 : return dbg->fireEnterFrame(cx, &rval);
878 0 : });
879 :
880 0 : switch (status) {
881 : case JSTRAP_CONTINUE:
882 0 : break;
883 :
884 : case JSTRAP_THROW:
885 0 : cx->setPendingException(rval);
886 0 : break;
887 :
888 : case JSTRAP_ERROR:
889 0 : cx->clearPendingException();
890 0 : break;
891 :
892 : case JSTRAP_RETURN:
893 0 : frame.setReturnValue(rval);
894 0 : break;
895 :
896 : default:
897 0 : MOZ_CRASH("bad Debugger::onEnterFrame JSTrapStatus value");
898 : }
899 :
900 0 : return status;
901 : }
902 :
903 : static void
904 : DebuggerFrame_maybeDecrementFrameScriptStepModeCount(FreeOp* fop, AbstractFramePtr frame,
905 : NativeObject* frameobj);
906 :
907 : static void
908 : DebuggerFrame_freeScriptFrameIterData(FreeOp* fop, JSObject* obj);
909 :
910 : /*
911 : * Handle leaving a frame with debuggers watching. |frameOk| indicates whether
912 : * the frame is exiting normally or abruptly. Set |cx|'s exception and/or
913 : * |cx->fp()|'s return value, and return a new success value.
914 : */
915 : /* static */ bool
916 0 : Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc, bool frameOk)
917 : {
918 0 : mozilla::DebugOnly<Handle<GlobalObject*>> debuggeeGlobal = cx->global();
919 :
920 0 : auto frameMapsGuard = MakeScopeExit([&] {
921 : // Clean up all Debugger.Frame instances.
922 0 : removeFromFrameMapsAndClearBreakpointsIn(cx, frame);
923 0 : });
924 :
925 : // The onPop handler and associated clean up logic should not run multiple
926 : // times on the same frame. If slowPathOnLeaveFrame has already been
927 : // called, the frame will not be present in the Debugger frame maps.
928 0 : Rooted<DebuggerFrameVector> frames(cx, DebuggerFrameVector(cx));
929 0 : if (!getDebuggerFrames(frame, &frames))
930 0 : return false;
931 0 : if (frames.empty())
932 0 : return frameOk;
933 :
934 : /* Save the frame's completion value. */
935 : JSTrapStatus status;
936 0 : RootedValue value(cx);
937 0 : Debugger::resultToCompletion(cx, frameOk, frame.returnValue(), &status, &value);
938 :
939 : // This path can be hit via unwinding the stack due to over-recursion or
940 : // OOM. In those cases, don't fire the frames' onPop handlers, because
941 : // invoking JS will only trigger the same condition. See
942 : // slowPathOnExceptionUnwind.
943 0 : if (!cx->isThrowingOverRecursed() && !cx->isThrowingOutOfMemory()) {
944 : /* For each Debugger.Frame, fire its onPop handler, if any. */
945 0 : for (size_t i = 0; i < frames.length(); i++) {
946 0 : HandleDebuggerFrame frameobj = frames[i];
947 0 : Debugger* dbg = Debugger::fromChildJSObject(frameobj);
948 0 : EnterDebuggeeNoExecute nx(cx, *dbg);
949 :
950 0 : if (dbg->enabled && frameobj->onPopHandler())
951 : {
952 0 : OnPopHandler* handler = frameobj->onPopHandler();
953 :
954 0 : Maybe<AutoCompartment> ac;
955 0 : ac.emplace(cx, dbg->object);
956 :
957 0 : RootedValue wrappedValue(cx, value);
958 0 : RootedValue completion(cx);
959 0 : if (!dbg->wrapDebuggeeValue(cx, &wrappedValue))
960 : {
961 0 : status = dbg->reportUncaughtException(ac);
962 0 : break;
963 : }
964 :
965 : /* Call the onPop handler. */
966 0 : JSTrapStatus nextStatus = status;
967 0 : RootedValue nextValue(cx, wrappedValue);
968 0 : bool success = handler->onPop(cx, frameobj, nextStatus, &nextValue);
969 0 : nextStatus = dbg->processParsedHandlerResult(ac, frame, pc, success, nextStatus,
970 : &nextValue);
971 :
972 : /*
973 : * At this point, we are back in the debuggee compartment, and any error has
974 : * been wrapped up as a completion value.
975 : */
976 0 : MOZ_ASSERT(cx->compartment() == debuggeeGlobal->compartment());
977 0 : MOZ_ASSERT(!cx->isExceptionPending());
978 :
979 : /* JSTRAP_CONTINUE means "make no change". */
980 0 : if (nextStatus != JSTRAP_CONTINUE) {
981 0 : status = nextStatus;
982 0 : value = nextValue;
983 : }
984 : }
985 : }
986 : }
987 :
988 : /* Establish (status, value) as our resumption value. */
989 0 : switch (status) {
990 : case JSTRAP_RETURN:
991 0 : frame.setReturnValue(value);
992 0 : return true;
993 :
994 : case JSTRAP_THROW:
995 0 : cx->setPendingException(value);
996 0 : return false;
997 :
998 : case JSTRAP_ERROR:
999 0 : MOZ_ASSERT(!cx->isExceptionPending());
1000 0 : return false;
1001 :
1002 : default:
1003 0 : MOZ_CRASH("bad final trap status");
1004 : }
1005 : }
1006 :
1007 : /* static */ JSTrapStatus
1008 0 : Debugger::slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame)
1009 : {
1010 0 : RootedValue rval(cx);
1011 0 : JSTrapStatus status = dispatchHook(
1012 : cx,
1013 0 : [](Debugger* dbg) -> bool { return dbg->getHook(OnDebuggerStatement); },
1014 0 : [&](Debugger* dbg) -> JSTrapStatus {
1015 0 : return dbg->fireDebuggerStatement(cx, &rval);
1016 0 : });
1017 :
1018 0 : switch (status) {
1019 : case JSTRAP_CONTINUE:
1020 : case JSTRAP_ERROR:
1021 0 : break;
1022 :
1023 : case JSTRAP_RETURN:
1024 0 : frame.setReturnValue(rval);
1025 0 : break;
1026 :
1027 : case JSTRAP_THROW:
1028 0 : cx->setPendingException(rval);
1029 0 : break;
1030 :
1031 : default:
1032 0 : MOZ_CRASH("Invalid onDebuggerStatement trap status");
1033 : }
1034 :
1035 0 : return status;
1036 : }
1037 :
1038 : /* static */ JSTrapStatus
1039 0 : Debugger::slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame)
1040 : {
1041 : // Invoking more JS on an over-recursed stack or after OOM is only going
1042 : // to result in more of the same error.
1043 0 : if (cx->isThrowingOverRecursed() || cx->isThrowingOutOfMemory())
1044 0 : return JSTRAP_CONTINUE;
1045 :
1046 : // The Debugger API mustn't muck with frames from self-hosted scripts.
1047 0 : if (frame.hasScript() && frame.script()->selfHosted())
1048 0 : return JSTRAP_CONTINUE;
1049 :
1050 0 : RootedValue rval(cx);
1051 0 : JSTrapStatus status = dispatchHook(
1052 : cx,
1053 0 : [](Debugger* dbg) -> bool { return dbg->getHook(OnExceptionUnwind); },
1054 0 : [&](Debugger* dbg) -> JSTrapStatus {
1055 0 : return dbg->fireExceptionUnwind(cx, &rval);
1056 0 : });
1057 :
1058 0 : switch (status) {
1059 : case JSTRAP_CONTINUE:
1060 0 : break;
1061 :
1062 : case JSTRAP_THROW:
1063 0 : cx->setPendingException(rval);
1064 0 : break;
1065 :
1066 : case JSTRAP_ERROR:
1067 0 : cx->clearPendingException();
1068 0 : break;
1069 :
1070 : case JSTRAP_RETURN:
1071 0 : cx->clearPendingException();
1072 0 : frame.setReturnValue(rval);
1073 0 : break;
1074 :
1075 : default:
1076 0 : MOZ_CRASH("Invalid onExceptionUnwind trap status");
1077 : }
1078 :
1079 0 : return status;
1080 : }
1081 :
1082 : // TODO: Remove Remove this function when all properties/methods returning a
1083 : /// DebuggerEnvironment have been given a C++ interface (bug 1271649).
1084 : bool
1085 0 : Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env, MutableHandleValue rval)
1086 : {
1087 0 : if (!env) {
1088 0 : rval.setNull();
1089 0 : return true;
1090 : }
1091 :
1092 0 : RootedDebuggerEnvironment envobj(cx);
1093 :
1094 0 : if (!wrapEnvironment(cx, env, &envobj))
1095 0 : return false;
1096 :
1097 0 : rval.setObject(*envobj);
1098 0 : return true;
1099 : }
1100 :
1101 : bool
1102 0 : Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env,
1103 : MutableHandleDebuggerEnvironment result)
1104 : {
1105 0 : MOZ_ASSERT(env);
1106 :
1107 : /*
1108 : * DebuggerEnv should only wrap a debug scope chain obtained (transitively)
1109 : * from GetDebugEnvironmentFor(Frame|Function).
1110 : */
1111 0 : MOZ_ASSERT(!IsSyntacticEnvironment(env));
1112 :
1113 0 : DependentAddPtr<ObjectWeakMap> p(cx, environments, env);
1114 0 : if (p) {
1115 0 : result.set(&p->value()->as<DebuggerEnvironment>());
1116 : } else {
1117 : /* Create a new Debugger.Environment for env. */
1118 0 : RootedObject proto(cx, &object->getReservedSlot(JSSLOT_DEBUG_ENV_PROTO).toObject());
1119 0 : RootedNativeObject debugger(cx, object);
1120 :
1121 : RootedDebuggerEnvironment envobj(cx,
1122 0 : DebuggerEnvironment::create(cx, proto, env, debugger));
1123 0 : if (!envobj)
1124 0 : return false;
1125 :
1126 0 : if (!p.add(cx, environments, env, envobj)) {
1127 0 : NukeDebuggerWrapper(envobj);
1128 0 : return false;
1129 : }
1130 :
1131 0 : CrossCompartmentKey key(object, env, CrossCompartmentKey::DebuggerEnvironment);
1132 0 : if (!object->compartment()->putWrapper(cx, key, ObjectValue(*envobj))) {
1133 0 : NukeDebuggerWrapper(envobj);
1134 0 : environments.remove(env);
1135 0 : return false;
1136 : }
1137 :
1138 0 : result.set(envobj);
1139 : }
1140 :
1141 0 : return true;
1142 : }
1143 :
1144 : bool
1145 0 : Debugger::wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp)
1146 : {
1147 0 : assertSameCompartment(cx, object.get());
1148 :
1149 0 : if (vp.isObject()) {
1150 0 : RootedObject obj(cx, &vp.toObject());
1151 0 : RootedDebuggerObject dobj(cx);
1152 :
1153 0 : if (!wrapDebuggeeObject(cx, obj, &dobj))
1154 0 : return false;
1155 :
1156 0 : vp.setObject(*dobj);
1157 0 : } else if (vp.isMagic()) {
1158 0 : RootedPlainObject optObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
1159 0 : if (!optObj)
1160 0 : return false;
1161 :
1162 : // We handle three sentinel values: missing arguments (overloading
1163 : // JS_OPTIMIZED_ARGUMENTS), optimized out slots (JS_OPTIMIZED_OUT),
1164 : // and uninitialized bindings (JS_UNINITIALIZED_LEXICAL).
1165 : //
1166 : // Other magic values should not have escaped.
1167 : PropertyName* name;
1168 0 : switch (vp.whyMagic()) {
1169 0 : case JS_OPTIMIZED_ARGUMENTS: name = cx->names().missingArguments; break;
1170 0 : case JS_OPTIMIZED_OUT: name = cx->names().optimizedOut; break;
1171 0 : case JS_UNINITIALIZED_LEXICAL: name = cx->names().uninitialized; break;
1172 0 : default: MOZ_CRASH("Unsupported magic value escaped to Debugger");
1173 : }
1174 :
1175 0 : RootedValue trueVal(cx, BooleanValue(true));
1176 0 : if (!DefineProperty(cx, optObj, name, trueVal))
1177 0 : return false;
1178 :
1179 0 : vp.setObject(*optObj);
1180 0 : } else if (!cx->compartment()->wrap(cx, vp)) {
1181 0 : vp.setUndefined();
1182 0 : return false;
1183 : }
1184 :
1185 0 : return true;
1186 : }
1187 :
1188 : bool
1189 0 : Debugger::wrapDebuggeeObject(JSContext* cx, HandleObject obj,
1190 : MutableHandleDebuggerObject result)
1191 : {
1192 0 : MOZ_ASSERT(obj);
1193 :
1194 0 : if (obj->is<JSFunction>()) {
1195 0 : MOZ_ASSERT(!IsInternalFunctionObject(*obj));
1196 0 : RootedFunction fun(cx, &obj->as<JSFunction>());
1197 0 : if (!EnsureFunctionHasScript(cx, fun))
1198 0 : return false;
1199 : }
1200 :
1201 0 : DependentAddPtr<ObjectWeakMap> p(cx, objects, obj);
1202 0 : if (p) {
1203 0 : result.set(&p->value()->as<DebuggerObject>());
1204 : } else {
1205 : /* Create a new Debugger.Object for obj. */
1206 0 : RootedNativeObject debugger(cx, object);
1207 0 : RootedObject proto(cx, &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject());
1208 0 : RootedDebuggerObject dobj(cx, DebuggerObject::create(cx, proto, obj, debugger));
1209 0 : if (!dobj)
1210 0 : return false;
1211 :
1212 0 : if (!p.add(cx, objects, obj, dobj)) {
1213 0 : NukeDebuggerWrapper(dobj);
1214 0 : return false;
1215 : }
1216 :
1217 0 : if (obj->compartment() != object->compartment()) {
1218 0 : CrossCompartmentKey key(object, obj, CrossCompartmentKey::DebuggerObject);
1219 0 : if (!object->compartment()->putWrapper(cx, key, ObjectValue(*dobj))) {
1220 0 : NukeDebuggerWrapper(dobj);
1221 0 : objects.remove(obj);
1222 0 : ReportOutOfMemory(cx);
1223 0 : return false;
1224 : }
1225 : }
1226 :
1227 0 : result.set(dobj);
1228 : }
1229 :
1230 0 : return true;
1231 : }
1232 :
1233 : static NativeObject*
1234 0 : ToNativeDebuggerObject(JSContext* cx, MutableHandleObject obj)
1235 : {
1236 0 : if (obj->getClass() != &DebuggerObject::class_) {
1237 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
1238 0 : "Debugger", "Debugger.Object", obj->getClass()->name);
1239 0 : return nullptr;
1240 : }
1241 :
1242 0 : NativeObject* ndobj = &obj->as<NativeObject>();
1243 :
1244 0 : Value owner = ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);
1245 0 : if (owner.isUndefined()) {
1246 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1247 0 : JSMSG_DEBUG_PROTO, "Debugger.Object", "Debugger.Object");
1248 0 : return nullptr;
1249 : }
1250 :
1251 0 : return ndobj;
1252 : }
1253 :
1254 : bool
1255 0 : Debugger::unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj)
1256 : {
1257 0 : NativeObject* ndobj = ToNativeDebuggerObject(cx, obj);
1258 0 : if (!ndobj)
1259 0 : return false;
1260 :
1261 0 : Value owner = ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);
1262 0 : if (&owner.toObject() != object) {
1263 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1264 0 : JSMSG_DEBUG_WRONG_OWNER, "Debugger.Object");
1265 0 : return false;
1266 : }
1267 :
1268 0 : obj.set(static_cast<JSObject*>(ndobj->getPrivate()));
1269 0 : return true;
1270 : }
1271 :
1272 : bool
1273 0 : Debugger::unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp)
1274 : {
1275 0 : assertSameCompartment(cx, object.get(), vp);
1276 0 : if (vp.isObject()) {
1277 0 : RootedObject dobj(cx, &vp.toObject());
1278 0 : if (!unwrapDebuggeeObject(cx, &dobj))
1279 0 : return false;
1280 0 : vp.setObject(*dobj);
1281 : }
1282 0 : return true;
1283 : }
1284 :
1285 : static bool
1286 0 : CheckArgCompartment(JSContext* cx, JSObject* obj, JSObject* arg,
1287 : const char* methodname, const char* propname)
1288 : {
1289 0 : if (arg->compartment() != obj->compartment()) {
1290 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_COMPARTMENT_MISMATCH,
1291 0 : methodname, propname);
1292 0 : return false;
1293 : }
1294 0 : return true;
1295 : }
1296 :
1297 : static bool
1298 0 : CheckArgCompartment(JSContext* cx, JSObject* obj, HandleValue v,
1299 : const char* methodname, const char* propname)
1300 : {
1301 0 : if (v.isObject())
1302 0 : return CheckArgCompartment(cx, obj, &v.toObject(), methodname, propname);
1303 0 : return true;
1304 : }
1305 :
1306 : bool
1307 0 : Debugger::unwrapPropertyDescriptor(JSContext* cx, HandleObject obj,
1308 : MutableHandle<PropertyDescriptor> desc)
1309 : {
1310 0 : if (desc.hasValue()) {
1311 0 : RootedValue value(cx, desc.value());
1312 0 : if (!unwrapDebuggeeValue(cx, &value) ||
1313 0 : !CheckArgCompartment(cx, obj, value, "defineProperty", "value"))
1314 : {
1315 0 : return false;
1316 : }
1317 0 : desc.setValue(value);
1318 : }
1319 :
1320 0 : if (desc.hasGetterObject()) {
1321 0 : RootedObject get(cx, desc.getterObject());
1322 0 : if (get) {
1323 0 : if (!unwrapDebuggeeObject(cx, &get))
1324 0 : return false;
1325 0 : if (!CheckArgCompartment(cx, obj, get, "defineProperty", "get"))
1326 0 : return false;
1327 : }
1328 0 : desc.setGetterObject(get);
1329 : }
1330 :
1331 0 : if (desc.hasSetterObject()) {
1332 0 : RootedObject set(cx, desc.setterObject());
1333 0 : if (set) {
1334 0 : if (!unwrapDebuggeeObject(cx, &set))
1335 0 : return false;
1336 0 : if (!CheckArgCompartment(cx, obj, set, "defineProperty", "set"))
1337 0 : return false;
1338 : }
1339 0 : desc.setSetterObject(set);
1340 : }
1341 :
1342 0 : return true;
1343 : }
1344 :
1345 : JSTrapStatus
1346 0 : Debugger::reportUncaughtException(Maybe<AutoCompartment>& ac)
1347 : {
1348 0 : JSContext* cx = ac->context();
1349 :
1350 : // Uncaught exceptions arise from Debugger code, and so we must already be
1351 : // in an NX section.
1352 0 : MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx, *this));
1353 :
1354 0 : if (cx->isExceptionPending()) {
1355 : /*
1356 : * We want to report the pending exception, but we want to let the
1357 : * embedding handle it however it wants to. So pretend like we're
1358 : * starting a new script execution on our current compartment (which
1359 : * is the debugger compartment, so reported errors won't get
1360 : * reported to various onerror handlers in debuggees) and as part of
1361 : * that "execution" simply throw our exception so the embedding can
1362 : * deal.
1363 : */
1364 0 : RootedValue exn(cx);
1365 0 : if (cx->getPendingException(&exn)) {
1366 : /*
1367 : * Clear the exception, because ReportErrorToGlobal will assert that
1368 : * we don't have one.
1369 : */
1370 0 : cx->clearPendingException();
1371 0 : ReportErrorToGlobal(cx, cx->global(), exn);
1372 : }
1373 : /*
1374 : * And if not, or if PrepareScriptEnvironmentAndInvoke somehow left
1375 : * an exception on cx (which it totally shouldn't do), just give
1376 : * up.
1377 : */
1378 0 : cx->clearPendingException();
1379 : }
1380 :
1381 0 : ac.reset();
1382 0 : return JSTRAP_ERROR;
1383 : }
1384 :
1385 : JSTrapStatus
1386 0 : Debugger::handleUncaughtExceptionHelper(Maybe<AutoCompartment>& ac, MutableHandleValue* vp,
1387 : const Maybe<HandleValue>& thisVForCheck,
1388 : AbstractFramePtr frame)
1389 : {
1390 0 : JSContext* cx = ac->context();
1391 :
1392 : // Uncaught exceptions arise from Debugger code, and so we must already be
1393 : // in an NX section.
1394 0 : MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx, *this));
1395 :
1396 0 : if (cx->isExceptionPending()) {
1397 0 : if (uncaughtExceptionHook) {
1398 0 : RootedValue exc(cx);
1399 0 : if (!cx->getPendingException(&exc))
1400 0 : return JSTRAP_ERROR;
1401 0 : cx->clearPendingException();
1402 :
1403 0 : RootedValue fval(cx, ObjectValue(*uncaughtExceptionHook));
1404 0 : RootedValue rv(cx);
1405 0 : if (js::Call(cx, fval, object, exc, &rv)) {
1406 0 : if (vp) {
1407 0 : JSTrapStatus status = JSTRAP_CONTINUE;
1408 0 : if (processResumptionValue(ac, frame, thisVForCheck, rv, status, *vp))
1409 0 : return status;
1410 : } else {
1411 0 : return JSTRAP_CONTINUE;
1412 : }
1413 : }
1414 : }
1415 :
1416 0 : return reportUncaughtException(ac);
1417 : }
1418 :
1419 0 : ac.reset();
1420 0 : return JSTRAP_ERROR;
1421 : }
1422 :
1423 : JSTrapStatus
1424 0 : Debugger::handleUncaughtException(Maybe<AutoCompartment>& ac, MutableHandleValue vp,
1425 : const Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame)
1426 : {
1427 0 : return handleUncaughtExceptionHelper(ac, &vp, thisVForCheck, frame);
1428 : }
1429 :
1430 : JSTrapStatus
1431 0 : Debugger::handleUncaughtException(Maybe<AutoCompartment>& ac)
1432 : {
1433 0 : return handleUncaughtExceptionHelper(ac, nullptr, mozilla::Nothing(), NullFramePtr());
1434 : }
1435 :
1436 : /* static */ void
1437 0 : Debugger::resultToCompletion(JSContext* cx, bool ok, const Value& rv,
1438 : JSTrapStatus* status, MutableHandleValue value)
1439 : {
1440 0 : MOZ_ASSERT_IF(ok, !cx->isExceptionPending());
1441 :
1442 0 : if (ok) {
1443 0 : *status = JSTRAP_RETURN;
1444 0 : value.set(rv);
1445 0 : } else if (cx->isExceptionPending()) {
1446 0 : *status = JSTRAP_THROW;
1447 0 : if (!cx->getPendingException(value))
1448 0 : *status = JSTRAP_ERROR;
1449 0 : cx->clearPendingException();
1450 : } else {
1451 0 : *status = JSTRAP_ERROR;
1452 0 : value.setUndefined();
1453 : }
1454 0 : }
1455 :
1456 : bool
1457 0 : Debugger::newCompletionValue(JSContext* cx, JSTrapStatus status, const Value& value_,
1458 : MutableHandleValue result)
1459 : {
1460 : /*
1461 : * We must be in the debugger's compartment, since that's where we want
1462 : * to construct the completion value.
1463 : */
1464 0 : assertSameCompartment(cx, object.get());
1465 0 : assertSameCompartment(cx, value_);
1466 :
1467 0 : RootedId key(cx);
1468 0 : RootedValue value(cx, value_);
1469 :
1470 0 : switch (status) {
1471 : case JSTRAP_RETURN:
1472 0 : key = NameToId(cx->names().return_);
1473 0 : break;
1474 :
1475 : case JSTRAP_THROW:
1476 0 : key = NameToId(cx->names().throw_);
1477 0 : break;
1478 :
1479 : case JSTRAP_ERROR:
1480 0 : result.setNull();
1481 0 : return true;
1482 :
1483 : default:
1484 0 : MOZ_CRASH("bad status passed to Debugger::newCompletionValue");
1485 : }
1486 :
1487 : /* Common tail for JSTRAP_RETURN and JSTRAP_THROW. */
1488 0 : RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
1489 0 : if (!obj ||
1490 0 : !NativeDefineProperty(cx, obj, key, value, nullptr, nullptr, JSPROP_ENUMERATE))
1491 : {
1492 0 : return false;
1493 : }
1494 :
1495 0 : result.setObject(*obj);
1496 0 : return true;
1497 : }
1498 :
1499 : bool
1500 0 : Debugger::receiveCompletionValue(Maybe<AutoCompartment>& ac, bool ok,
1501 : HandleValue val,
1502 : MutableHandleValue vp)
1503 : {
1504 0 : JSContext* cx = ac->context();
1505 :
1506 : JSTrapStatus status;
1507 0 : RootedValue value(cx);
1508 0 : resultToCompletion(cx, ok, val, &status, &value);
1509 0 : ac.reset();
1510 0 : return wrapDebuggeeValue(cx, &value) &&
1511 0 : newCompletionValue(cx, status, value, vp);
1512 : }
1513 :
1514 : static bool
1515 0 : GetStatusProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, JSTrapStatus status,
1516 : JSTrapStatus& statusp, MutableHandleValue vp, int* hits)
1517 : {
1518 : bool found;
1519 0 : if (!HasProperty(cx, obj, name, &found))
1520 0 : return false;
1521 0 : if (found) {
1522 0 : ++*hits;
1523 0 : statusp = status;
1524 0 : if (!GetProperty(cx, obj, obj, name, vp))
1525 0 : return false;
1526 : }
1527 0 : return true;
1528 : }
1529 :
1530 : static bool
1531 0 : ParseResumptionValueAsObject(JSContext* cx, HandleValue rv, JSTrapStatus& statusp,
1532 : MutableHandleValue vp)
1533 : {
1534 0 : int hits = 0;
1535 0 : if (rv.isObject()) {
1536 0 : RootedObject obj(cx, &rv.toObject());
1537 0 : if (!GetStatusProperty(cx, obj, cx->names().return_, JSTRAP_RETURN, statusp, vp, &hits))
1538 0 : return false;
1539 0 : if (!GetStatusProperty(cx, obj, cx->names().throw_, JSTRAP_THROW, statusp, vp, &hits))
1540 0 : return false;
1541 : }
1542 :
1543 0 : if (hits != 1) {
1544 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_RESUMPTION);
1545 0 : return false;
1546 : }
1547 0 : return true;
1548 : }
1549 :
1550 : static bool
1551 0 : ParseResumptionValue(JSContext* cx, HandleValue rval, JSTrapStatus& statusp, MutableHandleValue vp)
1552 : {
1553 0 : if (rval.isUndefined()) {
1554 0 : statusp = JSTRAP_CONTINUE;
1555 0 : vp.setUndefined();
1556 0 : return true;
1557 : }
1558 0 : if (rval.isNull()) {
1559 0 : statusp = JSTRAP_ERROR;
1560 0 : vp.setUndefined();
1561 0 : return true;
1562 : }
1563 0 : return ParseResumptionValueAsObject(cx, rval, statusp, vp);
1564 : }
1565 :
1566 : static bool
1567 0 : CheckResumptionValue(JSContext* cx, AbstractFramePtr frame, const Maybe<HandleValue>& maybeThisv,
1568 : JSTrapStatus status, MutableHandleValue vp)
1569 : {
1570 0 : if (status == JSTRAP_RETURN && frame && frame.isFunctionFrame()) {
1571 : // Don't let a { return: ... } resumption value make a generator
1572 : // function violate the iterator protocol. The return value from
1573 : // such a frame must have the form { done: <bool>, value: <anything> }.
1574 0 : RootedFunction callee(cx, frame.callee());
1575 0 : if (callee->isStarGenerator()) {
1576 0 : if (!CheckStarGeneratorResumptionValue(cx, vp)) {
1577 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD);
1578 0 : return false;
1579 : }
1580 : }
1581 : }
1582 :
1583 0 : if (maybeThisv.isSome()) {
1584 0 : const HandleValue& thisv = maybeThisv.ref();
1585 0 : if (status == JSTRAP_RETURN && vp.isPrimitive()) {
1586 0 : if (vp.isUndefined()) {
1587 0 : if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL))
1588 0 : return ThrowUninitializedThis(cx, frame);
1589 :
1590 0 : vp.set(thisv);
1591 : } else {
1592 0 : ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, vp, nullptr);
1593 0 : return false;
1594 : }
1595 : }
1596 : }
1597 0 : return true;
1598 : }
1599 :
1600 : static bool
1601 0 : GetThisValueForCheck(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
1602 : MutableHandleValue thisv, Maybe<HandleValue>& maybeThisv)
1603 : {
1604 0 : if (frame.debuggerNeedsCheckPrimitiveReturn()) {
1605 : {
1606 0 : AutoCompartment ac(cx, frame.environmentChain());
1607 0 : if (!GetThisValueForDebuggerMaybeOptimizedOut(cx, frame, pc, thisv))
1608 0 : return false;
1609 : }
1610 :
1611 0 : if (!cx->compartment()->wrap(cx, thisv))
1612 0 : return false;
1613 :
1614 0 : MOZ_ASSERT_IF(thisv.isMagic(), thisv.isMagic(JS_UNINITIALIZED_LEXICAL));
1615 0 : maybeThisv.emplace(HandleValue(thisv));
1616 : }
1617 :
1618 0 : return true;
1619 : }
1620 :
1621 : bool
1622 0 : Debugger::processResumptionValue(Maybe<AutoCompartment>& ac, AbstractFramePtr frame,
1623 : const Maybe<HandleValue>& maybeThisv, HandleValue rval,
1624 : JSTrapStatus& statusp, MutableHandleValue vp)
1625 : {
1626 0 : JSContext* cx = ac->context();
1627 :
1628 0 : if (!ParseResumptionValue(cx, rval, statusp, vp) ||
1629 0 : !unwrapDebuggeeValue(cx, vp) ||
1630 0 : !CheckResumptionValue(cx, frame, maybeThisv, statusp, vp))
1631 : {
1632 0 : return false;
1633 : }
1634 :
1635 0 : ac.reset();
1636 0 : if (!cx->compartment()->wrap(cx, vp)) {
1637 0 : statusp = JSTRAP_ERROR;
1638 0 : vp.setUndefined();
1639 : }
1640 :
1641 0 : return true;
1642 : }
1643 :
1644 : JSTrapStatus
1645 0 : Debugger::processParsedHandlerResultHelper(Maybe<AutoCompartment>& ac, AbstractFramePtr frame,
1646 : const Maybe<HandleValue>& maybeThisv, bool success,
1647 : JSTrapStatus status, MutableHandleValue vp)
1648 : {
1649 0 : if (!success)
1650 0 : return handleUncaughtException(ac, vp, maybeThisv, frame);
1651 :
1652 0 : JSContext* cx = ac->context();
1653 :
1654 0 : if (!unwrapDebuggeeValue(cx, vp) ||
1655 0 : !CheckResumptionValue(cx, frame, maybeThisv, status, vp))
1656 : {
1657 0 : return handleUncaughtException(ac, vp, maybeThisv, frame);
1658 : }
1659 :
1660 0 : ac.reset();
1661 0 : if (!cx->compartment()->wrap(cx, vp)) {
1662 0 : status = JSTRAP_ERROR;
1663 0 : vp.setUndefined();
1664 : }
1665 :
1666 0 : return status;
1667 : }
1668 :
1669 : JSTrapStatus
1670 0 : Debugger::processParsedHandlerResult(Maybe<AutoCompartment>& ac, AbstractFramePtr frame,
1671 : jsbytecode* pc, bool success, JSTrapStatus status,
1672 : MutableHandleValue vp)
1673 : {
1674 0 : JSContext* cx = ac->context();
1675 :
1676 0 : RootedValue thisv(cx);
1677 0 : Maybe<HandleValue> maybeThisv;
1678 0 : if (!GetThisValueForCheck(cx, frame, pc, &thisv, maybeThisv)) {
1679 0 : ac.reset();
1680 0 : return JSTRAP_ERROR;
1681 : }
1682 :
1683 0 : return processParsedHandlerResultHelper(ac, frame, maybeThisv, success, status, vp);
1684 : }
1685 :
1686 : JSTrapStatus
1687 0 : Debugger::processHandlerResult(Maybe<AutoCompartment>& ac, bool success, const Value& rv,
1688 : AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp)
1689 : {
1690 0 : JSContext* cx = ac->context();
1691 :
1692 0 : RootedValue thisv(cx);
1693 0 : Maybe<HandleValue> maybeThisv;
1694 0 : if (!GetThisValueForCheck(cx, frame, pc, &thisv, maybeThisv)) {
1695 0 : ac.reset();
1696 0 : return JSTRAP_ERROR;
1697 : }
1698 :
1699 0 : if (!success)
1700 0 : return handleUncaughtException(ac, vp, maybeThisv, frame);
1701 :
1702 0 : RootedValue rootRv(cx, rv);
1703 0 : JSTrapStatus status = JSTRAP_CONTINUE;
1704 0 : success = ParseResumptionValue(cx, rootRv, status, vp);
1705 :
1706 0 : return processParsedHandlerResultHelper(ac, frame, maybeThisv, success, status, vp);
1707 : }
1708 :
1709 : static bool
1710 0 : CallMethodIfPresent(JSContext* cx, HandleObject obj, const char* name, size_t argc, Value* argv,
1711 : MutableHandleValue rval)
1712 : {
1713 0 : rval.setUndefined();
1714 0 : JSAtom* atom = Atomize(cx, name, strlen(name));
1715 0 : if (!atom)
1716 0 : return false;
1717 :
1718 0 : RootedId id(cx, AtomToId(atom));
1719 0 : RootedValue fval(cx);
1720 0 : if (!GetProperty(cx, obj, obj, id, &fval))
1721 0 : return false;
1722 :
1723 0 : if (!IsCallable(fval))
1724 0 : return true;
1725 :
1726 0 : InvokeArgs args(cx);
1727 0 : if (!args.init(cx, argc))
1728 0 : return false;
1729 :
1730 0 : for (size_t i = 0; i < argc; i++)
1731 0 : args[i].set(argv[i]);
1732 :
1733 0 : rval.setObject(*obj); // overwritten by successful Call
1734 0 : return js::Call(cx, fval, rval, args, rval);
1735 : }
1736 :
1737 : JSTrapStatus
1738 0 : Debugger::fireDebuggerStatement(JSContext* cx, MutableHandleValue vp)
1739 : {
1740 0 : RootedObject hook(cx, getHook(OnDebuggerStatement));
1741 0 : MOZ_ASSERT(hook);
1742 0 : MOZ_ASSERT(hook->isCallable());
1743 :
1744 0 : Maybe<AutoCompartment> ac;
1745 0 : ac.emplace(cx, object);
1746 :
1747 0 : ScriptFrameIter iter(cx);
1748 0 : RootedValue scriptFrame(cx);
1749 0 : if (!getScriptFrame(cx, iter, &scriptFrame))
1750 0 : return reportUncaughtException(ac);
1751 :
1752 0 : RootedValue fval(cx, ObjectValue(*hook));
1753 0 : RootedValue rv(cx);
1754 0 : bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
1755 0 : return processHandlerResult(ac, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
1756 : }
1757 :
1758 : JSTrapStatus
1759 0 : Debugger::fireExceptionUnwind(JSContext* cx, MutableHandleValue vp)
1760 : {
1761 0 : RootedObject hook(cx, getHook(OnExceptionUnwind));
1762 0 : MOZ_ASSERT(hook);
1763 0 : MOZ_ASSERT(hook->isCallable());
1764 :
1765 0 : RootedValue exc(cx);
1766 0 : if (!cx->getPendingException(&exc))
1767 0 : return JSTRAP_ERROR;
1768 0 : cx->clearPendingException();
1769 :
1770 0 : Maybe<AutoCompartment> ac;
1771 0 : ac.emplace(cx, object);
1772 :
1773 0 : RootedValue scriptFrame(cx);
1774 0 : RootedValue wrappedExc(cx, exc);
1775 :
1776 0 : FrameIter iter(cx);
1777 0 : if (!getScriptFrame(cx, iter, &scriptFrame) || !wrapDebuggeeValue(cx, &wrappedExc))
1778 0 : return reportUncaughtException(ac);
1779 :
1780 0 : RootedValue fval(cx, ObjectValue(*hook));
1781 0 : RootedValue rv(cx);
1782 0 : bool ok = js::Call(cx, fval, object, scriptFrame, wrappedExc, &rv);
1783 0 : JSTrapStatus st = processHandlerResult(ac, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
1784 0 : if (st == JSTRAP_CONTINUE)
1785 0 : cx->setPendingException(exc);
1786 0 : return st;
1787 : }
1788 :
1789 : JSTrapStatus
1790 0 : Debugger::fireEnterFrame(JSContext* cx, MutableHandleValue vp)
1791 : {
1792 0 : RootedObject hook(cx, getHook(OnEnterFrame));
1793 0 : MOZ_ASSERT(hook);
1794 0 : MOZ_ASSERT(hook->isCallable());
1795 :
1796 0 : Maybe<AutoCompartment> ac;
1797 0 : ac.emplace(cx, object);
1798 :
1799 0 : RootedValue scriptFrame(cx);
1800 :
1801 0 : FrameIter iter(cx);
1802 0 : if (!getScriptFrame(cx, iter, &scriptFrame))
1803 0 : return reportUncaughtException(ac);
1804 :
1805 0 : RootedValue fval(cx, ObjectValue(*hook));
1806 0 : RootedValue rv(cx);
1807 0 : bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
1808 :
1809 0 : return processHandlerResult(ac, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
1810 : }
1811 :
1812 : void
1813 0 : Debugger::fireNewScript(JSContext* cx, Handle<DebuggerScriptReferent> scriptReferent)
1814 : {
1815 0 : RootedObject hook(cx, getHook(OnNewScript));
1816 0 : MOZ_ASSERT(hook);
1817 0 : MOZ_ASSERT(hook->isCallable());
1818 :
1819 0 : Maybe<AutoCompartment> ac;
1820 0 : ac.emplace(cx, object);
1821 :
1822 0 : JSObject* dsobj = wrapVariantReferent(cx, scriptReferent);
1823 0 : if (!dsobj) {
1824 0 : reportUncaughtException(ac);
1825 0 : return;
1826 : }
1827 :
1828 0 : RootedValue fval(cx, ObjectValue(*hook));
1829 0 : RootedValue dsval(cx, ObjectValue(*dsobj));
1830 0 : RootedValue rv(cx);
1831 0 : if (!js::Call(cx, fval, object, dsval, &rv))
1832 0 : handleUncaughtException(ac);
1833 : }
1834 :
1835 : void
1836 0 : Debugger::fireOnGarbageCollectionHook(JSContext* cx,
1837 : const JS::dbg::GarbageCollectionEvent::Ptr& gcData)
1838 : {
1839 0 : MOZ_ASSERT(observedGC(gcData->majorGCNumber()));
1840 0 : observedGCs.remove(gcData->majorGCNumber());
1841 :
1842 0 : RootedObject hook(cx, getHook(OnGarbageCollection));
1843 0 : MOZ_ASSERT(hook);
1844 0 : MOZ_ASSERT(hook->isCallable());
1845 :
1846 0 : Maybe<AutoCompartment> ac;
1847 0 : ac.emplace(cx, object);
1848 :
1849 0 : JSObject* dataObj = gcData->toJSObject(cx);
1850 0 : if (!dataObj) {
1851 0 : reportUncaughtException(ac);
1852 0 : return;
1853 : }
1854 :
1855 0 : RootedValue fval(cx, ObjectValue(*hook));
1856 0 : RootedValue dataVal(cx, ObjectValue(*dataObj));
1857 0 : RootedValue rv(cx);
1858 0 : if (!js::Call(cx, fval, object, dataVal, &rv))
1859 0 : handleUncaughtException(ac);
1860 : }
1861 :
1862 : template <typename HookIsEnabledFun /* bool (Debugger*) */,
1863 : typename FireHookFun /* JSTrapStatus (Debugger*) */>
1864 : /* static */ JSTrapStatus
1865 736 : Debugger::dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled, FireHookFun fireHook)
1866 : {
1867 : /*
1868 : * Determine which debuggers will receive this event, and in what order.
1869 : * Make a copy of the list, since the original is mutable and we will be
1870 : * calling into arbitrary JS.
1871 : *
1872 : * Note: In the general case, 'triggered' contains references to objects in
1873 : * different compartments--every compartment *except* this one.
1874 : */
1875 1472 : AutoValueVector triggered(cx);
1876 736 : Handle<GlobalObject*> global = cx->global();
1877 736 : if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
1878 0 : for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
1879 0 : Debugger* dbg = *p;
1880 0 : if (dbg->enabled && hookIsEnabled(dbg)) {
1881 0 : if (!triggered.append(ObjectValue(*dbg->toJSObject())))
1882 0 : return JSTRAP_ERROR;
1883 : }
1884 : }
1885 : }
1886 :
1887 : /*
1888 : * Deliver the event to each debugger, checking again to make sure it
1889 : * should still be delivered.
1890 : */
1891 736 : for (Value* p = triggered.begin(); p != triggered.end(); p++) {
1892 0 : Debugger* dbg = Debugger::fromJSObject(&p->toObject());
1893 0 : EnterDebuggeeNoExecute nx(cx, *dbg);
1894 0 : if (dbg->debuggees.has(global) && dbg->enabled && hookIsEnabled(dbg)) {
1895 0 : JSTrapStatus st = fireHook(dbg);
1896 0 : if (st != JSTRAP_CONTINUE)
1897 0 : return st;
1898 : }
1899 : }
1900 736 : return JSTRAP_CONTINUE;
1901 : }
1902 :
1903 : void
1904 0 : Debugger::slowPathOnNewScript(JSContext* cx, HandleScript script)
1905 : {
1906 0 : JSTrapStatus status = dispatchHook(
1907 : cx,
1908 0 : [script](Debugger* dbg) -> bool {
1909 0 : return dbg->observesNewScript() && dbg->observesScript(script);
1910 : },
1911 0 : [&](Debugger* dbg) -> JSTrapStatus {
1912 0 : Rooted<DebuggerScriptReferent> scriptReferent(cx, script.get());
1913 0 : dbg->fireNewScript(cx, scriptReferent);
1914 0 : return JSTRAP_CONTINUE;
1915 0 : });
1916 :
1917 : // dispatchHook may fail due to OOM. This OOM is not handlable at the
1918 : // callsites of onNewScript in the engine.
1919 0 : if (status == JSTRAP_ERROR) {
1920 0 : cx->clearPendingException();
1921 0 : return;
1922 : }
1923 :
1924 0 : MOZ_ASSERT(status == JSTRAP_CONTINUE);
1925 : }
1926 :
1927 : void
1928 0 : Debugger::slowPathOnNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance)
1929 : {
1930 0 : JSTrapStatus status = dispatchHook(
1931 : cx,
1932 0 : [wasmInstance](Debugger* dbg) -> bool {
1933 0 : return dbg->observesNewScript() && dbg->observesGlobal(&wasmInstance->global());
1934 : },
1935 0 : [&](Debugger* dbg) -> JSTrapStatus {
1936 0 : Rooted<DebuggerScriptReferent> scriptReferent(cx, wasmInstance.get());
1937 0 : dbg->fireNewScript(cx, scriptReferent);
1938 0 : return JSTRAP_CONTINUE;
1939 0 : });
1940 :
1941 : // dispatchHook may fail due to OOM. This OOM is not handlable at the
1942 : // callsites of onNewWasmInstance in the engine.
1943 0 : if (status == JSTRAP_ERROR) {
1944 0 : cx->clearPendingException();
1945 0 : return;
1946 : }
1947 :
1948 0 : MOZ_ASSERT(status == JSTRAP_CONTINUE);
1949 : }
1950 :
1951 : /* static */ JSTrapStatus
1952 0 : Debugger::onTrap(JSContext* cx, MutableHandleValue vp)
1953 : {
1954 0 : FrameIter iter(cx);
1955 0 : JS::AutoSaveExceptionState saveExc(cx);
1956 0 : Rooted<GlobalObject*> global(cx);
1957 : BreakpointSite* site;
1958 : bool isJS; // true when iter.hasScript(), false when iter.isWasm()
1959 : jsbytecode* pc; // valid when isJS == true
1960 : uint32_t bytecodeOffset; // valid when isJS == false
1961 0 : if (iter.hasScript()) {
1962 0 : RootedScript script(cx, iter.script());
1963 0 : MOZ_ASSERT(script->isDebuggee());
1964 0 : global.set(&script->global());
1965 0 : isJS = true;
1966 0 : pc = iter.pc();
1967 0 : bytecodeOffset = 0;
1968 0 : site = script->getBreakpointSite(pc);
1969 : } else {
1970 0 : MOZ_ASSERT(iter.isWasm());
1971 0 : global.set(&iter.wasmInstance()->object()->global());
1972 0 : isJS = false;
1973 0 : pc = nullptr;
1974 0 : bytecodeOffset = iter.wasmBytecodeOffset();
1975 0 : site = iter.wasmInstance()->debug().getOrCreateBreakpointSite(cx, bytecodeOffset);
1976 : }
1977 :
1978 : /* Build list of breakpoint handlers. */
1979 0 : Vector<Breakpoint*> triggered(cx);
1980 0 : for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
1981 : // Skip a breakpoint that is not set for the current wasm::Instance --
1982 : // single wasm::Code can handle breakpoints for mutiple instances.
1983 0 : if (!isJS && &bp->asWasm()->wasmInstance->instance() != iter.wasmInstance())
1984 0 : continue;
1985 0 : if (!triggered.append(bp))
1986 0 : return JSTRAP_ERROR;
1987 : }
1988 :
1989 0 : for (Breakpoint** p = triggered.begin(); p != triggered.end(); p++) {
1990 0 : Breakpoint* bp = *p;
1991 :
1992 : /* Handlers can clear breakpoints. Check that bp still exists. */
1993 0 : if (!site || !site->hasBreakpoint(bp))
1994 0 : continue;
1995 :
1996 : /*
1997 : * There are two reasons we have to check whether dbg is enabled and
1998 : * debugging global.
1999 : *
2000 : * One is just that one breakpoint handler can disable other Debuggers
2001 : * or remove debuggees.
2002 : *
2003 : * The other has to do with non-compile-and-go scripts, which have no
2004 : * specific global--until they are executed. Only now do we know which
2005 : * global the script is running against.
2006 : */
2007 0 : Debugger* dbg = bp->debugger;
2008 0 : bool hasDebuggee = dbg->enabled && dbg->debuggees.has(global);
2009 0 : if (hasDebuggee) {
2010 0 : Maybe<AutoCompartment> ac;
2011 0 : ac.emplace(cx, dbg->object);
2012 0 : EnterDebuggeeNoExecute nx(cx, *dbg);
2013 :
2014 0 : RootedValue scriptFrame(cx);
2015 0 : if (!dbg->getScriptFrame(cx, iter, &scriptFrame))
2016 0 : return dbg->reportUncaughtException(ac);
2017 0 : RootedValue rv(cx);
2018 0 : Rooted<JSObject*> handler(cx, bp->handler);
2019 0 : bool ok = CallMethodIfPresent(cx, handler, "hit", 1, scriptFrame.address(), &rv);
2020 0 : JSTrapStatus st = dbg->processHandlerResult(ac, ok, rv, iter.abstractFramePtr(),
2021 0 : iter.pc(), vp);
2022 0 : if (st != JSTRAP_CONTINUE)
2023 0 : return st;
2024 :
2025 : /* Calling JS code invalidates site. Reload it. */
2026 0 : if (isJS)
2027 0 : site = iter.script()->getBreakpointSite(pc);
2028 : else
2029 0 : site = iter.wasmInstance()->debug().getOrCreateBreakpointSite(cx, bytecodeOffset);
2030 : }
2031 : }
2032 :
2033 : // By convention, return the true op to the interpreter in vp, and return
2034 : // undefined in vp to the wasm debug trap.
2035 0 : if (isJS)
2036 0 : vp.setInt32(JSOp(*pc));
2037 : else
2038 0 : vp.set(UndefinedValue());
2039 0 : return JSTRAP_CONTINUE;
2040 : }
2041 :
2042 : /* static */ JSTrapStatus
2043 0 : Debugger::onSingleStep(JSContext* cx, MutableHandleValue vp)
2044 : {
2045 0 : FrameIter iter(cx);
2046 :
2047 : /*
2048 : * We may be stepping over a JSOP_EXCEPTION, that pushes the context's
2049 : * pending exception for a 'catch' clause to handle. Don't let the
2050 : * onStep handlers mess with that (other than by returning a resumption
2051 : * value).
2052 : */
2053 0 : JS::AutoSaveExceptionState saveExc(cx);
2054 :
2055 : /*
2056 : * Build list of Debugger.Frame instances referring to this frame with
2057 : * onStep handlers.
2058 : */
2059 0 : Rooted<DebuggerFrameVector> frames(cx, DebuggerFrameVector(cx));
2060 0 : if (!getDebuggerFrames(iter.abstractFramePtr(), &frames))
2061 0 : return JSTRAP_ERROR;
2062 :
2063 : #ifdef DEBUG
2064 : /*
2065 : * Validate the single-step count on this frame's script, to ensure that
2066 : * we're not receiving traps we didn't ask for. Even when frames is
2067 : * non-empty (and thus we know this trap was requested), do the check
2068 : * anyway, to make sure the count has the correct non-zero value.
2069 : *
2070 : * The converse --- ensuring that we do receive traps when we should --- can
2071 : * be done with unit tests.
2072 : */
2073 0 : if (iter.hasScript()) {
2074 0 : uint32_t stepperCount = 0;
2075 0 : JSScript* trappingScript = iter.script();
2076 0 : GlobalObject* global = cx->global();
2077 0 : if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
2078 0 : for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
2079 0 : Debugger* dbg = *p;
2080 0 : for (FrameMap::Range r = dbg->frames.all(); !r.empty(); r.popFront()) {
2081 0 : AbstractFramePtr frame = r.front().key();
2082 0 : NativeObject* frameobj = r.front().value();
2083 0 : if (frame.isWasmDebugFrame())
2084 0 : continue;
2085 0 : if (frame.script() == trappingScript &&
2086 0 : !frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
2087 : {
2088 0 : stepperCount++;
2089 : }
2090 : }
2091 : }
2092 : }
2093 0 : MOZ_ASSERT(stepperCount == trappingScript->stepModeCount());
2094 : }
2095 : #endif
2096 :
2097 : // Call onStep for frames that have the handler set.
2098 0 : for (size_t i = 0; i < frames.length(); i++) {
2099 0 : HandleDebuggerFrame frame = frames[i];
2100 0 : OnStepHandler* handler = frame->onStepHandler();
2101 0 : if (!handler)
2102 0 : continue;
2103 :
2104 0 : Debugger* dbg = Debugger::fromChildJSObject(frame);
2105 0 : EnterDebuggeeNoExecute nx(cx, *dbg);
2106 :
2107 0 : Maybe<AutoCompartment> ac;
2108 0 : ac.emplace(cx, dbg->object);
2109 :
2110 0 : JSTrapStatus status = JSTRAP_CONTINUE;
2111 0 : bool success = handler->onStep(cx, frame, status, vp);
2112 0 : status = dbg->processParsedHandlerResult(ac, iter.abstractFramePtr(), iter.pc(), success,
2113 : status, vp);
2114 0 : if (status != JSTRAP_CONTINUE)
2115 0 : return status;
2116 : }
2117 :
2118 0 : vp.setUndefined();
2119 0 : return JSTRAP_CONTINUE;
2120 : }
2121 :
2122 : JSTrapStatus
2123 0 : Debugger::fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global, MutableHandleValue vp)
2124 : {
2125 0 : RootedObject hook(cx, getHook(OnNewGlobalObject));
2126 0 : MOZ_ASSERT(hook);
2127 0 : MOZ_ASSERT(hook->isCallable());
2128 :
2129 0 : Maybe<AutoCompartment> ac;
2130 0 : ac.emplace(cx, object);
2131 :
2132 0 : RootedValue wrappedGlobal(cx, ObjectValue(*global));
2133 0 : if (!wrapDebuggeeValue(cx, &wrappedGlobal))
2134 0 : return reportUncaughtException(ac);
2135 :
2136 : // onNewGlobalObject is infallible, and thus is only allowed to return
2137 : // undefined as a resumption value. If it returns anything else, we throw.
2138 : // And if that happens, or if the hook itself throws, we invoke the
2139 : // uncaughtExceptionHook so that we never leave an exception pending on the
2140 : // cx. This allows JS_NewGlobalObject to avoid handling failures from debugger
2141 : // hooks.
2142 0 : RootedValue rv(cx);
2143 0 : RootedValue fval(cx, ObjectValue(*hook));
2144 0 : bool ok = js::Call(cx, fval, object, wrappedGlobal, &rv);
2145 0 : if (ok && !rv.isUndefined()) {
2146 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2147 0 : JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);
2148 0 : ok = false;
2149 : }
2150 : // NB: Even though we don't care about what goes into it, we have to pass vp
2151 : // to handleUncaughtException so that it parses resumption values from the
2152 : // uncaughtExceptionHook and tells the caller whether we should execute the
2153 : // rest of the onNewGlobalObject hooks or not.
2154 0 : JSTrapStatus status = ok ? JSTRAP_CONTINUE
2155 0 : : handleUncaughtException(ac, vp);
2156 0 : MOZ_ASSERT(!cx->isExceptionPending());
2157 0 : return status;
2158 : }
2159 :
2160 : void
2161 0 : Debugger::slowPathOnNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global)
2162 : {
2163 0 : MOZ_ASSERT(!cx->runtime()->onNewGlobalObjectWatchers().isEmpty());
2164 0 : if (global->compartment()->creationOptions().invisibleToDebugger())
2165 0 : return;
2166 :
2167 : /*
2168 : * Make a copy of the runtime's onNewGlobalObjectWatchers before running the
2169 : * handlers. Since one Debugger's handler can disable another's, the list
2170 : * can be mutated while we're walking it.
2171 : */
2172 0 : AutoObjectVector watchers(cx);
2173 0 : for (auto& dbg : cx->runtime()->onNewGlobalObjectWatchers()) {
2174 0 : MOZ_ASSERT(dbg.observesNewGlobalObject());
2175 0 : JSObject* obj = dbg.object;
2176 0 : JS::ExposeObjectToActiveJS(obj);
2177 0 : if (!watchers.append(obj)) {
2178 0 : if (cx->isExceptionPending())
2179 0 : cx->clearPendingException();
2180 0 : return;
2181 : }
2182 : }
2183 :
2184 0 : JSTrapStatus status = JSTRAP_CONTINUE;
2185 0 : RootedValue value(cx);
2186 :
2187 0 : for (size_t i = 0; i < watchers.length(); i++) {
2188 0 : Debugger* dbg = fromJSObject(watchers[i]);
2189 0 : EnterDebuggeeNoExecute nx(cx, *dbg);
2190 :
2191 : // We disallow resumption values from onNewGlobalObject hooks, because we
2192 : // want the debugger hooks for global object creation to be infallible.
2193 : // But if an onNewGlobalObject hook throws, and the uncaughtExceptionHook
2194 : // decides to raise an error, we want to at least avoid invoking the rest
2195 : // of the onNewGlobalObject handlers in the list (not for any super
2196 : // compelling reason, just because it seems like the right thing to do).
2197 : // So we ignore whatever comes out in |value|, but break out of the loop
2198 : // if a non-success trap status is returned.
2199 0 : if (dbg->observesNewGlobalObject()) {
2200 0 : status = dbg->fireNewGlobalObject(cx, global, &value);
2201 0 : if (status != JSTRAP_CONTINUE && status != JSTRAP_RETURN)
2202 0 : break;
2203 : }
2204 : }
2205 0 : MOZ_ASSERT(!cx->isExceptionPending());
2206 : }
2207 :
2208 : /* static */ bool
2209 0 : Debugger::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
2210 : mozilla::TimeStamp when, GlobalObject::DebuggerVector& dbgs)
2211 : {
2212 0 : MOZ_ASSERT(!dbgs.empty());
2213 0 : mozilla::DebugOnly<ReadBarriered<Debugger*>*> begin = dbgs.begin();
2214 :
2215 : // Root all the Debuggers while we're iterating over them;
2216 : // appendAllocationSite calls JSCompartment::wrap, and thus can GC.
2217 : //
2218 : // SpiderMonkey protocol is generally for the caller to prove that it has
2219 : // rooted the stuff it's asking you to operate on (i.e. by passing a
2220 : // Handle), but in this case, we're iterating over a global's list of
2221 : // Debuggers, and globals only hold their Debuggers weakly.
2222 0 : Rooted<GCVector<JSObject*>> activeDebuggers(cx, GCVector<JSObject*>(cx));
2223 0 : for (auto dbgp = dbgs.begin(); dbgp < dbgs.end(); dbgp++) {
2224 0 : if (!activeDebuggers.append((*dbgp)->object))
2225 0 : return false;
2226 : }
2227 :
2228 0 : for (auto dbgp = dbgs.begin(); dbgp < dbgs.end(); dbgp++) {
2229 : // The set of debuggers had better not change while we're iterating,
2230 : // such that the vector gets reallocated.
2231 0 : MOZ_ASSERT(dbgs.begin() == begin);
2232 :
2233 0 : if ((*dbgp)->trackingAllocationSites &&
2234 0 : (*dbgp)->enabled &&
2235 0 : !(*dbgp)->appendAllocationSite(cx, obj, frame, when))
2236 : {
2237 0 : return false;
2238 : }
2239 : }
2240 :
2241 0 : return true;
2242 : }
2243 :
2244 : bool
2245 0 : Debugger::isDebuggeeUnbarriered(const JSCompartment* compartment) const
2246 : {
2247 0 : MOZ_ASSERT(compartment);
2248 0 : return compartment->isDebuggee() && debuggees.has(compartment->unsafeUnbarrieredMaybeGlobal());
2249 : }
2250 :
2251 : bool
2252 0 : Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
2253 : mozilla::TimeStamp when)
2254 : {
2255 0 : MOZ_ASSERT(trackingAllocationSites && enabled);
2256 :
2257 0 : AutoCompartment ac(cx, object);
2258 0 : RootedObject wrappedFrame(cx, frame);
2259 0 : if (!cx->compartment()->wrap(cx, &wrappedFrame))
2260 0 : return false;
2261 :
2262 0 : RootedAtom ctorName(cx);
2263 : {
2264 0 : AutoCompartment ac(cx, obj);
2265 0 : if (!JSObject::constructorDisplayAtom(cx, obj, &ctorName))
2266 0 : return false;
2267 : }
2268 0 : if (ctorName)
2269 0 : cx->markAtom(ctorName);
2270 :
2271 0 : auto className = obj->getClass()->name;
2272 0 : auto size = JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf);
2273 0 : auto inNursery = gc::IsInsideNursery(obj);
2274 :
2275 0 : if (!allocationsLog.emplaceBack(wrappedFrame, when, className, ctorName, size, inNursery)) {
2276 0 : ReportOutOfMemory(cx);
2277 0 : return false;
2278 : }
2279 :
2280 0 : if (allocationsLog.length() > maxAllocationsLogLength) {
2281 0 : if (!allocationsLog.popFront()) {
2282 0 : ReportOutOfMemory(cx);
2283 0 : return false;
2284 : }
2285 0 : MOZ_ASSERT(allocationsLog.length() == maxAllocationsLogLength);
2286 0 : allocationsLogOverflowed = true;
2287 : }
2288 :
2289 0 : return true;
2290 : }
2291 :
2292 : JSTrapStatus
2293 0 : Debugger::firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp)
2294 : {
2295 0 : MOZ_ASSERT(hook == OnNewPromise || hook == OnPromiseSettled);
2296 :
2297 0 : RootedObject hookObj(cx, getHook(hook));
2298 0 : MOZ_ASSERT(hookObj);
2299 0 : MOZ_ASSERT(hookObj->isCallable());
2300 :
2301 0 : Maybe<AutoCompartment> ac;
2302 0 : ac.emplace(cx, object);
2303 :
2304 0 : RootedValue dbgObj(cx, ObjectValue(*promise));
2305 0 : if (!wrapDebuggeeValue(cx, &dbgObj))
2306 0 : return reportUncaughtException(ac);
2307 :
2308 : // Like onNewGlobalObject, the Promise hooks are infallible and the comments
2309 : // in |Debugger::fireNewGlobalObject| apply here as well.
2310 0 : RootedValue fval(cx, ObjectValue(*hookObj));
2311 0 : RootedValue rv(cx);
2312 0 : bool ok = js::Call(cx, fval, object, dbgObj, &rv);
2313 0 : if (ok && !rv.isUndefined()) {
2314 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2315 0 : JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);
2316 0 : ok = false;
2317 : }
2318 :
2319 0 : JSTrapStatus status = ok ? JSTRAP_CONTINUE
2320 0 : : handleUncaughtException(ac, vp);
2321 0 : MOZ_ASSERT(!cx->isExceptionPending());
2322 0 : return status;
2323 : }
2324 :
2325 : /* static */ void
2326 736 : Debugger::slowPathPromiseHook(JSContext* cx, Hook hook, HandleObject promise)
2327 : {
2328 736 : MOZ_ASSERT(hook == OnNewPromise || hook == OnPromiseSettled);
2329 1472 : RootedValue rval(cx);
2330 :
2331 736 : JSTrapStatus status = dispatchHook(
2332 : cx,
2333 0 : [hook](Debugger* dbg) -> bool { return dbg->getHook(hook); },
2334 0 : [&](Debugger* dbg) -> JSTrapStatus {
2335 0 : (void) dbg->firePromiseHook(cx, hook, promise, &rval);
2336 0 : return JSTRAP_CONTINUE;
2337 736 : });
2338 :
2339 736 : if (status == JSTRAP_ERROR) {
2340 : // The dispatch hook function might fail to append into the list of
2341 : // Debuggers which are watching for the hook.
2342 0 : cx->clearPendingException();
2343 0 : return;
2344 : }
2345 :
2346 : // Promise hooks are infallible and we ignore errors from uncaught
2347 : // exceptions by design.
2348 736 : MOZ_ASSERT(status == JSTRAP_CONTINUE);
2349 : }
2350 :
2351 :
2352 : /*** Debugger code invalidation for observing execution ******************************************/
2353 :
2354 0 : class MOZ_RAII ExecutionObservableCompartments : public Debugger::ExecutionObservableSet
2355 : {
2356 : HashSet<JSCompartment*> compartments_;
2357 : HashSet<Zone*> zones_;
2358 :
2359 : public:
2360 0 : explicit ExecutionObservableCompartments(JSContext* cx
2361 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
2362 0 : : compartments_(cx),
2363 0 : zones_(cx)
2364 : {
2365 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
2366 0 : }
2367 :
2368 0 : bool init() { return compartments_.init() && zones_.init(); }
2369 0 : bool add(JSCompartment* comp) { return compartments_.put(comp) && zones_.put(comp->zone()); }
2370 :
2371 : typedef HashSet<JSCompartment*>::Range CompartmentRange;
2372 0 : const HashSet<JSCompartment*>* compartments() const { return &compartments_; }
2373 :
2374 0 : const HashSet<Zone*>* zones() const { return &zones_; }
2375 0 : bool shouldRecompileOrInvalidate(JSScript* script) const {
2376 0 : return script->hasBaselineScript() && compartments_.has(script->compartment());
2377 : }
2378 0 : bool shouldMarkAsDebuggee(FrameIter& iter) const {
2379 : // AbstractFramePtr can't refer to non-remateralized Ion frames or
2380 : // non-debuggee wasm frames, so if iter refers to one such, we know we
2381 : // don't match.
2382 0 : return iter.hasUsableAbstractFramePtr() && compartments_.has(iter.compartment());
2383 : }
2384 :
2385 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
2386 : };
2387 :
2388 : // Given a particular AbstractFramePtr F that has become observable, this
2389 : // represents the stack frames that need to be bailed out or marked as
2390 : // debuggees, and the scripts that need to be recompiled, taking inlining into
2391 : // account.
2392 0 : class MOZ_RAII ExecutionObservableFrame : public Debugger::ExecutionObservableSet
2393 : {
2394 : AbstractFramePtr frame_;
2395 :
2396 : public:
2397 0 : explicit ExecutionObservableFrame(AbstractFramePtr frame
2398 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
2399 0 : : frame_(frame)
2400 : {
2401 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
2402 0 : }
2403 :
2404 0 : Zone* singleZone() const {
2405 : // We never inline across compartments, let alone across zones, so
2406 : // frames_'s script's zone is the only one of interest.
2407 0 : return frame_.script()->compartment()->zone();
2408 : }
2409 :
2410 0 : JSScript* singleScriptForZoneInvalidation() const {
2411 0 : MOZ_CRASH("ExecutionObservableFrame shouldn't need zone-wide invalidation.");
2412 : return nullptr;
2413 : }
2414 :
2415 0 : bool shouldRecompileOrInvalidate(JSScript* script) const {
2416 : // Normally, *this represents exactly one script: the one frame_ is
2417 : // running.
2418 : //
2419 : // However, debug-mode OSR uses *this for both invalidating Ion frames,
2420 : // and recompiling the Baseline scripts that those Ion frames will bail
2421 : // out into. Suppose frame_ is an inline frame, executing a copy of its
2422 : // JSScript, S_inner, that has been inlined into the IonScript of some
2423 : // other JSScript, S_outer. We must match S_outer, to decide which Ion
2424 : // frame to invalidate; and we must match S_inner, to decide which
2425 : // Baseline script to recompile.
2426 : //
2427 : // Note that this does not, by design, invalidate *all* inliners of
2428 : // frame_.script(), as only frame_ is made observable, not
2429 : // frame_.script().
2430 0 : if (!script->hasBaselineScript())
2431 0 : return false;
2432 :
2433 0 : if (frame_.hasScript() && script == frame_.script())
2434 0 : return true;
2435 :
2436 0 : return frame_.isRematerializedFrame() &&
2437 0 : script == frame_.asRematerializedFrame()->outerScript();
2438 : }
2439 :
2440 0 : bool shouldMarkAsDebuggee(FrameIter& iter) const {
2441 : // AbstractFramePtr can't refer to non-remateralized Ion frames or
2442 : // non-debuggee wasm frames, so if iter refers to one such, we know we
2443 : // don't match.
2444 : //
2445 : // We never use this 'has' overload for frame invalidation, only for
2446 : // frame debuggee marking; so this overload doesn't need a parallel to
2447 : // the just-so inlining logic above.
2448 0 : return iter.hasUsableAbstractFramePtr() && iter.abstractFramePtr() == frame_;
2449 : }
2450 :
2451 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
2452 : };
2453 :
2454 0 : class MOZ_RAII ExecutionObservableScript : public Debugger::ExecutionObservableSet
2455 : {
2456 : RootedScript script_;
2457 :
2458 : public:
2459 0 : ExecutionObservableScript(JSContext* cx, JSScript* script
2460 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
2461 0 : : script_(cx, script)
2462 : {
2463 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
2464 0 : }
2465 :
2466 0 : Zone* singleZone() const { return script_->compartment()->zone(); }
2467 0 : JSScript* singleScriptForZoneInvalidation() const { return script_; }
2468 0 : bool shouldRecompileOrInvalidate(JSScript* script) const {
2469 0 : return script->hasBaselineScript() && script == script_;
2470 : }
2471 0 : bool shouldMarkAsDebuggee(FrameIter& iter) const {
2472 : // AbstractFramePtr can't refer to non-remateralized Ion frames, and
2473 : // while a non-rematerialized Ion frame may indeed be running script_,
2474 : // we cannot mark them as debuggees until they bail out.
2475 : //
2476 : // Upon bailing out, any newly constructed Baseline frames that came
2477 : // from Ion frames with scripts that are isDebuggee() is marked as
2478 : // debuggee. This is correct in that the only other way a frame may be
2479 : // marked as debuggee is via Debugger.Frame reflection, which would
2480 : // have rematerialized any Ion frames.
2481 : //
2482 : // Also AbstractFramePtr can't refer to non-debuggee wasm frames, so if
2483 : // iter refers to one such, we know we don't match.
2484 0 : return iter.hasUsableAbstractFramePtr() && !iter.isWasm() &&
2485 0 : iter.abstractFramePtr().script() == script_;
2486 : }
2487 :
2488 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
2489 : };
2490 :
2491 : /* static */ bool
2492 0 : Debugger::updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObservableSet& obs,
2493 : IsObserving observing)
2494 : {
2495 0 : AutoSuppressProfilerSampling suppressProfilerSampling(cx);
2496 :
2497 : {
2498 0 : jit::JitContext jctx(cx, nullptr);
2499 0 : if (!jit::RecompileOnStackBaselineScriptsForDebugMode(cx, obs, observing)) {
2500 0 : ReportOutOfMemory(cx);
2501 0 : return false;
2502 : }
2503 : }
2504 :
2505 0 : AbstractFramePtr oldestEnabledFrame;
2506 0 : for (FrameIter iter(cx);
2507 0 : !iter.done();
2508 : ++iter)
2509 : {
2510 0 : if (obs.shouldMarkAsDebuggee(iter)) {
2511 0 : if (observing) {
2512 0 : if (!iter.abstractFramePtr().isDebuggee()) {
2513 0 : oldestEnabledFrame = iter.abstractFramePtr();
2514 0 : oldestEnabledFrame.setIsDebuggee();
2515 : }
2516 0 : if (iter.abstractFramePtr().isWasmDebugFrame())
2517 0 : iter.abstractFramePtr().asWasmDebugFrame()->observe(cx);
2518 : } else {
2519 : #ifdef DEBUG
2520 : // Debugger.Frame lifetimes are managed by the debug epilogue,
2521 : // so in general it's unsafe to unmark a frame if it has a
2522 : // Debugger.Frame associated with it.
2523 0 : MOZ_ASSERT(!inFrameMaps(iter.abstractFramePtr()));
2524 : #endif
2525 0 : iter.abstractFramePtr().unsetIsDebuggee();
2526 : }
2527 : }
2528 : }
2529 :
2530 : // See comment in unsetPrevUpToDateUntil.
2531 0 : if (oldestEnabledFrame) {
2532 0 : AutoCompartment ac(cx, oldestEnabledFrame.environmentChain());
2533 0 : DebugEnvironments::unsetPrevUpToDateUntil(cx, oldestEnabledFrame);
2534 : }
2535 :
2536 0 : return true;
2537 : }
2538 :
2539 : static inline void
2540 0 : MarkBaselineScriptActiveIfObservable(JSScript* script, const Debugger::ExecutionObservableSet& obs)
2541 : {
2542 0 : if (obs.shouldRecompileOrInvalidate(script))
2543 0 : script->baselineScript()->setActive();
2544 0 : }
2545 :
2546 : static bool
2547 0 : AppendAndInvalidateScript(JSContext* cx, Zone* zone, JSScript* script, Vector<JSScript*>& scripts)
2548 : {
2549 : // Enter the script's compartment as addPendingRecompile attempts to
2550 : // cancel off-thread compilations, whose books are kept on the
2551 : // script's compartment.
2552 0 : MOZ_ASSERT(script->compartment()->zone() == zone);
2553 0 : AutoCompartment ac(cx, script);
2554 0 : zone->types.addPendingRecompile(cx, script);
2555 0 : return scripts.append(script);
2556 : }
2557 :
2558 : static bool
2559 0 : UpdateExecutionObservabilityOfScriptsInZone(JSContext* cx, Zone* zone,
2560 : const Debugger::ExecutionObservableSet& obs,
2561 : Debugger::IsObserving observing)
2562 : {
2563 : using namespace js::jit;
2564 :
2565 0 : AutoSuppressProfilerSampling suppressProfilerSampling(cx);
2566 :
2567 0 : FreeOp* fop = cx->runtime()->defaultFreeOp();
2568 :
2569 0 : Vector<JSScript*> scripts(cx);
2570 :
2571 : // Iterate through observable scripts, invalidating their Ion scripts and
2572 : // appending them to a vector for discarding their baseline scripts later.
2573 : {
2574 0 : AutoEnterAnalysis enter(fop, zone);
2575 0 : if (JSScript* script = obs.singleScriptForZoneInvalidation()) {
2576 0 : if (obs.shouldRecompileOrInvalidate(script)) {
2577 0 : if (!AppendAndInvalidateScript(cx, zone, script, scripts))
2578 0 : return false;
2579 : }
2580 : } else {
2581 0 : for (auto iter = zone->cellIter<JSScript>(); !iter.done(); iter.next()) {
2582 0 : JSScript* script = iter;
2583 0 : if (obs.shouldRecompileOrInvalidate(script) &&
2584 0 : !gc::IsAboutToBeFinalizedUnbarriered(&script))
2585 : {
2586 0 : if (!AppendAndInvalidateScript(cx, zone, script, scripts))
2587 0 : return false;
2588 : }
2589 : }
2590 : }
2591 : }
2592 :
2593 : // Code below this point must be infallible to ensure the active bit of
2594 : // BaselineScripts is in a consistent state.
2595 : //
2596 : // Mark active baseline scripts in the observable set so that they don't
2597 : // get discarded. They will be recompiled.
2598 0 : for (JitActivationIterator actIter(cx, zone->group()->ownerContext()); !actIter.done(); ++actIter) {
2599 0 : if (actIter->compartment()->zone() != zone)
2600 0 : continue;
2601 :
2602 0 : for (JitFrameIterator iter(actIter); !iter.done(); ++iter) {
2603 0 : switch (iter.type()) {
2604 : case JitFrame_BaselineJS:
2605 0 : MarkBaselineScriptActiveIfObservable(iter.script(), obs);
2606 0 : break;
2607 : case JitFrame_IonJS:
2608 0 : MarkBaselineScriptActiveIfObservable(iter.script(), obs);
2609 0 : for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter)
2610 0 : MarkBaselineScriptActiveIfObservable(inlineIter.script(), obs);
2611 0 : break;
2612 : default:;
2613 : }
2614 : }
2615 : }
2616 :
2617 : // Iterate through the scripts again and finish discarding
2618 : // BaselineScripts. This must be done as a separate phase as we can only
2619 : // discard the BaselineScript on scripts that have no IonScript.
2620 0 : for (size_t i = 0; i < scripts.length(); i++) {
2621 0 : MOZ_ASSERT_IF(scripts[i]->isDebuggee(), observing);
2622 0 : FinishDiscardBaselineScript(fop, scripts[i]);
2623 : }
2624 :
2625 : // Iterate through all wasm instances to find ones that need to be updated.
2626 0 : for (JSCompartment* c : zone->compartments()) {
2627 0 : for (wasm::Instance* instance : c->wasm.instances()) {
2628 0 : if (!instance->debugEnabled())
2629 0 : continue;
2630 :
2631 0 : bool enableTrap = observing == Debugger::IsObserving::Observing;
2632 0 : instance->ensureEnterFrameTrapsState(cx, enableTrap);
2633 : }
2634 : }
2635 :
2636 0 : return true;
2637 : }
2638 :
2639 : /* static */ bool
2640 0 : Debugger::updateExecutionObservabilityOfScripts(JSContext* cx, const ExecutionObservableSet& obs,
2641 : IsObserving observing)
2642 : {
2643 0 : if (Zone* zone = obs.singleZone())
2644 0 : return UpdateExecutionObservabilityOfScriptsInZone(cx, zone, obs, observing);
2645 :
2646 : typedef ExecutionObservableSet::ZoneRange ZoneRange;
2647 0 : for (ZoneRange r = obs.zones()->all(); !r.empty(); r.popFront()) {
2648 0 : if (!UpdateExecutionObservabilityOfScriptsInZone(cx, r.front(), obs, observing))
2649 0 : return false;
2650 : }
2651 :
2652 0 : return true;
2653 : }
2654 :
2655 : template <typename FrameFn>
2656 : /* static */ void
2657 12315 : Debugger::forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn)
2658 : {
2659 12315 : GlobalObject* global = frame.global();
2660 12315 : if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
2661 0 : for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
2662 0 : Debugger* dbg = *p;
2663 0 : if (FrameMap::Ptr entry = dbg->frames.lookup(frame))
2664 0 : fn(entry->value());
2665 : }
2666 : }
2667 12315 : }
2668 :
2669 : /* static */ bool
2670 0 : Debugger::getDebuggerFrames(AbstractFramePtr frame, MutableHandle<DebuggerFrameVector> frames)
2671 : {
2672 0 : bool hadOOM = false;
2673 0 : forEachDebuggerFrame(frame, [&](DebuggerFrame* frameobj) {
2674 0 : if (!hadOOM && !frames.append(frameobj))
2675 0 : hadOOM = true;
2676 0 : });
2677 0 : return !hadOOM;
2678 : }
2679 :
2680 : /* static */ bool
2681 0 : Debugger::updateExecutionObservability(JSContext* cx, ExecutionObservableSet& obs,
2682 : IsObserving observing)
2683 : {
2684 0 : if (!obs.singleZone() && obs.zones()->empty())
2685 0 : return true;
2686 :
2687 : // Invalidate scripts first so we can set the needsArgsObj flag on scripts
2688 : // before patching frames.
2689 0 : return updateExecutionObservabilityOfScripts(cx, obs, observing) &&
2690 0 : updateExecutionObservabilityOfFrames(cx, obs, observing);
2691 : }
2692 :
2693 : /* static */ bool
2694 0 : Debugger::ensureExecutionObservabilityOfScript(JSContext* cx, JSScript* script)
2695 : {
2696 0 : if (script->isDebuggee())
2697 0 : return true;
2698 0 : ExecutionObservableScript obs(cx, script);
2699 0 : return updateExecutionObservability(cx, obs, Observing);
2700 : }
2701 :
2702 : /* static */ bool
2703 0 : Debugger::ensureExecutionObservabilityOfOsrFrame(JSContext* cx, InterpreterFrame* frame)
2704 : {
2705 0 : MOZ_ASSERT(frame->isDebuggee());
2706 0 : if (frame->script()->hasBaselineScript() &&
2707 0 : frame->script()->baselineScript()->hasDebugInstrumentation())
2708 : {
2709 0 : return true;
2710 : }
2711 0 : ExecutionObservableFrame obs(frame);
2712 0 : return updateExecutionObservabilityOfFrames(cx, obs, Observing);
2713 : }
2714 :
2715 : /* static */ bool
2716 0 : Debugger::ensureExecutionObservabilityOfFrame(JSContext* cx, AbstractFramePtr frame)
2717 : {
2718 0 : MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(), frame.isDebuggee());
2719 0 : MOZ_ASSERT_IF(frame.isWasmDebugFrame(), frame.wasmInstance()->debugEnabled());
2720 0 : if (frame.isDebuggee())
2721 0 : return true;
2722 0 : ExecutionObservableFrame obs(frame);
2723 0 : return updateExecutionObservabilityOfFrames(cx, obs, Observing);
2724 : }
2725 :
2726 : /* static */ bool
2727 0 : Debugger::ensureExecutionObservabilityOfCompartment(JSContext* cx, JSCompartment* comp)
2728 : {
2729 0 : if (comp->debuggerObservesAllExecution())
2730 0 : return true;
2731 0 : ExecutionObservableCompartments obs(cx);
2732 0 : if (!obs.init() || !obs.add(comp))
2733 0 : return false;
2734 0 : comp->updateDebuggerObservesAllExecution();
2735 0 : return updateExecutionObservability(cx, obs, Observing);
2736 : }
2737 :
2738 : /* static */ bool
2739 0 : Debugger::hookObservesAllExecution(Hook which)
2740 : {
2741 0 : return which == OnEnterFrame;
2742 : }
2743 :
2744 : Debugger::IsObserving
2745 0 : Debugger::observesAllExecution() const
2746 : {
2747 0 : if (enabled && !!getHook(OnEnterFrame))
2748 0 : return Observing;
2749 0 : return NotObserving;
2750 : }
2751 :
2752 : Debugger::IsObserving
2753 0 : Debugger::observesAsmJS() const
2754 : {
2755 0 : if (enabled && !allowUnobservedAsmJS)
2756 0 : return Observing;
2757 0 : return NotObserving;
2758 : }
2759 :
2760 : Debugger::IsObserving
2761 0 : Debugger::observesBinarySource() const
2762 : {
2763 0 : if (enabled && allowWasmBinarySource)
2764 0 : return Observing;
2765 0 : return NotObserving;
2766 : }
2767 :
2768 : Debugger::IsObserving
2769 0 : Debugger::observesCoverage() const
2770 : {
2771 0 : if (enabled && collectCoverageInfo)
2772 0 : return Observing;
2773 0 : return NotObserving;
2774 : }
2775 :
2776 : // Toggle whether this Debugger's debuggees observe all execution. This is
2777 : // called when a hook that observes all execution is set or unset. See
2778 : // hookObservesAllExecution.
2779 : bool
2780 0 : Debugger::updateObservesAllExecutionOnDebuggees(JSContext* cx, IsObserving observing)
2781 : {
2782 0 : ExecutionObservableCompartments obs(cx);
2783 0 : if (!obs.init())
2784 0 : return false;
2785 :
2786 0 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2787 0 : GlobalObject* global = r.front();
2788 0 : JSCompartment* comp = global->compartment();
2789 :
2790 0 : if (comp->debuggerObservesAllExecution() == observing)
2791 0 : continue;
2792 :
2793 : // It's expensive to eagerly invalidate and recompile a compartment,
2794 : // so add the compartment to the set only if we are observing.
2795 0 : if (observing && !obs.add(comp))
2796 0 : return false;
2797 : }
2798 :
2799 0 : if (!updateExecutionObservability(cx, obs, observing))
2800 0 : return false;
2801 :
2802 : typedef ExecutionObservableCompartments::CompartmentRange CompartmentRange;
2803 0 : for (CompartmentRange r = obs.compartments()->all(); !r.empty(); r.popFront())
2804 0 : r.front()->updateDebuggerObservesAllExecution();
2805 :
2806 0 : return true;
2807 : }
2808 :
2809 : bool
2810 0 : Debugger::updateObservesCoverageOnDebuggees(JSContext* cx, IsObserving observing)
2811 : {
2812 0 : ExecutionObservableCompartments obs(cx);
2813 0 : if (!obs.init())
2814 0 : return false;
2815 :
2816 0 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2817 0 : GlobalObject* global = r.front();
2818 0 : JSCompartment* comp = global->compartment();
2819 :
2820 0 : if (comp->debuggerObservesCoverage() == observing)
2821 0 : continue;
2822 :
2823 : // Invalidate and recompile a compartment to add or remove PCCounts
2824 : // increments. We have to eagerly invalidate, as otherwise we might have
2825 : // dangling pointers to freed PCCounts.
2826 0 : if (!obs.add(comp))
2827 0 : return false;
2828 : }
2829 :
2830 : // If any frame on the stack belongs to the debuggee, then we cannot update
2831 : // the ScriptCounts, because this would imply to invalidate a Debugger.Frame
2832 : // to recompile it with/without ScriptCount support.
2833 0 : for (FrameIter iter(cx);
2834 0 : !iter.done();
2835 : ++iter)
2836 : {
2837 0 : if (obs.shouldMarkAsDebuggee(iter)) {
2838 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_IDLE);
2839 0 : return false;
2840 : }
2841 : }
2842 :
2843 0 : if (!updateExecutionObservability(cx, obs, observing))
2844 0 : return false;
2845 :
2846 : // All compartments can safely be toggled, and all scripts will be
2847 : // recompiled. Thus we can update each compartment accordingly.
2848 : typedef ExecutionObservableCompartments::CompartmentRange CompartmentRange;
2849 0 : for (CompartmentRange r = obs.compartments()->all(); !r.empty(); r.popFront())
2850 0 : r.front()->updateDebuggerObservesCoverage();
2851 :
2852 0 : return true;
2853 : }
2854 :
2855 : void
2856 0 : Debugger::updateObservesAsmJSOnDebuggees(IsObserving observing)
2857 : {
2858 0 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2859 0 : GlobalObject* global = r.front();
2860 0 : JSCompartment* comp = global->compartment();
2861 :
2862 0 : if (comp->debuggerObservesAsmJS() == observing)
2863 0 : continue;
2864 :
2865 0 : comp->updateDebuggerObservesAsmJS();
2866 : }
2867 0 : }
2868 :
2869 : void
2870 0 : Debugger::updateObservesBinarySourceDebuggees(IsObserving observing)
2871 : {
2872 0 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2873 0 : GlobalObject* global = r.front();
2874 0 : JSCompartment* comp = global->compartment();
2875 :
2876 0 : if (comp->debuggerObservesBinarySource() == observing)
2877 0 : continue;
2878 :
2879 0 : comp->updateDebuggerObservesBinarySource();
2880 : }
2881 0 : }
2882 :
2883 :
2884 : /*** Allocations Tracking *************************************************************************/
2885 :
2886 : /* static */ bool
2887 0 : Debugger::cannotTrackAllocations(const GlobalObject& global)
2888 : {
2889 0 : auto existingCallback = global.compartment()->getAllocationMetadataBuilder();
2890 0 : return existingCallback && existingCallback != &SavedStacks::metadataBuilder;
2891 : }
2892 :
2893 : /* static */ bool
2894 0 : Debugger::isObservedByDebuggerTrackingAllocations(const GlobalObject& debuggee)
2895 : {
2896 0 : if (auto* v = debuggee.getDebuggers()) {
2897 0 : for (auto p = v->begin(); p != v->end(); p++) {
2898 0 : if ((*p)->trackingAllocationSites && (*p)->enabled) {
2899 0 : return true;
2900 : }
2901 : }
2902 : }
2903 :
2904 0 : return false;
2905 : }
2906 :
2907 : /* static */ bool
2908 0 : Debugger::addAllocationsTracking(JSContext* cx, Handle<GlobalObject*> debuggee)
2909 : {
2910 : // Precondition: the given global object is being observed by at least one
2911 : // Debugger that is tracking allocations.
2912 0 : MOZ_ASSERT(isObservedByDebuggerTrackingAllocations(*debuggee));
2913 :
2914 0 : if (Debugger::cannotTrackAllocations(*debuggee)) {
2915 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2916 0 : JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
2917 0 : return false;
2918 : }
2919 :
2920 0 : debuggee->compartment()->setAllocationMetadataBuilder(&SavedStacks::metadataBuilder);
2921 0 : debuggee->compartment()->chooseAllocationSamplingProbability();
2922 0 : return true;
2923 : }
2924 :
2925 : /* static */ void
2926 0 : Debugger::removeAllocationsTracking(GlobalObject& global)
2927 : {
2928 : // If there are still Debuggers that are observing allocations, we cannot
2929 : // remove the metadata callback yet. Recompute the sampling probability
2930 : // based on the remaining debuggers' needs.
2931 0 : if (isObservedByDebuggerTrackingAllocations(global)) {
2932 0 : global.compartment()->chooseAllocationSamplingProbability();
2933 0 : return;
2934 : }
2935 :
2936 0 : global.compartment()->forgetAllocationMetadataBuilder();
2937 : }
2938 :
2939 : bool
2940 0 : Debugger::addAllocationsTrackingForAllDebuggees(JSContext* cx)
2941 : {
2942 0 : MOZ_ASSERT(trackingAllocationSites);
2943 :
2944 : // We don't want to end up in a state where we added allocations
2945 : // tracking to some of our debuggees, but failed to do so for
2946 : // others. Before attempting to start tracking allocations in *any* of
2947 : // our debuggees, ensure that we will be able to track allocations for
2948 : // *all* of our debuggees.
2949 0 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2950 0 : if (Debugger::cannotTrackAllocations(*r.front().get())) {
2951 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2952 0 : JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
2953 0 : return false;
2954 : }
2955 : }
2956 :
2957 0 : Rooted<GlobalObject*> g(cx);
2958 0 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
2959 : // This should always succeed, since we already checked for the
2960 : // error case above.
2961 0 : g = r.front().get();
2962 0 : MOZ_ALWAYS_TRUE(Debugger::addAllocationsTracking(cx, g));
2963 : }
2964 :
2965 0 : return true;
2966 : }
2967 :
2968 : void
2969 0 : Debugger::removeAllocationsTrackingForAllDebuggees()
2970 : {
2971 0 : for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront())
2972 0 : Debugger::removeAllocationsTracking(*r.front().get());
2973 :
2974 0 : allocationsLog.clear();
2975 0 : }
2976 :
2977 :
2978 :
2979 : /*** Debugger JSObjects **************************************************************************/
2980 :
2981 : void
2982 0 : Debugger::traceCrossCompartmentEdges(JSTracer* trc)
2983 : {
2984 0 : objects.traceCrossCompartmentEdges<DebuggerObject_trace>(trc);
2985 0 : environments.traceCrossCompartmentEdges<DebuggerEnv_trace>(trc);
2986 0 : scripts.traceCrossCompartmentEdges<DebuggerScript_trace>(trc);
2987 0 : sources.traceCrossCompartmentEdges<DebuggerSource_trace>(trc);
2988 0 : wasmInstanceScripts.traceCrossCompartmentEdges<DebuggerScript_trace>(trc);
2989 0 : wasmInstanceSources.traceCrossCompartmentEdges<DebuggerSource_trace>(trc);
2990 0 : }
2991 :
2992 : /*
2993 : * Ordinarily, WeakMap keys and values are marked because at some point it was
2994 : * discovered that the WeakMap was live; that is, some object containing the
2995 : * WeakMap was marked during mark phase.
2996 : *
2997 : * However, during zone GC, we have to do something about cross-compartment
2998 : * edges in non-GC'd compartments. Since the source may be live, we
2999 : * conservatively assume it is and mark the edge.
3000 : *
3001 : * Each Debugger object keeps four cross-compartment WeakMaps: objects, scripts,
3002 : * script source objects, and environments. They have the property that all
3003 : * their values are in the same compartment as the Debugger object, but we have
3004 : * to mark the keys and the private pointer in the wrapper object.
3005 : *
3006 : * We must scan all Debugger objects regardless of whether they *currently* have
3007 : * any debuggees in a compartment being GC'd, because the WeakMap entries
3008 : * persist even when debuggees are removed.
3009 : *
3010 : * This happens during the initial mark phase, not iterative marking, because
3011 : * all the edges being reported here are strong references.
3012 : *
3013 : * This method is also used during compacting GC to update cross compartment
3014 : * pointers into zones that are being compacted.
3015 : */
3016 : /* static */ void
3017 1 : Debugger::traceIncomingCrossCompartmentEdges(JSTracer* trc)
3018 : {
3019 1 : JSRuntime* rt = trc->runtime();
3020 1 : gc::State state = rt->gc.state();
3021 1 : MOZ_ASSERT(state == gc::State::MarkRoots || state == gc::State::Compact);
3022 :
3023 12 : for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
3024 11 : for (Debugger* dbg : group->debuggerList()) {
3025 0 : Zone* zone = MaybeForwarded(dbg->object.get())->zone();
3026 0 : if (!zone->isCollecting() || state == gc::State::Compact)
3027 0 : dbg->traceCrossCompartmentEdges(trc);
3028 : }
3029 : }
3030 1 : }
3031 :
3032 : /*
3033 : * This method has two tasks:
3034 : * 1. Mark Debugger objects that are unreachable except for debugger hooks that
3035 : * may yet be called.
3036 : * 2. Mark breakpoint handlers.
3037 : *
3038 : * This happens during the iterative part of the GC mark phase. This method
3039 : * returns true if it has to mark anything; GC calls it repeatedly until it
3040 : * returns false.
3041 : */
3042 : /* static */ bool
3043 0 : Debugger::markIteratively(GCMarker* marker)
3044 : {
3045 0 : bool markedAny = false;
3046 :
3047 : /*
3048 : * Find all Debugger objects in danger of GC. This code is a little
3049 : * convoluted since the easiest way to find them is via their debuggees.
3050 : */
3051 0 : JSRuntime* rt = marker->runtime();
3052 0 : for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
3053 0 : if (c->isDebuggee()) {
3054 0 : GlobalObject* global = c->unsafeUnbarrieredMaybeGlobal();
3055 0 : if (!IsMarkedUnbarriered(rt, &global))
3056 0 : continue;
3057 :
3058 : /*
3059 : * Every debuggee has at least one debugger, so in this case
3060 : * getDebuggers can't return nullptr.
3061 : */
3062 0 : const GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
3063 0 : MOZ_ASSERT(debuggers);
3064 0 : for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
3065 0 : Debugger* dbg = *p;
3066 :
3067 : /*
3068 : * dbg is a Debugger with at least one debuggee. Check three things:
3069 : * - dbg is actually in a compartment that is being marked
3070 : * - it isn't already marked
3071 : * - it actually has hooks that might be called
3072 : */
3073 0 : GCPtrNativeObject& dbgobj = dbg->toJSObjectRef();
3074 0 : if (!dbgobj->zone()->isGCMarking())
3075 0 : continue;
3076 :
3077 0 : bool dbgMarked = IsMarked(rt, &dbgobj);
3078 0 : if (!dbgMarked && dbg->hasAnyLiveHooks(rt)) {
3079 : /*
3080 : * obj could be reachable only via its live, enabled
3081 : * debugger hooks, which may yet be called.
3082 : */
3083 0 : TraceEdge(marker, &dbgobj, "enabled Debugger");
3084 0 : markedAny = true;
3085 0 : dbgMarked = true;
3086 : }
3087 :
3088 0 : if (dbgMarked) {
3089 : /* Search for breakpoints to mark. */
3090 0 : for (Breakpoint* bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
3091 0 : switch (bp->site->type()) {
3092 : case BreakpointSite::Type::JS:
3093 0 : if (IsMarkedUnbarriered(rt, &bp->site->asJS()->script)) {
3094 : /*
3095 : * The debugger and the script are both live.
3096 : * Therefore the breakpoint handler is live.
3097 : */
3098 0 : if (!IsMarked(rt, &bp->getHandlerRef())) {
3099 0 : TraceEdge(marker, &bp->getHandlerRef(), "breakpoint handler");
3100 0 : markedAny = true;
3101 : }
3102 : }
3103 0 : break;
3104 : case BreakpointSite::Type::Wasm:
3105 0 : if (IsMarkedUnbarriered(rt, &bp->asWasm()->wasmInstance)) {
3106 : /*
3107 : * The debugger and the wasm instance are both live.
3108 : * Therefore the breakpoint handler is live.
3109 : */
3110 0 : if (!IsMarked(rt, &bp->getHandlerRef())) {
3111 0 : TraceEdge(marker, &bp->getHandlerRef(), "wasm breakpoint handler");
3112 0 : markedAny = true;
3113 : }
3114 : }
3115 0 : break;
3116 : }
3117 : }
3118 : }
3119 : }
3120 : }
3121 : }
3122 0 : return markedAny;
3123 : }
3124 :
3125 : /* static */ void
3126 21 : Debugger::traceAllForMovingGC(JSTracer* trc)
3127 : {
3128 21 : JSRuntime* rt = trc->runtime();
3129 185 : for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
3130 164 : for (Debugger* dbg : group->debuggerList())
3131 0 : dbg->traceForMovingGC(trc);
3132 : }
3133 21 : }
3134 :
3135 : /*
3136 : * Trace all debugger-owned GC things unconditionally. This is used during
3137 : * compacting GC and in minor GC: the minor GC cannot apply the weak constraints
3138 : * of the full GC because it visits only part of the heap.
3139 : */
3140 : void
3141 0 : Debugger::traceForMovingGC(JSTracer* trc)
3142 : {
3143 0 : trace(trc);
3144 :
3145 0 : for (WeakGlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront())
3146 0 : TraceManuallyBarrieredEdge(trc, e.mutableFront().unsafeGet(), "Global Object");
3147 :
3148 0 : for (Breakpoint* bp = firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
3149 0 : switch (bp->site->type()) {
3150 : case BreakpointSite::Type::JS:
3151 0 : TraceManuallyBarrieredEdge(trc, &bp->site->asJS()->script,
3152 0 : "breakpoint script");
3153 0 : break;
3154 : case BreakpointSite::Type::Wasm:
3155 0 : TraceManuallyBarrieredEdge(trc, &bp->asWasm()->wasmInstance, "breakpoint wasm instance");
3156 0 : break;
3157 : }
3158 0 : TraceEdge(trc, &bp->getHandlerRef(), "breakpoint handler");
3159 : }
3160 0 : }
3161 :
3162 : /* static */ void
3163 0 : Debugger::traceObject(JSTracer* trc, JSObject* obj)
3164 : {
3165 0 : if (Debugger* dbg = Debugger::fromJSObject(obj))
3166 0 : dbg->trace(trc);
3167 0 : }
3168 :
3169 : void
3170 0 : Debugger::trace(JSTracer* trc)
3171 : {
3172 0 : TraceEdge(trc, &object, "Debugger Object");
3173 :
3174 0 : TraceNullableEdge(trc, &uncaughtExceptionHook, "hooks");
3175 :
3176 : /*
3177 : * Mark Debugger.Frame objects. These are all reachable from JS, because the
3178 : * corresponding JS frames are still on the stack.
3179 : *
3180 : * (Once we support generator frames properly, we will need
3181 : * weakly-referenced Debugger.Frame objects as well, for suspended generator
3182 : * frames.)
3183 : */
3184 0 : if (frames.initialized()) {
3185 0 : for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
3186 0 : HeapPtr<DebuggerFrame*>& frameobj = r.front().value();
3187 0 : MOZ_ASSERT(MaybeForwarded(frameobj.get())->getPrivate());
3188 0 : TraceEdge(trc, &frameobj, "live Debugger.Frame");
3189 : }
3190 : }
3191 :
3192 0 : allocationsLog.trace(trc);
3193 :
3194 : /* Trace the weak map from JSScript instances to Debugger.Script objects. */
3195 0 : scripts.trace(trc);
3196 :
3197 : /* Trace the referent -> Debugger.Source weak map */
3198 0 : sources.trace(trc);
3199 :
3200 : /* Trace the referent -> Debugger.Object weak map. */
3201 0 : objects.trace(trc);
3202 :
3203 : /* Trace the referent -> Debugger.Environment weak map. */
3204 0 : environments.trace(trc);
3205 :
3206 : /* Trace the WasmInstanceObject -> synthesized Debugger.Script weak map. */
3207 0 : wasmInstanceScripts.trace(trc);
3208 :
3209 : /* Trace the WasmInstanceObject -> synthesized Debugger.Source weak map. */
3210 0 : wasmInstanceSources.trace(trc);
3211 0 : }
3212 :
3213 : /* static */ void
3214 0 : Debugger::sweepAll(FreeOp* fop)
3215 : {
3216 0 : JSRuntime* rt = fop->runtime();
3217 :
3218 0 : for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
3219 0 : Debugger* dbg = group->debuggerList().getFirst();
3220 0 : while (dbg) {
3221 0 : Debugger* next = dbg->getNext();
3222 :
3223 : // Detach dying debuggers and debuggees from each other. Since this
3224 : // requires access to both objects it must be done before either
3225 : // object is finalized.
3226 0 : bool debuggerDying = IsAboutToBeFinalized(&dbg->object);
3227 0 : for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
3228 0 : GlobalObject* global = e.front().unbarrieredGet();
3229 0 : if (debuggerDying || IsAboutToBeFinalizedUnbarriered(&global))
3230 0 : dbg->removeDebuggeeGlobal(fop, e.front().unbarrieredGet(), &e);
3231 : }
3232 :
3233 0 : if (debuggerDying)
3234 0 : fop->delete_(dbg);
3235 :
3236 0 : dbg = next;
3237 : }
3238 : }
3239 0 : }
3240 :
3241 : /* static */ void
3242 0 : Debugger::detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global)
3243 : {
3244 0 : const GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
3245 0 : MOZ_ASSERT(!debuggers->empty());
3246 0 : while (!debuggers->empty())
3247 0 : debuggers->back()->removeDebuggeeGlobal(fop, global, nullptr);
3248 0 : }
3249 :
3250 : /* static */ void
3251 0 : Debugger::findZoneEdges(Zone* zone, js::gc::ZoneComponentFinder& finder)
3252 : {
3253 : /*
3254 : * For debugger cross compartment wrappers, add edges in the opposite
3255 : * direction to those already added by JSCompartment::findOutgoingEdges.
3256 : * This ensure that debuggers and their debuggees are finalized in the same
3257 : * group.
3258 : */
3259 0 : for (ZoneGroupsIter group(zone->runtimeFromActiveCooperatingThread()); !group.done(); group.next()) {
3260 0 : for (Debugger* dbg : group->debuggerList()) {
3261 0 : Zone* w = dbg->object->zone();
3262 0 : if (w == zone || !w->isGCMarking())
3263 0 : continue;
3264 0 : if (dbg->debuggeeZones.has(zone) ||
3265 0 : dbg->scripts.hasKeyInZone(zone) ||
3266 0 : dbg->sources.hasKeyInZone(zone) ||
3267 0 : dbg->objects.hasKeyInZone(zone) ||
3268 0 : dbg->environments.hasKeyInZone(zone) ||
3269 0 : dbg->wasmInstanceScripts.hasKeyInZone(zone) ||
3270 0 : dbg->wasmInstanceSources.hasKeyInZone(zone))
3271 : {
3272 0 : finder.addEdgeTo(w);
3273 : }
3274 : }
3275 : }
3276 0 : }
3277 :
3278 : const ClassOps Debugger::classOps_ = {
3279 : nullptr, /* addProperty */
3280 : nullptr, /* delProperty */
3281 : nullptr, /* getProperty */
3282 : nullptr, /* setProperty */
3283 : nullptr, /* enumerate */
3284 : nullptr, /* newEnumerate */
3285 : nullptr, /* resolve */
3286 : nullptr, /* mayResolve */
3287 : nullptr, /* finalize */
3288 : nullptr, /* call */
3289 : nullptr, /* hasInstance */
3290 : nullptr, /* construct */
3291 : Debugger::traceObject
3292 : };
3293 :
3294 : const Class Debugger::class_ = {
3295 : "Debugger",
3296 : JSCLASS_HAS_PRIVATE |
3297 : JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
3298 : &Debugger::classOps_
3299 : };
3300 :
3301 : static Debugger*
3302 0 : Debugger_fromThisValue(JSContext* cx, const CallArgs& args, const char* fnname)
3303 : {
3304 0 : JSObject* thisobj = NonNullObject(cx, args.thisv());
3305 0 : if (!thisobj)
3306 0 : return nullptr;
3307 0 : if (thisobj->getClass() != &Debugger::class_) {
3308 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
3309 0 : "Debugger", fnname, thisobj->getClass()->name);
3310 0 : return nullptr;
3311 : }
3312 :
3313 : /*
3314 : * Forbid Debugger.prototype, which is of the Debugger JSClass but isn't
3315 : * really a Debugger object. The prototype object is distinguished by
3316 : * having a nullptr private value.
3317 : */
3318 0 : Debugger* dbg = Debugger::fromJSObject(thisobj);
3319 0 : if (!dbg) {
3320 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
3321 0 : "Debugger", fnname, "prototype object");
3322 : }
3323 0 : return dbg;
3324 : }
3325 :
3326 : #define THIS_DEBUGGER(cx, argc, vp, fnname, args, dbg) \
3327 : CallArgs args = CallArgsFromVp(argc, vp); \
3328 : Debugger* dbg = Debugger_fromThisValue(cx, args, fnname); \
3329 : if (!dbg) \
3330 : return false
3331 :
3332 : /* static */ bool
3333 0 : Debugger::getEnabled(JSContext* cx, unsigned argc, Value* vp)
3334 : {
3335 0 : THIS_DEBUGGER(cx, argc, vp, "get enabled", args, dbg);
3336 0 : args.rval().setBoolean(dbg->enabled);
3337 0 : return true;
3338 : }
3339 :
3340 : /* static */ bool
3341 0 : Debugger::setEnabled(JSContext* cx, unsigned argc, Value* vp)
3342 : {
3343 0 : THIS_DEBUGGER(cx, argc, vp, "set enabled", args, dbg);
3344 0 : if (!args.requireAtLeast(cx, "Debugger.set enabled", 1))
3345 0 : return false;
3346 :
3347 0 : bool wasEnabled = dbg->enabled;
3348 0 : dbg->enabled = ToBoolean(args[0]);
3349 :
3350 0 : if (wasEnabled != dbg->enabled) {
3351 0 : if (dbg->trackingAllocationSites) {
3352 0 : if (wasEnabled) {
3353 0 : dbg->removeAllocationsTrackingForAllDebuggees();
3354 : } else {
3355 0 : if (!dbg->addAllocationsTrackingForAllDebuggees(cx)) {
3356 0 : dbg->enabled = false;
3357 0 : return false;
3358 : }
3359 : }
3360 : }
3361 :
3362 0 : for (Breakpoint* bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
3363 0 : if (!wasEnabled)
3364 0 : bp->site->inc(cx->runtime()->defaultFreeOp());
3365 : else
3366 0 : bp->site->dec(cx->runtime()->defaultFreeOp());
3367 : }
3368 :
3369 : /*
3370 : * Add or remove ourselves from the runtime's list of Debuggers
3371 : * that care about new globals.
3372 : */
3373 0 : if (dbg->getHook(OnNewGlobalObject)) {
3374 0 : if (!wasEnabled) {
3375 0 : cx->runtime()->onNewGlobalObjectWatchers().pushBack(dbg);
3376 : } else {
3377 0 : cx->runtime()->onNewGlobalObjectWatchers().remove(dbg);
3378 : }
3379 : }
3380 :
3381 : // Ensure the compartment is observable if we are re-enabling a
3382 : // Debugger with hooks that observe all execution.
3383 0 : if (!dbg->updateObservesAllExecutionOnDebuggees(cx, dbg->observesAllExecution()))
3384 0 : return false;
3385 :
3386 : // Note: To toogle code coverage, we currently need to have no live
3387 : // stack frame, thus the coverage does not depend on the enabled flag.
3388 :
3389 0 : dbg->updateObservesAsmJSOnDebuggees(dbg->observesAsmJS());
3390 0 : dbg->updateObservesBinarySourceDebuggees(dbg->observesBinarySource());
3391 : }
3392 :
3393 0 : args.rval().setUndefined();
3394 0 : return true;
3395 : }
3396 :
3397 : /* static */ bool
3398 0 : Debugger::getHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which)
3399 : {
3400 0 : MOZ_ASSERT(which >= 0 && which < HookCount);
3401 0 : args.rval().set(dbg.object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + which));
3402 0 : return true;
3403 : }
3404 :
3405 : /* static */ bool
3406 0 : Debugger::setHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which)
3407 : {
3408 0 : MOZ_ASSERT(which >= 0 && which < HookCount);
3409 0 : if (!args.requireAtLeast(cx, "Debugger.setHook", 1))
3410 0 : return false;
3411 0 : if (args[0].isObject()) {
3412 0 : if (!args[0].toObject().isCallable())
3413 0 : return ReportIsNotFunction(cx, args[0], args.length() - 1);
3414 0 : } else if (!args[0].isUndefined()) {
3415 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
3416 0 : return false;
3417 : }
3418 0 : uint32_t slot = JSSLOT_DEBUG_HOOK_START + which;
3419 0 : RootedValue oldHook(cx, dbg.object->getReservedSlot(slot));
3420 0 : dbg.object->setReservedSlot(slot, args[0]);
3421 0 : if (hookObservesAllExecution(which)) {
3422 0 : if (!dbg.updateObservesAllExecutionOnDebuggees(cx, dbg.observesAllExecution())) {
3423 0 : dbg.object->setReservedSlot(slot, oldHook);
3424 0 : return false;
3425 : }
3426 : }
3427 0 : args.rval().setUndefined();
3428 0 : return true;
3429 : }
3430 :
3431 : /* static */ bool
3432 0 : Debugger::getOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp)
3433 : {
3434 0 : THIS_DEBUGGER(cx, argc, vp, "(get onDebuggerStatement)", args, dbg);
3435 0 : return getHookImpl(cx, args, *dbg, OnDebuggerStatement);
3436 : }
3437 :
3438 : /* static */ bool
3439 0 : Debugger::setOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp)
3440 : {
3441 0 : THIS_DEBUGGER(cx, argc, vp, "(set onDebuggerStatement)", args, dbg);
3442 0 : return setHookImpl(cx, args, *dbg, OnDebuggerStatement);
3443 : }
3444 :
3445 : /* static */ bool
3446 0 : Debugger::getOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp)
3447 : {
3448 0 : THIS_DEBUGGER(cx, argc, vp, "(get onExceptionUnwind)", args, dbg);
3449 0 : return getHookImpl(cx, args, *dbg, OnExceptionUnwind);
3450 : }
3451 :
3452 : /* static */ bool
3453 0 : Debugger::setOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp)
3454 : {
3455 0 : THIS_DEBUGGER(cx, argc, vp, "(set onExceptionUnwind)", args, dbg);
3456 0 : return setHookImpl(cx, args, *dbg, OnExceptionUnwind);
3457 : }
3458 :
3459 : /* static */ bool
3460 0 : Debugger::getOnNewScript(JSContext* cx, unsigned argc, Value* vp)
3461 : {
3462 0 : THIS_DEBUGGER(cx, argc, vp, "(get onNewScript)", args, dbg);
3463 0 : return getHookImpl(cx, args, *dbg, OnNewScript);
3464 : }
3465 :
3466 : /* static */ bool
3467 0 : Debugger::setOnNewScript(JSContext* cx, unsigned argc, Value* vp)
3468 : {
3469 0 : THIS_DEBUGGER(cx, argc, vp, "(set onNewScript)", args, dbg);
3470 0 : return setHookImpl(cx, args, *dbg, OnNewScript);
3471 : }
3472 :
3473 : /* static */ bool
3474 0 : Debugger::getOnNewPromise(JSContext* cx, unsigned argc, Value* vp)
3475 : {
3476 0 : THIS_DEBUGGER(cx, argc, vp, "(get onNewPromise)", args, dbg);
3477 0 : return getHookImpl(cx, args, *dbg, OnNewPromise);
3478 : }
3479 :
3480 : /* static */ bool
3481 0 : Debugger::setOnNewPromise(JSContext* cx, unsigned argc, Value* vp)
3482 : {
3483 0 : THIS_DEBUGGER(cx, argc, vp, "(set onNewPromise)", args, dbg);
3484 0 : return setHookImpl(cx, args, *dbg, OnNewPromise);
3485 : }
3486 :
3487 : /* static */ bool
3488 0 : Debugger::getOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp)
3489 : {
3490 0 : THIS_DEBUGGER(cx, argc, vp, "(get onPromiseSettled)", args, dbg);
3491 0 : return getHookImpl(cx, args, *dbg, OnPromiseSettled);
3492 : }
3493 :
3494 : /* static */ bool
3495 0 : Debugger::setOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp)
3496 : {
3497 0 : THIS_DEBUGGER(cx, argc, vp, "(set onPromiseSettled)", args, dbg);
3498 0 : return setHookImpl(cx, args, *dbg, OnPromiseSettled);
3499 : }
3500 :
3501 : /* static */ bool
3502 0 : Debugger::getOnEnterFrame(JSContext* cx, unsigned argc, Value* vp)
3503 : {
3504 0 : THIS_DEBUGGER(cx, argc, vp, "(get onEnterFrame)", args, dbg);
3505 0 : return getHookImpl(cx, args, *dbg, OnEnterFrame);
3506 : }
3507 :
3508 : /* static */ bool
3509 0 : Debugger::setOnEnterFrame(JSContext* cx, unsigned argc, Value* vp)
3510 : {
3511 0 : THIS_DEBUGGER(cx, argc, vp, "(set onEnterFrame)", args, dbg);
3512 0 : return setHookImpl(cx, args, *dbg, OnEnterFrame);
3513 : }
3514 :
3515 : /* static */ bool
3516 0 : Debugger::getOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp)
3517 : {
3518 0 : THIS_DEBUGGER(cx, argc, vp, "(get onNewGlobalObject)", args, dbg);
3519 0 : return getHookImpl(cx, args, *dbg, OnNewGlobalObject);
3520 : }
3521 :
3522 : /* static */ bool
3523 0 : Debugger::setOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp)
3524 : {
3525 0 : THIS_DEBUGGER(cx, argc, vp, "setOnNewGlobalObject", args, dbg);
3526 0 : RootedObject oldHook(cx, dbg->getHook(OnNewGlobalObject));
3527 :
3528 0 : if (!setHookImpl(cx, args, *dbg, OnNewGlobalObject))
3529 0 : return false;
3530 :
3531 : /*
3532 : * Add or remove ourselves from the runtime's list of Debuggers that
3533 : * care about new globals.
3534 : */
3535 0 : if (dbg->enabled) {
3536 0 : JSObject* newHook = dbg->getHook(OnNewGlobalObject);
3537 0 : if (!oldHook && newHook) {
3538 0 : cx->runtime()->onNewGlobalObjectWatchers().pushBack(dbg);
3539 0 : } else if (oldHook && !newHook) {
3540 0 : cx->runtime()->onNewGlobalObjectWatchers().remove(dbg);
3541 : }
3542 : }
3543 :
3544 0 : return true;
3545 : }
3546 :
3547 : /* static */ bool
3548 0 : Debugger::getUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp)
3549 : {
3550 0 : THIS_DEBUGGER(cx, argc, vp, "get uncaughtExceptionHook", args, dbg);
3551 0 : args.rval().setObjectOrNull(dbg->uncaughtExceptionHook);
3552 0 : return true;
3553 : }
3554 :
3555 : /* static */ bool
3556 0 : Debugger::setUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp)
3557 : {
3558 0 : THIS_DEBUGGER(cx, argc, vp, "set uncaughtExceptionHook", args, dbg);
3559 0 : if (!args.requireAtLeast(cx, "Debugger.set uncaughtExceptionHook", 1))
3560 0 : return false;
3561 0 : if (!args[0].isNull() && (!args[0].isObject() || !args[0].toObject().isCallable())) {
3562 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ASSIGN_FUNCTION_OR_NULL,
3563 0 : "uncaughtExceptionHook");
3564 0 : return false;
3565 : }
3566 0 : dbg->uncaughtExceptionHook = args[0].toObjectOrNull();
3567 0 : args.rval().setUndefined();
3568 0 : return true;
3569 : }
3570 :
3571 : /* static */ bool
3572 0 : Debugger::getAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp)
3573 : {
3574 0 : THIS_DEBUGGER(cx, argc, vp, "get allowUnobservedAsmJS", args, dbg);
3575 0 : args.rval().setBoolean(dbg->allowUnobservedAsmJS);
3576 0 : return true;
3577 : }
3578 :
3579 : /* static */ bool
3580 0 : Debugger::setAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp)
3581 : {
3582 0 : THIS_DEBUGGER(cx, argc, vp, "set allowUnobservedAsmJS", args, dbg);
3583 0 : if (!args.requireAtLeast(cx, "Debugger.set allowUnobservedAsmJS", 1))
3584 0 : return false;
3585 0 : dbg->allowUnobservedAsmJS = ToBoolean(args[0]);
3586 :
3587 0 : for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
3588 0 : GlobalObject* global = r.front();
3589 0 : JSCompartment* comp = global->compartment();
3590 0 : comp->updateDebuggerObservesAsmJS();
3591 : }
3592 :
3593 0 : args.rval().setUndefined();
3594 0 : return true;
3595 : }
3596 :
3597 : /* static */ bool
3598 0 : Debugger::getAllowWasmBinarySource(JSContext* cx, unsigned argc, Value* vp)
3599 : {
3600 0 : THIS_DEBUGGER(cx, argc, vp, "get allowWasmBinarySource", args, dbg);
3601 0 : args.rval().setBoolean(dbg->allowWasmBinarySource);
3602 0 : return true;
3603 : }
3604 :
3605 : /* static */ bool
3606 0 : Debugger::setAllowWasmBinarySource(JSContext* cx, unsigned argc, Value* vp)
3607 : {
3608 0 : THIS_DEBUGGER(cx, argc, vp, "set allowWasmBinarySource", args, dbg);
3609 0 : if (!args.requireAtLeast(cx, "Debugger.set allowWasmBinarySource", 1))
3610 0 : return false;
3611 0 : dbg->allowWasmBinarySource = ToBoolean(args[0]);
3612 :
3613 0 : for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
3614 0 : GlobalObject* global = r.front();
3615 0 : JSCompartment* comp = global->compartment();
3616 0 : comp->updateDebuggerObservesBinarySource();
3617 : }
3618 :
3619 0 : args.rval().setUndefined();
3620 0 : return true;
3621 : }
3622 :
3623 : /* static */ bool
3624 0 : Debugger::getCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp)
3625 : {
3626 0 : THIS_DEBUGGER(cx, argc, vp, "get collectCoverageInfo", args, dbg);
3627 0 : args.rval().setBoolean(dbg->collectCoverageInfo);
3628 0 : return true;
3629 : }
3630 :
3631 : /* static */ bool
3632 0 : Debugger::setCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp)
3633 : {
3634 0 : THIS_DEBUGGER(cx, argc, vp, "set collectCoverageInfo", args, dbg);
3635 0 : if (!args.requireAtLeast(cx, "Debugger.set collectCoverageInfo", 1))
3636 0 : return false;
3637 0 : dbg->collectCoverageInfo = ToBoolean(args[0]);
3638 :
3639 0 : IsObserving observing = dbg->collectCoverageInfo ? Observing : NotObserving;
3640 0 : if (!dbg->updateObservesCoverageOnDebuggees(cx, observing))
3641 0 : return false;
3642 :
3643 0 : args.rval().setUndefined();
3644 0 : return true;
3645 : }
3646 :
3647 : /* static */ bool
3648 0 : Debugger::getMemory(JSContext* cx, unsigned argc, Value* vp)
3649 : {
3650 0 : THIS_DEBUGGER(cx, argc, vp, "get memory", args, dbg);
3651 0 : Value memoryValue = dbg->object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE);
3652 :
3653 0 : if (!memoryValue.isObject()) {
3654 0 : RootedObject memory(cx, DebuggerMemory::create(cx, dbg));
3655 0 : if (!memory)
3656 0 : return false;
3657 0 : memoryValue = ObjectValue(*memory);
3658 : }
3659 :
3660 0 : args.rval().set(memoryValue);
3661 0 : return true;
3662 : }
3663 :
3664 : /*
3665 : * Given a value used to designate a global (there's quite a variety; see the
3666 : * docs), return the actual designee.
3667 : *
3668 : * Note that this does not check whether the designee is marked "invisible to
3669 : * Debugger" or not; different callers need to handle invisible-to-Debugger
3670 : * globals in different ways.
3671 : */
3672 : GlobalObject*
3673 0 : Debugger::unwrapDebuggeeArgument(JSContext* cx, const Value& v)
3674 : {
3675 0 : if (!v.isObject()) {
3676 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
3677 0 : "argument", "not a global object");
3678 0 : return nullptr;
3679 : }
3680 :
3681 0 : RootedObject obj(cx, &v.toObject());
3682 :
3683 : /* If it's a Debugger.Object belonging to this debugger, dereference that. */
3684 0 : if (obj->getClass() == &DebuggerObject::class_) {
3685 0 : RootedValue rv(cx, v);
3686 0 : if (!unwrapDebuggeeValue(cx, &rv))
3687 0 : return nullptr;
3688 0 : obj = &rv.toObject();
3689 : }
3690 :
3691 : /* If we have a cross-compartment wrapper, dereference as far as is secure. */
3692 0 : obj = CheckedUnwrap(obj);
3693 0 : if (!obj) {
3694 0 : ReportAccessDenied(cx);
3695 0 : return nullptr;
3696 : }
3697 :
3698 : /* If that produced a WindowProxy, get the Window (global). */
3699 0 : obj = ToWindowIfWindowProxy(obj);
3700 :
3701 : /* If that didn't produce a global object, it's an error. */
3702 0 : if (!obj->is<GlobalObject>()) {
3703 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
3704 0 : "argument", "not a global object");
3705 0 : return nullptr;
3706 : }
3707 :
3708 0 : return &obj->as<GlobalObject>();
3709 : }
3710 :
3711 : /* static */ bool
3712 0 : Debugger::addDebuggee(JSContext* cx, unsigned argc, Value* vp)
3713 : {
3714 0 : THIS_DEBUGGER(cx, argc, vp, "addDebuggee", args, dbg);
3715 0 : if (!args.requireAtLeast(cx, "Debugger.addDebuggee", 1))
3716 0 : return false;
3717 0 : Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
3718 0 : if (!global)
3719 0 : return false;
3720 :
3721 0 : if (!dbg->addDebuggeeGlobal(cx, global))
3722 0 : return false;
3723 :
3724 0 : RootedValue v(cx, ObjectValue(*global));
3725 0 : if (!dbg->wrapDebuggeeValue(cx, &v))
3726 0 : return false;
3727 0 : args.rval().set(v);
3728 0 : return true;
3729 : }
3730 :
3731 : /* static */ bool
3732 0 : Debugger::addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp)
3733 : {
3734 0 : THIS_DEBUGGER(cx, argc, vp, "addAllGlobalsAsDebuggees", args, dbg);
3735 0 : for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
3736 0 : for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
3737 0 : if (c == dbg->object->compartment() || c->creationOptions().invisibleToDebugger())
3738 0 : continue;
3739 0 : c->scheduledForDestruction = false;
3740 0 : GlobalObject* global = c->maybeGlobal();
3741 0 : if (global) {
3742 0 : Rooted<GlobalObject*> rg(cx, global);
3743 0 : if (!dbg->addDebuggeeGlobal(cx, rg))
3744 0 : return false;
3745 : }
3746 : }
3747 : }
3748 :
3749 0 : args.rval().setUndefined();
3750 0 : return true;
3751 : }
3752 :
3753 : /* static */ bool
3754 0 : Debugger::removeDebuggee(JSContext* cx, unsigned argc, Value* vp)
3755 : {
3756 0 : THIS_DEBUGGER(cx, argc, vp, "removeDebuggee", args, dbg);
3757 :
3758 0 : if (!args.requireAtLeast(cx, "Debugger.removeDebuggee", 1))
3759 0 : return false;
3760 0 : Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
3761 0 : if (!global)
3762 0 : return false;
3763 :
3764 0 : ExecutionObservableCompartments obs(cx);
3765 0 : if (!obs.init())
3766 0 : return false;
3767 :
3768 0 : if (dbg->debuggees.has(global)) {
3769 0 : dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, nullptr);
3770 :
3771 : // Only update the compartment if there are no Debuggers left, as it's
3772 : // expensive to check if no other Debugger has a live script or frame hook
3773 : // on any of the current on-stack debuggee frames.
3774 0 : if (global->getDebuggers()->empty() && !obs.add(global->compartment()))
3775 0 : return false;
3776 0 : if (!updateExecutionObservability(cx, obs, NotObserving))
3777 0 : return false;
3778 : }
3779 :
3780 0 : args.rval().setUndefined();
3781 0 : return true;
3782 : }
3783 :
3784 : /* static */ bool
3785 0 : Debugger::removeAllDebuggees(JSContext* cx, unsigned argc, Value* vp)
3786 : {
3787 0 : THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg);
3788 :
3789 0 : ExecutionObservableCompartments obs(cx);
3790 0 : if (!obs.init())
3791 0 : return false;
3792 :
3793 0 : for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
3794 0 : Rooted<GlobalObject*> global(cx, e.front());
3795 0 : dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, &e);
3796 :
3797 : // See note about adding to the observable set in removeDebuggee.
3798 0 : if (global->getDebuggers()->empty() && !obs.add(global->compartment()))
3799 0 : return false;
3800 : }
3801 :
3802 0 : if (!updateExecutionObservability(cx, obs, NotObserving))
3803 0 : return false;
3804 :
3805 0 : args.rval().setUndefined();
3806 0 : return true;
3807 : }
3808 :
3809 : /* static */ bool
3810 0 : Debugger::hasDebuggee(JSContext* cx, unsigned argc, Value* vp)
3811 : {
3812 0 : THIS_DEBUGGER(cx, argc, vp, "hasDebuggee", args, dbg);
3813 0 : if (!args.requireAtLeast(cx, "Debugger.hasDebuggee", 1))
3814 0 : return false;
3815 0 : GlobalObject* global = dbg->unwrapDebuggeeArgument(cx, args[0]);
3816 0 : if (!global)
3817 0 : return false;
3818 0 : args.rval().setBoolean(!!dbg->debuggees.lookup(global));
3819 0 : return true;
3820 : }
3821 :
3822 : /* static */ bool
3823 0 : Debugger::getDebuggees(JSContext* cx, unsigned argc, Value* vp)
3824 : {
3825 0 : THIS_DEBUGGER(cx, argc, vp, "getDebuggees", args, dbg);
3826 :
3827 : // Obtain the list of debuggees before wrapping each debuggee, as a GC could
3828 : // update the debuggees set while we are iterating it.
3829 0 : unsigned count = dbg->debuggees.count();
3830 0 : AutoValueVector debuggees(cx);
3831 0 : if (!debuggees.resize(count))
3832 0 : return false;
3833 0 : unsigned i = 0;
3834 : {
3835 0 : JS::AutoCheckCannotGC nogc;
3836 0 : for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
3837 0 : debuggees[i++].setObject(*e.front().get());
3838 : }
3839 :
3840 0 : RootedArrayObject arrobj(cx, NewDenseFullyAllocatedArray(cx, count));
3841 0 : if (!arrobj)
3842 0 : return false;
3843 0 : arrobj->ensureDenseInitializedLength(cx, 0, count);
3844 0 : for (i = 0; i < count; i++) {
3845 0 : RootedValue v(cx, debuggees[i]);
3846 0 : if (!dbg->wrapDebuggeeValue(cx, &v))
3847 0 : return false;
3848 0 : arrobj->setDenseElement(i, v);
3849 : }
3850 :
3851 0 : args.rval().setObject(*arrobj);
3852 0 : return true;
3853 : }
3854 :
3855 : /* static */ bool
3856 0 : Debugger::getNewestFrame(JSContext* cx, unsigned argc, Value* vp)
3857 : {
3858 0 : THIS_DEBUGGER(cx, argc, vp, "getNewestFrame", args, dbg);
3859 :
3860 : /* Since there may be multiple contexts, use AllFramesIter. */
3861 0 : for (AllFramesIter i(cx); !i.done(); ++i) {
3862 0 : if (dbg->observesFrame(i)) {
3863 : // Ensure that Ion frames are rematerialized. Only rematerialized
3864 : // Ion frames may be used as AbstractFramePtrs.
3865 0 : if (i.isIon() && !i.ensureHasRematerializedFrame(cx))
3866 0 : return false;
3867 0 : AbstractFramePtr frame = i.abstractFramePtr();
3868 0 : FrameIter iter(i.activation()->cx());
3869 0 : while (!iter.hasUsableAbstractFramePtr() || iter.abstractFramePtr() != frame)
3870 0 : ++iter;
3871 0 : return dbg->getScriptFrame(cx, iter, args.rval());
3872 : }
3873 : }
3874 0 : args.rval().setNull();
3875 0 : return true;
3876 : }
3877 :
3878 : /* static */ bool
3879 0 : Debugger::clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp)
3880 : {
3881 0 : THIS_DEBUGGER(cx, argc, vp, "clearAllBreakpoints", args, dbg);
3882 0 : for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront())
3883 0 : r.front()->compartment()->clearBreakpointsIn(cx->runtime()->defaultFreeOp(),
3884 0 : dbg, nullptr);
3885 0 : return true;
3886 : }
3887 :
3888 : /* static */ bool
3889 0 : Debugger::construct(JSContext* cx, unsigned argc, Value* vp)
3890 : {
3891 0 : CallArgs args = CallArgsFromVp(argc, vp);
3892 :
3893 : /* Check that the arguments, if any, are cross-compartment wrappers. */
3894 0 : for (unsigned i = 0; i < args.length(); i++) {
3895 0 : JSObject* argobj = NonNullObject(cx, args[i]);
3896 0 : if (!argobj)
3897 0 : return false;
3898 0 : if (!argobj->is<CrossCompartmentWrapperObject>()) {
3899 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_CCW_REQUIRED,
3900 0 : "Debugger");
3901 0 : return false;
3902 : }
3903 : }
3904 :
3905 : /* Get Debugger.prototype. */
3906 0 : RootedValue v(cx);
3907 0 : RootedObject callee(cx, &args.callee());
3908 0 : if (!GetProperty(cx, callee, callee, cx->names().prototype, &v))
3909 0 : return false;
3910 0 : RootedNativeObject proto(cx, &v.toObject().as<NativeObject>());
3911 0 : MOZ_ASSERT(proto->getClass() == &Debugger::class_);
3912 : /*
3913 : * Make the new Debugger object. Each one has a reference to
3914 : * Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The
3915 : * rest of the reserved slots are for hooks; they default to undefined.
3916 : */
3917 0 : RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &Debugger::class_, proto,
3918 0 : TenuredObject));
3919 0 : if (!obj)
3920 0 : return false;
3921 0 : for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++)
3922 0 : obj->setReservedSlot(slot, proto->getReservedSlot(slot));
3923 0 : obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue());
3924 :
3925 : // Debuggers currently require single threaded execution. A debugger may be
3926 : // used to debug content in other zone groups, and may be used to observe
3927 : // all activity in the runtime via hooks like OnNewGlobalObject.
3928 0 : if (!cx->runtime()->beginSingleThreadedExecution(cx)) {
3929 0 : JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in Debugger");
3930 0 : return false;
3931 : }
3932 :
3933 : Debugger* debugger;
3934 : {
3935 : /* Construct the underlying C++ object. */
3936 0 : auto dbg = cx->make_unique<Debugger>(cx, obj.get());
3937 0 : if (!dbg) {
3938 0 : JS::AutoSuppressGCAnalysis nogc; // Suppress warning about |dbg|.
3939 0 : cx->runtime()->endSingleThreadedExecution(cx);
3940 0 : return false;
3941 : }
3942 0 : if (!dbg->init(cx))
3943 0 : return false;
3944 :
3945 0 : debugger = dbg.release();
3946 0 : obj->setPrivate(debugger); // owns the released pointer
3947 : }
3948 :
3949 : /* Add the initial debuggees, if any. */
3950 0 : for (unsigned i = 0; i < args.length(); i++) {
3951 : Rooted<GlobalObject*>
3952 0 : debuggee(cx, &args[i].toObject().as<ProxyObject>().private_().toObject().global());
3953 0 : if (!debugger->addDebuggeeGlobal(cx, debuggee))
3954 0 : return false;
3955 : }
3956 :
3957 0 : args.rval().setObject(*obj);
3958 0 : return true;
3959 : }
3960 :
3961 : bool
3962 0 : Debugger::addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> global)
3963 : {
3964 0 : if (debuggees.has(global))
3965 0 : return true;
3966 :
3967 : // 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 0 : JSCompartment* debuggeeCompartment = global->compartment();
3972 0 : if (debuggeeCompartment->creationOptions().invisibleToDebugger()) {
3973 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_CANT_DEBUG_GLOBAL);
3974 0 : return false;
3975 : }
3976 :
3977 : /*
3978 : * Check for cycles. If global's compartment is reachable from this
3979 : * Debugger object's compartment 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 0 : Vector<JSCompartment*> visited(cx);
3984 0 : if (!visited.append(object->compartment()))
3985 0 : return false;
3986 0 : for (size_t i = 0; i < visited.length(); i++) {
3987 0 : JSCompartment* c = visited[i];
3988 0 : if (c == debuggeeCompartment) {
3989 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_LOOP);
3990 0 : return false;
3991 : }
3992 :
3993 : /*
3994 : * Find all compartments containing debuggers debugging c's global
3995 : * object. Add those compartments to visited.
3996 : */
3997 0 : if (c->isDebuggee()) {
3998 0 : GlobalObject::DebuggerVector* v = c->maybeGlobal()->getDebuggers();
3999 0 : for (auto p = v->begin(); p != v->end(); p++) {
4000 0 : JSCompartment* next = (*p)->object->compartment();
4001 0 : if (Find(visited, next) == visited.end() && !visited.append(next))
4002 0 : return false;
4003 : }
4004 : }
4005 : }
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 compartment, and
4016 : * 6. JSCompartment::isDebuggee()'s bit must be set.
4017 : *
4018 : * All six indications must be kept consistent.
4019 : */
4020 :
4021 0 : AutoCompartment ac(cx, global);
4022 0 : Zone* zone = global->zone();
4023 :
4024 : // (1)
4025 0 : auto* globalDebuggers = GlobalObject::getOrCreateDebuggers(cx, global);
4026 0 : if (!globalDebuggers)
4027 0 : return false;
4028 0 : if (!globalDebuggers->append(this)) {
4029 0 : ReportOutOfMemory(cx);
4030 0 : return false;
4031 : }
4032 0 : auto globalDebuggersGuard = MakeScopeExit([&] {
4033 0 : globalDebuggers->popBack();
4034 0 : });
4035 :
4036 : // (2)
4037 0 : if (!debuggees.put(global)) {
4038 0 : ReportOutOfMemory(cx);
4039 0 : return false;
4040 : }
4041 0 : auto debuggeesGuard = MakeScopeExit([&] {
4042 0 : debuggees.remove(global);
4043 0 : });
4044 :
4045 0 : bool addingZoneRelation = !debuggeeZones.has(zone);
4046 :
4047 : // (3)
4048 0 : auto* zoneDebuggers = zone->getOrCreateDebuggers(cx);
4049 0 : if (!zoneDebuggers)
4050 0 : return false;
4051 0 : if (addingZoneRelation && !zoneDebuggers->append(this)) {
4052 0 : ReportOutOfMemory(cx);
4053 0 : return false;
4054 : }
4055 0 : auto zoneDebuggersGuard = MakeScopeExit([&] {
4056 0 : if (addingZoneRelation)
4057 0 : zoneDebuggers->popBack();
4058 0 : });
4059 :
4060 : // (4)
4061 0 : if (addingZoneRelation && !debuggeeZones.put(zone)) {
4062 0 : ReportOutOfMemory(cx);
4063 0 : return false;
4064 : }
4065 0 : auto debuggeeZonesGuard = MakeScopeExit([&] {
4066 0 : if (addingZoneRelation)
4067 0 : debuggeeZones.remove(zone);
4068 0 : });
4069 :
4070 : // (5)
4071 0 : if (trackingAllocationSites && enabled && !Debugger::addAllocationsTracking(cx, global))
4072 0 : return false;
4073 :
4074 0 : auto allocationsTrackingGuard = MakeScopeExit([&] {
4075 0 : if (trackingAllocationSites && enabled)
4076 0 : Debugger::removeAllocationsTracking(*global);
4077 0 : });
4078 :
4079 : // (6)
4080 0 : AutoRestoreCompartmentDebugMode debugModeGuard(debuggeeCompartment);
4081 0 : debuggeeCompartment->setIsDebuggee();
4082 0 : debuggeeCompartment->updateDebuggerObservesAsmJS();
4083 0 : debuggeeCompartment->updateDebuggerObservesBinarySource();
4084 0 : debuggeeCompartment->updateDebuggerObservesCoverage();
4085 0 : if (observesAllExecution() && !ensureExecutionObservabilityOfCompartment(cx, debuggeeCompartment))
4086 0 : return false;
4087 :
4088 0 : globalDebuggersGuard.release();
4089 0 : debuggeesGuard.release();
4090 0 : zoneDebuggersGuard.release();
4091 0 : debuggeeZonesGuard.release();
4092 0 : allocationsTrackingGuard.release();
4093 0 : debugModeGuard.release();
4094 0 : return true;
4095 : }
4096 :
4097 : void
4098 0 : Debugger::recomputeDebuggeeZoneSet()
4099 : {
4100 0 : AutoEnterOOMUnsafeRegion oomUnsafe;
4101 0 : debuggeeZones.clear();
4102 0 : 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 : }
4106 0 : }
4107 :
4108 : template <typename T>
4109 : static T*
4110 0 : findDebuggerInVector(Debugger* dbg, Vector<T, 0, js::SystemAllocPolicy>* vec)
4111 : {
4112 : T* p;
4113 0 : for (p = vec->begin(); p != vec->end(); p++) {
4114 0 : if (*p == dbg)
4115 0 : break;
4116 : }
4117 0 : MOZ_ASSERT(p != vec->end());
4118 0 : return p;
4119 : }
4120 :
4121 : void
4122 0 : Debugger::removeDebuggeeGlobal(FreeOp* fop, GlobalObject* global,
4123 : WeakGlobalObjectSet::Enum* debugEnum)
4124 : {
4125 : /*
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 0 : MOZ_ASSERT(debuggees.has(global));
4131 0 : MOZ_ASSERT(debuggeeZones.has(global->zone()));
4132 0 : MOZ_ASSERT_IF(debugEnum, debugEnum->front().unbarrieredGet() == global);
4133 :
4134 : /*
4135 : * 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 0 : for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) {
4144 0 : AbstractFramePtr frame = e.front().key();
4145 0 : NativeObject* frameobj = e.front().value();
4146 0 : if (frame.global() == global) {
4147 0 : DebuggerFrame_freeScriptFrameIterData(fop, frameobj);
4148 0 : DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj);
4149 0 : e.removeFront();
4150 : }
4151 : }
4152 :
4153 0 : auto *globalDebuggersVector = global->getDebuggers();
4154 0 : auto *zoneDebuggersVector = global->zone()->getDebuggers();
4155 :
4156 : /*
4157 : * 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 0 : globalDebuggersVector->erase(findDebuggerInVector(this, globalDebuggersVector));
4168 :
4169 0 : if (debugEnum)
4170 0 : debugEnum->removeFront();
4171 : else
4172 0 : debuggees.remove(global);
4173 :
4174 0 : recomputeDebuggeeZoneSet();
4175 :
4176 0 : if (!debuggeeZones.has(global->zone()))
4177 0 : zoneDebuggersVector->erase(findDebuggerInVector(this, zoneDebuggersVector));
4178 :
4179 : /* Remove all breakpoints for the debuggee. */
4180 : Breakpoint* nextbp;
4181 0 : for (Breakpoint* bp = firstBreakpoint(); bp; bp = nextbp) {
4182 0 : nextbp = bp->nextInDebugger();
4183 0 : switch (bp->site->type()) {
4184 : case BreakpointSite::Type::JS:
4185 0 : if (bp->site->asJS()->script->compartment() == global->compartment())
4186 0 : bp->destroy(fop);
4187 0 : break;
4188 : case BreakpointSite::Type::Wasm:
4189 0 : if (bp->asWasm()->wasmInstance->compartment() == global->compartment())
4190 0 : bp->destroy(fop);
4191 0 : break;
4192 : }
4193 : }
4194 0 : MOZ_ASSERT_IF(debuggees.empty(), !firstBreakpoint());
4195 :
4196 : /*
4197 : * If we are tracking allocation sites, we need to remove the object
4198 : * metadata callback from this global's compartment.
4199 : */
4200 0 : if (trackingAllocationSites)
4201 0 : Debugger::removeAllocationsTracking(*global);
4202 :
4203 0 : if (global->getDebuggers()->empty()) {
4204 0 : global->compartment()->unsetIsDebuggee();
4205 : } else {
4206 0 : global->compartment()->updateDebuggerObservesAllExecution();
4207 0 : global->compartment()->updateDebuggerObservesAsmJS();
4208 0 : global->compartment()->updateDebuggerObservesBinarySource();
4209 0 : global->compartment()->updateDebuggerObservesCoverage();
4210 : }
4211 0 : }
4212 :
4213 :
4214 : 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 0 : class MOZ_STACK_CLASS Debugger::ScriptQuery
4221 : {
4222 : public:
4223 : /* Construct a ScriptQuery to use matching scripts for |dbg|. */
4224 0 : ScriptQuery(JSContext* cx, Debugger* dbg):
4225 : cx(cx),
4226 : debugger(dbg),
4227 0 : iterMarker(&cx->runtime()->gc),
4228 : compartments(cx->runtime()),
4229 : url(cx),
4230 : displayURLString(cx),
4231 : hasSource(false),
4232 0 : source(cx, AsVariant(static_cast<ScriptSourceObject*>(nullptr))),
4233 : innermostForCompartment(cx->runtime()),
4234 0 : vector(cx, ScriptVector(cx)),
4235 0 : wasmInstanceVector(cx, WasmInstanceObjectVector(cx))
4236 0 : {}
4237 :
4238 : /*
4239 : * Initialize this ScriptQuery. Raise an error and return false if we
4240 : * haven't enough memory.
4241 : */
4242 0 : bool init() {
4243 0 : if (!compartments.init() ||
4244 0 : !innermostForCompartment.init())
4245 : {
4246 0 : ReportOutOfMemory(cx);
4247 0 : return false;
4248 : }
4249 :
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 0 : bool parseQuery(HandleObject query) {
4258 : /*
4259 : * Check for a 'global' property, which limits the results to those
4260 : * scripts scoped to a particular global object.
4261 : */
4262 0 : RootedValue global(cx);
4263 0 : if (!GetProperty(cx, query, query, cx->names().global, &global))
4264 0 : return false;
4265 0 : if (global.isUndefined()) {
4266 0 : if (!matchAllDebuggeeGlobals())
4267 0 : return false;
4268 : } else {
4269 0 : GlobalObject* globalObject = debugger->unwrapDebuggeeArgument(cx, global);
4270 0 : if (!globalObject)
4271 0 : return false;
4272 :
4273 : /*
4274 : * If the given global isn't a debuggee, just leave the set of
4275 : * acceptable globals empty; we'll return no scripts.
4276 : */
4277 0 : if (debugger->debuggees.has(globalObject)) {
4278 0 : if (!matchSingleGlobal(globalObject))
4279 0 : return false;
4280 : }
4281 : }
4282 :
4283 : /* Check for a 'url' property. */
4284 0 : if (!GetProperty(cx, query, query, cx->names().url, &url))
4285 0 : return false;
4286 0 : 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 :
4293 : /* Check for a 'source' property */
4294 0 : RootedValue debuggerSource(cx);
4295 0 : if (!GetProperty(cx, query, query, cx->names().source, &debuggerSource))
4296 0 : return false;
4297 0 : if (!debuggerSource.isUndefined()) {
4298 0 : if (!debuggerSource.isObject() ||
4299 0 : debuggerSource.toObject().getClass() != &DebuggerSource_class) {
4300 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
4301 : "query object's 'source' property",
4302 0 : "not undefined nor a Debugger.Source object");
4303 0 : return false;
4304 : }
4305 :
4306 0 : Value owner = debuggerSource.toObject()
4307 0 : .as<NativeObject>()
4308 0 : .getReservedSlot(JSSLOT_DEBUGSOURCE_OWNER);
4309 :
4310 : /*
4311 : * 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 0 : if (!owner.isObject()) {
4316 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_PROTO,
4317 0 : "Debugger.Source", "Debugger.Source");
4318 0 : return false;
4319 : }
4320 :
4321 : /*
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 0 : if (&owner.toObject() != debugger->object) {
4327 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_WRONG_OWNER,
4328 0 : "Debugger.Source");
4329 0 : return false;
4330 : }
4331 :
4332 0 : hasSource = true;
4333 0 : source = GetSourceReferent(&debuggerSource.toObject());
4334 : }
4335 :
4336 : /* Check for a 'displayURL' property. */
4337 0 : RootedValue displayURL(cx);
4338 0 : if (!GetProperty(cx, query, query, cx->names().displayURL, &displayURL))
4339 0 : 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 :
4347 0 : if (displayURL.isString()) {
4348 0 : displayURLString = displayURL.toString()->ensureLinear(cx);
4349 0 : if (!displayURLString)
4350 0 : return false;
4351 : }
4352 :
4353 : /* Check for a 'line' property. */
4354 0 : RootedValue lineProperty(cx);
4355 0 : if (!GetProperty(cx, query, query, cx->names().line, &lineProperty))
4356 0 : return false;
4357 0 : if (lineProperty.isUndefined()) {
4358 0 : hasLine = false;
4359 0 : } 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 : }
4365 0 : double doubleLine = lineProperty.toNumber();
4366 0 : if (doubleLine <= 0 || (unsigned int) doubleLine != doubleLine) {
4367 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_LINE);
4368 0 : return false;
4369 : }
4370 0 : hasLine = true;
4371 0 : line = doubleLine;
4372 : } else {
4373 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
4374 : "query object's 'line' property",
4375 0 : "neither undefined nor an integer");
4376 0 : return false;
4377 : }
4378 :
4379 : /* Check for an 'innermost' property. */
4380 0 : PropertyName* innermostName = cx->names().innermost;
4381 0 : RootedValue innermostProperty(cx);
4382 0 : 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 0 : JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL);
4390 0 : return false;
4391 : }
4392 : }
4393 :
4394 0 : return true;
4395 : }
4396 :
4397 : /* Set up this ScriptQuery appropriately for a missing query argument. */
4398 0 : bool omittedQuery() {
4399 0 : url.setUndefined();
4400 0 : hasLine = false;
4401 0 : innermost = false;
4402 0 : displayURLString = nullptr;
4403 0 : return matchAllDebuggeeGlobals();
4404 : }
4405 :
4406 : /*
4407 : * Search all relevant compartments and the stack for scripts matching
4408 : * this query, and append the matching scripts to |vector|.
4409 : */
4410 0 : bool findScripts() {
4411 0 : if (!prepareQuery() || !delazifyScripts())
4412 0 : return false;
4413 :
4414 0 : JSCompartment* singletonComp = nullptr;
4415 0 : if (compartments.count() == 1)
4416 0 : singletonComp = compartments.all().front();
4417 :
4418 : /* Search each compartment for debuggee scripts. */
4419 0 : MOZ_ASSERT(vector.empty());
4420 0 : oom = false;
4421 0 : IterateScripts(cx, singletonComp, this, considerScript);
4422 0 : if (oom) {
4423 0 : ReportOutOfMemory(cx);
4424 0 : return false;
4425 : }
4426 :
4427 : /* We cannot touch the gray bits while isHeapBusy, so do this now. */
4428 0 : for (JSScript** i = vector.begin(); i != vector.end(); ++i)
4429 0 : JS::ExposeScriptToActiveJS(*i);
4430 :
4431 : /*
4432 : * 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 'innermostForCompartment' map. In that case, we now need to
4435 : * walk that map and populate 'vector'.
4436 : */
4437 0 : if (innermost) {
4438 0 : for (CompartmentToScriptMap::Range r = innermostForCompartment.all();
4439 0 : !r.empty();
4440 0 : r.popFront())
4441 : {
4442 0 : JS::ExposeScriptToActiveJS(r.front().value());
4443 0 : if (!vector.append(r.front().value())) {
4444 0 : ReportOutOfMemory(cx);
4445 0 : return false;
4446 : }
4447 : }
4448 : }
4449 :
4450 : // TODOshu: Until such time that wasm modules are real ES6 modules,
4451 : // unconditionally consider all wasm toplevel instance scripts.
4452 0 : for (WeakGlobalObjectSet::Range r = debugger->allDebuggees(); !r.empty(); r.popFront()) {
4453 0 : for (wasm::Instance* instance : r.front()->compartment()->wasm.instances()) {
4454 0 : consider(instance->object());
4455 0 : if (oom) {
4456 0 : ReportOutOfMemory(cx);
4457 0 : return false;
4458 : }
4459 : }
4460 : }
4461 :
4462 0 : return true;
4463 : }
4464 :
4465 0 : Handle<ScriptVector> foundScripts() const {
4466 0 : return vector;
4467 : }
4468 :
4469 0 : Handle<WasmInstanceObjectVector> foundWasmInstances() const {
4470 0 : return wasmInstanceVector;
4471 : }
4472 :
4473 : 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 compartments to stay fixed while the ScriptQuery is alive. */
4481 : gc::AutoEnterIteration iterMarker;
4482 :
4483 : typedef HashSet<JSCompartment*, DefaultHasher<JSCompartment*>, RuntimeAllocPolicy>
4484 : CompartmentSet;
4485 :
4486 : /* A script must be in one of these compartments to match the query. */
4487 : CompartmentSet compartments;
4488 :
4489 : /* If this is a string, matching scripts have urls equal to it. */
4490 : RootedValue url;
4491 :
4492 : /* url as a C string. */
4493 : JSAutoByteString urlCString;
4494 :
4495 : /* If this is a string, matching scripts' sources have displayURLs equal to
4496 : * it. */
4497 : RootedLinearString displayURLString;
4498 :
4499 : /*
4500 : * If this is a source referent, matching scripts will have sources equal
4501 : * to this instance. Ideally we'd use a Maybe here, but Maybe interacts
4502 : * very badly with Rooted's LIFO invariant.
4503 : */
4504 : bool hasSource;
4505 : Rooted<DebuggerSourceReferent> source;
4506 :
4507 : /* True if the query contained a 'line' property. */
4508 : bool hasLine;
4509 :
4510 : /* The line matching scripts must cover. */
4511 : unsigned int line;
4512 :
4513 : /* True if the query has an 'innermost' property whose value is true. */
4514 : bool innermost;
4515 :
4516 : typedef HashMap<JSCompartment*, JSScript*, DefaultHasher<JSCompartment*>, RuntimeAllocPolicy>
4517 : CompartmentToScriptMap;
4518 :
4519 : /*
4520 : * For 'innermost' queries, a map from compartments to the innermost script
4521 : * we've seen so far in that compartment. (Template instantiation code size
4522 : * explosion ho!)
4523 : */
4524 : CompartmentToScriptMap innermostForCompartment;
4525 :
4526 : /*
4527 : * Accumulate the scripts in an Rooted<ScriptVector>, instead of creating
4528 : * the JS array as we go, because we mustn't allocate JS objects or GC
4529 : * while we use the CellIter.
4530 : */
4531 : Rooted<ScriptVector> vector;
4532 :
4533 : /*
4534 : * Like above, but for wasm modules.
4535 : */
4536 : Rooted<WasmInstanceObjectVector> wasmInstanceVector;
4537 :
4538 : /* Indicates whether OOM has occurred while matching. */
4539 : bool oom;
4540 :
4541 0 : bool addCompartment(JSCompartment* comp) {
4542 0 : return compartments.put(comp);
4543 : }
4544 :
4545 : /* Arrange for this ScriptQuery to match only scripts that run in |global|. */
4546 0 : bool matchSingleGlobal(GlobalObject* global) {
4547 0 : MOZ_ASSERT(compartments.count() == 0);
4548 0 : if (!addCompartment(global->compartment())) {
4549 0 : ReportOutOfMemory(cx);
4550 0 : return false;
4551 : }
4552 0 : return true;
4553 : }
4554 :
4555 : /*
4556 : * Arrange for this ScriptQuery to match all scripts running in debuggee
4557 : * globals.
4558 : */
4559 0 : bool matchAllDebuggeeGlobals() {
4560 0 : MOZ_ASSERT(compartments.count() == 0);
4561 : /* Build our compartment set from the debugger's set of debuggee globals. */
4562 0 : for (WeakGlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) {
4563 0 : if (!addCompartment(r.front()->compartment())) {
4564 0 : ReportOutOfMemory(cx);
4565 0 : return false;
4566 : }
4567 : }
4568 0 : return true;
4569 : }
4570 :
4571 : /*
4572 : * Given that parseQuery or omittedQuery has been called, prepare to match
4573 : * scripts. Set urlCString and displayURLChars as appropriate.
4574 : */
4575 0 : bool prepareQuery() {
4576 : /* Compute urlCString and displayURLChars, if a url or displayURL was
4577 : * given respectively. */
4578 0 : if (url.isString()) {
4579 0 : if (!urlCString.encodeLatin1(cx, url.toString()))
4580 0 : return false;
4581 : }
4582 :
4583 0 : return true;
4584 : }
4585 :
4586 0 : bool delazifyScripts() {
4587 : // All scripts in debuggee compartments must be visible, so delazify
4588 : // everything.
4589 0 : for (auto r = compartments.all(); !r.empty(); r.popFront()) {
4590 0 : JSCompartment* comp = r.front();
4591 0 : if (!comp->ensureDelazifyScriptsForDebugger(cx))
4592 0 : return false;
4593 : }
4594 0 : return true;
4595 : }
4596 :
4597 0 : static void considerScript(JSRuntime* rt, void* data, JSScript* script) {
4598 0 : ScriptQuery* self = static_cast<ScriptQuery*>(data);
4599 0 : self->consider(script);
4600 0 : }
4601 :
4602 : /*
4603 : * If |script| matches this query, append it to |vector| or place it in
4604 : * |innermostForCompartment|, as appropriate. Set |oom| if an out of memory
4605 : * condition occurred.
4606 : */
4607 0 : void consider(JSScript* script) {
4608 : // We check for presence of script->code() because it is possible that
4609 : // the script was created and thus exposed to GC, but *not* fully
4610 : // initialized from fullyInit{FromEmitter,Trivial} due to errors.
4611 0 : if (oom || script->selfHosted() || !script->code())
4612 0 : return;
4613 0 : JSCompartment* compartment = script->compartment();
4614 0 : if (!compartments.has(compartment))
4615 0 : return;
4616 0 : if (urlCString.ptr()) {
4617 0 : bool gotFilename = false;
4618 0 : if (script->filename() && strcmp(script->filename(), urlCString.ptr()) == 0)
4619 0 : gotFilename = true;
4620 :
4621 0 : bool gotSourceURL = false;
4622 0 : if (!gotFilename && script->scriptSource()->introducerFilename() &&
4623 0 : strcmp(script->scriptSource()->introducerFilename(), urlCString.ptr()) == 0)
4624 : {
4625 0 : gotSourceURL = true;
4626 : }
4627 0 : if (!gotFilename && !gotSourceURL)
4628 0 : return;
4629 : }
4630 0 : if (hasLine) {
4631 0 : if (line < script->lineno() || script->lineno() + GetScriptLineExtent(script) < line)
4632 0 : return;
4633 : }
4634 0 : if (displayURLString) {
4635 0 : if (!script->scriptSource() || !script->scriptSource()->hasDisplayURL())
4636 0 : return;
4637 :
4638 0 : const char16_t* s = script->scriptSource()->displayURL();
4639 0 : if (CompareChars(s, js_strlen(s), displayURLString) != 0)
4640 0 : return;
4641 : }
4642 0 : if (hasSource && !(source.is<ScriptSourceObject*>() &&
4643 0 : source.as<ScriptSourceObject*>()->source() == script->scriptSource()))
4644 : {
4645 0 : return;
4646 : }
4647 :
4648 0 : if (innermost) {
4649 : /*
4650 : * For 'innermost' queries, we don't place scripts in |vector| right
4651 : * away; we may later find another script that is nested inside this
4652 : * one. Instead, we record the innermost script we've found so far
4653 : * for each compartment in innermostForCompartment, and only
4654 : * populate |vector| at the bottom of findScripts, when we've
4655 : * traversed all the scripts.
4656 : *
4657 : * So: check this script against the innermost one we've found so
4658 : * far (if any), as recorded in innermostForCompartment, and replace
4659 : * that if it's better.
4660 : */
4661 0 : CompartmentToScriptMap::AddPtr p = innermostForCompartment.lookupForAdd(compartment);
4662 0 : if (p) {
4663 : /* Is our newly found script deeper than the last one we found? */
4664 0 : JSScript* incumbent = p->value();
4665 0 : if (script->innermostScope()->chainLength() >
4666 0 : incumbent->innermostScope()->chainLength())
4667 : {
4668 0 : p->value() = script;
4669 : }
4670 : } else {
4671 : /*
4672 : * This is the first matching script we've encountered for this
4673 : * compartment, so it is thus the innermost such script.
4674 : */
4675 0 : if (!innermostForCompartment.add(p, compartment, script)) {
4676 0 : oom = true;
4677 0 : return;
4678 : }
4679 : }
4680 : } else {
4681 : /* Record this matching script in the results vector. */
4682 0 : if (!vector.append(script)) {
4683 0 : oom = true;
4684 0 : return;
4685 : }
4686 : }
4687 :
4688 0 : return;
4689 : }
4690 :
4691 : /*
4692 : * If |instanceObject| matches this query, append it to |wasmInstanceVector|.
4693 : * Set |oom| if an out of memory condition occurred.
4694 : */
4695 0 : void consider(WasmInstanceObject* instanceObject) {
4696 0 : if (oom)
4697 0 : return;
4698 :
4699 0 : if (hasSource && source != AsVariant(instanceObject))
4700 0 : return;
4701 :
4702 0 : if (!wasmInstanceVector.append(instanceObject))
4703 0 : oom = true;
4704 : }
4705 : };
4706 :
4707 : /* static */ bool
4708 0 : Debugger::findScripts(JSContext* cx, unsigned argc, Value* vp)
4709 : {
4710 0 : THIS_DEBUGGER(cx, argc, vp, "findScripts", args, dbg);
4711 :
4712 0 : ScriptQuery query(cx, dbg);
4713 0 : if (!query.init())
4714 0 : return false;
4715 :
4716 0 : if (args.length() >= 1) {
4717 0 : RootedObject queryObject(cx, NonNullObject(cx, args[0]));
4718 0 : if (!queryObject || !query.parseQuery(queryObject))
4719 0 : return false;
4720 : } else {
4721 0 : if (!query.omittedQuery())
4722 0 : return false;
4723 : }
4724 :
4725 0 : if (!query.findScripts())
4726 0 : return false;
4727 :
4728 0 : Handle<ScriptVector> scripts(query.foundScripts());
4729 0 : Handle<WasmInstanceObjectVector> wasmInstances(query.foundWasmInstances());
4730 :
4731 0 : size_t resultLength = scripts.length() + wasmInstances.length();
4732 0 : RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, resultLength));
4733 0 : if (!result)
4734 0 : return false;
4735 :
4736 0 : result->ensureDenseInitializedLength(cx, 0, resultLength);
4737 :
4738 0 : for (size_t i = 0; i < scripts.length(); i++) {
4739 0 : JSObject* scriptObject = dbg->wrapScript(cx, scripts[i]);
4740 0 : if (!scriptObject)
4741 0 : return false;
4742 0 : result->setDenseElement(i, ObjectValue(*scriptObject));
4743 : }
4744 :
4745 0 : size_t wasmStart = scripts.length();
4746 0 : for (size_t i = 0; i < wasmInstances.length(); i++) {
4747 0 : JSObject* scriptObject = dbg->wrapWasmScript(cx, wasmInstances[i]);
4748 0 : if (!scriptObject)
4749 0 : return false;
4750 0 : result->setDenseElement(wasmStart + i, ObjectValue(*scriptObject));
4751 : }
4752 :
4753 0 : args.rval().setObject(*result);
4754 0 : return true;
4755 : }
4756 :
4757 : /*
4758 : * A class for parsing 'findObjects' query arguments and searching for objects
4759 : * that match the criteria they represent.
4760 : */
4761 0 : class MOZ_STACK_CLASS Debugger::ObjectQuery
4762 : {
4763 : public:
4764 : /* Construct an ObjectQuery to use matching scripts for |dbg|. */
4765 0 : ObjectQuery(JSContext* cx, Debugger* dbg) :
4766 0 : objects(cx), cx(cx), dbg(dbg), className(cx)
4767 0 : { }
4768 :
4769 : /* The vector that we are accumulating results in. */
4770 : AutoObjectVector objects;
4771 :
4772 : /*
4773 : * Parse the query object |query|, and prepare to match only the objects it
4774 : * specifies.
4775 : */
4776 0 : bool parseQuery(HandleObject query) {
4777 : /* Check for the 'class' property */
4778 0 : RootedValue cls(cx);
4779 0 : if (!GetProperty(cx, query, query, cx->names().class_, &cls))
4780 0 : return false;
4781 0 : if (!cls.isUndefined()) {
4782 0 : if (!cls.isString()) {
4783 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
4784 : "query object's 'class' property",
4785 0 : "neither undefined nor a string");
4786 0 : return false;
4787 : }
4788 0 : className = cls;
4789 : }
4790 0 : return true;
4791 : }
4792 :
4793 : /* Set up this ObjectQuery appropriately for a missing query argument. */
4794 0 : void omittedQuery() {
4795 0 : className.setUndefined();
4796 0 : }
4797 :
4798 : /*
4799 : * Traverse the heap to find all relevant objects and add them to the
4800 : * provided vector.
4801 : */
4802 0 : bool findObjects() {
4803 0 : if (!prepareQuery())
4804 0 : return false;
4805 :
4806 : {
4807 : /*
4808 : * We can't tolerate the GC moving things around while we're
4809 : * searching the heap. Check that nothing we do causes a GC.
4810 : */
4811 0 : Maybe<JS::AutoCheckCannotGC> maybeNoGC;
4812 0 : RootedObject dbgObj(cx, dbg->object);
4813 0 : JS::ubi::RootList rootList(cx, maybeNoGC);
4814 0 : if (!rootList.init(dbgObj)) {
4815 0 : ReportOutOfMemory(cx);
4816 0 : return false;
4817 : }
4818 :
4819 0 : Traversal traversal(cx, *this, maybeNoGC.ref());
4820 0 : if (!traversal.init()) {
4821 0 : ReportOutOfMemory(cx);
4822 0 : return false;
4823 : }
4824 0 : traversal.wantNames = false;
4825 :
4826 0 : return traversal.addStart(JS::ubi::Node(&rootList)) &&
4827 0 : traversal.traverse();
4828 : }
4829 : }
4830 :
4831 : /*
4832 : * |ubi::Node::BreadthFirst| interface.
4833 : */
4834 : class NodeData {};
4835 : typedef JS::ubi::BreadthFirst<ObjectQuery> Traversal;
4836 0 : bool operator() (Traversal& traversal, JS::ubi::Node origin, const JS::ubi::Edge& edge,
4837 : NodeData*, bool first)
4838 : {
4839 0 : if (!first)
4840 0 : return true;
4841 :
4842 0 : JS::ubi::Node referent = edge.referent;
4843 : /*
4844 : * Only follow edges within our set of debuggee compartments; we don't
4845 : * care about the heap's subgraphs outside of our debuggee compartments,
4846 : * so we abandon the referent. Either (1) there is not a path from this
4847 : * non-debuggee node back to a node in our debuggee compartments, and we
4848 : * don't need to follow edges to or from this node, or (2) there does
4849 : * exist some path from this non-debuggee node back to a node in our
4850 : * debuggee compartments. However, if that were true, then the incoming
4851 : * cross compartment edge back into a debuggee compartment is already
4852 : * listed as an edge in the RootList we started traversal with, and
4853 : * therefore we don't need to follow edges to or from this non-debuggee
4854 : * node.
4855 : */
4856 0 : JSCompartment* comp = referent.compartment();
4857 0 : if (comp && !dbg->isDebuggeeUnbarriered(comp)) {
4858 0 : traversal.abandonReferent();
4859 0 : return true;
4860 : }
4861 :
4862 : /*
4863 : * If the referent is an object and matches our query's restrictions,
4864 : * add it to the vector accumulating results. Skip objects that should
4865 : * never be exposed to JS, like EnvironmentObjects and internal
4866 : * functions.
4867 : */
4868 :
4869 0 : if (!referent.is<JSObject>() || referent.exposeToJS().isUndefined())
4870 0 : return true;
4871 :
4872 0 : JSObject* obj = referent.as<JSObject>();
4873 :
4874 0 : if (!className.isUndefined()) {
4875 0 : const char* objClassName = obj->getClass()->name;
4876 0 : if (strcmp(objClassName, classNameCString.ptr()) != 0)
4877 0 : return true;
4878 : }
4879 :
4880 0 : return objects.append(obj);
4881 : }
4882 :
4883 : private:
4884 : /* The context in which we should do our work. */
4885 : JSContext* cx;
4886 :
4887 : /* The debugger for which we conduct queries. */
4888 : Debugger* dbg;
4889 :
4890 : /*
4891 : * If this is non-null, matching objects will have a class whose name is
4892 : * this property.
4893 : */
4894 : RootedValue className;
4895 :
4896 : /* The className member, as a C string. */
4897 : JSAutoByteString classNameCString;
4898 :
4899 : /*
4900 : * Given that either omittedQuery or parseQuery has been called, prepare the
4901 : * query for matching objects.
4902 : */
4903 0 : bool prepareQuery() {
4904 0 : if (className.isString()) {
4905 0 : if (!classNameCString.encodeLatin1(cx, className.toString()))
4906 0 : return false;
4907 : }
4908 :
4909 0 : return true;
4910 : }
4911 : };
4912 :
4913 : bool
4914 0 : Debugger::findObjects(JSContext* cx, unsigned argc, Value* vp)
4915 : {
4916 0 : THIS_DEBUGGER(cx, argc, vp, "findObjects", args, dbg);
4917 :
4918 0 : ObjectQuery query(cx, dbg);
4919 :
4920 0 : if (args.length() >= 1) {
4921 0 : RootedObject queryObject(cx, NonNullObject(cx, args[0]));
4922 0 : if (!queryObject || !query.parseQuery(queryObject))
4923 0 : return false;
4924 : } else {
4925 0 : query.omittedQuery();
4926 : }
4927 :
4928 0 : if (!query.findObjects())
4929 0 : return false;
4930 :
4931 0 : size_t length = query.objects.length();
4932 0 : RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
4933 0 : if (!result)
4934 0 : return false;
4935 :
4936 0 : result->ensureDenseInitializedLength(cx, 0, length);
4937 :
4938 0 : for (size_t i = 0; i < length; i++) {
4939 0 : RootedValue debuggeeVal(cx, ObjectValue(*query.objects[i]));
4940 0 : if (!dbg->wrapDebuggeeValue(cx, &debuggeeVal))
4941 0 : return false;
4942 0 : result->setDenseElement(i, debuggeeVal);
4943 : }
4944 :
4945 0 : args.rval().setObject(*result);
4946 0 : return true;
4947 : }
4948 :
4949 : /* static */ bool
4950 0 : Debugger::findAllGlobals(JSContext* cx, unsigned argc, Value* vp)
4951 : {
4952 0 : THIS_DEBUGGER(cx, argc, vp, "findAllGlobals", args, dbg);
4953 :
4954 0 : AutoObjectVector globals(cx);
4955 :
4956 : {
4957 : // Accumulate the list of globals before wrapping them, because
4958 : // wrapping can GC and collect compartments from under us, while
4959 : // iterating.
4960 0 : JS::AutoCheckCannotGC nogc;
4961 :
4962 0 : for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
4963 0 : if (c->creationOptions().invisibleToDebugger())
4964 0 : continue;
4965 :
4966 0 : c->scheduledForDestruction = false;
4967 :
4968 0 : GlobalObject* global = c->maybeGlobal();
4969 :
4970 0 : if (cx->runtime()->isSelfHostingGlobal(global))
4971 0 : continue;
4972 :
4973 0 : if (global) {
4974 : /*
4975 : * We pulled |global| out of nowhere, so it's possible that it was
4976 : * marked gray by XPConnect. Since we're now exposing it to JS code,
4977 : * we need to mark it black.
4978 : */
4979 0 : JS::ExposeObjectToActiveJS(global);
4980 0 : if (!globals.append(global))
4981 0 : return false;
4982 : }
4983 : }
4984 : }
4985 :
4986 0 : RootedObject result(cx, NewDenseEmptyArray(cx));
4987 0 : if (!result)
4988 0 : return false;
4989 :
4990 0 : for (size_t i = 0; i < globals.length(); i++) {
4991 0 : RootedValue globalValue(cx, ObjectValue(*globals[i]));
4992 0 : if (!dbg->wrapDebuggeeValue(cx, &globalValue))
4993 0 : return false;
4994 0 : if (!NewbornArrayPush(cx, result, globalValue))
4995 0 : return false;
4996 : }
4997 :
4998 0 : args.rval().setObject(*result);
4999 0 : return true;
5000 : }
5001 :
5002 : /* static */ bool
5003 0 : Debugger::makeGlobalObjectReference(JSContext* cx, unsigned argc, Value* vp)
5004 : {
5005 0 : THIS_DEBUGGER(cx, argc, vp, "makeGlobalObjectReference", args, dbg);
5006 0 : if (!args.requireAtLeast(cx, "Debugger.makeGlobalObjectReference", 1))
5007 0 : return false;
5008 :
5009 0 : Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
5010 0 : if (!global)
5011 0 : return false;
5012 :
5013 : // If we create a D.O referring to a global in an invisible compartment,
5014 : // then from it we can reach function objects, scripts, environments, etc.,
5015 : // none of which we're ever supposed to see.
5016 0 : JSCompartment* globalCompartment = global->compartment();
5017 0 : if (globalCompartment->creationOptions().invisibleToDebugger()) {
5018 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5019 0 : JSMSG_DEBUG_INVISIBLE_COMPARTMENT);
5020 0 : return false;
5021 : }
5022 :
5023 0 : args.rval().setObject(*global);
5024 0 : return dbg->wrapDebuggeeValue(cx, args.rval());
5025 : }
5026 :
5027 : #ifdef JS_TRACE_LOGGING
5028 : static bool
5029 0 : DefineProperty(JSContext* cx, HandleObject obj, HandleId id, const char* value, size_t n)
5030 : {
5031 0 : JSString* text = JS_NewStringCopyN(cx, value, n);
5032 0 : if (!text)
5033 0 : return false;
5034 :
5035 0 : RootedValue str(cx, StringValue(text));
5036 0 : return JS_DefinePropertyById(cx, obj, id, str, JSPROP_ENUMERATE);
5037 : }
5038 :
5039 : # ifdef NIGHTLY_BUILD
5040 : bool
5041 0 : Debugger::setupTraceLogger(JSContext* cx, unsigned argc, Value* vp)
5042 : {
5043 0 : THIS_DEBUGGER(cx, argc, vp, "setupTraceLogger", args, dbg);
5044 0 : if (!args.requireAtLeast(cx, "Debugger.setupTraceLogger", 1))
5045 0 : return false;
5046 :
5047 0 : RootedObject obj(cx, ToObject(cx, args[0]));
5048 0 : if (!obj)
5049 0 : return false;
5050 :
5051 0 : AutoIdVector ids(cx);
5052 0 : if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &ids))
5053 0 : return false;
5054 :
5055 0 : if (ids.length() == 0) {
5056 0 : args.rval().setBoolean(true);
5057 0 : return true;
5058 : }
5059 :
5060 0 : Vector<uint32_t> textIds(cx);
5061 0 : if (!textIds.reserve(ids.length()))
5062 0 : return false;
5063 :
5064 0 : Vector<bool> values(cx);
5065 0 : if (!values.reserve(ids.length()))
5066 0 : return false;
5067 :
5068 0 : for (size_t i = 0; i < ids.length(); i++) {
5069 0 : if (!JSID_IS_STRING(ids[i])) {
5070 0 : args.rval().setBoolean(false);
5071 0 : return true;
5072 : }
5073 :
5074 0 : JSString* id = JSID_TO_STRING(ids[i]);
5075 0 : JSLinearString* linear = id->ensureLinear(cx);
5076 0 : if (!linear)
5077 0 : return false;
5078 :
5079 0 : uint32_t textId = TLStringToTextId(linear);
5080 :
5081 0 : if (!TLTextIdIsTogglable(textId)) {
5082 0 : args.rval().setBoolean(false);
5083 0 : return true;
5084 : }
5085 :
5086 0 : RootedValue v(cx);
5087 0 : if (!GetProperty(cx, obj, obj, ids[i], &v))
5088 0 : return false;
5089 :
5090 0 : textIds.infallibleAppend(textId);
5091 0 : values.infallibleAppend(ToBoolean(v));
5092 : }
5093 :
5094 0 : MOZ_ASSERT(ids.length() == textIds.length());
5095 0 : MOZ_ASSERT(textIds.length() == values.length());
5096 :
5097 0 : for (size_t i = 0; i < textIds.length(); i++) {
5098 0 : if (values[i])
5099 0 : TraceLogEnableTextId(cx, textIds[i]);
5100 : else
5101 0 : TraceLogDisableTextId(cx, textIds[i]);
5102 : }
5103 :
5104 0 : args.rval().setBoolean(true);
5105 0 : return true;
5106 : }
5107 :
5108 : bool
5109 0 : Debugger::drainTraceLogger(JSContext* cx, unsigned argc, Value* vp)
5110 : {
5111 0 : THIS_DEBUGGER(cx, argc, vp, "drainTraceLogger", args, dbg);
5112 :
5113 0 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
5114 0 : bool lostEvents = logger->lostEvents(dbg->traceLoggerLastDrainedIteration,
5115 0 : dbg->traceLoggerLastDrainedSize);
5116 :
5117 : size_t numEvents;
5118 0 : EventEntry* events = logger->getEventsStartingAt(&dbg->traceLoggerLastDrainedIteration,
5119 : &dbg->traceLoggerLastDrainedSize,
5120 0 : &numEvents);
5121 :
5122 0 : RootedObject array(cx, NewDenseEmptyArray(cx));
5123 0 : if (!array)
5124 0 : return false;
5125 :
5126 0 : JSAtom* dataAtom = Atomize(cx, "data", strlen("data"));
5127 0 : if (!dataAtom)
5128 0 : return false;
5129 :
5130 0 : RootedId dataId(cx, AtomToId(dataAtom));
5131 :
5132 : /* Add all events to the array. */
5133 0 : uint32_t index = 0;
5134 0 : for (EventEntry* eventItem = events; eventItem < events + numEvents; eventItem++, index++) {
5135 0 : RootedObject item(cx, NewObjectWithGivenProto(cx, &PlainObject::class_, nullptr));
5136 0 : if (!item)
5137 0 : return false;
5138 :
5139 0 : const char* eventText = logger->eventText(eventItem->textId);
5140 0 : if (!DefineProperty(cx, item, dataId, eventText, strlen(eventText)))
5141 0 : return false;
5142 :
5143 0 : RootedValue obj(cx, ObjectValue(*item));
5144 0 : if (!JS_DefineElement(cx, array, index, obj, JSPROP_ENUMERATE))
5145 0 : return false;
5146 : }
5147 :
5148 : /* Add "lostEvents" indicating if there are events that were lost. */
5149 0 : RootedValue lost(cx, BooleanValue(lostEvents));
5150 0 : if (!JS_DefineProperty(cx, array, "lostEvents", lost, JSPROP_ENUMERATE))
5151 0 : return false;
5152 :
5153 0 : args.rval().setObject(*array);
5154 :
5155 0 : return true;
5156 : }
5157 : # endif // NIGHTLY_BUILD
5158 :
5159 : bool
5160 0 : Debugger::setupTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp)
5161 : {
5162 0 : THIS_DEBUGGER(cx, argc, vp, "setupTraceLoggerScriptCalls", args, dbg);
5163 0 : if (!args.requireAtLeast(cx, "Debugger.setupTraceLoggerScriptCalls", 0))
5164 0 : return false;
5165 :
5166 0 : TraceLogEnableTextId(cx, TraceLogger_Scripts);
5167 0 : TraceLogEnableTextId(cx, TraceLogger_InlinedScripts);
5168 0 : TraceLogDisableTextId(cx, TraceLogger_AnnotateScripts);
5169 :
5170 0 : args.rval().setBoolean(true);
5171 :
5172 0 : return true;
5173 : }
5174 :
5175 : bool
5176 0 : Debugger::startTraceLogger(JSContext* cx, unsigned argc, Value* vp)
5177 : {
5178 0 : THIS_DEBUGGER(cx, argc, vp, "startTraceLogger", args, dbg);
5179 0 : if (!args.requireAtLeast(cx, "Debugger.startTraceLogger", 0))
5180 0 : return false;
5181 :
5182 0 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
5183 0 : if (!TraceLoggerEnable(logger, cx))
5184 0 : return false;
5185 :
5186 0 : args.rval().setUndefined();
5187 :
5188 0 : return true;
5189 : }
5190 :
5191 : bool
5192 0 : Debugger::endTraceLogger(JSContext* cx, unsigned argc, Value* vp)
5193 : {
5194 0 : THIS_DEBUGGER(cx, argc, vp, "endTraceLogger", args, dbg);
5195 0 : if (!args.requireAtLeast(cx, "Debugger.endTraceLogger", 0))
5196 0 : return false;
5197 :
5198 0 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
5199 0 : TraceLoggerDisable(logger);
5200 :
5201 0 : args.rval().setUndefined();
5202 :
5203 0 : return true;
5204 : }
5205 :
5206 : bool
5207 0 : Debugger::drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp)
5208 : {
5209 0 : THIS_DEBUGGER(cx, argc, vp, "drainTraceLoggerScriptCalls", args, dbg);
5210 :
5211 0 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
5212 0 : bool lostEvents = logger->lostEvents(dbg->traceLoggerScriptedCallsLastDrainedIteration,
5213 0 : dbg->traceLoggerScriptedCallsLastDrainedSize);
5214 :
5215 : size_t numEvents;
5216 0 : EventEntry* events = logger->getEventsStartingAt(
5217 : &dbg->traceLoggerScriptedCallsLastDrainedIteration,
5218 : &dbg->traceLoggerScriptedCallsLastDrainedSize,
5219 0 : &numEvents);
5220 :
5221 0 : RootedObject array(cx, NewDenseEmptyArray(cx));
5222 0 : if (!array)
5223 0 : return false;
5224 :
5225 0 : JSAtom* logTypeAtom = Atomize(cx, "logType", strlen("logType"));
5226 0 : if (!logTypeAtom)
5227 0 : return false;
5228 :
5229 0 : RootedId fileNameId(cx, AtomToId(cx->names().fileName));
5230 0 : RootedId lineNumberId(cx, AtomToId(cx->names().lineNumber));
5231 0 : RootedId columnNumberId(cx, AtomToId(cx->names().columnNumber));
5232 0 : RootedId logTypeId(cx, AtomToId(logTypeAtom));
5233 :
5234 : /* Add all events to the array. */
5235 0 : uint32_t index = 0;
5236 0 : for (EventEntry* eventItem = events; eventItem < events + numEvents; eventItem++) {
5237 0 : RootedObject item(cx, NewObjectWithGivenProto(cx, &PlainObject::class_, nullptr));
5238 0 : if (!item)
5239 0 : return false;
5240 :
5241 : // Filter out internal time.
5242 0 : uint32_t textId = eventItem->textId;
5243 0 : if (textId == TraceLogger_Internal) {
5244 0 : eventItem++;
5245 0 : MOZ_ASSERT(eventItem->textId == TraceLogger_Stop);
5246 0 : continue;
5247 : }
5248 :
5249 0 : if (textId != TraceLogger_Stop && !logger->textIdIsScriptEvent(textId))
5250 0 : continue;
5251 :
5252 0 : const char* type = (textId == TraceLogger_Stop) ? "Stop" : "Script";
5253 0 : if (!DefineProperty(cx, item, logTypeId, type, strlen(type)))
5254 0 : return false;
5255 :
5256 0 : if (textId != TraceLogger_Stop) {
5257 : const char* filename;
5258 : const char* lineno;
5259 : const char* colno;
5260 : size_t filename_len, lineno_len, colno_len;
5261 : logger->extractScriptDetails(textId, &filename, &filename_len, &lineno, &lineno_len,
5262 0 : &colno, &colno_len);
5263 :
5264 0 : if (!DefineProperty(cx, item, fileNameId, filename, filename_len))
5265 0 : return false;
5266 0 : if (!DefineProperty(cx, item, lineNumberId, lineno, lineno_len))
5267 0 : return false;
5268 0 : if (!DefineProperty(cx, item, columnNumberId, colno, colno_len))
5269 0 : return false;
5270 : }
5271 :
5272 0 : RootedValue obj(cx, ObjectValue(*item));
5273 0 : if (!JS_DefineElement(cx, array, index, obj, JSPROP_ENUMERATE))
5274 0 : return false;
5275 :
5276 0 : index++;
5277 : }
5278 :
5279 : /* Add "lostEvents" indicating if there are events that were lost. */
5280 0 : RootedValue lost(cx, BooleanValue(lostEvents));
5281 0 : if (!JS_DefineProperty(cx, array, "lostEvents", lost, JSPROP_ENUMERATE))
5282 0 : return false;
5283 :
5284 0 : args.rval().setObject(*array);
5285 :
5286 0 : return true;
5287 : }
5288 : #endif
5289 :
5290 : bool
5291 0 : Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp)
5292 : {
5293 0 : CallArgs args = CallArgsFromVp(argc, vp);
5294 :
5295 0 : if (!args.requireAtLeast(cx, "Debugger.isCompilableUnit", 1))
5296 0 : return false;
5297 :
5298 0 : if (!args[0].isString()) {
5299 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
5300 : "Debugger.isCompilableUnit", "string",
5301 0 : InformalValueTypeName(args[0]));
5302 0 : return false;
5303 : }
5304 :
5305 0 : JSString* str = args[0].toString();
5306 0 : size_t length = GetStringLength(str);
5307 :
5308 0 : AutoStableStringChars chars(cx);
5309 0 : if (!chars.initTwoByte(cx, str))
5310 0 : return false;
5311 :
5312 0 : bool result = true;
5313 :
5314 0 : CompileOptions options(cx);
5315 0 : frontend::UsedNameTracker usedNames(cx);
5316 0 : if (!usedNames.init())
5317 0 : return false;
5318 : frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
5319 : options, chars.twoByteChars(),
5320 : length,
5321 : /* foldConstants = */ true,
5322 0 : usedNames, nullptr, nullptr);
5323 0 : JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
5324 0 : if (!parser.checkOptions() || !parser.parse()) {
5325 : // We ran into an error. If it was because we ran out of memory we report
5326 : // it in the usual way.
5327 0 : if (cx->isThrowingOutOfMemory()) {
5328 0 : JS::SetWarningReporter(cx, older);
5329 0 : return false;
5330 : }
5331 :
5332 : // If it was because we ran out of source, we return false so our caller
5333 : // knows to try to collect more [source].
5334 0 : if (parser.isUnexpectedEOF())
5335 0 : result = false;
5336 :
5337 0 : cx->clearPendingException();
5338 : }
5339 0 : JS::SetWarningReporter(cx, older);
5340 0 : args.rval().setBoolean(result);
5341 0 : return true;
5342 : }
5343 :
5344 : bool
5345 0 : Debugger::adoptDebuggeeValue(JSContext* cx, unsigned argc, Value* vp)
5346 : {
5347 0 : THIS_DEBUGGER(cx, argc, vp, "adoptDebuggeeValue", args, dbg);
5348 0 : if (!args.requireAtLeast(cx, "Debugger.adoptDebuggeeValue", 1))
5349 0 : return false;
5350 :
5351 0 : RootedValue v(cx, args[0]);
5352 0 : if (v.isObject()) {
5353 0 : RootedObject obj(cx, &v.toObject());
5354 0 : NativeObject* ndobj = ToNativeDebuggerObject(cx, &obj);
5355 0 : if (!ndobj) {
5356 0 : return false;
5357 : }
5358 :
5359 0 : obj.set(static_cast<JSObject*>(ndobj->getPrivate()));
5360 0 : v = ObjectValue(*obj);
5361 :
5362 0 : if (!dbg->wrapDebuggeeValue(cx, &v)) {
5363 0 : return false;
5364 : }
5365 : }
5366 :
5367 0 : args.rval().set(v);
5368 0 : return true;
5369 : }
5370 :
5371 : const JSPropertySpec Debugger::properties[] = {
5372 : JS_PSGS("enabled", Debugger::getEnabled, Debugger::setEnabled, 0),
5373 : JS_PSGS("onDebuggerStatement", Debugger::getOnDebuggerStatement,
5374 : Debugger::setOnDebuggerStatement, 0),
5375 : JS_PSGS("onExceptionUnwind", Debugger::getOnExceptionUnwind,
5376 : Debugger::setOnExceptionUnwind, 0),
5377 : JS_PSGS("onNewScript", Debugger::getOnNewScript, Debugger::setOnNewScript, 0),
5378 : JS_PSGS("onNewPromise", Debugger::getOnNewPromise, Debugger::setOnNewPromise, 0),
5379 : JS_PSGS("onPromiseSettled", Debugger::getOnPromiseSettled, Debugger::setOnPromiseSettled, 0),
5380 : JS_PSGS("onEnterFrame", Debugger::getOnEnterFrame, Debugger::setOnEnterFrame, 0),
5381 : JS_PSGS("onNewGlobalObject", Debugger::getOnNewGlobalObject, Debugger::setOnNewGlobalObject, 0),
5382 : JS_PSGS("uncaughtExceptionHook", Debugger::getUncaughtExceptionHook,
5383 : Debugger::setUncaughtExceptionHook, 0),
5384 : JS_PSGS("allowUnobservedAsmJS", Debugger::getAllowUnobservedAsmJS,
5385 : Debugger::setAllowUnobservedAsmJS, 0),
5386 : JS_PSGS("allowWasmBinarySource", Debugger::getAllowWasmBinarySource,
5387 : Debugger::setAllowWasmBinarySource, 0),
5388 : JS_PSGS("collectCoverageInfo", Debugger::getCollectCoverageInfo,
5389 : Debugger::setCollectCoverageInfo, 0),
5390 : JS_PSG("memory", Debugger::getMemory, 0),
5391 : JS_PS_END
5392 : };
5393 :
5394 : const JSFunctionSpec Debugger::methods[] = {
5395 : JS_FN("addDebuggee", Debugger::addDebuggee, 1, 0),
5396 : JS_FN("addAllGlobalsAsDebuggees", Debugger::addAllGlobalsAsDebuggees, 0, 0),
5397 : JS_FN("removeDebuggee", Debugger::removeDebuggee, 1, 0),
5398 : JS_FN("removeAllDebuggees", Debugger::removeAllDebuggees, 0, 0),
5399 : JS_FN("hasDebuggee", Debugger::hasDebuggee, 1, 0),
5400 : JS_FN("getDebuggees", Debugger::getDebuggees, 0, 0),
5401 : JS_FN("getNewestFrame", Debugger::getNewestFrame, 0, 0),
5402 : JS_FN("clearAllBreakpoints", Debugger::clearAllBreakpoints, 0, 0),
5403 : JS_FN("findScripts", Debugger::findScripts, 1, 0),
5404 : JS_FN("findObjects", Debugger::findObjects, 1, 0),
5405 : JS_FN("findAllGlobals", Debugger::findAllGlobals, 0, 0),
5406 : JS_FN("makeGlobalObjectReference", Debugger::makeGlobalObjectReference, 1, 0),
5407 : #ifdef JS_TRACE_LOGGING
5408 : JS_FN("setupTraceLoggerScriptCalls", Debugger::setupTraceLoggerScriptCalls, 0, 0),
5409 : JS_FN("drainTraceLoggerScriptCalls", Debugger::drainTraceLoggerScriptCalls, 0, 0),
5410 : JS_FN("startTraceLogger", Debugger::startTraceLogger, 0, 0),
5411 : JS_FN("endTraceLogger", Debugger::endTraceLogger, 0, 0),
5412 : # ifdef NIGHTLY_BUILD
5413 : JS_FN("setupTraceLogger", Debugger::setupTraceLogger, 1, 0),
5414 : JS_FN("drainTraceLogger", Debugger::drainTraceLogger, 0, 0),
5415 : # endif
5416 : #endif
5417 : JS_FN("adoptDebuggeeValue", Debugger::adoptDebuggeeValue, 1, 0),
5418 : JS_FS_END
5419 : };
5420 :
5421 : const JSFunctionSpec Debugger::static_methods[] {
5422 : JS_FN("isCompilableUnit", Debugger::isCompilableUnit, 1, 0),
5423 : JS_FS_END
5424 : };
5425 :
5426 : /*** Debugger.Script *****************************************************************************/
5427 :
5428 : // Get the Debugger.Script referent as bare Cell. This should only be used for
5429 : // GC operations like tracing. Please use GetScriptReferent below.
5430 : static inline gc::Cell*
5431 0 : GetScriptReferentCell(JSObject* obj)
5432 : {
5433 0 : MOZ_ASSERT(obj->getClass() == &DebuggerScript_class);
5434 0 : return static_cast<gc::Cell*>(obj->as<NativeObject>().getPrivate());
5435 : }
5436 :
5437 : static inline DebuggerScriptReferent
5438 0 : GetScriptReferent(JSObject* obj)
5439 : {
5440 0 : MOZ_ASSERT(obj->getClass() == &DebuggerScript_class);
5441 0 : if (gc::Cell* cell = GetScriptReferentCell(obj)) {
5442 0 : if (cell->getTraceKind() == JS::TraceKind::Script)
5443 0 : return AsVariant(static_cast<JSScript*>(cell));
5444 0 : MOZ_ASSERT(cell->getTraceKind() == JS::TraceKind::Object);
5445 0 : return AsVariant(&static_cast<NativeObject*>(cell)->as<WasmInstanceObject>());
5446 : }
5447 0 : return AsVariant(static_cast<JSScript*>(nullptr));
5448 : }
5449 :
5450 : void
5451 0 : DebuggerScript_trace(JSTracer* trc, JSObject* obj)
5452 : {
5453 : /* This comes from a private pointer, so no barrier needed. */
5454 0 : gc::Cell* cell = GetScriptReferentCell(obj);
5455 0 : if (cell) {
5456 0 : if (cell->getTraceKind() == JS::TraceKind::Script) {
5457 0 : JSScript* script = static_cast<JSScript*>(cell);
5458 : TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &script,
5459 0 : "Debugger.Script script referent");
5460 0 : obj->as<NativeObject>().setPrivateUnbarriered(script);
5461 : } else {
5462 0 : JSObject* wasm = static_cast<JSObject*>(cell);
5463 : TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &wasm,
5464 0 : "Debugger.Script wasm referent");
5465 0 : MOZ_ASSERT(wasm->is<WasmInstanceObject>());
5466 0 : obj->as<NativeObject>().setPrivateUnbarriered(wasm);
5467 : }
5468 : }
5469 0 : }
5470 :
5471 : class DebuggerScriptSetPrivateMatcher
5472 : {
5473 : NativeObject* obj_;
5474 : public:
5475 0 : explicit DebuggerScriptSetPrivateMatcher(NativeObject* obj) : obj_(obj) { }
5476 : using ReturnType = void;
5477 0 : ReturnType match(HandleScript script) { obj_->setPrivateGCThing(script); }
5478 0 : ReturnType match(Handle<WasmInstanceObject*> instance) { obj_->setPrivateGCThing(instance); }
5479 : };
5480 :
5481 : NativeObject*
5482 0 : Debugger::newDebuggerScript(JSContext* cx, Handle<DebuggerScriptReferent> referent)
5483 : {
5484 0 : assertSameCompartment(cx, object.get());
5485 :
5486 0 : RootedObject proto(cx, &object->getReservedSlot(JSSLOT_DEBUG_SCRIPT_PROTO).toObject());
5487 0 : MOZ_ASSERT(proto);
5488 0 : NativeObject* scriptobj = NewNativeObjectWithGivenProto(cx, &DebuggerScript_class,
5489 0 : proto, TenuredObject);
5490 0 : if (!scriptobj)
5491 0 : return nullptr;
5492 0 : scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_OWNER, ObjectValue(*object));
5493 0 : DebuggerScriptSetPrivateMatcher matcher(scriptobj);
5494 0 : referent.match(matcher);
5495 :
5496 0 : return scriptobj;
5497 : }
5498 :
5499 : template <typename ReferentVariant, typename Referent, typename Map>
5500 : JSObject*
5501 0 : Debugger::wrapVariantReferent(JSContext* cx, Map& map, Handle<CrossCompartmentKey> key,
5502 : Handle<ReferentVariant> referent)
5503 : {
5504 0 : assertSameCompartment(cx, object);
5505 :
5506 0 : Handle<Referent> untaggedReferent = referent.template as<Referent>();
5507 0 : MOZ_ASSERT(cx->compartment() != untaggedReferent->compartment());
5508 :
5509 0 : DependentAddPtr<Map> p(cx, map, untaggedReferent);
5510 0 : if (!p) {
5511 0 : NativeObject* wrapper = newVariantWrapper(cx, referent);
5512 0 : if (!wrapper)
5513 0 : return nullptr;
5514 :
5515 0 : if (!p.add(cx, map, untaggedReferent, wrapper)) {
5516 0 : NukeDebuggerWrapper(wrapper);
5517 0 : return nullptr;
5518 : }
5519 :
5520 0 : if (!object->compartment()->putWrapper(cx, key, ObjectValue(*wrapper))) {
5521 0 : NukeDebuggerWrapper(wrapper);
5522 0 : map.remove(untaggedReferent);
5523 0 : ReportOutOfMemory(cx);
5524 0 : return nullptr;
5525 : }
5526 :
5527 : }
5528 :
5529 0 : return p->value();
5530 : }
5531 :
5532 : JSObject*
5533 0 : Debugger::wrapVariantReferent(JSContext* cx, Handle<DebuggerScriptReferent> referent)
5534 : {
5535 : JSObject* obj;
5536 0 : if (referent.is<JSScript*>()) {
5537 0 : Handle<JSScript*> untaggedReferent = referent.template as<JSScript*>();
5538 0 : Rooted<CrossCompartmentKey> key(cx, CrossCompartmentKey(object, untaggedReferent));
5539 0 : obj = wrapVariantReferent<DebuggerScriptReferent, JSScript*, ScriptWeakMap>(
5540 0 : cx, scripts, key, referent);
5541 : } else {
5542 0 : Handle<WasmInstanceObject*> untaggedReferent = referent.template as<WasmInstanceObject*>();
5543 0 : Rooted<CrossCompartmentKey> key(cx, CrossCompartmentKey(object, untaggedReferent,
5544 0 : CrossCompartmentKey::DebuggerObjectKind::DebuggerWasmScript));
5545 0 : obj = wrapVariantReferent<DebuggerScriptReferent, WasmInstanceObject*, WasmInstanceWeakMap>(
5546 0 : cx, wasmInstanceScripts, key, referent);
5547 : }
5548 0 : MOZ_ASSERT_IF(obj, GetScriptReferent(obj) == referent);
5549 0 : return obj;
5550 : }
5551 :
5552 : JSObject*
5553 0 : Debugger::wrapScript(JSContext* cx, HandleScript script)
5554 : {
5555 0 : Rooted<DebuggerScriptReferent> referent(cx, script.get());
5556 0 : return wrapVariantReferent(cx, referent);
5557 : }
5558 :
5559 : JSObject*
5560 0 : Debugger::wrapWasmScript(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance)
5561 : {
5562 0 : Rooted<DebuggerScriptReferent> referent(cx, wasmInstance.get());
5563 0 : return wrapVariantReferent(cx, referent);
5564 : }
5565 :
5566 : static JSObject*
5567 0 : DebuggerScript_check(JSContext* cx, const Value& v, const char* fnname)
5568 : {
5569 0 : JSObject* thisobj = NonNullObject(cx, v);
5570 0 : if (!thisobj)
5571 0 : return nullptr;
5572 0 : if (thisobj->getClass() != &DebuggerScript_class) {
5573 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
5574 0 : "Debugger.Script", fnname, thisobj->getClass()->name);
5575 0 : return nullptr;
5576 : }
5577 :
5578 : /*
5579 : * Check for Debugger.Script.prototype, which is of class DebuggerScript_class
5580 : * but whose script is null.
5581 : */
5582 0 : if (!GetScriptReferentCell(thisobj)) {
5583 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
5584 0 : "Debugger.Script", fnname, "prototype object");
5585 0 : return nullptr;
5586 : }
5587 :
5588 0 : return thisobj;
5589 : }
5590 :
5591 : template <typename ReferentT>
5592 : static JSObject*
5593 0 : DebuggerScript_checkThis(JSContext* cx, const CallArgs& args, const char* fnname,
5594 : const char* refname)
5595 : {
5596 0 : JSObject* thisobj = DebuggerScript_check(cx, args.thisv(), fnname);
5597 0 : if (!thisobj)
5598 0 : return nullptr;
5599 :
5600 0 : if (!GetScriptReferent(thisobj).is<ReferentT>()) {
5601 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT,
5602 : JSDVG_SEARCH_STACK, args.thisv(), nullptr,
5603 : refname, nullptr);
5604 0 : return nullptr;
5605 : }
5606 :
5607 0 : return thisobj;
5608 : }
5609 :
5610 : #define THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, fnname, args, obj, referent) \
5611 : CallArgs args = CallArgsFromVp(argc, vp); \
5612 : RootedObject obj(cx, DebuggerScript_check(cx, args.thisv(), fnname)); \
5613 : if (!obj) \
5614 : return false; \
5615 : Rooted<DebuggerScriptReferent> referent(cx, GetScriptReferent(obj))
5616 :
5617 : #define THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, fnname, args, obj, script) \
5618 : CallArgs args = CallArgsFromVp(argc, vp); \
5619 : RootedObject obj(cx, DebuggerScript_checkThis<JSScript*>(cx, args, fnname, \
5620 : "a JS script")); \
5621 : if (!obj) \
5622 : return false; \
5623 : RootedScript script(cx, GetScriptReferent(obj).as<JSScript*>())
5624 :
5625 : static bool
5626 0 : DebuggerScript_getDisplayName(JSContext* cx, unsigned argc, Value* vp)
5627 : {
5628 0 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get displayName)", args, obj, script);
5629 0 : Debugger* dbg = Debugger::fromChildJSObject(obj);
5630 :
5631 0 : JSFunction* func = script->functionNonDelazifying();
5632 0 : JSString* name = func ? func->displayAtom() : nullptr;
5633 0 : if (!name) {
5634 0 : args.rval().setUndefined();
5635 0 : return true;
5636 : }
5637 :
5638 0 : RootedValue namev(cx, StringValue(name));
5639 0 : if (!dbg->wrapDebuggeeValue(cx, &namev))
5640 0 : return false;
5641 0 : args.rval().set(namev);
5642 0 : return true;
5643 : }
5644 :
5645 : static bool
5646 0 : DebuggerScript_getUrl(JSContext* cx, unsigned argc, Value* vp)
5647 : {
5648 0 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get url)", args, obj, script);
5649 :
5650 0 : if (script->filename()) {
5651 : JSString* str;
5652 0 : if (script->scriptSource()->introducerFilename())
5653 0 : str = NewStringCopyZ<CanGC>(cx, script->scriptSource()->introducerFilename());
5654 : else
5655 0 : str = NewStringCopyZ<CanGC>(cx, script->filename());
5656 0 : if (!str)
5657 0 : return false;
5658 0 : args.rval().setString(str);
5659 : } else {
5660 0 : args.rval().setNull();
5661 : }
5662 0 : return true;
5663 : }
5664 :
5665 : struct DebuggerScriptGetStartLineMatcher
5666 : {
5667 : using ReturnType = uint32_t;
5668 :
5669 0 : ReturnType match(HandleScript script) {
5670 0 : return uint32_t(script->lineno());
5671 : }
5672 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
5673 0 : return 1;
5674 : }
5675 : };
5676 :
5677 : static bool
5678 0 : DebuggerScript_getStartLine(JSContext* cx, unsigned argc, Value* vp)
5679 : {
5680 0 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get startLine)", args, obj, referent);
5681 : DebuggerScriptGetStartLineMatcher matcher;
5682 0 : args.rval().setNumber(referent.match(matcher));
5683 0 : return true;
5684 : }
5685 :
5686 : struct DebuggerScriptGetLineCountMatcher
5687 : {
5688 : JSContext* cx_;
5689 : double totalLines;
5690 :
5691 0 : explicit DebuggerScriptGetLineCountMatcher(JSContext* cx) : cx_(cx) {}
5692 : using ReturnType = bool;
5693 :
5694 0 : ReturnType match(HandleScript script) {
5695 0 : totalLines = double(GetScriptLineExtent(script));
5696 0 : return true;
5697 : }
5698 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
5699 : uint32_t result;
5700 0 : if (!wasmInstance->instance().debug().totalSourceLines(cx_, &result))
5701 0 : return false;
5702 0 : totalLines = double(result);
5703 0 : return true;
5704 : }
5705 : };
5706 :
5707 : static bool
5708 0 : DebuggerScript_getLineCount(JSContext* cx, unsigned argc, Value* vp)
5709 : {
5710 0 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get lineCount)", args, obj, referent);
5711 0 : DebuggerScriptGetLineCountMatcher matcher(cx);
5712 0 : if (!referent.match(matcher))
5713 0 : return false;
5714 0 : args.rval().setNumber(matcher.totalLines);
5715 0 : return true;
5716 : }
5717 :
5718 : class DebuggerScriptGetSourceMatcher
5719 : {
5720 : JSContext* cx_;
5721 : Debugger* dbg_;
5722 :
5723 : public:
5724 0 : DebuggerScriptGetSourceMatcher(JSContext* cx, Debugger* dbg)
5725 0 : : cx_(cx), dbg_(dbg)
5726 0 : { }
5727 :
5728 : using ReturnType = JSObject*;
5729 :
5730 0 : ReturnType match(HandleScript script) {
5731 : RootedScriptSource source(cx_,
5732 0 : &UncheckedUnwrap(script->sourceObject())->as<ScriptSourceObject>());
5733 0 : return dbg_->wrapSource(cx_, source);
5734 : }
5735 :
5736 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
5737 0 : return dbg_->wrapWasmSource(cx_, wasmInstance);
5738 : }
5739 : };
5740 :
5741 : static bool
5742 0 : DebuggerScript_getSource(JSContext* cx, unsigned argc, Value* vp)
5743 : {
5744 0 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get source)", args, obj, referent);
5745 0 : Debugger* dbg = Debugger::fromChildJSObject(obj);
5746 :
5747 0 : DebuggerScriptGetSourceMatcher matcher(cx, dbg);
5748 0 : RootedObject sourceObject(cx, referent.match(matcher));
5749 0 : if (!sourceObject)
5750 0 : return false;
5751 :
5752 0 : args.rval().setObject(*sourceObject);
5753 0 : return true;
5754 : }
5755 :
5756 : static bool
5757 0 : DebuggerScript_getSourceStart(JSContext* cx, unsigned argc, Value* vp)
5758 : {
5759 0 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get sourceStart)", args, obj, script);
5760 0 : args.rval().setNumber(uint32_t(script->sourceStart()));
5761 0 : return true;
5762 : }
5763 :
5764 : static bool
5765 0 : DebuggerScript_getSourceLength(JSContext* cx, unsigned argc, Value* vp)
5766 : {
5767 0 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get sourceEnd)", args, obj, script);
5768 0 : args.rval().setNumber(uint32_t(script->sourceEnd() - script->sourceStart()));
5769 0 : return true;
5770 : }
5771 :
5772 : static bool
5773 0 : DebuggerScript_getGlobal(JSContext* cx, unsigned argc, Value* vp)
5774 : {
5775 0 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get global)", args, obj, script);
5776 0 : Debugger* dbg = Debugger::fromChildJSObject(obj);
5777 :
5778 0 : RootedValue v(cx, ObjectValue(script->global()));
5779 0 : if (!dbg->wrapDebuggeeValue(cx, &v))
5780 0 : return false;
5781 0 : args.rval().set(v);
5782 0 : return true;
5783 : }
5784 :
5785 : class DebuggerScriptGetFormatMatcher
5786 : {
5787 : const JSAtomState& names_;
5788 : public:
5789 0 : explicit DebuggerScriptGetFormatMatcher(const JSAtomState& names) : names_(names) { }
5790 : using ReturnType = JSAtom*;
5791 0 : ReturnType match(HandleScript script) { return names_.js; }
5792 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return names_.wasm; }
5793 : };
5794 :
5795 : static bool
5796 0 : DebuggerScript_getFormat(JSContext* cx, unsigned argc, Value* vp)
5797 : {
5798 0 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get format)", args, obj, referent);
5799 0 : DebuggerScriptGetFormatMatcher matcher(cx->names());
5800 0 : args.rval().setString(referent.match(matcher));
5801 0 : return true;
5802 : }
5803 :
5804 : static bool
5805 0 : DebuggerScript_getChildScripts(JSContext* cx, unsigned argc, Value* vp)
5806 : {
5807 0 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getChildScripts", args, obj, script);
5808 0 : Debugger* dbg = Debugger::fromChildJSObject(obj);
5809 :
5810 0 : RootedObject result(cx, NewDenseEmptyArray(cx));
5811 0 : if (!result)
5812 0 : return false;
5813 0 : if (script->hasObjects()) {
5814 : /*
5815 : * script->savedCallerFun indicates that this is a direct eval script
5816 : * and the calling function is stored as script->objects()->vector[0].
5817 : * It is not really a child script of this script, so skip it using
5818 : * innerObjectsStart().
5819 : */
5820 0 : ObjectArray* objects = script->objects();
5821 0 : RootedFunction fun(cx);
5822 0 : RootedScript funScript(cx);
5823 0 : RootedObject obj(cx), s(cx);
5824 0 : for (uint32_t i = 0; i < objects->length; i++) {
5825 0 : obj = objects->vector[i];
5826 0 : if (obj->is<JSFunction>()) {
5827 0 : fun = &obj->as<JSFunction>();
5828 : // The inner function could be a wasm native.
5829 0 : if (fun->isNative())
5830 0 : continue;
5831 0 : funScript = GetOrCreateFunctionScript(cx, fun);
5832 0 : if (!funScript)
5833 0 : return false;
5834 0 : s = dbg->wrapScript(cx, funScript);
5835 0 : if (!s || !NewbornArrayPush(cx, result, ObjectValue(*s)))
5836 0 : return false;
5837 : }
5838 : }
5839 : }
5840 0 : args.rval().setObject(*result);
5841 0 : return true;
5842 : }
5843 :
5844 : static bool
5845 0 : ScriptOffset(JSContext* cx, const Value& v, size_t* offsetp)
5846 : {
5847 : double d;
5848 : size_t off;
5849 :
5850 0 : bool ok = v.isNumber();
5851 0 : if (ok) {
5852 0 : d = v.toNumber();
5853 0 : off = size_t(d);
5854 : }
5855 0 : if (!ok || off != d) {
5856 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
5857 0 : return false;
5858 : }
5859 0 : *offsetp = off;
5860 0 : return true;
5861 : }
5862 :
5863 : static bool
5864 0 : EnsureScriptOffsetIsValid(JSContext* cx, JSScript* script, size_t offset)
5865 : {
5866 0 : if (IsValidBytecodeOffset(cx, script, offset))
5867 0 : return true;
5868 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
5869 0 : return false;
5870 : }
5871 :
5872 : namespace {
5873 :
5874 0 : class BytecodeRangeWithPosition : private BytecodeRange
5875 : {
5876 : public:
5877 : using BytecodeRange::empty;
5878 : using BytecodeRange::frontPC;
5879 : using BytecodeRange::frontOpcode;
5880 : using BytecodeRange::frontOffset;
5881 :
5882 0 : BytecodeRangeWithPosition(JSContext* cx, JSScript* script)
5883 0 : : BytecodeRange(cx, script), lineno(script->lineno()), column(0),
5884 0 : sn(script->notes()), snpc(script->code()), isEntryPoint(false),
5885 0 : wasArtifactEntryPoint(false)
5886 : {
5887 0 : if (!SN_IS_TERMINATOR(sn))
5888 0 : snpc += SN_DELTA(sn);
5889 0 : updatePosition();
5890 0 : while (frontPC() != script->main())
5891 0 : popFront();
5892 :
5893 0 : if (frontOpcode() != JSOP_JUMPTARGET)
5894 0 : isEntryPoint = true;
5895 : else
5896 0 : wasArtifactEntryPoint = true;
5897 0 : }
5898 :
5899 0 : void popFront() {
5900 0 : BytecodeRange::popFront();
5901 0 : if (empty())
5902 0 : isEntryPoint = false;
5903 : else
5904 0 : updatePosition();
5905 :
5906 : // The following conditions are handling artifacts introduced by the
5907 : // bytecode emitter, such that we do not add breakpoints on empty
5908 : // statements of the source code of the user.
5909 0 : if (wasArtifactEntryPoint) {
5910 0 : wasArtifactEntryPoint = false;
5911 0 : isEntryPoint = true;
5912 : }
5913 :
5914 0 : if (isEntryPoint && frontOpcode() == JSOP_JUMPTARGET) {
5915 0 : wasArtifactEntryPoint = isEntryPoint;
5916 0 : isEntryPoint = false;
5917 : }
5918 0 : }
5919 :
5920 0 : size_t frontLineNumber() const { return lineno; }
5921 0 : size_t frontColumnNumber() const { return column; }
5922 :
5923 : // Entry points are restricted to bytecode offsets that have an
5924 : // explicit mention in the line table. This restriction avoids a
5925 : // number of failing cases caused by some instructions not having
5926 : // sensible (to the user) line numbers, and it is one way to
5927 : // implement the idea that the bytecode emitter should tell the
5928 : // debugger exactly which offsets represent "interesting" (to the
5929 : // user) places to stop.
5930 0 : bool frontIsEntryPoint() const { return isEntryPoint; }
5931 :
5932 : private:
5933 0 : void updatePosition() {
5934 : /*
5935 : * Determine the current line number by reading all source notes up to
5936 : * and including the current offset.
5937 : */
5938 0 : jsbytecode *lastLinePC = nullptr;
5939 0 : while (!SN_IS_TERMINATOR(sn) && snpc <= frontPC()) {
5940 0 : SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
5941 0 : if (type == SRC_COLSPAN) {
5942 0 : ptrdiff_t colspan = SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, 0));
5943 0 : MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
5944 0 : column += colspan;
5945 0 : lastLinePC = snpc;
5946 0 : } else if (type == SRC_SETLINE) {
5947 0 : lineno = size_t(GetSrcNoteOffset(sn, 0));
5948 0 : column = 0;
5949 0 : lastLinePC = snpc;
5950 0 : } else if (type == SRC_NEWLINE) {
5951 0 : lineno++;
5952 0 : column = 0;
5953 0 : lastLinePC = snpc;
5954 : }
5955 :
5956 0 : sn = SN_NEXT(sn);
5957 0 : snpc += SN_DELTA(sn);
5958 : }
5959 0 : isEntryPoint = lastLinePC == frontPC();
5960 0 : }
5961 :
5962 : size_t lineno;
5963 : size_t column;
5964 : jssrcnote* sn;
5965 : jsbytecode* snpc;
5966 : bool isEntryPoint;
5967 : bool wasArtifactEntryPoint;
5968 : };
5969 :
5970 : /*
5971 : * FlowGraphSummary::populate(cx, script) computes a summary of script's
5972 : * control flow graph used by DebuggerScript_{getAllOffsets,getLineOffsets}.
5973 : *
5974 : * An instruction on a given line is an entry point for that line if it can be
5975 : * reached from (an instruction on) a different line. We distinguish between the
5976 : * following cases:
5977 : * - hasNoEdges:
5978 : * The instruction cannot be reached, so the instruction is not an entry
5979 : * point for the line it is on.
5980 : * - hasSingleEdge:
5981 : * - hasMultipleEdgesFromSingleLine:
5982 : * The instruction can be reached from a single line. If this line is
5983 : * different from the line the instruction is on, the instruction is an
5984 : * entry point for that line.
5985 : * - hasMultipleEdgesFromMultipleLines:
5986 : * The instruction can be reached from multiple lines. At least one of
5987 : * these lines is guaranteed to be different from the line the instruction
5988 : * is on, so the instruction is an entry point for that line.
5989 : *
5990 : * Similarly, an instruction on a given position (line/column pair) is an
5991 : * entry point for that position if it can be reached from (an instruction on) a
5992 : * different position. Again, we distinguish between the following cases:
5993 : * - hasNoEdges:
5994 : * The instruction cannot be reached, so the instruction is not an entry
5995 : * point for the position it is on.
5996 : * - hasSingleEdge:
5997 : * The instruction can be reached from a single position. If this line is
5998 : * different from the position the instruction is on, the instruction is
5999 : * an entry point for that position.
6000 : * - hasMultipleEdgesFromSingleLine:
6001 : * - hasMultipleEdgesFromMultipleLines:
6002 : * The instruction can be reached from multiple positions. At least one
6003 : * of these positions is guaranteed to be different from the position the
6004 : * instruction is on, so the instruction is an entry point for that
6005 : * position.
6006 : */
6007 0 : class FlowGraphSummary {
6008 : public:
6009 : class Entry {
6010 : public:
6011 : static Entry createWithNoEdges() {
6012 : return Entry(SIZE_MAX, 0);
6013 : }
6014 :
6015 0 : static Entry createWithSingleEdge(size_t lineno, size_t column) {
6016 0 : return Entry(lineno, column);
6017 : }
6018 :
6019 0 : static Entry createWithMultipleEdgesFromSingleLine(size_t lineno) {
6020 0 : return Entry(lineno, SIZE_MAX);
6021 : }
6022 :
6023 0 : static Entry createWithMultipleEdgesFromMultipleLines() {
6024 0 : return Entry(SIZE_MAX, SIZE_MAX);
6025 : }
6026 :
6027 0 : Entry() : lineno_(SIZE_MAX), column_(0) {}
6028 :
6029 0 : bool hasNoEdges() const {
6030 0 : return lineno_ == SIZE_MAX && column_ != SIZE_MAX;
6031 : }
6032 :
6033 0 : bool hasSingleEdge() const {
6034 0 : return lineno_ != SIZE_MAX && column_ != SIZE_MAX;
6035 : }
6036 :
6037 : bool hasMultipleEdgesFromSingleLine() const {
6038 : return lineno_ != SIZE_MAX && column_ == SIZE_MAX;
6039 : }
6040 :
6041 : bool hasMultipleEdgesFromMultipleLines() const {
6042 : return lineno_ == SIZE_MAX && column_ == SIZE_MAX;
6043 : }
6044 :
6045 : bool operator==(const Entry& other) const {
6046 : return lineno_ == other.lineno_ && column_ == other.column_;
6047 : }
6048 :
6049 : bool operator!=(const Entry& other) const {
6050 : return lineno_ != other.lineno_ || column_ != other.column_;
6051 : }
6052 :
6053 0 : size_t lineno() const {
6054 0 : return lineno_;
6055 : }
6056 :
6057 0 : size_t column() const {
6058 0 : return column_;
6059 : }
6060 :
6061 : private:
6062 0 : Entry(size_t lineno, size_t column) : lineno_(lineno), column_(column) {}
6063 :
6064 : size_t lineno_;
6065 : size_t column_;
6066 : };
6067 :
6068 0 : explicit FlowGraphSummary(JSContext* cx) : entries_(cx) {}
6069 :
6070 0 : Entry& operator[](size_t index) {
6071 0 : return entries_[index];
6072 : }
6073 :
6074 0 : bool populate(JSContext* cx, JSScript* script) {
6075 0 : if (!entries_.growBy(script->length()))
6076 0 : return false;
6077 0 : unsigned mainOffset = script->pcToOffset(script->main());
6078 0 : entries_[mainOffset] = Entry::createWithMultipleEdgesFromMultipleLines();
6079 :
6080 0 : size_t prevLineno = script->lineno();
6081 0 : size_t prevColumn = 0;
6082 0 : JSOp prevOp = JSOP_NOP;
6083 0 : for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) {
6084 0 : size_t lineno = prevLineno;
6085 0 : size_t column = prevColumn;
6086 0 : JSOp op = r.frontOpcode();
6087 :
6088 0 : if (FlowsIntoNext(prevOp))
6089 0 : addEdge(prevLineno, prevColumn, r.frontOffset());
6090 :
6091 : // If we visit the branch target before we visit the
6092 : // branch op itself, just reuse the previous location.
6093 : // This is reasonable for the time being because this
6094 : // situation can currently only arise from loop heads,
6095 : // where this assumption holds.
6096 0 : if (BytecodeIsJumpTarget(op) && !entries_[r.frontOffset()].hasNoEdges()) {
6097 0 : lineno = entries_[r.frontOffset()].lineno();
6098 0 : column = entries_[r.frontOffset()].column();
6099 : }
6100 :
6101 0 : if (r.frontIsEntryPoint()) {
6102 0 : lineno = r.frontLineNumber();
6103 0 : column = r.frontColumnNumber();
6104 : }
6105 :
6106 0 : if (CodeSpec[op].type() == JOF_JUMP) {
6107 0 : addEdge(lineno, column, r.frontOffset() + GET_JUMP_OFFSET(r.frontPC()));
6108 0 : } else if (op == JSOP_TABLESWITCH) {
6109 0 : jsbytecode* pc = r.frontPC();
6110 0 : size_t offset = r.frontOffset();
6111 0 : ptrdiff_t step = JUMP_OFFSET_LEN;
6112 0 : size_t defaultOffset = offset + GET_JUMP_OFFSET(pc);
6113 0 : pc += step;
6114 0 : addEdge(lineno, column, defaultOffset);
6115 :
6116 0 : int32_t low = GET_JUMP_OFFSET(pc);
6117 0 : pc += JUMP_OFFSET_LEN;
6118 0 : int ncases = GET_JUMP_OFFSET(pc) - low + 1;
6119 0 : pc += JUMP_OFFSET_LEN;
6120 :
6121 0 : for (int i = 0; i < ncases; i++) {
6122 0 : size_t target = offset + GET_JUMP_OFFSET(pc);
6123 0 : addEdge(lineno, column, target);
6124 0 : pc += step;
6125 : }
6126 0 : } else if (op == JSOP_TRY) {
6127 : // As there is no literal incoming edge into the catch block, we
6128 : // make a fake one by copying the JSOP_TRY location, as-if this
6129 : // was an incoming edge of the catch block. This is needed
6130 : // because we only report offsets of entry points which have
6131 : // valid incoming edges.
6132 0 : JSTryNote* tn = script->trynotes()->vector;
6133 0 : JSTryNote* tnlimit = tn + script->trynotes()->length;
6134 0 : for (; tn < tnlimit; tn++) {
6135 0 : uint32_t startOffset = script->mainOffset() + tn->start;
6136 0 : if (startOffset == r.frontOffset() + 1) {
6137 0 : uint32_t catchOffset = startOffset + tn->length;
6138 0 : if (tn->kind == JSTRY_CATCH || tn->kind == JSTRY_FINALLY)
6139 0 : addEdge(lineno, column, catchOffset);
6140 : }
6141 : }
6142 : }
6143 :
6144 0 : prevLineno = lineno;
6145 0 : prevColumn = column;
6146 0 : prevOp = op;
6147 : }
6148 :
6149 0 : return true;
6150 : }
6151 :
6152 : private:
6153 0 : void addEdge(size_t sourceLineno, size_t sourceColumn, size_t targetOffset) {
6154 0 : if (entries_[targetOffset].hasNoEdges())
6155 0 : entries_[targetOffset] = Entry::createWithSingleEdge(sourceLineno, sourceColumn);
6156 0 : else if (entries_[targetOffset].lineno() != sourceLineno)
6157 0 : entries_[targetOffset] = Entry::createWithMultipleEdgesFromMultipleLines();
6158 0 : else if (entries_[targetOffset].column() != sourceColumn)
6159 0 : entries_[targetOffset] = Entry::createWithMultipleEdgesFromSingleLine(sourceLineno);
6160 0 : }
6161 :
6162 : Vector<Entry> entries_;
6163 : };
6164 :
6165 : } /* anonymous namespace */
6166 :
6167 : class DebuggerScriptGetOffsetLocationMatcher
6168 : {
6169 : JSContext* cx_;
6170 : size_t offset_;
6171 : MutableHandlePlainObject result_;
6172 :
6173 : public:
6174 0 : explicit DebuggerScriptGetOffsetLocationMatcher(JSContext* cx, size_t offset,
6175 : MutableHandlePlainObject result)
6176 0 : : cx_(cx), offset_(offset), result_(result) { }
6177 : using ReturnType = bool;
6178 0 : ReturnType match(HandleScript script) {
6179 0 : if (!EnsureScriptOffsetIsValid(cx_, script, offset_))
6180 0 : return false;
6181 :
6182 0 : FlowGraphSummary flowData(cx_);
6183 0 : if (!flowData.populate(cx_, script))
6184 0 : return false;
6185 :
6186 0 : result_.set(NewBuiltinClassInstance<PlainObject>(cx_));
6187 0 : if (!result_)
6188 0 : return false;
6189 :
6190 0 : BytecodeRangeWithPosition r(cx_, script);
6191 0 : while (!r.empty() && r.frontOffset() < offset_)
6192 0 : r.popFront();
6193 :
6194 0 : size_t offset = r.frontOffset();
6195 0 : bool isEntryPoint = r.frontIsEntryPoint();
6196 :
6197 : // Line numbers are only correctly defined on entry points. Thus looks
6198 : // either for the next valid offset in the flowData, being the last entry
6199 : // point flowing into the current offset, or for the next valid entry point.
6200 0 : while (!r.frontIsEntryPoint() && !flowData[r.frontOffset()].hasSingleEdge()) {
6201 0 : r.popFront();
6202 0 : MOZ_ASSERT(!r.empty());
6203 : }
6204 :
6205 : // If this is an entry point, take the line number associated with the entry
6206 : // point, otherwise settle on the next instruction and take the incoming
6207 : // edge position.
6208 : size_t lineno;
6209 : size_t column;
6210 0 : if (r.frontIsEntryPoint()) {
6211 0 : lineno = r.frontLineNumber();
6212 0 : column = r.frontColumnNumber();
6213 : } else {
6214 0 : MOZ_ASSERT(flowData[r.frontOffset()].hasSingleEdge());
6215 0 : lineno = flowData[r.frontOffset()].lineno();
6216 0 : column = flowData[r.frontOffset()].column();
6217 : }
6218 :
6219 0 : RootedId id(cx_, NameToId(cx_->names().lineNumber));
6220 0 : RootedValue value(cx_, NumberValue(lineno));
6221 0 : if (!DefineProperty(cx_, result_, id, value))
6222 0 : return false;
6223 :
6224 0 : value = NumberValue(column);
6225 0 : if (!DefineProperty(cx_, result_, cx_->names().columnNumber, value))
6226 0 : return false;
6227 :
6228 : // The same entry point test that is used by getAllColumnOffsets.
6229 0 : isEntryPoint = (isEntryPoint &&
6230 0 : !flowData[offset].hasNoEdges() &&
6231 0 : (flowData[offset].lineno() != r.frontLineNumber() ||
6232 0 : flowData[offset].column() != r.frontColumnNumber()));
6233 0 : value.setBoolean(isEntryPoint);
6234 0 : if (!DefineProperty(cx_, result_, cx_->names().isEntryPoint, value))
6235 0 : return false;
6236 :
6237 0 : return true;
6238 : }
6239 :
6240 0 : ReturnType match(Handle<WasmInstanceObject*> instance) {
6241 : size_t lineno;
6242 : size_t column;
6243 : bool found;
6244 0 : if (!instance->instance().debug().getOffsetLocation(cx_, offset_, &found, &lineno, &column))
6245 0 : return false;
6246 :
6247 0 : if (!found) {
6248 0 : JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
6249 0 : return false;
6250 : }
6251 :
6252 0 : result_.set(NewBuiltinClassInstance<PlainObject>(cx_));
6253 0 : if (!result_)
6254 0 : return false;
6255 :
6256 0 : RootedId id(cx_, NameToId(cx_->names().lineNumber));
6257 0 : RootedValue value(cx_, NumberValue(lineno));
6258 0 : if (!DefineProperty(cx_, result_, id, value))
6259 0 : return false;
6260 :
6261 0 : value = NumberValue(column);
6262 0 : if (!DefineProperty(cx_, result_, cx_->names().columnNumber, value))
6263 0 : return false;
6264 :
6265 0 : value.setBoolean(true);
6266 0 : if (!DefineProperty(cx_, result_, cx_->names().isEntryPoint, value))
6267 0 : return false;
6268 :
6269 0 : return true;
6270 : }
6271 : };
6272 :
6273 : static bool
6274 0 : DebuggerScript_getOffsetLocation(JSContext* cx, unsigned argc, Value* vp)
6275 : {
6276 0 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getOffsetLocation", args, obj, referent);
6277 0 : if (!args.requireAtLeast(cx, "Debugger.Script.getOffsetLocation", 1))
6278 0 : return false;
6279 : size_t offset;
6280 0 : if (!ScriptOffset(cx, args[0], &offset))
6281 0 : return false;
6282 :
6283 0 : RootedPlainObject result(cx);
6284 0 : DebuggerScriptGetOffsetLocationMatcher matcher(cx, offset, &result);
6285 0 : if (!referent.match(matcher))
6286 0 : return false;
6287 :
6288 0 : args.rval().setObject(*result);
6289 0 : return true;
6290 : }
6291 :
6292 : static bool
6293 0 : DebuggerScript_getAllOffsets(JSContext* cx, unsigned argc, Value* vp)
6294 : {
6295 0 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getAllOffsets", args, obj, script);
6296 :
6297 : /*
6298 : * First pass: determine which offsets in this script are jump targets and
6299 : * which line numbers jump to them.
6300 : */
6301 0 : FlowGraphSummary flowData(cx);
6302 0 : if (!flowData.populate(cx, script))
6303 0 : return false;
6304 :
6305 : /* Second pass: build the result array. */
6306 0 : RootedObject result(cx, NewDenseEmptyArray(cx));
6307 0 : if (!result)
6308 0 : return false;
6309 0 : for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) {
6310 0 : if (!r.frontIsEntryPoint())
6311 0 : continue;
6312 :
6313 0 : size_t offset = r.frontOffset();
6314 0 : size_t lineno = r.frontLineNumber();
6315 :
6316 : /* Make a note, if the current instruction is an entry point for the current line. */
6317 0 : if (!flowData[offset].hasNoEdges() && flowData[offset].lineno() != lineno) {
6318 : /* Get the offsets array for this line. */
6319 0 : RootedObject offsets(cx);
6320 0 : RootedValue offsetsv(cx);
6321 :
6322 0 : RootedId id(cx, INT_TO_JSID(lineno));
6323 :
6324 : bool found;
6325 0 : if (!HasOwnProperty(cx, result, id, &found))
6326 0 : return false;
6327 0 : if (found && !GetProperty(cx, result, result, id, &offsetsv))
6328 0 : return false;
6329 :
6330 0 : if (offsetsv.isObject()) {
6331 0 : offsets = &offsetsv.toObject();
6332 : } else {
6333 0 : MOZ_ASSERT(offsetsv.isUndefined());
6334 :
6335 : /*
6336 : * Create an empty offsets array for this line.
6337 : * Store it in the result array.
6338 : */
6339 0 : RootedId id(cx);
6340 0 : RootedValue v(cx, NumberValue(lineno));
6341 0 : offsets = NewDenseEmptyArray(cx);
6342 0 : if (!offsets ||
6343 0 : !ValueToId<CanGC>(cx, v, &id))
6344 : {
6345 0 : return false;
6346 : }
6347 :
6348 0 : RootedValue value(cx, ObjectValue(*offsets));
6349 0 : if (!DefineProperty(cx, result, id, value))
6350 0 : return false;
6351 : }
6352 :
6353 : /* Append the current offset to the offsets array. */
6354 0 : if (!NewbornArrayPush(cx, offsets, NumberValue(offset)))
6355 0 : return false;
6356 : }
6357 : }
6358 :
6359 0 : args.rval().setObject(*result);
6360 0 : return true;
6361 : }
6362 :
6363 : class DebuggerScriptGetAllColumnOffsetsMatcher
6364 : {
6365 : JSContext* cx_;
6366 : MutableHandleObject result_;
6367 :
6368 0 : bool appendColumnOffsetEntry(size_t lineno, size_t column, size_t offset) {
6369 0 : RootedPlainObject entry(cx_, NewBuiltinClassInstance<PlainObject>(cx_));
6370 0 : if (!entry)
6371 0 : return false;
6372 :
6373 0 : RootedId id(cx_, NameToId(cx_->names().lineNumber));
6374 0 : RootedValue value(cx_, NumberValue(lineno));
6375 0 : if (!DefineProperty(cx_, entry, id, value))
6376 0 : return false;
6377 :
6378 0 : value = NumberValue(column);
6379 0 : if (!DefineProperty(cx_, entry, cx_->names().columnNumber, value))
6380 0 : return false;
6381 :
6382 0 : id = NameToId(cx_->names().offset);
6383 0 : value = NumberValue(offset);
6384 0 : if (!DefineProperty(cx_, entry, id, value))
6385 0 : return false;
6386 :
6387 0 : return NewbornArrayPush(cx_, result_, ObjectValue(*entry));
6388 : }
6389 :
6390 : public:
6391 0 : explicit DebuggerScriptGetAllColumnOffsetsMatcher(JSContext* cx, MutableHandleObject result)
6392 0 : : cx_(cx), result_(result) { }
6393 : using ReturnType = bool;
6394 0 : ReturnType match(HandleScript script) {
6395 : /*
6396 : * First pass: determine which offsets in this script are jump targets
6397 : * and which positions jump to them.
6398 : */
6399 0 : FlowGraphSummary flowData(cx_);
6400 0 : if (!flowData.populate(cx_, script))
6401 0 : return false;
6402 :
6403 : /* Second pass: build the result array. */
6404 0 : result_.set(NewDenseEmptyArray(cx_));
6405 0 : if (!result_)
6406 0 : return false;
6407 :
6408 0 : for (BytecodeRangeWithPosition r(cx_, script); !r.empty(); r.popFront()) {
6409 0 : size_t lineno = r.frontLineNumber();
6410 0 : size_t column = r.frontColumnNumber();
6411 0 : size_t offset = r.frontOffset();
6412 :
6413 : /*
6414 : * Make a note, if the current instruction is an entry point for
6415 : * the current position.
6416 : */
6417 0 : if (r.frontIsEntryPoint() &&
6418 0 : !flowData[offset].hasNoEdges() &&
6419 0 : (flowData[offset].lineno() != lineno ||
6420 0 : flowData[offset].column() != column)) {
6421 0 : if (!appendColumnOffsetEntry(lineno, column, offset))
6422 0 : return false;
6423 : }
6424 : }
6425 0 : return true;
6426 : }
6427 :
6428 0 : ReturnType match(Handle<WasmInstanceObject*> instance) {
6429 0 : Vector<wasm::ExprLoc> offsets(cx_);
6430 0 : if (!instance->instance().debug().getAllColumnOffsets(cx_, &offsets))
6431 0 : return false;
6432 :
6433 0 : result_.set(NewDenseEmptyArray(cx_));
6434 0 : if (!result_)
6435 0 : return false;
6436 :
6437 0 : for (uint32_t i = 0; i < offsets.length(); i++) {
6438 0 : size_t lineno = offsets[i].lineno;
6439 0 : size_t column = offsets[i].column;
6440 0 : size_t offset = offsets[i].offset;
6441 0 : if (!appendColumnOffsetEntry(lineno, column, offset))
6442 0 : return false;
6443 : }
6444 0 : return true;
6445 : }
6446 : };
6447 :
6448 : static bool
6449 0 : DebuggerScript_getAllColumnOffsets(JSContext* cx, unsigned argc, Value* vp)
6450 : {
6451 0 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getAllColumnOffsets", args, obj, referent);
6452 :
6453 0 : RootedObject result(cx);
6454 0 : DebuggerScriptGetAllColumnOffsetsMatcher matcher(cx, &result);
6455 0 : if (!referent.match(matcher))
6456 0 : return false;
6457 :
6458 0 : args.rval().setObject(*result);
6459 0 : return true;
6460 : }
6461 :
6462 : class DebuggerScriptGetLineOffsetsMatcher
6463 : {
6464 : JSContext* cx_;
6465 : size_t lineno_;
6466 : MutableHandleObject result_;
6467 :
6468 : public:
6469 0 : explicit DebuggerScriptGetLineOffsetsMatcher(JSContext* cx, size_t lineno, MutableHandleObject result)
6470 0 : : cx_(cx), lineno_(lineno), result_(result) { }
6471 : using ReturnType = bool;
6472 0 : ReturnType match(HandleScript script) {
6473 : /*
6474 : * First pass: determine which offsets in this script are jump targets and
6475 : * which line numbers jump to them.
6476 : */
6477 0 : FlowGraphSummary flowData(cx_);
6478 0 : if (!flowData.populate(cx_, script))
6479 0 : return false;
6480 :
6481 0 : result_.set(NewDenseEmptyArray(cx_));
6482 0 : if (!result_)
6483 0 : return false;
6484 :
6485 : /* Second pass: build the result array. */
6486 0 : for (BytecodeRangeWithPosition r(cx_, script); !r.empty(); r.popFront()) {
6487 0 : if (!r.frontIsEntryPoint())
6488 0 : continue;
6489 :
6490 0 : size_t offset = r.frontOffset();
6491 :
6492 : /* If the op at offset is an entry point, append offset to result. */
6493 0 : if (r.frontLineNumber() == lineno_ &&
6494 0 : !flowData[offset].hasNoEdges() &&
6495 0 : flowData[offset].lineno() != lineno_)
6496 : {
6497 0 : if (!NewbornArrayPush(cx_, result_, NumberValue(offset)))
6498 0 : return false;
6499 : }
6500 : }
6501 :
6502 0 : return true;
6503 : }
6504 :
6505 0 : ReturnType match(Handle<WasmInstanceObject*> instance) {
6506 0 : Vector<uint32_t> offsets(cx_);
6507 0 : if (!instance->instance().debug().getLineOffsets(cx_, lineno_, &offsets))
6508 0 : return false;
6509 :
6510 0 : result_.set(NewDenseEmptyArray(cx_));
6511 0 : if (!result_)
6512 0 : return false;
6513 :
6514 0 : for (uint32_t i = 0; i < offsets.length(); i++) {
6515 0 : if (!NewbornArrayPush(cx_, result_, NumberValue(offsets[i])))
6516 0 : return false;
6517 : }
6518 0 : return true;
6519 : }
6520 : };
6521 :
6522 : static bool
6523 0 : DebuggerScript_getLineOffsets(JSContext* cx, unsigned argc, Value* vp)
6524 : {
6525 0 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getLineOffsets", args, obj, referent);
6526 0 : if (!args.requireAtLeast(cx, "Debugger.Script.getLineOffsets", 1))
6527 0 : return false;
6528 :
6529 : /* Parse lineno argument. */
6530 0 : RootedValue linenoValue(cx, args[0]);
6531 : size_t lineno;
6532 0 : if (!ToNumber(cx, &linenoValue))
6533 0 : return false;
6534 : {
6535 0 : double d = linenoValue.toNumber();
6536 0 : lineno = size_t(d);
6537 0 : if (lineno != d) {
6538 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_LINE);
6539 0 : return false;
6540 : }
6541 : }
6542 :
6543 0 : RootedObject result(cx);
6544 0 : DebuggerScriptGetLineOffsetsMatcher matcher(cx, lineno, &result);
6545 0 : if (!referent.match(matcher))
6546 0 : return false;
6547 :
6548 0 : args.rval().setObject(*result);
6549 0 : return true;
6550 : }
6551 :
6552 : bool
6553 0 : Debugger::observesFrame(AbstractFramePtr frame) const
6554 : {
6555 0 : if (frame.isWasmDebugFrame())
6556 0 : return observesWasm(frame.wasmInstance());
6557 :
6558 0 : return observesScript(frame.script());
6559 : }
6560 :
6561 : bool
6562 0 : Debugger::observesFrame(const FrameIter& iter) const
6563 : {
6564 : // Skip frames not yet fully initialized during their prologue.
6565 0 : if (iter.isInterp() && iter.isFunctionFrame()) {
6566 0 : const Value& thisVal = iter.interpFrame()->thisArgument();
6567 0 : if (thisVal.isMagic() && thisVal.whyMagic() == JS_IS_CONSTRUCTING)
6568 0 : return false;
6569 : }
6570 0 : if (iter.isWasm()) {
6571 : // Skip frame of wasm instances we cannot observe.
6572 0 : if (!iter.wasmDebugEnabled())
6573 0 : return false;
6574 0 : return observesWasm(iter.wasmInstance());
6575 : }
6576 0 : return observesScript(iter.script());
6577 : }
6578 :
6579 : bool
6580 0 : Debugger::observesScript(JSScript* script) const
6581 : {
6582 0 : if (!enabled)
6583 0 : return false;
6584 : // Don't ever observe self-hosted scripts: the Debugger API can break
6585 : // self-hosted invariants.
6586 0 : return observesGlobal(&script->global()) && !script->selfHosted();
6587 : }
6588 :
6589 : bool
6590 0 : Debugger::observesWasm(wasm::Instance* instance) const
6591 : {
6592 0 : if (!enabled || !instance->debugEnabled())
6593 0 : return false;
6594 0 : return observesGlobal(&instance->object()->global());
6595 : }
6596 :
6597 : /* static */ bool
6598 0 : Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to,
6599 : ScriptFrameIter& iter)
6600 : {
6601 0 : auto removeFromDebuggerFramesOnExit = MakeScopeExit([&] {
6602 : // Remove any remaining old entries on exit, as the 'from' frame will
6603 : // be gone. This is only done in the failure case. On failure, the
6604 : // removeToDebuggerFramesOnExit lambda below will rollback any frames
6605 : // that were replaced, resulting in !frameMaps(to). On success, the
6606 : // range will be empty, as all from Frame.Debugger instances will have
6607 : // been removed.
6608 0 : MOZ_ASSERT_IF(inFrameMaps(to), !inFrameMaps(from));
6609 0 : removeFromFrameMapsAndClearBreakpointsIn(cx, from);
6610 :
6611 : // Rekey missingScopes to maintain Debugger.Environment identity and
6612 : // forward liveScopes to point to the new frame.
6613 0 : DebugEnvironments::forwardLiveFrame(cx, from, to);
6614 0 : });
6615 :
6616 : // Forward live Debugger.Frame objects.
6617 0 : Rooted<DebuggerFrameVector> frames(cx, DebuggerFrameVector(cx));
6618 0 : if (!getDebuggerFrames(from, &frames)) {
6619 : // An OOM here means that all Debuggers' frame maps still contain
6620 : // entries for 'from' and no entries for 'to'. Since the 'from' frame
6621 : // will be gone, they are removed by removeFromDebuggerFramesOnExit
6622 : // above.
6623 0 : return false;
6624 : }
6625 :
6626 : // If during the loop below we hit an OOM, we must also rollback any of
6627 : // the frames that were successfully replaced. For OSR frames, OOM here
6628 : // means those frames will pop from the OSR trampoline, which does not
6629 : // call Debugger::onLeaveFrame.
6630 0 : auto removeToDebuggerFramesOnExit = MakeScopeExit([&] {
6631 0 : removeFromFrameMapsAndClearBreakpointsIn(cx, to);
6632 0 : });
6633 :
6634 0 : for (size_t i = 0; i < frames.length(); i++) {
6635 0 : HandleDebuggerFrame frameobj = frames[i];
6636 0 : Debugger* dbg = Debugger::fromChildJSObject(frameobj);
6637 :
6638 : // Update frame object's ScriptFrameIter::data pointer.
6639 0 : DebuggerFrame_freeScriptFrameIterData(cx->runtime()->defaultFreeOp(), frameobj);
6640 0 : ScriptFrameIter::Data* data = iter.copyData();
6641 0 : if (!data) {
6642 : // An OOM here means that some Debuggers' frame maps may still
6643 : // contain entries for 'from' and some Debuggers' frame maps may
6644 : // also contain entries for 'to'. Thus both
6645 : // removeFromDebuggerFramesOnExit and
6646 : // removeToDebuggerFramesOnExit must both run.
6647 : //
6648 : // The current frameobj in question is still in its Debugger's
6649 : // frame map keyed by 'from', so it will be covered by
6650 : // removeFromDebuggerFramesOnExit.
6651 0 : return false;
6652 : }
6653 0 : frameobj->setPrivate(data);
6654 :
6655 : // Remove old frame.
6656 0 : dbg->frames.remove(from);
6657 :
6658 : // Add the frame object with |to| as key.
6659 0 : if (!dbg->frames.putNew(to, frameobj)) {
6660 : // This OOM is subtle. At this point, both
6661 : // removeFromDebuggerFramesOnExit and removeToDebuggerFramesOnExit
6662 : // must both run for the same reason given above.
6663 : //
6664 : // The difference is that the current frameobj is no longer in its
6665 : // Debugger's frame map, so it will not be cleaned up by neither
6666 : // lambda. Manually clean it up here.
6667 0 : FreeOp* fop = cx->runtime()->defaultFreeOp();
6668 0 : DebuggerFrame_freeScriptFrameIterData(fop, frameobj);
6669 0 : DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, to, frameobj);
6670 :
6671 0 : ReportOutOfMemory(cx);
6672 0 : return false;
6673 : }
6674 : }
6675 :
6676 : // All frames successfuly replaced, cancel the rollback.
6677 0 : removeToDebuggerFramesOnExit.release();
6678 :
6679 0 : return true;
6680 : }
6681 :
6682 : /* static */ bool
6683 12315 : Debugger::inFrameMaps(AbstractFramePtr frame)
6684 : {
6685 12315 : bool foundAny = false;
6686 12315 : forEachDebuggerFrame(frame, [&](NativeObject* frameobj) { foundAny = true; });
6687 12315 : return foundAny;
6688 : }
6689 :
6690 : /* static */ void
6691 0 : Debugger::removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame)
6692 : {
6693 0 : forEachDebuggerFrame(frame, [&](NativeObject* frameobj) {
6694 0 : Debugger* dbg = Debugger::fromChildJSObject(frameobj);
6695 :
6696 0 : FreeOp* fop = cx->runtime()->defaultFreeOp();
6697 0 : DebuggerFrame_freeScriptFrameIterData(fop, frameobj);
6698 0 : DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj);
6699 :
6700 0 : dbg->frames.remove(frame);
6701 0 : });
6702 :
6703 : /*
6704 : * If this is an eval frame, then from the debugger's perspective the
6705 : * script is about to be destroyed. Remove any breakpoints in it.
6706 : */
6707 0 : if (frame.isEvalFrame()) {
6708 0 : RootedScript script(cx, frame.script());
6709 0 : script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), nullptr, nullptr);
6710 : }
6711 0 : }
6712 :
6713 : /* static */ bool
6714 0 : Debugger::handleBaselineOsr(JSContext* cx, InterpreterFrame* from, jit::BaselineFrame* to)
6715 : {
6716 0 : ScriptFrameIter iter(cx);
6717 0 : MOZ_ASSERT(iter.abstractFramePtr() == to);
6718 0 : return replaceFrameGuts(cx, from, to, iter);
6719 : }
6720 :
6721 : /* static */ bool
6722 0 : Debugger::handleIonBailout(JSContext* cx, jit::RematerializedFrame* from, jit::BaselineFrame* to)
6723 : {
6724 : // When we return to a bailed-out Ion real frame, we must update all
6725 : // Debugger.Frames that refer to its inline frames. However, since we
6726 : // can't pop individual inline frames off the stack (we can only pop the
6727 : // real frame that contains them all, as a unit), we cannot assume that
6728 : // the frame we're dealing with is the top frame. Advance the iterator
6729 : // across any inlined frames younger than |to|, the baseline frame
6730 : // reconstructed during bailout from the Ion frame corresponding to
6731 : // |from|.
6732 0 : ScriptFrameIter iter(cx);
6733 0 : while (iter.abstractFramePtr() != to)
6734 0 : ++iter;
6735 0 : return replaceFrameGuts(cx, from, to, iter);
6736 : }
6737 :
6738 : /* static */ void
6739 0 : Debugger::handleUnrecoverableIonBailoutError(JSContext* cx, jit::RematerializedFrame* frame)
6740 : {
6741 : // Ion bailout can fail due to overrecursion. In such cases we cannot
6742 : // honor any further Debugger hooks on the frame, and need to ensure that
6743 : // its Debugger.Frame entry is cleaned up.
6744 0 : removeFromFrameMapsAndClearBreakpointsIn(cx, frame);
6745 0 : }
6746 :
6747 : /* static */ void
6748 0 : Debugger::propagateForcedReturn(JSContext* cx, AbstractFramePtr frame, HandleValue rval)
6749 : {
6750 : // Invoking the interrupt handler is considered a step and invokes the
6751 : // youngest frame's onStep handler, if any. However, we cannot handle
6752 : // { return: ... } resumption values straightforwardly from the interrupt
6753 : // handler. Instead, we set the intended return value in the frame's rval
6754 : // slot and set the propagating-forced-return flag on the JSContext.
6755 : //
6756 : // The interrupt handler then returns false with no exception set,
6757 : // signaling an uncatchable exception. In the exception handlers, we then
6758 : // check for the special propagating-forced-return flag.
6759 0 : MOZ_ASSERT(!cx->isExceptionPending());
6760 0 : cx->setPropagatingForcedReturn();
6761 0 : frame.setReturnValue(rval);
6762 0 : }
6763 :
6764 0 : struct DebuggerScriptSetBreakpointMatcher
6765 : {
6766 : JSContext* cx_;
6767 : Debugger* dbg_;
6768 : size_t offset_;
6769 : RootedObject handler_;
6770 :
6771 : public:
6772 0 : explicit DebuggerScriptSetBreakpointMatcher(JSContext* cx, Debugger* dbg, size_t offset, HandleObject handler)
6773 0 : : cx_(cx), dbg_(dbg), offset_(offset), handler_(cx, handler)
6774 0 : { }
6775 :
6776 : using ReturnType = bool;
6777 :
6778 0 : ReturnType match(HandleScript script) {
6779 0 : if (!dbg_->observesScript(script)) {
6780 0 : JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_DEBUGGING);
6781 0 : return false;
6782 : }
6783 :
6784 0 : if (!EnsureScriptOffsetIsValid(cx_, script, offset_))
6785 0 : return false;
6786 :
6787 : // Ensure observability *before* setting the breakpoint. If the script is
6788 : // not already a debuggee, trying to ensure observability after setting
6789 : // the breakpoint (and thus marking the script as a debuggee) will skip
6790 : // actually ensuring observability.
6791 0 : if (!dbg_->ensureExecutionObservabilityOfScript(cx_, script))
6792 0 : return false;
6793 :
6794 0 : jsbytecode* pc = script->offsetToPC(offset_);
6795 0 : BreakpointSite* site = script->getOrCreateBreakpointSite(cx_, pc);
6796 0 : if (!site)
6797 0 : return false;
6798 0 : site->inc(cx_->runtime()->defaultFreeOp());
6799 0 : if (cx_->runtime()->new_<Breakpoint>(dbg_, site, handler_))
6800 0 : return true;
6801 0 : site->dec(cx_->runtime()->defaultFreeOp());
6802 0 : site->destroyIfEmpty(cx_->runtime()->defaultFreeOp());
6803 0 : return false;
6804 : }
6805 :
6806 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
6807 0 : wasm::Instance& instance = wasmInstance->instance();
6808 0 : if (!instance.debug().hasBreakpointTrapAtOffset(offset_)) {
6809 0 : JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET);
6810 0 : return false;
6811 : }
6812 0 : WasmBreakpointSite* site = instance.debug().getOrCreateBreakpointSite(cx_, offset_);
6813 0 : if (!site)
6814 0 : return false;
6815 0 : site->inc(cx_->runtime()->defaultFreeOp());
6816 0 : if (cx_->runtime()->new_<WasmBreakpoint>(dbg_, site, handler_, instance.object()))
6817 0 : return true;
6818 0 : site->dec(cx_->runtime()->defaultFreeOp());
6819 0 : site->destroyIfEmpty(cx_->runtime()->defaultFreeOp());
6820 0 : return false;
6821 : }
6822 : };
6823 :
6824 : static bool
6825 0 : DebuggerScript_setBreakpoint(JSContext* cx, unsigned argc, Value* vp)
6826 : {
6827 0 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "setBreakpoint", args, obj, referent);
6828 0 : if (!args.requireAtLeast(cx, "Debugger.Script.setBreakpoint", 2))
6829 0 : return false;
6830 0 : Debugger* dbg = Debugger::fromChildJSObject(obj);
6831 :
6832 : size_t offset;
6833 0 : if (!ScriptOffset(cx, args[0], &offset))
6834 0 : return false;
6835 :
6836 0 : RootedObject handler(cx, NonNullObject(cx, args[1]));
6837 0 : if (!handler)
6838 0 : return false;
6839 :
6840 0 : DebuggerScriptSetBreakpointMatcher matcher(cx, dbg, offset, handler);
6841 0 : if (!referent.match(matcher))
6842 0 : return false;
6843 0 : args.rval().setUndefined();
6844 0 : return true;
6845 : }
6846 :
6847 : static bool
6848 0 : DebuggerScript_getBreakpoints(JSContext* cx, unsigned argc, Value* vp)
6849 : {
6850 0 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getBreakpoints", args, obj, script);
6851 0 : Debugger* dbg = Debugger::fromChildJSObject(obj);
6852 :
6853 : jsbytecode* pc;
6854 0 : if (args.length() > 0) {
6855 : size_t offset;
6856 0 : if (!ScriptOffset(cx, args[0], &offset) || !EnsureScriptOffsetIsValid(cx, script, offset))
6857 0 : return false;
6858 0 : pc = script->offsetToPC(offset);
6859 : } else {
6860 0 : pc = nullptr;
6861 : }
6862 :
6863 0 : RootedObject arr(cx, NewDenseEmptyArray(cx));
6864 0 : if (!arr)
6865 0 : return false;
6866 :
6867 0 : for (unsigned i = 0; i < script->length(); i++) {
6868 0 : BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
6869 0 : if (!site)
6870 0 : continue;
6871 0 : MOZ_ASSERT(site->type() == BreakpointSite::Type::JS);
6872 0 : if (!pc || site->asJS()->pc == pc) {
6873 0 : for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
6874 0 : if (bp->debugger == dbg &&
6875 0 : !NewbornArrayPush(cx, arr, ObjectValue(*bp->getHandler())))
6876 : {
6877 0 : return false;
6878 : }
6879 : }
6880 : }
6881 : }
6882 0 : args.rval().setObject(*arr);
6883 0 : return true;
6884 : }
6885 :
6886 : class DebuggerScriptClearBreakpointMatcher
6887 : {
6888 : JSContext* cx_;
6889 : Debugger* dbg_;
6890 : JSObject* handler_;
6891 :
6892 : public:
6893 0 : explicit DebuggerScriptClearBreakpointMatcher(JSContext* cx, Debugger* dbg, JSObject* handler) : cx_(cx), dbg_(dbg), handler_(handler) { }
6894 : using ReturnType = bool;
6895 :
6896 0 : ReturnType match(HandleScript script) {
6897 0 : script->clearBreakpointsIn(cx_->runtime()->defaultFreeOp(), dbg_, handler_);
6898 0 : return true;
6899 : }
6900 :
6901 0 : ReturnType match(Handle<WasmInstanceObject*> instance) {
6902 0 : return instance->instance().debug().clearBreakpointsIn(cx_, instance, dbg_, handler_);
6903 : }
6904 : };
6905 :
6906 :
6907 : static bool
6908 0 : DebuggerScript_clearBreakpoint(JSContext* cx, unsigned argc, Value* vp)
6909 : {
6910 0 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "clearBreakpoint", args, obj, referent);
6911 0 : if (!args.requireAtLeast(cx, "Debugger.Script.clearBreakpoint", 1))
6912 0 : return false;
6913 0 : Debugger* dbg = Debugger::fromChildJSObject(obj);
6914 :
6915 0 : JSObject* handler = NonNullObject(cx, args[0]);
6916 0 : if (!handler)
6917 0 : return false;
6918 :
6919 0 : DebuggerScriptClearBreakpointMatcher matcher(cx, dbg, handler);
6920 0 : if (!referent.match(matcher))
6921 0 : return false;
6922 :
6923 0 : args.rval().setUndefined();
6924 0 : return true;
6925 : }
6926 :
6927 : static bool
6928 0 : DebuggerScript_clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp)
6929 : {
6930 0 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "clearAllBreakpoints", args, obj, referent);
6931 0 : Debugger* dbg = Debugger::fromChildJSObject(obj);
6932 0 : DebuggerScriptClearBreakpointMatcher matcher(cx, dbg, nullptr);
6933 0 : if (!referent.match(matcher))
6934 0 : return false;
6935 0 : args.rval().setUndefined();
6936 0 : return true;
6937 : }
6938 :
6939 : class DebuggerScriptIsInCatchScopeMatcher
6940 : {
6941 : JSContext* cx_;
6942 : size_t offset_;
6943 : bool isInCatch_;
6944 :
6945 : public:
6946 0 : explicit DebuggerScriptIsInCatchScopeMatcher(JSContext* cx, size_t offset) : cx_(cx), offset_(offset) { }
6947 : using ReturnType = bool;
6948 :
6949 0 : inline bool isInCatch() const { return isInCatch_; }
6950 :
6951 0 : ReturnType match(HandleScript script) {
6952 0 : if (!EnsureScriptOffsetIsValid(cx_, script, offset_))
6953 0 : return false;
6954 :
6955 : /*
6956 : * Try note ranges are relative to the mainOffset of the script, so adjust
6957 : * offset accordingly.
6958 : */
6959 0 : size_t offset = offset_ - script->mainOffset();
6960 :
6961 0 : if (script->hasTrynotes()) {
6962 0 : JSTryNote* tnBegin = script->trynotes()->vector;
6963 0 : JSTryNote* tnEnd = tnBegin + script->trynotes()->length;
6964 0 : while (tnBegin != tnEnd) {
6965 0 : if (tnBegin->start <= offset &&
6966 0 : offset <= tnBegin->start + tnBegin->length &&
6967 0 : tnBegin->kind == JSTRY_CATCH)
6968 : {
6969 0 : isInCatch_ = true;
6970 0 : return true;
6971 : }
6972 0 : ++tnBegin;
6973 : }
6974 : }
6975 0 : isInCatch_ = false;
6976 0 : return true;
6977 : }
6978 :
6979 0 : ReturnType match(Handle<WasmInstanceObject*> instance) {
6980 0 : isInCatch_ = false;
6981 0 : return true;
6982 : }
6983 : };
6984 :
6985 : static bool
6986 0 : DebuggerScript_isInCatchScope(JSContext* cx, unsigned argc, Value* vp)
6987 : {
6988 0 : THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "isInCatchScope", args, obj, referent);
6989 0 : if (!args.requireAtLeast(cx, "Debugger.Script.isInCatchScope", 1))
6990 0 : return false;
6991 :
6992 : size_t offset;
6993 0 : if (!ScriptOffset(cx, args[0], &offset))
6994 0 : return false;
6995 :
6996 0 : DebuggerScriptIsInCatchScopeMatcher matcher(cx, offset);
6997 0 : if (!referent.match(matcher))
6998 0 : return false;
6999 0 : args.rval().setBoolean(matcher.isInCatch());
7000 0 : return true;
7001 : }
7002 :
7003 : static bool
7004 0 : DebuggerScript_getOffsetsCoverage(JSContext* cx, unsigned argc, Value* vp)
7005 : {
7006 0 : THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getOffsetsCoverage", args, obj, script);
7007 :
7008 : // If the script has no coverage information, then skip this and return null
7009 : // instead.
7010 0 : if (!script->hasScriptCounts()) {
7011 0 : args.rval().setNull();
7012 0 : return true;
7013 : }
7014 :
7015 0 : ScriptCounts* sc = &script->getScriptCounts();
7016 :
7017 : // If the main ever got visited, then assume that any code before main got
7018 : // visited once.
7019 0 : uint64_t hits = 0;
7020 0 : const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(script->main()));
7021 0 : if (counts->numExec())
7022 0 : hits = 1;
7023 :
7024 : // Build an array of objects which are composed of 4 properties:
7025 : // - offset PC offset of the current opcode.
7026 : // - lineNumber Line of the current opcode.
7027 : // - columnNumber Column of the current opcode.
7028 : // - count Number of times the instruction got executed.
7029 0 : RootedObject result(cx, NewDenseEmptyArray(cx));
7030 0 : if (!result)
7031 0 : return false;
7032 :
7033 0 : RootedId offsetId(cx, AtomToId(cx->names().offset));
7034 0 : RootedId lineNumberId(cx, AtomToId(cx->names().lineNumber));
7035 0 : RootedId columnNumberId(cx, AtomToId(cx->names().columnNumber));
7036 0 : RootedId countId(cx, AtomToId(cx->names().count));
7037 :
7038 0 : RootedObject item(cx);
7039 0 : RootedValue offsetValue(cx);
7040 0 : RootedValue lineNumberValue(cx);
7041 0 : RootedValue columnNumberValue(cx);
7042 0 : RootedValue countValue(cx);
7043 :
7044 : // Iterate linearly over the bytecode.
7045 0 : for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) {
7046 0 : size_t offset = r.frontOffset();
7047 :
7048 : // The beginning of each non-branching sequences of instruction set the
7049 : // number of execution of the current instruction and any following
7050 : // instruction.
7051 0 : counts = sc->maybeGetPCCounts(offset);
7052 0 : if (counts)
7053 0 : hits = counts->numExec();
7054 :
7055 0 : offsetValue.setNumber(double(offset));
7056 0 : lineNumberValue.setNumber(double(r.frontLineNumber()));
7057 0 : columnNumberValue.setNumber(double(r.frontColumnNumber()));
7058 0 : countValue.setNumber(double(hits));
7059 :
7060 : // Create a new object with the offset, line number, column number, the
7061 : // number of hit counts, and append it to the array.
7062 0 : item = NewObjectWithGivenProto<PlainObject>(cx, nullptr);
7063 0 : if (!item ||
7064 0 : !DefineProperty(cx, item, offsetId, offsetValue) ||
7065 0 : !DefineProperty(cx, item, lineNumberId, lineNumberValue) ||
7066 0 : !DefineProperty(cx, item, columnNumberId, columnNumberValue) ||
7067 0 : !DefineProperty(cx, item, countId, countValue) ||
7068 0 : !NewbornArrayPush(cx, result, ObjectValue(*item)))
7069 : {
7070 0 : return false;
7071 : }
7072 :
7073 : // If the current instruction has thrown, then decrement the hit counts
7074 : // with the number of throws.
7075 0 : counts = sc->maybeGetThrowCounts(offset);
7076 0 : if (counts)
7077 0 : hits -= counts->numExec();
7078 : }
7079 :
7080 0 : args.rval().setObject(*result);
7081 0 : return true;
7082 : }
7083 :
7084 : static bool
7085 0 : DebuggerScript_construct(JSContext* cx, unsigned argc, Value* vp)
7086 : {
7087 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
7088 0 : "Debugger.Script");
7089 0 : return false;
7090 : }
7091 :
7092 : static const JSPropertySpec DebuggerScript_properties[] = {
7093 : JS_PSG("displayName", DebuggerScript_getDisplayName, 0),
7094 : JS_PSG("url", DebuggerScript_getUrl, 0),
7095 : JS_PSG("startLine", DebuggerScript_getStartLine, 0),
7096 : JS_PSG("lineCount", DebuggerScript_getLineCount, 0),
7097 : JS_PSG("source", DebuggerScript_getSource, 0),
7098 : JS_PSG("sourceStart", DebuggerScript_getSourceStart, 0),
7099 : JS_PSG("sourceLength", DebuggerScript_getSourceLength, 0),
7100 : JS_PSG("global", DebuggerScript_getGlobal, 0),
7101 : JS_PSG("format", DebuggerScript_getFormat, 0),
7102 : JS_PS_END
7103 : };
7104 :
7105 : static const JSFunctionSpec DebuggerScript_methods[] = {
7106 : JS_FN("getChildScripts", DebuggerScript_getChildScripts, 0, 0),
7107 : JS_FN("getAllOffsets", DebuggerScript_getAllOffsets, 0, 0),
7108 : JS_FN("getAllColumnOffsets", DebuggerScript_getAllColumnOffsets, 0, 0),
7109 : JS_FN("getLineOffsets", DebuggerScript_getLineOffsets, 1, 0),
7110 : JS_FN("getOffsetLocation", DebuggerScript_getOffsetLocation, 0, 0),
7111 : JS_FN("setBreakpoint", DebuggerScript_setBreakpoint, 2, 0),
7112 : JS_FN("getBreakpoints", DebuggerScript_getBreakpoints, 1, 0),
7113 : JS_FN("clearBreakpoint", DebuggerScript_clearBreakpoint, 1, 0),
7114 : JS_FN("clearAllBreakpoints", DebuggerScript_clearAllBreakpoints, 0, 0),
7115 : JS_FN("isInCatchScope", DebuggerScript_isInCatchScope, 1, 0),
7116 : JS_FN("getOffsetsCoverage", DebuggerScript_getOffsetsCoverage, 0, 0),
7117 : JS_FS_END
7118 : };
7119 :
7120 :
7121 : /*** Debugger.Source *****************************************************************************/
7122 :
7123 : // For internal use only.
7124 : static inline NativeObject*
7125 0 : GetSourceReferentRawObject(JSObject* obj)
7126 : {
7127 0 : MOZ_ASSERT(obj->getClass() == &DebuggerSource_class);
7128 0 : return static_cast<NativeObject*>(obj->as<NativeObject>().getPrivate());
7129 : }
7130 :
7131 : static inline DebuggerSourceReferent
7132 0 : GetSourceReferent(JSObject* obj)
7133 : {
7134 0 : if (NativeObject* referent = GetSourceReferentRawObject(obj)) {
7135 0 : if (referent->is<ScriptSourceObject>())
7136 0 : return AsVariant(&referent->as<ScriptSourceObject>());
7137 0 : return AsVariant(&referent->as<WasmInstanceObject>());
7138 : }
7139 0 : return AsVariant(static_cast<ScriptSourceObject*>(nullptr));
7140 : }
7141 :
7142 : void
7143 0 : DebuggerSource_trace(JSTracer* trc, JSObject* obj)
7144 : {
7145 : /*
7146 : * There is a barrier on private pointers, so the Unbarriered marking
7147 : * is okay.
7148 : */
7149 0 : if (JSObject *referent = GetSourceReferentRawObject(obj)) {
7150 : TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &referent,
7151 0 : "Debugger.Source referent");
7152 0 : obj->as<NativeObject>().setPrivateUnbarriered(referent);
7153 : }
7154 0 : }
7155 :
7156 : class SetDebuggerSourcePrivateMatcher
7157 : {
7158 : NativeObject* obj_;
7159 : public:
7160 0 : explicit SetDebuggerSourcePrivateMatcher(NativeObject* obj) : obj_(obj) { }
7161 : using ReturnType = void;
7162 0 : ReturnType match(HandleScriptSource source) { obj_->setPrivateGCThing(source); }
7163 0 : ReturnType match(Handle<WasmInstanceObject*> instance) { obj_->setPrivateGCThing(instance); }
7164 : };
7165 :
7166 : NativeObject*
7167 0 : Debugger::newDebuggerSource(JSContext* cx, Handle<DebuggerSourceReferent> referent)
7168 : {
7169 0 : assertSameCompartment(cx, object.get());
7170 :
7171 0 : RootedObject proto(cx, &object->getReservedSlot(JSSLOT_DEBUG_SOURCE_PROTO).toObject());
7172 0 : MOZ_ASSERT(proto);
7173 0 : NativeObject* sourceobj = NewNativeObjectWithGivenProto(cx, &DebuggerSource_class,
7174 0 : proto, TenuredObject);
7175 0 : if (!sourceobj)
7176 0 : return nullptr;
7177 0 : sourceobj->setReservedSlot(JSSLOT_DEBUGSOURCE_OWNER, ObjectValue(*object));
7178 0 : SetDebuggerSourcePrivateMatcher matcher(sourceobj);
7179 0 : referent.match(matcher);
7180 :
7181 0 : return sourceobj;
7182 : }
7183 :
7184 : JSObject*
7185 0 : Debugger::wrapVariantReferent(JSContext* cx, Handle<DebuggerSourceReferent> referent)
7186 : {
7187 : JSObject* obj;
7188 0 : if (referent.is<ScriptSourceObject*>()) {
7189 0 : Handle<ScriptSourceObject*> untaggedReferent = referent.template as<ScriptSourceObject*>();
7190 0 : Rooted<CrossCompartmentKey> key(cx, CrossCompartmentKey(object, untaggedReferent,
7191 0 : CrossCompartmentKey::DebuggerObjectKind::DebuggerSource));
7192 0 : obj = wrapVariantReferent<DebuggerSourceReferent, ScriptSourceObject*, SourceWeakMap>(
7193 0 : cx, sources, key, referent);
7194 : } else {
7195 0 : Handle<WasmInstanceObject*> untaggedReferent = referent.template as<WasmInstanceObject*>();
7196 0 : Rooted<CrossCompartmentKey> key(cx, CrossCompartmentKey(object, untaggedReferent,
7197 0 : CrossCompartmentKey::DebuggerObjectKind::DebuggerWasmSource));
7198 0 : obj = wrapVariantReferent<DebuggerSourceReferent, WasmInstanceObject*, WasmInstanceWeakMap>(
7199 0 : cx, wasmInstanceSources, key, referent);
7200 : }
7201 0 : MOZ_ASSERT_IF(obj, GetSourceReferent(obj) == referent);
7202 0 : return obj;
7203 : }
7204 :
7205 : JSObject*
7206 0 : Debugger::wrapSource(JSContext* cx, HandleScriptSource source)
7207 : {
7208 0 : Rooted<DebuggerSourceReferent> referent(cx, source.get());
7209 0 : return wrapVariantReferent(cx, referent);
7210 : }
7211 :
7212 : JSObject*
7213 0 : Debugger::wrapWasmSource(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance)
7214 : {
7215 0 : Rooted<DebuggerSourceReferent> referent(cx, wasmInstance.get());
7216 0 : return wrapVariantReferent(cx, referent);
7217 : }
7218 :
7219 : static bool
7220 0 : DebuggerSource_construct(JSContext* cx, unsigned argc, Value* vp)
7221 : {
7222 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
7223 0 : "Debugger.Source");
7224 0 : return false;
7225 : }
7226 :
7227 : static NativeObject*
7228 0 : DebuggerSource_check(JSContext* cx, HandleValue thisv, const char* fnname)
7229 : {
7230 0 : JSObject* thisobj = NonNullObject(cx, thisv);
7231 0 : if (!thisobj)
7232 0 : return nullptr;
7233 0 : if (thisobj->getClass() != &DebuggerSource_class) {
7234 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
7235 0 : "Debugger.Source", fnname, thisobj->getClass()->name);
7236 0 : return nullptr;
7237 : }
7238 :
7239 0 : NativeObject* nthisobj = &thisobj->as<NativeObject>();
7240 :
7241 0 : if (!GetSourceReferentRawObject(thisobj)) {
7242 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
7243 0 : "Debugger.Source", fnname, "prototype object");
7244 0 : return nullptr;
7245 : }
7246 :
7247 0 : return nthisobj;
7248 : }
7249 :
7250 : template <typename ReferentT>
7251 : static NativeObject*
7252 0 : DebuggerSource_checkThis(JSContext* cx, const CallArgs& args, const char* fnname,
7253 : const char* refname)
7254 : {
7255 0 : NativeObject* thisobj = DebuggerSource_check(cx, args.thisv(), fnname);
7256 0 : if (!thisobj)
7257 0 : return nullptr;
7258 :
7259 0 : if (!GetSourceReferent(thisobj).is<ReferentT>()) {
7260 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT,
7261 : JSDVG_SEARCH_STACK, args.thisv(), nullptr,
7262 : refname, nullptr);
7263 0 : return nullptr;
7264 : }
7265 :
7266 0 : return thisobj;
7267 : }
7268 :
7269 : #define THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, fnname, args, obj, referent) \
7270 : CallArgs args = CallArgsFromVp(argc, vp); \
7271 : RootedNativeObject obj(cx, DebuggerSource_check(cx, args.thisv(), fnname)); \
7272 : if (!obj) \
7273 : return false; \
7274 : Rooted<DebuggerSourceReferent> referent(cx, GetSourceReferent(obj))
7275 :
7276 : #define THIS_DEBUGSOURCE_SOURCE(cx, argc, vp, fnname, args, obj, sourceObject) \
7277 : CallArgs args = CallArgsFromVp(argc, vp); \
7278 : RootedNativeObject obj(cx, \
7279 : DebuggerSource_checkThis<ScriptSourceObject*>(cx, args, fnname, \
7280 : "a JS source")); \
7281 : if (!obj) \
7282 : return false; \
7283 : RootedScriptSource sourceObject(cx, GetSourceReferent(obj).as<ScriptSourceObject*>())
7284 :
7285 : class DebuggerSourceGetTextMatcher
7286 : {
7287 : JSContext* cx_;
7288 :
7289 : public:
7290 0 : explicit DebuggerSourceGetTextMatcher(JSContext* cx) : cx_(cx) { }
7291 :
7292 : using ReturnType = JSString*;
7293 :
7294 0 : ReturnType match(HandleScriptSource sourceObject) {
7295 0 : ScriptSource* ss = sourceObject->source();
7296 0 : bool hasSourceData = ss->hasSourceData();
7297 0 : if (!ss->hasSourceData() && !JSScript::loadSource(cx_, ss, &hasSourceData))
7298 0 : return nullptr;
7299 0 : if (!hasSourceData)
7300 0 : return NewStringCopyZ<CanGC>(cx_, "[no source]");
7301 :
7302 0 : if (ss->isFunctionBody())
7303 0 : return ss->functionBodyString(cx_);
7304 :
7305 0 : return ss->substring(cx_, 0, ss->length());
7306 : }
7307 :
7308 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7309 0 : if (wasmInstance->instance().debug().maybeBytecode() &&
7310 0 : wasmInstance->instance().debug().binarySource())
7311 : {
7312 0 : return NewStringCopyZ<CanGC>(cx_, "[wasm]");
7313 : }
7314 0 : return wasmInstance->instance().debug().createText(cx_);
7315 : }
7316 : };
7317 :
7318 : static bool
7319 0 : DebuggerSource_getText(JSContext* cx, unsigned argc, Value* vp)
7320 : {
7321 0 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get text)", args, obj, referent);
7322 0 : Value textv = obj->getReservedSlot(JSSLOT_DEBUGSOURCE_TEXT);
7323 0 : if (!textv.isUndefined()) {
7324 0 : MOZ_ASSERT(textv.isString());
7325 0 : args.rval().set(textv);
7326 0 : return true;
7327 : }
7328 :
7329 0 : DebuggerSourceGetTextMatcher matcher(cx);
7330 0 : JSString* str = referent.match(matcher);
7331 0 : if (!str)
7332 0 : return false;
7333 :
7334 0 : args.rval().setString(str);
7335 0 : obj->setReservedSlot(JSSLOT_DEBUGSOURCE_TEXT, args.rval());
7336 0 : return true;
7337 : }
7338 :
7339 : static bool
7340 0 : DebuggerSource_getBinary(JSContext* cx, unsigned argc, Value* vp)
7341 : {
7342 0 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get binary)", args, obj, referent);
7343 :
7344 0 : if (!referent.is<WasmInstanceObject*>()) {
7345 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT,
7346 : JSDVG_SEARCH_STACK, args.thisv(), nullptr,
7347 0 : "a wasm source", nullptr);
7348 0 : return false;
7349 : }
7350 :
7351 0 : RootedWasmInstanceObject wasmInstance(cx, referent.as<WasmInstanceObject*>());
7352 0 : if (!wasmInstance->instance().debug().binarySource()) {
7353 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
7354 0 : JSMSG_DEBUG_NO_BINARY_SOURCE);
7355 0 : return false;
7356 : }
7357 :
7358 0 : auto bytecode = wasmInstance->instance().debug().maybeBytecode();
7359 0 : size_t arrLength = bytecode ? bytecode->length() : 0;
7360 0 : RootedObject arr(cx, JS_NewUint8Array(cx, arrLength));
7361 0 : if (!arr)
7362 0 : return false;
7363 0 : if (bytecode)
7364 0 : memcpy(arr->as<TypedArrayObject>().viewDataUnshared(), bytecode->begin(), arrLength);
7365 :
7366 0 : args.rval().setObject(*arr);
7367 0 : return true;
7368 : }
7369 :
7370 : class DebuggerSourceGetURLMatcher
7371 : {
7372 : JSContext* cx_;
7373 :
7374 : public:
7375 0 : explicit DebuggerSourceGetURLMatcher(JSContext* cx) : cx_(cx) { }
7376 :
7377 : using ReturnType = Maybe<JSString*>;
7378 :
7379 0 : ReturnType match(HandleScriptSource sourceObject) {
7380 0 : ScriptSource* ss = sourceObject->source();
7381 0 : MOZ_ASSERT(ss);
7382 0 : if (ss->filename()) {
7383 0 : JSString* str = NewStringCopyZ<CanGC>(cx_, ss->filename());
7384 0 : return Some(str);
7385 : }
7386 0 : return Nothing();
7387 : }
7388 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7389 0 : if (JSString* str = wasmInstance->instance().debug().debugDisplayURL(cx_))
7390 0 : return Some(str);
7391 0 : return Nothing();
7392 : }
7393 : };
7394 :
7395 : static bool
7396 0 : DebuggerSource_getURL(JSContext* cx, unsigned argc, Value* vp)
7397 : {
7398 0 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get url)", args, obj, referent);
7399 :
7400 0 : DebuggerSourceGetURLMatcher matcher(cx);
7401 0 : Maybe<JSString*> str = referent.match(matcher);
7402 0 : if (str.isSome()) {
7403 0 : if (!*str)
7404 0 : return false;
7405 0 : args.rval().setString(*str);
7406 : } else {
7407 0 : args.rval().setNull();
7408 : }
7409 0 : return true;
7410 : }
7411 :
7412 : struct DebuggerSourceGetDisplayURLMatcher
7413 : {
7414 : using ReturnType = const char16_t*;
7415 0 : ReturnType match(HandleScriptSource sourceObject) {
7416 0 : ScriptSource* ss = sourceObject->source();
7417 0 : MOZ_ASSERT(ss);
7418 0 : return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
7419 : }
7420 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7421 0 : return wasmInstance->instance().metadata().displayURL();
7422 : }
7423 : };
7424 :
7425 : static bool
7426 0 : DebuggerSource_getDisplayURL(JSContext* cx, unsigned argc, Value* vp)
7427 : {
7428 0 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get url)", args, obj, referent);
7429 :
7430 : DebuggerSourceGetDisplayURLMatcher matcher;
7431 0 : if (const char16_t* displayURL = referent.match(matcher)) {
7432 0 : JSString* str = JS_NewUCStringCopyZ(cx, displayURL);
7433 0 : if (!str)
7434 0 : return false;
7435 0 : args.rval().setString(str);
7436 : } else {
7437 0 : args.rval().setNull();
7438 : }
7439 0 : return true;
7440 : }
7441 :
7442 : struct DebuggerSourceGetElementMatcher
7443 : {
7444 : using ReturnType = JSObject*;
7445 0 : ReturnType match(HandleScriptSource sourceObject) {
7446 0 : return sourceObject->element();
7447 : }
7448 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7449 0 : return nullptr;
7450 : }
7451 : };
7452 :
7453 : static bool
7454 0 : DebuggerSource_getElement(JSContext* cx, unsigned argc, Value* vp)
7455 : {
7456 0 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get element)", args, obj, referent);
7457 :
7458 : DebuggerSourceGetElementMatcher matcher;
7459 0 : if (JSObject* element = referent.match(matcher)) {
7460 0 : args.rval().setObjectOrNull(element);
7461 0 : if (!Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval()))
7462 0 : return false;
7463 : } else {
7464 0 : args.rval().setUndefined();
7465 : }
7466 0 : return true;
7467 : }
7468 :
7469 : struct DebuggerSourceGetElementPropertyMatcher
7470 : {
7471 : using ReturnType = Value;
7472 0 : ReturnType match(HandleScriptSource sourceObject) {
7473 0 : return sourceObject->elementAttributeName();
7474 : }
7475 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7476 0 : return UndefinedValue();
7477 : }
7478 : };
7479 :
7480 : static bool
7481 0 : DebuggerSource_getElementProperty(JSContext* cx, unsigned argc, Value* vp)
7482 : {
7483 0 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get elementAttributeName)", args, obj, referent);
7484 : DebuggerSourceGetElementPropertyMatcher matcher;
7485 0 : args.rval().set(referent.match(matcher));
7486 0 : return Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval());
7487 : }
7488 :
7489 : class DebuggerSourceGetIntroductionScriptMatcher
7490 : {
7491 : JSContext* cx_;
7492 : Debugger* dbg_;
7493 : MutableHandleValue rval_;
7494 :
7495 : public:
7496 0 : DebuggerSourceGetIntroductionScriptMatcher(JSContext* cx, Debugger* dbg,
7497 : MutableHandleValue rval)
7498 0 : : cx_(cx),
7499 : dbg_(dbg),
7500 0 : rval_(rval)
7501 0 : { }
7502 :
7503 : using ReturnType = bool;
7504 :
7505 0 : ReturnType match(HandleScriptSource sourceObject) {
7506 0 : RootedScript script(cx_, sourceObject->introductionScript());
7507 0 : if (script) {
7508 0 : RootedObject scriptDO(cx_, dbg_->wrapScript(cx_, script));
7509 0 : if (!scriptDO)
7510 0 : return false;
7511 0 : rval_.setObject(*scriptDO);
7512 : } else {
7513 0 : rval_.setUndefined();
7514 : }
7515 0 : return true;
7516 : }
7517 :
7518 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7519 0 : RootedObject ds(cx_, dbg_->wrapWasmScript(cx_, wasmInstance));
7520 0 : if (!ds)
7521 0 : return false;
7522 0 : rval_.setObject(*ds);
7523 0 : return true;
7524 : }
7525 : };
7526 :
7527 : static bool
7528 0 : DebuggerSource_getIntroductionScript(JSContext* cx, unsigned argc, Value* vp)
7529 : {
7530 0 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionScript)", args, obj, referent);
7531 0 : Debugger* dbg = Debugger::fromChildJSObject(obj);
7532 0 : DebuggerSourceGetIntroductionScriptMatcher matcher(cx, dbg, args.rval());
7533 0 : return referent.match(matcher);
7534 : }
7535 :
7536 : struct DebuggerGetIntroductionOffsetMatcher
7537 : {
7538 : using ReturnType = Value;
7539 0 : ReturnType match(HandleScriptSource sourceObject) {
7540 : // Regardless of what's recorded in the ScriptSourceObject and
7541 : // ScriptSource, only hand out the introduction offset if we also have
7542 : // the script within which it applies.
7543 0 : ScriptSource* ss = sourceObject->source();
7544 0 : if (ss->hasIntroductionOffset() && sourceObject->introductionScript())
7545 0 : return Int32Value(ss->introductionOffset());
7546 0 : return UndefinedValue();
7547 : }
7548 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7549 0 : return UndefinedValue();
7550 : }
7551 : };
7552 :
7553 : static bool
7554 0 : DebuggerSource_getIntroductionOffset(JSContext* cx, unsigned argc, Value* vp)
7555 : {
7556 0 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionOffset)", args, obj, referent);
7557 : DebuggerGetIntroductionOffsetMatcher matcher;
7558 0 : args.rval().set(referent.match(matcher));
7559 0 : return true;
7560 : }
7561 :
7562 : struct DebuggerSourceGetIntroductionTypeMatcher
7563 : {
7564 : using ReturnType = const char*;
7565 0 : ReturnType match(HandleScriptSource sourceObject) {
7566 0 : ScriptSource* ss = sourceObject->source();
7567 0 : MOZ_ASSERT(ss);
7568 0 : return ss->hasIntroductionType() ? ss->introductionType() : nullptr;
7569 : }
7570 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7571 0 : return "wasm";
7572 : }
7573 : };
7574 :
7575 : static bool
7576 0 : DebuggerSource_getIntroductionType(JSContext* cx, unsigned argc, Value* vp)
7577 : {
7578 0 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionType)", args, obj, referent);
7579 :
7580 : DebuggerSourceGetIntroductionTypeMatcher matcher;
7581 0 : if (const char* introductionType = referent.match(matcher)) {
7582 0 : JSString* str = NewStringCopyZ<CanGC>(cx, introductionType);
7583 0 : if (!str)
7584 0 : return false;
7585 0 : args.rval().setString(str);
7586 : } else {
7587 0 : args.rval().setUndefined();
7588 : }
7589 :
7590 0 : return true;
7591 : }
7592 :
7593 : static bool
7594 0 : DebuggerSource_setSourceMapURL(JSContext* cx, unsigned argc, Value* vp)
7595 : {
7596 0 : THIS_DEBUGSOURCE_SOURCE(cx, argc, vp, "sourceMapURL", args, obj, sourceObject);
7597 0 : ScriptSource* ss = sourceObject->source();
7598 0 : MOZ_ASSERT(ss);
7599 :
7600 0 : JSString* str = ToString<CanGC>(cx, args[0]);
7601 0 : if (!str)
7602 0 : return false;
7603 :
7604 0 : AutoStableStringChars stableChars(cx);
7605 0 : if (!stableChars.initTwoByte(cx, str))
7606 0 : return false;
7607 :
7608 0 : if (!ss->setSourceMapURL(cx, stableChars.twoByteChars()))
7609 0 : return false;
7610 :
7611 0 : args.rval().setUndefined();
7612 0 : return true;
7613 : }
7614 :
7615 : class DebuggerSourceGetSourceMapURLMatcher
7616 : {
7617 : JSContext* cx_;
7618 : MutableHandleString result_;
7619 :
7620 : public:
7621 0 : explicit DebuggerSourceGetSourceMapURLMatcher(JSContext* cx, MutableHandleString result)
7622 0 : : cx_(cx),
7623 0 : result_(result)
7624 0 : { }
7625 :
7626 : using ReturnType = bool;
7627 0 : ReturnType match(HandleScriptSource sourceObject) {
7628 0 : ScriptSource* ss = sourceObject->source();
7629 0 : MOZ_ASSERT(ss);
7630 0 : if (!ss->hasSourceMapURL()) {
7631 0 : result_.set(nullptr);
7632 0 : return true;
7633 : }
7634 0 : JSString* str = JS_NewUCStringCopyZ(cx_, ss->sourceMapURL());
7635 0 : if (!str)
7636 0 : return false;
7637 0 : result_.set(str);
7638 0 : return true;
7639 : }
7640 0 : ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
7641 : // sourceMapURL is not available if debugger was not in
7642 : // allowWasmBinarySource mode.
7643 0 : if (!wasmInstance->instance().debug().binarySource()) {
7644 0 : result_.set(nullptr);
7645 0 : return true;
7646 : }
7647 0 : RootedString str(cx_);
7648 0 : if (!wasmInstance->instance().debug().getSourceMappingURL(cx_, &str))
7649 0 : return false;
7650 0 : result_.set(str);
7651 0 : return true;
7652 : }
7653 : };
7654 :
7655 : static bool
7656 0 : DebuggerSource_getSourceMapURL(JSContext* cx, unsigned argc, Value* vp)
7657 : {
7658 0 : THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get sourceMapURL)", args, obj, referent);
7659 :
7660 0 : RootedString result(cx);
7661 0 : DebuggerSourceGetSourceMapURLMatcher matcher(cx, &result);
7662 0 : if (!referent.match(matcher))
7663 0 : return false;
7664 0 : if (result)
7665 0 : args.rval().setString(result);
7666 : else
7667 0 : args.rval().setNull();
7668 0 : return true;
7669 : }
7670 :
7671 : static const JSPropertySpec DebuggerSource_properties[] = {
7672 : JS_PSG("text", DebuggerSource_getText, 0),
7673 : JS_PSG("binary", DebuggerSource_getBinary, 0),
7674 : JS_PSG("url", DebuggerSource_getURL, 0),
7675 : JS_PSG("element", DebuggerSource_getElement, 0),
7676 : JS_PSG("displayURL", DebuggerSource_getDisplayURL, 0),
7677 : JS_PSG("introductionScript", DebuggerSource_getIntroductionScript, 0),
7678 : JS_PSG("introductionOffset", DebuggerSource_getIntroductionOffset, 0),
7679 : JS_PSG("introductionType", DebuggerSource_getIntroductionType, 0),
7680 : JS_PSG("elementAttributeName", DebuggerSource_getElementProperty, 0),
7681 : JS_PSGS("sourceMapURL", DebuggerSource_getSourceMapURL, DebuggerSource_setSourceMapURL, 0),
7682 : JS_PS_END
7683 : };
7684 :
7685 : static const JSFunctionSpec DebuggerSource_methods[] = {
7686 : JS_FS_END
7687 : };
7688 :
7689 :
7690 : /*** Debugger.Frame ******************************************************************************/
7691 :
7692 0 : ScriptedOnStepHandler::ScriptedOnStepHandler(JSObject* object)
7693 0 : : object_(object)
7694 : {
7695 0 : MOZ_ASSERT(object_->isCallable());
7696 0 : }
7697 :
7698 : JSObject*
7699 0 : ScriptedOnStepHandler::object() const
7700 : {
7701 0 : return object_;
7702 : }
7703 :
7704 : void
7705 0 : ScriptedOnStepHandler::drop()
7706 : {
7707 0 : this->~ScriptedOnStepHandler();
7708 0 : js_free(this);
7709 0 : }
7710 :
7711 : void
7712 0 : ScriptedOnStepHandler::trace(JSTracer* tracer)
7713 : {
7714 0 : TraceEdge(tracer, &object_, "OnStepHandlerFunction.object");
7715 0 : }
7716 :
7717 : bool
7718 0 : ScriptedOnStepHandler::onStep(JSContext* cx, HandleDebuggerFrame frame, JSTrapStatus& statusp,
7719 : MutableHandleValue vp)
7720 : {
7721 0 : RootedValue fval(cx, ObjectValue(*object_));
7722 0 : RootedValue rval(cx);
7723 0 : if (!js::Call(cx, fval, frame, &rval))
7724 0 : return false;
7725 :
7726 0 : return ParseResumptionValue(cx, rval, statusp, vp);
7727 : };
7728 :
7729 0 : ScriptedOnPopHandler::ScriptedOnPopHandler(JSObject* object)
7730 0 : : object_(object)
7731 : {
7732 0 : MOZ_ASSERT(object->isCallable());
7733 0 : }
7734 :
7735 : JSObject*
7736 0 : ScriptedOnPopHandler::object() const
7737 : {
7738 0 : return object_;
7739 : }
7740 :
7741 : void
7742 0 : ScriptedOnPopHandler::drop()
7743 : {
7744 0 : this->~ScriptedOnPopHandler();
7745 0 : js_free(this);
7746 0 : }
7747 :
7748 : void
7749 0 : ScriptedOnPopHandler::trace(JSTracer* tracer)
7750 : {
7751 0 : TraceEdge(tracer, &object_, "OnStepHandlerFunction.object");
7752 0 : }
7753 :
7754 : bool
7755 0 : ScriptedOnPopHandler::onPop(JSContext* cx, HandleDebuggerFrame frame, JSTrapStatus& statusp,
7756 : MutableHandleValue vp)
7757 : {
7758 0 : Debugger *dbg = frame->owner();
7759 :
7760 0 : RootedValue completion(cx);
7761 0 : if (!dbg->newCompletionValue(cx, statusp, vp, &completion))
7762 0 : return false;
7763 :
7764 0 : RootedValue fval(cx, ObjectValue(*object_));
7765 0 : RootedValue rval(cx);
7766 0 : if (!js::Call(cx, fval, frame, completion, &rval))
7767 0 : return false;
7768 :
7769 0 : return ParseResumptionValue(cx, rval, statusp, vp);
7770 : };
7771 :
7772 : /* static */ NativeObject*
7773 0 : DebuggerFrame::initClass(JSContext* cx, HandleObject dbgCtor, HandleObject obj)
7774 : {
7775 0 : Handle<GlobalObject*> global = obj.as<GlobalObject>();
7776 0 : RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
7777 :
7778 0 : return InitClass(cx, dbgCtor, objProto, &class_, construct, 0, properties_,
7779 0 : methods_, nullptr, nullptr);
7780 : }
7781 :
7782 : /* static */ DebuggerFrame*
7783 0 : DebuggerFrame::create(JSContext* cx, HandleObject proto, AbstractFramePtr referent,
7784 : const FrameIter* maybeIter, HandleNativeObject debugger)
7785 : {
7786 0 : JSObject* obj = NewObjectWithGivenProto(cx, &DebuggerFrame::class_, proto);
7787 0 : if (!obj)
7788 0 : return nullptr;
7789 :
7790 0 : DebuggerFrame& frame = obj->as<DebuggerFrame>();
7791 :
7792 : // Eagerly copy FrameIter data if we've already walked the stack.
7793 0 : if (maybeIter) {
7794 0 : AbstractFramePtr data = maybeIter->copyDataAsAbstractFramePtr();
7795 0 : if (!data)
7796 0 : return nullptr;
7797 0 : frame.setPrivate(data.raw());
7798 : } else {
7799 0 : frame.setPrivate(referent.raw());
7800 : }
7801 :
7802 0 : frame.setReservedSlot(JSSLOT_DEBUGFRAME_OWNER, ObjectValue(*debugger));
7803 :
7804 0 : return &frame;
7805 : }
7806 :
7807 : /* static */ bool
7808 0 : DebuggerFrame::getCallee(JSContext* cx, HandleDebuggerFrame frame,
7809 : MutableHandleDebuggerObject result)
7810 : {
7811 0 : MOZ_ASSERT(frame->isLive());
7812 :
7813 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
7814 0 : if (!referent.isFunctionFrame()) {
7815 0 : result.set(nullptr);
7816 0 : return true;
7817 : }
7818 :
7819 0 : Debugger* dbg = frame->owner();
7820 :
7821 0 : RootedObject callee(cx, referent.callee());
7822 0 : return dbg->wrapDebuggeeObject(cx, callee, result);
7823 : }
7824 :
7825 : /* static */ bool
7826 0 : DebuggerFrame::getIsConstructing(JSContext* cx, HandleDebuggerFrame frame, bool& result)
7827 : {
7828 0 : MOZ_ASSERT(frame->isLive());
7829 :
7830 0 : Maybe<FrameIter> maybeIter;
7831 0 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
7832 0 : return false;
7833 0 : FrameIter& iter = *maybeIter;
7834 :
7835 0 : result = iter.isFunctionFrame() && iter.isConstructing();
7836 0 : return true;
7837 : }
7838 :
7839 : static void
7840 0 : UpdateFrameIterPc(FrameIter& iter)
7841 : {
7842 0 : if (iter.abstractFramePtr().isWasmDebugFrame()) {
7843 : // Wasm debug frames don't need their pc updated -- it's null.
7844 0 : return;
7845 : }
7846 :
7847 0 : if (iter.abstractFramePtr().isRematerializedFrame()) {
7848 : #ifdef DEBUG
7849 : // Rematerialized frames don't need their pc updated. The reason we
7850 : // need to update pc is because we might get the same Debugger.Frame
7851 : // object for multiple re-entries into debugger code from debuggee
7852 : // code. This reentrancy is not possible with rematerialized frames,
7853 : // because when returning to debuggee code, we would have bailed out
7854 : // to baseline.
7855 : //
7856 : // We walk the stack to assert that it doesn't need updating.
7857 0 : jit::RematerializedFrame* frame = iter.abstractFramePtr().asRematerializedFrame();
7858 0 : jit::JitFrameLayout* jsFrame = (jit::JitFrameLayout*)frame->top();
7859 0 : jit::JitActivation* activation = iter.activation()->asJit();
7860 :
7861 0 : JSContext* cx = TlsContext.get();
7862 0 : MOZ_ASSERT(cx == activation->cx());
7863 :
7864 0 : ActivationIterator activationIter(cx);
7865 0 : while (activationIter.activation() != activation)
7866 0 : ++activationIter;
7867 :
7868 0 : jit::JitFrameIterator jitIter(activationIter);
7869 0 : while (!jitIter.isIonJS() || jitIter.jsFrame() != jsFrame)
7870 0 : ++jitIter;
7871 :
7872 0 : jit::InlineFrameIterator ionInlineIter(cx, &jitIter);
7873 0 : while (ionInlineIter.frameNo() != frame->frameNo())
7874 0 : ++ionInlineIter;
7875 :
7876 0 : MOZ_ASSERT(ionInlineIter.pc() == iter.pc());
7877 : #endif
7878 0 : return;
7879 : }
7880 :
7881 0 : iter.updatePcQuadratic();
7882 : }
7883 :
7884 : /* static */ bool
7885 0 : DebuggerFrame::getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
7886 : MutableHandleDebuggerEnvironment result)
7887 : {
7888 0 : MOZ_ASSERT(frame->isLive());
7889 :
7890 0 : Debugger* dbg = frame->owner();
7891 :
7892 0 : Maybe<FrameIter> maybeIter;
7893 0 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
7894 0 : return false;
7895 0 : FrameIter& iter = *maybeIter;
7896 :
7897 0 : Rooted<Env*> env(cx);
7898 : {
7899 0 : AutoCompartment ac(cx, iter.abstractFramePtr().environmentChain());
7900 0 : UpdateFrameIterPc(iter);
7901 0 : env = GetDebugEnvironmentForFrame(cx, iter.abstractFramePtr(), iter.pc());
7902 0 : if (!env)
7903 0 : return false;
7904 : }
7905 :
7906 0 : return dbg->wrapEnvironment(cx, env, result);
7907 : }
7908 :
7909 : /* static */ bool
7910 0 : DebuggerFrame::getIsGenerator(HandleDebuggerFrame frame)
7911 : {
7912 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
7913 0 : return referent.hasScript() &&
7914 0 : (referent.script()->isStarGenerator() ||
7915 0 : referent.script()->isLegacyGenerator());
7916 : }
7917 :
7918 : /* static */ bool
7919 0 : DebuggerFrame::getOffset(JSContext* cx, HandleDebuggerFrame frame, size_t& result)
7920 : {
7921 0 : MOZ_ASSERT(frame->isLive());
7922 :
7923 0 : Maybe<FrameIter> maybeIter;
7924 0 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
7925 0 : return false;
7926 0 : FrameIter& iter = *maybeIter;
7927 :
7928 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
7929 0 : if (referent.isWasmDebugFrame()) {
7930 0 : iter.wasmUpdateBytecodeOffset();
7931 0 : result = iter.wasmBytecodeOffset();
7932 : } else {
7933 0 : JSScript* script = iter.script();
7934 0 : UpdateFrameIterPc(iter);
7935 0 : jsbytecode* pc = iter.pc();
7936 0 : result = script->pcToOffset(pc);
7937 : }
7938 0 : return true;
7939 : }
7940 :
7941 : /* static */ bool
7942 0 : DebuggerFrame::getOlder(JSContext* cx, HandleDebuggerFrame frame,
7943 : MutableHandleDebuggerFrame result)
7944 : {
7945 0 : MOZ_ASSERT(frame->isLive());
7946 :
7947 0 : Debugger* dbg = frame->owner();
7948 :
7949 0 : Maybe<FrameIter> maybeIter;
7950 0 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
7951 0 : return false;
7952 0 : FrameIter& iter = *maybeIter;
7953 :
7954 0 : for (++iter; !iter.done(); ++iter) {
7955 0 : if (dbg->observesFrame(iter)) {
7956 0 : if (iter.isIon() && !iter.ensureHasRematerializedFrame(cx))
7957 0 : return false;
7958 0 : return dbg->getScriptFrame(cx, iter, result);
7959 : }
7960 : }
7961 :
7962 0 : result.set(nullptr);
7963 0 : return true;
7964 : }
7965 :
7966 : /* static */ bool
7967 0 : DebuggerFrame::getThis(JSContext* cx, HandleDebuggerFrame frame, MutableHandleValue result)
7968 : {
7969 0 : MOZ_ASSERT(frame->isLive());
7970 :
7971 0 : if (!requireScriptReferent(cx, frame))
7972 0 : return false;
7973 :
7974 0 : Debugger* dbg = frame->owner();
7975 :
7976 0 : Maybe<FrameIter> maybeIter;
7977 0 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
7978 0 : return false;
7979 0 : FrameIter& iter = *maybeIter;
7980 :
7981 : {
7982 0 : AbstractFramePtr frame = iter.abstractFramePtr();
7983 0 : AutoCompartment ac(cx, frame.environmentChain());
7984 :
7985 0 : UpdateFrameIterPc(iter);
7986 :
7987 0 : if (!GetThisValueForDebuggerMaybeOptimizedOut(cx, frame, iter.pc(), result))
7988 0 : return false;
7989 : }
7990 :
7991 0 : return dbg->wrapDebuggeeValue(cx, result);
7992 : }
7993 :
7994 : /* static */ DebuggerFrameType
7995 0 : DebuggerFrame::getType(HandleDebuggerFrame frame)
7996 : {
7997 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
7998 :
7999 : /*
8000 : * Indirect eval frames are both isGlobalFrame() and isEvalFrame(), so the
8001 : * order of checks here is significant.
8002 : */
8003 0 : if (referent.isEvalFrame())
8004 0 : return DebuggerFrameType::Eval;
8005 0 : else if (referent.isGlobalFrame())
8006 0 : return DebuggerFrameType::Global;
8007 0 : else if (referent.isFunctionFrame())
8008 0 : return DebuggerFrameType::Call;
8009 0 : else if (referent.isModuleFrame())
8010 0 : return DebuggerFrameType::Module;
8011 0 : else if (referent.isWasmDebugFrame())
8012 0 : return DebuggerFrameType::WasmCall;
8013 0 : MOZ_CRASH("Unknown frame type");
8014 : }
8015 :
8016 : /* static */ DebuggerFrameImplementation
8017 0 : DebuggerFrame::getImplementation(HandleDebuggerFrame frame)
8018 : {
8019 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
8020 :
8021 0 : if (referent.isBaselineFrame())
8022 0 : return DebuggerFrameImplementation::Baseline;
8023 0 : else if (referent.isRematerializedFrame())
8024 0 : return DebuggerFrameImplementation::Ion;
8025 0 : else if (referent.isWasmDebugFrame())
8026 0 : return DebuggerFrameImplementation::Wasm;
8027 0 : return DebuggerFrameImplementation::Interpreter;
8028 : }
8029 :
8030 : /*
8031 : * If succesful, transfers the ownership of the given `handler` to this
8032 : * Debugger.Frame. Note that on failure, the ownership of `handler` is not
8033 : * transferred, and the caller is responsible for cleaning it up.
8034 : */
8035 : /* static */ bool
8036 0 : DebuggerFrame::setOnStepHandler(JSContext* cx, HandleDebuggerFrame frame, OnStepHandler* handler)
8037 : {
8038 0 : MOZ_ASSERT(frame->isLive());
8039 :
8040 0 : OnStepHandler* prior = frame->onStepHandler();
8041 0 : if (prior && handler != prior) {
8042 0 : prior->drop();
8043 : }
8044 :
8045 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
8046 0 : if (referent.isWasmDebugFrame()) {
8047 0 : wasm::Instance* instance = referent.asWasmDebugFrame()->instance();
8048 0 : wasm::DebugFrame* wasmFrame = referent.asWasmDebugFrame();
8049 0 : if (handler && !prior) {
8050 : // Single stepping toggled off->on.
8051 0 : if (!instance->debug().incrementStepModeCount(cx, wasmFrame->funcIndex()))
8052 0 : return false;
8053 0 : } else if (!handler && prior) {
8054 : // Single stepping toggled on->off.
8055 0 : FreeOp* fop = cx->runtime()->defaultFreeOp();
8056 0 : if (!instance->debug().decrementStepModeCount(fop, wasmFrame->funcIndex()))
8057 0 : return false;
8058 : }
8059 : } else {
8060 0 : if (handler && !prior) {
8061 : // Single stepping toggled off->on.
8062 0 : AutoCompartment ac(cx, referent.environmentChain());
8063 : // Ensure observability *before* incrementing the step mode count.
8064 : // Calling this function after calling incrementStepModeCount
8065 : // will make it a no-op.
8066 0 : Debugger* dbg = frame->owner();
8067 0 : if (!dbg->ensureExecutionObservabilityOfScript(cx, referent.script()))
8068 0 : return false;
8069 0 : if (!referent.script()->incrementStepModeCount(cx))
8070 0 : return false;
8071 0 : } else if (!handler && prior) {
8072 : // Single stepping toggled on->off.
8073 0 : referent.script()->decrementStepModeCount(cx->runtime()->defaultFreeOp());
8074 : }
8075 : }
8076 :
8077 : /* Now that the step mode switch has succeeded, we can install the handler. */
8078 0 : frame->setReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER,
8079 0 : handler ? PrivateValue(handler) : UndefinedValue());
8080 0 : return true;
8081 : }
8082 :
8083 : /* static */ bool
8084 0 : DebuggerFrame::getArguments(JSContext *cx, HandleDebuggerFrame frame,
8085 : MutableHandleDebuggerArguments result)
8086 : {
8087 0 : Value argumentsv = frame->getReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS);
8088 0 : if (!argumentsv.isUndefined()) {
8089 0 : result.set(argumentsv.isObject() ? &argumentsv.toObject().as<DebuggerArguments>() : nullptr);
8090 0 : return true;
8091 : }
8092 :
8093 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
8094 :
8095 0 : RootedDebuggerArguments arguments(cx);
8096 0 : if (referent.hasArgs()) {
8097 0 : Rooted<GlobalObject*> global(cx, &frame->global());
8098 0 : RootedObject proto(cx, GlobalObject::getOrCreateArrayPrototype(cx, global));
8099 0 : if (!proto)
8100 0 : return false;
8101 0 : arguments = DebuggerArguments::create(cx, proto, frame);
8102 0 : if (!arguments)
8103 0 : return false;
8104 : } else {
8105 0 : arguments = nullptr;
8106 : }
8107 :
8108 0 : result.set(arguments);
8109 0 : frame->setReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS, ObjectOrNullValue(result));
8110 0 : return true;
8111 : }
8112 :
8113 : /*
8114 : * Evaluate |chars[0..length-1]| in the environment |env|, treating that
8115 : * source as appearing starting at |lineno| in |filename|. Store the return
8116 : * value in |*rval|. Use |thisv| as the 'this' value.
8117 : *
8118 : * If |frame| is non-nullptr, evaluate as for a direct eval in that frame; |env|
8119 : * must be either |frame|'s DebugScopeObject, or some extension of that
8120 : * environment; either way, |frame|'s scope is where newly declared variables
8121 : * go. In this case, |frame| must have a computed 'this' value, equal to |thisv|.
8122 : */
8123 : static bool
8124 0 : EvaluateInEnv(JSContext* cx, Handle<Env*> env, AbstractFramePtr frame,
8125 : jsbytecode* pc, mozilla::Range<const char16_t> chars, const char* filename,
8126 : unsigned lineno, MutableHandleValue rval)
8127 : {
8128 0 : assertSameCompartment(cx, env, frame);
8129 0 : MOZ_ASSERT_IF(frame, pc);
8130 :
8131 0 : CompileOptions options(cx);
8132 0 : options.setIsRunOnce(true)
8133 0 : .setNoScriptRval(false)
8134 0 : .setFileAndLine(filename, lineno)
8135 0 : .setCanLazilyParse(false)
8136 0 : .setIntroductionType("debugger eval")
8137 0 : .maybeMakeStrictMode(frame ? frame.script()->strict() : false);
8138 0 : RootedScript callerScript(cx, frame ? frame.script() : nullptr);
8139 0 : SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), SourceBufferHolder::NoOwnership);
8140 0 : RootedScript script(cx);
8141 :
8142 : ScopeKind scopeKind;
8143 0 : if (IsGlobalLexicalEnvironment(env))
8144 0 : scopeKind = ScopeKind::Global;
8145 : else
8146 0 : scopeKind = ScopeKind::NonSyntactic;
8147 :
8148 0 : if (frame) {
8149 0 : MOZ_ASSERT(scopeKind == ScopeKind::NonSyntactic);
8150 0 : RootedScope scope(cx, GlobalScope::createEmpty(cx, ScopeKind::NonSyntactic));
8151 0 : if (!scope)
8152 0 : return false;
8153 0 : script = frontend::CompileEvalScript(cx, cx->tempLifoAlloc(), env, scope,
8154 0 : options, srcBuf);
8155 0 : if (script)
8156 0 : script->setActiveEval();
8157 : } else {
8158 : // Do not consider executeInGlobal{WithBindings} as an eval, but instead
8159 : // as executing a series of statements at the global level. This is to
8160 : // circumvent the fresh lexical scope that all eval have, so that the
8161 : // users of executeInGlobal, like the web console, may add new bindings to
8162 : // the global scope.
8163 0 : script = frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(), scopeKind, options,
8164 0 : srcBuf);
8165 : }
8166 :
8167 0 : if (!script)
8168 0 : return false;
8169 :
8170 0 : return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address());
8171 : }
8172 :
8173 : static bool
8174 0 : DebuggerGenericEval(JSContext* cx, const mozilla::Range<const char16_t> chars,
8175 : HandleObject bindings, const EvalOptions& options,
8176 : JSTrapStatus& status, MutableHandleValue value,
8177 : Debugger* dbg, HandleObject envArg, FrameIter* iter)
8178 : {
8179 : /* Either we're specifying the frame, or a global. */
8180 0 : MOZ_ASSERT_IF(iter, !envArg);
8181 0 : MOZ_ASSERT_IF(!iter, envArg && IsGlobalLexicalEnvironment(envArg));
8182 :
8183 : /*
8184 : * Gather keys and values of bindings, if any. This must be done in the
8185 : * debugger compartment, since that is where any exceptions must be
8186 : * thrown.
8187 : */
8188 0 : AutoIdVector keys(cx);
8189 0 : AutoValueVector values(cx);
8190 0 : if (bindings) {
8191 0 : if (!GetPropertyKeys(cx, bindings, JSITER_OWNONLY, &keys) ||
8192 0 : !values.growBy(keys.length()))
8193 : {
8194 0 : return false;
8195 : }
8196 0 : for (size_t i = 0; i < keys.length(); i++) {
8197 0 : MutableHandleValue valp = values[i];
8198 0 : if (!GetProperty(cx, bindings, bindings, keys[i], valp) ||
8199 0 : !dbg->unwrapDebuggeeValue(cx, valp))
8200 : {
8201 0 : return false;
8202 : }
8203 : }
8204 : }
8205 :
8206 0 : Maybe<AutoCompartment> ac;
8207 0 : if (iter)
8208 0 : ac.emplace(cx, iter->environmentChain(cx));
8209 : else
8210 0 : ac.emplace(cx, envArg);
8211 :
8212 0 : Rooted<Env*> env(cx);
8213 0 : if (iter) {
8214 0 : env = GetDebugEnvironmentForFrame(cx, iter->abstractFramePtr(), iter->pc());
8215 0 : if (!env)
8216 0 : return false;
8217 : } else {
8218 0 : env = envArg;
8219 : }
8220 :
8221 : /* If evalWithBindings, create the inner environment. */
8222 0 : if (bindings) {
8223 0 : RootedPlainObject nenv(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
8224 0 : if (!nenv)
8225 0 : return false;
8226 0 : RootedId id(cx);
8227 0 : for (size_t i = 0; i < keys.length(); i++) {
8228 0 : id = keys[i];
8229 0 : cx->markId(id);
8230 0 : MutableHandleValue val = values[i];
8231 0 : if (!cx->compartment()->wrap(cx, val) ||
8232 0 : !NativeDefineProperty(cx, nenv, id, val, nullptr, nullptr, 0))
8233 : {
8234 0 : return false;
8235 : }
8236 : }
8237 :
8238 0 : AutoObjectVector envChain(cx);
8239 0 : if (!envChain.append(nenv))
8240 0 : return false;
8241 :
8242 0 : RootedObject newEnv(cx);
8243 0 : if (!CreateObjectsForEnvironmentChain(cx, envChain, env, &newEnv))
8244 0 : return false;
8245 :
8246 0 : env = newEnv;
8247 : }
8248 :
8249 : /* Run the code and produce the completion value. */
8250 0 : LeaveDebuggeeNoExecute nnx(cx);
8251 0 : RootedValue rval(cx);
8252 0 : AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr();
8253 0 : jsbytecode* pc = iter ? iter->pc() : nullptr;
8254 :
8255 0 : bool ok = EvaluateInEnv(cx, env, frame, pc, chars,
8256 0 : options.filename() ? options.filename() : "debugger eval code",
8257 0 : options.lineno(), &rval);
8258 0 : Debugger::resultToCompletion(cx, ok, rval, &status, value);
8259 0 : ac.reset();
8260 0 : return dbg->wrapDebuggeeValue(cx, value);
8261 : }
8262 :
8263 : /* static */ bool
8264 0 : DebuggerFrame::eval(JSContext* cx, HandleDebuggerFrame frame, mozilla::Range<const char16_t> chars,
8265 : HandleObject bindings, const EvalOptions& options, JSTrapStatus& status,
8266 : MutableHandleValue value)
8267 : {
8268 0 : MOZ_ASSERT(frame->isLive());
8269 0 : if (!requireScriptReferent(cx, frame))
8270 0 : return false;
8271 :
8272 0 : Debugger* dbg = frame->owner();
8273 :
8274 0 : Maybe<FrameIter> maybeIter;
8275 0 : if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
8276 0 : return false;
8277 0 : FrameIter& iter = *maybeIter;
8278 :
8279 0 : UpdateFrameIterPc(iter);
8280 :
8281 0 : return DebuggerGenericEval(cx, chars, bindings, options, status, value, dbg, nullptr, &iter);
8282 : }
8283 :
8284 : /* statuc */ bool
8285 0 : DebuggerFrame::isLive() const
8286 : {
8287 0 : return !!getPrivate();
8288 : }
8289 :
8290 : OnStepHandler*
8291 0 : DebuggerFrame::onStepHandler() const
8292 : {
8293 0 : Value value = getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);
8294 0 : return value.isUndefined() ? nullptr : static_cast<OnStepHandler*>(value.toPrivate());
8295 : }
8296 :
8297 : OnPopHandler*
8298 0 : DebuggerFrame::onPopHandler() const
8299 : {
8300 0 : Value value = getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER);
8301 0 : return value.isUndefined() ? nullptr : static_cast<OnPopHandler*>(value.toPrivate());
8302 : }
8303 :
8304 : void
8305 0 : DebuggerFrame::setOnPopHandler(OnPopHandler* handler)
8306 : {
8307 0 : MOZ_ASSERT(isLive());
8308 :
8309 0 : OnPopHandler* prior = onPopHandler();
8310 0 : if (prior && prior != handler)
8311 0 : prior->drop();
8312 :
8313 0 : setReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER,
8314 0 : handler ? PrivateValue(handler) : UndefinedValue());
8315 0 : }
8316 :
8317 : static bool
8318 0 : DebuggerFrame_requireLive(JSContext* cx, HandleDebuggerFrame frame)
8319 : {
8320 0 : if (!frame->isLive()) {
8321 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
8322 0 : "Debugger.Frame");
8323 0 : return false;
8324 : }
8325 :
8326 0 : return true;
8327 : }
8328 :
8329 : /* static */ AbstractFramePtr
8330 0 : DebuggerFrame::getReferent(HandleDebuggerFrame frame)
8331 : {
8332 0 : AbstractFramePtr referent = AbstractFramePtr::FromRaw(frame->getPrivate());
8333 0 : if (referent.isScriptFrameIterData()) {
8334 0 : FrameIter iter(*(FrameIter::Data*)(referent.raw()));
8335 0 : referent = iter.abstractFramePtr();
8336 : }
8337 0 : return referent;
8338 : }
8339 :
8340 : /* static */ bool
8341 0 : DebuggerFrame::getFrameIter(JSContext* cx, HandleDebuggerFrame frame,
8342 : Maybe<FrameIter>& result)
8343 : {
8344 0 : AbstractFramePtr referent = AbstractFramePtr::FromRaw(frame->getPrivate());
8345 0 : if (referent.isScriptFrameIterData()) {
8346 0 : result.emplace(*reinterpret_cast<FrameIter::Data*>(referent.raw()));
8347 : } else {
8348 0 : result.emplace(cx, FrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK);
8349 0 : FrameIter& iter = *result;
8350 0 : while (!iter.hasUsableAbstractFramePtr() || iter.abstractFramePtr() != referent)
8351 0 : ++iter;
8352 0 : AbstractFramePtr data = iter.copyDataAsAbstractFramePtr();
8353 0 : if (!data)
8354 0 : return false;
8355 0 : frame->setPrivate(data.raw());
8356 : }
8357 0 : return true;
8358 : }
8359 :
8360 : /* static */ bool
8361 0 : DebuggerFrame::requireScriptReferent(JSContext* cx, HandleDebuggerFrame frame)
8362 : {
8363 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
8364 0 : if (!referent.hasScript()) {
8365 0 : RootedValue frameobj(cx, ObjectValue(*frame));
8366 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT,
8367 : JSDVG_SEARCH_STACK, frameobj, nullptr,
8368 0 : "a script frame", nullptr);
8369 0 : return false;
8370 : }
8371 0 : return true;
8372 : }
8373 :
8374 : static void
8375 0 : DebuggerFrame_freeScriptFrameIterData(FreeOp* fop, JSObject* obj)
8376 : {
8377 0 : AbstractFramePtr frame = AbstractFramePtr::FromRaw(obj->as<NativeObject>().getPrivate());
8378 0 : if (frame.isScriptFrameIterData())
8379 0 : fop->delete_((FrameIter::Data*) frame.raw());
8380 0 : obj->as<NativeObject>().setPrivate(nullptr);
8381 0 : }
8382 :
8383 : static void
8384 0 : DebuggerFrame_maybeDecrementFrameScriptStepModeCount(FreeOp* fop, AbstractFramePtr frame,
8385 : NativeObject* frameobj)
8386 : {
8387 : /* If this frame has an onStep handler, decrement the script's count. */
8388 0 : if (frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
8389 0 : return;
8390 0 : if (frame.isWasmDebugFrame()) {
8391 0 : wasm::Instance* instance = frame.wasmInstance();
8392 0 : instance->debug().decrementStepModeCount(fop, frame.asWasmDebugFrame()->funcIndex());
8393 : } else {
8394 0 : frame.script()->decrementStepModeCount(fop);
8395 : }
8396 : }
8397 :
8398 : static void
8399 0 : DebuggerFrame_finalize(FreeOp* fop, JSObject* obj)
8400 : {
8401 0 : MOZ_ASSERT(fop->maybeOnHelperThread());
8402 0 : DebuggerFrame_freeScriptFrameIterData(fop, obj);
8403 0 : OnStepHandler* onStepHandler = obj->as<DebuggerFrame>().onStepHandler();
8404 0 : if (onStepHandler)
8405 0 : onStepHandler->drop();
8406 0 : OnPopHandler* onPopHandler = obj->as<DebuggerFrame>().onPopHandler();
8407 0 : if (onPopHandler)
8408 0 : onPopHandler->drop();
8409 0 : }
8410 :
8411 : static void
8412 0 : DebuggerFrame_trace(JSTracer* trc, JSObject* obj)
8413 : {
8414 0 : OnStepHandler* onStepHandler = obj->as<DebuggerFrame>().onStepHandler();
8415 0 : if (onStepHandler)
8416 0 : onStepHandler->trace(trc);
8417 0 : OnPopHandler* onPopHandler = obj->as<DebuggerFrame>().onPopHandler();
8418 0 : if (onPopHandler)
8419 0 : onPopHandler->trace(trc);
8420 0 : }
8421 :
8422 : static DebuggerFrame*
8423 0 : DebuggerFrame_checkThis(JSContext* cx, const CallArgs& args, const char* fnname, bool checkLive)
8424 : {
8425 0 : JSObject* thisobj = NonNullObject(cx, args.thisv());
8426 0 : if (!thisobj)
8427 0 : return nullptr;
8428 0 : if (thisobj->getClass() != &DebuggerFrame::class_) {
8429 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
8430 0 : "Debugger.Frame", fnname, thisobj->getClass()->name);
8431 0 : return nullptr;
8432 : }
8433 :
8434 0 : RootedDebuggerFrame frame(cx, &thisobj->as<DebuggerFrame>());
8435 :
8436 : /*
8437 : * Forbid Debugger.Frame.prototype, which is of class DebuggerFrame::class_
8438 : * but isn't really a working Debugger.Frame object. The prototype object
8439 : * is distinguished by having a nullptr private value. Also, forbid popped
8440 : * frames.
8441 : */
8442 0 : if (!frame->getPrivate() &&
8443 0 : frame->getReservedSlot(JSSLOT_DEBUGFRAME_OWNER).isUndefined())
8444 : {
8445 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
8446 0 : "Debugger.Frame", fnname, "prototype object");
8447 0 : return nullptr;
8448 : }
8449 :
8450 0 : if (checkLive) {
8451 0 : if (!DebuggerFrame_requireLive(cx, frame))
8452 0 : return nullptr;
8453 : }
8454 :
8455 0 : return frame;
8456 : }
8457 :
8458 : /*
8459 : * To make frequently fired hooks like onEnterFrame more performant,
8460 : * Debugger.Frame methods should not create a FrameIter unless it
8461 : * absolutely needs to. That is, unless the method has to call a method on
8462 : * FrameIter that's otherwise not available on AbstractFramePtr.
8463 : *
8464 : * When a Debugger.Frame is first created, its private slot is set to the
8465 : * AbstractFramePtr itself. The first time the users asks for a
8466 : * FrameIter, we construct one, have it settle on the frame pointed to
8467 : * by the AbstractFramePtr and cache its internal Data in the Debugger.Frame
8468 : * object's private slot. Subsequent uses of the Debugger.Frame object will
8469 : * always create a FrameIter from the cached Data.
8470 : *
8471 : * Methods that only need the AbstractFramePtr should use THIS_FRAME.
8472 : * Methods that need a FrameIterator should use THIS_FRAME_ITER.
8473 : */
8474 :
8475 : #define THIS_DEBUGGER_FRAME(cx, argc, vp, fnname, args, frame) \
8476 : CallArgs args = CallArgsFromVp(argc, vp); \
8477 : RootedDebuggerFrame frame(cx, DebuggerFrame_checkThis(cx, args, fnname, true)); \
8478 : if (!frame) \
8479 : return false;
8480 :
8481 : #define THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj) \
8482 : CallArgs args = CallArgsFromVp(argc, vp); \
8483 : RootedNativeObject thisobj(cx, DebuggerFrame_checkThis(cx, args, fnname, true)); \
8484 : if (!thisobj) \
8485 : return false
8486 :
8487 : #define THIS_FRAME(cx, argc, vp, fnname, args, thisobj, frame) \
8488 : THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj); \
8489 : AbstractFramePtr frame = AbstractFramePtr::FromRaw(thisobj->getPrivate()); \
8490 : if (frame.isScriptFrameIterData()) { \
8491 : FrameIter iter(*(FrameIter::Data*)(frame.raw())); \
8492 : frame = iter.abstractFramePtr(); \
8493 : }
8494 :
8495 : #define THIS_FRAME_ITER(cx, argc, vp, fnname, args, thisobj, maybeIter, iter) \
8496 : THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj); \
8497 : Maybe<FrameIter> maybeIter; \
8498 : { \
8499 : AbstractFramePtr f = AbstractFramePtr::FromRaw(thisobj->getPrivate()); \
8500 : if (f.isScriptFrameIterData()) { \
8501 : maybeIter.emplace(*(FrameIter::Data*)(f.raw())); \
8502 : } else { \
8503 : maybeIter.emplace(cx, FrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK); \
8504 : FrameIter& iter = *maybeIter; \
8505 : while (!iter.hasUsableAbstractFramePtr() || iter.abstractFramePtr() != f) \
8506 : ++iter; \
8507 : AbstractFramePtr data = iter.copyDataAsAbstractFramePtr(); \
8508 : if (!data) \
8509 : return false; \
8510 : thisobj->setPrivate(data.raw()); \
8511 : } \
8512 : } \
8513 : FrameIter& iter = *maybeIter
8514 :
8515 : #define THIS_FRAME_OWNER(cx, argc, vp, fnname, args, thisobj, frame, dbg) \
8516 : THIS_FRAME(cx, argc, vp, fnname, args, thisobj, frame); \
8517 : Debugger* dbg = Debugger::fromChildJSObject(thisobj)
8518 :
8519 : #define THIS_FRAME_OWNER_ITER(cx, argc, vp, fnname, args, thisobj, maybeIter, iter, dbg) \
8520 : THIS_FRAME_ITER(cx, argc, vp, fnname, args, thisobj, maybeIter, iter); \
8521 : Debugger* dbg = Debugger::fromChildJSObject(thisobj)
8522 :
8523 : /* static */ bool
8524 0 : DebuggerFrame::typeGetter(JSContext* cx, unsigned argc, Value* vp)
8525 : {
8526 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get type", args, frame);
8527 :
8528 0 : DebuggerFrameType type = DebuggerFrame::getType(frame);
8529 :
8530 : JSString* str;
8531 0 : switch (type) {
8532 : case DebuggerFrameType::Eval:
8533 0 : str = cx->names().eval;
8534 0 : break;
8535 : case DebuggerFrameType::Global:
8536 0 : str = cx->names().global;
8537 0 : break;
8538 : case DebuggerFrameType::Call:
8539 0 : str = cx->names().call;
8540 0 : break;
8541 : case DebuggerFrameType::Module:
8542 0 : str = cx->names().module;
8543 0 : break;
8544 : case DebuggerFrameType::WasmCall:
8545 0 : str = cx->names().wasmcall;
8546 0 : break;
8547 : default:
8548 0 : MOZ_CRASH("bad DebuggerFrameType value");
8549 : }
8550 :
8551 0 : args.rval().setString(str);
8552 0 : return true;
8553 : }
8554 :
8555 : /* static */ bool
8556 0 : DebuggerFrame::implementationGetter(JSContext* cx, unsigned argc, Value* vp)
8557 : {
8558 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get implementation", args, frame);
8559 :
8560 0 : DebuggerFrameImplementation implementation = DebuggerFrame::getImplementation(frame);
8561 :
8562 : const char* s;
8563 0 : switch (implementation) {
8564 : case DebuggerFrameImplementation::Baseline:
8565 0 : s = "baseline";
8566 0 : break;
8567 : case DebuggerFrameImplementation::Ion:
8568 0 : s = "ion";
8569 0 : break;
8570 : case DebuggerFrameImplementation::Interpreter:
8571 0 : s = "interpreter";
8572 0 : break;
8573 : case DebuggerFrameImplementation::Wasm:
8574 0 : s = "wasm";
8575 0 : break;
8576 : default:
8577 0 : MOZ_CRASH("bad DebuggerFrameImplementation value");
8578 : }
8579 :
8580 0 : JSAtom* str = Atomize(cx, s, strlen(s));
8581 0 : if (!str)
8582 0 : return false;
8583 :
8584 0 : args.rval().setString(str);
8585 0 : return true;
8586 : }
8587 :
8588 : /* static */ bool
8589 0 : DebuggerFrame::environmentGetter(JSContext* cx, unsigned argc, Value* vp)
8590 : {
8591 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get environment", args, frame);
8592 :
8593 0 : RootedDebuggerEnvironment result(cx);
8594 0 : if (!DebuggerFrame::getEnvironment(cx, frame, &result))
8595 0 : return false;
8596 :
8597 0 : args.rval().setObject(*result);
8598 0 : return true;
8599 : }
8600 :
8601 : /* static */ bool
8602 0 : DebuggerFrame::calleeGetter(JSContext* cx, unsigned argc, Value* vp)
8603 : {
8604 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get callee", args, frame);
8605 :
8606 0 : RootedDebuggerObject result(cx);
8607 0 : if (!DebuggerFrame::getCallee(cx, frame, &result))
8608 0 : return false;
8609 :
8610 0 : args.rval().setObjectOrNull(result);
8611 0 : return true;
8612 : }
8613 :
8614 : /* static */ bool
8615 0 : DebuggerFrame::generatorGetter(JSContext* cx, unsigned argc, Value* vp)
8616 : {
8617 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get callee", args, frame);
8618 :
8619 0 : args.rval().setBoolean(DebuggerFrame::getIsGenerator(frame));
8620 0 : return true;
8621 : }
8622 :
8623 : /* static */ bool
8624 0 : DebuggerFrame::constructingGetter(JSContext* cx, unsigned argc, Value* vp)
8625 : {
8626 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get callee", args, frame);
8627 :
8628 : bool result;
8629 0 : if (!DebuggerFrame::getIsConstructing(cx, frame, result))
8630 0 : return false;
8631 :
8632 0 : args.rval().setBoolean(result);
8633 0 : return true;
8634 : }
8635 :
8636 : /* static */ bool
8637 0 : DebuggerFrame::thisGetter(JSContext* cx, unsigned argc, Value* vp)
8638 : {
8639 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get this", args, frame);
8640 :
8641 0 : return DebuggerFrame::getThis(cx, frame, args.rval());
8642 : }
8643 :
8644 : /* static */ bool
8645 0 : DebuggerFrame::olderGetter(JSContext* cx, unsigned argc, Value* vp)
8646 : {
8647 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get older", args, frame);
8648 :
8649 0 : RootedDebuggerFrame result(cx);
8650 0 : if (!DebuggerFrame::getOlder(cx, frame, &result))
8651 0 : return false;
8652 :
8653 0 : args.rval().setObjectOrNull(result);
8654 0 : return true;
8655 : }
8656 :
8657 : /* The getter used for each element of frame.arguments. See DebuggerFrame_getArguments. */
8658 : static bool
8659 0 : DebuggerArguments_getArg(JSContext* cx, unsigned argc, Value* vp)
8660 : {
8661 0 : CallArgs args = CallArgsFromVp(argc, vp);
8662 0 : int32_t i = args.callee().as<JSFunction>().getExtendedSlot(0).toInt32();
8663 :
8664 : /* Check that the this value is an Arguments object. */
8665 0 : RootedObject argsobj(cx, NonNullObject(cx, args.thisv()));
8666 0 : if (!argsobj)
8667 0 : return false;
8668 0 : if (argsobj->getClass() != &DebuggerArguments::class_) {
8669 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
8670 0 : "Arguments", "getArgument", argsobj->getClass()->name);
8671 0 : return false;
8672 : }
8673 :
8674 : /*
8675 : * Put the Debugger.Frame into the this-value slot, then use THIS_FRAME
8676 : * to check that it is still live and get the fp.
8677 : */
8678 0 : args.setThis(argsobj->as<NativeObject>().getReservedSlot(JSSLOT_DEBUGARGUMENTS_FRAME));
8679 0 : THIS_FRAME(cx, argc, vp, "get argument", ca2, thisobj, frame);
8680 :
8681 : // TODO handle wasm frame arguments -- they are not yet reflectable.
8682 0 : MOZ_ASSERT(!frame.isWasmDebugFrame(), "a wasm frame args");
8683 :
8684 : /*
8685 : * Since getters can be extracted and applied to other objects,
8686 : * there is no guarantee this object has an ith argument.
8687 : */
8688 0 : MOZ_ASSERT(i >= 0);
8689 0 : RootedValue arg(cx);
8690 0 : RootedScript script(cx);
8691 0 : if (unsigned(i) < frame.numActualArgs()) {
8692 0 : script = frame.script();
8693 : {
8694 0 : AutoCompartment ac(cx, script);
8695 0 : if (!script->ensureHasAnalyzedArgsUsage(cx))
8696 0 : return false;
8697 : }
8698 0 : if (unsigned(i) < frame.numFormalArgs()) {
8699 0 : for (PositionalFormalParameterIter fi(script); fi; fi++) {
8700 0 : if (fi.argumentSlot() == unsigned(i)) {
8701 : // We might've been called before the CallObject was
8702 : // created.
8703 0 : if (fi.closedOver() && frame.hasInitialEnvironment())
8704 0 : arg = frame.callObj().aliasedBinding(fi);
8705 : else
8706 0 : arg = frame.unaliasedActual(i, DONT_CHECK_ALIASING);
8707 0 : break;
8708 : }
8709 : }
8710 0 : } else if (script->argsObjAliasesFormals() && frame.hasArgsObj()) {
8711 0 : arg = frame.argsObj().arg(i);
8712 : } else {
8713 0 : arg = frame.unaliasedActual(i, DONT_CHECK_ALIASING);
8714 : }
8715 : } else {
8716 0 : arg.setUndefined();
8717 : }
8718 :
8719 0 : if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &arg))
8720 0 : return false;
8721 0 : args.rval().set(arg);
8722 0 : return true;
8723 : }
8724 :
8725 : /* static */ DebuggerArguments*
8726 0 : DebuggerArguments::create(JSContext* cx, HandleObject proto, HandleDebuggerFrame frame)
8727 : {
8728 0 : AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
8729 :
8730 0 : RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &DebuggerArguments::class_, proto));
8731 0 : if (!obj)
8732 0 : return nullptr;
8733 :
8734 0 : SetReservedSlot(obj, FRAME_SLOT, ObjectValue(*frame));
8735 :
8736 0 : MOZ_ASSERT(referent.numActualArgs() <= 0x7fffffff);
8737 0 : unsigned fargc = referent.numActualArgs();
8738 0 : RootedValue fargcVal(cx, Int32Value(fargc));
8739 0 : if (!NativeDefineProperty(cx, obj, cx->names().length, fargcVal, nullptr, nullptr,
8740 : JSPROP_PERMANENT | JSPROP_READONLY))
8741 : {
8742 0 : return nullptr;
8743 : }
8744 :
8745 0 : Rooted<jsid> id(cx);
8746 0 : for (unsigned i = 0; i < fargc; i++) {
8747 0 : RootedFunction getobj(cx);
8748 0 : getobj = NewNativeFunction(cx, DebuggerArguments_getArg, 0, nullptr,
8749 0 : gc::AllocKind::FUNCTION_EXTENDED);
8750 0 : if (!getobj)
8751 0 : return nullptr;
8752 0 : id = INT_TO_JSID(i);
8753 0 : if (!getobj ||
8754 0 : !NativeDefineProperty(cx, obj, id, UndefinedHandleValue,
8755 0 : JS_DATA_TO_FUNC_PTR(GetterOp, getobj.get()), nullptr,
8756 : JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER))
8757 : {
8758 0 : return nullptr;
8759 : }
8760 0 : getobj->setExtendedSlot(0, Int32Value(i));
8761 : }
8762 :
8763 0 : return &obj->as<DebuggerArguments>();
8764 : }
8765 :
8766 : /* static */ bool
8767 0 : DebuggerFrame::argumentsGetter(JSContext* cx, unsigned argc, Value* vp)
8768 : {
8769 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get arguments", args, frame);
8770 :
8771 0 : RootedDebuggerArguments result(cx);
8772 0 : if (!DebuggerFrame::getArguments(cx, frame, &result))
8773 0 : return false;
8774 :
8775 0 : args.rval().setObjectOrNull(result);
8776 0 : return true;
8777 : }
8778 :
8779 : static bool
8780 0 : DebuggerFrame_getScript(JSContext* cx, unsigned argc, Value* vp)
8781 : {
8782 0 : THIS_FRAME(cx, argc, vp, "get script", args, thisobj, frame);
8783 0 : Debugger* debug = Debugger::fromChildJSObject(thisobj);
8784 :
8785 0 : RootedObject scriptObject(cx);
8786 0 : if (frame.isFunctionFrame()) {
8787 0 : RootedFunction callee(cx, frame.callee());
8788 0 : if (callee->isInterpreted()) {
8789 0 : RootedScript script(cx, callee->nonLazyScript());
8790 0 : scriptObject = debug->wrapScript(cx, script);
8791 0 : if (!scriptObject)
8792 0 : return false;
8793 : }
8794 0 : } else if (frame.isWasmDebugFrame()) {
8795 0 : RootedWasmInstanceObject instance(cx, frame.wasmInstance()->object());
8796 0 : scriptObject = debug->wrapWasmScript(cx, instance);
8797 0 : if (!scriptObject)
8798 0 : return false;
8799 : } else {
8800 : /*
8801 : * We got eval, JS_Evaluate*, or JS_ExecuteScript non-function script
8802 : * frames.
8803 : */
8804 0 : RootedScript script(cx, frame.script());
8805 0 : scriptObject = debug->wrapScript(cx, script);
8806 0 : if (!scriptObject)
8807 0 : return false;
8808 : }
8809 0 : args.rval().setObjectOrNull(scriptObject);
8810 0 : return true;
8811 : }
8812 :
8813 : /* static */ bool
8814 0 : DebuggerFrame::offsetGetter(JSContext* cx, unsigned argc, Value* vp)
8815 : {
8816 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get offset", args, frame);
8817 :
8818 : size_t result;
8819 0 : if (!DebuggerFrame::getOffset(cx, frame, result))
8820 0 : return false;
8821 :
8822 0 : args.rval().setNumber(double(result));
8823 0 : return true;
8824 : }
8825 :
8826 : /* static */ bool
8827 0 : DebuggerFrame::liveGetter(JSContext* cx, unsigned argc, Value* vp)
8828 : {
8829 0 : CallArgs args = CallArgsFromVp(argc, vp);
8830 0 : RootedDebuggerFrame frame(cx, DebuggerFrame_checkThis(cx, args, "get live", false));
8831 0 : if (!frame)
8832 0 : return false;
8833 :
8834 0 : args.rval().setBoolean(frame->isLive());
8835 0 : return true;
8836 : }
8837 :
8838 : static bool
8839 0 : IsValidHook(const Value& v)
8840 : {
8841 0 : return v.isUndefined() || (v.isObject() && v.toObject().isCallable());
8842 : }
8843 :
8844 : /* static */ bool
8845 0 : DebuggerFrame::onStepGetter(JSContext* cx, unsigned argc, Value* vp)
8846 : {
8847 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get onStep", args, frame);
8848 :
8849 0 : OnStepHandler* handler = frame->onStepHandler();
8850 0 : RootedValue value(cx, handler ? ObjectOrNullValue(handler->object()) : UndefinedValue());
8851 0 : MOZ_ASSERT(IsValidHook(value));
8852 0 : args.rval().set(value);
8853 0 : return true;
8854 : }
8855 :
8856 : /* static */ bool
8857 0 : DebuggerFrame::onStepSetter(JSContext* cx, unsigned argc, Value* vp)
8858 : {
8859 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "set onStep", args, frame);
8860 0 : if (!args.requireAtLeast(cx, "Debugger.Frame.set onStep", 1))
8861 0 : return false;
8862 0 : if (!IsValidHook(args[0])) {
8863 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
8864 0 : return false;
8865 : }
8866 :
8867 0 : ScriptedOnStepHandler* handler = nullptr;
8868 0 : if (!args[0].isUndefined()) {
8869 0 : handler = cx->new_<ScriptedOnStepHandler>(&args[0].toObject());
8870 0 : if (!handler)
8871 0 : return false;
8872 : }
8873 :
8874 0 : if (!DebuggerFrame::setOnStepHandler(cx, frame, handler)) {
8875 0 : handler->drop();
8876 0 : return false;
8877 : }
8878 :
8879 0 : args.rval().setUndefined();
8880 0 : return true;
8881 : }
8882 :
8883 : /* static */ bool
8884 0 : DebuggerFrame::onPopGetter(JSContext* cx, unsigned argc, Value* vp)
8885 : {
8886 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "get onPop", args, frame);
8887 :
8888 0 : OnPopHandler* handler = frame->onPopHandler();
8889 0 : RootedValue value(cx, handler ? ObjectValue(*handler->object()) : UndefinedValue());
8890 0 : MOZ_ASSERT(IsValidHook(value));
8891 0 : args.rval().set(value);
8892 0 : return true;
8893 : }
8894 :
8895 : /* static */ bool
8896 0 : DebuggerFrame::onPopSetter(JSContext* cx, unsigned argc, Value* vp)
8897 : {
8898 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "set onPop", args, frame);
8899 0 : if (!args.requireAtLeast(cx, "Debugger.Frame.set onPop", 1))
8900 0 : return false;
8901 0 : if (!IsValidHook(args[0])) {
8902 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
8903 0 : return false;
8904 : }
8905 :
8906 0 : ScriptedOnPopHandler* handler = nullptr;
8907 0 : if (!args[0].isUndefined()) {
8908 0 : handler = cx->new_<ScriptedOnPopHandler>(&args[0].toObject());
8909 0 : if (!handler)
8910 0 : return false;
8911 : }
8912 :
8913 0 : frame->setOnPopHandler(handler);
8914 :
8915 0 : args.rval().setUndefined();
8916 0 : return true;
8917 : }
8918 :
8919 : /* static */ bool
8920 0 : DebuggerFrame::evalMethod(JSContext* cx, unsigned argc, Value* vp)
8921 : {
8922 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "eval", args, frame);
8923 0 : if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.eval", 1))
8924 0 : return false;
8925 :
8926 0 : AutoStableStringChars stableChars(cx);
8927 0 : if (!ValueToStableChars(cx, "Debugger.Frame.prototype.eval", args[0], stableChars))
8928 0 : return false;
8929 0 : mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
8930 :
8931 0 : EvalOptions options;
8932 0 : if (!ParseEvalOptions(cx, args.get(1), options))
8933 0 : return false;
8934 :
8935 : JSTrapStatus status;
8936 0 : RootedValue value(cx);
8937 0 : if (!DebuggerFrame::eval(cx, frame, chars, nullptr, options, status, &value))
8938 0 : return false;
8939 :
8940 0 : return frame->owner()->newCompletionValue(cx, status, value, args.rval());
8941 : }
8942 :
8943 : /* static */ bool
8944 0 : DebuggerFrame::evalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp)
8945 : {
8946 0 : THIS_DEBUGGER_FRAME(cx, argc, vp, "evalWithBindings", args, frame);
8947 0 : if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.evalWithBindings", 2))
8948 0 : return false;
8949 :
8950 0 : AutoStableStringChars stableChars(cx);
8951 0 : if (!ValueToStableChars(cx, "Debugger.Frame.prototype.evalWithBindings", args[0],
8952 : stableChars))
8953 : {
8954 0 : return false;
8955 : }
8956 0 : mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
8957 :
8958 0 : RootedObject bindings(cx, NonNullObject(cx, args[1]));
8959 0 : if (!bindings)
8960 0 : return false;
8961 :
8962 0 : EvalOptions options;
8963 0 : if (!ParseEvalOptions(cx, args.get(2), options))
8964 0 : return false;
8965 :
8966 : JSTrapStatus status;
8967 0 : RootedValue value(cx);
8968 0 : if (!DebuggerFrame::eval(cx, frame, chars, bindings, options, status, &value))
8969 0 : return false;
8970 :
8971 0 : return frame->owner()->newCompletionValue(cx, status, value, args.rval());
8972 : }
8973 :
8974 : /* static */ bool
8975 0 : DebuggerFrame::construct(JSContext* cx, unsigned argc, Value* vp)
8976 : {
8977 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
8978 0 : "Debugger.Frame");
8979 0 : return false;
8980 : }
8981 :
8982 : const JSPropertySpec DebuggerFrame::properties_[] = {
8983 : JS_PSG("arguments", DebuggerFrame::argumentsGetter, 0),
8984 : JS_PSG("callee", DebuggerFrame::calleeGetter, 0),
8985 : JS_PSG("constructing", DebuggerFrame::constructingGetter, 0),
8986 : JS_PSG("environment", DebuggerFrame::environmentGetter, 0),
8987 : JS_PSG("generator", DebuggerFrame::generatorGetter, 0),
8988 : JS_PSG("live", DebuggerFrame::liveGetter, 0),
8989 : JS_PSG("offset", DebuggerFrame::offsetGetter, 0),
8990 : JS_PSG("older", DebuggerFrame::olderGetter, 0),
8991 : JS_PSG("script", DebuggerFrame_getScript, 0),
8992 : JS_PSG("this", DebuggerFrame::thisGetter, 0),
8993 : JS_PSG("type", DebuggerFrame::typeGetter, 0),
8994 : JS_PSG("implementation", DebuggerFrame::implementationGetter, 0),
8995 : JS_PSGS("onStep", DebuggerFrame::onStepGetter, DebuggerFrame::onStepSetter, 0),
8996 : JS_PSGS("onPop", DebuggerFrame::onPopGetter, DebuggerFrame::onPopSetter, 0),
8997 : JS_PS_END
8998 : };
8999 :
9000 : const JSFunctionSpec DebuggerFrame::methods_[] = {
9001 : JS_FN("eval", DebuggerFrame::evalMethod, 1, 0),
9002 : JS_FN("evalWithBindings", DebuggerFrame::evalWithBindingsMethod, 1, 0),
9003 : JS_FS_END
9004 : };
9005 :
9006 :
9007 : /*** Debugger.Object *****************************************************************************/
9008 :
9009 : void
9010 0 : DebuggerObject_trace(JSTracer* trc, JSObject* obj)
9011 : {
9012 : /*
9013 : * There is a barrier on private pointers, so the Unbarriered marking
9014 : * is okay.
9015 : */
9016 0 : if (JSObject* referent = (JSObject*) obj->as<NativeObject>().getPrivate()) {
9017 : TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &referent,
9018 0 : "Debugger.Object referent");
9019 0 : obj->as<NativeObject>().setPrivateUnbarriered(referent);
9020 : }
9021 0 : }
9022 :
9023 : static DebuggerObject*
9024 0 : DebuggerObject_checkThis(JSContext* cx, const CallArgs& args, const char* fnname)
9025 : {
9026 0 : JSObject* thisobj = NonNullObject(cx, args.thisv());
9027 0 : if (!thisobj)
9028 0 : return nullptr;
9029 0 : if (thisobj->getClass() != &DebuggerObject::class_) {
9030 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
9031 0 : "Debugger.Object", fnname, thisobj->getClass()->name);
9032 0 : return nullptr;
9033 : }
9034 :
9035 : /*
9036 : * Forbid Debugger.Object.prototype, which is of class DebuggerObject::class_
9037 : * but isn't a real working Debugger.Object. The prototype object is
9038 : * distinguished by having no referent.
9039 : */
9040 0 : DebuggerObject* nthisobj = &thisobj->as<DebuggerObject>();
9041 0 : if (!nthisobj->getPrivate()) {
9042 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
9043 0 : "Debugger.Object", fnname, "prototype object");
9044 0 : return nullptr;
9045 : }
9046 0 : return nthisobj;
9047 : }
9048 :
9049 : #define THIS_DEBUGOBJECT(cx, argc, vp, fnname, args, object) \
9050 : CallArgs args = CallArgsFromVp(argc, vp); \
9051 : RootedDebuggerObject object(cx, DebuggerObject_checkThis(cx, args, fnname)); \
9052 : if (!object) \
9053 : return false; \
9054 :
9055 : #define THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj) \
9056 : CallArgs args = CallArgsFromVp(argc, vp); \
9057 : RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname)); \
9058 : if (!obj) \
9059 : return false; \
9060 : obj = (JSObject*) obj->as<NativeObject>().getPrivate(); \
9061 : MOZ_ASSERT(obj)
9062 :
9063 : #define THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj) \
9064 : CallArgs args = CallArgsFromVp(argc, vp); \
9065 : RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname)); \
9066 : if (!obj) \
9067 : return false; \
9068 : Debugger* dbg = Debugger::fromChildJSObject(obj); \
9069 : obj = (JSObject*) obj->as<NativeObject>().getPrivate(); \
9070 : MOZ_ASSERT(obj)
9071 :
9072 : #define THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, fnname, args, obj) \
9073 : THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj); \
9074 : obj = CheckedUnwrap(obj); \
9075 : if (!obj) { \
9076 : ReportAccessDenied(cx); \
9077 : return false; \
9078 : } \
9079 : if (!obj->is<PromiseObject>()) { \
9080 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,\
9081 : "Debugger", "Promise", obj->getClass()->name); \
9082 : return false; \
9083 : } \
9084 : Rooted<PromiseObject*> promise(cx, &obj->as<PromiseObject>());
9085 :
9086 : #define THIS_DEBUGOBJECT_OWNER_PROMISE(cx, argc, vp, fnname, args, dbg, obj) \
9087 : THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj); \
9088 : obj = CheckedUnwrap(obj); \
9089 : if (!obj) { \
9090 : ReportAccessDenied(cx); \
9091 : return false; \
9092 : } \
9093 : if (!obj->is<PromiseObject>()) { \
9094 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,\
9095 : "Debugger", "Promise", obj->getClass()->name); \
9096 : return false; \
9097 : } \
9098 : Rooted<PromiseObject*> promise(cx, &obj->as<PromiseObject>());
9099 :
9100 : /* static */ bool
9101 0 : DebuggerObject::construct(JSContext* cx, unsigned argc, Value* vp)
9102 : {
9103 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
9104 0 : "Debugger.Object");
9105 0 : return false;
9106 : }
9107 :
9108 : /* static */ bool
9109 0 : DebuggerObject::callableGetter(JSContext* cx, unsigned argc, Value* vp)
9110 : {
9111 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get callable", args, object)
9112 :
9113 0 : args.rval().setBoolean(object->isCallable());
9114 0 : return true;
9115 : }
9116 :
9117 : /* static */ bool
9118 0 : DebuggerObject::isBoundFunctionGetter(JSContext* cx, unsigned argc, Value* vp)
9119 : {
9120 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get isBoundFunction", args, object)
9121 :
9122 0 : if (!object->isDebuggeeFunction()) {
9123 0 : args.rval().setUndefined();
9124 0 : return true;
9125 : }
9126 :
9127 0 : args.rval().setBoolean(object->isBoundFunction());
9128 0 : return true;
9129 : }
9130 :
9131 : /* static */ bool
9132 0 : DebuggerObject::isArrowFunctionGetter(JSContext* cx, unsigned argc, Value* vp)
9133 : {
9134 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get isArrowFunction", args, object)
9135 :
9136 0 : if (!object->isDebuggeeFunction()) {
9137 0 : args.rval().setUndefined();
9138 0 : return true;
9139 : }
9140 :
9141 0 : args.rval().setBoolean(object->isArrowFunction());
9142 0 : return true;
9143 : }
9144 :
9145 : /* static */ bool
9146 0 : DebuggerObject::protoGetter(JSContext* cx, unsigned argc, Value* vp)
9147 : {
9148 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get proto", args, object)
9149 :
9150 0 : RootedDebuggerObject result(cx);
9151 0 : if (!DebuggerObject::getPrototypeOf(cx, object, &result))
9152 0 : return false;
9153 :
9154 0 : args.rval().setObjectOrNull(result);
9155 0 : return true;
9156 : }
9157 :
9158 : /* static */ bool
9159 0 : DebuggerObject::classGetter(JSContext* cx, unsigned argc, Value* vp)
9160 : {
9161 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get class", args, object)
9162 :
9163 0 : RootedString result(cx);
9164 0 : if (!DebuggerObject::getClassName(cx, object, &result))
9165 0 : return false;
9166 :
9167 0 : args.rval().setString(result);
9168 0 : return true;
9169 : }
9170 :
9171 : /* static */ bool
9172 0 : DebuggerObject::nameGetter(JSContext* cx, unsigned argc, Value* vp)
9173 : {
9174 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get name", args, object)
9175 :
9176 0 : if (!object->isFunction()) {
9177 0 : args.rval().setUndefined();
9178 0 : return true;
9179 : }
9180 :
9181 0 : RootedString result(cx, object->name(cx));
9182 0 : if (result)
9183 0 : args.rval().setString(result);
9184 : else
9185 0 : args.rval().setUndefined();
9186 0 : return true;
9187 : }
9188 :
9189 : /* static */ bool
9190 0 : DebuggerObject::displayNameGetter(JSContext* cx, unsigned argc, Value* vp)
9191 : {
9192 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get displayName", args, object)
9193 :
9194 0 : if (!object->isFunction()) {
9195 0 : args.rval().setUndefined();
9196 0 : return true;
9197 : }
9198 :
9199 0 : RootedString result(cx, object->displayName(cx));
9200 0 : if (result)
9201 0 : args.rval().setString(result);
9202 : else
9203 0 : args.rval().setUndefined();
9204 0 : return true;
9205 : }
9206 :
9207 : /* static */ bool
9208 0 : DebuggerObject::parameterNamesGetter(JSContext* cx, unsigned argc, Value* vp)
9209 : {
9210 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get parameterNames", args, object)
9211 :
9212 0 : if (!object->isDebuggeeFunction()) {
9213 0 : args.rval().setUndefined();
9214 0 : return true;
9215 : }
9216 :
9217 0 : Rooted<StringVector> names(cx, StringVector(cx));
9218 0 : if (!DebuggerObject::getParameterNames(cx, object, &names))
9219 0 : return false;
9220 :
9221 0 : RootedArrayObject obj(cx, NewDenseFullyAllocatedArray(cx, names.length()));
9222 0 : if (!obj)
9223 0 : return false;
9224 :
9225 0 : obj->ensureDenseInitializedLength(cx, 0, names.length());
9226 0 : for (size_t i = 0; i < names.length(); ++i) {
9227 0 : Value v;
9228 0 : if (names[i])
9229 0 : v = StringValue(names[i]);
9230 : else
9231 0 : v = UndefinedValue();
9232 0 : obj->setDenseElement(i, v);
9233 : }
9234 :
9235 0 : args.rval().setObject(*obj);
9236 0 : return true;
9237 : }
9238 :
9239 : /* static */ bool
9240 0 : DebuggerObject::scriptGetter(JSContext* cx, unsigned argc, Value* vp)
9241 : {
9242 0 : THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get script", args, dbg, obj);
9243 :
9244 0 : if (!obj->is<JSFunction>()) {
9245 0 : args.rval().setUndefined();
9246 0 : return true;
9247 : }
9248 :
9249 0 : RootedFunction fun(cx, &obj->as<JSFunction>());
9250 0 : if (!fun->isInterpreted()) {
9251 0 : args.rval().setUndefined();
9252 0 : return true;
9253 : }
9254 :
9255 0 : RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
9256 0 : if (!script)
9257 0 : return false;
9258 :
9259 : /* Only hand out debuggee scripts. */
9260 0 : if (!dbg->observesScript(script)) {
9261 0 : args.rval().setNull();
9262 0 : return true;
9263 : }
9264 :
9265 0 : RootedObject scriptObject(cx, dbg->wrapScript(cx, script));
9266 0 : if (!scriptObject)
9267 0 : return false;
9268 :
9269 0 : args.rval().setObject(*scriptObject);
9270 0 : return true;
9271 : }
9272 :
9273 : /* static */ bool
9274 0 : DebuggerObject::environmentGetter(JSContext* cx, unsigned argc, Value* vp)
9275 : {
9276 0 : THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get environment", args, dbg, obj);
9277 :
9278 : /* Don't bother switching compartments just to check obj's type and get its env. */
9279 0 : if (!obj->is<JSFunction>() || !obj->as<JSFunction>().isInterpreted()) {
9280 0 : args.rval().setUndefined();
9281 0 : return true;
9282 : }
9283 :
9284 : /* Only hand out environments of debuggee functions. */
9285 0 : if (!dbg->observesGlobal(&obj->global())) {
9286 0 : args.rval().setNull();
9287 0 : return true;
9288 : }
9289 :
9290 0 : Rooted<Env*> env(cx);
9291 : {
9292 0 : AutoCompartment ac(cx, obj);
9293 0 : RootedFunction fun(cx, &obj->as<JSFunction>());
9294 0 : env = GetDebugEnvironmentForFunction(cx, fun);
9295 0 : if (!env)
9296 0 : return false;
9297 : }
9298 :
9299 0 : return dbg->wrapEnvironment(cx, env, args.rval());
9300 : }
9301 :
9302 : /* static */ bool
9303 0 : DebuggerObject::boundTargetFunctionGetter(JSContext* cx, unsigned argc, Value* vp)
9304 : {
9305 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get boundTargetFunction", args, object)
9306 :
9307 0 : if (!object->isDebuggeeFunction() || !object->isBoundFunction()) {
9308 0 : args.rval().setUndefined();
9309 0 : return true;
9310 : }
9311 :
9312 0 : RootedDebuggerObject result(cx);
9313 0 : if (!DebuggerObject::getBoundTargetFunction(cx, object, &result))
9314 0 : return false;
9315 :
9316 0 : args.rval().setObject(*result);
9317 0 : return true;
9318 : }
9319 :
9320 : /* static */ bool
9321 0 : DebuggerObject::boundThisGetter(JSContext* cx, unsigned argc, Value* vp)
9322 : {
9323 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get boundThis", args, object)
9324 :
9325 0 : if (!object->isDebuggeeFunction() || !object->isBoundFunction()) {
9326 0 : args.rval().setUndefined();
9327 0 : return true;
9328 : }
9329 :
9330 0 : return DebuggerObject::getBoundThis(cx, object, args.rval());
9331 : }
9332 :
9333 : /* static */ bool
9334 0 : DebuggerObject::boundArgumentsGetter(JSContext* cx, unsigned argc, Value* vp)
9335 : {
9336 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get boundArguments", args, object)
9337 :
9338 0 : if (!object->isDebuggeeFunction() || !object->isBoundFunction()) {
9339 0 : args.rval().setUndefined();
9340 0 : return true;
9341 : }
9342 :
9343 0 : Rooted<ValueVector> result(cx, ValueVector(cx));
9344 0 : if (!DebuggerObject::getBoundArguments(cx, object, &result))
9345 0 : return false;
9346 :
9347 0 : RootedObject obj(cx, NewDenseCopiedArray(cx, result.length(), result.begin()));
9348 0 : if (!obj)
9349 0 : return false;
9350 :
9351 0 : args.rval().setObject(*obj);
9352 0 : return true;
9353 : }
9354 :
9355 : /* static */ bool
9356 0 : DebuggerObject::globalGetter(JSContext* cx, unsigned argc, Value* vp)
9357 : {
9358 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get global", args, object)
9359 :
9360 0 : RootedDebuggerObject result(cx);
9361 0 : if (!DebuggerObject::getGlobal(cx, object, &result))
9362 0 : return false;
9363 :
9364 0 : args.rval().setObject(*result);
9365 0 : return true;
9366 : }
9367 :
9368 : /* static */ bool
9369 0 : DebuggerObject::allocationSiteGetter(JSContext* cx, unsigned argc, Value* vp)
9370 : {
9371 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get allocationSite", args, object)
9372 :
9373 0 : RootedObject result(cx);
9374 0 : if (!DebuggerObject::getAllocationSite(cx, object, &result))
9375 0 : return false;
9376 :
9377 0 : args.rval().setObjectOrNull(result);
9378 0 : return true;
9379 : }
9380 :
9381 : // Returns the "name" field (see js.msg), which may be used as a unique
9382 : // identifier, for any error object with a JSErrorReport or undefined
9383 : // if the object has no JSErrorReport.
9384 : /* static */ bool
9385 0 : DebuggerObject::errorMessageNameGetter(JSContext *cx, unsigned argc, Value* vp)
9386 : {
9387 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get errorMessageName", args, object)
9388 :
9389 0 : RootedString result(cx);
9390 0 : if (!DebuggerObject::getErrorMessageName(cx, object, &result))
9391 0 : return false;
9392 :
9393 0 : if (result)
9394 0 : args.rval().setString(result);
9395 : else
9396 0 : args.rval().setUndefined();
9397 0 : return true;
9398 : }
9399 :
9400 : /* static */ bool
9401 0 : DebuggerObject::errorNotesGetter(JSContext *cx, unsigned argc, Value* vp)
9402 : {
9403 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get errorNotes", args, object)
9404 :
9405 0 : return DebuggerObject::getErrorNotes(cx, object, args.rval());
9406 : }
9407 :
9408 : /* static */ bool
9409 0 : DebuggerObject::errorLineNumberGetter(JSContext *cx, unsigned argc, Value* vp)
9410 : {
9411 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get errorLineNumber", args, object)
9412 :
9413 0 : return DebuggerObject::getErrorLineNumber(cx, object, args.rval());
9414 : }
9415 :
9416 : /* static */ bool
9417 0 : DebuggerObject::errorColumnNumberGetter(JSContext *cx, unsigned argc, Value* vp)
9418 : {
9419 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get errorColumnNumber", args, object)
9420 :
9421 0 : return DebuggerObject::getErrorColumnNumber(cx, object, args.rval());
9422 : }
9423 :
9424 : /* static */ bool
9425 0 : DebuggerObject::isProxyGetter(JSContext* cx, unsigned argc, Value* vp)
9426 : {
9427 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get isProxy", args, object)
9428 :
9429 0 : args.rval().setBoolean(object->isScriptedProxy());
9430 0 : return true;
9431 : }
9432 :
9433 : /* static */ bool
9434 0 : DebuggerObject::proxyTargetGetter(JSContext* cx, unsigned argc, Value* vp)
9435 : {
9436 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get proxyTarget", args, object)
9437 :
9438 0 : if (!object->isScriptedProxy()) {
9439 0 : args.rval().setUndefined();
9440 0 : return true;
9441 : }
9442 :
9443 0 : Rooted<DebuggerObject*> result(cx);
9444 0 : if (!DebuggerObject::getScriptedProxyTarget(cx, object, &result))
9445 0 : return false;
9446 :
9447 0 : args.rval().setObjectOrNull(result);
9448 0 : return true;
9449 : }
9450 :
9451 : /* static */ bool
9452 0 : DebuggerObject::proxyHandlerGetter(JSContext* cx, unsigned argc, Value* vp)
9453 : {
9454 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get proxyHandler", args, object)
9455 :
9456 0 : if (!object->isScriptedProxy()) {
9457 0 : args.rval().setUndefined();
9458 0 : return true;
9459 : }
9460 0 : Rooted<DebuggerObject*> result(cx);
9461 0 : if (!DebuggerObject::getScriptedProxyHandler(cx, object, &result))
9462 0 : return false;
9463 :
9464 0 : args.rval().setObjectOrNull(result);
9465 0 : return true;
9466 : }
9467 :
9468 : /* static */ bool
9469 0 : DebuggerObject::isPromiseGetter(JSContext* cx, unsigned argc, Value* vp)
9470 : {
9471 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get isPromise", args, object)
9472 :
9473 0 : args.rval().setBoolean(object->isPromise());
9474 0 : return true;
9475 : }
9476 :
9477 : /* static */ bool
9478 0 : DebuggerObject::promiseStateGetter(JSContext* cx, unsigned argc, Value* vp)
9479 : {
9480 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get promiseState", args, object);
9481 :
9482 0 : if (!DebuggerObject::requirePromise(cx, object))
9483 0 : return false;
9484 :
9485 0 : RootedValue result(cx);
9486 0 : switch (object->promiseState()) {
9487 : case JS::PromiseState::Pending:
9488 0 : result.setString(cx->names().pending);
9489 0 : break;
9490 : case JS::PromiseState::Fulfilled:
9491 0 : result.setString(cx->names().fulfilled);
9492 0 : break;
9493 : case JS::PromiseState::Rejected:
9494 0 : result.setString(cx->names().rejected);
9495 0 : break;
9496 : }
9497 :
9498 0 : args.rval().set(result);
9499 0 : return true;
9500 : }
9501 :
9502 : /* static */ bool
9503 0 : DebuggerObject::promiseValueGetter(JSContext* cx, unsigned argc, Value* vp)
9504 : {
9505 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get promiseValue", args, object);
9506 :
9507 0 : if (!DebuggerObject::requirePromise(cx, object))
9508 0 : return false;
9509 :
9510 0 : if (object->promiseState() != JS::PromiseState::Fulfilled) {
9511 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_PROMISE_NOT_FULFILLED);
9512 0 : return false;
9513 : }
9514 :
9515 0 : return DebuggerObject::getPromiseValue(cx, object, args.rval());;
9516 : }
9517 :
9518 : /* static */ bool
9519 0 : DebuggerObject::promiseReasonGetter(JSContext* cx, unsigned argc, Value* vp)
9520 : {
9521 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get promiseReason", args, object);
9522 :
9523 0 : if (!DebuggerObject::requirePromise(cx, object))
9524 0 : return false;
9525 :
9526 0 : if (object->promiseState() != JS::PromiseState::Rejected) {
9527 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_PROMISE_NOT_REJECTED);
9528 0 : return false;
9529 : }
9530 :
9531 0 : return DebuggerObject::getPromiseReason(cx, object, args.rval());;
9532 : }
9533 :
9534 : /* static */ bool
9535 0 : DebuggerObject::promiseLifetimeGetter(JSContext* cx, unsigned argc, Value* vp)
9536 : {
9537 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get promiseLifetime", args, object);
9538 :
9539 0 : if (!DebuggerObject::requirePromise(cx, object))
9540 0 : return false;
9541 :
9542 0 : args.rval().setNumber(object->promiseLifetime());
9543 0 : return true;
9544 : }
9545 :
9546 : /* static */ bool
9547 0 : DebuggerObject::promiseTimeToResolutionGetter(JSContext* cx, unsigned argc, Value* vp)
9548 : {
9549 0 : THIS_DEBUGOBJECT(cx, argc, vp, "get promiseTimeToResolution", args, object);
9550 :
9551 0 : if (!DebuggerObject::requirePromise(cx, object))
9552 0 : return false;
9553 :
9554 0 : if (object->promiseState() == JS::PromiseState::Pending) {
9555 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_PROMISE_NOT_RESOLVED);
9556 0 : return false;
9557 : }
9558 :
9559 0 : args.rval().setNumber(object->promiseTimeToResolution());
9560 0 : return true;
9561 : }
9562 :
9563 : /* static */ bool
9564 0 : DebuggerObject::promiseAllocationSiteGetter(JSContext* cx, unsigned argc, Value* vp)
9565 : {
9566 0 : THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, "get promiseAllocationSite", args, refobj);
9567 :
9568 0 : RootedObject allocSite(cx, promise->allocationSite());
9569 0 : if (!allocSite) {
9570 0 : args.rval().setNull();
9571 0 : return true;
9572 : }
9573 :
9574 0 : if (!cx->compartment()->wrap(cx, &allocSite))
9575 0 : return false;
9576 0 : args.rval().set(ObjectValue(*allocSite));
9577 0 : return true;
9578 : }
9579 :
9580 : /* static */ bool
9581 0 : DebuggerObject::promiseResolutionSiteGetter(JSContext* cx, unsigned argc, Value* vp)
9582 : {
9583 0 : THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, "get promiseResolutionSite", args, refobj);
9584 :
9585 0 : if (promise->state() == JS::PromiseState::Pending) {
9586 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_PROMISE_NOT_RESOLVED);
9587 0 : return false;
9588 : }
9589 :
9590 0 : RootedObject resolutionSite(cx, promise->resolutionSite());
9591 0 : if (!resolutionSite) {
9592 0 : args.rval().setNull();
9593 0 : return true;
9594 : }
9595 :
9596 0 : if (!cx->compartment()->wrap(cx, &resolutionSite))
9597 0 : return false;
9598 0 : args.rval().set(ObjectValue(*resolutionSite));
9599 0 : return true;
9600 : }
9601 :
9602 : /* static */ bool
9603 0 : DebuggerObject::promiseIDGetter(JSContext* cx, unsigned argc, Value* vp)
9604 : {
9605 0 : THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, "get promiseID", args, refobj);
9606 :
9607 0 : args.rval().setNumber(double(promise->getID()));
9608 0 : return true;
9609 : }
9610 :
9611 : /* static */ bool
9612 0 : DebuggerObject::promiseDependentPromisesGetter(JSContext* cx, unsigned argc, Value* vp)
9613 : {
9614 0 : THIS_DEBUGOBJECT_OWNER_PROMISE(cx, argc, vp, "get promiseDependentPromises", args, dbg, refobj);
9615 :
9616 0 : Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
9617 : {
9618 0 : JSAutoCompartment ac(cx, promise);
9619 0 : if (!promise->dependentPromises(cx, &values))
9620 0 : return false;
9621 : }
9622 0 : for (size_t i = 0; i < values.length(); i++) {
9623 0 : if (!dbg->wrapDebuggeeValue(cx, values[i]))
9624 0 : return false;
9625 : }
9626 0 : RootedArrayObject promises(cx);
9627 0 : if (values.length() == 0)
9628 0 : promises = NewDenseEmptyArray(cx);
9629 : else
9630 0 : promises = NewDenseCopiedArray(cx, values.length(), values[0].address());
9631 0 : if (!promises)
9632 0 : return false;
9633 0 : args.rval().setObject(*promises);
9634 0 : return true;
9635 : }
9636 :
9637 : /* static */ bool
9638 0 : DebuggerObject::isExtensibleMethod(JSContext* cx, unsigned argc, Value* vp)
9639 : {
9640 0 : THIS_DEBUGOBJECT(cx, argc, vp, "isExtensible", args, object)
9641 :
9642 : bool result;
9643 0 : if (!DebuggerObject::isExtensible(cx, object, result))
9644 0 : return false;
9645 :
9646 0 : args.rval().setBoolean(result);
9647 0 : return true;
9648 : }
9649 :
9650 : /* static */ bool
9651 0 : DebuggerObject::isSealedMethod(JSContext* cx, unsigned argc, Value* vp)
9652 : {
9653 0 : THIS_DEBUGOBJECT(cx, argc, vp, "isSealed", args, object)
9654 :
9655 : bool result;
9656 0 : if (!DebuggerObject::isSealed(cx, object, result))
9657 0 : return false;
9658 :
9659 0 : args.rval().setBoolean(result);
9660 0 : return true;
9661 : }
9662 :
9663 : /* static */ bool
9664 0 : DebuggerObject::isFrozenMethod(JSContext* cx, unsigned argc, Value* vp)
9665 : {
9666 0 : THIS_DEBUGOBJECT(cx, argc, vp, "isFrozen", args, object)
9667 :
9668 : bool result;
9669 0 : if (!DebuggerObject::isFrozen(cx, object, result))
9670 0 : return false;
9671 :
9672 0 : args.rval().setBoolean(result);
9673 0 : return true;
9674 : }
9675 :
9676 : static JSObject*
9677 0 : IdVectorToArray(JSContext* cx, Handle<IdVector> ids)
9678 : {
9679 0 : Rooted<ValueVector> vals(cx, ValueVector(cx));
9680 0 : if (!vals.growBy(ids.length()))
9681 0 : return nullptr;
9682 :
9683 0 : for (size_t i = 0, len = ids.length(); i < len; i++) {
9684 0 : jsid id = ids[i];
9685 0 : if (JSID_IS_INT(id)) {
9686 0 : JSString* str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
9687 0 : if (!str)
9688 0 : return nullptr;
9689 0 : vals[i].setString(str);
9690 0 : } else if (JSID_IS_ATOM(id)) {
9691 0 : vals[i].setString(JSID_TO_STRING(id));
9692 0 : } else if (JSID_IS_SYMBOL(id)) {
9693 0 : vals[i].setSymbol(JSID_TO_SYMBOL(id));
9694 : } else {
9695 0 : MOZ_ASSERT_UNREACHABLE("IdVector must contain only string, int, and Symbol jsids");
9696 : }
9697 : }
9698 :
9699 0 : return NewDenseCopiedArray(cx, vals.length(), vals.begin());
9700 : }
9701 :
9702 : /* static */ bool
9703 0 : DebuggerObject::getOwnPropertyNamesMethod(JSContext* cx, unsigned argc, Value* vp)
9704 : {
9705 0 : THIS_DEBUGOBJECT(cx, argc, vp, "getOwnPropertyNames", args, object)
9706 :
9707 0 : Rooted<IdVector> ids(cx, IdVector(cx));
9708 0 : if (!DebuggerObject::getOwnPropertyNames(cx, object, &ids))
9709 0 : return false;
9710 :
9711 0 : RootedObject obj(cx, IdVectorToArray(cx, ids));
9712 0 : if (!obj)
9713 0 : return false;
9714 :
9715 0 : args.rval().setObject(*obj);
9716 0 : return true;
9717 : }
9718 :
9719 : /* static */ bool
9720 0 : DebuggerObject::getOwnPropertySymbolsMethod(JSContext* cx, unsigned argc, Value* vp)
9721 : {
9722 0 : THIS_DEBUGOBJECT(cx, argc, vp, "getOwnPropertySymbols", args, object)
9723 :
9724 0 : Rooted<IdVector> ids(cx, IdVector(cx));
9725 0 : if (!DebuggerObject::getOwnPropertySymbols(cx, object, &ids))
9726 0 : return false;
9727 :
9728 0 : RootedObject obj(cx, IdVectorToArray(cx, ids));
9729 0 : if (!obj)
9730 0 : return false;
9731 :
9732 0 : args.rval().setObject(*obj);
9733 0 : return true;
9734 : }
9735 :
9736 : /* static */ bool
9737 0 : DebuggerObject::getOwnPropertyDescriptorMethod(JSContext* cx, unsigned argc, Value* vp)
9738 : {
9739 0 : THIS_DEBUGOBJECT(cx, argc, vp, "getOwnPropertyDescriptor", args, object)
9740 :
9741 0 : RootedId id(cx);
9742 0 : if (!ValueToId<CanGC>(cx, args.get(0), &id))
9743 0 : return false;
9744 :
9745 0 : Rooted<PropertyDescriptor> desc(cx);
9746 0 : if (!DebuggerObject::getOwnPropertyDescriptor(cx, object, id, &desc))
9747 0 : return false;
9748 :
9749 0 : return JS::FromPropertyDescriptor(cx, desc, args.rval());
9750 : }
9751 :
9752 : /* static */ bool
9753 0 : DebuggerObject::preventExtensionsMethod(JSContext* cx, unsigned argc, Value* vp)
9754 : {
9755 0 : THIS_DEBUGOBJECT(cx, argc, vp, "preventExtensions", args, object)
9756 :
9757 0 : if (!DebuggerObject::preventExtensions(cx, object))
9758 0 : return false;
9759 :
9760 0 : args.rval().setUndefined();
9761 0 : return true;
9762 : }
9763 :
9764 : /* static */ bool
9765 0 : DebuggerObject::sealMethod(JSContext* cx, unsigned argc, Value* vp)
9766 : {
9767 0 : THIS_DEBUGOBJECT(cx, argc, vp, "seal", args, object)
9768 :
9769 0 : if (!DebuggerObject::seal(cx, object))
9770 0 : return false;
9771 :
9772 0 : args.rval().setUndefined();
9773 0 : return true;
9774 : }
9775 :
9776 : /* static */ bool
9777 0 : DebuggerObject::freezeMethod(JSContext* cx, unsigned argc, Value* vp)
9778 : {
9779 0 : THIS_DEBUGOBJECT(cx, argc, vp, "freeze", args, object)
9780 :
9781 0 : if (!DebuggerObject::freeze(cx, object))
9782 0 : return false;
9783 :
9784 0 : args.rval().setUndefined();
9785 0 : return true;
9786 : }
9787 :
9788 : /* static */ bool
9789 0 : DebuggerObject::definePropertyMethod(JSContext* cx, unsigned argc, Value* vp)
9790 : {
9791 0 : THIS_DEBUGOBJECT(cx, argc, vp, "defineProperty", args, object)
9792 0 : if (!args.requireAtLeast(cx, "Debugger.Object.defineProperty", 2))
9793 0 : return false;
9794 :
9795 0 : RootedId id(cx);
9796 0 : if (!ValueToId<CanGC>(cx, args[0], &id))
9797 0 : return false;
9798 :
9799 0 : Rooted<PropertyDescriptor> desc(cx);
9800 0 : if (!ToPropertyDescriptor(cx, args[1], false, &desc))
9801 0 : return false;
9802 :
9803 0 : if (!DebuggerObject::defineProperty(cx, object, id, desc))
9804 0 : return false;
9805 :
9806 0 : args.rval().setUndefined();
9807 0 : return true;
9808 : }
9809 :
9810 : /* static */ bool
9811 0 : DebuggerObject::definePropertiesMethod(JSContext* cx, unsigned argc, Value* vp)
9812 : {
9813 0 : THIS_DEBUGOBJECT(cx, argc, vp, "defineProperties", args, object);
9814 0 : if (!args.requireAtLeast(cx, "Debugger.Object.defineProperties", 1))
9815 0 : return false;
9816 :
9817 0 : RootedValue arg(cx, args[0]);
9818 0 : RootedObject props(cx, ToObject(cx, arg));
9819 0 : if (!props)
9820 0 : return false;
9821 0 : AutoIdVector ids(cx);
9822 0 : Rooted<PropertyDescriptorVector> descs(cx, PropertyDescriptorVector(cx));
9823 0 : if (!ReadPropertyDescriptors(cx, props, false, &ids, &descs))
9824 0 : return false;
9825 0 : Rooted<IdVector> ids2(cx, IdVector(cx));
9826 0 : if (!ids2.append(ids.begin(), ids.end()))
9827 0 : return false;
9828 :
9829 0 : if (!DebuggerObject::defineProperties(cx, object, ids2, descs))
9830 0 : return false;
9831 :
9832 0 : args.rval().setUndefined();
9833 0 : return true;
9834 : }
9835 :
9836 : /*
9837 : * This does a non-strict delete, as a matter of API design. The case where the
9838 : * property is non-configurable isn't necessarily exceptional here.
9839 : */
9840 : /* static */ bool
9841 0 : DebuggerObject::deletePropertyMethod(JSContext* cx, unsigned argc, Value* vp)
9842 : {
9843 0 : THIS_DEBUGOBJECT(cx, argc, vp, "deleteProperty", args, object)
9844 :
9845 0 : RootedId id(cx);
9846 0 : if (!ValueToId<CanGC>(cx, args.get(0), &id))
9847 0 : return false;
9848 :
9849 0 : ObjectOpResult result;
9850 0 : if (!DebuggerObject::deleteProperty(cx, object, id, result))
9851 0 : return false;
9852 :
9853 0 : args.rval().setBoolean(result.ok());
9854 0 : return true;
9855 : }
9856 :
9857 : /* static */ bool
9858 0 : DebuggerObject::callMethod(JSContext* cx, unsigned argc, Value* vp)
9859 : {
9860 0 : THIS_DEBUGOBJECT(cx, argc, vp, "call", callArgs, object);
9861 :
9862 0 : RootedValue thisv(cx, callArgs.get(0));
9863 :
9864 0 : Rooted<ValueVector> args(cx, ValueVector(cx));
9865 0 : if (callArgs.length() >= 2) {
9866 0 : if (!args.growBy(callArgs.length() - 1))
9867 0 : return false;
9868 0 : for (size_t i = 1; i < callArgs.length(); ++i)
9869 0 : args[i - 1].set(callArgs[i]);
9870 : }
9871 :
9872 0 : return object->call(cx, object, thisv, args, callArgs.rval());
9873 : }
9874 :
9875 : /* static */ bool
9876 0 : DebuggerObject::applyMethod(JSContext* cx, unsigned argc, Value* vp)
9877 : {
9878 0 : THIS_DEBUGOBJECT(cx, argc, vp, "apply", callArgs, object);
9879 :
9880 0 : RootedValue thisv(cx, callArgs.get(0));
9881 :
9882 0 : Rooted<ValueVector> args(cx, ValueVector(cx));
9883 0 : if (callArgs.length() >= 2 && !callArgs[1].isNullOrUndefined()) {
9884 0 : if (!callArgs[1].isObject()) {
9885 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_APPLY_ARGS,
9886 0 : js_apply_str);
9887 0 : return false;
9888 : }
9889 :
9890 0 : RootedObject argsobj(cx, &callArgs[1].toObject());
9891 :
9892 0 : unsigned argc = 0;
9893 0 : if (!GetLengthProperty(cx, argsobj, &argc))
9894 0 : return false;
9895 0 : argc = unsigned(Min(argc, ARGS_LENGTH_MAX));
9896 :
9897 0 : if (!args.growBy(argc) || !GetElements(cx, argsobj, argc, args.begin()))
9898 0 : return false;
9899 : }
9900 :
9901 0 : return object->call(cx, object, thisv, args, callArgs.rval());
9902 : }
9903 :
9904 : /* static */ bool
9905 0 : DebuggerObject::asEnvironmentMethod(JSContext* cx, unsigned argc, Value* vp)
9906 : {
9907 0 : THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "asEnvironment", args, dbg, referent);
9908 0 : if (!RequireGlobalObject(cx, args.thisv(), referent))
9909 0 : return false;
9910 :
9911 0 : Rooted<Env*> env(cx);
9912 : {
9913 0 : AutoCompartment ac(cx, referent);
9914 0 : env = GetDebugEnvironmentForGlobalLexicalEnvironment(cx);
9915 0 : if (!env)
9916 0 : return false;
9917 : }
9918 :
9919 0 : return dbg->wrapEnvironment(cx, env, args.rval());
9920 : }
9921 :
9922 : // Lookup a binding on the referent's global scope and change it to undefined
9923 : // if it is an uninitialized lexical, otherwise do nothing. The method's
9924 : // JavaScript return value is true _only_ when an uninitialized lexical has been
9925 : // altered, otherwise it is false.
9926 : /* static */ bool
9927 0 : DebuggerObject::forceLexicalInitializationByNameMethod(JSContext *cx, unsigned argc, Value* vp)
9928 : {
9929 0 : THIS_DEBUGOBJECT(cx, argc, vp, "forceLexicalInitializationByName", args, object)
9930 0 : if (!args.requireAtLeast(cx, "Debugger.Object.prototype.forceLexicalInitializationByName", 1))
9931 0 : return false;
9932 :
9933 0 : if (!DebuggerObject::requireGlobal(cx, object))
9934 0 : return false;
9935 :
9936 0 : RootedId id(cx);
9937 0 : if (!ValueToIdentifier(cx, args[0], &id))
9938 0 : return false;
9939 :
9940 : bool result;
9941 0 : if (!DebuggerObject::forceLexicalInitializationByName(cx, object, id, result))
9942 0 : return false;
9943 :
9944 0 : args.rval().setBoolean(result);
9945 0 : return true;
9946 : }
9947 :
9948 : /* static */ bool
9949 0 : DebuggerObject::executeInGlobalMethod(JSContext* cx, unsigned argc, Value* vp)
9950 : {
9951 0 : THIS_DEBUGOBJECT(cx, argc, vp, "executeInGlobal", args, object);
9952 0 : if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobal", 1))
9953 0 : return false;
9954 :
9955 0 : if (!DebuggerObject::requireGlobal(cx, object))
9956 0 : return false;
9957 :
9958 0 : AutoStableStringChars stableChars(cx);
9959 0 : if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobal", args[0],
9960 : stableChars))
9961 : {
9962 0 : return false;
9963 : }
9964 0 : mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
9965 :
9966 0 : EvalOptions options;
9967 0 : if (!ParseEvalOptions(cx, args.get(1), options))
9968 0 : return false;
9969 :
9970 : JSTrapStatus status;
9971 0 : RootedValue value(cx);
9972 0 : if (!DebuggerObject::executeInGlobal(cx, object, chars, nullptr, options, status, &value))
9973 0 : return false;
9974 :
9975 0 : return object->owner()->newCompletionValue(cx, status, value, args.rval());
9976 : }
9977 :
9978 : /* static */ bool
9979 0 : DebuggerObject::executeInGlobalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp)
9980 : {
9981 0 : THIS_DEBUGOBJECT(cx, argc, vp, "executeInGlobalWithBindings", args, object);
9982 0 : if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", 2))
9983 0 : return false;
9984 :
9985 0 : if (!DebuggerObject::requireGlobal(cx, object))
9986 0 : return false;
9987 :
9988 0 : AutoStableStringChars stableChars(cx);
9989 0 : if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", args[0],
9990 : stableChars))
9991 : {
9992 0 : return false;
9993 : }
9994 0 : mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
9995 :
9996 0 : RootedObject bindings(cx, NonNullObject(cx, args[1]));
9997 0 : if (!bindings)
9998 0 : return false;
9999 :
10000 0 : EvalOptions options;
10001 0 : if (!ParseEvalOptions(cx, args.get(2), options))
10002 0 : return false;
10003 :
10004 : JSTrapStatus status;
10005 0 : RootedValue value(cx);
10006 0 : if (!DebuggerObject::executeInGlobal(cx, object, chars, bindings, options, status, &value))
10007 0 : return false;
10008 :
10009 0 : return object->owner()->newCompletionValue(cx, status, value, args.rval());
10010 : }
10011 :
10012 : /* static */ bool
10013 0 : DebuggerObject::makeDebuggeeValueMethod(JSContext* cx, unsigned argc, Value* vp)
10014 : {
10015 0 : THIS_DEBUGOBJECT(cx, argc, vp, "makeDebuggeeValue", args, object);
10016 0 : if (!args.requireAtLeast(cx, "Debugger.Object.prototype.makeDebuggeeValue", 1))
10017 0 : return false;
10018 :
10019 0 : return DebuggerObject::makeDebuggeeValue(cx, object, args[0], args.rval());
10020 : }
10021 :
10022 : /* static */ bool
10023 0 : DebuggerObject::unsafeDereferenceMethod(JSContext* cx, unsigned argc, Value* vp)
10024 : {
10025 0 : THIS_DEBUGOBJECT(cx, argc, vp, "unsafeDereference", args, object);
10026 :
10027 0 : RootedObject result(cx);
10028 0 : if (!DebuggerObject::unsafeDereference(cx, object, &result))
10029 0 : return false;
10030 :
10031 0 : args.rval().setObject(*result);
10032 0 : return true;
10033 : }
10034 :
10035 : /* static */ bool
10036 0 : DebuggerObject::unwrapMethod(JSContext* cx, unsigned argc, Value* vp)
10037 : {
10038 0 : THIS_DEBUGOBJECT(cx, argc, vp, "unwrap", args, object);
10039 :
10040 0 : RootedDebuggerObject result(cx);
10041 0 : if (!DebuggerObject::unwrap(cx, object, &result))
10042 0 : return false;
10043 :
10044 0 : args.rval().setObjectOrNull(result);
10045 0 : return true;
10046 : }
10047 :
10048 : const JSPropertySpec DebuggerObject::properties_[] = {
10049 : JS_PSG("callable", DebuggerObject::callableGetter, 0),
10050 : JS_PSG("isBoundFunction", DebuggerObject::isBoundFunctionGetter, 0),
10051 : JS_PSG("isArrowFunction", DebuggerObject::isArrowFunctionGetter, 0),
10052 : JS_PSG("proto", DebuggerObject::protoGetter, 0),
10053 : JS_PSG("class", DebuggerObject::classGetter, 0),
10054 : JS_PSG("name", DebuggerObject::nameGetter, 0),
10055 : JS_PSG("displayName", DebuggerObject::displayNameGetter, 0),
10056 : JS_PSG("parameterNames", DebuggerObject::parameterNamesGetter, 0),
10057 : JS_PSG("script", DebuggerObject::scriptGetter, 0),
10058 : JS_PSG("environment", DebuggerObject::environmentGetter, 0),
10059 : JS_PSG("boundTargetFunction", DebuggerObject::boundTargetFunctionGetter, 0),
10060 : JS_PSG("boundThis", DebuggerObject::boundThisGetter, 0),
10061 : JS_PSG("boundArguments", DebuggerObject::boundArgumentsGetter, 0),
10062 : JS_PSG("global", DebuggerObject::globalGetter, 0),
10063 : JS_PSG("allocationSite", DebuggerObject::allocationSiteGetter, 0),
10064 : JS_PSG("errorMessageName", DebuggerObject::errorMessageNameGetter, 0),
10065 : JS_PSG("errorNotes", DebuggerObject::errorNotesGetter, 0),
10066 : JS_PSG("errorLineNumber", DebuggerObject::errorLineNumberGetter, 0),
10067 : JS_PSG("errorColumnNumber", DebuggerObject::errorColumnNumberGetter, 0),
10068 : JS_PSG("isProxy", DebuggerObject::isProxyGetter, 0),
10069 : JS_PSG("proxyTarget", DebuggerObject::proxyTargetGetter, 0),
10070 : JS_PSG("proxyHandler", DebuggerObject::proxyHandlerGetter, 0),
10071 : JS_PS_END
10072 : };
10073 :
10074 : const JSPropertySpec DebuggerObject::promiseProperties_[] = {
10075 : JS_PSG("isPromise", DebuggerObject::isPromiseGetter, 0),
10076 : JS_PSG("promiseState", DebuggerObject::promiseStateGetter, 0),
10077 : JS_PSG("promiseValue", DebuggerObject::promiseValueGetter, 0),
10078 : JS_PSG("promiseReason", DebuggerObject::promiseReasonGetter, 0),
10079 : JS_PSG("promiseLifetime", DebuggerObject::promiseLifetimeGetter, 0),
10080 : JS_PSG("promiseTimeToResolution", DebuggerObject::promiseTimeToResolutionGetter, 0),
10081 : JS_PSG("promiseAllocationSite", DebuggerObject::promiseAllocationSiteGetter, 0),
10082 : JS_PSG("promiseResolutionSite", DebuggerObject::promiseResolutionSiteGetter, 0),
10083 : JS_PSG("promiseID", DebuggerObject::promiseIDGetter, 0),
10084 : JS_PSG("promiseDependentPromises", DebuggerObject::promiseDependentPromisesGetter, 0),
10085 : JS_PS_END
10086 : };
10087 :
10088 : const JSFunctionSpec DebuggerObject::methods_[] = {
10089 : JS_FN("isExtensible", DebuggerObject::isExtensibleMethod, 0, 0),
10090 : JS_FN("isSealed", DebuggerObject::isSealedMethod, 0, 0),
10091 : JS_FN("isFrozen", DebuggerObject::isFrozenMethod, 0, 0),
10092 : JS_FN("getOwnPropertyNames", DebuggerObject::getOwnPropertyNamesMethod, 0, 0),
10093 : JS_FN("getOwnPropertySymbols", DebuggerObject::getOwnPropertySymbolsMethod, 0, 0),
10094 : JS_FN("getOwnPropertyDescriptor", DebuggerObject::getOwnPropertyDescriptorMethod, 1, 0),
10095 : JS_FN("preventExtensions", DebuggerObject::preventExtensionsMethod, 0, 0),
10096 : JS_FN("seal", DebuggerObject::sealMethod, 0, 0),
10097 : JS_FN("freeze", DebuggerObject::freezeMethod, 0, 0),
10098 : JS_FN("defineProperty", DebuggerObject::definePropertyMethod, 2, 0),
10099 : JS_FN("defineProperties", DebuggerObject::definePropertiesMethod, 1, 0),
10100 : JS_FN("deleteProperty", DebuggerObject::deletePropertyMethod, 1, 0),
10101 : JS_FN("call", DebuggerObject::callMethod, 0, 0),
10102 : JS_FN("apply", DebuggerObject::applyMethod, 0, 0),
10103 : JS_FN("asEnvironment", DebuggerObject::asEnvironmentMethod, 0, 0),
10104 : JS_FN("forceLexicalInitializationByName", DebuggerObject::forceLexicalInitializationByNameMethod, 1, 0),
10105 : JS_FN("executeInGlobal", DebuggerObject::executeInGlobalMethod, 1, 0),
10106 : JS_FN("executeInGlobalWithBindings", DebuggerObject::executeInGlobalWithBindingsMethod, 2, 0),
10107 : JS_FN("makeDebuggeeValue", DebuggerObject::makeDebuggeeValueMethod, 1, 0),
10108 : JS_FN("unsafeDereference", DebuggerObject::unsafeDereferenceMethod, 0, 0),
10109 : JS_FN("unwrap", DebuggerObject::unwrapMethod, 0, 0),
10110 : JS_FS_END
10111 : };
10112 :
10113 : /* static */ NativeObject*
10114 0 : DebuggerObject::initClass(JSContext* cx, HandleObject obj, HandleObject debugCtor)
10115 : {
10116 0 : Handle<GlobalObject*> global = obj.as<GlobalObject>();
10117 0 : RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
10118 :
10119 0 : RootedNativeObject objectProto(cx, InitClass(cx, debugCtor, objProto, &class_,
10120 : construct, 0, properties_,
10121 0 : methods_, nullptr, nullptr));
10122 :
10123 0 : if (!objectProto)
10124 0 : return nullptr;
10125 :
10126 0 : if (!DefinePropertiesAndFunctions(cx, objectProto, promiseProperties_, nullptr))
10127 0 : return nullptr;
10128 :
10129 0 : return objectProto;
10130 : }
10131 :
10132 : /* static */ DebuggerObject*
10133 0 : DebuggerObject::create(JSContext* cx, HandleObject proto, HandleObject referent,
10134 : HandleNativeObject debugger)
10135 : {
10136 0 : NewObjectKind newKind = IsInsideNursery(referent) ? GenericObject : TenuredObject;
10137 0 : JSObject* obj = NewObjectWithGivenProto(cx, &DebuggerObject::class_, proto, newKind);
10138 0 : if (!obj)
10139 0 : return nullptr;
10140 :
10141 0 : DebuggerObject& object = obj->as<DebuggerObject>();
10142 0 : object.setPrivateGCThing(referent);
10143 0 : object.setReservedSlot(JSSLOT_DEBUGOBJECT_OWNER, ObjectValue(*debugger));
10144 :
10145 0 : return &object;
10146 : }
10147 :
10148 : bool
10149 0 : DebuggerObject::isCallable() const
10150 : {
10151 0 : return referent()->isCallable();
10152 : }
10153 :
10154 : bool
10155 0 : DebuggerObject::isFunction() const
10156 : {
10157 0 : return referent()->is<JSFunction>();
10158 : }
10159 :
10160 : bool
10161 0 : DebuggerObject::isDebuggeeFunction() const
10162 : {
10163 0 : return referent()->is<JSFunction>() &&
10164 0 : owner()->observesGlobal(&referent()->as<JSFunction>().global());
10165 : }
10166 :
10167 : bool
10168 0 : DebuggerObject::isBoundFunction() const
10169 : {
10170 0 : MOZ_ASSERT(isDebuggeeFunction());
10171 :
10172 0 : return referent()->isBoundFunction();
10173 : }
10174 :
10175 : bool
10176 0 : DebuggerObject::isArrowFunction() const
10177 : {
10178 0 : MOZ_ASSERT(isDebuggeeFunction());
10179 :
10180 0 : return referent()->as<JSFunction>().isArrow();
10181 : }
10182 :
10183 : bool
10184 0 : DebuggerObject::isGlobal() const
10185 : {
10186 0 : return referent()->is<GlobalObject>();
10187 : }
10188 :
10189 : bool
10190 0 : DebuggerObject::isScriptedProxy() const
10191 : {
10192 0 : return js::IsScriptedProxy(referent());
10193 : }
10194 :
10195 : bool
10196 0 : DebuggerObject::isPromise() const
10197 : {
10198 0 : JSObject* referent = this->referent();
10199 :
10200 0 : if (IsCrossCompartmentWrapper(referent)) {
10201 0 : referent = CheckedUnwrap(referent);
10202 0 : if (!referent)
10203 0 : return false;
10204 : }
10205 :
10206 0 : return referent->is<PromiseObject>();
10207 : }
10208 :
10209 : /* static */ bool
10210 0 : DebuggerObject::getClassName(JSContext* cx, HandleDebuggerObject object,
10211 : MutableHandleString result)
10212 : {
10213 0 : RootedObject referent(cx, object->referent());
10214 :
10215 : const char* className;
10216 : {
10217 0 : AutoCompartment ac(cx, referent);
10218 0 : className = GetObjectClassName(cx, referent);
10219 : }
10220 :
10221 0 : JSAtom* str = Atomize(cx, className, strlen(className));
10222 0 : if (!str)
10223 0 : return false;
10224 :
10225 0 : result.set(str);
10226 0 : return true;
10227 : }
10228 :
10229 : /* static */ bool
10230 0 : DebuggerObject::getGlobal(JSContext* cx, HandleDebuggerObject object,
10231 : MutableHandleDebuggerObject result)
10232 : {
10233 0 : RootedObject referent(cx, object->referent());
10234 0 : Debugger* dbg = object->owner();
10235 :
10236 0 : RootedObject global(cx, &referent->global());
10237 0 : return dbg->wrapDebuggeeObject(cx, global, result);
10238 : }
10239 :
10240 : JSAtom*
10241 0 : DebuggerObject::name(JSContext* cx) const
10242 : {
10243 0 : MOZ_ASSERT(isFunction());
10244 :
10245 0 : JSAtom* atom = referent()->as<JSFunction>().explicitName();
10246 0 : if (atom)
10247 0 : cx->markAtom(atom);
10248 0 : return atom;
10249 : }
10250 :
10251 : JSAtom*
10252 0 : DebuggerObject::displayName(JSContext* cx) const
10253 : {
10254 0 : MOZ_ASSERT(isFunction());
10255 :
10256 0 : JSAtom* atom = referent()->as<JSFunction>().displayAtom();
10257 0 : if (atom)
10258 0 : cx->markAtom(atom);
10259 0 : return atom;
10260 : }
10261 :
10262 : JS::PromiseState
10263 0 : DebuggerObject::promiseState() const
10264 : {
10265 0 : return promise()->state();
10266 : }
10267 :
10268 : double
10269 0 : DebuggerObject::promiseLifetime() const
10270 : {
10271 0 : return promise()->lifetime();
10272 : }
10273 :
10274 : double
10275 0 : DebuggerObject::promiseTimeToResolution() const
10276 : {
10277 0 : MOZ_ASSERT(promiseState() != JS::PromiseState::Pending);
10278 :
10279 0 : return promise()->timeToResolution();
10280 : }
10281 :
10282 : /* static */ bool
10283 0 : DebuggerObject::getParameterNames(JSContext* cx, HandleDebuggerObject object,
10284 : MutableHandle<StringVector> result)
10285 : {
10286 0 : MOZ_ASSERT(object->isDebuggeeFunction());
10287 :
10288 0 : RootedFunction referent(cx, &object->referent()->as<JSFunction>());
10289 :
10290 0 : if (!result.growBy(referent->nargs()))
10291 0 : return false;
10292 0 : if (referent->isInterpreted()) {
10293 0 : RootedScript script(cx, GetOrCreateFunctionScript(cx, referent));
10294 0 : if (!script)
10295 0 : return false;
10296 :
10297 0 : MOZ_ASSERT(referent->nargs() == script->numArgs());
10298 :
10299 0 : if (referent->nargs() > 0) {
10300 0 : PositionalFormalParameterIter fi(script);
10301 0 : for (size_t i = 0; i < referent->nargs(); i++, fi++) {
10302 0 : MOZ_ASSERT(fi.argumentSlot() == i);
10303 0 : JSAtom* atom = fi.name();
10304 0 : if (atom)
10305 0 : cx->markAtom(atom);
10306 0 : result[i].set(atom);
10307 : }
10308 : }
10309 : } else {
10310 0 : for (size_t i = 0; i < referent->nargs(); i++)
10311 0 : result[i].set(nullptr);
10312 : }
10313 :
10314 0 : return true;
10315 : }
10316 :
10317 : /* static */ bool
10318 0 : DebuggerObject::getBoundTargetFunction(JSContext* cx, HandleDebuggerObject object,
10319 : MutableHandleDebuggerObject result)
10320 : {
10321 0 : MOZ_ASSERT(object->isBoundFunction());
10322 :
10323 0 : RootedFunction referent(cx, &object->referent()->as<JSFunction>());
10324 0 : Debugger* dbg = object->owner();
10325 :
10326 0 : RootedObject target(cx, referent->getBoundFunctionTarget());
10327 0 : return dbg->wrapDebuggeeObject(cx, target, result);
10328 : }
10329 :
10330 : /* static */ bool
10331 0 : DebuggerObject::getBoundThis(JSContext* cx, HandleDebuggerObject object,
10332 : MutableHandleValue result)
10333 : {
10334 0 : MOZ_ASSERT(object->isBoundFunction());
10335 :
10336 0 : RootedFunction referent(cx, &object->referent()->as<JSFunction>());
10337 0 : Debugger* dbg = object->owner();
10338 :
10339 0 : result.set(referent->getBoundFunctionThis());
10340 0 : return dbg->wrapDebuggeeValue(cx, result);
10341 : }
10342 :
10343 : /* static */ bool
10344 0 : DebuggerObject::getBoundArguments(JSContext* cx, HandleDebuggerObject object,
10345 : MutableHandle<ValueVector> result)
10346 : {
10347 0 : MOZ_ASSERT(object->isBoundFunction());
10348 :
10349 0 : RootedFunction referent(cx, &object->referent()->as<JSFunction>());
10350 0 : Debugger* dbg = object->owner();
10351 :
10352 0 : size_t length = referent->getBoundFunctionArgumentCount();
10353 0 : if (!result.resize(length))
10354 0 : return false;
10355 0 : for (size_t i = 0; i < length; i++) {
10356 0 : result[i].set(referent->getBoundFunctionArgument(i));
10357 0 : if (!dbg->wrapDebuggeeValue(cx, result[i]))
10358 0 : return false;
10359 : }
10360 0 : return true;
10361 : }
10362 :
10363 : /* static */ SavedFrame*
10364 0 : Debugger::getObjectAllocationSite(JSObject& obj)
10365 : {
10366 0 : JSObject* metadata = GetAllocationMetadata(&obj);
10367 0 : if (!metadata)
10368 0 : return nullptr;
10369 :
10370 0 : MOZ_ASSERT(!metadata->is<WrapperObject>());
10371 0 : return SavedFrame::isSavedFrameAndNotProto(*metadata)
10372 0 : ? &metadata->as<SavedFrame>()
10373 0 : : nullptr;
10374 : }
10375 :
10376 : /* static */ bool
10377 0 : DebuggerObject::getAllocationSite(JSContext* cx, HandleDebuggerObject object,
10378 : MutableHandleObject result)
10379 : {
10380 0 : RootedObject referent(cx, object->referent());
10381 :
10382 0 : RootedObject allocSite(cx, Debugger::getObjectAllocationSite(*referent));
10383 0 : if (!cx->compartment()->wrap(cx, &allocSite))
10384 0 : return false;
10385 :
10386 0 : result.set(allocSite);
10387 0 : return true;
10388 : }
10389 :
10390 : /* static */ bool
10391 0 : DebuggerObject::getErrorReport(JSContext* cx, HandleObject maybeError, JSErrorReport*& report)
10392 : {
10393 0 : JSObject* obj = maybeError;
10394 0 : if (IsCrossCompartmentWrapper(obj))
10395 0 : obj = CheckedUnwrap(obj);
10396 :
10397 0 : if (!obj) {
10398 0 : ReportAccessDenied(cx);
10399 0 : return false;
10400 : }
10401 :
10402 0 : if (!obj->is<ErrorObject>()) {
10403 0 : report = nullptr;
10404 0 : return true;
10405 : }
10406 :
10407 0 : report = obj->as<ErrorObject>().getErrorReport();
10408 0 : return true;
10409 : }
10410 :
10411 : /* static */ bool
10412 0 : DebuggerObject::getErrorMessageName(JSContext* cx, HandleDebuggerObject object,
10413 : MutableHandleString result)
10414 : {
10415 0 : RootedObject referent(cx, object->referent());
10416 : JSErrorReport* report;
10417 0 : if (!getErrorReport(cx, referent, report))
10418 0 : return false;
10419 :
10420 0 : if (!report) {
10421 0 : result.set(nullptr);
10422 0 : return true;
10423 : }
10424 :
10425 0 : const JSErrorFormatString* efs = GetErrorMessage(nullptr, report->errorNumber);
10426 0 : if (!efs) {
10427 0 : result.set(nullptr);
10428 0 : return true;
10429 : }
10430 :
10431 0 : RootedString str(cx, JS_NewStringCopyZ(cx, efs->name));
10432 0 : if (!cx->compartment()->wrap(cx, &str))
10433 0 : return false;
10434 :
10435 0 : result.set(str);
10436 0 : return true;
10437 : }
10438 :
10439 : /* static */ bool
10440 0 : DebuggerObject::getErrorNotes(JSContext* cx, HandleDebuggerObject object,
10441 : MutableHandleValue result)
10442 : {
10443 0 : RootedObject referent(cx, object->referent());
10444 : JSErrorReport* report;
10445 0 : if (!getErrorReport(cx, referent, report))
10446 0 : return false;
10447 :
10448 0 : if (!report) {
10449 0 : result.setUndefined();
10450 0 : return true;
10451 : }
10452 :
10453 0 : RootedObject errorNotesArray(cx, CreateErrorNotesArray(cx, report));
10454 0 : if (!errorNotesArray)
10455 0 : return false;
10456 :
10457 0 : if (!cx->compartment()->wrap(cx, &errorNotesArray))
10458 0 : return false;
10459 0 : result.setObject(*errorNotesArray);
10460 0 : return true;
10461 : }
10462 :
10463 : /* static */ bool
10464 0 : DebuggerObject::getErrorLineNumber(JSContext* cx, HandleDebuggerObject object,
10465 : MutableHandleValue result)
10466 : {
10467 0 : RootedObject referent(cx, object->referent());
10468 : JSErrorReport* report;
10469 0 : if (!getErrorReport(cx, referent, report))
10470 0 : return false;
10471 :
10472 0 : if (!report) {
10473 0 : result.setUndefined();
10474 0 : return true;
10475 : }
10476 :
10477 0 : result.setNumber(report->lineno);
10478 0 : return true;
10479 : }
10480 :
10481 : /* static */ bool
10482 0 : DebuggerObject::getErrorColumnNumber(JSContext* cx, HandleDebuggerObject object,
10483 : MutableHandleValue result)
10484 : {
10485 0 : RootedObject referent(cx, object->referent());
10486 : JSErrorReport* report;
10487 0 : if (!getErrorReport(cx, referent, report))
10488 0 : return false;
10489 :
10490 0 : if (!report) {
10491 0 : result.setUndefined();
10492 0 : return true;
10493 : }
10494 :
10495 0 : result.setNumber(report->column);
10496 0 : return true;
10497 : }
10498 :
10499 : /* static */ bool
10500 0 : DebuggerObject::getPromiseValue(JSContext* cx, HandleDebuggerObject object,
10501 : MutableHandleValue result)
10502 : {
10503 0 : MOZ_ASSERT(object->promiseState() == JS::PromiseState::Fulfilled);
10504 :
10505 0 : result.set(object->promise()->value());
10506 0 : return object->owner()->wrapDebuggeeValue(cx, result);
10507 : }
10508 :
10509 : /* static */ bool
10510 0 : DebuggerObject::getPromiseReason(JSContext* cx, HandleDebuggerObject object,
10511 : MutableHandleValue result)
10512 : {
10513 0 : MOZ_ASSERT(object->promiseState() == JS::PromiseState::Rejected);
10514 :
10515 0 : result.set(object->promise()->reason());
10516 0 : return object->owner()->wrapDebuggeeValue(cx, result);
10517 : }
10518 :
10519 : /* static */ bool
10520 0 : DebuggerObject::isExtensible(JSContext* cx, HandleDebuggerObject object, bool& result)
10521 : {
10522 0 : RootedObject referent(cx, object->referent());
10523 :
10524 0 : Maybe<AutoCompartment> ac;
10525 0 : ac.emplace(cx, referent);
10526 0 : ErrorCopier ec(ac);
10527 0 : return IsExtensible(cx, referent, &result);
10528 : }
10529 :
10530 : /* static */ bool
10531 0 : DebuggerObject::isSealed(JSContext* cx, HandleDebuggerObject object, bool& result)
10532 : {
10533 0 : RootedObject referent(cx, object->referent());
10534 :
10535 0 : Maybe<AutoCompartment> ac;
10536 0 : ac.emplace(cx, referent);
10537 :
10538 0 : ErrorCopier ec(ac);
10539 0 : return TestIntegrityLevel(cx, referent, IntegrityLevel::Sealed, &result);
10540 : }
10541 :
10542 : /* static */ bool
10543 0 : DebuggerObject::isFrozen(JSContext* cx, HandleDebuggerObject object, bool& result)
10544 : {
10545 0 : RootedObject referent(cx, object->referent());
10546 :
10547 0 : Maybe<AutoCompartment> ac;
10548 0 : ac.emplace(cx, referent);
10549 :
10550 0 : ErrorCopier ec(ac);
10551 0 : return TestIntegrityLevel(cx, referent, IntegrityLevel::Frozen, &result);
10552 : }
10553 :
10554 : /* static */ bool
10555 0 : DebuggerObject::getPrototypeOf(JSContext* cx, HandleDebuggerObject object,
10556 : MutableHandleDebuggerObject result)
10557 : {
10558 0 : RootedObject referent(cx, object->referent());
10559 0 : Debugger* dbg = object->owner();
10560 :
10561 0 : RootedObject proto(cx);
10562 : {
10563 0 : AutoCompartment ac(cx, referent);
10564 0 : if (!GetPrototype(cx, referent, &proto))
10565 0 : return false;
10566 : }
10567 :
10568 0 : if (!proto) {
10569 0 : result.set(nullptr);
10570 0 : return true;
10571 : }
10572 :
10573 0 : return dbg->wrapDebuggeeObject(cx, proto, result);
10574 : }
10575 :
10576 : /* static */ bool
10577 0 : DebuggerObject::getOwnPropertyNames(JSContext* cx, HandleDebuggerObject object,
10578 : MutableHandle<IdVector> result)
10579 : {
10580 0 : RootedObject referent(cx, object->referent());
10581 :
10582 0 : AutoIdVector ids(cx);
10583 : {
10584 0 : Maybe<AutoCompartment> ac;
10585 0 : ac.emplace(cx, referent);
10586 :
10587 0 : ErrorCopier ec(ac);
10588 0 : if (!GetPropertyKeys(cx, referent, JSITER_OWNONLY | JSITER_HIDDEN, &ids))
10589 0 : return false;
10590 : }
10591 :
10592 0 : for (size_t i = 0; i < ids.length(); i++)
10593 0 : cx->markId(ids[i]);
10594 :
10595 0 : return result.append(ids.begin(), ids.end());
10596 : }
10597 :
10598 : /* static */ bool
10599 0 : DebuggerObject::getOwnPropertySymbols(JSContext* cx, HandleDebuggerObject object,
10600 : MutableHandle<IdVector> result)
10601 : {
10602 0 : RootedObject referent(cx, object->referent());
10603 :
10604 0 : AutoIdVector ids(cx);
10605 : {
10606 0 : Maybe<AutoCompartment> ac;
10607 0 : ac.emplace(cx, referent);
10608 :
10609 0 : ErrorCopier ec(ac);
10610 0 : if (!GetPropertyKeys(cx, referent,
10611 : JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY,
10612 : &ids))
10613 0 : return false;
10614 : }
10615 :
10616 0 : for (size_t i = 0; i < ids.length(); i++)
10617 0 : cx->markId(ids[i]);
10618 :
10619 0 : return result.append(ids.begin(), ids.end());
10620 : }
10621 :
10622 : /* static */ bool
10623 0 : DebuggerObject::getOwnPropertyDescriptor(JSContext* cx, HandleDebuggerObject object,
10624 : HandleId id, MutableHandle<PropertyDescriptor> desc)
10625 : {
10626 0 : RootedObject referent(cx, object->referent());
10627 0 : Debugger* dbg = object->owner();
10628 :
10629 : /* Bug: This can cause the debuggee to run! */
10630 : {
10631 0 : Maybe<AutoCompartment> ac;
10632 0 : ac.emplace(cx, referent);
10633 0 : cx->markId(id);
10634 :
10635 0 : ErrorCopier ec(ac);
10636 0 : if (!GetOwnPropertyDescriptor(cx, referent, id, desc))
10637 0 : return false;
10638 : }
10639 :
10640 0 : if (desc.object()) {
10641 : /* Rewrap the debuggee values in desc for the debugger. */
10642 0 : if (!dbg->wrapDebuggeeValue(cx, desc.value()))
10643 0 : return false;
10644 :
10645 0 : if (desc.hasGetterObject()) {
10646 0 : RootedValue get(cx, ObjectOrNullValue(desc.getterObject()));
10647 0 : if (!dbg->wrapDebuggeeValue(cx, &get))
10648 0 : return false;
10649 0 : desc.setGetterObject(get.toObjectOrNull());
10650 : }
10651 0 : if (desc.hasSetterObject()) {
10652 0 : RootedValue set(cx, ObjectOrNullValue(desc.setterObject()));
10653 0 : if (!dbg->wrapDebuggeeValue(cx, &set))
10654 0 : return false;
10655 0 : desc.setSetterObject(set.toObjectOrNull());
10656 : }
10657 :
10658 : // Avoid tripping same-compartment assertions in JS::FromPropertyDescriptor().
10659 0 : desc.object().set(object);
10660 : }
10661 :
10662 0 : return true;
10663 : }
10664 :
10665 : /* static */ bool
10666 0 : DebuggerObject::preventExtensions(JSContext* cx, HandleDebuggerObject object)
10667 : {
10668 0 : RootedObject referent(cx, object->referent());
10669 :
10670 0 : Maybe<AutoCompartment> ac;
10671 0 : ac.emplace(cx, referent);
10672 :
10673 0 : ErrorCopier ec(ac);
10674 0 : return PreventExtensions(cx, referent);
10675 : }
10676 :
10677 : /* static */ bool
10678 0 : DebuggerObject::seal(JSContext* cx, HandleDebuggerObject object)
10679 : {
10680 0 : RootedObject referent(cx, object->referent());
10681 :
10682 0 : Maybe<AutoCompartment> ac;
10683 0 : ac.emplace(cx, referent);
10684 :
10685 0 : ErrorCopier ec(ac);
10686 0 : return SetIntegrityLevel(cx, referent, IntegrityLevel::Sealed);
10687 : }
10688 :
10689 : /* static */ bool
10690 0 : DebuggerObject::freeze(JSContext* cx, HandleDebuggerObject object)
10691 : {
10692 0 : RootedObject referent(cx, object->referent());
10693 :
10694 0 : Maybe<AutoCompartment> ac;
10695 0 : ac.emplace(cx, referent);
10696 :
10697 0 : ErrorCopier ec(ac);
10698 0 : return SetIntegrityLevel(cx, referent, IntegrityLevel::Frozen);
10699 : }
10700 :
10701 : /* static */ bool
10702 0 : DebuggerObject::defineProperty(JSContext* cx, HandleDebuggerObject object, HandleId id,
10703 : Handle<PropertyDescriptor> desc_)
10704 : {
10705 0 : RootedObject referent(cx, object->referent());
10706 0 : Debugger* dbg = object->owner();
10707 :
10708 0 : Rooted<PropertyDescriptor> desc(cx, desc_);
10709 0 : if (!dbg->unwrapPropertyDescriptor(cx, referent, &desc))
10710 0 : return false;
10711 0 : JS_TRY_OR_RETURN_FALSE(cx, CheckPropertyDescriptorAccessors(cx, desc));
10712 :
10713 0 : Maybe<AutoCompartment> ac;
10714 0 : ac.emplace(cx, referent);
10715 0 : if (!cx->compartment()->wrap(cx, &desc))
10716 0 : return false;
10717 0 : cx->markId(id);
10718 :
10719 0 : ErrorCopier ec(ac);
10720 0 : if (!DefineProperty(cx, referent, id, desc))
10721 0 : return false;
10722 :
10723 0 : return true;
10724 : }
10725 :
10726 : /* static */ bool
10727 0 : DebuggerObject::defineProperties(JSContext* cx, HandleDebuggerObject object,
10728 : Handle<IdVector> ids,
10729 : Handle<PropertyDescriptorVector> descs_)
10730 : {
10731 0 : RootedObject referent(cx, object->referent());
10732 0 : Debugger* dbg = object->owner();
10733 :
10734 0 : Rooted<PropertyDescriptorVector> descs(cx, PropertyDescriptorVector(cx));
10735 0 : if (!descs.append(descs_.begin(), descs_.end()))
10736 0 : return false;
10737 0 : for (size_t i = 0; i < descs.length(); i++) {
10738 0 : if (!dbg->unwrapPropertyDescriptor(cx, referent, descs[i]))
10739 0 : return false;
10740 0 : JS_TRY_OR_RETURN_FALSE(cx, CheckPropertyDescriptorAccessors(cx, descs[i]));
10741 : }
10742 :
10743 0 : Maybe<AutoCompartment> ac;
10744 0 : ac.emplace(cx, referent);
10745 0 : for (size_t i = 0; i < descs.length(); i++) {
10746 0 : if (!cx->compartment()->wrap(cx, descs[i]))
10747 0 : return false;
10748 0 : cx->markId(ids[i]);
10749 : }
10750 :
10751 0 : ErrorCopier ec(ac);
10752 0 : for (size_t i = 0; i < descs.length(); i++) {
10753 0 : if (!DefineProperty(cx, referent, ids[i], descs[i]))
10754 0 : return false;
10755 : }
10756 :
10757 0 : return true;
10758 : }
10759 :
10760 : /* static */ bool
10761 0 : DebuggerObject::deleteProperty(JSContext* cx, HandleDebuggerObject object, HandleId id,
10762 : ObjectOpResult& result)
10763 : {
10764 0 : RootedObject referent(cx, object->referent());
10765 :
10766 0 : Maybe<AutoCompartment> ac;
10767 0 : ac.emplace(cx, referent);
10768 :
10769 0 : cx->markId(id);
10770 :
10771 0 : ErrorCopier ec(ac);
10772 0 : return DeleteProperty(cx, referent, id, result);
10773 : }
10774 :
10775 : /* static */ bool
10776 0 : DebuggerObject::call(JSContext* cx, HandleDebuggerObject object, HandleValue thisv_,
10777 : Handle<ValueVector> args, MutableHandleValue result)
10778 : {
10779 0 : RootedObject referent(cx, object->referent());
10780 0 : Debugger* dbg = object->owner();
10781 :
10782 0 : if (!referent->isCallable()) {
10783 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
10784 0 : "Debugger.Object", "call", referent->getClass()->name);
10785 0 : return false;
10786 : }
10787 :
10788 0 : RootedValue calleev(cx, ObjectValue(*referent));
10789 :
10790 : /*
10791 : * Unwrap Debugger.Objects. This happens in the debugger's compartment since
10792 : * that is where any exceptions must be reported.
10793 : */
10794 0 : RootedValue thisv(cx, thisv_);
10795 0 : if (!dbg->unwrapDebuggeeValue(cx, &thisv))
10796 0 : return false;
10797 0 : Rooted<ValueVector> args2(cx, ValueVector(cx));
10798 0 : if (!args2.append(args.begin(), args.end()))
10799 0 : return false;
10800 0 : for (unsigned i = 0; i < args2.length(); ++i) {
10801 0 : if (!dbg->unwrapDebuggeeValue(cx, args2[i]))
10802 0 : return false;
10803 : }
10804 :
10805 : /*
10806 : * Enter the debuggee compartment and rewrap all input value for that compartment.
10807 : * (Rewrapping always takes place in the destination compartment.)
10808 : */
10809 0 : Maybe<AutoCompartment> ac;
10810 0 : ac.emplace(cx, referent);
10811 0 : if (!cx->compartment()->wrap(cx, &calleev) || !cx->compartment()->wrap(cx, &thisv))
10812 0 : return false;
10813 0 : for (unsigned i = 0; i < args2.length(); ++i) {
10814 0 : if (!cx->compartment()->wrap(cx, args2[i]))
10815 0 : return false;
10816 : }
10817 :
10818 : /*
10819 : * Call the function. Use receiveCompletionValue to return to the debugger
10820 : * compartment and populate args.rval().
10821 : */
10822 0 : LeaveDebuggeeNoExecute nnx(cx);
10823 :
10824 : bool ok;
10825 : {
10826 0 : InvokeArgs invokeArgs(cx);
10827 :
10828 0 : ok = invokeArgs.init(cx, args2.length());
10829 0 : if (ok) {
10830 0 : for (size_t i = 0; i < args2.length(); ++i)
10831 0 : invokeArgs[i].set(args2[i]);
10832 :
10833 0 : ok = js::Call(cx, calleev, thisv, invokeArgs, result);
10834 : }
10835 : }
10836 :
10837 0 : return dbg->receiveCompletionValue(ac, ok, result, result);
10838 : }
10839 :
10840 : /* static */ bool
10841 0 : DebuggerObject::forceLexicalInitializationByName(JSContext* cx, HandleDebuggerObject object,
10842 : HandleId id, bool& result)
10843 : {
10844 0 : if (!JSID_IS_STRING(id)) {
10845 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
10846 : "Debugger.Object.prototype.forceLexicalInitializationByName",
10847 0 : "string", InformalValueTypeName(IdToValue(id)));
10848 0 : return false;
10849 : }
10850 :
10851 0 : MOZ_ASSERT(object->isGlobal());
10852 :
10853 0 : Rooted<GlobalObject*> referent(cx, &object->referent()->as<GlobalObject>());
10854 :
10855 0 : RootedObject globalLexical(cx, &referent->lexicalEnvironment());
10856 0 : RootedObject pobj(cx);
10857 0 : Rooted<PropertyResult> prop(cx);
10858 0 : if (!LookupProperty(cx, globalLexical, id, &pobj, &prop))
10859 0 : return false;
10860 :
10861 0 : result = false;
10862 0 : if (prop) {
10863 0 : MOZ_ASSERT(prop.isNativeProperty());
10864 0 : Shape* shape = prop.shape();
10865 0 : Value v = globalLexical->as<NativeObject>().getSlot(shape->slot());
10866 0 : if (shape->hasSlot() && v.isMagic() && v.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
10867 0 : globalLexical->as<NativeObject>().setSlot(shape->slot(), UndefinedValue());
10868 0 : result = true;
10869 : }
10870 : }
10871 :
10872 0 : return true;
10873 : }
10874 :
10875 : /* static */ bool
10876 0 : DebuggerObject::executeInGlobal(JSContext* cx, HandleDebuggerObject object,
10877 : mozilla::Range<const char16_t> chars, HandleObject bindings,
10878 : const EvalOptions& options, JSTrapStatus& status,
10879 : MutableHandleValue value)
10880 : {
10881 0 : MOZ_ASSERT(object->isGlobal());
10882 :
10883 0 : Rooted<GlobalObject*> referent(cx, &object->referent()->as<GlobalObject>());
10884 0 : Debugger* dbg = object->owner();
10885 :
10886 0 : RootedObject globalLexical(cx, &referent->lexicalEnvironment());
10887 0 : return DebuggerGenericEval(cx, chars, bindings, options, status, value, dbg, globalLexical,
10888 0 : nullptr);
10889 : }
10890 :
10891 : /* static */ bool
10892 0 : DebuggerObject::makeDebuggeeValue(JSContext* cx, HandleDebuggerObject object,
10893 : HandleValue value_, MutableHandleValue result)
10894 : {
10895 0 : RootedObject referent(cx, object->referent());
10896 0 : Debugger* dbg = object->owner();
10897 :
10898 0 : RootedValue value(cx, value_);
10899 :
10900 : /* Non-objects are already debuggee values. */
10901 0 : if (value.isObject()) {
10902 : // Enter this Debugger.Object's referent's compartment, and wrap the
10903 : // argument as appropriate for references from there.
10904 : {
10905 0 : AutoCompartment ac(cx, referent);
10906 0 : if (!cx->compartment()->wrap(cx, &value))
10907 0 : return false;
10908 : }
10909 :
10910 : // Back in the debugger's compartment, produce a new Debugger.Object
10911 : // instance referring to the wrapped argument.
10912 0 : if (!dbg->wrapDebuggeeValue(cx, &value))
10913 0 : return false;
10914 : }
10915 :
10916 0 : result.set(value);
10917 0 : return true;
10918 : }
10919 :
10920 : /* static */ bool
10921 0 : DebuggerObject::unsafeDereference(JSContext* cx, HandleDebuggerObject object,
10922 : MutableHandleObject result)
10923 : {
10924 0 : RootedObject referent(cx, object->referent());
10925 :
10926 0 : if (!cx->compartment()->wrap(cx, &referent))
10927 0 : return false;
10928 :
10929 : // Wrapping should return the WindowProxy.
10930 0 : MOZ_ASSERT(!IsWindow(referent));
10931 :
10932 0 : result.set(referent);
10933 0 : return true;
10934 : }
10935 :
10936 : /* static */ bool
10937 0 : DebuggerObject::unwrap(JSContext* cx, HandleDebuggerObject object,
10938 : MutableHandleDebuggerObject result)
10939 : {
10940 0 : RootedObject referent(cx, object->referent());
10941 0 : Debugger* dbg = object->owner();
10942 :
10943 0 : RootedObject unwrapped(cx, UnwrapOneChecked(referent));
10944 0 : if (!unwrapped) {
10945 0 : result.set(nullptr);
10946 0 : return true;
10947 : }
10948 :
10949 : // Don't allow unwrapping to create a D.O whose referent is in an
10950 : // invisible-to-Debugger global. (If our referent is a *wrapper* to such,
10951 : // and the wrapper is in a visible compartment, that's fine.)
10952 0 : JSCompartment* unwrappedCompartment = unwrapped->compartment();
10953 0 : if (unwrappedCompartment->creationOptions().invisibleToDebugger()) {
10954 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_INVISIBLE_COMPARTMENT);
10955 0 : return false;
10956 : }
10957 :
10958 0 : return dbg->wrapDebuggeeObject(cx, unwrapped, result);
10959 : }
10960 :
10961 : /* static */ bool
10962 0 : DebuggerObject::requireGlobal(JSContext* cx, HandleDebuggerObject object)
10963 : {
10964 0 : if (!object->isGlobal()) {
10965 0 : RootedObject referent(cx, object->referent());
10966 :
10967 0 : const char* isWrapper = "";
10968 0 : const char* isWindowProxy = "";
10969 :
10970 : /* Help the poor programmer by pointing out wrappers around globals... */
10971 0 : if (referent->is<WrapperObject>()) {
10972 0 : referent = js::UncheckedUnwrap(referent);
10973 0 : isWrapper = "a wrapper around ";
10974 : }
10975 :
10976 : /* ... and WindowProxies around Windows. */
10977 0 : if (IsWindowProxy(referent)) {
10978 0 : referent = ToWindowIfWindowProxy(referent);
10979 0 : isWindowProxy = "a WindowProxy referring to ";
10980 : }
10981 :
10982 0 : RootedValue dbgobj(cx, ObjectValue(*object));
10983 0 : if (referent->is<GlobalObject>()) {
10984 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_WRAPPER_IN_WAY,
10985 : JSDVG_SEARCH_STACK, dbgobj, nullptr,
10986 0 : isWrapper, isWindowProxy);
10987 : } else {
10988 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT,
10989 : JSDVG_SEARCH_STACK, dbgobj, nullptr,
10990 0 : "a global object", nullptr);
10991 : }
10992 0 : return false;
10993 : }
10994 :
10995 0 : return true;
10996 : }
10997 :
10998 : /* static */ bool
10999 0 : DebuggerObject::requirePromise(JSContext* cx, HandleDebuggerObject object)
11000 : {
11001 0 : RootedObject referent(cx, object->referent());
11002 :
11003 0 : if (IsCrossCompartmentWrapper(referent)) {
11004 0 : referent = CheckedUnwrap(referent);
11005 0 : if (!referent) {
11006 0 : ReportAccessDenied(cx);
11007 0 : return false;
11008 : }
11009 : }
11010 :
11011 0 : if (!referent->is<PromiseObject>()) {
11012 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
11013 0 : "Debugger", "Promise", object->getClass()->name);
11014 0 : return false;
11015 : }
11016 :
11017 0 : return true;
11018 : }
11019 :
11020 : /* static */ bool
11021 0 : DebuggerObject::getScriptedProxyTarget(JSContext* cx, HandleDebuggerObject object,
11022 : MutableHandleDebuggerObject result)
11023 : {
11024 0 : MOZ_ASSERT(object->isScriptedProxy());
11025 0 : RootedObject referent(cx, object->referent());
11026 0 : Debugger* dbg = object->owner();
11027 0 : RootedObject unwrapped(cx, js::GetProxyTargetObject(referent));
11028 0 : if(!unwrapped) {
11029 0 : result.set(nullptr);
11030 0 : return true;
11031 : }
11032 0 : return dbg->wrapDebuggeeObject(cx, unwrapped, result);
11033 : }
11034 :
11035 : /* static */ bool
11036 0 : DebuggerObject::getScriptedProxyHandler(JSContext* cx, HandleDebuggerObject object,
11037 : MutableHandleDebuggerObject result)
11038 : {
11039 0 : MOZ_ASSERT(object->isScriptedProxy());
11040 0 : RootedObject referent(cx, object->referent());
11041 0 : Debugger* dbg = object->owner();
11042 0 : RootedObject unwrapped(cx, ScriptedProxyHandler::handlerObject(referent));
11043 0 : if(!unwrapped) {
11044 0 : result.set(nullptr);
11045 0 : return true;
11046 : }
11047 0 : return dbg->wrapDebuggeeObject(cx, unwrapped, result);
11048 : }
11049 :
11050 :
11051 : /*** Debugger.Environment ************************************************************************/
11052 :
11053 : void
11054 0 : DebuggerEnv_trace(JSTracer* trc, JSObject* obj)
11055 : {
11056 : /*
11057 : * There is a barrier on private pointers, so the Unbarriered marking
11058 : * is okay.
11059 : */
11060 0 : if (Env* referent = (JSObject*) obj->as<NativeObject>().getPrivate()) {
11061 : TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &referent,
11062 0 : "Debugger.Environment referent");
11063 0 : obj->as<NativeObject>().setPrivateUnbarriered(referent);
11064 : }
11065 0 : }
11066 :
11067 : static DebuggerEnvironment*
11068 0 : DebuggerEnvironment_checkThis(JSContext* cx, const CallArgs& args, const char* fnname,
11069 : bool requireDebuggee)
11070 : {
11071 0 : JSObject* thisobj = NonNullObject(cx, args.thisv());
11072 0 : if (!thisobj)
11073 0 : return nullptr;
11074 0 : if (thisobj->getClass() != &DebuggerEnvironment::class_) {
11075 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
11076 0 : "Debugger.Environment", fnname, thisobj->getClass()->name);
11077 0 : return nullptr;
11078 : }
11079 :
11080 : /*
11081 : * Forbid Debugger.Environment.prototype, which is of class DebuggerEnvironment::class_
11082 : * but isn't a real working Debugger.Environment. The prototype object is
11083 : * distinguished by having no referent.
11084 : */
11085 0 : DebuggerEnvironment* nthisobj = &thisobj->as<DebuggerEnvironment>();
11086 0 : if (!nthisobj->getPrivate()) {
11087 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
11088 0 : "Debugger.Environment", fnname, "prototype object");
11089 0 : return nullptr;
11090 : }
11091 :
11092 : /*
11093 : * Forbid access to Debugger.Environment objects that are not debuggee
11094 : * environments.
11095 : */
11096 0 : if (requireDebuggee) {
11097 0 : Rooted<Env*> env(cx, static_cast<Env*>(nthisobj->getPrivate()));
11098 0 : if (!Debugger::fromChildJSObject(nthisobj)->observesGlobal(&env->global())) {
11099 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_DEBUGGEE,
11100 0 : "Debugger.Environment", "environment");
11101 0 : return nullptr;
11102 : }
11103 : }
11104 :
11105 0 : return nthisobj;
11106 : }
11107 :
11108 : #define THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, fnname, args, environment) \
11109 : CallArgs args = CallArgsFromVp(argc, vp); \
11110 : Rooted<DebuggerEnvironment*> environment(cx, DebuggerEnvironment_checkThis(cx, args, fnname, false)); \
11111 : if (!environment) \
11112 : return false; \
11113 :
11114 : /* static */ bool
11115 0 : DebuggerEnvironment::construct(JSContext* cx, unsigned argc, Value* vp)
11116 : {
11117 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
11118 0 : "Debugger.Environment");
11119 0 : return false;
11120 : }
11121 :
11122 : static bool
11123 0 : IsDeclarative(Env* env)
11124 : {
11125 0 : return env->is<DebugEnvironmentProxy>() &&
11126 0 : env->as<DebugEnvironmentProxy>().isForDeclarative();
11127 : }
11128 :
11129 : template <typename T>
11130 : static bool
11131 0 : IsDebugEnvironmentWrapper(Env* env)
11132 : {
11133 0 : return env->is<DebugEnvironmentProxy>() &&
11134 0 : env->as<DebugEnvironmentProxy>().environment().is<T>();
11135 : }
11136 :
11137 : bool
11138 0 : DebuggerEnvironment::typeGetter(JSContext* cx, unsigned argc, Value* vp)
11139 : {
11140 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get type", args, environment);
11141 :
11142 0 : if (!environment->requireDebuggee(cx))
11143 0 : return false;
11144 :
11145 0 : DebuggerEnvironmentType type = environment->type();
11146 :
11147 : const char* s;
11148 0 : switch (type) {
11149 : case DebuggerEnvironmentType::Declarative:
11150 0 : s = "declarative";
11151 0 : break;
11152 : case DebuggerEnvironmentType::With:
11153 0 : s = "with";
11154 0 : break;
11155 : case DebuggerEnvironmentType::Object:
11156 0 : s = "object";
11157 0 : break;
11158 : }
11159 :
11160 0 : JSAtom* str = Atomize(cx, s, strlen(s), PinAtom);
11161 0 : if (!str)
11162 0 : return false;
11163 :
11164 0 : args.rval().setString(str);
11165 0 : return true;
11166 : }
11167 :
11168 : /* static */ bool
11169 0 : DebuggerEnvironment::parentGetter(JSContext* cx, unsigned argc, Value* vp)
11170 : {
11171 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get type", args, environment);
11172 :
11173 0 : if (!environment->requireDebuggee(cx))
11174 0 : return false;
11175 :
11176 0 : RootedDebuggerEnvironment result(cx);
11177 0 : if (!environment->getParent(cx, &result))
11178 0 : return false;
11179 :
11180 0 : args.rval().setObjectOrNull(result);
11181 0 : return true;
11182 : }
11183 :
11184 : /* static */ bool
11185 0 : DebuggerEnvironment::objectGetter(JSContext* cx, unsigned argc, Value* vp)
11186 : {
11187 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get type", args, environment);
11188 :
11189 0 : if (!environment->requireDebuggee(cx))
11190 0 : return false;
11191 :
11192 0 : if (environment->type() == DebuggerEnvironmentType::Declarative) {
11193 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NO_ENV_OBJECT);
11194 0 : return false;
11195 : }
11196 :
11197 0 : RootedDebuggerObject result(cx);
11198 0 : if (!environment->getObject(cx, &result))
11199 0 : return false;
11200 :
11201 0 : args.rval().setObject(*result);
11202 0 : return true;
11203 : }
11204 :
11205 : /* static */ bool
11206 0 : DebuggerEnvironment::calleeGetter(JSContext* cx, unsigned argc, Value* vp)
11207 : {
11208 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get callee", args, environment);
11209 :
11210 0 : if (!environment->requireDebuggee(cx))
11211 0 : return false;
11212 :
11213 0 : RootedDebuggerObject result(cx);
11214 0 : if (!environment->getCallee(cx, &result))
11215 0 : return false;
11216 :
11217 0 : args.rval().setObjectOrNull(result);
11218 0 : return true;
11219 : }
11220 :
11221 : /* static */ bool
11222 0 : DebuggerEnvironment::inspectableGetter(JSContext* cx, unsigned argc, Value* vp)
11223 : {
11224 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get inspectable", args, environment);
11225 :
11226 0 : args.rval().setBoolean(environment->isDebuggee());
11227 0 : return true;
11228 : }
11229 :
11230 : /* static */ bool
11231 0 : DebuggerEnvironment::optimizedOutGetter(JSContext* cx, unsigned argc, Value* vp)
11232 : {
11233 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get optimizedOut", args, environment);
11234 :
11235 0 : args.rval().setBoolean(environment->isOptimized());
11236 0 : return true;
11237 : }
11238 :
11239 : /* static */ bool
11240 0 : DebuggerEnvironment::namesMethod(JSContext* cx, unsigned argc, Value* vp)
11241 : {
11242 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "names", args, environment);
11243 :
11244 0 : if (!environment->requireDebuggee(cx))
11245 0 : return false;
11246 :
11247 0 : Rooted<IdVector> ids(cx, IdVector(cx));
11248 0 : if (!DebuggerEnvironment::getNames(cx, environment, &ids))
11249 0 : return false;
11250 :
11251 0 : RootedObject obj(cx, IdVectorToArray(cx, ids));
11252 0 : if (!obj)
11253 0 : return false;
11254 :
11255 0 : args.rval().setObject(*obj);
11256 0 : return true;
11257 : }
11258 :
11259 : /* static */ bool
11260 0 : DebuggerEnvironment::findMethod(JSContext* cx, unsigned argc, Value* vp)
11261 : {
11262 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "find", args, environment);
11263 0 : if (!args.requireAtLeast(cx, "Debugger.Environment.find", 1))
11264 0 : return false;
11265 :
11266 0 : if (!environment->requireDebuggee(cx))
11267 0 : return false;
11268 :
11269 0 : RootedId id(cx);
11270 0 : if (!ValueToIdentifier(cx, args[0], &id))
11271 0 : return false;
11272 :
11273 0 : RootedDebuggerEnvironment result(cx);
11274 0 : if (!DebuggerEnvironment::find(cx, environment, id, &result))
11275 0 : return false;
11276 :
11277 0 : args.rval().setObjectOrNull(result);
11278 0 : return true;
11279 : }
11280 :
11281 : /* static */ bool
11282 0 : DebuggerEnvironment::getVariableMethod(JSContext* cx, unsigned argc, Value* vp)
11283 : {
11284 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "getVariable", args, environment);
11285 0 : if (!args.requireAtLeast(cx, "Debugger.Environment.getVariable", 1))
11286 0 : return false;
11287 :
11288 0 : if (!environment->requireDebuggee(cx))
11289 0 : return false;
11290 :
11291 0 : RootedId id(cx);
11292 0 : if (!ValueToIdentifier(cx, args[0], &id))
11293 0 : return false;
11294 :
11295 0 : return DebuggerEnvironment::getVariable(cx, environment, id, args.rval());
11296 : }
11297 :
11298 : /* static */ bool
11299 0 : DebuggerEnvironment::setVariableMethod(JSContext* cx, unsigned argc, Value* vp)
11300 : {
11301 0 : THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "setVariable", args, environment);
11302 0 : if (!args.requireAtLeast(cx, "Debugger.Environment.setVariable", 2))
11303 0 : return false;
11304 :
11305 0 : if (!environment->requireDebuggee(cx))
11306 0 : return false;
11307 :
11308 0 : RootedId id(cx);
11309 0 : if (!ValueToIdentifier(cx, args[0], &id))
11310 0 : return false;
11311 :
11312 0 : if (!DebuggerEnvironment::setVariable(cx, environment, id, args[1]))
11313 0 : return false;
11314 :
11315 0 : args.rval().setUndefined();
11316 0 : return true;
11317 : }
11318 :
11319 : bool
11320 0 : DebuggerEnvironment::requireDebuggee(JSContext* cx) const
11321 : {
11322 0 : if (!isDebuggee()) {
11323 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_DEBUGGEE,
11324 0 : "Debugger.Environment", "environment");
11325 :
11326 0 : return false;
11327 : }
11328 :
11329 0 : return true;
11330 : }
11331 :
11332 : const JSPropertySpec DebuggerEnvironment::properties_[] = {
11333 : JS_PSG("type", DebuggerEnvironment::typeGetter, 0),
11334 : JS_PSG("parent", DebuggerEnvironment::parentGetter, 0),
11335 : JS_PSG("object", DebuggerEnvironment::objectGetter, 0),
11336 : JS_PSG("callee", DebuggerEnvironment::calleeGetter, 0),
11337 : JS_PSG("inspectable", DebuggerEnvironment::inspectableGetter, 0),
11338 : JS_PSG("optimizedOut", DebuggerEnvironment::optimizedOutGetter, 0),
11339 : JS_PS_END
11340 : };
11341 :
11342 : const JSFunctionSpec DebuggerEnvironment::methods_[] = {
11343 : JS_FN("names", DebuggerEnvironment::namesMethod, 0, 0),
11344 : JS_FN("find", DebuggerEnvironment::findMethod, 1, 0),
11345 : JS_FN("getVariable", DebuggerEnvironment::getVariableMethod, 1, 0),
11346 : JS_FN("setVariable", DebuggerEnvironment::setVariableMethod, 2, 0),
11347 : JS_FS_END
11348 : };
11349 :
11350 : /* static */ NativeObject*
11351 0 : DebuggerEnvironment::initClass(JSContext* cx, HandleObject dbgCtor, HandleObject obj)
11352 : {
11353 0 : Handle<GlobalObject*> global = obj.as<GlobalObject>();
11354 0 : RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
11355 :
11356 0 : return InitClass(cx, dbgCtor, objProto, &DebuggerEnvironment::class_, construct, 0,
11357 0 : properties_, methods_, nullptr, nullptr);
11358 : }
11359 :
11360 : /* static */ DebuggerEnvironment*
11361 0 : DebuggerEnvironment::create(JSContext* cx, HandleObject proto, HandleObject referent,
11362 : HandleNativeObject debugger)
11363 : {
11364 0 : NewObjectKind newKind = IsInsideNursery(referent) ? GenericObject : TenuredObject;
11365 0 : RootedObject obj(cx, NewObjectWithGivenProto(cx, &DebuggerEnvironment::class_, proto, newKind));
11366 0 : if (!obj)
11367 0 : return nullptr;
11368 :
11369 0 : DebuggerEnvironment& environment = obj->as<DebuggerEnvironment>();
11370 0 : environment.setPrivateGCThing(referent);
11371 0 : environment.setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));
11372 :
11373 0 : return &environment;
11374 : }
11375 :
11376 : /* static */ DebuggerEnvironmentType
11377 0 : DebuggerEnvironment::type() const
11378 : {
11379 : /* Don't bother switching compartments just to check env's type. */
11380 0 : if (IsDeclarative(referent()))
11381 0 : return DebuggerEnvironmentType::Declarative;
11382 0 : if (IsDebugEnvironmentWrapper<WithEnvironmentObject>(referent()))
11383 0 : return DebuggerEnvironmentType::With;
11384 0 : return DebuggerEnvironmentType::Object;
11385 : }
11386 :
11387 : bool
11388 0 : DebuggerEnvironment::getParent(JSContext* cx, MutableHandleDebuggerEnvironment result) const
11389 : {
11390 : /* Don't bother switching compartments just to get env's parent. */
11391 0 : Rooted<Env*> parent(cx, referent()->enclosingEnvironment());
11392 0 : if (!parent) {
11393 0 : result.set(nullptr);
11394 0 : return true;
11395 : }
11396 :
11397 0 : return owner()->wrapEnvironment(cx, parent, result);
11398 : }
11399 :
11400 : bool
11401 0 : DebuggerEnvironment::getObject(JSContext* cx, MutableHandleDebuggerObject result) const
11402 : {
11403 0 : MOZ_ASSERT(type() != DebuggerEnvironmentType::Declarative);
11404 :
11405 : /* Don't bother switching compartments just to get env's object. */
11406 0 : RootedObject object(cx);
11407 0 : if (IsDebugEnvironmentWrapper<WithEnvironmentObject>(referent())) {
11408 0 : object.set(&referent()->as<DebugEnvironmentProxy>()
11409 0 : .environment().as<WithEnvironmentObject>().object());
11410 0 : } else if (IsDebugEnvironmentWrapper<NonSyntacticVariablesObject>(referent())) {
11411 0 : object.set(&referent()->as<DebugEnvironmentProxy>()
11412 0 : .environment().as<NonSyntacticVariablesObject>());
11413 : } else {
11414 0 : object.set(referent());
11415 0 : MOZ_ASSERT(!object->is<DebugEnvironmentProxy>());
11416 : }
11417 :
11418 0 : return owner()->wrapDebuggeeObject(cx, object, result);
11419 : }
11420 :
11421 : bool
11422 0 : DebuggerEnvironment::getCallee(JSContext* cx, MutableHandleDebuggerObject result) const
11423 : {
11424 0 : if (!referent()->is<DebugEnvironmentProxy>()) {
11425 0 : result.set(nullptr);
11426 0 : return true;
11427 : }
11428 :
11429 0 : JSObject& scope = referent()->as<DebugEnvironmentProxy>().environment();
11430 0 : if (!scope.is<CallObject>()) {
11431 0 : result.set(nullptr);
11432 0 : return true;
11433 : }
11434 :
11435 0 : RootedObject callee(cx, &scope.as<CallObject>().callee());
11436 0 : if (IsInternalFunctionObject(*callee)) {
11437 0 : result.set(nullptr);
11438 0 : return true;
11439 : }
11440 :
11441 0 : return owner()->wrapDebuggeeObject(cx, callee, result);
11442 : }
11443 :
11444 : bool
11445 0 : DebuggerEnvironment::isDebuggee() const
11446 : {
11447 0 : MOZ_ASSERT(referent());
11448 0 : MOZ_ASSERT(!referent()->is<EnvironmentObject>());
11449 :
11450 0 : return owner()->observesGlobal(&referent()->global());
11451 : }
11452 :
11453 : bool
11454 0 : DebuggerEnvironment::isOptimized() const
11455 : {
11456 0 : return referent()->is<DebugEnvironmentProxy>() &&
11457 0 : referent()->as<DebugEnvironmentProxy>().isOptimizedOut();
11458 : }
11459 :
11460 : /* static */ bool
11461 0 : DebuggerEnvironment::getNames(JSContext* cx, HandleDebuggerEnvironment environment,
11462 : MutableHandle<IdVector> result)
11463 : {
11464 0 : MOZ_ASSERT(environment->isDebuggee());
11465 :
11466 0 : Rooted<Env*> referent(cx, environment->referent());
11467 :
11468 0 : AutoIdVector ids(cx);
11469 : {
11470 0 : Maybe<AutoCompartment> ac;
11471 0 : ac.emplace(cx, referent);
11472 :
11473 0 : ErrorCopier ec(ac);
11474 0 : if (!GetPropertyKeys(cx, referent, JSITER_HIDDEN, &ids))
11475 0 : return false;
11476 : }
11477 :
11478 0 : for (size_t i = 0; i < ids.length(); ++i) {
11479 0 : jsid id = ids[i];
11480 0 : if (JSID_IS_ATOM(id) && IsIdentifier(JSID_TO_ATOM(id))) {
11481 0 : cx->markId(id);
11482 0 : if (!result.append(id))
11483 0 : return false;
11484 : }
11485 : }
11486 :
11487 0 : return true;
11488 : }
11489 :
11490 : /* static */ bool
11491 0 : DebuggerEnvironment::find(JSContext* cx, HandleDebuggerEnvironment environment, HandleId id,
11492 : MutableHandleDebuggerEnvironment result)
11493 : {
11494 0 : MOZ_ASSERT(environment->isDebuggee());
11495 :
11496 0 : Rooted<Env*> env(cx, environment->referent());
11497 0 : Debugger* dbg = environment->owner();
11498 :
11499 : {
11500 0 : Maybe<AutoCompartment> ac;
11501 0 : ac.emplace(cx, env);
11502 :
11503 0 : cx->markId(id);
11504 :
11505 : /* This can trigger resolve hooks. */
11506 0 : ErrorCopier ec(ac);
11507 0 : for (; env; env = env->enclosingEnvironment()) {
11508 : bool found;
11509 0 : if (!HasProperty(cx, env, id, &found))
11510 0 : return false;
11511 0 : if (found)
11512 0 : break;
11513 : }
11514 : }
11515 :
11516 0 : if (!env) {
11517 0 : result.set(nullptr);
11518 0 : return true;
11519 : }
11520 :
11521 0 : return dbg->wrapEnvironment(cx, env, result);
11522 : }
11523 :
11524 : /* static */ bool
11525 0 : DebuggerEnvironment::getVariable(JSContext* cx, HandleDebuggerEnvironment environment,
11526 : HandleId id, MutableHandleValue result)
11527 : {
11528 0 : MOZ_ASSERT(environment->isDebuggee());
11529 :
11530 0 : Rooted<Env*> referent(cx, environment->referent());
11531 0 : Debugger* dbg = environment->owner();
11532 :
11533 : {
11534 0 : Maybe<AutoCompartment> ac;
11535 0 : ac.emplace(cx, referent);
11536 :
11537 0 : cx->markId(id);
11538 :
11539 : /* This can trigger getters. */
11540 0 : ErrorCopier ec(ac);
11541 :
11542 : bool found;
11543 0 : if (!HasProperty(cx, referent, id, &found))
11544 0 : return false;
11545 0 : if (!found) {
11546 0 : result.setUndefined();
11547 0 : return true;
11548 : }
11549 :
11550 : // For DebugEnvironmentProxys, we get sentinel values for optimized out
11551 : // slots and arguments instead of throwing (the default behavior).
11552 : //
11553 : // See wrapDebuggeeValue for how the sentinel values are wrapped.
11554 0 : if (referent->is<DebugEnvironmentProxy>()) {
11555 0 : Rooted<DebugEnvironmentProxy*> env(cx, &referent->as<DebugEnvironmentProxy>());
11556 0 : if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, env, id, result))
11557 0 : return false;
11558 : } else {
11559 0 : if (!GetProperty(cx, referent, referent, id, result))
11560 0 : return false;
11561 : }
11562 : }
11563 :
11564 : // When we've faked up scope chain objects for optimized-out scopes,
11565 : // declarative environments may contain internal JSFunction objects, which
11566 : // we shouldn't expose to the user.
11567 0 : if (result.isObject()) {
11568 0 : RootedObject obj(cx, &result.toObject());
11569 0 : if (obj->is<JSFunction>() &&
11570 0 : IsInternalFunctionObject(obj->as<JSFunction>()))
11571 0 : result.setMagic(JS_OPTIMIZED_OUT);
11572 : }
11573 :
11574 0 : return dbg->wrapDebuggeeValue(cx, result);
11575 : }
11576 :
11577 : /* static */ bool
11578 0 : DebuggerEnvironment::setVariable(JSContext* cx, HandleDebuggerEnvironment environment,
11579 : HandleId id, HandleValue value_)
11580 : {
11581 0 : MOZ_ASSERT(environment->isDebuggee());
11582 :
11583 0 : Rooted<Env*> referent(cx, environment->referent());
11584 0 : Debugger* dbg = environment->owner();
11585 :
11586 0 : RootedValue value(cx, value_);
11587 0 : if (!dbg->unwrapDebuggeeValue(cx, &value))
11588 0 : return false;
11589 :
11590 : {
11591 0 : Maybe<AutoCompartment> ac;
11592 0 : ac.emplace(cx, referent);
11593 0 : if (!cx->compartment()->wrap(cx, &value))
11594 0 : return false;
11595 0 : cx->markId(id);
11596 :
11597 : /* This can trigger setters. */
11598 0 : ErrorCopier ec(ac);
11599 :
11600 : /* Make sure the environment actually has the specified binding. */
11601 : bool found;
11602 0 : if (!HasProperty(cx, referent, id, &found))
11603 0 : return false;
11604 0 : if (!found) {
11605 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_VARIABLE_NOT_FOUND);
11606 0 : return false;
11607 : }
11608 :
11609 : /* Just set the property. */
11610 0 : if (!SetProperty(cx, referent, id, value))
11611 0 : return false;
11612 : }
11613 :
11614 0 : return true;
11615 : }
11616 :
11617 :
11618 : /*** JS::dbg::Builder ****************************************************************************/
11619 :
11620 0 : Builder::Builder(JSContext* cx, js::Debugger* debugger)
11621 0 : : debuggerObject(cx, debugger->toJSObject().get()),
11622 0 : debugger(debugger)
11623 0 : { }
11624 :
11625 :
11626 : #if DEBUG
11627 : void
11628 0 : Builder::assertBuilt(JSObject* obj)
11629 : {
11630 : // We can't use assertSameCompartment here, because that is always keyed to
11631 : // some JSContext's current compartment, whereas BuiltThings can be
11632 : // constructed and assigned to without respect to any particular context;
11633 : // the only constraint is that they should be in their debugger's compartment.
11634 0 : MOZ_ASSERT_IF(obj, debuggerObject->compartment() == obj->compartment());
11635 0 : }
11636 : #endif
11637 :
11638 : bool
11639 0 : Builder::Object::definePropertyToTrusted(JSContext* cx, const char* name,
11640 : JS::MutableHandleValue trusted)
11641 : {
11642 : // We should have checked for false Objects before calling this.
11643 0 : MOZ_ASSERT(value);
11644 :
11645 0 : JSAtom* atom = Atomize(cx, name, strlen(name));
11646 0 : if (!atom)
11647 0 : return false;
11648 0 : RootedId id(cx, AtomToId(atom));
11649 :
11650 0 : return DefineProperty(cx, value, id, trusted);
11651 : }
11652 :
11653 : bool
11654 0 : Builder::Object::defineProperty(JSContext* cx, const char* name, JS::HandleValue propval_)
11655 : {
11656 0 : AutoCompartment ac(cx, debuggerObject());
11657 :
11658 0 : RootedValue propval(cx, propval_);
11659 0 : if (!debugger()->wrapDebuggeeValue(cx, &propval))
11660 0 : return false;
11661 :
11662 0 : return definePropertyToTrusted(cx, name, &propval);
11663 : }
11664 :
11665 : bool
11666 0 : Builder::Object::defineProperty(JSContext* cx, const char* name, JS::HandleObject propval_)
11667 : {
11668 0 : RootedValue propval(cx, ObjectOrNullValue(propval_));
11669 0 : return defineProperty(cx, name, propval);
11670 : }
11671 :
11672 : bool
11673 0 : Builder::Object::defineProperty(JSContext* cx, const char* name, Builder::Object& propval_)
11674 : {
11675 0 : AutoCompartment ac(cx, debuggerObject());
11676 :
11677 0 : RootedValue propval(cx, ObjectOrNullValue(propval_.value));
11678 0 : return definePropertyToTrusted(cx, name, &propval);
11679 : }
11680 :
11681 : Builder::Object
11682 0 : Builder::newObject(JSContext* cx)
11683 : {
11684 0 : AutoCompartment ac(cx, debuggerObject);
11685 :
11686 0 : RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
11687 :
11688 : // If the allocation failed, this will return a false Object, as the spec promises.
11689 0 : return Object(cx, *this, obj);
11690 : }
11691 :
11692 :
11693 : /*** JS::dbg::AutoEntryMonitor ******************************************************************/
11694 :
11695 0 : AutoEntryMonitor::AutoEntryMonitor(JSContext* cx)
11696 : : cx_(cx),
11697 0 : savedMonitor_(cx->entryMonitor)
11698 : {
11699 0 : cx->entryMonitor = this;
11700 0 : }
11701 :
11702 0 : AutoEntryMonitor::~AutoEntryMonitor()
11703 : {
11704 0 : cx_->entryMonitor = savedMonitor_;
11705 0 : }
11706 :
11707 :
11708 : /*** Glue ****************************************************************************************/
11709 :
11710 : extern JS_PUBLIC_API(bool)
11711 0 : JS_DefineDebuggerObject(JSContext* cx, HandleObject obj)
11712 : {
11713 : RootedNativeObject
11714 0 : objProto(cx),
11715 0 : debugCtor(cx),
11716 0 : debugProto(cx),
11717 0 : frameProto(cx),
11718 0 : scriptProto(cx),
11719 0 : sourceProto(cx),
11720 0 : objectProto(cx),
11721 0 : envProto(cx),
11722 0 : memoryProto(cx);
11723 0 : RootedObject debuggeeWouldRunProto(cx);
11724 0 : RootedValue debuggeeWouldRunCtor(cx);
11725 0 : Handle<GlobalObject*> global = obj.as<GlobalObject>();
11726 :
11727 0 : objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
11728 0 : if (!objProto)
11729 0 : return false;
11730 0 : debugProto = InitClass(cx, obj,
11731 : objProto, &Debugger::class_, Debugger::construct,
11732 : 1, Debugger::properties, Debugger::methods, nullptr,
11733 0 : Debugger::static_methods, debugCtor.address());
11734 0 : if (!debugProto)
11735 0 : return false;
11736 :
11737 0 : frameProto = DebuggerFrame::initClass(cx, debugCtor, obj);
11738 0 : if (!frameProto)
11739 0 : return false;
11740 :
11741 0 : scriptProto = InitClass(cx, debugCtor, objProto, &DebuggerScript_class,
11742 : DebuggerScript_construct, 0,
11743 : DebuggerScript_properties, DebuggerScript_methods,
11744 0 : nullptr, nullptr);
11745 0 : if (!scriptProto)
11746 0 : return false;
11747 :
11748 0 : sourceProto = InitClass(cx, debugCtor, sourceProto, &DebuggerSource_class,
11749 : DebuggerSource_construct, 0,
11750 : DebuggerSource_properties, DebuggerSource_methods,
11751 0 : nullptr, nullptr);
11752 0 : if (!sourceProto)
11753 0 : return false;
11754 :
11755 0 : objectProto = DebuggerObject::initClass(cx, obj, debugCtor);
11756 0 : if (!objectProto)
11757 0 : return false;
11758 :
11759 0 : envProto = DebuggerEnvironment::initClass(cx, debugCtor, obj);
11760 0 : if (!envProto)
11761 0 : return false;
11762 :
11763 0 : memoryProto = InitClass(cx, debugCtor, objProto, &DebuggerMemory::class_,
11764 : DebuggerMemory::construct, 0, DebuggerMemory::properties,
11765 0 : DebuggerMemory::methods, nullptr, nullptr);
11766 0 : if (!memoryProto)
11767 0 : return false;
11768 :
11769 : debuggeeWouldRunProto =
11770 0 : GlobalObject::getOrCreateCustomErrorPrototype(cx, global, JSEXN_DEBUGGEEWOULDRUN);
11771 0 : if (!debuggeeWouldRunProto)
11772 0 : return false;
11773 0 : debuggeeWouldRunCtor = global->getConstructor(JSProto_DebuggeeWouldRun);
11774 0 : RootedId debuggeeWouldRunId(cx, NameToId(ClassName(JSProto_DebuggeeWouldRun, cx)));
11775 0 : if (!DefineProperty(cx, debugCtor, debuggeeWouldRunId, debuggeeWouldRunCtor,
11776 : nullptr, nullptr, 0))
11777 : {
11778 0 : return false;
11779 : }
11780 :
11781 0 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_FRAME_PROTO, ObjectValue(*frameProto));
11782 0 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_OBJECT_PROTO, ObjectValue(*objectProto));
11783 0 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SCRIPT_PROTO, ObjectValue(*scriptProto));
11784 0 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SOURCE_PROTO, ObjectValue(*sourceProto));
11785 0 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_ENV_PROTO, ObjectValue(*envProto));
11786 0 : debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_MEMORY_PROTO, ObjectValue(*memoryProto));
11787 0 : return true;
11788 : }
11789 :
11790 : static inline void
11791 736 : AssertIsPromise(JSContext* cx, HandleObject promise)
11792 : {
11793 736 : MOZ_ASSERT(promise);
11794 736 : assertSameCompartment(cx, promise);
11795 736 : MOZ_ASSERT(strcmp(promise->getClass()->name, "Promise") == 0);
11796 736 : }
11797 :
11798 : JS_PUBLIC_API(void)
11799 505 : JS::dbg::onNewPromise(JSContext* cx, HandleObject promise_)
11800 : {
11801 1010 : RootedObject promise(cx, promise_);
11802 505 : if (IsWrapper(promise))
11803 0 : promise = UncheckedUnwrap(promise);
11804 1010 : AutoCompartment ac(cx, promise);
11805 505 : AssertIsPromise(cx, promise);
11806 505 : Debugger::slowPathPromiseHook(cx, Debugger::OnNewPromise, promise);
11807 505 : }
11808 :
11809 : JS_PUBLIC_API(void)
11810 231 : JS::dbg::onPromiseSettled(JSContext* cx, HandleObject promise)
11811 : {
11812 231 : AssertIsPromise(cx, promise);
11813 231 : Debugger::slowPathPromiseHook(cx, Debugger::OnPromiseSettled, promise);
11814 231 : }
11815 :
11816 : JS_PUBLIC_API(bool)
11817 0 : JS::dbg::IsDebugger(JSObject& obj)
11818 : {
11819 0 : JSObject* unwrapped = CheckedUnwrap(&obj);
11820 0 : return unwrapped &&
11821 0 : js::GetObjectClass(unwrapped) == &Debugger::class_ &&
11822 0 : js::Debugger::fromJSObject(unwrapped) != nullptr;
11823 : }
11824 :
11825 : JS_PUBLIC_API(bool)
11826 0 : JS::dbg::GetDebuggeeGlobals(JSContext* cx, JSObject& dbgObj, AutoObjectVector& vector)
11827 : {
11828 0 : MOZ_ASSERT(IsDebugger(dbgObj));
11829 0 : js::Debugger* dbg = js::Debugger::fromJSObject(CheckedUnwrap(&dbgObj));
11830 :
11831 0 : if (!vector.reserve(vector.length() + dbg->debuggees.count())) {
11832 0 : JS_ReportOutOfMemory(cx);
11833 0 : return false;
11834 : }
11835 :
11836 0 : for (WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront())
11837 0 : vector.infallibleAppend(static_cast<JSObject*>(r.front()));
11838 :
11839 0 : return true;
11840 : }
11841 :
11842 : #ifdef DEBUG
11843 : /* static */ bool
11844 0 : Debugger::isDebuggerCrossCompartmentEdge(JSObject* obj, const gc::Cell* target)
11845 : {
11846 0 : MOZ_ASSERT(target);
11847 :
11848 0 : auto cls = obj->getClass();
11849 0 : const gc::Cell* referent = nullptr;
11850 0 : if (cls == &DebuggerScript_class) {
11851 0 : referent = GetScriptReferentCell(obj);
11852 0 : } else if (cls == &DebuggerSource_class) {
11853 0 : referent = GetSourceReferentRawObject(obj);
11854 0 : } else if (obj->is<DebuggerObject>()) {
11855 0 : referent = static_cast<gc::Cell*>(obj->as<DebuggerObject>().getPrivate());
11856 0 : } else if (obj->is<DebuggerEnvironment>()) {
11857 0 : referent = static_cast<gc::Cell*>(obj->as<DebuggerEnvironment>().getPrivate());
11858 : }
11859 :
11860 0 : return referent == target;
11861 : }
11862 : #endif
11863 :
11864 :
11865 : /*** JS::dbg::GarbageCollectionEvent **************************************************************/
11866 :
11867 : namespace JS {
11868 : namespace dbg {
11869 :
11870 : /* static */ GarbageCollectionEvent::Ptr
11871 0 : GarbageCollectionEvent::Create(JSRuntime* rt, ::js::gcstats::Statistics& stats, uint64_t gcNumber)
11872 : {
11873 0 : auto data = rt->make_unique<GarbageCollectionEvent>(gcNumber);
11874 0 : if (!data)
11875 0 : return nullptr;
11876 :
11877 0 : data->nonincrementalReason = stats.nonincrementalReason();
11878 :
11879 0 : for (auto& slice : stats.slices()) {
11880 0 : if (!data->reason) {
11881 : // There is only one GC reason for the whole cycle, but for legacy
11882 : // reasons this data is stored and replicated on each slice. Each
11883 : // slice used to have its own GCReason, but now they are all the
11884 : // same.
11885 0 : data->reason = gcreason::ExplainReason(slice.reason);
11886 0 : MOZ_ASSERT(data->reason);
11887 : }
11888 :
11889 0 : if (!data->collections.growBy(1))
11890 0 : return nullptr;
11891 :
11892 0 : data->collections.back().startTimestamp = slice.start;
11893 0 : data->collections.back().endTimestamp = slice.end;
11894 : }
11895 :
11896 0 : return data;
11897 : }
11898 :
11899 : static bool
11900 0 : DefineStringProperty(JSContext* cx, HandleObject obj, PropertyName* propName, const char* strVal)
11901 : {
11902 0 : RootedValue val(cx, UndefinedValue());
11903 0 : if (strVal) {
11904 0 : JSAtom* atomized = Atomize(cx, strVal, strlen(strVal));
11905 0 : if (!atomized)
11906 0 : return false;
11907 0 : val = StringValue(atomized);
11908 : }
11909 0 : return DefineProperty(cx, obj, propName, val);
11910 : }
11911 :
11912 : JSObject*
11913 0 : GarbageCollectionEvent::toJSObject(JSContext* cx) const
11914 : {
11915 0 : RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
11916 0 : RootedValue gcCycleNumberVal(cx, NumberValue(majorGCNumber_));
11917 0 : if (!obj ||
11918 0 : !DefineStringProperty(cx, obj, cx->names().nonincrementalReason, nonincrementalReason) ||
11919 0 : !DefineStringProperty(cx, obj, cx->names().reason, reason) ||
11920 0 : !DefineProperty(cx, obj, cx->names().gcCycleNumber, gcCycleNumberVal))
11921 : {
11922 0 : return nullptr;
11923 : }
11924 :
11925 0 : RootedArrayObject slicesArray(cx, NewDenseEmptyArray(cx));
11926 0 : if (!slicesArray)
11927 0 : return nullptr;
11928 :
11929 0 : TimeStamp originTime = TimeStamp::ProcessCreation();
11930 :
11931 0 : size_t idx = 0;
11932 0 : for (auto range = collections.all(); !range.empty(); range.popFront()) {
11933 0 : RootedPlainObject collectionObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
11934 0 : if (!collectionObj)
11935 0 : return nullptr;
11936 :
11937 0 : RootedValue start(cx), end(cx);
11938 0 : start = NumberValue((range.front().startTimestamp - originTime).ToMilliseconds());
11939 0 : end = NumberValue((range.front().endTimestamp - originTime).ToMilliseconds());
11940 0 : if (!DefineProperty(cx, collectionObj, cx->names().startTimestamp, start) ||
11941 0 : !DefineProperty(cx, collectionObj, cx->names().endTimestamp, end))
11942 : {
11943 0 : return nullptr;
11944 : }
11945 :
11946 0 : RootedValue collectionVal(cx, ObjectValue(*collectionObj));
11947 0 : if (!DefineElement(cx, slicesArray, idx++, collectionVal))
11948 0 : return nullptr;
11949 : }
11950 :
11951 0 : RootedValue slicesValue(cx, ObjectValue(*slicesArray));
11952 0 : if (!DefineProperty(cx, obj, cx->names().collections, slicesValue))
11953 0 : return nullptr;
11954 :
11955 0 : return obj;
11956 : }
11957 :
11958 : JS_PUBLIC_API(bool)
11959 0 : FireOnGarbageCollectionHookRequired(JSContext* cx)
11960 : {
11961 0 : AutoCheckCannotGC noGC;
11962 :
11963 0 : for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) {
11964 0 : for (Debugger* dbg : group->debuggerList()) {
11965 0 : if (dbg->enabled &&
11966 0 : dbg->observedGC(cx->runtime()->gc.majorGCCount()) &&
11967 0 : dbg->getHook(Debugger::OnGarbageCollection))
11968 : {
11969 0 : return true;
11970 : }
11971 : }
11972 : }
11973 :
11974 0 : return false;
11975 : }
11976 :
11977 : JS_PUBLIC_API(bool)
11978 0 : FireOnGarbageCollectionHook(JSContext* cx, JS::dbg::GarbageCollectionEvent::Ptr&& data)
11979 : {
11980 0 : AutoObjectVector triggered(cx);
11981 :
11982 : {
11983 : // We had better not GC (and potentially get a dangling Debugger
11984 : // pointer) while finding all Debuggers observing a debuggee that
11985 : // participated in this GC.
11986 0 : AutoCheckCannotGC noGC;
11987 :
11988 0 : for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) {
11989 0 : for (Debugger* dbg : group->debuggerList()) {
11990 0 : if (dbg->enabled &&
11991 0 : dbg->observedGC(data->majorGCNumber()) &&
11992 0 : dbg->getHook(Debugger::OnGarbageCollection))
11993 : {
11994 0 : if (!triggered.append(dbg->object)) {
11995 0 : JS_ReportOutOfMemory(cx);
11996 0 : return false;
11997 : }
11998 : }
11999 : }
12000 : }
12001 : }
12002 :
12003 0 : for ( ; !triggered.empty(); triggered.popBack()) {
12004 0 : Debugger* dbg = Debugger::fromJSObject(triggered.back());
12005 0 : dbg->fireOnGarbageCollectionHook(cx, data);
12006 0 : MOZ_ASSERT(!cx->isExceptionPending());
12007 : }
12008 :
12009 0 : return true;
12010 : }
12011 :
12012 : } // namespace dbg
12013 : } // namespace JS
|