tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Debugger.cpp (252971B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "debugger/Debugger-inl.h"
      8 
      9 #include "mozilla/Attributes.h"        // for MOZ_STACK_CLASS, MOZ_RAII
     10 #include "mozilla/DebugOnly.h"         // for DebugOnly
     11 #include "mozilla/DoublyLinkedList.h"  // for DoublyLinkedList<>::Iterator
     12 #include "mozilla/HashTable.h"         // for HashSet<>::Range, HashMapEntry
     13 #include "mozilla/Maybe.h"             // for Maybe, Nothing, Some
     14 #include "mozilla/ScopeExit.h"         // for MakeScopeExit, ScopeExit
     15 #include "mozilla/Sprintf.h"           // for SprintfLiteral
     16 #include "mozilla/TimeStamp.h"         // for TimeStamp
     17 #include "mozilla/UniquePtr.h"         // for UniquePtr
     18 #include "mozilla/Variant.h"           // for AsVariant, AsVariantTemporary
     19 #include "mozilla/Vector.h"            // for Vector, Vector<>::ConstRange
     20 
     21 #include <algorithm>    // for std::find, std::max
     22 #include <functional>   // for function
     23 #include <stddef.h>     // for size_t
     24 #include <stdint.h>     // for uint32_t, uint64_t, int32_t
     25 #include <string.h>     // for strlen, strcmp
     26 #include <type_traits>  // for std::underlying_type_t
     27 #include <utility>      // for std::move
     28 
     29 #include "jsapi.h"    // for CallArgs, CallArgsFromVp
     30 #include "jstypes.h"  // for JS_PUBLIC_API
     31 
     32 #include "builtin/Array.h"            // for NewDenseFullyAllocatedArray
     33 #include "debugger/DebugAPI.h"        // for ResumeMode, DebugAPI
     34 #include "debugger/DebuggerMemory.h"  // for DebuggerMemory
     35 #include "debugger/DebugScript.h"     // for DebugScript
     36 #include "debugger/Environment.h"     // for DebuggerEnvironment
     37 #ifdef MOZ_EXECUTION_TRACING
     38 #  include "debugger/ExecutionTracer.h"  // for ExecutionTracer::onEnterFrame, ExecutionTracer::onLeaveFrame
     39 #endif
     40 #include "debugger/Frame.h"               // for DebuggerFrame
     41 #include "debugger/NoExecute.h"           // for EnterDebuggeeNoExecute
     42 #include "debugger/Object.h"              // for DebuggerObject
     43 #include "debugger/Script.h"              // for DebuggerScript
     44 #include "debugger/Source.h"              // for DebuggerSource
     45 #include "frontend/CompilationStencil.h"  // for CompilationStencil
     46 #include "frontend/FrontendContext.h"     // for AutoReportFrontendContext
     47 #include "frontend/Parser.h"              // for Parser
     48 #include "gc/GC.h"                        // for IterateScripts
     49 #include "gc/GCContext.h"                 // for JS::GCContext
     50 #include "gc/GCMarker.h"                  // for GCMarker
     51 #include "gc/GCRuntime.h"                 // for GCRuntime, AutoEnterIteration
     52 #include "gc/HashUtil.h"                  // for DependentAddPtr
     53 #include "gc/Marking.h"                   // for IsAboutToBeFinalized
     54 #include "gc/PublicIterators.h"           // for RealmsIter, CompartmentsIter
     55 #include "gc/Statistics.h"                // for Statistics::SliceData
     56 #include "gc/Tracer.h"                    // for TraceEdge
     57 #include "gc/Zone.h"                      // for Zone
     58 #include "gc/ZoneAllocator.h"             // for ZoneAllocPolicy
     59 #include "jit/BaselineDebugModeOSR.h"  // for RecompileOnStackBaselineScriptsForDebugMode
     60 #include "jit/BaselineJIT.h"           // for FinishDiscardBaselineScript
     61 #include "jit/Invalidation.h"         // for IonScriptKeyVector
     62 #include "jit/JitContext.h"           // for JitContext
     63 #include "jit/JitOptions.h"           // for fuzzingSafe
     64 #include "jit/JitScript.h"            // for JitScript
     65 #include "jit/JSJitFrameIter.h"       // for InlineFrameIterator
     66 #include "jit/RematerializedFrame.h"  // for RematerializedFrame
     67 #include "js/CallAndConstruct.h"      // JS::IsCallable
     68 #include "js/Conversions.h"           // for ToBoolean, ToUint32
     69 #include "js/Debug.h"                 // for Builder::Object, Builder
     70 #include "js/friend/ErrorMessages.h"  // for GetErrorMessage, JSMSG_*
     71 #include "js/GCAPI.h"                 // for GarbageCollectionEvent
     72 #include "js/GCVariant.h"             // for GCVariant
     73 #include "js/HeapAPI.h"               // for ExposeObjectToActiveJS
     74 #include "js/Promise.h"               // for AutoDebuggerJobQueueInterruption
     75 #include "js/PropertyAndElement.h"    // for JS_GetProperty
     76 #include "js/Proxy.h"                 // for PropertyDescriptor
     77 #include "js/SourceText.h"            // for SourceText
     78 #include "js/StableStringChars.h"     // for AutoStableStringChars
     79 #include "js/UbiNode.h"               // for Node, RootList, Edge
     80 #include "js/UbiNodeBreadthFirst.h"   // for BreadthFirst
     81 #include "js/Wrapper.h"               // for CheckedUnwrapStatic
     82 #include "util/Identifier.h"          // for IsIdentifier
     83 #include "util/Text.h"                // for DuplicateString, js_strlen
     84 #include "vm/ArrayObject.h"           // for ArrayObject
     85 #include "vm/AsyncFunction.h"         // for AsyncFunctionGeneratorObject
     86 #include "vm/AsyncIteration.h"        // for AsyncGeneratorObject
     87 #include "vm/BytecodeUtil.h"          // for JSDVG_IGNORE_STACK
     88 #include "vm/Compartment.h"           // for CrossCompartmentKey
     89 #include "vm/EnvironmentObject.h"     // for IsSyntacticEnvironment
     90 #include "vm/ErrorReporting.h"        // for ReportErrorToGlobal
     91 #include "vm/GeneratorObject.h"       // for AbstractGeneratorObject
     92 #include "vm/GlobalObject.h"          // for GlobalObject
     93 #include "vm/Interpreter.h"           // for Call, ReportIsNotFunction
     94 #include "vm/Iteration.h"             // for CreateIterResultObject
     95 #include "vm/JSAtomUtils.h"  // for Atomize, AtomizeUTF8Chars, AtomIsMarked, AtomToId, ClassName
     96 #include "vm/JSContext.h"         // for JSContext
     97 #include "vm/JSFunction.h"        // for JSFunction
     98 #include "vm/JSObject.h"          // for JSObject, RequireObject,
     99 #include "vm/JSScript.h"          // for BaseScript, ScriptSourceObject
    100 #include "vm/ObjectOperations.h"  // for DefineDataProperty
    101 #include "vm/PlainObject.h"       // for js::PlainObject
    102 #include "vm/PromiseObject.h"     // for js::PromiseObject
    103 #include "vm/ProxyObject.h"       // for ProxyObject, JSObject::is
    104 #include "vm/Realm.h"             // for AutoRealm, Realm
    105 #include "vm/Runtime.h"           // for ReportOutOfMemory, JSRuntime
    106 #include "vm/SavedFrame.h"        // for SavedFrame
    107 #include "vm/SavedStacks.h"       // for SavedStacks
    108 #include "vm/Scope.h"             // for Scope
    109 #include "vm/StringType.h"        // for JSString, PropertyName
    110 #include "vm/WrapperObject.h"     // for CrossCompartmentWrapperObject
    111 #include "wasm/WasmDebug.h"       // for DebugState
    112 #include "wasm/WasmInstance.h"    // for Instance
    113 #include "wasm/WasmJS.h"          // for WasmInstanceObject
    114 #include "wasm/WasmRealm.h"       // for Realm
    115 #include "wasm/WasmTypeDecls.h"   // for WasmInstanceObjectVector
    116 
    117 #include "debugger/DebugAPI-inl.h"
    118 #include "debugger/Environment-inl.h"  // for DebuggerEnvironment::owner
    119 #include "debugger/Frame-inl.h"        // for DebuggerFrame::hasGeneratorInfo
    120 #include "debugger/Object-inl.h"  // for DebuggerObject::owner and isInstance.
    121 #include "debugger/Script-inl.h"  // for DebuggerScript::getReferent
    122 #include "gc/GC-inl.h"            // for ZoneCellIter
    123 #include "gc/Marking-inl.h"       // for MaybeForwarded
    124 #include "gc/StableCellHasher-inl.h"
    125 #include "gc/WeakMap-inl.h"        // for DebuggerWeakMap::trace
    126 #include "vm/Compartment-inl.h"    // for Compartment::wrap
    127 #include "vm/GeckoProfiler-inl.h"  // for AutoSuppressProfilerSampling
    128 #include "vm/JSAtomUtils-inl.h"    // for AtomToId, ValueToId
    129 #include "vm/JSContext-inl.h"      // for JSContext::check
    130 #include "vm/JSObject-inl.h"  // for JSObject::isCallable, NewTenuredObjectWithGivenProto
    131 #include "vm/JSScript-inl.h"      // for JSScript::isDebuggee, JSScript
    132 #include "vm/NativeObject-inl.h"  // for NativeObject::ensureDenseInitializedLength
    133 #include "vm/ObjectOperations-inl.h"  // for GetProperty, HasProperty
    134 #include "vm/Realm-inl.h"             // for AutoRealm::AutoRealm
    135 #include "vm/Stack-inl.h"             // for AbstractFramePtr::script
    136 
    137 namespace js {
    138 
    139 namespace frontend {
    140 class FullParseHandler;
    141 }
    142 
    143 namespace gc {
    144 struct Cell;
    145 }
    146 
    147 namespace jit {
    148 class BaselineFrame;
    149 }
    150 
    151 } /* namespace js */
    152 
    153 using namespace js;
    154 
    155 using JS::AutoStableStringChars;
    156 using JS::CompileOptions;
    157 using JS::dbg::Builder;
    158 using mozilla::AsVariant;
    159 using mozilla::DebugOnly;
    160 using mozilla::MakeScopeExit;
    161 using mozilla::Maybe;
    162 using mozilla::Nothing;
    163 using mozilla::Some;
    164 using mozilla::TimeStamp;
    165 
    166 /*** Utils ******************************************************************/
    167 
    168 bool js::IsInterpretedNonSelfHostedFunction(JSFunction* fun) {
    169  return fun->isInterpreted() && !fun->isSelfHostedBuiltin();
    170 }
    171 
    172 JSScript* js::GetOrCreateFunctionScript(JSContext* cx, HandleFunction fun) {
    173  MOZ_ASSERT(IsInterpretedNonSelfHostedFunction(fun));
    174  AutoRealm ar(cx, fun);
    175  return JSFunction::getOrCreateScript(cx, fun);
    176 }
    177 
    178 ArrayObject* js::GetFunctionParameterNamesArray(JSContext* cx,
    179                                                HandleFunction fun) {
    180  RootedValueVector names(cx);
    181 
    182  // The default value for each argument is |undefined|.
    183  if (!names.growBy(fun->nargs())) {
    184    return nullptr;
    185  }
    186 
    187  if (IsInterpretedNonSelfHostedFunction(fun) && fun->nargs() > 0) {
    188    RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
    189    if (!script) {
    190      return nullptr;
    191    }
    192 
    193    MOZ_ASSERT(fun->nargs() == script->numArgs());
    194 
    195    PositionalFormalParameterIter fi(script);
    196    for (size_t i = 0; i < fun->nargs(); i++, fi++) {
    197      MOZ_ASSERT(fi.argumentSlot() == i);
    198      if (JSAtom* atom = fi.name()) {
    199        // Skip any internal, non-identifier names, like for example ".args".
    200        if (IsIdentifier(atom)) {
    201          cx->markAtom(atom);
    202          names[i].setString(atom);
    203        }
    204      }
    205    }
    206  }
    207 
    208  return NewDenseCopiedArray(cx, names.length(), names.begin());
    209 }
    210 
    211 bool js::ValueToIdentifier(JSContext* cx, HandleValue v, MutableHandleId id) {
    212  if (!ToPropertyKey(cx, v, id)) {
    213    return false;
    214  }
    215  if (!id.isAtom() || !IsIdentifier(id.toAtom())) {
    216    RootedValue val(cx, v);
    217    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, val,
    218                     nullptr, "not an identifier");
    219    return false;
    220  }
    221  return true;
    222 }
    223 
    224 class js::AutoRestoreRealmDebugMode {
    225  Realm* realm_;
    226  uint32_t bits_;
    227 
    228 public:
    229  explicit AutoRestoreRealmDebugMode(Realm* realm)
    230      : realm_(realm), bits_(realm->debugModeBits_) {
    231    MOZ_ASSERT(realm_);
    232  }
    233 
    234  ~AutoRestoreRealmDebugMode() {
    235    if (realm_) {
    236      realm_->restoreDebugModeBitsOnOOM(bits_);
    237    }
    238  }
    239 
    240  void release() { realm_ = nullptr; }
    241 };
    242 
    243 /* static */
    244 bool DebugAPI::slowPathCheckNoExecute(JSContext* cx, HandleScript script) {
    245  MOZ_ASSERT(cx->realm()->isDebuggee());
    246  MOZ_ASSERT(cx->noExecuteDebuggerTop);
    247  return EnterDebuggeeNoExecute::reportIfFoundInStack(cx, script);
    248 }
    249 
    250 static void PropagateForcedReturn(JSContext* cx, AbstractFramePtr frame,
    251                                  HandleValue rval) {
    252  // The Debugger's hooks may return a value that affects the completion
    253  // value of the given frame. For example, a hook may return `{ return: 42 }`
    254  // to terminate the frame and return `42` as the final frame result.
    255  // To accomplish this, the debugger treats these return values as if
    256  // execution of the JS function has been terminated without a pending
    257  // exception, but with a special flag. When the error is handled by the
    258  // interpreter or JIT, the special flag and the error state will be cleared
    259  // and execution will continue from the end of the frame.
    260  MOZ_ASSERT(!cx->isExceptionPending());
    261  cx->setPropagatingForcedReturn();
    262  frame.setReturnValue(rval);
    263 }
    264 
    265 [[nodiscard]] static bool AdjustGeneratorResumptionValue(JSContext* cx,
    266                                                         AbstractFramePtr frame,
    267                                                         ResumeMode& resumeMode,
    268                                                         MutableHandleValue vp);
    269 
    270 [[nodiscard]] static bool ApplyFrameResumeMode(JSContext* cx,
    271                                               AbstractFramePtr frame,
    272                                               ResumeMode resumeMode,
    273                                               HandleValue rv,
    274                                               Handle<SavedFrame*> exnStack) {
    275  RootedValue rval(cx, rv);
    276 
    277  // The value passed in here is unwrapped and has no guarantees about what
    278  // compartment it may be associated with, so we explicitly wrap it into the
    279  // debuggee compartment.
    280  if (!cx->compartment()->wrap(cx, &rval)) {
    281    return false;
    282  }
    283 
    284  if (!AdjustGeneratorResumptionValue(cx, frame, resumeMode, &rval)) {
    285    return false;
    286  }
    287 
    288  switch (resumeMode) {
    289    case ResumeMode::Continue:
    290      break;
    291 
    292    case ResumeMode::Throw:
    293      // If we have a stack from the original throw, use it instead of
    294      // associating the throw with the current execution point.
    295      if (exnStack) {
    296        cx->setPendingException(rval, exnStack);
    297      } else {
    298        cx->setPendingException(rval, ShouldCaptureStack::Always);
    299      }
    300      return false;
    301 
    302    case ResumeMode::Terminate:
    303      cx->reportUncatchableException();
    304      return false;
    305 
    306    case ResumeMode::Return:
    307      PropagateForcedReturn(cx, frame, rval);
    308      return false;
    309 
    310    default:
    311      MOZ_CRASH("bad Debugger::onEnterFrame resume mode");
    312  }
    313 
    314  return true;
    315 }
    316 static bool ApplyFrameResumeMode(JSContext* cx, AbstractFramePtr frame,
    317                                 ResumeMode resumeMode, HandleValue rval) {
    318  Rooted<SavedFrame*> nullStack(cx);
    319  return ApplyFrameResumeMode(cx, frame, resumeMode, rval, nullStack);
    320 }
    321 
    322 bool js::ValueToStableChars(JSContext* cx, const char* fnname,
    323                            HandleValue value,
    324                            AutoStableStringChars& stableChars) {
    325  if (!value.isString()) {
    326    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    327                              JSMSG_NOT_EXPECTED_TYPE, fnname, "string",
    328                              InformalValueTypeName(value));
    329    return false;
    330  }
    331  Rooted<JSLinearString*> linear(cx, value.toString()->ensureLinear(cx));
    332  if (!linear) {
    333    return false;
    334  }
    335  if (!stableChars.initTwoByte(cx, linear)) {
    336    return false;
    337  }
    338  return true;
    339 }
    340 
    341 bool EvalOptions::setFilename(JSContext* cx, const char* filename) {
    342  JS::UniqueChars copy;
    343  if (filename) {
    344    copy = DuplicateString(cx, filename);
    345    if (!copy) {
    346      return false;
    347    }
    348  }
    349 
    350  filename_ = std::move(copy);
    351  return true;
    352 }
    353 
    354 bool js::ParseEvalOptions(JSContext* cx, HandleValue value,
    355                          EvalOptions& options) {
    356  if (!value.isObject()) {
    357    return true;
    358  }
    359 
    360  RootedObject opts(cx, &value.toObject());
    361 
    362  RootedValue v(cx);
    363  if (!JS_GetProperty(cx, opts, "url", &v)) {
    364    return false;
    365  }
    366  if (!v.isUndefined()) {
    367    RootedString url_str(cx, ToString<CanGC>(cx, v));
    368    if (!url_str) {
    369      return false;
    370    }
    371    UniqueChars url_bytes = JS_EncodeStringToUTF8(cx, url_str);
    372    if (!url_bytes) {
    373      return false;
    374    }
    375    if (!options.setFilename(cx, url_bytes.get())) {
    376      return false;
    377    }
    378  }
    379 
    380  if (!JS_GetProperty(cx, opts, "lineNumber", &v)) {
    381    return false;
    382  }
    383  if (!v.isUndefined()) {
    384    uint32_t lineno;
    385    if (!ToUint32(cx, v, &lineno)) {
    386      return false;
    387    }
    388    options.setLineno(lineno);
    389  }
    390 
    391  if (!JS_GetProperty(cx, opts, "hideFromDebugger", &v)) {
    392    return false;
    393  }
    394  options.setHideFromDebugger(ToBoolean(v));
    395 
    396  if (!JS_GetProperty(cx, opts, "bypassCSP", &v)) {
    397    return false;
    398  }
    399  options.setBypassCSP(ToBoolean(v));
    400 
    401  if (options.kind() == EvalOptions::EnvKind::GlobalWithExtraOuterBindings) {
    402    if (!JS_GetProperty(cx, opts, "useInnerBindings", &v)) {
    403      return false;
    404    }
    405    if (ToBoolean(v)) {
    406      options.setUseInnerBindings();
    407    }
    408  }
    409 
    410  return true;
    411 }
    412 
    413 /*** Breakpoints ************************************************************/
    414 
    415 bool BreakpointSite::isEmpty() const { return breakpoints.isEmpty(); }
    416 
    417 void BreakpointSite::trace(JSTracer* trc) {
    418  for (auto p = breakpoints.begin(); p; p++) {
    419    p->trace(trc);
    420  }
    421 }
    422 
    423 void BreakpointSite::finalize(JS::GCContext* gcx) {
    424  while (!breakpoints.isEmpty()) {
    425    breakpoints.begin()->delete_(gcx);
    426  }
    427 }
    428 
    429 Breakpoint* BreakpointSite::firstBreakpoint() const {
    430  if (isEmpty()) {
    431    return nullptr;
    432  }
    433  return &(*breakpoints.begin());
    434 }
    435 
    436 bool BreakpointSite::hasBreakpoint(Breakpoint* toFind) {
    437  const BreakpointList::Iterator bp(toFind);
    438  for (auto p = breakpoints.begin(); p; p++) {
    439    if (p == bp) {
    440      return true;
    441    }
    442  }
    443  return false;
    444 }
    445 
    446 Breakpoint::Breakpoint(Debugger* debugger, HandleObject wrappedDebugger,
    447                       BreakpointSite* site, HandleObject handler)
    448    : debugger(debugger),
    449      wrappedDebugger(wrappedDebugger),
    450      site(site),
    451      handler(handler) {
    452  MOZ_ASSERT(UncheckedUnwrap(wrappedDebugger) == debugger->object);
    453  MOZ_ASSERT(handler->compartment() == wrappedDebugger->compartment());
    454 
    455  debugger->breakpoints.pushBack(this);
    456  site->breakpoints.pushBack(this);
    457 }
    458 
    459 void Breakpoint::trace(JSTracer* trc) {
    460  MOZ_ASSERT_IF(trc->kind() != JS::TracerKind::Moving,
    461                !IsDeadProxyObject(wrappedDebugger));
    462  TraceEdge(trc, &wrappedDebugger, "breakpoint owner");
    463 
    464  TraceEdge(trc, &handler, "breakpoint handler");
    465 }
    466 
    467 void Breakpoint::delete_(JS::GCContext* gcx) {
    468  debugger->breakpoints.remove(this);
    469  site->breakpoints.remove(this);
    470  gc::Cell* cell = site->owningCell();
    471  gcx->delete_(cell, this, MemoryUse::Breakpoint);
    472 }
    473 
    474 void Breakpoint::remove(JS::GCContext* gcx) {
    475  BreakpointSite* savedSite = site;
    476  delete_(gcx);
    477 
    478  savedSite->destroyIfEmpty(gcx);
    479 }
    480 
    481 Breakpoint* Breakpoint::nextInDebugger() { return debuggerLink.mNext; }
    482 
    483 Breakpoint* Breakpoint::nextInSite() { return siteLink.mNext; }
    484 
    485 JSBreakpointSite::JSBreakpointSite(JSScript* script, jsbytecode* pc)
    486    : script(script), pc(pc) {
    487  MOZ_ASSERT(!DebugAPI::hasBreakpointsAt(script, pc));
    488 }
    489 
    490 void JSBreakpointSite::remove(JS::GCContext* gcx) {
    491  DebugScript::destroyBreakpointSite(gcx, script, pc);
    492 }
    493 
    494 void JSBreakpointSite::trace(JSTracer* trc) {
    495  BreakpointSite::trace(trc);
    496  TraceEdge(trc, &script, "breakpoint script");
    497 }
    498 
    499 void JSBreakpointSite::delete_(JS::GCContext* gcx) {
    500  BreakpointSite::finalize(gcx);
    501 
    502  gcx->delete_(script, this, MemoryUse::BreakpointSite);
    503 }
    504 
    505 gc::Cell* JSBreakpointSite::owningCell() { return script; }
    506 
    507 Realm* JSBreakpointSite::realm() const { return script->realm(); }
    508 
    509 WasmBreakpointSite::WasmBreakpointSite(WasmInstanceObject* instanceObject_,
    510                                       uint32_t offset_)
    511    : instanceObject(instanceObject_), offset(offset_) {
    512  MOZ_ASSERT(instanceObject_);
    513  MOZ_ASSERT(instanceObject_->instance().debugEnabled());
    514 }
    515 
    516 void WasmBreakpointSite::trace(JSTracer* trc) {
    517  BreakpointSite::trace(trc);
    518  TraceEdge(trc, &instanceObject, "breakpoint Wasm instance");
    519 }
    520 
    521 void WasmBreakpointSite::remove(JS::GCContext* gcx) {
    522  instanceObject->instance().destroyBreakpointSite(gcx, offset);
    523 }
    524 
    525 void WasmBreakpointSite::delete_(JS::GCContext* gcx) {
    526  BreakpointSite::finalize(gcx);
    527 
    528  gcx->delete_(instanceObject, this, MemoryUse::BreakpointSite);
    529 }
    530 
    531 gc::Cell* WasmBreakpointSite::owningCell() { return instanceObject; }
    532 
    533 Realm* WasmBreakpointSite::realm() const { return instanceObject->realm(); }
    534 
    535 /*** Debugger hook dispatch *************************************************/
    536 
    537 Debugger::Debugger(JSContext* cx, NativeObject* dbg)
    538    : object(dbg),
    539      debuggees(cx->zone()),
    540      uncaughtExceptionHook(nullptr),
    541      allowUnobservedAsmJS(false),
    542      allowUnobservedWasm(false),
    543      exclusiveDebuggerOnEval(false),
    544      inspectNativeCallArguments(false),
    545      collectCoverageInfo(false),
    546      shouldAvoidSideEffects(false),
    547      observedGCs(cx->zone()),
    548      allocationsLog(cx),
    549      trackingAllocationSites(false),
    550      allocationSamplingProbability(1.0),
    551      maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH),
    552      allocationsLogOverflowed(false),
    553      frames(cx->zone()),
    554      generatorFrames(cx),
    555      scripts(cx),
    556      sources(cx),
    557      objects(cx),
    558      environments(cx),
    559      wasmInstanceScripts(cx),
    560      wasmInstanceSources(cx) {
    561  cx->check(dbg);
    562 
    563  cx->runtime()->debuggerList().insertBack(this);
    564 }
    565 
    566 template <typename ElementAccess>
    567 static void RemoveDebuggerEntry(
    568    mozilla::DoublyLinkedList<Debugger, ElementAccess>& list, Debugger* dbg) {
    569  // The "probably" here is because there could technically be multiple lists
    570  // with this type signature and theoretically the debugger could be an entry
    571  // in a different one. That is not actually possible however because there
    572  // is only one list the debugger could be in.
    573  if (list.ElementProbablyInList(dbg)) {
    574    list.remove(dbg);
    575  }
    576 }
    577 
    578 Debugger::~Debugger() {
    579  MOZ_ASSERT(debuggees.empty());
    580  allocationsLog.clear();
    581 
    582  // Breakpoints should hold us alive, so any breakpoints remaining must be set
    583  // in dying JSScripts. We should clean them up, but this never asserts. I'm
    584  // not sure why.
    585  MOZ_ASSERT(breakpoints.isEmpty());
    586 
    587  // We don't have to worry about locking here since Debugger is not
    588  // background finalized.
    589  JSContext* cx = TlsContext.get();
    590  RemoveDebuggerEntry(cx->runtime()->onNewGlobalObjectWatchers(), this);
    591  RemoveDebuggerEntry(cx->runtime()->onGarbageCollectionWatchers(), this);
    592 }
    593 
    594 #ifdef DEBUG
    595 /* static */
    596 bool Debugger::isChildJSObject(JSObject* obj) {
    597  return obj->getClass() == &DebuggerFrame::class_ ||
    598         obj->getClass() == &DebuggerScript::class_ ||
    599         obj->getClass() == &DebuggerSource::class_ ||
    600         obj->getClass() == &DebuggerObject::class_ ||
    601         obj->getClass() == &DebuggerEnvironment::class_;
    602 }
    603 #endif
    604 
    605 bool Debugger::hasMemory() const {
    606  return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE).isObject();
    607 }
    608 
    609 DebuggerMemory& Debugger::memory() const {
    610  MOZ_ASSERT(hasMemory());
    611  return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE)
    612      .toObject()
    613      .as<DebuggerMemory>();
    614 }
    615 
    616 /*** Debugger accessors *******************************************************/
    617 
    618 bool Debugger::getFrame(JSContext* cx, const FrameIter& iter,
    619                        MutableHandleValue vp) {
    620  Rooted<DebuggerFrame*> result(cx);
    621  if (!Debugger::getFrame(cx, iter, &result)) {
    622    return false;
    623  }
    624  vp.setObject(*result);
    625  return true;
    626 }
    627 
    628 bool Debugger::getFrame(JSContext* cx, MutableHandle<DebuggerFrame*> result) {
    629  RootedObject proto(
    630      cx, &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject());
    631  Rooted<NativeObject*> debugger(cx, object);
    632 
    633  // Since there is no frame/generator data to associate with this frame, this
    634  // will create a new, "terminated" Debugger.Frame object.
    635  Rooted<DebuggerFrame*> frame(
    636      cx, DebuggerFrame::create(cx, proto, debugger, nullptr, nullptr));
    637  if (!frame) {
    638    return false;
    639  }
    640 
    641  result.set(frame);
    642  return true;
    643 }
    644 
    645 bool Debugger::getFrame(JSContext* cx, const FrameIter& iter,
    646                        MutableHandle<DebuggerFrame*> result) {
    647  AbstractFramePtr referent = iter.abstractFramePtr();
    648  MOZ_ASSERT_IF(referent.hasScript(), !referent.script()->selfHosted());
    649 
    650  FrameMap::AddPtr p = frames.lookupForAdd(referent);
    651  if (!p) {
    652    Rooted<AbstractGeneratorObject*> genObj(cx);
    653    if (referent.isGeneratorFrame()) {
    654      if (referent.isFunctionFrame()) {
    655        AutoRealm ar(cx, referent.callee());
    656        genObj = GetGeneratorObjectForFrame(cx, referent);
    657      } else {
    658        MOZ_ASSERT(referent.isModuleFrame());
    659        AutoRealm ar(cx, referent.script()->module());
    660        genObj = GetGeneratorObjectForFrame(cx, referent);
    661      }
    662 
    663      // If this frame has a generator associated with it, but no on-stack
    664      // Debugger.Frame object was found, there should not be a suspended
    665      // Debugger.Frame either because otherwise slowPathOnResumeFrame would
    666      // have already populated the "frames" map with a Debugger.Frame.
    667      MOZ_ASSERT_IF(genObj, !generatorFrames.has(genObj));
    668 
    669      // If the frame's generator is closed, there is no way to associate the
    670      // generator with the frame successfully because there is no way to
    671      // get the generator's callee script, and even if we could, having it
    672      // there would in no way affect the behavior of the frame.
    673      if (genObj && genObj->isClosed()) {
    674        genObj = nullptr;
    675      }
    676 
    677      // If no AbstractGeneratorObject exists yet, we create a Debugger.Frame
    678      // below anyway, and Debugger::onNewGenerator() will associate it
    679      // with the AbstractGeneratorObject later when we hit JSOp::Generator.
    680    }
    681 
    682    // Create and populate the Debugger.Frame object.
    683    RootedObject proto(
    684        cx, &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject());
    685    Rooted<NativeObject*> debugger(cx, object);
    686 
    687    Rooted<DebuggerFrame*> frame(
    688        cx, DebuggerFrame::create(cx, proto, debugger, &iter, genObj));
    689    if (!frame) {
    690      return false;
    691    }
    692 
    693    auto terminateDebuggerFrameGuard = MakeScopeExit([&] {
    694      terminateDebuggerFrame(cx->gcContext(), this, frame, referent);
    695    });
    696 
    697    if (genObj) {
    698      DependentAddPtr<GeneratorWeakMap> genPtr(cx, generatorFrames, genObj);
    699      if (!genPtr.add(cx, generatorFrames, genObj, frame)) {
    700        return false;
    701      }
    702    }
    703 
    704    if (!ensureExecutionObservabilityOfFrame(cx, referent)) {
    705      return false;
    706    }
    707 
    708    if (!frames.add(p, referent, frame)) {
    709      ReportOutOfMemory(cx);
    710      return false;
    711    }
    712 
    713    terminateDebuggerFrameGuard.release();
    714  }
    715 
    716  result.set(p->value());
    717  return true;
    718 }
    719 
    720 bool Debugger::getFrame(JSContext* cx, Handle<AbstractGeneratorObject*> genObj,
    721                        MutableHandle<DebuggerFrame*> result) {
    722  // To create a Debugger.Frame for a running generator, we'd also need a
    723  // FrameIter for its stack frame. We could make this work by searching the
    724  // stack for the generator's frame, but for the moment, we only need this
    725  // function to handle generators we've found on promises' reaction records,
    726  // which should always be suspended.
    727  MOZ_ASSERT(genObj->isSuspended());
    728 
    729  // Do we have an existing Debugger.Frame for this generator?
    730  DependentAddPtr<GeneratorWeakMap> p(cx, generatorFrames, genObj);
    731  if (p) {
    732    MOZ_ASSERT(&p->value()->unwrappedGenerator() == genObj);
    733    result.set(p->value());
    734    return true;
    735  }
    736 
    737  // Create a new Debugger.Frame.
    738  RootedObject proto(
    739      cx, &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject());
    740  Rooted<NativeObject*> debugger(cx, object);
    741 
    742  result.set(DebuggerFrame::create(cx, proto, debugger, nullptr, genObj));
    743  if (!result) {
    744    return false;
    745  }
    746 
    747  if (!p.add(cx, generatorFrames, genObj, result)) {
    748    terminateDebuggerFrame(cx->gcContext(), this, result, NullFramePtr());
    749    return false;
    750  }
    751 
    752  return true;
    753 }
    754 
    755 static bool DebuggerExists(
    756    GlobalObject* global, const std::function<bool(Debugger* dbg)>& predicate) {
    757  // The GC analysis can't determine that the predicate can't GC, so let it know
    758  // explicitly.
    759  JS::AutoSuppressGCAnalysis nogc;
    760 
    761  for (Realm::DebuggerVectorEntry& entry : global->getDebuggers(nogc)) {
    762    // Callbacks should not create new references to the debugger, so don't
    763    // use a barrier. This allows this method to be called during GC.
    764    if (predicate(entry.dbg.unbarrieredGet())) {
    765      return true;
    766    }
    767  }
    768  return false;
    769 }
    770 
    771 /* static */
    772 bool Debugger::hasLiveHook(GlobalObject* global, Hook which) {
    773  return DebuggerExists(global,
    774                        [=](Debugger* dbg) { return dbg->getHook(which); });
    775 }
    776 
    777 /* static */
    778 bool DebugAPI::debuggerObservesAllExecution(GlobalObject* global) {
    779  return DebuggerExists(
    780      global, [=](Debugger* dbg) { return dbg->observesAllExecution(); });
    781 }
    782 
    783 /* static */
    784 bool DebugAPI::debuggerObservesCoverage(GlobalObject* global) {
    785  return DebuggerExists(global,
    786                        [=](Debugger* dbg) { return dbg->observesCoverage(); });
    787 }
    788 
    789 /* static */
    790 bool DebugAPI::debuggerObservesAsmJS(GlobalObject* global) {
    791  return DebuggerExists(global,
    792                        [=](Debugger* dbg) { return dbg->observesAsmJS(); });
    793 }
    794 
    795 /* static */
    796 bool DebugAPI::debuggerObservesWasm(GlobalObject* global) {
    797  return DebuggerExists(global,
    798                        [=](Debugger* dbg) { return dbg->observesWasm(); });
    799 }
    800 
    801 /* static */
    802 bool DebugAPI::debuggerObservesNativeCall(GlobalObject* global) {
    803  return DebuggerExists(
    804      global, [=](Debugger* dbg) { return dbg->observesNativeCalls(); });
    805 }
    806 
    807 /* static */
    808 bool DebugAPI::hasExceptionUnwindHook(GlobalObject* global) {
    809  return Debugger::hasLiveHook(global, Debugger::OnExceptionUnwind);
    810 }
    811 
    812 /* static */
    813 bool DebugAPI::hasDebuggerStatementHook(GlobalObject* global) {
    814  return Debugger::hasLiveHook(global, Debugger::OnDebuggerStatement);
    815 }
    816 
    817 template <typename HookIsEnabledFun /* bool (Debugger*) */>
    818 bool DebuggerList<HookIsEnabledFun>::init(JSContext* cx) {
    819  // Determine which debuggers will receive this event, and in what order.
    820  // Make a copy of the list, since the original is mutable and we will be
    821  // calling into arbitrary JS.
    822  Handle<GlobalObject*> global = cx->global();
    823  JS::AutoAssertNoGC nogc;
    824  for (Realm::DebuggerVectorEntry& entry : global->getDebuggers(nogc)) {
    825    Debugger* dbg = entry.dbg;
    826    if (dbg->isHookCallAllowed(cx) && hookIsEnabled(dbg)) {
    827      if (!debuggers.append(ObjectValue(*dbg->toJSObject()))) {
    828        return false;
    829      }
    830    }
    831  }
    832  return true;
    833 }
    834 
    835 template <typename HookIsEnabledFun /* bool (Debugger*) */>
    836 template <typename FireHookFun /* bool (Debugger*) */>
    837 bool DebuggerList<HookIsEnabledFun>::dispatchHook(JSContext* cx,
    838                                                  FireHookFun fireHook) {
    839  // Preserve the debuggee's microtask event queue while we run the hooks, so
    840  // the debugger's microtask checkpoints don't run from the debuggee's
    841  // microtasks, and vice versa.
    842  JS::AutoDebuggerJobQueueInterruption adjqi;
    843  if (!adjqi.init(cx)) {
    844    return false;
    845  }
    846 
    847  // Deliver the event to each debugger, checking again to make sure it
    848  // should still be delivered.
    849  Handle<GlobalObject*> global = cx->global();
    850  for (Value* p = debuggers.begin(); p != debuggers.end(); p++) {
    851    Debugger* dbg = Debugger::fromJSObject(&p->toObject());
    852    EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
    853    if (dbg->debuggees.has(global) && hookIsEnabled(dbg)) {
    854      bool result =
    855          dbg->enterDebuggerHook(cx, [&]() -> bool { return fireHook(dbg); });
    856      adjqi.runJobs();
    857      if (!result) {
    858        return false;
    859      }
    860    }
    861  }
    862  return true;
    863 }
    864 
    865 template <typename HookIsEnabledFun /* bool (Debugger*) */>
    866 template <typename FireHookFun /* bool (Debugger*) */>
    867 void DebuggerList<HookIsEnabledFun>::dispatchQuietHook(JSContext* cx,
    868                                                       FireHookFun fireHook) {
    869  bool result =
    870      dispatchHook(cx, [&](Debugger* dbg) -> bool { return fireHook(dbg); });
    871 
    872  // dispatchHook may fail due to OOM. This OOM is not handlable at the
    873  // callsites of dispatchQuietHook in the engine.
    874  if (!result) {
    875    cx->clearPendingException();
    876  }
    877 }
    878 
    879 template <typename HookIsEnabledFun /* bool (Debugger*) */>
    880 template <typename FireHookFun /* bool (Debugger*, ResumeMode&, MutableHandleValue vp) */>
    881 bool DebuggerList<HookIsEnabledFun>::dispatchResumptionHook(
    882    JSContext* cx, AbstractFramePtr frame, FireHookFun fireHook) {
    883  ResumeMode resumeMode = ResumeMode::Continue;
    884  RootedValue rval(cx);
    885  return dispatchHook(cx,
    886                      [&](Debugger* dbg) -> bool {
    887                        return fireHook(dbg, resumeMode, &rval);
    888                      }) &&
    889         ApplyFrameResumeMode(cx, frame, resumeMode, rval);
    890 }
    891 
    892 JSObject* Debugger::getHook(Hook hook) const {
    893  MOZ_ASSERT(hook >= 0 && hook < HookCount);
    894  const Value& v = object->getReservedSlot(JSSLOT_DEBUG_HOOK_START +
    895                                           std::underlying_type_t<Hook>(hook));
    896  return v.isUndefined() ? nullptr : &v.toObject();
    897 }
    898 
    899 bool Debugger::hasAnyLiveHooks() const {
    900  // A onNewGlobalObject hook does not hold its Debugger live, so its behavior
    901  // is nondeterministic. This behavior is not satisfying, but it is at least
    902  // documented.
    903  if (getHook(OnDebuggerStatement) || getHook(OnExceptionUnwind) ||
    904      getHook(OnNewScript) || getHook(OnEnterFrame)) {
    905    return true;
    906  }
    907 
    908  return false;
    909 }
    910 
    911 /* static */
    912 bool DebugAPI::slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame) {
    913 #ifdef MOZ_EXECUTION_TRACING
    914  if (cx->hasExecutionTracer()) {
    915    cx->getExecutionTracer().onEnterFrame(cx, frame);
    916  }
    917 #endif
    918  return Debugger::dispatchResumptionHook(
    919      cx, frame,
    920      [frame](Debugger* dbg) -> bool {
    921        return dbg->observesFrame(frame) && dbg->observesEnterFrame();
    922      },
    923      [&](Debugger* dbg, ResumeMode& resumeMode, MutableHandleValue vp)
    924          -> bool { return dbg->fireEnterFrame(cx, resumeMode, vp); });
    925 }
    926 
    927 /* static */
    928 bool DebugAPI::slowPathOnResumeFrame(JSContext* cx, AbstractFramePtr frame) {
    929 #ifdef MOZ_EXECUTION_TRACING
    930  if (cx->hasExecutionTracer()) {
    931    cx->getExecutionTracer().onEnterFrame(cx, frame);
    932  }
    933 #endif
    934  // Don't count on this method to be called every time a generator is
    935  // resumed! This is called only if the frame's debuggee bit is set,
    936  // i.e. the script has breakpoints or the frame is stepping.
    937  MOZ_ASSERT(frame.isGeneratorFrame());
    938  MOZ_ASSERT(frame.isDebuggee());
    939 
    940  Rooted<AbstractGeneratorObject*> genObj(
    941      cx, GetGeneratorObjectForFrame(cx, frame));
    942  MOZ_ASSERT(genObj);
    943 
    944  // If there is an OOM, we mark all of the Debugger.Frame objects terminated
    945  // because we want to ensure that none of the frames are in a partially
    946  // initialized state where they are in "generatorFrames" but not "frames".
    947  auto terminateDebuggerFramesGuard = MakeScopeExit([&] {
    948    Debugger::terminateDebuggerFrames(cx, frame);
    949 
    950    MOZ_ASSERT(!DebugAPI::inFrameMaps(frame));
    951  });
    952 
    953  // For each debugger, if there is an existing Debugger.Frame object for the
    954  // resumed `frame`, update it with the new frame pointer and make sure the
    955  // frame is observable.
    956  FrameIter iter(cx);
    957  MOZ_ASSERT(iter.abstractFramePtr() == frame);
    958  {
    959    JS::AutoAssertNoGC nogc;
    960    for (Realm::DebuggerVectorEntry& entry :
    961         frame.global()->getDebuggers(nogc)) {
    962      Debugger* dbg = entry.dbg;
    963      if (Debugger::GeneratorWeakMap::Ptr generatorEntry =
    964              dbg->generatorFrames.lookup(genObj)) {
    965        DebuggerFrame* frameObj = generatorEntry->value();
    966        MOZ_ASSERT(&frameObj->unwrappedGenerator() == genObj);
    967        if (!dbg->frames.putNew(frame, frameObj)) {
    968          ReportOutOfMemory(cx);
    969          return false;
    970        }
    971        if (!frameObj->resume(iter)) {
    972          return false;
    973        }
    974      }
    975    }
    976  }
    977 
    978  terminateDebuggerFramesGuard.release();
    979 
    980  return slowPathOnEnterFrame(cx, frame);
    981 }
    982 
    983 /* static */
    984 NativeResumeMode DebugAPI::slowPathOnNativeCall(JSContext* cx,
    985                                                const CallArgs& args,
    986                                                CallReason reason) {
    987  if (!cx->realm()->debuggerObservesNativeCall()) {
    988    return NativeResumeMode::Continue;
    989  }
    990 
    991  DebuggerList debuggerList(cx, [](Debugger* dbg) -> bool {
    992    return dbg->getHook(Debugger::OnNativeCall);
    993  });
    994 
    995  if (!debuggerList.init(cx)) {
    996    return NativeResumeMode::Abort;
    997  }
    998 
    999  if (debuggerList.empty()) {
   1000    return NativeResumeMode::Continue;
   1001  }
   1002 
   1003  // The onNativeCall hook is fired when self hosted functions are called,
   1004  // and any other self hosted function or C++ native that is directly called
   1005  // by the self hosted function is considered to be part of the same
   1006  // native call, except for the following 4 cases:
   1007  //
   1008  //  * callContentFunction and constructContentFunction,
   1009  //    which uses CallReason::CallContent
   1010  //  * Function.prototype.call and Function.prototype.apply,
   1011  //    which uses CallReason::FunCall
   1012  //  * Getter call which uses CallReason::Getter
   1013  //  * Setter call which uses CallReason::Setter
   1014  //
   1015  // We check this only after checking that debuggerList has items in order
   1016  // to avoid unnecessary calls to cx->currentScript(), which can be expensive
   1017  // when the top frame is in jitcode.
   1018  JSScript* script = cx->currentScript();
   1019  if (script && script->selfHosted() && reason != CallReason::CallContent &&
   1020      reason != CallReason::FunCall && reason != CallReason::Getter &&
   1021      reason != CallReason::Setter) {
   1022    return NativeResumeMode::Continue;
   1023  }
   1024 
   1025  RootedValue rval(cx);
   1026  ResumeMode resumeMode = ResumeMode::Continue;
   1027  bool result = debuggerList.dispatchHook(cx, [&](Debugger* dbg) -> bool {
   1028    return dbg->fireNativeCall(cx, args, reason, resumeMode, &rval);
   1029  });
   1030  if (!result) {
   1031    return NativeResumeMode::Abort;
   1032  }
   1033 
   1034  // Hook must follow normal native function conventions and not return
   1035  // primitive values.
   1036  if (resumeMode == ResumeMode::Return) {
   1037    if (args.isConstructing() && !rval.isObject()) {
   1038      JS_ReportErrorASCII(
   1039          cx, "onNativeCall hook must return an object for constructor call");
   1040      return NativeResumeMode::Abort;
   1041    }
   1042  }
   1043 
   1044  // The value is not in any particular compartment, so it needs to be
   1045  // explicitly wrapped into the debuggee compartment.
   1046  if (!cx->compartment()->wrap(cx, &rval)) {
   1047    return NativeResumeMode::Abort;
   1048  }
   1049 
   1050  switch (resumeMode) {
   1051    case ResumeMode::Continue:
   1052      break;
   1053 
   1054    case ResumeMode::Throw:
   1055      cx->setPendingException(rval, ShouldCaptureStack::Always);
   1056      return NativeResumeMode::Abort;
   1057 
   1058    case ResumeMode::Terminate:
   1059      cx->reportUncatchableException();
   1060      return NativeResumeMode::Abort;
   1061 
   1062    case ResumeMode::Return:
   1063      args.rval().set(rval);
   1064      return NativeResumeMode::Override;
   1065  }
   1066 
   1067  return NativeResumeMode::Continue;
   1068 }
   1069 
   1070 /* static */
   1071 bool DebugAPI::slowPathShouldAvoidSideEffects(JSContext* cx) {
   1072  return DebuggerExists(
   1073      cx->global(), [=](Debugger* dbg) { return dbg->shouldAvoidSideEffects; });
   1074 }
   1075 
   1076 /*
   1077 * RAII class to mark a generator as "running" temporarily while running
   1078 * debugger code.
   1079 *
   1080 * When Debugger::slowPathOnLeaveFrame is called for a frame that is yielding
   1081 * or awaiting, its generator is in the "suspended" state. Letting script
   1082 * observe this state, with the generator on stack yet also reenterable, would
   1083 * be bad, so we mark it running while we fire events.
   1084 */
   1085 class MOZ_RAII AutoSetGeneratorRunning {
   1086  int32_t resumeIndex_;
   1087  AsyncGeneratorObject::State asyncGenState_;
   1088  Rooted<AbstractGeneratorObject*> genObj_;
   1089 
   1090 public:
   1091  AutoSetGeneratorRunning(JSContext* cx,
   1092                          Handle<AbstractGeneratorObject*> genObj)
   1093      : resumeIndex_(0),
   1094        asyncGenState_(static_cast<AsyncGeneratorObject::State>(0)),
   1095        genObj_(cx, genObj) {
   1096    if (genObj) {
   1097      if (!genObj->isClosed() && !genObj->isBeforeInitialYield() &&
   1098          genObj->isSuspended()) {
   1099        // Yielding or awaiting.
   1100        resumeIndex_ = genObj->resumeIndex();
   1101        genObj->setRunning();
   1102 
   1103        // Async generators have additionally bookkeeping which must be
   1104        // adjusted when switching over to the running state.
   1105        if (genObj->is<AsyncGeneratorObject>()) {
   1106          auto* generator = &genObj->as<AsyncGeneratorObject>();
   1107          asyncGenState_ = generator->state();
   1108          generator->setExecuting();
   1109        }
   1110      } else {
   1111        // Returning or throwing. The generator is already closed, if
   1112        // it was ever exposed at all.
   1113        genObj_ = nullptr;
   1114      }
   1115    }
   1116  }
   1117 
   1118  ~AutoSetGeneratorRunning() {
   1119    if (genObj_) {
   1120      MOZ_ASSERT(genObj_->isRunning());
   1121      genObj_->setResumeIndex(resumeIndex_);
   1122      if (genObj_->is<AsyncGeneratorObject>()) {
   1123        genObj_->as<AsyncGeneratorObject>().setState(asyncGenState_);
   1124      }
   1125    }
   1126  }
   1127 };
   1128 
   1129 /*
   1130 * Handle leaving a frame with debuggers watching. |frameOk| indicates whether
   1131 * the frame is exiting normally or abruptly. Set |cx|'s exception and/or
   1132 * |cx->fp()|'s return value, and return a new success value.
   1133 */
   1134 /* static */
   1135 bool DebugAPI::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame,
   1136                                    const jsbytecode* pc, bool frameOk) {
   1137 #ifdef MOZ_EXECUTION_TRACING
   1138  if (cx->hasExecutionTracer()) {
   1139    cx->getExecutionTracer().onLeaveFrame(cx, frame);
   1140  }
   1141 #endif
   1142  MOZ_ASSERT_IF(!frame.isWasmDebugFrame(), pc);
   1143 
   1144  mozilla::DebugOnly<Handle<GlobalObject*>> debuggeeGlobal = cx->global();
   1145 
   1146  // These are updated below, but consulted by the cleanup code we register now,
   1147  // so declare them here, initialized to quiescent values.
   1148  Rooted<Completion> completion(cx);
   1149  bool success = false;
   1150 
   1151  auto frameMapsGuard = MakeScopeExit([&] {
   1152    // Clean up all Debugger.Frame instances on exit. On suspending, pass the
   1153    // flag that says to leave those frames `.live`. Note that if the completion
   1154    // is a suspension but success is false, the generator gets closed, not
   1155    // suspended.
   1156    if (success && completion.get().suspending()) {
   1157      Debugger::suspendGeneratorDebuggerFrames(cx, frame);
   1158    } else {
   1159      Debugger::terminateDebuggerFrames(cx, frame);
   1160    }
   1161  });
   1162 
   1163  // The onPop handler and associated clean up logic should not run multiple
   1164  // times on the same frame. If slowPathOnLeaveFrame has already been
   1165  // called, the frame will not be present in the Debugger frame maps.
   1166  Rooted<Debugger::DebuggerFrameVector> frames(cx);
   1167  if (!Debugger::getDebuggerFrames(frame, &frames)) {
   1168    // There is at least one match Debugger.Frame we failed to process, so drop
   1169    // the pending exception and raise an out-of-memory instead.
   1170    if (!frameOk) {
   1171      cx->clearPendingException();
   1172    }
   1173    ReportOutOfMemory(cx);
   1174    return false;
   1175  }
   1176  if (frames.empty()) {
   1177    return frameOk;
   1178  }
   1179 
   1180  // Convert current exception state into a Completion and clear exception off
   1181  // of the JSContext.
   1182  completion = Completion::fromJSFramePop(cx, frame, pc, frameOk);
   1183 
   1184  ResumeMode resumeMode = ResumeMode::Continue;
   1185  RootedValue rval(cx);
   1186 
   1187  {
   1188    // Preserve the debuggee's microtask event queue while we run the hooks, so
   1189    // the debugger's microtask checkpoints don't run from the debuggee's
   1190    // microtasks, and vice versa.
   1191    JS::AutoDebuggerJobQueueInterruption adjqi;
   1192    if (!adjqi.init(cx)) {
   1193      return false;
   1194    }
   1195 
   1196    // This path can be hit via unwinding the stack due to over-recursion or
   1197    // OOM. In those cases, don't fire the frames' onPop handlers, because
   1198    // invoking JS will only trigger the same condition. See
   1199    // slowPathOnExceptionUnwind.
   1200    if (!cx->isThrowingOverRecursed() && !cx->isThrowingOutOfMemory()) {
   1201      Rooted<AbstractGeneratorObject*> genObj(
   1202          cx, frame.isGeneratorFrame() ? GetGeneratorObjectForFrame(cx, frame)
   1203                                       : nullptr);
   1204 
   1205      // For each Debugger.Frame, fire its onPop handler, if any.
   1206      for (size_t i = 0; i < frames.length(); i++) {
   1207        Handle<DebuggerFrame*> frameobj = frames[i];
   1208        Debugger* dbg = frameobj->owner();
   1209        EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
   1210 
   1211        // Removing a global from a Debugger's debuggee set kills all of that
   1212        // Debugger's D.Fs in that global. This means that one D.F's onPop can
   1213        // kill the next D.F. So we have to check whether frameobj is still "on
   1214        // the stack".
   1215        if (frameobj->isOnStack() && frameobj->onPopHandler()) {
   1216          OnPopHandler* handler = frameobj->onPopHandler();
   1217 
   1218          bool result = dbg->enterDebuggerHook(cx, [&]() -> bool {
   1219            ResumeMode nextResumeMode = ResumeMode::Continue;
   1220            RootedValue nextValue(cx);
   1221 
   1222            // Call the onPop handler.
   1223            bool success;
   1224            {
   1225              // Mark the generator as running, to prevent reentrance.
   1226              //
   1227              // At certain points in a generator's lifetime,
   1228              // GetGeneratorObjectForFrame can return null even when the
   1229              // generator exists, but at those points the generator has not yet
   1230              // been exposed to JavaScript, so reentrance isn't possible
   1231              // anyway. So there's no harm done if this has no effect in that
   1232              // case.
   1233              AutoSetGeneratorRunning asgr(cx, genObj);
   1234              success = handler->onPop(cx, frameobj, completion, nextResumeMode,
   1235                                       &nextValue);
   1236            }
   1237 
   1238            return dbg->processParsedHandlerResult(cx, frame, pc, success,
   1239                                                   nextResumeMode, nextValue,
   1240                                                   resumeMode, &rval);
   1241          });
   1242          adjqi.runJobs();
   1243 
   1244          if (!result) {
   1245            return false;
   1246          }
   1247 
   1248          // At this point, we are back in the debuggee compartment, and
   1249          // any error has been wrapped up as a completion value.
   1250          MOZ_ASSERT(!cx->isExceptionPending());
   1251        }
   1252      }
   1253    }
   1254  }
   1255 
   1256  completion.get().updateFromHookResult(resumeMode, rval);
   1257 
   1258  // Now that we've run all the handlers, extract the final resumption mode. */
   1259  ResumeMode completionResumeMode;
   1260  RootedValue completionValue(cx);
   1261  Rooted<SavedFrame*> completionStack(cx);
   1262  completion.get().toResumeMode(completionResumeMode, &completionValue,
   1263                                &completionStack);
   1264 
   1265  // If we are returning the original value used to create the completion, then
   1266  // we don't want to treat the resumption value as a Return completion, because
   1267  // that would cause us to apply AdjustGeneratorResumptionValue to the
   1268  // already-adjusted value that the generator actually returned.
   1269  if (resumeMode == ResumeMode::Continue &&
   1270      completionResumeMode == ResumeMode::Return) {
   1271    completionResumeMode = ResumeMode::Continue;
   1272  }
   1273 
   1274  if (!ApplyFrameResumeMode(cx, frame, completionResumeMode, completionValue,
   1275                            completionStack)) {
   1276    if (!cx->isPropagatingForcedReturn()) {
   1277      // If this is an exception or termination, we just propagate that along.
   1278      return false;
   1279    }
   1280 
   1281    // Since we are leaving the frame here, we can convert a forced return
   1282    // into a normal return right away.
   1283    cx->clearPropagatingForcedReturn();
   1284  }
   1285  success = true;
   1286  return true;
   1287 }
   1288 
   1289 /* static */
   1290 bool DebugAPI::slowPathOnNewGenerator(JSContext* cx, AbstractFramePtr frame,
   1291                                      Handle<AbstractGeneratorObject*> genObj) {
   1292  // This is called from JSOp::Generator, after default parameter expressions
   1293  // are evaluated and well after onEnterFrame, so Debugger.Frame objects for
   1294  // `frame` may already have been exposed to debugger code. The
   1295  // AbstractGeneratorObject for this generator call, though, has just been
   1296  // created. It must be associated with any existing Debugger.Frames.
   1297 
   1298  // Initializing frames with their associated generator is critical to the
   1299  // functionality of the debugger, so if there is an OOM, we want to
   1300  // cleanly terminate all of the frames.
   1301  auto terminateDebuggerFramesGuard =
   1302      MakeScopeExit([&] { Debugger::terminateDebuggerFrames(cx, frame); });
   1303 
   1304  bool ok = true;
   1305  gc::AutoSuppressGC nogc(cx);
   1306  Debugger::forEachOnStackDebuggerFrame(
   1307      frame, nogc, [&](Debugger* dbg, DebuggerFrame* frameObjPtr) {
   1308        if (!ok) {
   1309          return;
   1310        }
   1311 
   1312        Rooted<DebuggerFrame*> frameObj(cx, frameObjPtr);
   1313 
   1314        AutoRealm ar(cx, frameObj);
   1315 
   1316        if (!DebuggerFrame::setGeneratorInfo(cx, frameObj, genObj)) {
   1317          // This leaves `genObj` and `frameObj` unassociated. It's OK
   1318          // because we won't pause again with this generator on the stack:
   1319          // the caller will immediately discard `genObj` and unwind `frame`.
   1320          ok = false;
   1321          return;
   1322        }
   1323 
   1324        DependentAddPtr<Debugger::GeneratorWeakMap> genPtr(
   1325            cx, dbg->generatorFrames, genObj);
   1326        if (!genPtr.add(cx, dbg->generatorFrames, genObj, frameObj)) {
   1327          ok = false;
   1328        }
   1329      });
   1330 
   1331  if (!ok) {
   1332    return false;
   1333  }
   1334 
   1335  terminateDebuggerFramesGuard.release();
   1336  return true;
   1337 }
   1338 
   1339 /* static */
   1340 bool DebugAPI::slowPathOnDebuggerStatement(JSContext* cx,
   1341                                           AbstractFramePtr frame) {
   1342  return Debugger::dispatchResumptionHook(
   1343      cx, frame,
   1344      [](Debugger* dbg) -> bool {
   1345        return dbg->getHook(Debugger::OnDebuggerStatement);
   1346      },
   1347      [&](Debugger* dbg, ResumeMode& resumeMode, MutableHandleValue vp)
   1348          -> bool { return dbg->fireDebuggerStatement(cx, resumeMode, vp); });
   1349 }
   1350 
   1351 /* static */
   1352 bool DebugAPI::slowPathOnExceptionUnwind(JSContext* cx,
   1353                                         AbstractFramePtr frame) {
   1354  // Invoking more JS on an over-recursed stack or after OOM is only going
   1355  // to result in more of the same error.
   1356  if (cx->isThrowingOverRecursed() || cx->isThrowingOutOfMemory()) {
   1357    return true;
   1358  }
   1359 
   1360  // The Debugger API mustn't muck with frames from self-hosted scripts.
   1361  if (frame.hasScript() && frame.script()->selfHosted()) {
   1362    return true;
   1363  }
   1364 
   1365  DebuggerList debuggerList(cx, [](Debugger* dbg) -> bool {
   1366    return dbg->getHook(Debugger::OnExceptionUnwind);
   1367  });
   1368 
   1369  if (!debuggerList.init(cx)) {
   1370    return false;
   1371  }
   1372 
   1373  if (debuggerList.empty()) {
   1374    return true;
   1375  }
   1376 
   1377  // We save and restore the exception once up front to avoid having to do it
   1378  // for each 'onExceptionUnwind' hook that has been registered, and we also
   1379  // only do it if the debuggerList contains items in order to avoid extra work.
   1380  RootedValue exc(cx);
   1381  Rooted<SavedFrame*> stack(cx, cx->getPendingExceptionStack());
   1382  if (!cx->getPendingException(&exc)) {
   1383    return false;
   1384  }
   1385  cx->clearPendingException();
   1386 
   1387  bool result = debuggerList.dispatchResumptionHook(
   1388      cx, frame,
   1389      [&](Debugger* dbg, ResumeMode& resumeMode,
   1390          MutableHandleValue vp) -> bool {
   1391        return dbg->fireExceptionUnwind(cx, exc, resumeMode, vp);
   1392      });
   1393  if (!result) {
   1394    return false;
   1395  }
   1396 
   1397  cx->setPendingException(exc, stack);
   1398  return true;
   1399 }
   1400 
   1401 // TODO: Remove Remove this function when all properties/methods returning a
   1402 ///      DebuggerEnvironment have been given a C++ interface (bug 1271649).
   1403 bool Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env,
   1404                               MutableHandleValue rval) {
   1405  if (!env) {
   1406    rval.setNull();
   1407    return true;
   1408  }
   1409 
   1410  Rooted<DebuggerEnvironment*> envobj(cx);
   1411 
   1412  if (!wrapEnvironment(cx, env, &envobj)) {
   1413    return false;
   1414  }
   1415 
   1416  rval.setObject(*envobj);
   1417  return true;
   1418 }
   1419 
   1420 bool Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env,
   1421                               MutableHandle<DebuggerEnvironment*> result) {
   1422  MOZ_ASSERT(env);
   1423 
   1424  // DebuggerEnv should only wrap a debug scope chain obtained (transitively)
   1425  // from GetDebugEnvironmentFor(Frame|Function).
   1426  MOZ_ASSERT(!IsSyntacticEnvironment(env));
   1427 
   1428  DependentAddPtr<EnvironmentWeakMap> p(cx, environments, env);
   1429  if (p) {
   1430    result.set(&p->value()->as<DebuggerEnvironment>());
   1431  } else {
   1432    // Create a new Debugger.Environment for env.
   1433    RootedObject proto(
   1434        cx, &object->getReservedSlot(JSSLOT_DEBUG_ENV_PROTO).toObject());
   1435    Rooted<NativeObject*> debugger(cx, object);
   1436 
   1437    Rooted<DebuggerEnvironment*> envobj(
   1438        cx, DebuggerEnvironment::create(cx, proto, env, debugger));
   1439    if (!envobj) {
   1440      return false;
   1441    }
   1442 
   1443    if (!p.add(cx, environments, env, envobj)) {
   1444      // We need to destroy the edge to the referent, to avoid trying to trace
   1445      // it during untimely collections.
   1446      envobj->clearReferent();
   1447      return false;
   1448    }
   1449 
   1450    result.set(envobj);
   1451  }
   1452 
   1453  return true;
   1454 }
   1455 
   1456 bool Debugger::wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp) {
   1457  cx->check(object.get());
   1458 
   1459  if (vp.isObject()) {
   1460    RootedObject obj(cx, &vp.toObject());
   1461    Rooted<DebuggerObject*> dobj(cx);
   1462 
   1463    if (!wrapDebuggeeObject(cx, obj, &dobj)) {
   1464      return false;
   1465    }
   1466 
   1467    vp.setObject(*dobj);
   1468  } else if (vp.isMagic()) {
   1469    Rooted<PlainObject*> optObj(cx, NewPlainObject(cx));
   1470    if (!optObj) {
   1471      return false;
   1472    }
   1473 
   1474    // We handle three sentinel values: missing arguments
   1475    // (JS_MISSING_ARGUMENTS), optimized out slots (JS_OPTIMIZED_OUT),
   1476    // and uninitialized bindings (JS_UNINITIALIZED_LEXICAL).
   1477    //
   1478    // Other magic values should not have escaped.
   1479    PropertyName* name;
   1480    switch (vp.whyMagic()) {
   1481      case JS_MISSING_ARGUMENTS:
   1482        name = cx->names().missingArguments;
   1483        break;
   1484      case JS_OPTIMIZED_OUT:
   1485        name = cx->names().optimizedOut;
   1486        break;
   1487      case JS_UNINITIALIZED_LEXICAL:
   1488        name = cx->names().uninitialized;
   1489        break;
   1490      default:
   1491        MOZ_CRASH("Unsupported magic value escaped to Debugger");
   1492    }
   1493 
   1494    RootedValue trueVal(cx, BooleanValue(true));
   1495    if (!DefineDataProperty(cx, optObj, name, trueVal)) {
   1496      return false;
   1497    }
   1498 
   1499    vp.setObject(*optObj);
   1500  } else if (!cx->compartment()->wrap(cx, vp)) {
   1501    vp.setUndefined();
   1502    return false;
   1503  }
   1504 
   1505  return true;
   1506 }
   1507 
   1508 bool Debugger::wrapNullableDebuggeeObject(
   1509    JSContext* cx, HandleObject obj, MutableHandle<DebuggerObject*> result) {
   1510  if (!obj) {
   1511    result.set(nullptr);
   1512    return true;
   1513  }
   1514 
   1515  return wrapDebuggeeObject(cx, obj, result);
   1516 }
   1517 
   1518 bool Debugger::wrapDebuggeeObject(JSContext* cx, HandleObject obj,
   1519                                  MutableHandle<DebuggerObject*> result) {
   1520  MOZ_ASSERT(obj);
   1521 
   1522  DependentAddPtr<ObjectWeakMap> p(cx, objects, obj);
   1523  if (p) {
   1524    result.set(&p->value()->as<DebuggerObject>());
   1525  } else {
   1526    // Create a new Debugger.Object for obj.
   1527    Rooted<NativeObject*> debugger(cx, object);
   1528    RootedObject proto(
   1529        cx, &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject());
   1530    Rooted<DebuggerObject*> dobj(
   1531        cx, DebuggerObject::create(cx, proto, obj, debugger));
   1532    if (!dobj) {
   1533      return false;
   1534    }
   1535 
   1536    if (!p.add(cx, objects, obj, dobj)) {
   1537      // We need to destroy the edge to the referent, to avoid trying to trace
   1538      // it during untimely collections.
   1539      dobj->clearReferent();
   1540      return false;
   1541    }
   1542 
   1543    result.set(dobj);
   1544  }
   1545 
   1546  return true;
   1547 }
   1548 
   1549 static DebuggerObject* ToNativeDebuggerObject(JSContext* cx,
   1550                                              MutableHandleObject obj) {
   1551  if (!obj->is<DebuggerObject>()) {
   1552    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1553                              JSMSG_NOT_EXPECTED_TYPE, "Debugger",
   1554                              "Debugger.Object", obj->getClass()->name);
   1555    return nullptr;
   1556  }
   1557 
   1558  return &obj->as<DebuggerObject>();
   1559 }
   1560 
   1561 bool Debugger::unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj) {
   1562  DebuggerObject* ndobj = ToNativeDebuggerObject(cx, obj);
   1563  if (!ndobj) {
   1564    return false;
   1565  }
   1566 
   1567  if (ndobj->owner() != Debugger::fromJSObject(object)) {
   1568    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1569                              JSMSG_DEBUG_WRONG_OWNER, "Debugger.Object");
   1570    return false;
   1571  }
   1572 
   1573  obj.set(ndobj->referent());
   1574  return true;
   1575 }
   1576 
   1577 bool Debugger::unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp) {
   1578  cx->check(object.get(), vp);
   1579  if (vp.isObject()) {
   1580    RootedObject dobj(cx, &vp.toObject());
   1581    if (!unwrapDebuggeeObject(cx, &dobj)) {
   1582      return false;
   1583    }
   1584    vp.setObject(*dobj);
   1585  }
   1586  return true;
   1587 }
   1588 
   1589 static bool CheckArgCompartment(JSContext* cx, JSObject* obj, JSObject* arg,
   1590                                const char* methodname, const char* propname) {
   1591  if (arg->compartment() != obj->compartment()) {
   1592    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1593                              JSMSG_DEBUG_COMPARTMENT_MISMATCH, methodname,
   1594                              propname);
   1595    return false;
   1596  }
   1597  return true;
   1598 }
   1599 
   1600 static bool CheckArgCompartment(JSContext* cx, JSObject* obj, HandleValue v,
   1601                                const char* methodname, const char* propname) {
   1602  if (v.isObject()) {
   1603    return CheckArgCompartment(cx, obj, &v.toObject(), methodname, propname);
   1604  }
   1605  return true;
   1606 }
   1607 
   1608 bool Debugger::unwrapPropertyDescriptor(
   1609    JSContext* cx, HandleObject obj, MutableHandle<PropertyDescriptor> desc) {
   1610  if (desc.hasValue()) {
   1611    RootedValue value(cx, desc.value());
   1612    if (!unwrapDebuggeeValue(cx, &value) ||
   1613        !CheckArgCompartment(cx, obj, value, "defineProperty", "value")) {
   1614      return false;
   1615    }
   1616    desc.setValue(value);
   1617  }
   1618 
   1619  if (desc.hasGetter()) {
   1620    RootedObject get(cx, desc.getter());
   1621    if (get) {
   1622      if (!unwrapDebuggeeObject(cx, &get)) {
   1623        return false;
   1624      }
   1625      if (!CheckArgCompartment(cx, obj, get, "defineProperty", "get")) {
   1626        return false;
   1627      }
   1628    }
   1629    desc.setGetter(get);
   1630  }
   1631 
   1632  if (desc.hasSetter()) {
   1633    RootedObject set(cx, desc.setter());
   1634    if (set) {
   1635      if (!unwrapDebuggeeObject(cx, &set)) {
   1636        return false;
   1637      }
   1638      if (!CheckArgCompartment(cx, obj, set, "defineProperty", "set")) {
   1639        return false;
   1640      }
   1641    }
   1642    desc.setSetter(set);
   1643  }
   1644 
   1645  return true;
   1646 }
   1647 
   1648 /*** Debuggee resumption values and debugger error handling *****************/
   1649 
   1650 static bool GetResumptionProperty(JSContext* cx, HandleObject obj,
   1651                                  Handle<PropertyName*> name,
   1652                                  ResumeMode namedMode, ResumeMode& resumeMode,
   1653                                  MutableHandleValue vp, int* hits) {
   1654  bool found;
   1655  if (!HasProperty(cx, obj, name, &found)) {
   1656    return false;
   1657  }
   1658  if (found) {
   1659    ++*hits;
   1660    resumeMode = namedMode;
   1661    if (!GetProperty(cx, obj, obj, name, vp)) {
   1662      return false;
   1663    }
   1664  }
   1665  return true;
   1666 }
   1667 
   1668 bool js::ParseResumptionValue(JSContext* cx, HandleValue rval,
   1669                              ResumeMode& resumeMode, MutableHandleValue vp) {
   1670  if (rval.isUndefined()) {
   1671    resumeMode = ResumeMode::Continue;
   1672    vp.setUndefined();
   1673    return true;
   1674  }
   1675  if (rval.isNull()) {
   1676    resumeMode = ResumeMode::Terminate;
   1677    vp.setUndefined();
   1678    return true;
   1679  }
   1680 
   1681  int hits = 0;
   1682  if (rval.isObject()) {
   1683    RootedObject obj(cx, &rval.toObject());
   1684    if (!GetResumptionProperty(cx, obj, cx->names().return_, ResumeMode::Return,
   1685                               resumeMode, vp, &hits)) {
   1686      return false;
   1687    }
   1688    if (!GetResumptionProperty(cx, obj, cx->names().throw_, ResumeMode::Throw,
   1689                               resumeMode, vp, &hits)) {
   1690      return false;
   1691    }
   1692  }
   1693 
   1694  if (hits != 1) {
   1695    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1696                              JSMSG_DEBUG_BAD_RESUMPTION);
   1697    return false;
   1698  }
   1699  return true;
   1700 }
   1701 
   1702 static bool CheckResumptionValue(JSContext* cx, AbstractFramePtr frame,
   1703                                 const jsbytecode* pc, ResumeMode resumeMode,
   1704                                 MutableHandleValue vp) {
   1705  // Only forced returns from a frame need to be validated because forced
   1706  // throw values behave just like debuggee `throw` statements. Since
   1707  // forced-return is all custom logic within SpiderMonkey itself, we need
   1708  // our own custom validation for it to conform with what is expected.
   1709  if (resumeMode != ResumeMode::Return || !frame) {
   1710    return true;
   1711  }
   1712 
   1713  // This replicates the ECMA spec's behavior for [[Construct]] in derived
   1714  // class constructors (section 9.2.2 of ECMA262-2020), where returning a
   1715  // non-undefined primitive causes an exception tobe thrown.
   1716  if (frame.debuggerNeedsCheckPrimitiveReturn() && vp.isPrimitive()) {
   1717    if (!vp.isUndefined()) {
   1718      ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, vp,
   1719                       nullptr);
   1720      return false;
   1721    }
   1722 
   1723    RootedValue thisv(cx);
   1724    {
   1725      AutoRealm ar(cx, frame.environmentChain());
   1726      if (!GetThisValueForDebuggerFrameMaybeOptimizedOut(cx, frame, pc,
   1727                                                         &thisv)) {
   1728        return false;
   1729      }
   1730    }
   1731 
   1732    if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL)) {
   1733      return ThrowUninitializedThis(cx);
   1734    }
   1735    MOZ_ASSERT(!thisv.isMagic());
   1736 
   1737    if (!cx->compartment()->wrap(cx, &thisv)) {
   1738      return false;
   1739    }
   1740    vp.set(thisv);
   1741  }
   1742 
   1743  // Check for forcing return from a generator before the initial yield. This
   1744  // is not supported because some engine-internal code assumes a call to a
   1745  // generator will return a GeneratorObject; see bug 1477084.
   1746  if (frame.isFunctionFrame() && frame.callee()->isGenerator()) {
   1747    Rooted<AbstractGeneratorObject*> genObj(cx);
   1748    {
   1749      AutoRealm ar(cx, frame.callee());
   1750      genObj = GetGeneratorObjectForFrame(cx, frame);
   1751    }
   1752 
   1753    if (!genObj || genObj->isBeforeInitialYield()) {
   1754      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1755                                JSMSG_DEBUG_FORCED_RETURN_DISALLOWED);
   1756      return false;
   1757    }
   1758  }
   1759 
   1760  return true;
   1761 }
   1762 
   1763 // Last-minute sanity adjustments to resumption.
   1764 //
   1765 // This is called last, as we leave the debugger. It must happen outside the
   1766 // control of the uncaughtExceptionHook, because this code assumes we won't
   1767 // change our minds and continue execution--we must not close the generator
   1768 // object unless we're really going to force-return.
   1769 [[nodiscard]] static bool AdjustGeneratorResumptionValue(
   1770    JSContext* cx, AbstractFramePtr frame, ResumeMode& resumeMode,
   1771    MutableHandleValue vp) {
   1772  if (resumeMode != ResumeMode::Return && resumeMode != ResumeMode::Throw) {
   1773    return true;
   1774  }
   1775 
   1776  if (!frame) {
   1777    return true;
   1778  }
   1779  // Async modules need to be handled separately, as they do not have a callee.
   1780  // frame.callee will throw if it is called on a moduleFrame.
   1781  bool isAsyncModule = frame.isModuleFrame() && frame.script()->isAsync();
   1782  if (!frame.isFunctionFrame() && !isAsyncModule) {
   1783    return true;
   1784  }
   1785 
   1786  // Treat `{return: <value>}` like a `return` statement. Simulate what the
   1787  // debuggee would do for an ordinary `return` statement, using a few bytecode
   1788  // instructions. It's simpler to do the work manually than to count on that
   1789  // bytecode sequence existing in the debuggee, somehow jump to it, and then
   1790  // avoid re-entering the debugger from it.
   1791  //
   1792  // Similarly treat `{throw: <value>}` like a `throw` statement.
   1793  //
   1794  // Note: Async modules use the same handling as async functions.
   1795  if (frame.isFunctionFrame() && frame.callee()->isGenerator()) {
   1796    // Throw doesn't require any special processing for (async) generators.
   1797    if (resumeMode == ResumeMode::Throw) {
   1798      return true;
   1799    }
   1800 
   1801    // Forcing return from a (possibly async) generator.
   1802    Rooted<AbstractGeneratorObject*> genObj(
   1803        cx, GetGeneratorObjectForFrame(cx, frame));
   1804 
   1805    // We already went through CheckResumptionValue, which would have replaced
   1806    // this invalid resumption value with an error if we were trying to force
   1807    // return before the initial yield.
   1808    MOZ_RELEASE_ASSERT(genObj && !genObj->isBeforeInitialYield());
   1809 
   1810    // 1.  `return <value>` creates and returns a new object,
   1811    //     `{value: <value>, done: true}`.
   1812    //
   1813    // For non-async generators, the iterator result object is created in
   1814    // bytecode, so we have to simulate that here. For async generators, our
   1815    // C++ implementation of AsyncGeneratorResolve will do this. So don't do it
   1816    // twice:
   1817    if (!genObj->is<AsyncGeneratorObject>()) {
   1818      PlainObject* pair = CreateIterResultObject(cx, vp, true);
   1819      if (!pair) {
   1820        return false;
   1821      }
   1822      vp.setObject(*pair);
   1823    }
   1824 
   1825    // 2.  The generator must be closed.
   1826    genObj->setClosed(cx);
   1827 
   1828    // Async generators have additionally bookkeeping which must be adjusted
   1829    // when switching over to the closed state.
   1830    if (genObj->is<AsyncGeneratorObject>()) {
   1831      genObj->as<AsyncGeneratorObject>().setCompleted();
   1832    }
   1833  } else if (isAsyncModule || frame.callee()->isAsync()) {
   1834    if (AbstractGeneratorObject* genObj =
   1835            GetGeneratorObjectForFrame(cx, frame)) {
   1836      // Throw doesn't require any special processing for async functions when
   1837      // the internal generator object is already present.
   1838      if (resumeMode == ResumeMode::Throw) {
   1839        return true;
   1840      }
   1841 
   1842      Rooted<AsyncFunctionGeneratorObject*> generator(
   1843          cx, &genObj->as<AsyncFunctionGeneratorObject>());
   1844 
   1845      // 1.  `return <value>` fulfills and returns the async function's promise.
   1846      Rooted<PromiseObject*> promise(cx, generator->promise());
   1847      if (promise->state() == JS::PromiseState::Pending) {
   1848        if (!AsyncFunctionResolve(cx, generator, vp)) {
   1849          return false;
   1850        }
   1851      }
   1852      vp.setObject(*promise);
   1853 
   1854      // 2.  The generator must be closed.
   1855      generator->setClosed(cx);
   1856    } else {
   1857      // We're before entering the actual function code.
   1858 
   1859      // 1.  `throw <value>` creates a promise rejected with the value *vp.
   1860      // 1.  `return <value>` creates a promise resolved with the value *vp.
   1861      JSObject* promise = resumeMode == ResumeMode::Throw
   1862                              ? PromiseObject::unforgeableReject(cx, vp)
   1863                              : PromiseObject::unforgeableResolve(cx, vp);
   1864      if (!promise) {
   1865        return false;
   1866      }
   1867      vp.setObject(*promise);
   1868 
   1869      // 2.  Return normally in both cases.
   1870      resumeMode = ResumeMode::Return;
   1871    }
   1872  }
   1873 
   1874  return true;
   1875 }
   1876 
   1877 bool Debugger::processParsedHandlerResult(JSContext* cx, AbstractFramePtr frame,
   1878                                          const jsbytecode* pc, bool success,
   1879                                          ResumeMode resumeMode,
   1880                                          HandleValue value,
   1881                                          ResumeMode& resultMode,
   1882                                          MutableHandleValue vp) {
   1883  RootedValue rootValue(cx, value);
   1884  if (!success || !prepareResumption(cx, frame, pc, resumeMode, &rootValue)) {
   1885    RootedValue exceptionRv(cx);
   1886    if (!callUncaughtExceptionHandler(cx, &exceptionRv) ||
   1887        !ParseResumptionValue(cx, exceptionRv, resumeMode, &rootValue) ||
   1888        !prepareResumption(cx, frame, pc, resumeMode, &rootValue)) {
   1889      return false;
   1890    }
   1891  }
   1892 
   1893  // Since debugger hooks accumulate into the same final value handle, we
   1894  // use that to throw if multiple hooks try to set a resumption value.
   1895  if (resumeMode != ResumeMode::Continue) {
   1896    if (resultMode != ResumeMode::Continue) {
   1897      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1898                                JSMSG_DEBUG_RESUMPTION_CONFLICT);
   1899      return false;
   1900    }
   1901 
   1902    vp.set(rootValue);
   1903    resultMode = resumeMode;
   1904  }
   1905 
   1906  return true;
   1907 }
   1908 
   1909 bool Debugger::processHandlerResult(JSContext* cx, bool success, HandleValue rv,
   1910                                    AbstractFramePtr frame, jsbytecode* pc,
   1911                                    ResumeMode& resultMode,
   1912                                    MutableHandleValue vp) {
   1913  ResumeMode resumeMode = ResumeMode::Continue;
   1914  RootedValue value(cx);
   1915  if (success) {
   1916    success = ParseResumptionValue(cx, rv, resumeMode, &value);
   1917  }
   1918  return processParsedHandlerResult(cx, frame, pc, success, resumeMode, value,
   1919                                    resultMode, vp);
   1920 }
   1921 
   1922 bool Debugger::prepareResumption(JSContext* cx, AbstractFramePtr frame,
   1923                                 const jsbytecode* pc, ResumeMode& resumeMode,
   1924                                 MutableHandleValue vp) {
   1925  return unwrapDebuggeeValue(cx, vp) &&
   1926         CheckResumptionValue(cx, frame, pc, resumeMode, vp);
   1927 }
   1928 
   1929 bool Debugger::callUncaughtExceptionHandler(JSContext* cx,
   1930                                            MutableHandleValue vp) {
   1931  // Uncaught exceptions arise from Debugger code, and so we must already be in
   1932  // an NX section. This also establishes that we are already within the scope
   1933  // of an AutoDebuggerJobQueueInterruption object.
   1934  MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx, *this));
   1935 
   1936  if (cx->isExceptionPending() && uncaughtExceptionHook) {
   1937    RootedValue exc(cx);
   1938    if (!cx->getPendingException(&exc)) {
   1939      return false;
   1940    }
   1941    cx->clearPendingException();
   1942 
   1943    RootedValue fval(cx, ObjectValue(*uncaughtExceptionHook));
   1944    if (js::Call(cx, fval, object, exc, vp)) {
   1945      return true;
   1946    }
   1947  }
   1948  return false;
   1949 }
   1950 
   1951 bool Debugger::handleUncaughtException(JSContext* cx) {
   1952  RootedValue rv(cx);
   1953 
   1954  return callUncaughtExceptionHandler(cx, &rv);
   1955 }
   1956 
   1957 void Debugger::reportUncaughtException(JSContext* cx) {
   1958  // Uncaught exceptions arise from Debugger code, and so we must already be
   1959  // in an NX section.
   1960  MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx, *this));
   1961 
   1962  if (cx->isExceptionPending()) {
   1963    // We want to report the pending exception, but we want to let the
   1964    // embedding handle it however it wants to.  So pretend like we're
   1965    // starting a new script execution on our current compartment (which
   1966    // is the debugger compartment, so reported errors won't get
   1967    // reported to various onerror handlers in debuggees) and as part of
   1968    // that "execution" simply throw our exception so the embedding can
   1969    // deal.
   1970    RootedValue exn(cx);
   1971    if (cx->getPendingException(&exn)) {
   1972      // Clear the exception, because ReportErrorToGlobal will assert that
   1973      // we don't have one.
   1974      cx->clearPendingException();
   1975      ReportErrorToGlobal(cx, cx->global(), exn);
   1976    }
   1977 
   1978    // And if not, or if PrepareScriptEnvironmentAndInvoke somehow left an
   1979    // exception on cx (which it totally shouldn't do), just give up.
   1980    cx->clearPendingException();
   1981  }
   1982 }
   1983 
   1984 /*** Debuggee completion values *********************************************/
   1985 
   1986 /* static */
   1987 Completion Completion::fromJSResult(JSContext* cx, bool ok, const Value& rv) {
   1988  MOZ_ASSERT_IF(ok, !cx->isExceptionPending());
   1989 
   1990  if (ok) {
   1991    return Completion(Return(rv));
   1992  }
   1993 
   1994  if (!cx->isExceptionPending()) {
   1995    return Completion(Terminate());
   1996  }
   1997 
   1998  RootedValue exception(cx);
   1999  Rooted<SavedFrame*> stack(cx, cx->getPendingExceptionStack());
   2000  bool getSucceeded = cx->getPendingException(&exception);
   2001  cx->clearPendingException();
   2002  if (!getSucceeded) {
   2003    return Completion(Terminate());
   2004  }
   2005 
   2006  return Completion(Throw(exception, stack));
   2007 }
   2008 
   2009 /* static */
   2010 Completion Completion::fromJSFramePop(JSContext* cx, AbstractFramePtr frame,
   2011                                      const jsbytecode* pc, bool ok) {
   2012  // Only Wasm frames get a null pc.
   2013  MOZ_ASSERT_IF(!frame.isWasmDebugFrame(), pc);
   2014 
   2015  // If this isn't a generator suspension, then that's already handled above.
   2016  if (!ok || !frame.isGeneratorFrame()) {
   2017    return fromJSResult(cx, ok, frame.returnValue());
   2018  }
   2019 
   2020  // A generator is being suspended or returning.
   2021 
   2022  // Since generators are never wasm, we can assume pc is not nullptr, and
   2023  // that analyzing bytecode is meaningful.
   2024  MOZ_ASSERT(!frame.isWasmDebugFrame());
   2025 
   2026  // If we're leaving successfully at a yield opcode, we're probably
   2027  // suspending; the `isClosed()` check detects a debugger forced return from
   2028  // an `onStep` handler, which looks almost the same.
   2029  //
   2030  // GetGeneratorObjectForFrame can return nullptr even when a generator
   2031  // object does exist, if the frame is paused between the Generator and
   2032  // SetAliasedVar opcodes. But by checking the opcode first we eliminate that
   2033  // possibility, so it's fine to call genObj->isClosed().
   2034  Rooted<AbstractGeneratorObject*> generatorObj(
   2035      cx, GetGeneratorObjectForFrame(cx, frame));
   2036  switch (JSOp(*pc)) {
   2037    case JSOp::InitialYield:
   2038      MOZ_ASSERT(!generatorObj->isClosed());
   2039      return Completion(InitialYield(generatorObj));
   2040 
   2041    case JSOp::Yield:
   2042      MOZ_ASSERT(!generatorObj->isClosed());
   2043      return Completion(Yield(generatorObj, frame.returnValue()));
   2044 
   2045    case JSOp::Await:
   2046      MOZ_ASSERT(!generatorObj->isClosed());
   2047      return Completion(Await(generatorObj, frame.returnValue()));
   2048 
   2049    default:
   2050      return Completion(Return(frame.returnValue()));
   2051  }
   2052 }
   2053 
   2054 void Completion::trace(JSTracer* trc) {
   2055  variant.match([=](auto& var) { var.trace(trc); });
   2056 }
   2057 
   2058 struct MOZ_STACK_CLASS Completion::BuildValueMatcher {
   2059  JSContext* cx;
   2060  Debugger* dbg;
   2061  MutableHandleValue result;
   2062 
   2063  BuildValueMatcher(JSContext* cx, Debugger* dbg, MutableHandleValue result)
   2064      : cx(cx), dbg(dbg), result(result) {
   2065    cx->check(dbg->toJSObject());
   2066  }
   2067 
   2068  bool operator()(const Completion::Return& ret) {
   2069    Rooted<NativeObject*> obj(cx, newObject());
   2070    RootedValue retval(cx, ret.value);
   2071    if (!obj || !wrap(&retval) || !add(obj, cx->names().return_, retval)) {
   2072      return false;
   2073    }
   2074    result.setObject(*obj);
   2075    return true;
   2076  }
   2077 
   2078  bool operator()(const Completion::Throw& thr) {
   2079    Rooted<NativeObject*> obj(cx, newObject());
   2080    RootedValue exc(cx, thr.exception);
   2081    if (!obj || !wrap(&exc) || !add(obj, cx->names().throw_, exc)) {
   2082      return false;
   2083    }
   2084    if (thr.stack) {
   2085      RootedValue stack(cx, ObjectValue(*thr.stack));
   2086      if (!wrapStack(&stack) || !add(obj, cx->names().stack, stack)) {
   2087        return false;
   2088      }
   2089    }
   2090    result.setObject(*obj);
   2091    return true;
   2092  }
   2093 
   2094  bool operator()(const Completion::Terminate& term) {
   2095    result.setNull();
   2096    return true;
   2097  }
   2098 
   2099  bool operator()(const Completion::InitialYield& initialYield) {
   2100    Rooted<NativeObject*> obj(cx, newObject());
   2101    RootedValue gen(cx, ObjectValue(*initialYield.generatorObject));
   2102    if (!obj || !wrap(&gen) || !add(obj, cx->names().return_, gen) ||
   2103        !add(obj, cx->names().yield, TrueHandleValue) ||
   2104        !add(obj, cx->names().initial, TrueHandleValue)) {
   2105      return false;
   2106    }
   2107    result.setObject(*obj);
   2108    return true;
   2109  }
   2110 
   2111  bool operator()(const Completion::Yield& yield) {
   2112    Rooted<NativeObject*> obj(cx, newObject());
   2113    RootedValue iteratorResult(cx, yield.iteratorResult);
   2114    if (!obj || !wrap(&iteratorResult) ||
   2115        !add(obj, cx->names().return_, iteratorResult) ||
   2116        !add(obj, cx->names().yield, TrueHandleValue)) {
   2117      return false;
   2118    }
   2119    result.setObject(*obj);
   2120    return true;
   2121  }
   2122 
   2123  bool operator()(const Completion::Await& await) {
   2124    Rooted<NativeObject*> obj(cx, newObject());
   2125    RootedValue awaitee(cx, await.awaitee);
   2126    if (!obj || !wrap(&awaitee) || !add(obj, cx->names().return_, awaitee) ||
   2127        !add(obj, cx->names().await, TrueHandleValue)) {
   2128      return false;
   2129    }
   2130    result.setObject(*obj);
   2131    return true;
   2132  }
   2133 
   2134 private:
   2135  NativeObject* newObject() const { return NewPlainObject(cx); }
   2136 
   2137  bool add(Handle<NativeObject*> obj, PropertyName* name,
   2138           HandleValue value) const {
   2139    return NativeDefineDataProperty(cx, obj, name, value, JSPROP_ENUMERATE);
   2140  }
   2141 
   2142  bool wrap(MutableHandleValue v) const {
   2143    return dbg->wrapDebuggeeValue(cx, v);
   2144  }
   2145 
   2146  // Saved stacks are wrapped for direct consumption by debugger code.
   2147  bool wrapStack(MutableHandleValue stack) const {
   2148    return cx->compartment()->wrap(cx, stack);
   2149  }
   2150 };
   2151 
   2152 bool Completion::buildCompletionValue(JSContext* cx, Debugger* dbg,
   2153                                      MutableHandleValue result) const {
   2154  return variant.match(BuildValueMatcher(cx, dbg, result));
   2155 }
   2156 
   2157 void Completion::updateFromHookResult(ResumeMode resumeMode,
   2158                                      HandleValue value) {
   2159  switch (resumeMode) {
   2160    case ResumeMode::Continue:
   2161      // No change to how we'll resume.
   2162      break;
   2163 
   2164    case ResumeMode::Throw:
   2165      // Since this is a new exception, the stack for the old one may not apply.
   2166      // If we extend resumption values to specify stacks, we could revisit
   2167      // this.
   2168      variant = Variant(Throw(value, nullptr));
   2169      break;
   2170 
   2171    case ResumeMode::Terminate:
   2172      variant = Variant(Terminate());
   2173      break;
   2174 
   2175    case ResumeMode::Return:
   2176      variant = Variant(Return(value));
   2177      break;
   2178 
   2179    default:
   2180      MOZ_CRASH("invalid resumeMode value");
   2181  }
   2182 }
   2183 
   2184 struct MOZ_STACK_CLASS Completion::ToResumeModeMatcher {
   2185  MutableHandleValue value;
   2186  MutableHandle<SavedFrame*> exnStack;
   2187  ToResumeModeMatcher(MutableHandleValue value,
   2188                      MutableHandle<SavedFrame*> exnStack)
   2189      : value(value), exnStack(exnStack) {}
   2190 
   2191  ResumeMode operator()(const Return& ret) {
   2192    value.set(ret.value);
   2193    return ResumeMode::Return;
   2194  }
   2195 
   2196  ResumeMode operator()(const Throw& thr) {
   2197    value.set(thr.exception);
   2198    exnStack.set(thr.stack);
   2199    return ResumeMode::Throw;
   2200  }
   2201 
   2202  ResumeMode operator()(const Terminate& term) {
   2203    value.setUndefined();
   2204    return ResumeMode::Terminate;
   2205  }
   2206 
   2207  ResumeMode operator()(const InitialYield& initialYield) {
   2208    value.setObject(*initialYield.generatorObject);
   2209    return ResumeMode::Return;
   2210  }
   2211 
   2212  ResumeMode operator()(const Yield& yield) {
   2213    value.set(yield.iteratorResult);
   2214    return ResumeMode::Return;
   2215  }
   2216 
   2217  ResumeMode operator()(const Await& await) {
   2218    value.set(await.awaitee);
   2219    return ResumeMode::Return;
   2220  }
   2221 };
   2222 
   2223 void Completion::toResumeMode(ResumeMode& resumeMode, MutableHandleValue value,
   2224                              MutableHandle<SavedFrame*> exnStack) const {
   2225  resumeMode = variant.match(ToResumeModeMatcher(value, exnStack));
   2226 }
   2227 
   2228 /*** Firing debugger hooks **************************************************/
   2229 
   2230 static bool CallMethodIfPresent(JSContext* cx, HandleObject obj,
   2231                                const char* name, size_t argc, Value* argv,
   2232                                MutableHandleValue rval) {
   2233  rval.setUndefined();
   2234  JSAtom* atom = Atomize(cx, name, strlen(name));
   2235  if (!atom) {
   2236    return false;
   2237  }
   2238 
   2239  RootedId id(cx, AtomToId(atom));
   2240  RootedValue fval(cx);
   2241  if (!GetProperty(cx, obj, obj, id, &fval)) {
   2242    return false;
   2243  }
   2244 
   2245  if (!IsCallable(fval)) {
   2246    return true;
   2247  }
   2248 
   2249  InvokeArgs args(cx);
   2250  if (!args.init(cx, argc)) {
   2251    return false;
   2252  }
   2253 
   2254  for (size_t i = 0; i < argc; i++) {
   2255    args[i].set(argv[i]);
   2256  }
   2257 
   2258  rval.setObject(*obj);  // overwritten by successful Call
   2259  return js::Call(cx, fval, rval, args, rval);
   2260 }
   2261 
   2262 bool Debugger::fireDebuggerStatement(JSContext* cx, ResumeMode& resumeMode,
   2263                                     MutableHandleValue vp) {
   2264  RootedObject hook(cx, getHook(OnDebuggerStatement));
   2265  MOZ_ASSERT(hook);
   2266  MOZ_ASSERT(hook->isCallable());
   2267 
   2268  ScriptFrameIter iter(cx);
   2269  RootedValue scriptFrame(cx);
   2270  if (!getFrame(cx, iter, &scriptFrame)) {
   2271    return false;
   2272  }
   2273 
   2274  RootedValue fval(cx, ObjectValue(*hook));
   2275  RootedValue rv(cx);
   2276  bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
   2277  return processHandlerResult(cx, ok, rv, iter.abstractFramePtr(), iter.pc(),
   2278                              resumeMode, vp);
   2279 }
   2280 
   2281 bool Debugger::fireExceptionUnwind(JSContext* cx, HandleValue exc,
   2282                                   ResumeMode& resumeMode,
   2283                                   MutableHandleValue vp) {
   2284  RootedObject hook(cx, getHook(OnExceptionUnwind));
   2285  MOZ_ASSERT(hook);
   2286  MOZ_ASSERT(hook->isCallable());
   2287 
   2288  RootedValue scriptFrame(cx);
   2289  RootedValue wrappedExc(cx, exc);
   2290 
   2291  FrameIter iter(cx);
   2292  if (!getFrame(cx, iter, &scriptFrame) ||
   2293      !wrapDebuggeeValue(cx, &wrappedExc)) {
   2294    return false;
   2295  }
   2296 
   2297  RootedValue fval(cx, ObjectValue(*hook));
   2298  RootedValue rv(cx);
   2299  bool ok = js::Call(cx, fval, object, scriptFrame, wrappedExc, &rv);
   2300  return processHandlerResult(cx, ok, rv, iter.abstractFramePtr(), iter.pc(),
   2301                              resumeMode, vp);
   2302 }
   2303 
   2304 bool Debugger::fireEnterFrame(JSContext* cx, ResumeMode& resumeMode,
   2305                              MutableHandleValue vp) {
   2306  RootedObject hook(cx, getHook(OnEnterFrame));
   2307  MOZ_ASSERT(hook);
   2308  MOZ_ASSERT(hook->isCallable());
   2309 
   2310  RootedValue scriptFrame(cx);
   2311 
   2312  FrameIter iter(cx);
   2313 
   2314 #if DEBUG
   2315  // Assert that the hook won't be able to re-enter the generator.
   2316  if (iter.hasScript() && JSOp(*iter.pc()) == JSOp::AfterYield) {
   2317    AutoRealm ar(cx, iter.script());
   2318    auto* genObj = GetGeneratorObjectForFrame(cx, iter.abstractFramePtr());
   2319    MOZ_ASSERT(genObj->isRunning());
   2320  }
   2321 #endif
   2322 
   2323  if (!getFrame(cx, iter, &scriptFrame)) {
   2324    return false;
   2325  }
   2326 
   2327  RootedValue fval(cx, ObjectValue(*hook));
   2328  RootedValue rv(cx);
   2329  bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
   2330 
   2331  return processHandlerResult(cx, ok, rv, iter.abstractFramePtr(), iter.pc(),
   2332                              resumeMode, vp);
   2333 }
   2334 
   2335 bool Debugger::fireNativeCall(JSContext* cx, const CallArgs& args,
   2336                              CallReason reason, ResumeMode& resumeMode,
   2337                              MutableHandleValue vp) {
   2338  RootedObject hook(cx, getHook(OnNativeCall));
   2339  MOZ_ASSERT(hook);
   2340  MOZ_ASSERT(hook->isCallable());
   2341 
   2342  RootedValue fval(cx, ObjectValue(*hook));
   2343  RootedValue calleeval(cx, args.calleev());
   2344  if (!wrapDebuggeeValue(cx, &calleeval)) {
   2345    return false;
   2346  }
   2347 
   2348  JSAtom* reasonAtom = nullptr;
   2349  switch (reason) {
   2350    case CallReason::Call:
   2351      reasonAtom = cx->names().call;
   2352      break;
   2353    case CallReason::CallContent:
   2354      reasonAtom = cx->names().call;
   2355      break;
   2356    case CallReason::FunCall:
   2357      reasonAtom = cx->names().call;
   2358      break;
   2359    case CallReason::Getter:
   2360      reasonAtom = cx->names().get;
   2361      break;
   2362    case CallReason::Setter:
   2363      reasonAtom = cx->names().set;
   2364      break;
   2365  }
   2366  MOZ_ASSERT(AtomIsMarked(cx->zone(), reasonAtom));
   2367 
   2368  RootedValue reasonval(cx, StringValue(reasonAtom));
   2369 
   2370  bool ok = false;
   2371  RootedValue rv(cx);
   2372  if (inspectNativeCallArguments) {
   2373    RootedValue thisVal(cx, args.thisv());
   2374    // Ignore anything that may make wrapDebuggeeValue to throw
   2375    if (thisVal.isMagic() && thisVal.whyMagic() != JS_MISSING_ARGUMENTS &&
   2376        thisVal.whyMagic() != JS_UNINITIALIZED_LEXICAL) {
   2377      thisVal.setMagic(JS_OPTIMIZED_OUT);
   2378    }
   2379    if (!wrapDebuggeeValue(cx, &thisVal)) {
   2380      return false;
   2381    }
   2382 
   2383    unsigned arrsize = args.length();
   2384    Rooted<ArrayObject*> arrobj(cx, NewDenseFullyAllocatedArray(cx, arrsize));
   2385    if (!arrobj) {
   2386      return false;
   2387    }
   2388    arrobj->ensureDenseInitializedLength(0, arrsize);
   2389    for (unsigned i = 0; i < arrsize; i++) {
   2390      RootedValue v(cx, args.get(i));
   2391      if (!wrapDebuggeeValue(cx, &v)) {
   2392        return false;
   2393      }
   2394      arrobj->setDenseElement(i, v);
   2395    }
   2396    RootedValue arrayval(cx, ObjectValue(*arrobj));
   2397    if (!wrapDebuggeeValue(cx, &arrayval)) {
   2398      return false;
   2399    }
   2400 
   2401    FixedInvokeArgs<4> iargs(cx);
   2402    iargs[0].set(calleeval);
   2403    iargs[1].set(reasonval);
   2404    iargs[2].set(thisVal);
   2405    iargs[3].set(arrayval);
   2406 
   2407    RootedValue thisv(cx, ObjectOrNullValue(object));
   2408    ok = js::Call(cx, fval, thisv, iargs, &rv);
   2409  } else {
   2410    ok = js::Call(cx, fval, object, calleeval, reasonval, &rv);
   2411  }
   2412 
   2413  return processHandlerResult(cx, ok, rv, NullFramePtr(), nullptr, resumeMode,
   2414                              vp);
   2415 }
   2416 
   2417 bool Debugger::fireNewScript(JSContext* cx,
   2418                             Handle<DebuggerScriptReferent> scriptReferent) {
   2419  RootedObject hook(cx, getHook(OnNewScript));
   2420  MOZ_ASSERT(hook);
   2421  MOZ_ASSERT(hook->isCallable());
   2422 
   2423  JSObject* dsobj = wrapVariantReferent(cx, scriptReferent);
   2424  if (!dsobj) {
   2425    return false;
   2426  }
   2427 
   2428  RootedValue fval(cx, ObjectValue(*hook));
   2429  RootedValue dsval(cx, ObjectValue(*dsobj));
   2430  RootedValue rv(cx);
   2431  return js::Call(cx, fval, object, dsval, &rv) || handleUncaughtException(cx);
   2432 }
   2433 
   2434 bool Debugger::fireOnGarbageCollectionHook(
   2435    JSContext* cx, const JS::dbg::GarbageCollectionEvent::Ptr& gcData) {
   2436  MOZ_ASSERT(observedGC(gcData->majorGCNumber()));
   2437  observedGCs.remove(gcData->majorGCNumber());
   2438 
   2439  RootedObject hook(cx, getHook(OnGarbageCollection));
   2440  MOZ_ASSERT(hook);
   2441  MOZ_ASSERT(hook->isCallable());
   2442 
   2443  JSObject* dataObj = gcData->toJSObject(cx);
   2444  if (!dataObj) {
   2445    return false;
   2446  }
   2447 
   2448  RootedValue fval(cx, ObjectValue(*hook));
   2449  RootedValue dataVal(cx, ObjectValue(*dataObj));
   2450  RootedValue rv(cx);
   2451  return js::Call(cx, fval, object, dataVal, &rv) ||
   2452         handleUncaughtException(cx);
   2453 }
   2454 
   2455 template <typename HookIsEnabledFun /* bool (Debugger*) */,
   2456          typename FireHookFun /* bool (Debugger*) */>
   2457 /* static */
   2458 void Debugger::dispatchQuietHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
   2459                                 FireHookFun fireHook) {
   2460  DebuggerList<HookIsEnabledFun> debuggerList(cx, hookIsEnabled);
   2461 
   2462  if (!debuggerList.init(cx)) {
   2463    // init may fail due to OOM. This OOM is not handlable at the
   2464    // callsites of dispatchQuietHook in the engine.
   2465    cx->clearPendingException();
   2466    return;
   2467  }
   2468 
   2469  debuggerList.dispatchQuietHook(cx, fireHook);
   2470 }
   2471 
   2472 template <typename HookIsEnabledFun /* bool (Debugger*) */, typename FireHookFun /* bool (Debugger*, ResumeMode&, MutableHandleValue vp) */>
   2473 /* static */
   2474 bool Debugger::dispatchResumptionHook(JSContext* cx, AbstractFramePtr frame,
   2475                                      HookIsEnabledFun hookIsEnabled,
   2476                                      FireHookFun fireHook) {
   2477  DebuggerList<HookIsEnabledFun> debuggerList(cx, hookIsEnabled);
   2478 
   2479  if (!debuggerList.init(cx)) {
   2480    return false;
   2481  }
   2482 
   2483  return debuggerList.dispatchResumptionHook(cx, frame, fireHook);
   2484 }
   2485 
   2486 // Maximum length for source URLs that can be remembered.
   2487 static const size_t SourceURLMaxLength = 1024;
   2488 
   2489 // Maximum number of source URLs that can be remembered in a realm.
   2490 static const size_t SourceURLRealmLimit = 100;
   2491 
   2492 static bool RememberSourceURL(JSContext* cx, HandleScript script) {
   2493  cx->check(script);
   2494 
   2495  // Sources introduced dynamically are not remembered.
   2496  if (script->sourceObject()->unwrappedIntroductionScript()) {
   2497    return true;
   2498  }
   2499 
   2500  const char* filename = script->filename();
   2501  if (!filename ||
   2502      strnlen(filename, SourceURLMaxLength + 1) > SourceURLMaxLength) {
   2503    return true;
   2504  }
   2505 
   2506  Rooted<ArrayObject*> holder(cx, script->global().getSourceURLsHolder());
   2507  if (!holder) {
   2508    holder = NewDenseEmptyArray(cx);
   2509    if (!holder) {
   2510      return false;
   2511    }
   2512    script->global().setSourceURLsHolder(holder);
   2513  }
   2514 
   2515  if (holder->length() >= SourceURLRealmLimit) {
   2516    return true;
   2517  }
   2518 
   2519  RootedString filenameString(cx,
   2520                              AtomizeUTF8Chars(cx, filename, strlen(filename)));
   2521  if (!filenameString) {
   2522    return false;
   2523  }
   2524 
   2525  // The source URLs holder never escapes to script, so we can treat it as a
   2526  // newborn array for the purpose of adding elements.
   2527  return NewbornArrayPush(cx, holder, StringValue(filenameString));
   2528 }
   2529 
   2530 void DebugAPI::onNewScript(JSContext* cx, HandleScript script) {
   2531  if (!script->realm()->isDebuggee()) {
   2532    // Remember the URLs associated with scripts in non-system realms,
   2533    // in case the debugger is attached later.
   2534    if (!script->realm()->isSystem()) {
   2535      if (!RememberSourceURL(cx, script)) {
   2536        cx->clearPendingException();
   2537      }
   2538    }
   2539    return;
   2540  }
   2541 
   2542  Debugger::dispatchQuietHook(
   2543      cx,
   2544      [script](Debugger* dbg) -> bool {
   2545        return dbg->observesNewScript() && dbg->observesScript(script);
   2546      },
   2547      [&](Debugger* dbg) -> bool {
   2548        BaseScript* base = script.get();
   2549        Rooted<DebuggerScriptReferent> scriptReferent(cx, base);
   2550        return dbg->fireNewScript(cx, scriptReferent);
   2551      });
   2552 }
   2553 
   2554 /* static */
   2555 void DebugAPI::onSuspendWasmFrame(JSContext* cx, wasm::DebugFrame* debugFrame) {
   2556  AbstractFramePtr frame = AbstractFramePtr(debugFrame);
   2557  JS::AutoAssertNoGC nogc;
   2558  for (Realm::DebuggerVectorEntry& entry : frame.global()->getDebuggers(nogc)) {
   2559    Debugger* dbg = entry.dbg;
   2560    if (Debugger::FrameMap::Ptr p = dbg->frames.lookup(frame)) {
   2561      DebuggerFrame* frameObj = p->value();
   2562      frameObj->suspendWasmFrame(cx->gcContext());
   2563    }
   2564  }
   2565 }
   2566 
   2567 /* static */
   2568 void DebugAPI::onResumeWasmFrame(JSContext* cx, const FrameIter& iter) {
   2569  AbstractFramePtr frame = iter.abstractFramePtr();
   2570  MOZ_RELEASE_ASSERT(frame.isWasmDebugFrame());
   2571  JS::AutoAssertNoGC nogc;
   2572  for (Realm::DebuggerVectorEntry& entry : frame.global()->getDebuggers(nogc)) {
   2573    Debugger* dbg = entry.dbg;
   2574    if (Debugger::FrameMap::Ptr p = dbg->frames.lookup(frame)) {
   2575      DebuggerFrame* frameObj = p->value();
   2576      AutoEnterOOMUnsafeRegion oomUnsafe;
   2577      if (!frameObj->resume(iter)) {
   2578        oomUnsafe.crash("DebugAPI::onResumeWasmFrame");
   2579      }
   2580    }
   2581  }
   2582 }
   2583 
   2584 void DebugAPI::slowPathOnNewWasmInstance(
   2585    JSContext* cx, Handle<WasmInstanceObject*> wasmInstance) {
   2586  Debugger::dispatchQuietHook(
   2587      cx,
   2588      [wasmInstance](Debugger* dbg) -> bool {
   2589        return dbg->observesNewScript() &&
   2590               dbg->observesGlobal(&wasmInstance->global());
   2591      },
   2592      [&](Debugger* dbg) -> bool {
   2593        Rooted<DebuggerScriptReferent> scriptReferent(cx, wasmInstance.get());
   2594        return dbg->fireNewScript(cx, scriptReferent);
   2595      });
   2596 }
   2597 
   2598 /* static */
   2599 bool DebugAPI::onTrap(JSContext* cx) {
   2600  FrameIter iter(cx);
   2601  JS::AutoSaveExceptionState savedExc(cx);
   2602  Rooted<GlobalObject*> global(cx);
   2603  BreakpointSite* site;
   2604  bool isJS;       // true when iter.hasScript(), false when iter.isWasm()
   2605  jsbytecode* pc;  // valid when isJS == true
   2606  uint32_t bytecodeOffset;  // valid when isJS == false
   2607  if (iter.hasScript()) {
   2608    RootedScript script(cx, iter.script());
   2609    MOZ_ASSERT(script->isDebuggee());
   2610    global.set(&script->global());
   2611    isJS = true;
   2612    pc = iter.pc();
   2613    bytecodeOffset = 0;
   2614    site = DebugScript::getBreakpointSite(script, pc);
   2615  } else {
   2616    MOZ_ASSERT(iter.isWasm());
   2617    global.set(&iter.wasmInstance()->object()->global());
   2618    isJS = false;
   2619    pc = nullptr;
   2620    bytecodeOffset = iter.wasmBytecodeOffset();
   2621    site = iter.wasmInstance()->debug().getBreakpointSite(bytecodeOffset);
   2622  }
   2623 
   2624  // Build list of breakpoint handlers.
   2625  //
   2626  // This does not need to be rooted: since the JSScript/WasmInstance is on the
   2627  // stack, the Breakpoints will not be GC'd. However, they may be deleted, and
   2628  // we check for that case below.
   2629  Vector<Breakpoint*> triggered(cx);
   2630  for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
   2631    if (!triggered.append(bp)) {
   2632      return false;
   2633    }
   2634  }
   2635 
   2636  ResumeMode resumeMode = ResumeMode::Continue;
   2637  RootedValue rval(cx);
   2638 
   2639  if (triggered.length() > 0) {
   2640    // Preserve the debuggee's microtask event queue while we run the hooks, so
   2641    // the debugger's microtask checkpoints don't run from the debuggee's
   2642    // microtasks, and vice versa.
   2643    JS::AutoDebuggerJobQueueInterruption adjqi;
   2644    if (!adjqi.init(cx)) {
   2645      return false;
   2646    }
   2647 
   2648    for (Breakpoint* bp : triggered) {
   2649      // Handlers can clear breakpoints. Check that bp still exists.
   2650      if (!site || !site->hasBreakpoint(bp)) {
   2651        continue;
   2652      }
   2653 
   2654      // We have to check whether dbg is debugging this global here: a
   2655      // breakpoint handler can disable other Debuggers or remove debuggees.
   2656      Debugger* dbg = bp->debugger;
   2657      if (dbg->debuggees.has(global)) {
   2658        EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
   2659 
   2660        bool result = dbg->enterDebuggerHook(cx, [&]() -> bool {
   2661          RootedValue scriptFrame(cx);
   2662          if (!dbg->getFrame(cx, iter, &scriptFrame)) {
   2663            return false;
   2664          }
   2665 
   2666          // Re-wrap the breakpoint's handler for the Debugger's compartment.
   2667          // When the handler and the Debugger are in the same compartment (the
   2668          // usual case), this actually unwraps it, but there's no requirement
   2669          // that they be in the same compartment, so we can't be sure.
   2670          Rooted<JSObject*> handler(cx, bp->handler);
   2671          if (!cx->compartment()->wrap(cx, &handler)) {
   2672            return false;
   2673          }
   2674 
   2675          RootedValue rv(cx);
   2676          bool ok = CallMethodIfPresent(cx, handler, "hit", 1,
   2677                                        scriptFrame.address(), &rv);
   2678 
   2679          return dbg->processHandlerResult(cx, ok, rv, iter.abstractFramePtr(),
   2680                                           iter.pc(), resumeMode, &rval);
   2681        });
   2682        adjqi.runJobs();
   2683 
   2684        if (!result) {
   2685          return false;
   2686        }
   2687 
   2688        // Calling JS code invalidates site. Reload it.
   2689        if (isJS) {
   2690          site = DebugScript::getBreakpointSite(iter.script(), pc);
   2691        } else {
   2692          site = iter.wasmInstance()->debug().getBreakpointSite(bytecodeOffset);
   2693        }
   2694      }
   2695    }
   2696  }
   2697 
   2698  if (!ApplyFrameResumeMode(cx, iter.abstractFramePtr(), resumeMode, rval)) {
   2699    savedExc.drop();
   2700    return false;
   2701  }
   2702  return true;
   2703 }
   2704 
   2705 /* static */
   2706 bool DebugAPI::onSingleStep(JSContext* cx) {
   2707  FrameIter iter(cx);
   2708 
   2709  // We may be stepping over a JSOp::Exception, that pushes the context's
   2710  // pending exception for a 'catch' clause to handle. Don't let the onStep
   2711  // handlers mess with that (other than by returning a resumption value).
   2712  JS::AutoSaveExceptionState savedExc(cx);
   2713 
   2714  // Build list of Debugger.Frame instances referring to this frame with
   2715  // onStep handlers.
   2716  Rooted<Debugger::DebuggerFrameVector> frames(cx);
   2717  if (!Debugger::getDebuggerFrames(iter.abstractFramePtr(), &frames)) {
   2718    ReportOutOfMemory(cx);
   2719    return false;
   2720  }
   2721 
   2722 #ifdef DEBUG
   2723  // Validate the single-step count on this frame's script, to ensure that
   2724  // we're not receiving traps we didn't ask for. Even when frames is
   2725  // non-empty (and thus we know this trap was requested), do the check
   2726  // anyway, to make sure the count has the correct non-zero value.
   2727  //
   2728  // The converse --- ensuring that we do receive traps when we should --- can
   2729  // be done with unit tests.
   2730  if (iter.hasScript()) {
   2731    uint32_t liveStepperCount = 0;
   2732    uint32_t suspendedStepperCount = 0;
   2733    JSScript* trappingScript = iter.script();
   2734    JS::AutoAssertNoGC nogc;
   2735    for (Realm::DebuggerVectorEntry& entry : cx->global()->getDebuggers(nogc)) {
   2736      Debugger* dbg = entry.dbg;
   2737      for (Debugger::FrameMap::Range r = dbg->frames.all(); !r.empty();
   2738           r.popFront()) {
   2739        AbstractFramePtr frame = r.front().key();
   2740        NativeObject* frameobj = r.front().value();
   2741        if (frame.isWasmDebugFrame()) {
   2742          continue;
   2743        }
   2744        if (frame.script() == trappingScript &&
   2745            !frameobj->getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT)
   2746                 .isUndefined()) {
   2747          liveStepperCount++;
   2748        }
   2749      }
   2750 
   2751      // Also count hooks set on suspended generator frames.
   2752      for (Debugger::GeneratorWeakMap::Range r = dbg->generatorFrames.all();
   2753           !r.empty(); r.popFront()) {
   2754        AbstractGeneratorObject& genObj = *r.front().key();
   2755        DebuggerFrame& frameObj = *r.front().value();
   2756        MOZ_ASSERT(&frameObj.unwrappedGenerator() == &genObj);
   2757 
   2758        // Live Debugger.Frames were already counted in dbg->frames loop.
   2759        if (frameObj.isOnStack()) {
   2760          continue;
   2761        }
   2762 
   2763        // A closed generator no longer has a callee so it will not be able to
   2764        // compare with the trappingScript.
   2765        if (genObj.isClosed()) {
   2766          continue;
   2767        }
   2768 
   2769        // If a frame isn't live, but it has an entry in generatorFrames,
   2770        // it had better be suspended.
   2771        MOZ_ASSERT(genObj.isSuspended());
   2772 
   2773        if (genObj.callee().hasBaseScript() &&
   2774            genObj.callee().baseScript() == trappingScript &&
   2775            !frameObj.getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT)
   2776                 .isUndefined()) {
   2777          suspendedStepperCount++;
   2778        }
   2779      }
   2780    }
   2781 
   2782    MOZ_ASSERT(liveStepperCount + suspendedStepperCount ==
   2783               DebugScript::getStepperCount(trappingScript));
   2784  }
   2785 #endif
   2786 
   2787  RootedValue rval(cx);
   2788  ResumeMode resumeMode = ResumeMode::Continue;
   2789 
   2790  if (frames.length() > 0) {
   2791    // Preserve the debuggee's microtask event queue while we run the hooks, so
   2792    // the debugger's microtask checkpoints don't run from the debuggee's
   2793    // microtasks, and vice versa.
   2794    JS::AutoDebuggerJobQueueInterruption adjqi;
   2795    if (!adjqi.init(cx)) {
   2796      return false;
   2797    }
   2798 
   2799    // Call onStep for frames that have the handler set.
   2800    for (size_t i = 0; i < frames.length(); i++) {
   2801      Handle<DebuggerFrame*> frame = frames[i];
   2802      OnStepHandler* handler = frame->onStepHandler();
   2803      if (!handler) {
   2804        continue;
   2805      }
   2806 
   2807      Debugger* dbg = frame->owner();
   2808      EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
   2809 
   2810      bool result = dbg->enterDebuggerHook(cx, [&]() -> bool {
   2811        ResumeMode nextResumeMode = ResumeMode::Continue;
   2812        RootedValue nextValue(cx);
   2813 
   2814        bool success = handler->onStep(cx, frame, nextResumeMode, &nextValue);
   2815        return dbg->processParsedHandlerResult(
   2816            cx, iter.abstractFramePtr(), iter.pc(), success, nextResumeMode,
   2817            nextValue, resumeMode, &rval);
   2818      });
   2819      adjqi.runJobs();
   2820 
   2821      if (!result) {
   2822        return false;
   2823      }
   2824    }
   2825  }
   2826 
   2827  if (!ApplyFrameResumeMode(cx, iter.abstractFramePtr(), resumeMode, rval)) {
   2828    savedExc.drop();
   2829    return false;
   2830  }
   2831  return true;
   2832 }
   2833 
   2834 bool Debugger::fireNewGlobalObject(JSContext* cx,
   2835                                   Handle<GlobalObject*> global) {
   2836  RootedObject hook(cx, getHook(OnNewGlobalObject));
   2837  MOZ_ASSERT(hook);
   2838  MOZ_ASSERT(hook->isCallable());
   2839 
   2840  RootedValue wrappedGlobal(cx, ObjectValue(*global));
   2841  if (!wrapDebuggeeValue(cx, &wrappedGlobal)) {
   2842    return false;
   2843  }
   2844 
   2845  // onNewGlobalObject is infallible, and thus is only allowed to return
   2846  // undefined as a resumption value. If it returns anything else, we throw.
   2847  // And if that happens, or if the hook itself throws, we invoke the
   2848  // uncaughtExceptionHook so that we never leave an exception pending on the
   2849  // cx. This allows JS_NewGlobalObject to avoid handling failures from
   2850  // debugger hooks.
   2851  RootedValue rv(cx);
   2852  RootedValue fval(cx, ObjectValue(*hook));
   2853  bool ok = js::Call(cx, fval, object, wrappedGlobal, &rv);
   2854  if (ok && !rv.isUndefined()) {
   2855    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2856                              JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);
   2857    ok = false;
   2858  }
   2859 
   2860  return ok || handleUncaughtException(cx);
   2861 }
   2862 
   2863 void DebugAPI::slowPathOnNewGlobalObject(JSContext* cx,
   2864                                         Handle<GlobalObject*> global) {
   2865  MOZ_ASSERT(!cx->runtime()->onNewGlobalObjectWatchers().isEmpty());
   2866  if (global->realm()->creationOptions().invisibleToDebugger()) {
   2867    return;
   2868  }
   2869 
   2870  // Make a copy of the runtime's onNewGlobalObjectWatchers before running the
   2871  // handlers. Since one Debugger's handler can disable another's, the list
   2872  // can be mutated while we're walking it.
   2873  RootedObjectVector watchers(cx);
   2874  for (auto& dbg : cx->runtime()->onNewGlobalObjectWatchers()) {
   2875    MOZ_ASSERT(dbg.observesNewGlobalObject());
   2876    JSObject* obj = dbg.object;
   2877    JS::ExposeObjectToActiveJS(obj);
   2878    if (!watchers.append(obj)) {
   2879      if (cx->isExceptionPending()) {
   2880        cx->clearPendingException();
   2881      }
   2882      return;
   2883    }
   2884  }
   2885 
   2886  // Preserve the debuggee's microtask event queue while we run the hooks, so
   2887  // the debugger's microtask checkpoints don't run from the debuggee's
   2888  // microtasks, and vice versa.
   2889  JS::AutoDebuggerJobQueueInterruption adjqi;
   2890  if (!adjqi.init(cx)) {
   2891    cx->clearPendingException();
   2892    return;
   2893  }
   2894 
   2895  for (size_t i = 0; i < watchers.length(); i++) {
   2896    Debugger* dbg = Debugger::fromJSObject(watchers[i]);
   2897    EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
   2898 
   2899    if (dbg->observesNewGlobalObject()) {
   2900      bool result = dbg->enterDebuggerHook(
   2901          cx, [&]() -> bool { return dbg->fireNewGlobalObject(cx, global); });
   2902      adjqi.runJobs();
   2903 
   2904      if (!result) {
   2905        // Like other quiet hooks using dispatchQuietHook, this hook
   2906        // silently ignores all errors that propagate out of it and aren't
   2907        // already handled by the hook error reporting.
   2908        cx->clearPendingException();
   2909        break;
   2910      }
   2911    }
   2912  }
   2913  MOZ_ASSERT(!cx->isExceptionPending());
   2914 }
   2915 
   2916 /* static */
   2917 void DebugAPI::slowPathOnGeneratorClosed(JSContext* cx,
   2918                                         AbstractGeneratorObject* genObj) {
   2919  JS::AutoAssertNoGC nogc;
   2920  for (Realm::DebuggerVectorEntry& entry : cx->global()->getDebuggers(nogc)) {
   2921    Debugger* dbg = entry.dbg;
   2922    if (Debugger::GeneratorWeakMap::Ptr frameEntry =
   2923            dbg->generatorFrames.lookup(genObj)) {
   2924      DebuggerFrame* frameObj = frameEntry->value();
   2925      frameObj->onGeneratorClosed(cx->gcContext());
   2926    }
   2927  }
   2928 }
   2929 
   2930 /* static */
   2931 void DebugAPI::slowPathNotifyParticipatesInGC(uint64_t majorGCNumber,
   2932                                              Realm::DebuggerVector& dbgs,
   2933                                              const JS::AutoRequireNoGC& nogc) {
   2934  for (Realm::DebuggerVector::Range r = dbgs.all(); !r.empty(); r.popFront()) {
   2935    if (!r.front().dbg.unbarrieredGet()->debuggeeIsBeingCollected(
   2936            majorGCNumber)) {
   2937 #ifdef DEBUG
   2938      fprintf(stderr,
   2939              "OOM while notifying observing Debuggers of a GC: The "
   2940              "onGarbageCollection\n"
   2941              "hook will not be fired for this GC for some Debuggers!\n");
   2942 #endif
   2943      return;
   2944    }
   2945  }
   2946 }
   2947 
   2948 /* static */
   2949 Maybe<double> DebugAPI::allocationSamplingProbability(GlobalObject* global) {
   2950  JS::AutoAssertNoGC nogc;
   2951  Realm::DebuggerVector& dbgs = global->getDebuggers(nogc);
   2952  if (dbgs.empty()) {
   2953    return Nothing();
   2954  }
   2955 
   2956  DebugOnly<Realm::DebuggerVectorEntry*> begin = dbgs.begin();
   2957 
   2958  double probability = 0;
   2959  bool foundAnyDebuggers = false;
   2960  for (auto p = dbgs.begin(); p < dbgs.end(); p++) {
   2961    // The set of debuggers had better not change while we're iterating,
   2962    // such that the vector gets reallocated.
   2963    MOZ_ASSERT(dbgs.begin() == begin);
   2964    // Use unbarrieredGet() to prevent triggering read barrier while collecting,
   2965    // this is safe as long as dbgp does not escape.
   2966    Debugger* dbgp = p->dbg.unbarrieredGet();
   2967 
   2968    if (dbgp->trackingAllocationSites) {
   2969      foundAnyDebuggers = true;
   2970      probability = std::max(dbgp->allocationSamplingProbability, probability);
   2971    }
   2972  }
   2973 
   2974  return foundAnyDebuggers ? Some(probability) : Nothing();
   2975 }
   2976 
   2977 /* static */
   2978 bool DebugAPI::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj,
   2979                                           Handle<SavedFrame*> frame,
   2980                                           mozilla::TimeStamp when,
   2981                                           Realm::DebuggerVector& dbgs,
   2982                                           const gc::AutoSuppressGC& nogc) {
   2983  MOZ_ASSERT(!dbgs.empty());
   2984  mozilla::DebugOnly<Realm::DebuggerVectorEntry*> begin = dbgs.begin();
   2985 
   2986  // GC is suppressed so we can iterate over the debuggers; appendAllocationSite
   2987  // calls Compartment::wrap, and thus could GC.
   2988 
   2989  for (auto p = dbgs.begin(); p < dbgs.end(); p++) {
   2990    // The set of debuggers had better not change while we're iterating,
   2991    // such that the vector gets reallocated.
   2992    MOZ_ASSERT(dbgs.begin() == begin);
   2993 
   2994    if (p->dbg->trackingAllocationSites &&
   2995        !p->dbg->appendAllocationSite(cx, obj, frame, when)) {
   2996      return false;
   2997    }
   2998  }
   2999 
   3000  return true;
   3001 }
   3002 
   3003 bool Debugger::isDebuggeeUnbarriered(const Realm* realm) const {
   3004  MOZ_ASSERT(realm);
   3005  return realm->isDebuggee() &&
   3006         debuggees.has(realm->unsafeUnbarrieredMaybeGlobal());
   3007 }
   3008 
   3009 bool Debugger::appendAllocationSite(JSContext* cx, HandleObject obj,
   3010                                    Handle<SavedFrame*> frame,
   3011                                    mozilla::TimeStamp when) {
   3012  MOZ_ASSERT(trackingAllocationSites);
   3013 
   3014  AutoRealm ar(cx, object);
   3015  RootedObject wrappedFrame(cx, frame);
   3016  if (!cx->compartment()->wrap(cx, &wrappedFrame)) {
   3017    return false;
   3018  }
   3019 
   3020  auto className = obj->getClass()->name;
   3021  auto size =
   3022      JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf);
   3023  auto inNursery = gc::IsInsideNursery(obj);
   3024 
   3025  if (!allocationsLog.emplaceBack(wrappedFrame, when, className, size,
   3026                                  inNursery)) {
   3027    ReportOutOfMemory(cx);
   3028    return false;
   3029  }
   3030 
   3031  if (allocationsLog.length() > maxAllocationsLogLength) {
   3032    allocationsLog.popFront();
   3033    MOZ_ASSERT(allocationsLog.length() == maxAllocationsLogLength);
   3034    allocationsLogOverflowed = true;
   3035  }
   3036 
   3037  return true;
   3038 }
   3039 
   3040 bool Debugger::firePromiseHook(JSContext* cx, Hook hook, HandleObject promise) {
   3041  MOZ_ASSERT(hook == OnNewPromise || hook == OnPromiseSettled);
   3042 
   3043  RootedObject hookObj(cx, getHook(hook));
   3044  MOZ_ASSERT(hookObj);
   3045  MOZ_ASSERT(hookObj->isCallable());
   3046 
   3047  RootedValue dbgObj(cx, ObjectValue(*promise));
   3048  if (!wrapDebuggeeValue(cx, &dbgObj)) {
   3049    return false;
   3050  }
   3051 
   3052  // Like onNewGlobalObject, the Promise hooks are infallible and the comments
   3053  // in |Debugger::fireNewGlobalObject| apply here as well.
   3054  RootedValue fval(cx, ObjectValue(*hookObj));
   3055  RootedValue rv(cx);
   3056  bool ok = js::Call(cx, fval, object, dbgObj, &rv);
   3057  if (ok && !rv.isUndefined()) {
   3058    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3059                              JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);
   3060    ok = false;
   3061  }
   3062 
   3063  return ok || handleUncaughtException(cx);
   3064 }
   3065 
   3066 /* static */
   3067 void Debugger::slowPathPromiseHook(JSContext* cx, Hook hook,
   3068                                   Handle<PromiseObject*> promise) {
   3069  MOZ_ASSERT(hook == OnNewPromise || hook == OnPromiseSettled);
   3070 
   3071  if (hook == OnPromiseSettled) {
   3072    // We should be in the right compartment, but for simplicity always enter
   3073    // the promise's realm below.
   3074    cx->check(promise);
   3075  }
   3076 
   3077  AutoRealm ar(cx, promise);
   3078 
   3079  Debugger::dispatchQuietHook(
   3080      cx, [hook](Debugger* dbg) -> bool { return dbg->getHook(hook); },
   3081      [&](Debugger* dbg) -> bool {
   3082        return dbg->firePromiseHook(cx, hook, promise);
   3083      });
   3084 }
   3085 
   3086 /* static */
   3087 void DebugAPI::slowPathOnNewPromise(JSContext* cx,
   3088                                    Handle<PromiseObject*> promise) {
   3089  Debugger::slowPathPromiseHook(cx, Debugger::OnNewPromise, promise);
   3090 }
   3091 
   3092 /* static */
   3093 void DebugAPI::slowPathOnPromiseSettled(JSContext* cx,
   3094                                        Handle<PromiseObject*> promise) {
   3095  Debugger::slowPathPromiseHook(cx, Debugger::OnPromiseSettled, promise);
   3096 }
   3097 
   3098 /*** Debugger code invalidation for observing execution *********************/
   3099 
   3100 class MOZ_RAII ExecutionObservableRealms
   3101    : public DebugAPI::ExecutionObservableSet {
   3102  HashSet<Realm*> realms_;
   3103  HashSet<Zone*> zones_;
   3104 
   3105 public:
   3106  explicit ExecutionObservableRealms(JSContext* cx) : realms_(cx), zones_(cx) {}
   3107 
   3108  bool add(Realm* realm) {
   3109    return realms_.put(realm) && zones_.put(realm->zone());
   3110  }
   3111 
   3112  using RealmRange = HashSet<Realm*>::Range;
   3113  const HashSet<Realm*>* realms() const { return &realms_; }
   3114 
   3115  const HashSet<Zone*>* zones() const override { return &zones_; }
   3116  bool shouldRecompileOrInvalidate(JSScript* script) const override {
   3117    return script->hasBaselineScript() && realms_.has(script->realm());
   3118  }
   3119  bool shouldMarkAsDebuggee(FrameIter& iter) const override {
   3120    // AbstractFramePtr can't refer to non-remateralized Ion frames or
   3121    // non-debuggee wasm frames, so if iter refers to one such, we know we
   3122    // don't match.
   3123    return iter.hasUsableAbstractFramePtr() && realms_.has(iter.realm());
   3124  }
   3125 };
   3126 
   3127 // Given a particular AbstractFramePtr F that has become observable, this
   3128 // represents the stack frames that need to be bailed out or marked as
   3129 // debuggees, and the scripts that need to be recompiled, taking inlining into
   3130 // account.
   3131 class MOZ_RAII ExecutionObservableFrame
   3132    : public DebugAPI::ExecutionObservableSet {
   3133  AbstractFramePtr frame_;
   3134 
   3135 public:
   3136  explicit ExecutionObservableFrame(AbstractFramePtr frame) : frame_(frame) {}
   3137 
   3138  Zone* singleZone() const override {
   3139    // We never inline across realms, let alone across zones, so
   3140    // frames_'s script's zone is the only one of interest.
   3141    return frame_.script()->zone();
   3142  }
   3143 
   3144  JSScript* singleScriptForZoneInvalidation() const override {
   3145    MOZ_CRASH(
   3146        "ExecutionObservableFrame shouldn't need zone-wide invalidation.");
   3147    return nullptr;
   3148  }
   3149 
   3150  bool shouldRecompileOrInvalidate(JSScript* script) const override {
   3151    // Normally, *this represents exactly one script: the one frame_ is
   3152    // running.
   3153    //
   3154    // However, debug-mode OSR uses *this for both invalidating Ion frames,
   3155    // and recompiling the Baseline scripts that those Ion frames will bail
   3156    // out into. Suppose frame_ is an inline frame, executing a copy of its
   3157    // JSScript, S_inner, that has been inlined into the IonScript of some
   3158    // other JSScript, S_outer. We must match S_outer, to decide which Ion
   3159    // frame to invalidate; and we must match S_inner, to decide which
   3160    // Baseline script to recompile.
   3161    //
   3162    // Note that this does not, by design, invalidate *all* inliners of
   3163    // frame_.script(), as only frame_ is made observable, not
   3164    // frame_.script().
   3165    if (!script->hasBaselineScript()) {
   3166      return false;
   3167    }
   3168 
   3169    if (frame_.hasScript() && script == frame_.script()) {
   3170      return true;
   3171    }
   3172 
   3173    return frame_.isRematerializedFrame() &&
   3174           script == frame_.asRematerializedFrame()->outerScript();
   3175  }
   3176 
   3177  bool shouldMarkAsDebuggee(FrameIter& iter) const override {
   3178    // AbstractFramePtr can't refer to non-remateralized Ion frames or
   3179    // non-debuggee wasm frames, so if iter refers to one such, we know we
   3180    // don't match.
   3181    //
   3182    // We never use this 'has' overload for frame invalidation, only for
   3183    // frame debuggee marking; so this overload doesn't need a parallel to
   3184    // the just-so inlining logic above.
   3185    return iter.hasUsableAbstractFramePtr() &&
   3186           iter.abstractFramePtr() == frame_;
   3187  }
   3188 };
   3189 
   3190 class MOZ_RAII ExecutionObservableScript
   3191    : public DebugAPI::ExecutionObservableSet {
   3192  RootedScript script_;
   3193 
   3194 public:
   3195  ExecutionObservableScript(JSContext* cx, JSScript* script)
   3196      : script_(cx, script) {}
   3197 
   3198  Zone* singleZone() const override { return script_->zone(); }
   3199  JSScript* singleScriptForZoneInvalidation() const override { return script_; }
   3200  bool shouldRecompileOrInvalidate(JSScript* script) const override {
   3201    return script->hasBaselineScript() && script == script_;
   3202  }
   3203  bool shouldMarkAsDebuggee(FrameIter& iter) const override {
   3204    // AbstractFramePtr can't refer to non-remateralized Ion frames, and
   3205    // while a non-rematerialized Ion frame may indeed be running script_,
   3206    // we cannot mark them as debuggees until they bail out.
   3207    //
   3208    // Upon bailing out, any newly constructed Baseline frames that came
   3209    // from Ion frames with scripts that are isDebuggee() is marked as
   3210    // debuggee. This is correct in that the only other way a frame may be
   3211    // marked as debuggee is via Debugger.Frame reflection, which would
   3212    // have rematerialized any Ion frames.
   3213    //
   3214    // Also AbstractFramePtr can't refer to non-debuggee wasm frames, so if
   3215    // iter refers to one such, we know we don't match.
   3216    return iter.hasUsableAbstractFramePtr() && !iter.isWasm() &&
   3217           iter.abstractFramePtr().script() == script_;
   3218  }
   3219 };
   3220 
   3221 /* static */
   3222 bool Debugger::updateExecutionObservabilityOfFrames(
   3223    JSContext* cx, const DebugAPI::ExecutionObservableSet& obs,
   3224    IsObserving observing) {
   3225  AutoSuppressProfilerSampling suppressProfilerSampling(cx);
   3226 
   3227  if (!jit::RecompileOnStackBaselineScriptsForDebugMode(cx, obs, observing)) {
   3228    return false;
   3229  }
   3230 
   3231  AbstractFramePtr oldestEnabledFrame;
   3232  for (AllFramesIter iter(cx); !iter.done(); ++iter) {
   3233    if (obs.shouldMarkAsDebuggee(iter)) {
   3234      if (observing) {
   3235        if (!iter.abstractFramePtr().isDebuggee()) {
   3236          oldestEnabledFrame = iter.abstractFramePtr();
   3237          oldestEnabledFrame.setIsDebuggee();
   3238        }
   3239        if (iter.abstractFramePtr().isWasmDebugFrame()) {
   3240          iter.abstractFramePtr().asWasmDebugFrame()->observe(cx);
   3241        }
   3242      } else {
   3243 #ifdef DEBUG
   3244        // Debugger.Frame lifetimes are managed by the debug epilogue,
   3245        // so in general it's unsafe to unmark a frame if it has a
   3246        // Debugger.Frame associated with it.
   3247        MOZ_ASSERT(!DebugAPI::inFrameMaps(iter.abstractFramePtr()));
   3248 #endif
   3249        iter.abstractFramePtr().unsetIsDebuggee();
   3250      }
   3251    }
   3252  }
   3253 
   3254  // See comment in unsetPrevUpToDateUntil.
   3255  if (oldestEnabledFrame) {
   3256    AutoRealm ar(cx, oldestEnabledFrame.environmentChain());
   3257    DebugEnvironments::unsetPrevUpToDateUntil(cx, oldestEnabledFrame);
   3258  }
   3259 
   3260  return true;
   3261 }
   3262 
   3263 static inline void MarkJitScriptActiveIfObservable(
   3264    JSScript* script, const DebugAPI::ExecutionObservableSet& obs) {
   3265  if (obs.shouldRecompileOrInvalidate(script)) {
   3266    script->jitScript()->icScript()->setActive();
   3267  }
   3268 }
   3269 
   3270 static bool AppendAndInvalidateScript(JSContext* cx, Zone* zone,
   3271                                      JSScript* script,
   3272                                      jit::IonScriptKeyVector& invalid,
   3273                                      Vector<JSScript*>& scripts) {
   3274  // Enter the script's realm as AddPendingInvalidation attempts to
   3275  // cancel off-thread compilations, whose books are kept on the
   3276  // script's realm.
   3277  MOZ_ASSERT(script->zone() == zone);
   3278  AutoRealm ar(cx, script);
   3279  AddPendingInvalidation(invalid, script);
   3280  return scripts.append(script);
   3281 }
   3282 
   3283 static bool UpdateExecutionObservabilityOfScriptsInZone(
   3284    JSContext* cx, Zone* zone, const DebugAPI::ExecutionObservableSet& obs,
   3285    Debugger::IsObserving observing) {
   3286  using namespace js::jit;
   3287 
   3288  AutoSuppressProfilerSampling suppressProfilerSampling(cx);
   3289 
   3290  CancelOffThreadBaselineCompile(zone);
   3291 
   3292  JS::GCContext* gcx = cx->gcContext();
   3293 
   3294  Vector<JSScript*> scripts(cx);
   3295 
   3296  // Iterate through observable scripts, invalidating their Ion scripts and
   3297  // appending them to a vector for discarding their baseline scripts later.
   3298  {
   3299    IonScriptKeyVector invalid;
   3300    if (JSScript* script = obs.singleScriptForZoneInvalidation()) {
   3301      if (obs.shouldRecompileOrInvalidate(script)) {
   3302        if (!AppendAndInvalidateScript(cx, zone, script, invalid, scripts)) {
   3303          return false;
   3304        }
   3305      }
   3306    } else {
   3307      for (auto base = zone->cellIter<BaseScript>(); !base.done();
   3308           base.next()) {
   3309        if (!base->hasJitScript()) {
   3310          continue;
   3311        }
   3312        JSScript* script = base->asJSScript();
   3313        if (obs.shouldRecompileOrInvalidate(script)) {
   3314          if (!AppendAndInvalidateScript(cx, zone, script, invalid, scripts)) {
   3315            return false;
   3316          }
   3317        }
   3318      }
   3319    }
   3320    Invalidate(cx, invalid);
   3321  }
   3322 
   3323  for (size_t i = 0; i < scripts.length(); i++) {
   3324    MOZ_ASSERT(!scripts[i]->jitScript()->icScript()->active());
   3325  }
   3326 
   3327  // Code below this point must be infallible to ensure the active bit of
   3328  // BaselineScripts is in a consistent state.
   3329  //
   3330  // Mark active baseline scripts in the observable set so that they don't
   3331  // get discarded. They will be recompiled.
   3332  for (JitActivationIterator actIter(cx); !actIter.done(); ++actIter) {
   3333    if (actIter->compartment()->zone() != zone) {
   3334      continue;
   3335    }
   3336 
   3337    for (OnlyJSJitFrameIter iter(actIter); !iter.done(); ++iter) {
   3338      const JSJitFrameIter& frame = iter.frame();
   3339      switch (frame.type()) {
   3340        case FrameType::BaselineJS:
   3341          MarkJitScriptActiveIfObservable(frame.script(), obs);
   3342          break;
   3343        case FrameType::IonJS:
   3344          MarkJitScriptActiveIfObservable(frame.script(), obs);
   3345          for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more();
   3346               ++inlineIter) {
   3347            MarkJitScriptActiveIfObservable(inlineIter.script(), obs);
   3348          }
   3349          break;
   3350        default:;
   3351      }
   3352    }
   3353  }
   3354 
   3355  // Iterate through the scripts again and finish discarding
   3356  // BaselineScripts. This must be done as a separate phase as we can only
   3357  // discard the BaselineScript on scripts that have no IonScript.
   3358  for (size_t i = 0; i < scripts.length(); i++) {
   3359    MOZ_ASSERT_IF(scripts[i]->isDebuggee(), observing);
   3360    if (!scripts[i]->jitScript()->icScript()->active()) {
   3361      FinishDiscardBaselineScript(gcx, scripts[i]);
   3362    }
   3363    scripts[i]->jitScript()->icScript()->resetActive();
   3364  }
   3365 
   3366  // Iterate through all wasm instances to find ones that need to be updated.
   3367  for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
   3368    for (wasm::Instance* instance : r->wasm.instances()) {
   3369      if (!instance->debugEnabled()) {
   3370        continue;
   3371      }
   3372 
   3373      bool enableTrap = observing == Debugger::Observing;
   3374      instance->debug().ensureEnterFrameTrapsState(cx, instance, enableTrap);
   3375    }
   3376  }
   3377 
   3378  return true;
   3379 }
   3380 
   3381 /* static */
   3382 bool Debugger::updateExecutionObservabilityOfScripts(
   3383    JSContext* cx, const DebugAPI::ExecutionObservableSet& obs,
   3384    IsObserving observing) {
   3385  if (Zone* zone = obs.singleZone()) {
   3386    return UpdateExecutionObservabilityOfScriptsInZone(cx, zone, obs,
   3387                                                       observing);
   3388  }
   3389 
   3390  using ZoneRange = DebugAPI::ExecutionObservableSet::ZoneRange;
   3391  for (ZoneRange r = obs.zones()->all(); !r.empty(); r.popFront()) {
   3392    if (!UpdateExecutionObservabilityOfScriptsInZone(cx, r.front(), obs,
   3393                                                     observing)) {
   3394      return false;
   3395    }
   3396  }
   3397 
   3398  return true;
   3399 }
   3400 
   3401 template <typename FrameFn>
   3402 /* static */
   3403 void Debugger::forEachOnStackDebuggerFrame(AbstractFramePtr frame,
   3404                                           const JS::AutoRequireNoGC& nogc,
   3405                                           FrameFn fn) {
   3406  for (Realm::DebuggerVectorEntry& entry : frame.global()->getDebuggers(nogc)) {
   3407    Debugger* dbg = entry.dbg;
   3408    if (FrameMap::Ptr frameEntry = dbg->frames.lookup(frame)) {
   3409      fn(dbg, frameEntry->value());
   3410    }
   3411  }
   3412 }
   3413 
   3414 template <typename FrameFn>
   3415 /* static */
   3416 void Debugger::forEachOnStackOrSuspendedDebuggerFrame(
   3417    JSContext* cx, AbstractFramePtr frame, const JS::AutoRequireNoGC& nogc,
   3418    FrameFn fn) {
   3419  Rooted<AbstractGeneratorObject*> genObj(
   3420      cx, frame.isGeneratorFrame() ? GetGeneratorObjectForFrame(cx, frame)
   3421                                   : nullptr);
   3422 
   3423  for (Realm::DebuggerVectorEntry& entry : frame.global()->getDebuggers(nogc)) {
   3424    Debugger* dbg = entry.dbg;
   3425 
   3426    DebuggerFrame* frameObj = nullptr;
   3427    if (FrameMap::Ptr frameEntry = dbg->frames.lookup(frame)) {
   3428      frameObj = frameEntry->value();
   3429    } else if (GeneratorWeakMap::Ptr frameEntry =
   3430                   dbg->generatorFrames.lookup(genObj)) {
   3431      frameObj = frameEntry->value();
   3432    }
   3433 
   3434    if (frameObj) {
   3435      fn(dbg, frameObj);
   3436    }
   3437  }
   3438 }
   3439 
   3440 /* static */
   3441 bool Debugger::getDebuggerFrames(AbstractFramePtr frame,
   3442                                 MutableHandle<DebuggerFrameVector> frames) {
   3443  bool hadOOM = false;
   3444  JS::AutoAssertNoGC nogc;
   3445  forEachOnStackDebuggerFrame(frame, nogc,
   3446                              [&](Debugger*, DebuggerFrame* frameobj) {
   3447                                if (!hadOOM && !frames.append(frameobj)) {
   3448                                  hadOOM = true;
   3449                                }
   3450                              });
   3451  return !hadOOM;
   3452 }
   3453 
   3454 /* static */
   3455 bool Debugger::updateExecutionObservability(
   3456    JSContext* cx, DebugAPI::ExecutionObservableSet& obs,
   3457    IsObserving observing) {
   3458  if (!obs.singleZone() && obs.zones()->empty()) {
   3459    return true;
   3460  }
   3461 
   3462  // Invalidate scripts first so we can set the needsArgsObj flag on scripts
   3463  // before patching frames.
   3464  return updateExecutionObservabilityOfScripts(cx, obs, observing) &&
   3465         updateExecutionObservabilityOfFrames(cx, obs, observing);
   3466 }
   3467 
   3468 /* static */
   3469 bool Debugger::ensureExecutionObservabilityOfScript(JSContext* cx,
   3470                                                    JSScript* script) {
   3471  if (script->isDebuggee()) {
   3472    return true;
   3473  }
   3474  ExecutionObservableScript obs(cx, script);
   3475  return updateExecutionObservability(cx, obs, Observing);
   3476 }
   3477 
   3478 /* static */
   3479 bool DebugAPI::ensureExecutionObservabilityOfOsrFrame(
   3480    JSContext* cx, AbstractFramePtr osrSourceFrame) {
   3481  MOZ_ASSERT(osrSourceFrame.isDebuggee());
   3482  if (osrSourceFrame.script()->hasBaselineScript() &&
   3483      osrSourceFrame.script()->baselineScript()->hasDebugInstrumentation()) {
   3484    return true;
   3485  }
   3486  ExecutionObservableFrame obs(osrSourceFrame);
   3487  return Debugger::updateExecutionObservabilityOfFrames(cx, obs, Observing);
   3488 }
   3489 
   3490 /* static */
   3491 bool Debugger::ensureExecutionObservabilityOfFrame(JSContext* cx,
   3492                                                   AbstractFramePtr frame) {
   3493  MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(),
   3494                frame.isDebuggee());
   3495  MOZ_ASSERT_IF(frame.isWasmDebugFrame(), frame.wasmInstance()->debugEnabled());
   3496  if (frame.isDebuggee()) {
   3497    return true;
   3498  }
   3499  ExecutionObservableFrame obs(frame);
   3500  return updateExecutionObservabilityOfFrames(cx, obs, Observing);
   3501 }
   3502 
   3503 /* static */
   3504 bool Debugger::ensureExecutionObservabilityOfRealm(JSContext* cx,
   3505                                                   Realm* realm) {
   3506  if (realm->debuggerObservesAllExecution()) {
   3507    return true;
   3508  }
   3509  ExecutionObservableRealms obs(cx);
   3510  if (!obs.add(realm)) {
   3511    return false;
   3512  }
   3513  realm->updateDebuggerObservesAllExecution();
   3514  return updateExecutionObservability(cx, obs, Observing);
   3515 }
   3516 
   3517 /* static */
   3518 bool Debugger::hookObservesAllExecution(Hook which) {
   3519  return which == OnEnterFrame;
   3520 }
   3521 
   3522 Debugger::IsObserving Debugger::observesAllExecution() const {
   3523  if (!!getHook(OnEnterFrame)) {
   3524    return Observing;
   3525  }
   3526  return NotObserving;
   3527 }
   3528 
   3529 Debugger::IsObserving Debugger::observesAsmJS() const {
   3530  if (!allowUnobservedAsmJS) {
   3531    return Observing;
   3532  }
   3533  return NotObserving;
   3534 }
   3535 
   3536 Debugger::IsObserving Debugger::observesWasm() const {
   3537  if (!allowUnobservedWasm) {
   3538    return Observing;
   3539  }
   3540  return NotObserving;
   3541 }
   3542 
   3543 Debugger::IsObserving Debugger::observesCoverage() const {
   3544  if (collectCoverageInfo) {
   3545    return Observing;
   3546  }
   3547  return NotObserving;
   3548 }
   3549 
   3550 Debugger::IsObserving Debugger::observesNativeCalls() const {
   3551  if (getHook(Debugger::OnNativeCall)) {
   3552    return Observing;
   3553  }
   3554  return NotObserving;
   3555 }
   3556 
   3557 bool Debugger::isExclusiveDebuggerOnEval() const {
   3558  return exclusiveDebuggerOnEval;
   3559 }
   3560 
   3561 // Toggle whether this Debugger's debuggees observe all execution. This is
   3562 // called when a hook that observes all execution is set or unset. See
   3563 // hookObservesAllExecution.
   3564 bool Debugger::updateObservesAllExecutionOnDebuggees(JSContext* cx,
   3565                                                     IsObserving observing) {
   3566  ExecutionObservableRealms obs(cx);
   3567 
   3568  for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
   3569       r.popFront()) {
   3570    GlobalObject* global = r.front();
   3571    JS::Realm* realm = global->realm();
   3572 
   3573    if (realm->debuggerObservesAllExecution() == observing) {
   3574      continue;
   3575    }
   3576 
   3577    // It's expensive to eagerly invalidate and recompile a realm,
   3578    // so add the realm to the set only if we are observing.
   3579    if (observing && !obs.add(realm)) {
   3580      return false;
   3581    }
   3582  }
   3583 
   3584  if (!updateExecutionObservability(cx, obs, observing)) {
   3585    return false;
   3586  }
   3587 
   3588  using RealmRange = ExecutionObservableRealms::RealmRange;
   3589  for (RealmRange r = obs.realms()->all(); !r.empty(); r.popFront()) {
   3590    r.front()->updateDebuggerObservesAllExecution();
   3591  }
   3592 
   3593  return true;
   3594 }
   3595 
   3596 bool Debugger::updateObservesCoverageOnDebuggees(JSContext* cx,
   3597                                                 IsObserving observing) {
   3598  ExecutionObservableRealms obs(cx);
   3599 
   3600  for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
   3601       r.popFront()) {
   3602    GlobalObject* global = r.front();
   3603    Realm* realm = global->realm();
   3604 
   3605    if (realm->debuggerObservesCoverage() == observing) {
   3606      continue;
   3607    }
   3608 
   3609    // Invalidate and recompile a realm to add or remove PCCounts
   3610    // increments. We have to eagerly invalidate, as otherwise we might have
   3611    // dangling pointers to freed PCCounts.
   3612    if (!obs.add(realm)) {
   3613      return false;
   3614    }
   3615  }
   3616 
   3617  // If any frame on the stack belongs to the debuggee, then we cannot update
   3618  // the ScriptCounts, because this would imply to invalidate a Debugger.Frame
   3619  // to recompile it with/without ScriptCount support.
   3620  for (FrameIter iter(cx); !iter.done(); ++iter) {
   3621    if (obs.shouldMarkAsDebuggee(iter)) {
   3622      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3623                                JSMSG_DEBUG_NOT_IDLE);
   3624      return false;
   3625    }
   3626  }
   3627 
   3628  if (!updateExecutionObservability(cx, obs, observing)) {
   3629    return false;
   3630  }
   3631 
   3632  // All realms can safely be toggled, and all scripts will be recompiled.
   3633  // Thus we can update each realm accordingly.
   3634  using RealmRange = ExecutionObservableRealms::RealmRange;
   3635  for (RealmRange r = obs.realms()->all(); !r.empty(); r.popFront()) {
   3636    r.front()->updateDebuggerObservesCoverage();
   3637  }
   3638 
   3639  return true;
   3640 }
   3641 
   3642 void Debugger::updateObservesAsmJSOnDebuggees(IsObserving observing) {
   3643  for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
   3644       r.popFront()) {
   3645    GlobalObject* global = r.front();
   3646    Realm* realm = global->realm();
   3647 
   3648    if (realm->debuggerObservesAsmJS() == observing) {
   3649      continue;
   3650    }
   3651 
   3652    realm->updateDebuggerObservesAsmJS();
   3653  }
   3654 }
   3655 
   3656 void Debugger::updateObservesWasmOnDebuggees(IsObserving observing) {
   3657  for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
   3658       r.popFront()) {
   3659    GlobalObject* global = r.front();
   3660    Realm* realm = global->realm();
   3661 
   3662    if (realm->debuggerObservesWasm() == observing) {
   3663      continue;
   3664    }
   3665 
   3666    realm->updateDebuggerObservesWasm();
   3667  }
   3668 }
   3669 
   3670 void Debugger::updateObservesNativeCallOnDebuggees(IsObserving observing) {
   3671  for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
   3672       r.popFront()) {
   3673    GlobalObject* global = r.front();
   3674    Realm* realm = global->realm();
   3675 
   3676    if (realm->debuggerObservesNativeCall() == observing) {
   3677      continue;
   3678    }
   3679 
   3680    realm->updateDebuggerObservesNativeCall();
   3681  }
   3682 }
   3683 
   3684 /*** Allocations Tracking ***************************************************/
   3685 
   3686 /* static */
   3687 bool Debugger::cannotTrackAllocations(const GlobalObject& global) {
   3688  auto existingCallback = global.realm()->getAllocationMetadataBuilder();
   3689  return existingCallback && existingCallback != &SavedStacks::metadataBuilder;
   3690 }
   3691 
   3692 /* static */
   3693 bool DebugAPI::isObservedByDebuggerTrackingAllocations(
   3694    const GlobalObject& debuggee) {
   3695  JS::AutoAssertNoGC nogc;
   3696  for (Realm::DebuggerVectorEntry& entry : debuggee.getDebuggers(nogc)) {
   3697    // Use unbarrieredGet() to prevent triggering read barrier while
   3698    // collecting, this is safe as long as dbg does not escape.
   3699    Debugger* dbg = entry.dbg.unbarrieredGet();
   3700    if (dbg->trackingAllocationSites) {
   3701      return true;
   3702    }
   3703  }
   3704 
   3705  return false;
   3706 }
   3707 
   3708 /* static */
   3709 bool Debugger::addAllocationsTracking(JSContext* cx,
   3710                                      Handle<GlobalObject*> debuggee) {
   3711  // Precondition: the given global object is being observed by at least one
   3712  // Debugger that is tracking allocations.
   3713  MOZ_ASSERT(DebugAPI::isObservedByDebuggerTrackingAllocations(*debuggee));
   3714 
   3715  if (Debugger::cannotTrackAllocations(*debuggee)) {
   3716    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3717                              JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
   3718    return false;
   3719  }
   3720 
   3721  debuggee->realm()->setAllocationMetadataBuilder(
   3722      &SavedStacks::metadataBuilder);
   3723  debuggee->realm()->chooseAllocationSamplingProbability();
   3724  return true;
   3725 }
   3726 
   3727 /* static */
   3728 void Debugger::removeAllocationsTracking(GlobalObject& global) {
   3729  // If there are still Debuggers that are observing allocations, we cannot
   3730  // remove the metadata callback yet. Recompute the sampling probability
   3731  // based on the remaining debuggers' needs.
   3732  if (DebugAPI::isObservedByDebuggerTrackingAllocations(global)) {
   3733    global.realm()->chooseAllocationSamplingProbability();
   3734    return;
   3735  }
   3736 
   3737  if (!global.realm()->runtimeFromMainThread()->recordAllocationCallback) {
   3738    // Something like the Gecko Profiler could request from the the JS runtime
   3739    // to record allocations. If it is recording allocations, then do not
   3740    // destroy the allocation metadata builder at this time.
   3741    global.realm()->forgetAllocationMetadataBuilder();
   3742  }
   3743 }
   3744 
   3745 bool Debugger::addAllocationsTrackingForAllDebuggees(JSContext* cx) {
   3746  MOZ_ASSERT(trackingAllocationSites);
   3747 
   3748  // We don't want to end up in a state where we added allocations
   3749  // tracking to some of our debuggees, but failed to do so for
   3750  // others. Before attempting to start tracking allocations in *any* of
   3751  // our debuggees, ensure that we will be able to track allocations for
   3752  // *all* of our debuggees.
   3753  for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
   3754       r.popFront()) {
   3755    if (Debugger::cannotTrackAllocations(*r.front().get())) {
   3756      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3757                                JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
   3758      return false;
   3759    }
   3760  }
   3761 
   3762  Rooted<GlobalObject*> g(cx);
   3763  for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
   3764       r.popFront()) {
   3765    // This should always succeed, since we already checked for the
   3766    // error case above.
   3767    g = r.front().get();
   3768    MOZ_ALWAYS_TRUE(Debugger::addAllocationsTracking(cx, g));
   3769  }
   3770 
   3771  return true;
   3772 }
   3773 
   3774 void Debugger::removeAllocationsTrackingForAllDebuggees() {
   3775  for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
   3776       r.popFront()) {
   3777    Debugger::removeAllocationsTracking(*r.front().get());
   3778  }
   3779 
   3780  allocationsLog.clear();
   3781 }
   3782 
   3783 /*** Debugger JSObjects *****************************************************/
   3784 
   3785 template <typename F>
   3786 inline void Debugger::forEachWeakMap(const F& f) {
   3787  f(generatorFrames);
   3788  f(objects);
   3789  f(environments);
   3790  f(scripts);
   3791  f(sources);
   3792  f(wasmInstanceScripts);
   3793  f(wasmInstanceSources);
   3794 }
   3795 
   3796 void Debugger::traceCrossCompartmentEdges(JSTracer* trc) {
   3797  forEachWeakMap(
   3798      [trc](auto& weakMap) { weakMap.traceCrossCompartmentEdges(trc); });
   3799 }
   3800 
   3801 /*
   3802 * Ordinarily, WeakMap keys and values are marked because at some point it was
   3803 * discovered that the WeakMap was live; that is, some object containing the
   3804 * WeakMap was marked during mark phase.
   3805 *
   3806 * However, during zone GC, we have to do something about cross-compartment
   3807 * edges in non-GC'd compartments. Since the source may be live, we
   3808 * conservatively assume it is and mark the edge.
   3809 *
   3810 * Each Debugger object keeps five cross-compartment WeakMaps: objects, scripts,
   3811 * lazy scripts, script source objects, and environments. They have the property
   3812 * that all their values are in the same compartment as the Debugger object,
   3813 * but we have to mark the keys and the private pointer in the wrapper object.
   3814 *
   3815 * We must scan all Debugger objects regardless of whether they *currently* have
   3816 * any debuggees in a compartment being GC'd, because the WeakMap entries
   3817 * persist even when debuggees are removed.
   3818 *
   3819 * This happens during the initial mark phase, not iterative marking, because
   3820 * all the edges being reported here are strong references.
   3821 *
   3822 * This method is also used during compacting GC to update cross compartment
   3823 * pointers into zones that are being compacted.
   3824 */
   3825 /* static */
   3826 void DebugAPI::traceCrossCompartmentEdges(JSTracer* trc) {
   3827  MOZ_ASSERT(JS::RuntimeHeapIsMajorCollecting());
   3828 
   3829  JSRuntime* rt = trc->runtime();
   3830  gc::State state = rt->gc.state();
   3831 
   3832  for (Debugger* dbg : rt->debuggerList()) {
   3833    Zone* zone = MaybeForwarded(dbg->object.get())->zone();
   3834    if (!zone->isCollecting() || state == gc::State::Compact) {
   3835      dbg->traceCrossCompartmentEdges(trc);
   3836    }
   3837  }
   3838 }
   3839 
   3840 #ifdef DEBUG
   3841 
   3842 static bool RuntimeHasDebugger(JSRuntime* rt, Debugger* dbg) {
   3843  for (Debugger* d : rt->debuggerList()) {
   3844    if (d == dbg) {
   3845      return true;
   3846    }
   3847  }
   3848  return false;
   3849 }
   3850 
   3851 /* static */
   3852 bool DebugAPI::edgeIsInDebuggerWeakmap(JSRuntime* rt, JSObject* src,
   3853                                       JS::GCCellPtr dst) {
   3854  if (!Debugger::isChildJSObject(src)) {
   3855    return false;
   3856  }
   3857 
   3858  if (src->is<DebuggerFrame>()) {
   3859    DebuggerFrame* frame = &src->as<DebuggerFrame>();
   3860    Debugger* dbg = frame->owner();
   3861    MOZ_ASSERT(RuntimeHasDebugger(rt, dbg));
   3862 
   3863    if (dst.is<BaseScript>()) {
   3864      // The generatorFrames map is not keyed on the associated JSScript. Get
   3865      // the key from the source object and check everything matches.
   3866      AbstractGeneratorObject* genObj = &frame->unwrappedGenerator();
   3867      return frame->generatorScript() == &dst.as<BaseScript>() &&
   3868             dbg->generatorFrames.hasEntry(genObj, frame);
   3869    }
   3870    return dst.is<JSObject>() &&
   3871           dst.as<JSObject>().is<AbstractGeneratorObject>() &&
   3872           dbg->generatorFrames.hasEntry(
   3873               &dst.as<JSObject>().as<AbstractGeneratorObject>(), frame);
   3874  }
   3875  if (src->is<DebuggerObject>()) {
   3876    DebuggerObject* dobj = &src->as<DebuggerObject>();
   3877    Debugger* dbg = dobj->owner();
   3878    MOZ_ASSERT(RuntimeHasDebugger(rt, dbg));
   3879    return dst.is<JSObject>() &&
   3880           dbg->objects.hasEntry(&dst.as<JSObject>(), dobj);
   3881  }
   3882  if (src->is<DebuggerEnvironment>()) {
   3883    DebuggerEnvironment* denv = &src->as<DebuggerEnvironment>();
   3884    Debugger* dbg = denv->owner();
   3885    MOZ_ASSERT(RuntimeHasDebugger(rt, dbg));
   3886    return dst.is<JSObject>() &&
   3887           dbg->environments.hasEntry(&dst.as<JSObject>(), denv);
   3888  }
   3889  if (src->is<DebuggerScript>()) {
   3890    DebuggerScript* dscript = &src->as<DebuggerScript>();
   3891    Debugger* dbg = dscript->owner();
   3892    MOZ_ASSERT(RuntimeHasDebugger(rt, dbg));
   3893 
   3894    return src->as<DebuggerScript>().getReferent().match(
   3895        [=](BaseScript* script) {
   3896          return dst.is<BaseScript>() && script == &dst.as<BaseScript>() &&
   3897                 dbg->scripts.hasEntry(script, dscript);
   3898        },
   3899        [=](WasmInstanceObject* instance) {
   3900          return dst.is<JSObject>() && instance == &dst.as<JSObject>() &&
   3901                 dbg->wasmInstanceScripts.hasEntry(instance, dscript);
   3902        });
   3903  }
   3904  if (src->is<DebuggerSource>()) {
   3905    DebuggerSource* dsource = &src->as<DebuggerSource>();
   3906    Debugger* dbg = dsource->owner();
   3907    MOZ_ASSERT(RuntimeHasDebugger(rt, dbg));
   3908 
   3909    return src->as<DebuggerSource>().getReferent().match(
   3910        [=](ScriptSourceObject* sso) {
   3911          return dst.is<JSObject>() && sso == &dst.as<JSObject>() &&
   3912                 dbg->sources.hasEntry(sso, dsource);
   3913        },
   3914        [=](WasmInstanceObject* instance) {
   3915          return dst.is<JSObject>() && instance == &dst.as<JSObject>() &&
   3916                 dbg->wasmInstanceSources.hasEntry(instance, dsource);
   3917        });
   3918  }
   3919  MOZ_ASSERT_UNREACHABLE("Unhandled cross-compartment edge");
   3920 }
   3921 
   3922 #endif
   3923 
   3924 /* See comments in DebugAPI.h. */
   3925 void DebugAPI::traceFramesWithLiveHooks(JSTracer* tracer) {
   3926  JSRuntime* rt = tracer->runtime();
   3927 
   3928  // Note that we must loop over all Debuggers here, not just those known to be
   3929  // reachable from JavaScript. The existence of hooks set on a Debugger.Frame
   3930  // for a live stack frame makes the Debuger.Frame (and hence its Debugger)
   3931  // reachable.
   3932  for (Debugger* dbg : rt->debuggerList()) {
   3933    // Callback tracers set their own traversal boundaries, but otherwise we're
   3934    // only interested in Debugger.Frames participating in the collection.
   3935    if (!dbg->zone()->isGCMarking() && !tracer->isCallbackTracer()) {
   3936      continue;
   3937    }
   3938 
   3939    for (Debugger::FrameMap::Range r = dbg->frames.all(); !r.empty();
   3940         r.popFront()) {
   3941      HeapPtr<DebuggerFrame*>& frameobj = r.front().value();
   3942      MOZ_ASSERT(frameobj->isOnStackOrSuspendedWasmStack());
   3943      if (frameobj->hasAnyHooks()) {
   3944        TraceEdge(tracer, &frameobj, "Debugger.Frame with live hooks");
   3945      }
   3946    }
   3947  }
   3948 }
   3949 
   3950 void DebugAPI::slowPathTraceGeneratorFrame(JSTracer* tracer,
   3951                                           AbstractGeneratorObject* generator) {
   3952  MOZ_ASSERT(generator->realm()->isDebuggee());
   3953 
   3954  // Ignore generic tracers.
   3955  //
   3956  // There are two kinds of generic tracers we need to bar: MovingTracers used
   3957  // by compacting GC; and CompartmentCheckTracers.
   3958  //
   3959  // MovingTracers are used by the compacting GC to update pointers to objects
   3960  // that have been moved: the MovingTracer checks each outgoing pointer to see
   3961  // if it refers to a forwarding pointer, and if so, updates the pointer stored
   3962  // in the object.
   3963  //
   3964  // Generator objects are background finalized, so the compacting GC assumes it
   3965  // can update their pointers in the background as well. Since we treat
   3966  // generator objects as having an owning edge to their Debugger.Frame objects,
   3967  // a helper thread trying to update a generator object will end up calling
   3968  // this function. However, it is verboten to do weak map lookups (e.g., in
   3969  // Debugger::generatorFrames) off the main thread, since StableCellHasher
   3970  // must consult the Zone to find the key's unique id.
   3971  //
   3972  // Fortunately, it's not necessary for compacting GC to worry about that edge
   3973  // in the first place: the edge isn't a literal pointer stored on the
   3974  // generator object, it's only inferred from the realm's debuggee status and
   3975  // its Debuggers' generatorFrames weak maps. Those get relocated when the
   3976  // Debugger itself is visited, so compacting GC can just ignore this edge.
   3977  //
   3978  // CompartmentCheckTracers walk the graph and verify that all
   3979  // cross-compartment edges are recorded in the cross-compartment wrapper
   3980  // tables. But edges between Debugger.Foo objects and their referents are not
   3981  // in the CCW tables, so a CrossCompartmentCheckTracers also calls
   3982  // DebugAPI::edgeIsInDebuggerWeakmap to see if a given cross-compartment edge
   3983  // is accounted for there. However, edgeIsInDebuggerWeakmap only handles
   3984  // debugger -> debuggee edges, so it won't recognize the edge we're
   3985  // potentially traversing here, from a generator object to its Debugger.Frame.
   3986  //
   3987  // But since the purpose of this function is to retrieve such edges, if they
   3988  // exist, from the very tables that edgeIsInDebuggerWeakmap would consult,
   3989  // we're at no risk of reporting edges that they do not cover. So we can
   3990  // safely hide the edges from CompartmentCheckTracers.
   3991  //
   3992  // We can't quite recognize MovingTracers and CompartmentCheckTracers
   3993  // precisely, but they're both generic tracers, so we just show them all the
   3994  // door. This means the generator -> Debugger.Frame edge is going to be
   3995  // invisible to some traversals. We'll cope with that when it's a problem.
   3996  if (!tracer->isMarkingTracer()) {
   3997    return;
   3998  }
   3999 
   4000  mozilla::Maybe<AutoLockGC> lock;
   4001  GCMarker* marker = GCMarker::fromTracer(tracer);
   4002  if (marker->isParallelMarking()) {
   4003    // Synchronise access to generatorFrames.
   4004    lock.emplace(marker->runtime());
   4005  }
   4006 
   4007  JS::AutoAssertNoGC nogc;
   4008  for (Realm::DebuggerVectorEntry& entry :
   4009       generator->realm()->getDebuggers(nogc)) {
   4010    Debugger* dbg = entry.dbg.unbarrieredGet();
   4011 
   4012    if (Debugger::GeneratorWeakMap::Ptr entry =
   4013            dbg->generatorFrames.lookupUnbarriered(generator)) {
   4014      const PreBarriered<DebuggerFrame*>& frameObj = entry->value();
   4015      if (frameObj->hasAnyHooks()) {
   4016        // See comment above.
   4017        TraceCrossCompartmentEdge(tracer, generator, &frameObj,
   4018                                  "Debugger.Frame with hooks for generator");
   4019      }
   4020    }
   4021  }
   4022 }
   4023 
   4024 /* static */
   4025 void DebugAPI::traceAllForMovingGC(JSTracer* trc) {
   4026  JSRuntime* rt = trc->runtime();
   4027  for (Debugger* dbg : rt->debuggerList()) {
   4028    dbg->traceForMovingGC(trc);
   4029  }
   4030 }
   4031 
   4032 /*
   4033 * Trace all debugger-owned GC things unconditionally. This is used during
   4034 * compacting GC and in minor GC: the minor GC cannot apply the weak constraints
   4035 * of the full GC because it visits only part of the heap.
   4036 */
   4037 void Debugger::traceForMovingGC(JSTracer* trc) {
   4038  trace(trc);
   4039 
   4040  for (WeakGlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
   4041    TraceEdge(trc, &e.mutableFront(), "Global Object");
   4042  }
   4043 }
   4044 
   4045 /* static */
   4046 void Debugger::traceObject(JSTracer* trc, JSObject* obj) {
   4047  if (Debugger* dbg = Debugger::fromJSObject(obj)) {
   4048    dbg->trace(trc);
   4049  }
   4050 }
   4051 
   4052 void Debugger::trace(JSTracer* trc) {
   4053  TraceEdge(trc, &object, "Debugger Object");
   4054 
   4055  TraceNullableEdge(trc, &uncaughtExceptionHook, "hooks");
   4056 
   4057  // Mark Debugger.Frame objects. Since the Debugger is reachable, JS could call
   4058  // getNewestFrame and then walk the stack, so these are all reachable from JS.
   4059  //
   4060  // Note that if a Debugger.Frame has hooks set, it must be retained even if
   4061  // its Debugger is unreachable, since JS could observe that its hooks did not
   4062  // fire. That case is handled by DebugAPI::traceFrames.
   4063  //
   4064  // (We have weakly-referenced Debugger.Frame objects as well, for suspended
   4065  // generator frames; these are traced via generatorFrames just below.)
   4066  for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
   4067    HeapPtr<DebuggerFrame*>& frameobj = r.front().value();
   4068    TraceEdge(trc, &frameobj, "live Debugger.Frame");
   4069    MOZ_ASSERT(frameobj->isOnStackOrSuspendedWasmStack());
   4070  }
   4071 
   4072  allocationsLog.trace(trc);
   4073 
   4074  forEachWeakMap([trc](auto& weakMap) { weakMap.trace(trc); });
   4075 }
   4076 
   4077 /* static */
   4078 void DebugAPI::traceFromRealm(JSTracer* trc, Realm* realm) {
   4079  JS::AutoAssertNoGC nogc;
   4080  for (Realm::DebuggerVectorEntry& entry : realm->getDebuggers(nogc)) {
   4081    TraceEdge(trc, &entry.debuggerLink, "realm debugger");
   4082  }
   4083 }
   4084 
   4085 /* static */
   4086 void DebugAPI::sweepAll(JS::GCContext* gcx) {
   4087  JSRuntime* rt = gcx->runtime();
   4088 
   4089  Debugger* next;
   4090  for (Debugger* dbg = rt->debuggerList().getFirst(); dbg; dbg = next) {
   4091    next = dbg->getNext();
   4092 
   4093    // Debugger.Frames for generator calls bump the JSScript's
   4094    // generatorObserverCount, so the JIT will instrument the code to notify
   4095    // Debugger when the generator is resumed. When a Debugger.Frame gets GC'd,
   4096    // generatorObserverCount needs to be decremented. It's much easier to do
   4097    // this when we know that all parties involved - the Debugger.Frame, the
   4098    // generator object, and the JSScript - have not yet been finalized.
   4099    //
   4100    // Since DebugAPI::sweepAll is called after everything is marked, but before
   4101    // anything has been finalized, this is the perfect place to drop the count.
   4102    if (dbg->zone()->isGCSweeping()) {
   4103      for (Debugger::GeneratorWeakMap::Enum e(dbg->generatorFrames); !e.empty();
   4104           e.popFront()) {
   4105        DebuggerFrame* frameObj = e.front().value();
   4106        if (IsAboutToBeFinalizedUnbarriered(frameObj)) {
   4107          // If the DebuggerFrame is being finalized, that means either:
   4108          //  1) It is not present in "frames".
   4109          //  2) The Debugger itself is also being finalized.
   4110          //
   4111          // In the first case, passing the frame is not necessary because there
   4112          // isn't a frame entry to clear, and in the second case,
   4113          // removeDebuggeeGlobal below will iterate and remove the entries
   4114          // anyway, so things will be cleaned up properly.
   4115          Debugger::terminateDebuggerFrame(gcx, dbg, frameObj, NullFramePtr(),
   4116                                           nullptr, &e);
   4117        }
   4118      }
   4119    }
   4120 
   4121    // Detach dying debuggers and debuggees from each other. Since this
   4122    // requires access to both objects it must be done before either
   4123    // object is finalized.
   4124    bool debuggerDying = IsAboutToBeFinalized(dbg->object);
   4125    for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty();
   4126         e.popFront()) {
   4127      GlobalObject* global = e.front().unbarrieredGet();
   4128      if (debuggerDying || IsAboutToBeFinalizedUnbarriered(global)) {
   4129        dbg->removeDebuggeeGlobal(gcx, e.front().unbarrieredGet(), &e,
   4130                                  Debugger::FromSweep::Yes);
   4131      }
   4132    }
   4133 
   4134    if (debuggerDying) {
   4135      gcx->delete_(dbg->object, dbg, MemoryUse::Debugger);
   4136    }
   4137 
   4138    dbg = next;
   4139  }
   4140 }
   4141 
   4142 static inline bool SweepZonesInSameGroup(Zone* a, Zone* b) {
   4143  // Ensure two zones are swept in the same sweep group by adding an edge
   4144  // between them in each direction.
   4145  return a->addSweepGroupEdgeTo(b) && b->addSweepGroupEdgeTo(a);
   4146 }
   4147 
   4148 /* static */
   4149 bool DebugAPI::findSweepGroupEdges(JSRuntime* rt) {
   4150  // Ensure that debuggers and their debuggees are finalized in the same group
   4151  // by adding edges in both directions for debuggee zones. These are weak
   4152  // references that are not in the cross compartment wrapper map.
   4153 
   4154  for (Debugger* dbg : rt->debuggerList()) {
   4155    Zone* debuggerZone = dbg->object->zone();
   4156    if (!debuggerZone->isGCMarking()) {
   4157      continue;
   4158    }
   4159 
   4160    for (auto e = dbg->debuggeeZones.all(); !e.empty(); e.popFront()) {
   4161      Zone* debuggeeZone = e.front();
   4162      if (!debuggeeZone->isGCMarking()) {
   4163        continue;
   4164      }
   4165 
   4166      if (!SweepZonesInSameGroup(debuggerZone, debuggeeZone)) {
   4167        return false;
   4168      }
   4169    }
   4170  }
   4171 
   4172  return true;
   4173 }
   4174 
   4175 template <class UnbarrieredKey, class Wrapper, bool InvisibleKeysOk>
   4176 bool DebuggerWeakMap<UnbarrieredKey, Wrapper,
   4177                     InvisibleKeysOk>::findSweepGroupEdges(Zone* atomsZone) {
   4178  Zone* debuggerZone = zone();
   4179  MOZ_ASSERT(debuggerZone->isGCMarking());
   4180  for (Enum e(*this); !e.empty(); e.popFront()) {
   4181    MOZ_ASSERT(e.front().value()->zone() == debuggerZone);
   4182 
   4183    Zone* keyZone = e.front().key()->zone();
   4184    if (keyZone->isGCMarking() &&
   4185        !SweepZonesInSameGroup(debuggerZone, keyZone)) {
   4186      return false;
   4187    }
   4188  }
   4189 
   4190  // Add in edges for delegates, if relevant for the key type.
   4191  return Base::findSweepGroupEdges(atomsZone);
   4192 }
   4193 
   4194 const JSClassOps DebuggerInstanceObject::classOps_ = {
   4195    nullptr,                // addProperty
   4196    nullptr,                // delProperty
   4197    nullptr,                // enumerate
   4198    nullptr,                // newEnumerate
   4199    nullptr,                // resolve
   4200    nullptr,                // mayResolve
   4201    nullptr,                // finalize
   4202    nullptr,                // call
   4203    nullptr,                // construct
   4204    Debugger::traceObject,  // trace
   4205 };
   4206 
   4207 const JSClass DebuggerInstanceObject::class_ = {
   4208    "Debugger",
   4209    JSCLASS_HAS_RESERVED_SLOTS(Debugger::JSSLOT_DEBUG_COUNT),
   4210    &classOps_,
   4211 };
   4212 
   4213 static_assert(Debugger::JSSLOT_DEBUG_PROTO_START == 0,
   4214              "DebuggerPrototypeObject only needs slots for the proto objects");
   4215 
   4216 const JSClass DebuggerPrototypeObject::class_ = {
   4217    "DebuggerPrototype",
   4218    JSCLASS_HAS_RESERVED_SLOTS(Debugger::JSSLOT_DEBUG_PROTO_STOP),
   4219 };
   4220 
   4221 static Debugger* Debugger_fromThisValue(JSContext* cx, const CallArgs& args,
   4222                                        const char* fnname) {
   4223  JSObject* thisobj = RequireObject(cx, args.thisv());
   4224  if (!thisobj) {
   4225    return nullptr;
   4226  }
   4227  if (!thisobj->is<DebuggerInstanceObject>()) {
   4228    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4229                              JSMSG_INCOMPATIBLE_PROTO, "Debugger", fnname,
   4230                              thisobj->getClass()->name);
   4231    return nullptr;
   4232  }
   4233 
   4234  Debugger* dbg = Debugger::fromJSObject(thisobj);
   4235  MOZ_ASSERT(dbg);
   4236  return dbg;
   4237 }
   4238 
   4239 struct MOZ_STACK_CLASS Debugger::CallData {
   4240  JSContext* cx;
   4241  const CallArgs& args;
   4242 
   4243  Debugger* dbg;
   4244 
   4245  CallData(JSContext* cx, const CallArgs& args, Debugger* dbg)
   4246      : cx(cx), args(args), dbg(dbg) {}
   4247 
   4248  bool getOnDebuggerStatement();
   4249  bool setOnDebuggerStatement();
   4250  bool getOnExceptionUnwind();
   4251  bool setOnExceptionUnwind();
   4252  bool getOnNewScript();
   4253  bool setOnNewScript();
   4254  bool getOnEnterFrame();
   4255  bool setOnEnterFrame();
   4256  bool getOnNativeCall();
   4257  bool setOnNativeCall();
   4258  bool getShouldAvoidSideEffects();
   4259  bool setShouldAvoidSideEffects();
   4260  bool getOnNewGlobalObject();
   4261  bool setOnNewGlobalObject();
   4262  bool getOnNewPromise();
   4263  bool setOnNewPromise();
   4264  bool getOnPromiseSettled();
   4265  bool setOnPromiseSettled();
   4266  bool getUncaughtExceptionHook();
   4267  bool setUncaughtExceptionHook();
   4268  bool getAllowUnobservedAsmJS();
   4269  bool setAllowUnobservedAsmJS();
   4270  bool getAllowUnobservedWasm();
   4271  bool setAllowUnobservedWasm();
   4272  bool getExclusiveDebuggerOnEval();
   4273  bool setExclusiveDebuggerOnEval();
   4274  bool getInspectNativeCallArguments();
   4275  bool setInspectNativeCallArguments();
   4276  bool getCollectCoverageInfo();
   4277  bool setCollectCoverageInfo();
   4278  bool getMemory();
   4279  bool addDebuggee();
   4280  bool addAllGlobalsAsDebuggees();
   4281  bool removeDebuggee();
   4282  bool removeAllDebuggees();
   4283  bool hasDebuggee();
   4284  bool getDebuggees();
   4285  bool getNewestFrame();
   4286  bool clearAllBreakpoints();
   4287  bool findScripts();
   4288  bool findSources();
   4289  bool findObjects();
   4290  bool findAllGlobals();
   4291  bool findSourceURLs();
   4292  bool makeGlobalObjectReference();
   4293  bool adoptDebuggeeValue();
   4294  bool adoptFrame();
   4295  bool adoptSource();
   4296  bool enableAsyncStack();
   4297  bool disableAsyncStack();
   4298  bool enableUnlimitedStacksCapturing();
   4299  bool disableUnlimitedStacksCapturing();
   4300 
   4301  using Method = bool (CallData::*)();
   4302 
   4303  template <Method MyMethod>
   4304  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
   4305 };
   4306 
   4307 template <Debugger::CallData::Method MyMethod>
   4308 /* static */
   4309 bool Debugger::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
   4310  CallArgs args = CallArgsFromVp(argc, vp);
   4311 
   4312  Debugger* dbg = Debugger_fromThisValue(cx, args, "method");
   4313  if (!dbg) {
   4314    return false;
   4315  }
   4316 
   4317  CallData data(cx, args, dbg);
   4318  return (data.*MyMethod)();
   4319 }
   4320 
   4321 /* static */
   4322 bool Debugger::getHookImpl(JSContext* cx, const CallArgs& args, Debugger& dbg,
   4323                           Hook which) {
   4324  MOZ_ASSERT(which >= 0 && which < HookCount);
   4325  args.rval().set(dbg.object->getReservedSlot(
   4326      JSSLOT_DEBUG_HOOK_START + std::underlying_type_t<Hook>(which)));
   4327  return true;
   4328 }
   4329 
   4330 /* static */
   4331 bool Debugger::setHookImpl(JSContext* cx, const CallArgs& args, Debugger& dbg,
   4332                           Hook which) {
   4333  MOZ_ASSERT(which >= 0 && which < HookCount);
   4334  if (!args.requireAtLeast(cx, "Debugger.setHook", 1)) {
   4335    return false;
   4336  }
   4337  if (args[0].isObject()) {
   4338    if (!args[0].toObject().isCallable()) {
   4339      return ReportIsNotFunction(cx, args[0], args.length() - 1);
   4340    }
   4341  } else if (!args[0].isUndefined()) {
   4342    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4343                              JSMSG_NOT_CALLABLE_OR_UNDEFINED);
   4344    return false;
   4345  }
   4346 
   4347  // Disallow simultaneous activation of OnEnterFrame and code coverage support;
   4348  // as they both use the execution observer flag. See Bug 1608891.
   4349  if (dbg.collectCoverageInfo && which == Hook::OnEnterFrame) {
   4350    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4351                              JSMSG_DEBUG_EXCLUSIVE_FRAME_COVERAGE);
   4352    return false;
   4353  }
   4354 
   4355  uint32_t slot = JSSLOT_DEBUG_HOOK_START + std::underlying_type_t<Hook>(which);
   4356  RootedValue oldHook(cx, dbg.object->getReservedSlot(slot));
   4357  dbg.object->setReservedSlot(slot, args[0]);
   4358  if (hookObservesAllExecution(which)) {
   4359    if (!dbg.updateObservesAllExecutionOnDebuggees(
   4360            cx, dbg.observesAllExecution())) {
   4361      dbg.object->setReservedSlot(slot, oldHook);
   4362      return false;
   4363    }
   4364  }
   4365 
   4366  Rooted<DebuggerDebuggeeLink*> debuggeeLink(cx, dbg.getDebuggeeLink());
   4367  if (dbg.hasAnyLiveHooks()) {
   4368    debuggeeLink->setLinkSlot(dbg);
   4369  } else {
   4370    debuggeeLink->clearLinkSlot();
   4371  }
   4372 
   4373  args.rval().setUndefined();
   4374  return true;
   4375 }
   4376 
   4377 /* static */
   4378 bool Debugger::getGarbageCollectionHook(JSContext* cx, const CallArgs& args,
   4379                                        Debugger& dbg) {
   4380  return getHookImpl(cx, args, dbg, OnGarbageCollection);
   4381 }
   4382 
   4383 /* static */
   4384 bool Debugger::setGarbageCollectionHook(JSContext* cx, const CallArgs& args,
   4385                                        Debugger& dbg) {
   4386  Rooted<JSObject*> oldHook(cx, dbg.getHook(OnGarbageCollection));
   4387 
   4388  if (!setHookImpl(cx, args, dbg, OnGarbageCollection)) {
   4389    // We want to maintain the invariant that the hook is always set when the
   4390    // Debugger is in the runtime's list, and vice-versa, so if we return early
   4391    // and don't adjust the watcher list below, we need to be sure that the
   4392    // hook didn't change.
   4393    MOZ_ASSERT(dbg.getHook(OnGarbageCollection) == oldHook);
   4394    return false;
   4395  }
   4396 
   4397  // Add or remove ourselves from the runtime's list of Debuggers that care
   4398  // about garbage collection.
   4399  JSObject* newHook = dbg.getHook(OnGarbageCollection);
   4400  if (!oldHook && newHook) {
   4401    cx->runtime()->onGarbageCollectionWatchers().pushBack(&dbg);
   4402  } else if (oldHook && !newHook) {
   4403    cx->runtime()->onGarbageCollectionWatchers().remove(&dbg);
   4404  }
   4405 
   4406  return true;
   4407 }
   4408 
   4409 bool Debugger::CallData::getOnDebuggerStatement() {
   4410  return getHookImpl(cx, args, *dbg, OnDebuggerStatement);
   4411 }
   4412 
   4413 bool Debugger::CallData::setOnDebuggerStatement() {
   4414  return setHookImpl(cx, args, *dbg, OnDebuggerStatement);
   4415 }
   4416 
   4417 bool Debugger::CallData::getOnExceptionUnwind() {
   4418  return getHookImpl(cx, args, *dbg, OnExceptionUnwind);
   4419 }
   4420 
   4421 bool Debugger::CallData::setOnExceptionUnwind() {
   4422  return setHookImpl(cx, args, *dbg, OnExceptionUnwind);
   4423 }
   4424 
   4425 bool Debugger::CallData::getOnNewScript() {
   4426  return getHookImpl(cx, args, *dbg, OnNewScript);
   4427 }
   4428 
   4429 bool Debugger::CallData::setOnNewScript() {
   4430  return setHookImpl(cx, args, *dbg, OnNewScript);
   4431 }
   4432 
   4433 bool Debugger::CallData::getOnNewPromise() {
   4434  return getHookImpl(cx, args, *dbg, OnNewPromise);
   4435 }
   4436 
   4437 bool Debugger::CallData::setOnNewPromise() {
   4438  return setHookImpl(cx, args, *dbg, OnNewPromise);
   4439 }
   4440 
   4441 bool Debugger::CallData::getOnPromiseSettled() {
   4442  return getHookImpl(cx, args, *dbg, OnPromiseSettled);
   4443 }
   4444 
   4445 bool Debugger::CallData::setOnPromiseSettled() {
   4446  return setHookImpl(cx, args, *dbg, OnPromiseSettled);
   4447 }
   4448 
   4449 bool Debugger::CallData::getOnEnterFrame() {
   4450  return getHookImpl(cx, args, *dbg, OnEnterFrame);
   4451 }
   4452 
   4453 bool Debugger::CallData::setOnEnterFrame() {
   4454  return setHookImpl(cx, args, *dbg, OnEnterFrame);
   4455 }
   4456 
   4457 bool Debugger::CallData::getOnNativeCall() {
   4458  return getHookImpl(cx, args, *dbg, OnNativeCall);
   4459 }
   4460 
   4461 bool Debugger::CallData::setOnNativeCall() {
   4462  RootedObject oldHook(cx, dbg->getHook(OnNativeCall));
   4463 
   4464  if (!setHookImpl(cx, args, *dbg, OnNativeCall)) {
   4465    return false;
   4466  }
   4467 
   4468  JSObject* newHook = dbg->getHook(OnNativeCall);
   4469  if (!oldHook && newHook) {
   4470    dbg->updateObservesNativeCallOnDebuggees(Observing);
   4471  } else if (oldHook && !newHook) {
   4472    dbg->updateObservesNativeCallOnDebuggees(NotObserving);
   4473  }
   4474 
   4475  return true;
   4476 }
   4477 
   4478 bool Debugger::CallData::getShouldAvoidSideEffects() {
   4479  args.rval().setBoolean(dbg->shouldAvoidSideEffects);
   4480  return true;
   4481 }
   4482 
   4483 bool Debugger::CallData::setShouldAvoidSideEffects() {
   4484  if (!args.requireAtLeast(cx, "Debugger.set shouldAvoidSideEffects", 1)) {
   4485    return false;
   4486  }
   4487 
   4488  dbg->shouldAvoidSideEffects = ToBoolean(args[0]);
   4489 
   4490  args.rval().setUndefined();
   4491  return true;
   4492 }
   4493 
   4494 bool Debugger::CallData::getOnNewGlobalObject() {
   4495  return getHookImpl(cx, args, *dbg, OnNewGlobalObject);
   4496 }
   4497 
   4498 bool Debugger::CallData::setOnNewGlobalObject() {
   4499  RootedObject oldHook(cx, dbg->getHook(OnNewGlobalObject));
   4500 
   4501  if (!setHookImpl(cx, args, *dbg, OnNewGlobalObject)) {
   4502    return false;
   4503  }
   4504 
   4505  // Add or remove ourselves from the runtime's list of Debuggers that care
   4506  // about new globals.
   4507  JSObject* newHook = dbg->getHook(OnNewGlobalObject);
   4508  if (!oldHook && newHook) {
   4509    cx->runtime()->onNewGlobalObjectWatchers().pushBack(dbg);
   4510  } else if (oldHook && !newHook) {
   4511    cx->runtime()->onNewGlobalObjectWatchers().remove(dbg);
   4512  }
   4513 
   4514  return true;
   4515 }
   4516 
   4517 bool Debugger::CallData::getUncaughtExceptionHook() {
   4518  args.rval().setObjectOrNull(dbg->uncaughtExceptionHook);
   4519  return true;
   4520 }
   4521 
   4522 bool Debugger::CallData::setUncaughtExceptionHook() {
   4523  if (!args.requireAtLeast(cx, "Debugger.set uncaughtExceptionHook", 1)) {
   4524    return false;
   4525  }
   4526  if (!args[0].isNull() &&
   4527      (!args[0].isObject() || !args[0].toObject().isCallable())) {
   4528    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4529                              JSMSG_ASSIGN_FUNCTION_OR_NULL,
   4530                              "uncaughtExceptionHook");
   4531    return false;
   4532  }
   4533  dbg->uncaughtExceptionHook = args[0].toObjectOrNull();
   4534  args.rval().setUndefined();
   4535  return true;
   4536 }
   4537 
   4538 bool Debugger::CallData::getAllowUnobservedAsmJS() {
   4539  args.rval().setBoolean(dbg->allowUnobservedAsmJS);
   4540  return true;
   4541 }
   4542 
   4543 bool Debugger::CallData::setAllowUnobservedAsmJS() {
   4544  if (!args.requireAtLeast(cx, "Debugger.set allowUnobservedAsmJS", 1)) {
   4545    return false;
   4546  }
   4547  dbg->allowUnobservedAsmJS = ToBoolean(args[0]);
   4548 
   4549  for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty();
   4550       r.popFront()) {
   4551    GlobalObject* global = r.front();
   4552    Realm* realm = global->realm();
   4553    realm->updateDebuggerObservesAsmJS();
   4554  }
   4555 
   4556  args.rval().setUndefined();
   4557  return true;
   4558 }
   4559 
   4560 bool Debugger::CallData::getAllowUnobservedWasm() {
   4561  args.rval().setBoolean(dbg->allowUnobservedWasm);
   4562  return true;
   4563 }
   4564 
   4565 bool Debugger::CallData::setAllowUnobservedWasm() {
   4566  if (!args.requireAtLeast(cx, "Debugger.set allowUnobservedWasm", 1)) {
   4567    return false;
   4568  }
   4569  dbg->allowUnobservedWasm = ToBoolean(args[0]);
   4570 
   4571  for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty();
   4572       r.popFront()) {
   4573    GlobalObject* global = r.front();
   4574    Realm* realm = global->realm();
   4575    realm->updateDebuggerObservesWasm();
   4576  }
   4577 
   4578  args.rval().setUndefined();
   4579  return true;
   4580 }
   4581 
   4582 bool Debugger::CallData::getExclusiveDebuggerOnEval() {
   4583  args.rval().setBoolean(dbg->exclusiveDebuggerOnEval);
   4584  return true;
   4585 }
   4586 
   4587 bool Debugger::CallData::setExclusiveDebuggerOnEval() {
   4588  if (!args.requireAtLeast(cx, "Debugger.set exclusiveDebuggerOnEval", 1)) {
   4589    return false;
   4590  }
   4591  dbg->exclusiveDebuggerOnEval = ToBoolean(args[0]);
   4592 
   4593  args.rval().setUndefined();
   4594  return true;
   4595 }
   4596 
   4597 bool Debugger::CallData::getInspectNativeCallArguments() {
   4598  args.rval().setBoolean(dbg->inspectNativeCallArguments);
   4599  return true;
   4600 }
   4601 
   4602 bool Debugger::CallData::setInspectNativeCallArguments() {
   4603  if (!args.requireAtLeast(cx, "Debugger.set inspectNativeCallArguments", 1)) {
   4604    return false;
   4605  }
   4606  dbg->inspectNativeCallArguments = ToBoolean(args[0]);
   4607 
   4608  args.rval().setUndefined();
   4609  return true;
   4610 }
   4611 
   4612 bool Debugger::CallData::getCollectCoverageInfo() {
   4613  args.rval().setBoolean(dbg->collectCoverageInfo);
   4614  return true;
   4615 }
   4616 
   4617 bool Debugger::CallData::setCollectCoverageInfo() {
   4618  if (!args.requireAtLeast(cx, "Debugger.set collectCoverageInfo", 1)) {
   4619    return false;
   4620  }
   4621 
   4622  // Disallow simultaneous activation of OnEnterFrame and code coverage support;
   4623  // as they both use the execution observer flag. See Bug 1608891.
   4624  uint32_t slot = JSSLOT_DEBUG_HOOK_START +
   4625                  std::underlying_type_t<Hook>(Hook::OnEnterFrame);
   4626  if (!dbg->object->getReservedSlot(slot).isUndefined()) {
   4627    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4628                              JSMSG_DEBUG_EXCLUSIVE_FRAME_COVERAGE);
   4629    return false;
   4630  }
   4631 
   4632  if (cx->realm()->isTracingExecution()) {
   4633    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4634                              JSMSG_DEBUG_EXCLUSIVE_EXECUTION_TRACE_COVERAGE);
   4635    return false;
   4636  }
   4637 
   4638  dbg->collectCoverageInfo = ToBoolean(args[0]);
   4639 
   4640  IsObserving observing = dbg->collectCoverageInfo ? Observing : NotObserving;
   4641  if (!dbg->updateObservesCoverageOnDebuggees(cx, observing)) {
   4642    return false;
   4643  }
   4644 
   4645  args.rval().setUndefined();
   4646  return true;
   4647 }
   4648 
   4649 bool Debugger::CallData::getMemory() {
   4650  Value memoryValue =
   4651      dbg->object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE);
   4652 
   4653  if (!memoryValue.isObject()) {
   4654    RootedObject memory(cx, DebuggerMemory::create(cx, dbg));
   4655    if (!memory) {
   4656      return false;
   4657    }
   4658    memoryValue = ObjectValue(*memory);
   4659  }
   4660 
   4661  args.rval().set(memoryValue);
   4662  return true;
   4663 }
   4664 
   4665 /*
   4666 * Given a value used to designate a global (there's quite a variety; see the
   4667 * docs), return the actual designee.
   4668 *
   4669 * Note that this does not check whether the designee is marked "invisible to
   4670 * Debugger" or not; different callers need to handle invisible-to-Debugger
   4671 * globals in different ways.
   4672 */
   4673 GlobalObject* Debugger::unwrapDebuggeeArgument(JSContext* cx, const Value& v) {
   4674  if (!v.isObject()) {
   4675    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4676                              JSMSG_UNEXPECTED_TYPE, "argument",
   4677                              "not a global object");
   4678    return nullptr;
   4679  }
   4680 
   4681  RootedObject obj(cx, &v.toObject());
   4682 
   4683  // If it's a Debugger.Object belonging to this debugger, dereference that.
   4684  if (obj->getClass() == &DebuggerObject::class_) {
   4685    RootedValue rv(cx, v);
   4686    if (!unwrapDebuggeeValue(cx, &rv)) {
   4687      return nullptr;
   4688    }
   4689    obj = &rv.toObject();
   4690  }
   4691 
   4692  // If we have a cross-compartment wrapper, dereference as far as is secure.
   4693  //
   4694  // Since we're dealing with globals, we may have a WindowProxy here.  So we
   4695  // have to make sure to do a dynamic unwrap, and we want to unwrap the
   4696  // WindowProxy too, if we have one.
   4697  obj = CheckedUnwrapDynamic(obj, cx, /* stopAtWindowProxy = */ false);
   4698  if (!obj) {
   4699    ReportAccessDenied(cx);
   4700    return nullptr;
   4701  }
   4702 
   4703  if (JS_IsDeadWrapper(obj)) {
   4704    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
   4705    return nullptr;
   4706  }
   4707 
   4708  // If that didn't produce a global object, it's an error.
   4709  if (!obj->is<GlobalObject>()) {
   4710    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4711                              JSMSG_UNEXPECTED_TYPE, "argument",
   4712                              "not a global object");
   4713    return nullptr;
   4714  }
   4715 
   4716  return &obj->as<GlobalObject>();
   4717 }
   4718 
   4719 bool Debugger::CallData::addDebuggee() {
   4720  if (!args.requireAtLeast(cx, "Debugger.addDebuggee", 1)) {
   4721    return false;
   4722  }
   4723  Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
   4724  if (!global) {
   4725    return false;
   4726  }
   4727 
   4728  if (!dbg->addDebuggeeGlobal(cx, global)) {
   4729    return false;
   4730  }
   4731 
   4732  RootedValue v(cx, ObjectValue(*global));
   4733  if (!dbg->wrapDebuggeeValue(cx, &v)) {
   4734    return false;
   4735  }
   4736  args.rval().set(v);
   4737  return true;
   4738 }
   4739 
   4740 bool Debugger::CallData::addAllGlobalsAsDebuggees() {
   4741  for (CompartmentsIter comp(cx->runtime()); !comp.done(); comp.next()) {
   4742    if (comp == dbg->object->compartment()) {
   4743      continue;
   4744    }
   4745    for (RealmsInCompartmentIter r(comp); !r.done(); r.next()) {
   4746      if (r->creationOptions().invisibleToDebugger()) {
   4747        continue;
   4748      }
   4749      if (!r->hasInitializedGlobal()) {
   4750        continue;
   4751      }
   4752      r->compartment()->gcState.scheduledForDestruction = false;
   4753      Rooted<GlobalObject*> global(cx, r->maybeGlobal());
   4754      MOZ_ASSERT(global);
   4755      if (!dbg->addDebuggeeGlobal(cx, global)) {
   4756        return false;
   4757      }
   4758    }
   4759  }
   4760 
   4761  args.rval().setUndefined();
   4762  return true;
   4763 }
   4764 
   4765 bool Debugger::CallData::removeDebuggee() {
   4766  if (!args.requireAtLeast(cx, "Debugger.removeDebuggee", 1)) {
   4767    return false;
   4768  }
   4769  Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
   4770  if (!global) {
   4771    return false;
   4772  }
   4773 
   4774  ExecutionObservableRealms obs(cx);
   4775 
   4776  if (dbg->debuggees.has(global)) {
   4777    dbg->removeDebuggeeGlobal(cx->gcContext(), global, nullptr, FromSweep::No);
   4778 
   4779    // Only update the realm if there are no Debuggers left, as it's
   4780    // expensive to check if no other Debugger has a live script or frame
   4781    // hook on any of the current on-stack debuggee frames.
   4782    if (!global->hasDebuggers() && !obs.add(global->realm())) {
   4783      return false;
   4784    }
   4785    if (!updateExecutionObservability(cx, obs, NotObserving)) {
   4786      return false;
   4787    }
   4788  }
   4789 
   4790  args.rval().setUndefined();
   4791  return true;
   4792 }
   4793 
   4794 bool Debugger::CallData::removeAllDebuggees() {
   4795  ExecutionObservableRealms obs(cx);
   4796 
   4797  for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
   4798    Rooted<GlobalObject*> global(cx, e.front());
   4799    dbg->removeDebuggeeGlobal(cx->gcContext(), global, &e, FromSweep::No);
   4800 
   4801    // See note about adding to the observable set in removeDebuggee.
   4802    if (!global->hasDebuggers() && !obs.add(global->realm())) {
   4803      return false;
   4804    }
   4805  }
   4806 
   4807  if (!updateExecutionObservability(cx, obs, NotObserving)) {
   4808    return false;
   4809  }
   4810 
   4811  args.rval().setUndefined();
   4812  return true;
   4813 }
   4814 
   4815 bool Debugger::CallData::hasDebuggee() {
   4816  if (!args.requireAtLeast(cx, "Debugger.hasDebuggee", 1)) {
   4817    return false;
   4818  }
   4819  GlobalObject* global = dbg->unwrapDebuggeeArgument(cx, args[0]);
   4820  if (!global) {
   4821    return false;
   4822  }
   4823  args.rval().setBoolean(!!dbg->debuggees.lookup(global));
   4824  return true;
   4825 }
   4826 
   4827 bool Debugger::CallData::getDebuggees() {
   4828  // Obtain the list of debuggees before wrapping each debuggee, as a GC could
   4829  // update the debuggees set while we are iterating it.
   4830  unsigned count = dbg->debuggees.count();
   4831  RootedValueVector debuggees(cx);
   4832  if (!debuggees.resize(count)) {
   4833    return false;
   4834  }
   4835  unsigned i = 0;
   4836  {
   4837    JS::AutoCheckCannotGC nogc;
   4838    for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty();
   4839         e.popFront()) {
   4840      debuggees[i++].setObject(*e.front().get());
   4841    }
   4842  }
   4843 
   4844  Rooted<ArrayObject*> arrobj(cx, NewDenseFullyAllocatedArray(cx, count));
   4845  if (!arrobj) {
   4846    return false;
   4847  }
   4848  arrobj->ensureDenseInitializedLength(0, count);
   4849  for (i = 0; i < count; i++) {
   4850    RootedValue v(cx, debuggees[i]);
   4851    if (!dbg->wrapDebuggeeValue(cx, &v)) {
   4852      return false;
   4853    }
   4854    arrobj->setDenseElement(i, v);
   4855  }
   4856 
   4857  args.rval().setObject(*arrobj);
   4858  return true;
   4859 }
   4860 
   4861 bool Debugger::CallData::getNewestFrame() {
   4862  // Since there may be multiple contexts, use AllFramesIter.
   4863  for (AllFramesIter i(cx); !i.done(); ++i) {
   4864    if (dbg->observesFrame(i)) {
   4865      // Ensure that Ion frames are rematerialized. Only rematerialized
   4866      // Ion frames may be used as AbstractFramePtrs.
   4867      if (i.isIon() && !i.ensureHasRematerializedFrame(cx)) {
   4868        return false;
   4869      }
   4870      AbstractFramePtr frame = i.abstractFramePtr();
   4871      FrameIter iter(i.activation()->cx());
   4872      while (!iter.hasUsableAbstractFramePtr() ||
   4873             iter.abstractFramePtr() != frame) {
   4874        ++iter;
   4875      }
   4876      return dbg->getFrame(cx, iter, args.rval());
   4877    }
   4878  }
   4879  args.rval().setNull();
   4880  return true;
   4881 }
   4882 
   4883 bool Debugger::CallData::clearAllBreakpoints() {
   4884  JS::GCContext* gcx = cx->gcContext();
   4885  Breakpoint* nextbp;
   4886  for (Breakpoint* bp = dbg->firstBreakpoint(); bp; bp = nextbp) {
   4887    nextbp = bp->nextInDebugger();
   4888 
   4889    bp->remove(gcx);
   4890  }
   4891  MOZ_ASSERT(!dbg->firstBreakpoint());
   4892 
   4893  return true;
   4894 }
   4895 
   4896 /* static */
   4897 bool Debugger::construct(JSContext* cx, unsigned argc, Value* vp) {
   4898  CallArgs args = CallArgsFromVp(argc, vp);
   4899 
   4900  // Check that the arguments, if any, are cross-compartment wrappers.
   4901  for (unsigned i = 0; i < args.length(); i++) {
   4902    JSObject* argobj = RequireObject(cx, args[i]);
   4903    if (!argobj) {
   4904      return false;
   4905    }
   4906    if (!argobj->is<CrossCompartmentWrapperObject>()) {
   4907      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4908                                JSMSG_DEBUG_CCW_REQUIRED, "Debugger");
   4909      return false;
   4910    }
   4911  }
   4912 
   4913  // Get Debugger.prototype.
   4914  RootedValue v(cx);
   4915  RootedObject callee(cx, &args.callee());
   4916  if (!GetProperty(cx, callee, callee, cx->names().prototype, &v)) {
   4917    return false;
   4918  }
   4919  Rooted<NativeObject*> proto(cx, &v.toObject().as<NativeObject>());
   4920  MOZ_ASSERT(proto->is<DebuggerPrototypeObject>());
   4921 
   4922  // Make the new Debugger object. Each one has a reference to
   4923  // Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The
   4924  // rest of the reserved slots are for hooks; they default to undefined.
   4925  Rooted<DebuggerInstanceObject*> obj(
   4926      cx, NewTenuredObjectWithGivenProto<DebuggerInstanceObject>(cx, proto));
   4927  if (!obj) {
   4928    return false;
   4929  }
   4930  for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP;
   4931       slot++) {
   4932    obj->setReservedSlot(slot, proto->getReservedSlot(slot));
   4933  }
   4934  obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue());
   4935 
   4936  Rooted<NativeObject*> livenessLink(
   4937      cx, NewObjectWithGivenProto<DebuggerDebuggeeLink>(cx, nullptr));
   4938  if (!livenessLink) {
   4939    return false;
   4940  }
   4941  obj->setReservedSlot(JSSLOT_DEBUG_DEBUGGEE_LINK, ObjectValue(*livenessLink));
   4942 
   4943  Debugger* debugger;
   4944  {
   4945    // Construct the underlying C++ object.
   4946    auto dbg = cx->make_unique<Debugger>(cx, obj.get());
   4947    if (!dbg) {
   4948      return false;
   4949    }
   4950 
   4951    // The object owns the released pointer.
   4952    debugger = dbg.release();
   4953    InitReservedSlot(obj, JSSLOT_DEBUG_DEBUGGER, debugger, MemoryUse::Debugger);
   4954  }
   4955 
   4956  // Add the initial debuggees, if any.
   4957  for (unsigned i = 0; i < args.length(); i++) {
   4958    JSObject& wrappedObj =
   4959        args[i].toObject().as<ProxyObject>().private_().toObject();
   4960    Rooted<GlobalObject*> debuggee(cx, &wrappedObj.nonCCWGlobal());
   4961    if (!debugger->addDebuggeeGlobal(cx, debuggee)) {
   4962      return false;
   4963    }
   4964  }
   4965 
   4966  args.rval().setObject(*obj);
   4967  return true;
   4968 }
   4969 
   4970 bool Debugger::addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> global) {
   4971  if (debuggees.has(global)) {
   4972    return true;
   4973  }
   4974 
   4975  // Callers should generally be unable to get a reference to a debugger-
   4976  // invisible global in order to pass it to addDebuggee. But this is possible
   4977  // with certain testing aides we expose in the shell, so just make addDebuggee
   4978  // throw in that case.
   4979  Realm* debuggeeRealm = global->realm();
   4980  if (debuggeeRealm->creationOptions().invisibleToDebugger()) {
   4981    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4982                              JSMSG_DEBUG_CANT_DEBUG_GLOBAL);
   4983    return false;
   4984  }
   4985 
   4986  // Debugger and debuggee must be in different compartments.
   4987  if (debuggeeRealm->compartment() == object->compartment()) {
   4988    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4989                              JSMSG_DEBUG_SAME_COMPARTMENT);
   4990    return false;
   4991  }
   4992 
   4993  // Check for cycles. If global's realm is reachable from this Debugger
   4994  // object's realm by following debuggee-to-debugger links, then adding
   4995  // global would create a cycle. (Typically nobody is debugging the
   4996  // debugger, in which case we zip through this code without looping.)
   4997  Vector<Realm*> visited(cx);
   4998  if (!visited.append(object->realm())) {
   4999    return false;
   5000  }
   5001  for (size_t i = 0; i < visited.length(); i++) {
   5002    Realm* realm = visited[i];
   5003    if (realm == debuggeeRealm) {
   5004      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_LOOP);
   5005      return false;
   5006    }
   5007 
   5008    // Find all realms containing debuggers debugging realm's global object.
   5009    // Add those realms to visited.
   5010    if (realm->isDebuggee()) {
   5011      JS::AutoAssertNoGC nogc;
   5012      for (Realm::DebuggerVectorEntry& entry : realm->getDebuggers(nogc)) {
   5013        Realm* next = entry.dbg->object->realm();
   5014        if (std::find(visited.begin(), visited.end(), next) == visited.end()) {
   5015          if (!visited.append(next)) {
   5016            return false;
   5017          }
   5018        }
   5019      }
   5020    }
   5021  }
   5022 
   5023  // For global to become this js::Debugger's debuggee:
   5024  //
   5025  // 1. this js::Debugger must be in global->getDebuggers(),
   5026  // 2. global must be in this->debuggees,
   5027  // 3. the debuggee's zone must be in this->debuggeeZones,
   5028  // 4. if we are tracking allocations, the SavedStacksMetadataBuilder must be
   5029  //    installed for this realm, and
   5030  // 5. Realm::isDebuggee()'s bit must be set.
   5031  //
   5032  // All five indications must be kept consistent.
   5033 
   5034  AutoRealm ar(cx, global);
   5035  Zone* zone = global->zone();
   5036 
   5037  RootedObject debuggeeLink(cx, getDebuggeeLink());
   5038  if (!cx->compartment()->wrap(cx, &debuggeeLink)) {
   5039    return false;
   5040  }
   5041 
   5042  // (1)
   5043  JS::AutoAssertNoGC nogc;
   5044  auto& globalDebuggers = global->getDebuggers(nogc);
   5045  if (!globalDebuggers.append(Realm::DebuggerVectorEntry(this, debuggeeLink))) {
   5046    ReportOutOfMemory(cx);
   5047    return false;
   5048  }
   5049  auto globalDebuggersGuard = MakeScopeExit([&] { globalDebuggers.popBack(); });
   5050 
   5051  // (2)
   5052  if (!debuggees.put(global)) {
   5053    ReportOutOfMemory(cx);
   5054    return false;
   5055  }
   5056  auto debuggeesGuard = MakeScopeExit([&] { debuggees.remove(global); });
   5057 
   5058  bool addingZoneRelation = !debuggeeZones.has(zone);
   5059 
   5060  // (3)
   5061  if (addingZoneRelation && !debuggeeZones.put(zone)) {
   5062    ReportOutOfMemory(cx);
   5063    return false;
   5064  }
   5065  auto debuggeeZonesGuard = MakeScopeExit([&] {
   5066    if (addingZoneRelation) {
   5067      debuggeeZones.remove(zone);
   5068    }
   5069  });
   5070 
   5071  // (4)
   5072  if (trackingAllocationSites &&
   5073      !Debugger::addAllocationsTracking(cx, global)) {
   5074    return false;
   5075  }
   5076 
   5077  auto allocationsTrackingGuard = MakeScopeExit([&] {
   5078    if (trackingAllocationSites) {
   5079      Debugger::removeAllocationsTracking(*global);
   5080    }
   5081  });
   5082 
   5083  // (5)
   5084  AutoRestoreRealmDebugMode debugModeGuard(debuggeeRealm);
   5085  debuggeeRealm->setIsDebuggee();
   5086  debuggeeRealm->updateDebuggerObservesAsmJS();
   5087  debuggeeRealm->updateDebuggerObservesWasm();
   5088  debuggeeRealm->updateDebuggerObservesCoverage();
   5089  if (observesAllExecution() &&
   5090      !ensureExecutionObservabilityOfRealm(cx, debuggeeRealm)) {
   5091    return false;
   5092  }
   5093 
   5094  globalDebuggersGuard.release();
   5095  debuggeesGuard.release();
   5096  debuggeeZonesGuard.release();
   5097  allocationsTrackingGuard.release();
   5098  debugModeGuard.release();
   5099  return true;
   5100 }
   5101 
   5102 void Debugger::recomputeDebuggeeZoneSet() {
   5103  AutoEnterOOMUnsafeRegion oomUnsafe;
   5104  debuggeeZones.clear();
   5105  for (auto range = debuggees.all(); !range.empty(); range.popFront()) {
   5106    if (!debuggeeZones.put(range.front().unbarrieredGet()->zone())) {
   5107      oomUnsafe.crash("Debugger::removeDebuggeeGlobal");
   5108    }
   5109  }
   5110 }
   5111 
   5112 template <typename T, typename AP>
   5113 static T* findDebuggerInVector(Debugger* dbg, Vector<T, 0, AP>* vec) {
   5114  T* p;
   5115  for (p = vec->begin(); p != vec->end(); p++) {
   5116    if (p->dbg == dbg) {
   5117      break;
   5118    }
   5119  }
   5120  MOZ_ASSERT(p != vec->end());
   5121  return p;
   5122 }
   5123 
   5124 void Debugger::removeDebuggeeGlobal(JS::GCContext* gcx, GlobalObject* global,
   5125                                    WeakGlobalObjectSet::Enum* debugEnum,
   5126                                    FromSweep fromSweep) {
   5127  // The caller might have found global by enumerating this->debuggees; if
   5128  // so, use HashSet::Enum::removeFront rather than HashSet::remove below,
   5129  // to avoid invalidating the live enumerator.
   5130  MOZ_ASSERT(debuggees.has(global));
   5131  MOZ_ASSERT(debuggeeZones.has(global->zone()));
   5132  MOZ_ASSERT_IF(debugEnum, debugEnum->front().unbarrieredGet() == global);
   5133 
   5134  // Clear this global's generators from generatorFrames as well.
   5135  //
   5136  // This method can be called either from script (dbg.removeDebuggee) or during
   5137  // GC sweeping, because the Debugger, debuggee global, or both are being GC'd.
   5138  //
   5139  // When called from script, it's okay to iterate over generatorFrames and
   5140  // touch its keys and values (even when an incremental GC is in progress).
   5141  // When called from GC, it's not okay; the keys and values may be dying. But
   5142  // in that case, we can actually just skip the loop entirely! If the Debugger
   5143  // is going away, it doesn't care about the state of its generatorFrames
   5144  // table, and the Debugger.Frame finalizer will fix up the generator observer
   5145  // counts.
   5146  if (fromSweep == FromSweep::No) {
   5147    for (GeneratorWeakMap::Enum e(generatorFrames); !e.empty(); e.popFront()) {
   5148      AbstractGeneratorObject& genObj = *e.front().key();
   5149      if (&genObj.global() == global) {
   5150        terminateDebuggerFrame(gcx, this, e.front().value(), NullFramePtr(),
   5151                               nullptr, &e);
   5152      }
   5153    }
   5154  }
   5155 
   5156  for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) {
   5157    AbstractFramePtr frame = e.front().key();
   5158    if (frame.hasGlobal(global)) {
   5159      terminateDebuggerFrame(gcx, this, e.front().value(), frame, &e);
   5160    }
   5161  }
   5162 
   5163  JS::AutoAssertNoGC nogc;
   5164  auto& globalDebuggersVector = global->getDebuggers(nogc);
   5165 
   5166  // The relation must be removed from up to three places:
   5167  // globalDebuggersVector and debuggees for sure, and possibly the
   5168  // compartment's debuggee set.
   5169  //
   5170  // The debuggee zone set is recomputed on demand. This avoids refcounting
   5171  // and in practice we have relatively few debuggees that tend to all be in
   5172  // the same zone. If after recomputing the debuggee zone set, this global's
   5173  // zone is not in the set, then we must remove ourselves from the zone's
   5174  // vector of observing debuggers.
   5175  globalDebuggersVector.erase(
   5176      findDebuggerInVector(this, &globalDebuggersVector));
   5177 
   5178  if (debugEnum) {
   5179    debugEnum->removeFront();
   5180  } else {
   5181    debuggees.remove(global);
   5182  }
   5183 
   5184  recomputeDebuggeeZoneSet();
   5185 
   5186  // Remove all breakpoints for the debuggee.
   5187  Breakpoint* nextbp;
   5188  for (Breakpoint* bp = firstBreakpoint(); bp; bp = nextbp) {
   5189    nextbp = bp->nextInDebugger();
   5190 
   5191    if (bp->site->realm() == global->realm()) {
   5192      bp->remove(gcx);
   5193    }
   5194  }
   5195  MOZ_ASSERT_IF(debuggees.empty(), !firstBreakpoint());
   5196 
   5197  // If we are tracking allocation sites, we need to remove the object
   5198  // metadata callback from this global's realm.
   5199  if (trackingAllocationSites) {
   5200    Debugger::removeAllocationsTracking(*global);
   5201  }
   5202 
   5203  if (!global->realm()->hasDebuggers() &&
   5204      !global->realm()->isTracingExecution()) {
   5205    global->realm()->unsetIsDebuggee();
   5206  } else {
   5207    global->realm()->updateDebuggerObservesAllExecution();
   5208    global->realm()->updateDebuggerObservesAsmJS();
   5209    global->realm()->updateDebuggerObservesWasm();
   5210    global->realm()->updateDebuggerObservesCoverage();
   5211  }
   5212 }
   5213 
   5214 class MOZ_STACK_CLASS Debugger::QueryBase {
   5215 protected:
   5216  QueryBase(JSContext* cx, Debugger* dbg)
   5217      : cx(cx),
   5218        debugger(dbg),
   5219        iterMarker(&cx->runtime()->gc),
   5220        realms(cx->zone()) {}
   5221 
   5222  // The context in which we should do our work.
   5223  JSContext* cx;
   5224 
   5225  // The debugger for which we conduct queries.
   5226  Debugger* debugger;
   5227 
   5228  // Require the set of realms to stay fixed while the query is alive.
   5229  gc::AutoEnterIteration iterMarker;
   5230 
   5231  using RealmSet = HashSet<Realm*, DefaultHasher<Realm*>, ZoneAllocPolicy>;
   5232 
   5233  // A script must be in one of these realms to match the query.
   5234  RealmSet realms;
   5235 
   5236  // Indicates whether OOM has occurred while matching.
   5237  bool oom = false;
   5238 
   5239  bool addRealm(Realm* realm) { return realms.put(realm); }
   5240 
   5241  // Arrange for this query to match only scripts that run in |global|.
   5242  bool matchSingleGlobal(GlobalObject* global) {
   5243    MOZ_ASSERT(realms.count() == 0);
   5244    if (!addRealm(global->realm())) {
   5245      ReportOutOfMemory(cx);
   5246      return false;
   5247    }
   5248    return true;
   5249  }
   5250 
   5251  // Arrange for this ScriptQuery to match all scripts running in debuggee
   5252  // globals.
   5253  bool matchAllDebuggeeGlobals() {
   5254    MOZ_ASSERT(realms.count() == 0);
   5255    // Build our realm set from the debugger's set of debuggee globals.
   5256    for (WeakGlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty();
   5257         r.popFront()) {
   5258      if (!addRealm(r.front()->realm())) {
   5259        ReportOutOfMemory(cx);
   5260        return false;
   5261      }
   5262    }
   5263    return true;
   5264  }
   5265 };
   5266 
   5267 /*
   5268 * A class for parsing 'findScripts' query arguments and searching for
   5269 * scripts that match the criteria they represent.
   5270 */
   5271 class MOZ_STACK_CLASS Debugger::ScriptQuery : public Debugger::QueryBase {
   5272 public:
   5273  /* Construct a ScriptQuery to use matching scripts for |dbg|. */
   5274  ScriptQuery(JSContext* cx, Debugger* dbg)
   5275      : QueryBase(cx, dbg),
   5276        url(cx),
   5277        displayURLString(cx),
   5278        source(cx, AsVariant(static_cast<ScriptSourceObject*>(nullptr))),
   5279        scriptVector(cx, BaseScriptVector(cx)),
   5280        partialMatchVector(cx, BaseScriptVector(cx)),
   5281        wasmInstanceVector(cx, WasmInstanceObjectVector(cx)) {}
   5282 
   5283  /*
   5284   * Parse the query object |query|, and prepare to match only the scripts
   5285   * it specifies.
   5286   */
   5287  bool parseQuery(HandleObject query) {
   5288    // Check for a 'global' property, which limits the results to those
   5289    // scripts scoped to a particular global object.
   5290    RootedValue global(cx);
   5291    if (!GetProperty(cx, query, query, cx->names().global, &global)) {
   5292      return false;
   5293    }
   5294    if (global.isUndefined()) {
   5295      if (!matchAllDebuggeeGlobals()) {
   5296        return false;
   5297      }
   5298    } else {
   5299      GlobalObject* globalObject = debugger->unwrapDebuggeeArgument(cx, global);
   5300      if (!globalObject) {
   5301        return false;
   5302      }
   5303 
   5304      // If the given global isn't a debuggee, just leave the set of
   5305      // acceptable globals empty; we'll return no scripts.
   5306      if (debugger->debuggees.has(globalObject)) {
   5307        if (!matchSingleGlobal(globalObject)) {
   5308          return false;
   5309        }
   5310      }
   5311    }
   5312 
   5313    // Check for a 'url' property.
   5314    if (!GetProperty(cx, query, query, cx->names().url, &url)) {
   5315      return false;
   5316    }
   5317    if (!url.isUndefined() && !url.isString()) {
   5318      JS_ReportErrorNumberASCII(
   5319          cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   5320          "query object's 'url' property", "neither undefined nor a string");
   5321      return false;
   5322    }
   5323 
   5324    // Check for a 'source' property
   5325    RootedValue debuggerSource(cx);
   5326    if (!GetProperty(cx, query, query, cx->names().source, &debuggerSource)) {
   5327      return false;
   5328    }
   5329    if (!debuggerSource.isUndefined()) {
   5330      if (!debuggerSource.isObject() ||
   5331          !debuggerSource.toObject().is<DebuggerSource>()) {
   5332        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5333                                  JSMSG_UNEXPECTED_TYPE,
   5334                                  "query object's 'source' property",
   5335                                  "not undefined nor a Debugger.Source object");
   5336        return false;
   5337      }
   5338 
   5339      DebuggerSource& debuggerSourceObj =
   5340          debuggerSource.toObject().as<DebuggerSource>();
   5341 
   5342      // If it does have an owner, it should match the Debugger we're
   5343      // calling findScripts on. It would work fine even if it didn't,
   5344      // but mixing Debugger.Sources is probably a sign of confusion.
   5345      if (debuggerSourceObj.owner() != debugger) {
   5346        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5347                                  JSMSG_DEBUG_WRONG_OWNER, "Debugger.Source");
   5348        return false;
   5349      }
   5350 
   5351      hasSource = true;
   5352      source = debuggerSourceObj.getReferent();
   5353    }
   5354 
   5355    // Check for a 'displayURL' property.
   5356    RootedValue displayURL(cx);
   5357    if (!GetProperty(cx, query, query, cx->names().displayURL, &displayURL)) {
   5358      return false;
   5359    }
   5360    if (!displayURL.isUndefined() && !displayURL.isString()) {
   5361      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5362                                JSMSG_UNEXPECTED_TYPE,
   5363                                "query object's 'displayURL' property",
   5364                                "neither undefined nor a string");
   5365      return false;
   5366    }
   5367 
   5368    if (displayURL.isString()) {
   5369      displayURLString = displayURL.toString()->ensureLinear(cx);
   5370      if (!displayURLString) {
   5371        return false;
   5372      }
   5373    }
   5374 
   5375    // Check for a 'line' property.
   5376    RootedValue lineProperty(cx);
   5377    if (!GetProperty(cx, query, query, cx->names().line, &lineProperty)) {
   5378      return false;
   5379    }
   5380    if (lineProperty.isUndefined()) {
   5381      hasLine = false;
   5382    } else if (lineProperty.isNumber()) {
   5383      if (displayURL.isUndefined() && url.isUndefined() && !hasSource) {
   5384        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5385                                  JSMSG_QUERY_LINE_WITHOUT_URL,
   5386                                  "'line' property");
   5387        return false;
   5388      }
   5389      if (!parsePositiveInteger(lineProperty, line, JSMSG_DEBUG_BAD_LINE)) {
   5390        return false;
   5391      }
   5392      hasLine = true;
   5393      lineEnd = line;
   5394    } else {
   5395      JS_ReportErrorNumberASCII(
   5396          cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   5397          "query object's 'line' property", "neither undefined nor an integer");
   5398      return false;
   5399    }
   5400 
   5401    // Check for a 'start' property.
   5402    RootedValue startProperty(cx);
   5403    if (!GetProperty(cx, query, query, cx->names().start, &startProperty)) {
   5404      return false;
   5405    }
   5406    if (startProperty.isObject()) {
   5407      Rooted<JSObject*> startObject(cx, &startProperty.toObject());
   5408      if (!parseLineColumnObject(startObject, "start", line, columnStart)) {
   5409        return false;
   5410      }
   5411      hasLine = true;
   5412    } else if (!startProperty.isUndefined()) {
   5413      JS_ReportErrorNumberASCII(
   5414          cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   5415          "query object's 'start' property", "neither undefined nor an object");
   5416      return false;
   5417    }
   5418 
   5419    // Check for a 'end' property.
   5420    RootedValue endProperty(cx);
   5421    if (!GetProperty(cx, query, query, cx->names().end, &endProperty)) {
   5422      return false;
   5423    }
   5424    if (endProperty.isObject()) {
   5425      Rooted<JSObject*> endObject(cx, &endProperty.toObject());
   5426      if (!parseLineColumnObject(endObject, "end", lineEnd, columnEnd)) {
   5427        return false;
   5428      }
   5429    } else if (!endProperty.isUndefined()) {
   5430      JS_ReportErrorNumberASCII(
   5431          cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   5432          "query object's 'end' property", "neither undefined nor an object");
   5433      return false;
   5434    }
   5435 
   5436    if (startProperty.isUndefined() ^ endProperty.isUndefined()) {
   5437      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5438                                JSMSG_QUERY_USE_START_AND_END_TOGETHER);
   5439      return false;
   5440    }
   5441 
   5442    if (!startProperty.isUndefined()) {
   5443      // endProperty is also not undefined here
   5444      if (displayURL.isUndefined() && url.isUndefined() && !hasSource) {
   5445        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5446                                  JSMSG_QUERY_LINE_WITHOUT_URL,
   5447                                  "'start' and 'end' properties");
   5448        return false;
   5449      }
   5450    }
   5451 
   5452    if (hasLine && lineEnd < line) {
   5453      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5454                                JSMSG_QUERY_START_LINE_IS_AFTER_END);
   5455      return false;
   5456    }
   5457 
   5458    // Check for an 'innermost' property.
   5459    PropertyName* innermostName = cx->names().innermost;
   5460    RootedValue innermostProperty(cx);
   5461    if (!GetProperty(cx, query, query, innermostName, &innermostProperty)) {
   5462      return false;
   5463    }
   5464    innermost = ToBoolean(innermostProperty);
   5465    if (innermost) {
   5466      // Technically, we need only check hasLine, but this is clearer.
   5467      if ((displayURL.isUndefined() && url.isUndefined() && !hasSource) ||
   5468          !hasLine) {
   5469        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5470                                  JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL);
   5471        return false;
   5472      }
   5473    }
   5474 
   5475    return true;
   5476  }
   5477 
   5478  /* Set up this ScriptQuery appropriately for a missing query argument. */
   5479  bool omittedQuery() {
   5480    url.setUndefined();
   5481    hasLine = false;
   5482    innermost = false;
   5483    displayURLString = nullptr;
   5484    return matchAllDebuggeeGlobals();
   5485  }
   5486 
   5487  /*
   5488   * Search all relevant realms and the stack for scripts matching
   5489   * this query, and append the matching scripts to |scriptVector|.
   5490   */
   5491  bool findScripts() {
   5492    if (!prepareQuery()) {
   5493      return false;
   5494    }
   5495 
   5496    Realm* singletonRealm = nullptr;
   5497    if (realms.count() == 1) {
   5498      singletonRealm = realms.all().front();
   5499    }
   5500 
   5501    // Search each realm for debuggee scripts.
   5502    MOZ_ASSERT(scriptVector.empty());
   5503    MOZ_ASSERT(partialMatchVector.empty());
   5504    oom = false;
   5505    IterateScripts(cx, singletonRealm, this, considerScript);
   5506    if (oom) {
   5507      ReportOutOfMemory(cx);
   5508      return false;
   5509    }
   5510 
   5511    // If we are filtering by line number, the lazy BaseScripts were not checked
   5512    // yet since they do not implement `GetScriptLineExtent`. Instead we revisit
   5513    // each result script and delazify its children and add any matching ones to
   5514    // the results list.
   5515    MOZ_ASSERT(hasLine || partialMatchVector.empty());
   5516    Rooted<BaseScript*> script(cx);
   5517    RootedFunction fun(cx);
   5518    while (!partialMatchVector.empty()) {
   5519      script = partialMatchVector.popCopy();
   5520 
   5521      // As a performance optimization, we can skip scripts that are definitely
   5522      // out-of-bounds for the target line. This was checked before adding to
   5523      // the partialMatchVector, but the bound may have improved since then.
   5524      if (script->extent().sourceEnd <= sourceOffsetLowerBound) {
   5525        continue;
   5526      }
   5527 
   5528      MOZ_ASSERT(script->isFunction());
   5529      MOZ_ASSERT(script->isReadyForDelazification());
   5530 
   5531      fun = script->function();
   5532 
   5533      // Ignore any delazification placeholder functions. These should not be
   5534      // exposed to debugger in any way.
   5535      if (fun->isGhost()) {
   5536        continue;
   5537      }
   5538 
   5539      // Delazify script.
   5540      JSScript* compiledScript = GetOrCreateFunctionScript(cx, fun);
   5541      if (!compiledScript) {
   5542        return false;
   5543      }
   5544 
   5545      // If target line isn't in script, we are done with it.
   5546      if (!scriptIsLineMatch(compiledScript)) {
   5547        continue;
   5548      }
   5549 
   5550      // Add script to results now that we've completed checks.
   5551      if (!scriptVector.append(compiledScript)) {
   5552        return false;
   5553      }
   5554 
   5555      // If script was a leaf we are done with it. This is an optional
   5556      // optimization to avoid inspecting the `gcthings` list below.
   5557      if (!script->hasInnerFunctions()) {
   5558        continue;
   5559      }
   5560 
   5561      // Now add inner scripts to `partialMatchVector` work list to determine if
   5562      // they are matches. Note that out IterateScripts callback ignored them
   5563      // already since they did not have a compiled parent at the time.
   5564      for (JS::GCCellPtr thing : script->gcthings()) {
   5565        if (!thing.is<JSObject>() || !thing.as<JSObject>().is<JSFunction>()) {
   5566          continue;
   5567        }
   5568        JSFunction* fun = &thing.as<JSObject>().as<JSFunction>();
   5569        if (!fun->hasBaseScript()) {
   5570          continue;
   5571        }
   5572        BaseScript* inner = fun->baseScript();
   5573        MOZ_ASSERT(inner);
   5574        if (!inner) {
   5575          // If the function doesn't have script, ignore it.
   5576          continue;
   5577        }
   5578 
   5579        if (!scriptIsPartialLineMatch(inner)) {
   5580          continue;
   5581        }
   5582 
   5583        // Add the matching inner script to the back of the results queue
   5584        // where it will be processed recursively.
   5585        if (!partialMatchVector.append(inner)) {
   5586          return false;
   5587        }
   5588      }
   5589    }
   5590 
   5591    // If this is an 'innermost' query, we want to filter the results again to
   5592    // only return the innermost script for each realm. To do this we build a
   5593    // hashmap to track innermost and then recreate the `scriptVector` with the
   5594    // results that remain in the hashmap.
   5595    if (innermost) {
   5596      using RealmToScriptMap =
   5597          GCHashMap<Realm*, BaseScript*, DefaultHasher<Realm*>>;
   5598 
   5599      Rooted<RealmToScriptMap> innermostForRealm(cx, cx);
   5600 
   5601      // Visit each candidate script and find innermost in each realm.
   5602      for (BaseScript* script : scriptVector) {
   5603        Realm* realm = script->realm();
   5604        RealmToScriptMap::AddPtr p = innermostForRealm.lookupForAdd(realm);
   5605        if (p) {
   5606          // Is our newly found script deeper than the last one we found?
   5607          BaseScript* incumbent = p->value();
   5608          if (script->asJSScript()->innermostScope()->chainLength() >
   5609              incumbent->asJSScript()->innermostScope()->chainLength()) {
   5610            p->value() = script;
   5611          }
   5612        } else {
   5613          // This is the first matching script we've encountered for this
   5614          // realm, so it is thus the innermost such script.
   5615          if (!innermostForRealm.add(p, realm, script)) {
   5616            return false;
   5617          }
   5618        }
   5619      }
   5620 
   5621      // Reset the results vector.
   5622      scriptVector.clear();
   5623 
   5624      // Re-add only the innermost scripts to the results.
   5625      for (RealmToScriptMap::Range r = innermostForRealm.all(); !r.empty();
   5626           r.popFront()) {
   5627        if (!scriptVector.append(r.front().value())) {
   5628          return false;
   5629        }
   5630      }
   5631    }
   5632 
   5633    // TODO: Until such time that wasm modules are real ES6 modules,
   5634    // unconditionally consider all wasm toplevel instance scripts.
   5635    for (WeakGlobalObjectSet::Range r = debugger->allDebuggees(); !r.empty();
   5636         r.popFront()) {
   5637      for (wasm::Instance* instance : r.front()->realm()->wasm.instances()) {
   5638        consider(instance->object());
   5639        if (oom) {
   5640          ReportOutOfMemory(cx);
   5641          return false;
   5642        }
   5643      }
   5644    }
   5645 
   5646    return true;
   5647  }
   5648 
   5649  Handle<BaseScriptVector> foundScripts() const { return scriptVector; }
   5650 
   5651  Handle<WasmInstanceObjectVector> foundWasmInstances() const {
   5652    return wasmInstanceVector;
   5653  }
   5654 
   5655 private:
   5656  static const uint32_t LINE_CONSTRAINT_NOT_PROVIDED = 0;
   5657 
   5658  /* If this is a string, matching scripts have urls equal to it. */
   5659  RootedValue url;
   5660 
   5661  /* url as a C string. */
   5662  UniqueChars urlCString;
   5663 
   5664  /* If this is a string, matching scripts' sources have displayURLs equal to
   5665   * it. */
   5666  Rooted<JSLinearString*> displayURLString;
   5667 
   5668  /*
   5669   * If this is a source referent, matching scripts will have sources equal
   5670   * to this instance. Ideally we'd use a Maybe here, but Maybe interacts
   5671   * very badly with Rooted's LIFO invariant.
   5672   */
   5673  bool hasSource = false;
   5674  Rooted<DebuggerSourceReferent> source;
   5675 
   5676  /* True if the query contained a 'line' or 'start' property. */
   5677  bool hasLine = false;
   5678 
   5679  /* The start line of the target range, inclusive. A script's lines must
   5680   * overlap the target line range or it will be filtered out by the query. */
   5681  uint32_t line = LINE_CONSTRAINT_NOT_PROVIDED;
   5682 
   5683  /* The end line of the target range, inclusive. A script's lines must overlap
   5684   * the target line range or it will be filtered out by the query. */
   5685  uint32_t lineEnd = LINE_CONSTRAINT_NOT_PROVIDED;
   5686 
   5687  Maybe<JS::LimitedColumnNumberOneOrigin> columnStart;
   5688 
   5689  Maybe<JS::LimitedColumnNumberOneOrigin> columnEnd;
   5690 
   5691  // As a performance optimization (and to avoid delazifying as many scripts),
   5692  // we would like to know the source offset of the target range start line.
   5693  //
   5694  // Since we do not have a simple way to compute this precisely, we instead
   5695  // track a lower-bound of the offset value. As we collect SourceExtent
   5696  // examples with (line,column) <-> sourceStart mappings, we can improve the
   5697  // bound. The target range start line is within the range
   5698  // [sourceOffsetLowerBound, Inf).
   5699  //
   5700  // NOTE: Using a SourceExtent for updating the bound happens independently of
   5701  //       if the script matches the target range start line or not in the end.
   5702  mutable uint32_t sourceOffsetLowerBound = 0;
   5703 
   5704  /* True if the query has an 'innermost' property whose value is true. */
   5705  bool innermost = false;
   5706 
   5707  /*
   5708   * Accumulate the scripts in an Rooted<BaseScriptVector> instead of creating
   5709   * the JS array as we go, because we mustn't allocate JS objects or GC while
   5710   * we use the CellIter.
   5711   */
   5712  Rooted<BaseScriptVector> scriptVector;
   5713 
   5714  /*
   5715   * While in the CellIter we may find BaseScripts that need to be compiled
   5716   * before the query can be fully checked. Since we cannot compile while under
   5717   * CellIter we accumulate them here instead.
   5718   *
   5719   * This occurs when matching line numbers since `GetScriptLineExtent` cannot
   5720   * be computed without bytecode existing.
   5721   */
   5722  Rooted<BaseScriptVector> partialMatchVector;
   5723 
   5724  /*
   5725   * Like above, but for wasm modules.
   5726   */
   5727  Rooted<WasmInstanceObjectVector> wasmInstanceVector;
   5728 
   5729  /*
   5730   * Given that parseQuery or omittedQuery has been called, prepare to match
   5731   * scripts. Set urlCString and displayURLChars as appropriate.
   5732   */
   5733  bool prepareQuery() {
   5734    // Compute urlCString and displayURLChars, if a url or displayURL was
   5735    // given respectively.
   5736    if (url.isString()) {
   5737      Rooted<JSString*> str(cx, url.toString());
   5738      urlCString = JS_EncodeStringToUTF8(cx, str);
   5739      if (!urlCString) {
   5740        return false;
   5741      }
   5742    }
   5743 
   5744    return true;
   5745  }
   5746 
   5747  template <size_t N>
   5748  bool parseLineColumnObject(
   5749      Handle<JSObject*> obj, const char (&propName)[N], uint32_t& lineOut,
   5750      Maybe<JS::LimitedColumnNumberOneOrigin>& columnOut) {
   5751    RootedValue lineProp(cx);
   5752    if (!GetProperty(cx, obj, obj, cx->names().line, &lineProp)) {
   5753      return false;
   5754    }
   5755    if (!lineProp.isNumber()) {
   5756      static const char propMessageFormat[] =
   5757          "query object's '%s.line' property";
   5758      char propMessage[N - 1 /* propName's terminating null */
   5759                       + sizeof(propMessageFormat) - 2 /* '%s' is replaced */];
   5760      DebugOnly<size_t> checkLen =
   5761          SprintfLiteral(propMessage, propMessageFormat, propName);
   5762      MOZ_ASSERT(checkLen == sizeof(propMessage) - 1 /* terminating null */);
   5763      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5764                                JSMSG_UNEXPECTED_TYPE, propMessage,
   5765                                "not a number");
   5766      return false;
   5767    }
   5768    if (!parsePositiveInteger(lineProp, lineOut, JSMSG_DEBUG_BAD_LINE)) {
   5769      return false;
   5770    }
   5771 
   5772    RootedValue columnProp(cx);
   5773    if (!GetProperty(cx, obj, obj, cx->names().column, &columnProp)) {
   5774      return false;
   5775    }
   5776    if (!columnProp.isUndefined()) {
   5777      if (!columnProp.isNumber()) {
   5778        static const char propMessageFormat[] =
   5779            "query object's '%s.column' property";
   5780        char propMessage[N - 1 /* propName's terminating null */
   5781                         + sizeof(propMessageFormat) -
   5782                         2 /* '%s' is replaced */];
   5783        DebugOnly<size_t> checkLen =
   5784            SprintfLiteral(propMessage, propMessageFormat, propName);
   5785        MOZ_ASSERT(checkLen == sizeof(propMessage) - 1 /* terminating null */);
   5786        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5787                                  JSMSG_UNEXPECTED_TYPE, propMessage,
   5788                                  "not a number");
   5789        return false;
   5790      }
   5791      uint32_t uintColumn = 0;
   5792      if (!parsePositiveInteger(columnProp, uintColumn,
   5793                                JSMSG_BAD_COLUMN_NUMBER)) {
   5794        return false;
   5795      }
   5796      if (uintColumn > JS::LimitedColumnNumberOneOrigin::Limit) {
   5797        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5798                                  JSMSG_BAD_COLUMN_NUMBER);
   5799        return false;
   5800      }
   5801      columnOut.emplace(JS::LimitedColumnNumberOneOrigin(uintColumn));
   5802    }
   5803    return true;
   5804  }
   5805 
   5806  bool parsePositiveInteger(Handle<Value> numberProp, uint32_t& result,
   5807                            JSErrNum errorNumber) {
   5808    double doubleVal = numberProp.toNumber();
   5809    uint32_t uintVal = (uint32_t)doubleVal;
   5810    if (doubleVal <= 0 || uintVal != doubleVal) {
   5811      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
   5812      return false;
   5813    }
   5814    result = uintVal;
   5815    return true;
   5816  }
   5817 
   5818  void updateSourceOffsetLowerBound(const SourceExtent& extent) {
   5819    // We trying to find the offset of (target-range-start-line, 0), so ignore
   5820    // any scripts within the target range.
   5821    MOZ_ASSERT(line != LINE_CONSTRAINT_NOT_PROVIDED &&
   5822               lineEnd != LINE_CONSTRAINT_NOT_PROVIDED);
   5823    MOZ_ASSERT(extent.lineno <= lineEnd);
   5824    if (extent.lineno >= line) {
   5825      return;
   5826    }
   5827 
   5828    // The extent.sourceStart position is now definitely *before* the target
   5829    // range start line, so update sourceOffsetLowerBound if extent.sourceStart
   5830    // is a tighter bound.
   5831    if (extent.sourceStart > sourceOffsetLowerBound) {
   5832      sourceOffsetLowerBound = extent.sourceStart;
   5833    }
   5834  }
   5835 
   5836  // A partial match is a script that starts before the target range ends, but
   5837  // may or may not end before the target range starts. We can also return false
   5838  // if we can prove the script ends before the target range starts.
   5839  bool scriptIsPartialLineMatch(BaseScript* script) {
   5840    const SourceExtent& extent = script->extent();
   5841 
   5842    // We only know for sure that the script is outside the target line range
   5843    // if the start of script is after the target end line, because we don't
   5844    // know how many lines the script has yet.
   5845    MOZ_ASSERT(line != LINE_CONSTRAINT_NOT_PROVIDED &&
   5846               lineEnd != LINE_CONSTRAINT_NOT_PROVIDED);
   5847    MOZ_ASSERT(line <= lineEnd);
   5848    if (extent.lineno > lineEnd) {
   5849      return false;
   5850    }
   5851    if (columnEnd.isSome() && script->lineno() == lineEnd &&
   5852        script->column() > columnEnd.value()) {
   5853      return false;
   5854    }
   5855 
   5856    // Use the implicit (line, column) <-> sourceStart mapping from the
   5857    // SourceExtent to update our bounds on possible matches. We call this
   5858    // without knowing if the script is a match or not.
   5859    updateSourceOffsetLowerBound(script->extent());
   5860 
   5861    // As an optional performance optimization, we rule out any script that ends
   5862    // before the lower-bound on where target range start line exists.
   5863    return extent.sourceEnd > sourceOffsetLowerBound;
   5864  }
   5865 
   5866  // True if any part of script source overlaps the target range.
   5867  bool scriptIsLineMatch(JSScript* script) {
   5868    MOZ_ASSERT(scriptIsPartialLineMatch(script));
   5869 
   5870    JS::LimitedColumnNumberOneOrigin scriptEndColumn;
   5871    uint32_t lineCount = GetScriptLineExtent(script, &scriptEndColumn);
   5872    if (columnStart.isSome() && script->lineno() + lineCount - 1 == line) {
   5873      if (scriptEndColumn <= columnStart.value()) {
   5874        return false;
   5875      }
   5876    }
   5877    return (script->lineno() + lineCount > line);
   5878  }
   5879 
   5880  static void considerScript(JSRuntime* rt, void* data, BaseScript* script,
   5881                             const JS::AutoRequireNoGC& nogc) {
   5882    ScriptQuery* self = static_cast<ScriptQuery*>(data);
   5883    self->consider(script, nogc);
   5884  }
   5885 
   5886  template <typename T>
   5887  [[nodiscard]] bool commonFilter(T script, const JS::AutoRequireNoGC& nogc) {
   5888    if (urlCString) {
   5889      bool gotFilename = false;
   5890      if (script->filename() &&
   5891          strcmp(script->filename(), urlCString.get()) == 0) {
   5892        gotFilename = true;
   5893      }
   5894 
   5895      bool gotSourceURL = false;
   5896      if (!gotFilename && script->scriptSource()->introducerFilename() &&
   5897          strcmp(script->scriptSource()->introducerFilename(),
   5898                 urlCString.get()) == 0) {
   5899        gotSourceURL = true;
   5900      }
   5901      if (!gotFilename && !gotSourceURL) {
   5902        return false;
   5903      }
   5904    }
   5905    if (displayURLString) {
   5906      if (!script->scriptSource() || !script->scriptSource()->hasDisplayURL()) {
   5907        return false;
   5908      }
   5909 
   5910      const char16_t* s = script->scriptSource()->displayURL();
   5911      if (CompareChars(s, js_strlen(s), displayURLString) != 0) {
   5912        return false;
   5913      }
   5914    }
   5915    if (hasSource && !(source.is<ScriptSourceObject*>() &&
   5916                       source.as<ScriptSourceObject*>()->source() ==
   5917                           script->scriptSource())) {
   5918      return false;
   5919    }
   5920    return true;
   5921  }
   5922 
   5923  /*
   5924   * If |script| matches this query, append it to |scriptVector|. Set |oom| if
   5925   * an out of memory condition occurred.
   5926   */
   5927  void consider(BaseScript* script, const JS::AutoRequireNoGC& nogc) {
   5928    if (oom || script->selfHosted()) {
   5929      return;
   5930    }
   5931 
   5932    Realm* realm = script->realm();
   5933    if (!realms.has(realm)) {
   5934      return;
   5935    }
   5936 
   5937    if (!commonFilter(script, nogc)) {
   5938      return;
   5939    }
   5940 
   5941    bool partial = false;
   5942 
   5943    if (hasLine) {
   5944      if (!scriptIsPartialLineMatch(script)) {
   5945        return;
   5946      }
   5947 
   5948      if (script->hasBytecode()) {
   5949        // Check if line is within script (or any of its inner scripts).
   5950        if (!scriptIsLineMatch(script->asJSScript())) {
   5951          return;
   5952        }
   5953      } else {
   5954        // GetScriptLineExtent is not available on lazy scripts so instead to
   5955        // the partial match list for be compiled and reprocessed later. We only
   5956        // add scripts that are ready for delazification and they may in turn
   5957        // process their inner functions.
   5958        if (!script->isReadyForDelazification()) {
   5959          return;
   5960        }
   5961        partial = true;
   5962      }
   5963    }
   5964 
   5965    // If innermost filter is required, we collect everything that matches the
   5966    // line number and filter at the end of `findScripts`.
   5967    MOZ_ASSERT_IF(innermost, hasLine);
   5968 
   5969    Rooted<BaseScriptVector>& vec = partial ? partialMatchVector : scriptVector;
   5970    if (!vec.append(script)) {
   5971      oom = true;
   5972    }
   5973  }
   5974 
   5975  /*
   5976   * If |instanceObject| matches this query, append it to |wasmInstanceVector|.
   5977   * Set |oom| if an out of memory condition occurred.
   5978   */
   5979  void consider(WasmInstanceObject* instanceObject) {
   5980    if (oom) {
   5981      return;
   5982    }
   5983 
   5984    if (hasSource && source != AsVariant(instanceObject)) {
   5985      return;
   5986    }
   5987 
   5988    if (!wasmInstanceVector.append(instanceObject)) {
   5989      oom = true;
   5990    }
   5991  }
   5992 };
   5993 
   5994 bool Debugger::CallData::findScripts() {
   5995  ScriptQuery query(cx, dbg);
   5996 
   5997  if (args.length() >= 1) {
   5998    RootedObject queryObject(cx, RequireObject(cx, args[0]));
   5999    if (!queryObject || !query.parseQuery(queryObject)) {
   6000      return false;
   6001    }
   6002  } else {
   6003    if (!query.omittedQuery()) {
   6004      return false;
   6005    }
   6006  }
   6007 
   6008  if (!query.findScripts()) {
   6009    return false;
   6010  }
   6011 
   6012  Handle<BaseScriptVector> scripts(query.foundScripts());
   6013  Handle<WasmInstanceObjectVector> wasmInstances(query.foundWasmInstances());
   6014 
   6015  size_t resultLength = scripts.length() + wasmInstances.length();
   6016  Rooted<ArrayObject*> result(cx,
   6017                              NewDenseFullyAllocatedArray(cx, resultLength));
   6018  if (!result) {
   6019    return false;
   6020  }
   6021 
   6022  result->ensureDenseInitializedLength(0, resultLength);
   6023 
   6024  for (size_t i = 0; i < scripts.length(); i++) {
   6025    JSObject* scriptObject = dbg->wrapScript(cx, scripts[i]);
   6026    if (!scriptObject) {
   6027      return false;
   6028    }
   6029    result->setDenseElement(i, ObjectValue(*scriptObject));
   6030  }
   6031 
   6032  size_t wasmStart = scripts.length();
   6033  for (size_t i = 0; i < wasmInstances.length(); i++) {
   6034    JSObject* scriptObject = dbg->wrapWasmScript(cx, wasmInstances[i]);
   6035    if (!scriptObject) {
   6036      return false;
   6037    }
   6038    result->setDenseElement(wasmStart + i, ObjectValue(*scriptObject));
   6039  }
   6040 
   6041  args.rval().setObject(*result);
   6042  return true;
   6043 }
   6044 
   6045 /*
   6046 * A class for searching sources for 'findSources'.
   6047 */
   6048 class MOZ_STACK_CLASS Debugger::SourceQuery : public Debugger::QueryBase {
   6049 public:
   6050  using SourceSet = JS::GCHashSet<JSObject*, js::StableCellHasher<JSObject*>,
   6051                                  ZoneAllocPolicy>;
   6052 
   6053  SourceQuery(JSContext* cx, Debugger* dbg)
   6054      : QueryBase(cx, dbg), sources(cx, SourceSet(cx->zone())) {}
   6055 
   6056  bool findSources() {
   6057    if (!matchAllDebuggeeGlobals()) {
   6058      return false;
   6059    }
   6060 
   6061    Realm* singletonRealm = nullptr;
   6062    if (realms.count() == 1) {
   6063      singletonRealm = realms.all().front();
   6064    }
   6065 
   6066    // Search each realm for debuggee scripts.
   6067    MOZ_ASSERT(sources.empty());
   6068    oom = false;
   6069    IterateScripts(cx, singletonRealm, this, considerScript);
   6070    if (oom) {
   6071      ReportOutOfMemory(cx);
   6072      return false;
   6073    }
   6074 
   6075    // TODO: Until such time that wasm modules are real ES6 modules,
   6076    // unconditionally consider all wasm toplevel instance scripts.
   6077    for (WeakGlobalObjectSet::Range r = debugger->allDebuggees(); !r.empty();
   6078         r.popFront()) {
   6079      for (wasm::Instance* instance : r.front()->realm()->wasm.instances()) {
   6080        consider(instance->object());
   6081        if (oom) {
   6082          ReportOutOfMemory(cx);
   6083          return false;
   6084        }
   6085      }
   6086    }
   6087 
   6088    return true;
   6089  }
   6090 
   6091  Handle<SourceSet> foundSources() const { return sources; }
   6092 
   6093 private:
   6094  Rooted<SourceSet> sources;
   6095 
   6096  static void considerScript(JSRuntime* rt, void* data, BaseScript* script,
   6097                             const JS::AutoRequireNoGC& nogc) {
   6098    SourceQuery* self = static_cast<SourceQuery*>(data);
   6099    self->consider(script, nogc);
   6100  }
   6101 
   6102  void consider(BaseScript* script, const JS::AutoRequireNoGC& nogc) {
   6103    if (oom || script->selfHosted()) {
   6104      return;
   6105    }
   6106 
   6107    Realm* realm = script->realm();
   6108    if (!realms.has(realm)) {
   6109      return;
   6110    }
   6111 
   6112    ScriptSourceObject* source = script->sourceObject();
   6113    if (!sources.put(source)) {
   6114      oom = true;
   6115    }
   6116  }
   6117 
   6118  void consider(WasmInstanceObject* instanceObject) {
   6119    if (oom) {
   6120      return;
   6121    }
   6122 
   6123    if (!sources.put(instanceObject)) {
   6124      oom = true;
   6125    }
   6126  }
   6127 };
   6128 
   6129 static inline DebuggerSourceReferent AsSourceReferent(JSObject* obj) {
   6130  if (obj->is<ScriptSourceObject>()) {
   6131    return AsVariant(&obj->as<ScriptSourceObject>());
   6132  }
   6133  return AsVariant(&obj->as<WasmInstanceObject>());
   6134 }
   6135 
   6136 bool Debugger::CallData::findSources() {
   6137  SourceQuery query(cx, dbg);
   6138  if (!query.findSources()) {
   6139    return false;
   6140  }
   6141 
   6142  Handle<SourceQuery::SourceSet> sources(query.foundSources());
   6143 
   6144  size_t resultLength = sources.count();
   6145  Rooted<ArrayObject*> result(cx,
   6146                              NewDenseFullyAllocatedArray(cx, resultLength));
   6147  if (!result) {
   6148    return false;
   6149  }
   6150 
   6151  result->ensureDenseInitializedLength(0, resultLength);
   6152 
   6153  size_t i = 0;
   6154  for (auto iter = sources.get().iter(); !iter.done(); iter.next()) {
   6155    Rooted<DebuggerSourceReferent> sourceReferent(cx,
   6156                                                  AsSourceReferent(iter.get()));
   6157    RootedObject sourceObject(cx, dbg->wrapVariantReferent(cx, sourceReferent));
   6158    if (!sourceObject) {
   6159      return false;
   6160    }
   6161    result->setDenseElement(i, ObjectValue(*sourceObject));
   6162    i++;
   6163  }
   6164 
   6165  args.rval().setObject(*result);
   6166  return true;
   6167 }
   6168 
   6169 /*
   6170 * A class for parsing 'findObjects' query arguments and searching for objects
   6171 * that match the criteria they represent.
   6172 */
   6173 class MOZ_STACK_CLASS Debugger::ObjectQuery {
   6174 public:
   6175  /* Construct an ObjectQuery to use matching scripts for |dbg|. */
   6176  ObjectQuery(JSContext* cx, Debugger* dbg)
   6177      : objects(cx),
   6178        cx(cx),
   6179        dbg(dbg),
   6180        queryType(QueryType::None),
   6181        jsClassName(cx),
   6182        unwrappedCtorOrProto(cx) {}
   6183 
   6184  /* The vector that we are accumulating results in. */
   6185  RootedObjectVector objects;
   6186 
   6187  /* The set of debuggee compartments. */
   6188  JS::CompartmentSet debuggeeCompartments;
   6189 
   6190  /*
   6191   * Parse the query object |query|, and prepare to match only the objects it
   6192   * specifies.
   6193   */
   6194  bool parseQuery(HandleObject query) {
   6195    // Check for the 'class' property
   6196    RootedValue cls(cx);
   6197    if (!GetProperty(cx, query, query, cx->names().class_, &cls)) {
   6198      return false;
   6199    }
   6200 
   6201    if (cls.isUndefined()) {
   6202      return true;
   6203    }
   6204 
   6205    if (cls.isString()) {
   6206      JSLinearString* str = cls.toString()->ensureLinear(cx);
   6207      if (!str) {
   6208        return false;
   6209      }
   6210      if (!StringIsAscii(str)) {
   6211        JS_ReportErrorNumberASCII(
   6212            cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   6213            "query object's 'class' property string",
   6214            "not a string containing only ASCII characters");
   6215        return false;
   6216      }
   6217      jsClassName = cls;
   6218      queryType = QueryType::JSClassName;
   6219      return true;
   6220    }
   6221 
   6222    if (cls.isObject()) {
   6223      JS::Rooted<JSObject*> obj(cx, &cls.toObject());
   6224      obj = UncheckedUnwrap(obj);
   6225      if (JS_IsDeadWrapper(obj)) {
   6226        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   6227                                  JSMSG_DEAD_OBJECT);
   6228        return false;
   6229      }
   6230      if (!obj->is<DebuggerObject>()) {
   6231        JS_ReportErrorNumberASCII(
   6232            cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   6233            "query object's 'class' property object", "not Debugger.Object");
   6234        return false;
   6235      }
   6236 
   6237      unwrappedCtorOrProto = obj->as<DebuggerObject>().referent();
   6238      unwrappedCtorOrProto = UncheckedUnwrap(unwrappedCtorOrProto);
   6239      if (JS_IsDeadWrapper(unwrappedCtorOrProto)) {
   6240        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   6241                                  JSMSG_DEAD_OBJECT);
   6242        return false;
   6243      }
   6244      queryType = QueryType::CtorOrProto;
   6245      return true;
   6246    }
   6247 
   6248    JS_ReportErrorNumberASCII(
   6249        cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   6250        "query object's 'class' property",
   6251        "none of JSClass name string, constructor/prototype debuggee object, "
   6252        "or undefined");
   6253    return false;
   6254  }
   6255 
   6256  /* Set up this ObjectQuery appropriately for a missing query argument. */
   6257  void omittedQuery() {
   6258    jsClassName.setUndefined();
   6259    unwrappedCtorOrProto = nullptr;
   6260    queryType = QueryType::None;
   6261  }
   6262 
   6263  /*
   6264   * Traverse the heap to find all relevant objects and add them to the
   6265   * provided vector.
   6266   */
   6267  bool findObjects() {
   6268    if (!prepareQuery()) {
   6269      return false;
   6270    }
   6271 
   6272    for (WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty();
   6273         r.popFront()) {
   6274      if (!debuggeeCompartments.put(r.front()->compartment())) {
   6275        ReportOutOfMemory(cx);
   6276        return false;
   6277      }
   6278    }
   6279 
   6280    {
   6281      // We can't tolerate the GC moving things around while we're
   6282      // searching the heap. Check that nothing we do causes a GC.
   6283      RootedObject dbgObj(cx, dbg->object);
   6284      JS::ubi::RootList rootList(cx);
   6285      auto [ok, nogc] = rootList.init(dbgObj);
   6286      if (!ok) {
   6287        ReportOutOfMemory(cx);
   6288        return false;
   6289      }
   6290 
   6291      Traversal traversal(cx, *this, nogc);
   6292      traversal.wantNames = false;
   6293 
   6294      if (!traversal.addStart(JS::ubi::Node(&rootList)) ||
   6295          !traversal.traverse()) {
   6296        ReportOutOfMemory(cx);
   6297        return false;
   6298      }
   6299      return true;
   6300    }
   6301  }
   6302 
   6303  /*
   6304   * |ubi::Node::BreadthFirst| interface.
   6305   */
   6306  class NodeData {};
   6307  using Traversal = JS::ubi::BreadthFirst<ObjectQuery>;
   6308  bool operator()(Traversal& traversal, JS::ubi::Node origin,
   6309                  const JS::ubi::Edge& edge, NodeData*, bool first) {
   6310    if (!first) {
   6311      return true;
   6312    }
   6313 
   6314    JS::ubi::Node referent = edge.referent;
   6315 
   6316    // Only follow edges within our set of debuggee compartments; we don't
   6317    // care about the heap's subgraphs outside of our debuggee compartments,
   6318    // so we abandon the referent. Either (1) there is not a path from this
   6319    // non-debuggee node back to a node in our debuggee compartments, and we
   6320    // don't need to follow edges to or from this node, or (2) there does
   6321    // exist some path from this non-debuggee node back to a node in our
   6322    // debuggee compartments. However, if that were true, then the incoming
   6323    // cross compartment edge back into a debuggee compartment is already
   6324    // listed as an edge in the RootList we started traversal with, and
   6325    // therefore we don't need to follow edges to or from this non-debuggee
   6326    // node.
   6327    JS::Compartment* comp = referent.compartment();
   6328    if (comp && !debuggeeCompartments.has(comp)) {
   6329      traversal.abandonReferent();
   6330      return true;
   6331    }
   6332 
   6333    // If the referent has an associated realm and it's not a debuggee
   6334    // realm, skip it. Don't abandonReferent() here like above: realms
   6335    // within a compartment can reference each other without going through
   6336    // cross-compartment wrappers.
   6337    Realm* realm = referent.realm();
   6338    if (realm && !dbg->isDebuggeeUnbarriered(realm)) {
   6339      return true;
   6340    }
   6341 
   6342    // If the referent is an object and matches our query's restrictions,
   6343    // add it to the vector accumulating results. Skip objects that should
   6344    // never be exposed to JS, like EnvironmentObjects and internal
   6345    // functions.
   6346 
   6347    if (!referent.is<JSObject>() || referent.exposeToJS().isUndefined()) {
   6348      return true;
   6349    }
   6350 
   6351    JSObject* obj = referent.as<JSObject>();
   6352 
   6353    switch (queryType) {
   6354      case QueryType::None:
   6355        break;
   6356      case QueryType::JSClassName: {
   6357        const char* objJSClassName = obj->getClass()->name;
   6358        if (strcmp(objJSClassName, jsClassNameCString.get()) != 0) {
   6359          return true;
   6360        }
   6361        break;
   6362      }
   6363      case QueryType::CtorOrProto:
   6364        if (!hasConstructorOrPrototype(obj, unwrappedCtorOrProto, cx)) {
   6365          return true;
   6366        }
   6367        break;
   6368    }
   6369 
   6370    return objects.append(obj);
   6371  }
   6372 
   6373  // Returns true if `obj` is confirmed to have `ctorOrProto` as its
   6374  // constructor or prototype in the prototype chain.
   6375  //
   6376  // If it requires side-effect-ful operation for accessing the constructor or
   6377  // prototype, this can return false even if `obj instanceof ctorOrProto` is
   6378  // actually `true`.
   6379  static bool hasConstructorOrPrototype(JSObject* obj, JSObject* ctorOrProto,
   6380                                        JSContext* cx) {
   6381    obj = UncheckedUnwrap(obj);
   6382 
   6383    while (true) {
   6384      if (!obj->hasStaticPrototype()) {
   6385        // Dynamic prototype cannot be matched without side-effect.
   6386        break;
   6387      }
   6388 
   6389      JSObject* proto = obj->staticPrototype();
   6390      if (!proto) {
   6391        break;
   6392      }
   6393      proto = UncheckedUnwrap(proto);
   6394      if (proto == ctorOrProto) {
   6395        return true;
   6396      }
   6397 
   6398      JS::Value ctorVal;
   6399      bool result;
   6400      {
   6401        AutoRealm ar(cx, proto);
   6402        result = GetPropertyPure(cx, proto, NameToId(cx->names().constructor),
   6403                                 &ctorVal);
   6404      }
   6405      if (result && ctorVal.isObject()) {
   6406        JSObject* ctor = &ctorVal.toObject();
   6407        ctor = UncheckedUnwrap(ctor);
   6408        if (ctor == ctorOrProto) {
   6409          return true;
   6410        }
   6411      }
   6412 
   6413      obj = proto;
   6414    }
   6415 
   6416    return false;
   6417  }
   6418 
   6419 private:
   6420  /* The context in which we should do our work. */
   6421  JSContext* cx;
   6422 
   6423  /* The debugger for which we conduct queries. */
   6424  Debugger* dbg;
   6425 
   6426  enum class QueryType {
   6427    /* No filtering. */
   6428    None,
   6429 
   6430    /* Match objects with given JSClass name. */
   6431    JSClassName,
   6432 
   6433    /* Match objects with given object as constructor or prototype. */
   6434    CtorOrProto,
   6435  };
   6436  QueryType queryType;
   6437 
   6438  /* Matching objects will have a JSClass whose name is this property. */
   6439  RootedValue jsClassName;
   6440 
   6441  /* The jsClassName member, as a C string. */
   6442  UniqueChars jsClassNameCString;
   6443 
   6444  /* Matching objects will have given object as constructor or prototype. */
   6445  JS::Rooted<JSObject*> unwrappedCtorOrProto;
   6446 
   6447  /*
   6448   * Given that either omittedQuery or parseQuery has been called, prepare the
   6449   * query for matching objects.
   6450   */
   6451  bool prepareQuery() {
   6452    if (jsClassName.isString()) {
   6453      jsClassNameCString = JS_EncodeStringToASCII(cx, jsClassName.toString());
   6454      if (!jsClassNameCString) {
   6455        return false;
   6456      }
   6457    }
   6458 
   6459    return true;
   6460  }
   6461 };
   6462 
   6463 bool Debugger::CallData::findObjects() {
   6464  ObjectQuery query(cx, dbg);
   6465 
   6466  if (args.length() >= 1) {
   6467    RootedObject queryObject(cx, RequireObject(cx, args[0]));
   6468    if (!queryObject || !query.parseQuery(queryObject)) {
   6469      return false;
   6470    }
   6471  } else {
   6472    query.omittedQuery();
   6473  }
   6474 
   6475  if (!query.findObjects()) {
   6476    return false;
   6477  }
   6478 
   6479  // Returning internal objects (such as self-hosting intrinsics) to JS is not
   6480  // fuzzing-safe. We still want to call parseQuery/findObjects when fuzzing so
   6481  // just clear the Vector here.
   6482  if (fuzzingSafe) {
   6483    query.objects.clear();
   6484  }
   6485 
   6486  size_t length = query.objects.length();
   6487  Rooted<ArrayObject*> result(cx, NewDenseFullyAllocatedArray(cx, length));
   6488  if (!result) {
   6489    return false;
   6490  }
   6491 
   6492  result->ensureDenseInitializedLength(0, length);
   6493 
   6494  for (size_t i = 0; i < length; i++) {
   6495    RootedValue debuggeeVal(cx, ObjectValue(*query.objects[i]));
   6496    if (!dbg->wrapDebuggeeValue(cx, &debuggeeVal)) {
   6497      return false;
   6498    }
   6499    result->setDenseElement(i, debuggeeVal);
   6500  }
   6501 
   6502  args.rval().setObject(*result);
   6503  return true;
   6504 }
   6505 
   6506 bool Debugger::CallData::findAllGlobals() {
   6507  RootedObjectVector globals(cx);
   6508 
   6509  {
   6510    // Accumulate the list of globals before wrapping them, because
   6511    // wrapping can GC and collect realms from under us, while iterating.
   6512    JS::AutoCheckCannotGC nogc;
   6513 
   6514    for (RealmsIter r(cx->runtime()); !r.done(); r.next()) {
   6515      if (r->creationOptions().invisibleToDebugger()) {
   6516        continue;
   6517      }
   6518 
   6519      if (!r->hasInitializedGlobal()) {
   6520        continue;
   6521      }
   6522 
   6523      if (JS::RealmBehaviorsRef(r).isNonLive()) {
   6524        continue;
   6525      }
   6526 
   6527      r->compartment()->gcState.scheduledForDestruction = false;
   6528 
   6529      GlobalObject* global = r->maybeGlobal();
   6530 
   6531      // We pulled |global| out of nowhere, so it's possible that it was
   6532      // marked gray by XPConnect. Since we're now exposing it to JS code,
   6533      // we need to mark it black.
   6534      JS::ExposeObjectToActiveJS(global);
   6535      if (!globals.append(global)) {
   6536        return false;
   6537      }
   6538    }
   6539  }
   6540 
   6541  RootedObject result(cx, NewDenseEmptyArray(cx));
   6542  if (!result) {
   6543    return false;
   6544  }
   6545 
   6546  for (size_t i = 0; i < globals.length(); i++) {
   6547    RootedValue globalValue(cx, ObjectValue(*globals[i]));
   6548    if (!dbg->wrapDebuggeeValue(cx, &globalValue)) {
   6549      return false;
   6550    }
   6551    if (!NewbornArrayPush(cx, result, globalValue)) {
   6552      return false;
   6553    }
   6554  }
   6555 
   6556  args.rval().setObject(*result);
   6557  return true;
   6558 }
   6559 
   6560 bool Debugger::CallData::findSourceURLs() {
   6561  RootedObject result(cx, NewDenseEmptyArray(cx));
   6562  if (!result) {
   6563    return false;
   6564  }
   6565 
   6566  for (WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty();
   6567       r.popFront()) {
   6568    RootedObject holder(cx, r.front()->getSourceURLsHolder());
   6569    if (holder) {
   6570      for (size_t i = 0; i < holder->as<ArrayObject>().length(); i++) {
   6571        Value v = holder->as<ArrayObject>().getDenseElement(i);
   6572 
   6573        // The value is an atom and doesn't need wrapping, but the holder may be
   6574        // in another zone and the atom must be marked when we create a
   6575        // reference in this zone.
   6576        MOZ_ASSERT(v.isString() && v.toString()->isAtom());
   6577        cx->markAtomValue(v);
   6578 
   6579        if (!NewbornArrayPush(cx, result, v)) {
   6580          return false;
   6581        }
   6582      }
   6583    }
   6584  }
   6585 
   6586  args.rval().setObject(*result);
   6587  return true;
   6588 }
   6589 
   6590 bool Debugger::CallData::makeGlobalObjectReference() {
   6591  if (!args.requireAtLeast(cx, "Debugger.makeGlobalObjectReference", 1)) {
   6592    return false;
   6593  }
   6594 
   6595  Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
   6596  if (!global) {
   6597    return false;
   6598  }
   6599 
   6600  // If we create a D.O referring to a global in an invisible realm,
   6601  // then from it we can reach function objects, scripts, environments, etc.,
   6602  // none of which we're ever supposed to see.
   6603  if (global->realm()->creationOptions().invisibleToDebugger()) {
   6604    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   6605                              JSMSG_DEBUG_INVISIBLE_COMPARTMENT);
   6606    return false;
   6607  }
   6608 
   6609  args.rval().setObject(*global);
   6610  return dbg->wrapDebuggeeValue(cx, args.rval());
   6611 }
   6612 
   6613 bool Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp) {
   6614  CallArgs args = CallArgsFromVp(argc, vp);
   6615 
   6616  if (!args.requireAtLeast(cx, "Debugger.isCompilableUnit", 1)) {
   6617    return false;
   6618  }
   6619 
   6620  if (!args[0].isString()) {
   6621    JS_ReportErrorNumberASCII(
   6622        cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
   6623        "Debugger.isCompilableUnit", "string", InformalValueTypeName(args[0]));
   6624    return false;
   6625  }
   6626 
   6627  JSString* str = args[0].toString();
   6628  size_t length = str->length();
   6629 
   6630  AutoStableStringChars chars(cx);
   6631  if (!chars.initTwoByte(cx, str)) {
   6632    return false;
   6633  }
   6634 
   6635  bool result = true;
   6636 
   6637  AutoReportFrontendContext fc(cx,
   6638                               AutoReportFrontendContext::Warning::Suppress);
   6639  CompileOptions options(cx);
   6640  Rooted<frontend::CompilationInput> input(cx,
   6641                                           frontend::CompilationInput(options));
   6642  if (!input.get().initForGlobal(&fc)) {
   6643    return false;
   6644  }
   6645 
   6646  LifoAllocScope allocScope(&cx->tempLifoAlloc());
   6647  frontend::NoScopeBindingCache scopeCache;
   6648  frontend::CompilationState compilationState(&fc, allocScope, input.get());
   6649  if (!compilationState.init(&fc, &scopeCache)) {
   6650    return false;
   6651  }
   6652 
   6653  frontend::Parser<frontend::FullParseHandler, char16_t> parser(
   6654      &fc, options, chars.twoByteChars(), length, compilationState,
   6655      /* syntaxParser = */ nullptr);
   6656  if (!parser.checkOptions() || parser.parse().isErr()) {
   6657    // We ran into an error. If it was because we ran out of memory we report
   6658    // it in the usual way.
   6659    if (fc.hadOutOfMemory()) {
   6660      return false;
   6661    }
   6662 
   6663    // If it was because we ran out of source, we return false so our caller
   6664    // knows to try to collect more [source].
   6665    if (parser.isUnexpectedEOF()) {
   6666      result = false;
   6667    }
   6668 
   6669    fc.clearAutoReport();
   6670  }
   6671 
   6672  args.rval().setBoolean(result);
   6673  return true;
   6674 }
   6675 
   6676 bool Debugger::CallData::adoptDebuggeeValue() {
   6677  if (!args.requireAtLeast(cx, "Debugger.adoptDebuggeeValue", 1)) {
   6678    return false;
   6679  }
   6680 
   6681  RootedValue v(cx, args[0]);
   6682  if (v.isObject()) {
   6683    RootedObject obj(cx, &v.toObject());
   6684    DebuggerObject* ndobj = ToNativeDebuggerObject(cx, &obj);
   6685    if (!ndobj) {
   6686      return false;
   6687    }
   6688 
   6689    obj.set(ndobj->referent());
   6690    v = ObjectValue(*obj);
   6691 
   6692    if (!dbg->wrapDebuggeeValue(cx, &v)) {
   6693      return false;
   6694    }
   6695  }
   6696 
   6697  args.rval().set(v);
   6698  return true;
   6699 }
   6700 
   6701 class DebuggerAdoptSourceMatcher {
   6702  JSContext* cx_;
   6703  Debugger* dbg_;
   6704 
   6705 public:
   6706  explicit DebuggerAdoptSourceMatcher(JSContext* cx, Debugger* dbg)
   6707      : cx_(cx), dbg_(dbg) {}
   6708 
   6709  using ReturnType = DebuggerSource*;
   6710 
   6711  ReturnType match(Handle<ScriptSourceObject*> source) {
   6712    if (source->compartment() == cx_->compartment()) {
   6713      JS_ReportErrorASCII(cx_,
   6714                          "Source is in the same compartment as this debugger");
   6715      return nullptr;
   6716    }
   6717    return dbg_->wrapSource(cx_, source);
   6718  }
   6719  ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
   6720    if (wasmInstance->compartment() == cx_->compartment()) {
   6721      JS_ReportErrorASCII(
   6722          cx_, "WasmInstance is in the same compartment as this debugger");
   6723      return nullptr;
   6724    }
   6725    return dbg_->wrapWasmSource(cx_, wasmInstance);
   6726  }
   6727 };
   6728 
   6729 bool Debugger::CallData::adoptFrame() {
   6730  if (!args.requireAtLeast(cx, "Debugger.adoptFrame", 1)) {
   6731    return false;
   6732  }
   6733 
   6734  RootedObject obj(cx, RequireObject(cx, args[0]));
   6735  if (!obj) {
   6736    return false;
   6737  }
   6738 
   6739  obj = UncheckedUnwrap(obj);
   6740  if (!obj->is<DebuggerFrame>()) {
   6741    JS_ReportErrorASCII(cx, "Argument is not a Debugger.Frame");
   6742    return false;
   6743  }
   6744 
   6745  RootedValue objVal(cx, ObjectValue(*obj));
   6746  Rooted<DebuggerFrame*> frameObj(cx, DebuggerFrame::check(cx, objVal));
   6747  if (!frameObj) {
   6748    return false;
   6749  }
   6750 
   6751  Rooted<DebuggerFrame*> adoptedFrame(cx);
   6752  if (frameObj->isOnStack()) {
   6753    FrameIter iter = frameObj->getFrameIter(cx);
   6754    if (!dbg->observesFrame(iter)) {
   6755      JS_ReportErrorASCII(cx, "Debugger.Frame's global is not a debuggee");
   6756      return false;
   6757    }
   6758    if (!dbg->getFrame(cx, iter, &adoptedFrame)) {
   6759      return false;
   6760    }
   6761  } else if (frameObj->isSuspended()) {
   6762    Rooted<AbstractGeneratorObject*> gen(cx, &frameObj->unwrappedGenerator());
   6763    if (!dbg->observesGlobal(&gen->global())) {
   6764      JS_ReportErrorASCII(cx, "Debugger.Frame's global is not a debuggee");
   6765      return false;
   6766    }
   6767 
   6768    if (!dbg->getFrame(cx, gen, &adoptedFrame)) {
   6769      return false;
   6770    }
   6771  } else {
   6772    if (!dbg->getFrame(cx, &adoptedFrame)) {
   6773      return false;
   6774    }
   6775  }
   6776 
   6777  args.rval().setObject(*adoptedFrame);
   6778  return true;
   6779 }
   6780 
   6781 bool Debugger::CallData::adoptSource() {
   6782  if (!args.requireAtLeast(cx, "Debugger.adoptSource", 1)) {
   6783    return false;
   6784  }
   6785 
   6786  RootedObject obj(cx, RequireObject(cx, args[0]));
   6787  if (!obj) {
   6788    return false;
   6789  }
   6790 
   6791  obj = UncheckedUnwrap(obj);
   6792  if (!obj->is<DebuggerSource>()) {
   6793    JS_ReportErrorASCII(cx, "Argument is not a Debugger.Source");
   6794    return false;
   6795  }
   6796 
   6797  Rooted<DebuggerSource*> sourceObj(cx, &obj->as<DebuggerSource>());
   6798  if (!sourceObj->getReferentRawObject()) {
   6799    JS_ReportErrorASCII(cx, "Argument is Debugger.Source.prototype");
   6800    return false;
   6801  }
   6802 
   6803  Rooted<DebuggerSourceReferent> referent(cx, sourceObj->getReferent());
   6804 
   6805  DebuggerAdoptSourceMatcher matcher(cx, dbg);
   6806  DebuggerSource* res = referent.match(matcher);
   6807  if (!res) {
   6808    return false;
   6809  }
   6810 
   6811  args.rval().setObject(*res);
   6812  return true;
   6813 }
   6814 
   6815 bool Debugger::CallData::enableAsyncStack() {
   6816  if (!args.requireAtLeast(cx, "Debugger.enableAsyncStack", 1)) {
   6817    return false;
   6818  }
   6819  Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
   6820  if (!global) {
   6821    return false;
   6822  }
   6823 
   6824  global->realm()->isAsyncStackCapturingEnabled = true;
   6825 
   6826  args.rval().setUndefined();
   6827  return true;
   6828 }
   6829 
   6830 bool Debugger::CallData::disableAsyncStack() {
   6831  if (!args.requireAtLeast(cx, "Debugger.disableAsyncStack", 1)) {
   6832    return false;
   6833  }
   6834  Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
   6835  if (!global) {
   6836    return false;
   6837  }
   6838 
   6839  global->realm()->isAsyncStackCapturingEnabled = false;
   6840 
   6841  args.rval().setUndefined();
   6842  return true;
   6843 }
   6844 
   6845 bool Debugger::CallData::enableUnlimitedStacksCapturing() {
   6846  if (!args.requireAtLeast(cx, "Debugger.enableUnlimitedStacksCapturing", 1)) {
   6847    return false;
   6848  }
   6849  Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
   6850  if (!global) {
   6851    return false;
   6852  }
   6853 
   6854  global->realm()->isUnlimitedStacksCapturingEnabled = true;
   6855 
   6856  args.rval().setUndefined();
   6857  return true;
   6858 }
   6859 
   6860 bool Debugger::CallData::disableUnlimitedStacksCapturing() {
   6861  if (!args.requireAtLeast(cx, "Debugger.disableUnlimitedStacksCapturing", 1)) {
   6862    return false;
   6863  }
   6864  Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
   6865  if (!global) {
   6866    return false;
   6867  }
   6868 
   6869  global->realm()->isUnlimitedStacksCapturingEnabled = false;
   6870 
   6871  args.rval().setUndefined();
   6872  return true;
   6873 }
   6874 
   6875 const JSPropertySpec Debugger::properties[] = {
   6876    JS_DEBUG_PSGS("onDebuggerStatement", getOnDebuggerStatement,
   6877                  setOnDebuggerStatement),
   6878    JS_DEBUG_PSGS("onExceptionUnwind", getOnExceptionUnwind,
   6879                  setOnExceptionUnwind),
   6880    JS_DEBUG_PSGS("onNewScript", getOnNewScript, setOnNewScript),
   6881    JS_DEBUG_PSGS("onNewPromise", getOnNewPromise, setOnNewPromise),
   6882    JS_DEBUG_PSGS("onPromiseSettled", getOnPromiseSettled, setOnPromiseSettled),
   6883    JS_DEBUG_PSGS("onEnterFrame", getOnEnterFrame, setOnEnterFrame),
   6884    JS_DEBUG_PSGS("onNativeCall", getOnNativeCall, setOnNativeCall),
   6885    JS_DEBUG_PSGS("shouldAvoidSideEffects", getShouldAvoidSideEffects,
   6886                  setShouldAvoidSideEffects),
   6887    JS_DEBUG_PSGS("onNewGlobalObject", getOnNewGlobalObject,
   6888                  setOnNewGlobalObject),
   6889    JS_DEBUG_PSGS("uncaughtExceptionHook", getUncaughtExceptionHook,
   6890                  setUncaughtExceptionHook),
   6891    JS_DEBUG_PSGS("allowUnobservedAsmJS", getAllowUnobservedAsmJS,
   6892                  setAllowUnobservedAsmJS),
   6893    JS_DEBUG_PSGS("allowUnobservedWasm", getAllowUnobservedWasm,
   6894                  setAllowUnobservedWasm),
   6895    JS_DEBUG_PSGS("collectCoverageInfo", getCollectCoverageInfo,
   6896                  setCollectCoverageInfo),
   6897    JS_DEBUG_PSGS("exclusiveDebuggerOnEval", getExclusiveDebuggerOnEval,
   6898                  setExclusiveDebuggerOnEval),
   6899    JS_DEBUG_PSGS("inspectNativeCallArguments", getInspectNativeCallArguments,
   6900                  setInspectNativeCallArguments),
   6901    JS_DEBUG_PSG("memory", getMemory),
   6902    JS_STRING_SYM_PS(toStringTag, "Debugger", JSPROP_READONLY),
   6903    JS_PS_END,
   6904 };
   6905 
   6906 const JSFunctionSpec Debugger::methods[] = {
   6907    JS_DEBUG_FN("addDebuggee", addDebuggee, 1),
   6908    JS_DEBUG_FN("addAllGlobalsAsDebuggees", addAllGlobalsAsDebuggees, 0),
   6909    JS_DEBUG_FN("removeDebuggee", removeDebuggee, 1),
   6910    JS_DEBUG_FN("removeAllDebuggees", removeAllDebuggees, 0),
   6911    JS_DEBUG_FN("hasDebuggee", hasDebuggee, 1),
   6912    JS_DEBUG_FN("getDebuggees", getDebuggees, 0),
   6913    JS_DEBUG_FN("getNewestFrame", getNewestFrame, 0),
   6914    JS_DEBUG_FN("clearAllBreakpoints", clearAllBreakpoints, 0),
   6915    JS_DEBUG_FN("findScripts", findScripts, 1),
   6916    JS_DEBUG_FN("findSources", findSources, 1),
   6917    JS_DEBUG_FN("findObjects", findObjects, 1),
   6918    JS_DEBUG_FN("findAllGlobals", findAllGlobals, 0),
   6919    JS_DEBUG_FN("findSourceURLs", findSourceURLs, 0),
   6920    JS_DEBUG_FN("makeGlobalObjectReference", makeGlobalObjectReference, 1),
   6921    JS_DEBUG_FN("adoptDebuggeeValue", adoptDebuggeeValue, 1),
   6922    JS_DEBUG_FN("adoptFrame", adoptFrame, 1),
   6923    JS_DEBUG_FN("adoptSource", adoptSource, 1),
   6924    JS_DEBUG_FN("enableAsyncStack", enableAsyncStack, 1),
   6925    JS_DEBUG_FN("disableAsyncStack", disableAsyncStack, 1),
   6926    JS_DEBUG_FN("enableUnlimitedStacksCapturing",
   6927                enableUnlimitedStacksCapturing, 1),
   6928    JS_DEBUG_FN("disableUnlimitedStacksCapturing",
   6929                disableUnlimitedStacksCapturing, 1),
   6930    JS_FS_END,
   6931 };
   6932 
   6933 const JSFunctionSpec Debugger::static_methods[]{
   6934    JS_FN("isCompilableUnit", Debugger::isCompilableUnit, 1, 0),
   6935    JS_FS_END,
   6936 };
   6937 
   6938 DebuggerScript* Debugger::newDebuggerScript(
   6939    JSContext* cx, Handle<DebuggerScriptReferent> referent) {
   6940  cx->check(object.get());
   6941 
   6942  RootedObject proto(
   6943      cx, &object->getReservedSlot(JSSLOT_DEBUG_SCRIPT_PROTO).toObject());
   6944  MOZ_ASSERT(proto);
   6945  Rooted<NativeObject*> debugger(cx, object);
   6946 
   6947  return DebuggerScript::create(cx, proto, referent, debugger);
   6948 }
   6949 
   6950 template <typename ReferentType, typename Map>
   6951 typename Map::WrapperType* Debugger::wrapVariantReferent(
   6952    JSContext* cx, Map& map,
   6953    Handle<typename Map::WrapperType::ReferentVariant> referent) {
   6954  cx->check(object);
   6955 
   6956  Handle<ReferentType*> untaggedReferent =
   6957      referent.template as<ReferentType*>();
   6958  MOZ_ASSERT(cx->compartment() != untaggedReferent->compartment());
   6959 
   6960  DependentAddPtr<Map> p(cx, map, untaggedReferent);
   6961  if (!p) {
   6962    typename Map::WrapperType* wrapper = newVariantWrapper(cx, referent);
   6963    if (!wrapper) {
   6964      return nullptr;
   6965    }
   6966 
   6967    if (!p.add(cx, map, untaggedReferent, wrapper)) {
   6968      // We need to destroy the edge to the referent, to avoid trying to trace
   6969      // it during untimely collections.
   6970      wrapper->clearReferent();
   6971      return nullptr;
   6972    }
   6973  }
   6974 
   6975  return &p->value()->template as<typename Map::WrapperType>();
   6976 }
   6977 
   6978 DebuggerScript* Debugger::wrapVariantReferent(
   6979    JSContext* cx, Handle<DebuggerScriptReferent> referent) {
   6980  if (referent.is<BaseScript*>()) {
   6981    return wrapVariantReferent<BaseScript>(cx, scripts, referent);
   6982  }
   6983 
   6984  return wrapVariantReferent<WasmInstanceObject>(cx, wasmInstanceScripts,
   6985                                                 referent);
   6986 }
   6987 
   6988 DebuggerScript* Debugger::wrapScript(JSContext* cx,
   6989                                     Handle<BaseScript*> script) {
   6990  Rooted<DebuggerScriptReferent> referent(cx,
   6991                                          DebuggerScriptReferent(script.get()));
   6992  return wrapVariantReferent(cx, referent);
   6993 }
   6994 
   6995 DebuggerScript* Debugger::wrapWasmScript(
   6996    JSContext* cx, Handle<WasmInstanceObject*> wasmInstance) {
   6997  Rooted<DebuggerScriptReferent> referent(cx, wasmInstance.get());
   6998  return wrapVariantReferent(cx, referent);
   6999 }
   7000 
   7001 DebuggerSource* Debugger::newDebuggerSource(
   7002    JSContext* cx, Handle<DebuggerSourceReferent> referent) {
   7003  cx->check(object.get());
   7004 
   7005  RootedObject proto(
   7006      cx, &object->getReservedSlot(JSSLOT_DEBUG_SOURCE_PROTO).toObject());
   7007  MOZ_ASSERT(proto);
   7008  Rooted<NativeObject*> debugger(cx, object);
   7009  return DebuggerSource::create(cx, proto, referent, debugger);
   7010 }
   7011 
   7012 DebuggerSource* Debugger::wrapVariantReferent(
   7013    JSContext* cx, Handle<DebuggerSourceReferent> referent) {
   7014  DebuggerSource* obj;
   7015  if (referent.is<ScriptSourceObject*>()) {
   7016    obj = wrapVariantReferent<ScriptSourceObject>(cx, sources, referent);
   7017  } else {
   7018    obj = wrapVariantReferent<WasmInstanceObject>(cx, wasmInstanceSources,
   7019                                                  referent);
   7020  }
   7021  MOZ_ASSERT_IF(obj, obj->getReferent() == referent);
   7022  return obj;
   7023 }
   7024 
   7025 DebuggerSource* Debugger::wrapSource(JSContext* cx,
   7026                                     Handle<ScriptSourceObject*> source) {
   7027  Rooted<DebuggerSourceReferent> referent(cx, source.get());
   7028  return wrapVariantReferent(cx, referent);
   7029 }
   7030 
   7031 DebuggerSource* Debugger::wrapWasmSource(
   7032    JSContext* cx, Handle<WasmInstanceObject*> wasmInstance) {
   7033  Rooted<DebuggerSourceReferent> referent(cx, wasmInstance.get());
   7034  return wrapVariantReferent(cx, referent);
   7035 }
   7036 
   7037 bool Debugger::observesFrame(AbstractFramePtr frame) const {
   7038  if (frame.isWasmDebugFrame()) {
   7039    return observesWasm(frame.wasmInstance());
   7040  }
   7041 
   7042  return observesScript(frame.script());
   7043 }
   7044 
   7045 bool Debugger::observesFrame(const FrameIter& iter) const {
   7046  // Skip frames not yet fully initialized during their prologue.
   7047  if (iter.isInterp() && iter.isFunctionFrame()) {
   7048    const Value& thisVal = iter.interpFrame()->thisArgument();
   7049    if (thisVal.isMagic() && thisVal.whyMagic() == JS_IS_CONSTRUCTING) {
   7050      return false;
   7051    }
   7052  }
   7053  if (iter.isWasm()) {
   7054    // Skip frame of wasm instances we cannot observe.
   7055    if (!iter.wasmDebugEnabled()) {
   7056      return false;
   7057    }
   7058    return observesWasm(iter.wasmInstance());
   7059  }
   7060  return observesScript(iter.script());
   7061 }
   7062 
   7063 bool Debugger::observesScript(JSScript* script) const {
   7064  // Don't ever observe self-hosted scripts: the Debugger API can break
   7065  // self-hosted invariants.
   7066  return observesGlobal(&script->global()) && !script->selfHosted();
   7067 }
   7068 
   7069 bool Debugger::observesWasm(wasm::Instance* instance) const {
   7070  if (!instance->debugEnabled()) {
   7071    return false;
   7072  }
   7073  return observesGlobal(&instance->object()->global());
   7074 }
   7075 
   7076 /* static */
   7077 bool Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from,
   7078                                AbstractFramePtr to, ScriptFrameIter& iter) {
   7079  MOZ_ASSERT(from != to);
   7080 
   7081  // Rekey missingScopes to maintain Debugger.Environment identity and
   7082  // forward liveScopes to point to the new frame.
   7083  DebugEnvironments::forwardLiveFrame(cx, from, to);
   7084 
   7085  // If we hit an OOM anywhere in here, we need to make sure there aren't any
   7086  // Debugger.Frame objects left partially-initialized.
   7087  auto terminateDebuggerFramesOnExit = MakeScopeExit([&] {
   7088    terminateDebuggerFrames(cx, from);
   7089    terminateDebuggerFrames(cx, to);
   7090 
   7091    MOZ_ASSERT(!DebugAPI::inFrameMaps(from));
   7092    MOZ_ASSERT(!DebugAPI::inFrameMaps(to));
   7093  });
   7094 
   7095  // Forward live Debugger.Frame objects.
   7096  Rooted<DebuggerFrameVector> frames(cx);
   7097  if (!getDebuggerFrames(from, &frames)) {
   7098    // An OOM here means that all Debuggers' frame maps still contain
   7099    // entries for 'from' and no entries for 'to'. Since the 'from' frame
   7100    // will be gone, they are removed by terminateDebuggerFramesOnExit
   7101    // above.
   7102    ReportOutOfMemory(cx);
   7103    return false;
   7104  }
   7105 
   7106  for (size_t i = 0; i < frames.length(); i++) {
   7107    Handle<DebuggerFrame*> frameobj = frames[i];
   7108    Debugger* dbg = frameobj->owner();
   7109 
   7110    // Update frame object's ScriptFrameIter::data pointer.
   7111    if (!frameobj->replaceFrameIterData(cx, iter)) {
   7112      return false;
   7113    }
   7114 
   7115    // Add the frame object with |to| as key.
   7116    if (!dbg->frames.putNew(to, frameobj)) {
   7117      ReportOutOfMemory(cx);
   7118      return false;
   7119    }
   7120 
   7121    // Remove the old frame entry after all fallible operations are completed
   7122    // so that an OOM will be able to clean up properly.
   7123    dbg->frames.remove(from);
   7124  }
   7125 
   7126  // All frames successfuly replaced, cancel the rollback.
   7127  terminateDebuggerFramesOnExit.release();
   7128 
   7129  MOZ_ASSERT(!DebugAPI::inFrameMaps(from));
   7130  MOZ_ASSERT_IF(!frames.empty(), DebugAPI::inFrameMaps(to));
   7131  return true;
   7132 }
   7133 
   7134 /* static */
   7135 bool DebugAPI::inFrameMaps(AbstractFramePtr frame) {
   7136  bool foundAny = false;
   7137  JS::AutoAssertNoGC nogc;
   7138  Debugger::forEachOnStackDebuggerFrame(
   7139      frame, nogc,
   7140      [&](Debugger*, DebuggerFrame* frameobj) { foundAny = true; });
   7141  return foundAny;
   7142 }
   7143 
   7144 /* static */
   7145 void Debugger::suspendGeneratorDebuggerFrames(JSContext* cx,
   7146                                              AbstractFramePtr frame) {
   7147  JS::GCContext* gcx = cx->gcContext();
   7148  JS::AutoAssertNoGC nogc;
   7149  forEachOnStackDebuggerFrame(
   7150      frame, nogc, [&](Debugger* dbg, DebuggerFrame* dbgFrame) {
   7151        dbg->frames.remove(frame);
   7152 
   7153 #if DEBUG
   7154        MOZ_ASSERT(dbgFrame->hasGeneratorInfo());
   7155        AbstractGeneratorObject& genObj = dbgFrame->unwrappedGenerator();
   7156        GeneratorWeakMap::Ptr p = dbg->generatorFrames.lookup(&genObj);
   7157        MOZ_ASSERT(p);
   7158        MOZ_ASSERT(p->value() == dbgFrame);
   7159 #endif
   7160 
   7161        dbgFrame->suspend(gcx);
   7162      });
   7163 }
   7164 
   7165 /* static */
   7166 void Debugger::terminateDebuggerFrames(JSContext* cx, AbstractFramePtr frame) {
   7167  JS::GCContext* gcx = cx->gcContext();
   7168 
   7169  JS::AutoAssertNoGC nogc;
   7170  forEachOnStackOrSuspendedDebuggerFrame(
   7171      cx, frame, nogc, [&](Debugger* dbg, DebuggerFrame* dbgFrame) {
   7172        Debugger::terminateDebuggerFrame(gcx, dbg, dbgFrame, frame);
   7173      });
   7174 
   7175  // If this is an eval frame, then from the debugger's perspective the
   7176  // script is about to be destroyed. Remove any breakpoints in it.
   7177  if (frame.isEvalFrame()) {
   7178    RootedScript script(cx, frame.script());
   7179    DebugScript::clearBreakpointsIn(cx->gcContext(), script, nullptr, nullptr);
   7180  }
   7181 }
   7182 
   7183 /* static */
   7184 void Debugger::terminateDebuggerFrame(
   7185    JS::GCContext* gcx, Debugger* dbg, DebuggerFrame* dbgFrame,
   7186    AbstractFramePtr frame, FrameMap::Enum* maybeFramesEnum,
   7187    GeneratorWeakMap::Enum* maybeGeneratorFramesEnum) {
   7188  // If we were not passed the frame, either we are destroying a frame early
   7189  // on before it was inserted into the "frames" list, or else we are
   7190  // terminating a frame from "generatorFrames" and the "frames" entries will
   7191  // be cleaned up later on with a second call to this function.
   7192  MOZ_ASSERT_IF(!frame, !maybeFramesEnum);
   7193  MOZ_ASSERT_IF(!frame, dbgFrame->hasGeneratorInfo());
   7194  MOZ_ASSERT_IF(!dbgFrame->hasGeneratorInfo(), !maybeGeneratorFramesEnum);
   7195 
   7196  if (frame) {
   7197    if (maybeFramesEnum) {
   7198      maybeFramesEnum->removeFront();
   7199    } else {
   7200      dbg->frames.remove(frame);
   7201    }
   7202  }
   7203 
   7204  if (dbgFrame->hasGeneratorInfo()) {
   7205    if (maybeGeneratorFramesEnum) {
   7206      maybeGeneratorFramesEnum->removeFront();
   7207    } else {
   7208      dbg->generatorFrames.remove(&dbgFrame->unwrappedGenerator());
   7209    }
   7210  }
   7211 
   7212  dbgFrame->terminate(gcx, frame);
   7213 }
   7214 
   7215 DebuggerDebuggeeLink* Debugger::getDebuggeeLink() {
   7216  return &object->getReservedSlot(JSSLOT_DEBUG_DEBUGGEE_LINK)
   7217              .toObject()
   7218              .as<DebuggerDebuggeeLink>();
   7219 }
   7220 
   7221 void DebuggerDebuggeeLink::setLinkSlot(Debugger& dbg) {
   7222  setReservedSlot(DEBUGGER_LINK_SLOT, ObjectValue(*dbg.toJSObject()));
   7223 }
   7224 
   7225 void DebuggerDebuggeeLink::clearLinkSlot() {
   7226  setReservedSlot(DEBUGGER_LINK_SLOT, UndefinedValue());
   7227 }
   7228 
   7229 const JSClass DebuggerDebuggeeLink::class_ = {
   7230    "DebuggerDebuggeeLink",
   7231    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
   7232 };
   7233 
   7234 /* static */
   7235 bool DebugAPI::handleBaselineOsr(JSContext* cx, InterpreterFrame* from,
   7236                                 jit::BaselineFrame* to) {
   7237  ScriptFrameIter iter(cx);
   7238  MOZ_ASSERT(iter.abstractFramePtr() == to);
   7239  return Debugger::replaceFrameGuts(cx, from, to, iter);
   7240 }
   7241 
   7242 /* static */
   7243 bool DebugAPI::handleIonBailout(JSContext* cx, jit::RematerializedFrame* from,
   7244                                jit::BaselineFrame* to) {
   7245  // When we return to a bailed-out Ion real frame, we must update all
   7246  // Debugger.Frames that refer to its inline frames. However, since we
   7247  // can't pop individual inline frames off the stack (we can only pop the
   7248  // real frame that contains them all, as a unit), we cannot assume that
   7249  // the frame we're dealing with is the top frame. Advance the iterator
   7250  // across any inlined frames younger than |to|, the baseline frame
   7251  // reconstructed during bailout from the Ion frame corresponding to
   7252  // |from|.
   7253  ScriptFrameIter iter(cx);
   7254  while (iter.abstractFramePtr() != to) {
   7255    ++iter;
   7256  }
   7257  return Debugger::replaceFrameGuts(cx, from, to, iter);
   7258 }
   7259 
   7260 /* static */
   7261 void DebugAPI::handleUnrecoverableIonBailoutError(
   7262    JSContext* cx, jit::RematerializedFrame* frame) {
   7263  // Ion bailout can fail due to overrecursion. In such cases we cannot
   7264  // honor any further Debugger hooks on the frame, and need to ensure that
   7265  // its Debugger.Frame entry is cleaned up.
   7266  Debugger::terminateDebuggerFrames(cx, frame);
   7267 }
   7268 
   7269 /*** JS::dbg::Builder *******************************************************/
   7270 
   7271 Builder::Builder(JSContext* cx, js::Debugger* debugger)
   7272    : debuggerObject(cx, debugger->toJSObject().get()), debugger(debugger) {}
   7273 
   7274 #if DEBUG
   7275 void Builder::assertBuilt(JSObject* obj) {
   7276  // We can't use assertSameCompartment here, because that is always keyed to
   7277  // some JSContext's current compartment, whereas BuiltThings can be
   7278  // constructed and assigned to without respect to any particular context;
   7279  // the only constraint is that they should be in their debugger's compartment.
   7280  MOZ_ASSERT_IF(obj, debuggerObject->compartment() == obj->compartment());
   7281 }
   7282 #endif
   7283 
   7284 bool Builder::Object::definePropertyToTrusted(JSContext* cx, const char* name,
   7285                                              JS::MutableHandleValue trusted) {
   7286  // We should have checked for false Objects before calling this.
   7287  MOZ_ASSERT(value);
   7288 
   7289  JSAtom* atom = Atomize(cx, name, strlen(name));
   7290  if (!atom) {
   7291    return false;
   7292  }
   7293  RootedId id(cx, AtomToId(atom));
   7294 
   7295  return DefineDataProperty(cx, value, id, trusted);
   7296 }
   7297 
   7298 bool Builder::Object::defineProperty(JSContext* cx, const char* name,
   7299                                     JS::HandleValue propval_) {
   7300  AutoRealm ar(cx, debuggerObject());
   7301 
   7302  RootedValue propval(cx, propval_);
   7303  if (!debugger()->wrapDebuggeeValue(cx, &propval)) {
   7304    return false;
   7305  }
   7306 
   7307  return definePropertyToTrusted(cx, name, &propval);
   7308 }
   7309 
   7310 bool Builder::Object::defineProperty(JSContext* cx, const char* name,
   7311                                     JS::HandleObject propval_) {
   7312  RootedValue propval(cx, ObjectOrNullValue(propval_));
   7313  return defineProperty(cx, name, propval);
   7314 }
   7315 
   7316 bool Builder::Object::defineProperty(JSContext* cx, const char* name,
   7317                                     Builder::Object& propval_) {
   7318  AutoRealm ar(cx, debuggerObject());
   7319 
   7320  RootedValue propval(cx, ObjectOrNullValue(propval_.value));
   7321  return definePropertyToTrusted(cx, name, &propval);
   7322 }
   7323 
   7324 Builder::Object Builder::newObject(JSContext* cx) {
   7325  AutoRealm ar(cx, debuggerObject);
   7326 
   7327  Rooted<PlainObject*> obj(cx, NewPlainObject(cx));
   7328 
   7329  // If the allocation failed, this will return a false Object, as the spec
   7330  // promises.
   7331  return Object(cx, *this, obj);
   7332 }
   7333 
   7334 /*** Glue *******************************************************************/
   7335 
   7336 extern JS_PUBLIC_API bool JS_DefineDebuggerObject(JSContext* cx,
   7337                                                  HandleObject obj) {
   7338  Rooted<NativeObject*> debugCtor(cx), debugProto(cx), frameProto(cx),
   7339      scriptProto(cx), sourceProto(cx), objectProto(cx), envProto(cx),
   7340      memoryProto(cx);
   7341  RootedObject debuggeeWouldRunProto(cx);
   7342  RootedValue debuggeeWouldRunCtor(cx);
   7343  Handle<GlobalObject*> global = obj.as<GlobalObject>();
   7344 
   7345  debugProto = InitClass(cx, global, &DebuggerPrototypeObject::class_, nullptr,
   7346                         "Debugger", Debugger::construct, 1,
   7347                         Debugger::properties, Debugger::methods, nullptr,
   7348                         Debugger::static_methods, debugCtor.address());
   7349  if (!debugProto) {
   7350    return false;
   7351  }
   7352 
   7353  frameProto = DebuggerFrame::initClass(cx, global, debugCtor);
   7354  if (!frameProto) {
   7355    return false;
   7356  }
   7357 
   7358  scriptProto = DebuggerScript::initClass(cx, global, debugCtor);
   7359  if (!scriptProto) {
   7360    return false;
   7361  }
   7362 
   7363  sourceProto = DebuggerSource::initClass(cx, global, debugCtor);
   7364  if (!sourceProto) {
   7365    return false;
   7366  }
   7367 
   7368  objectProto = DebuggerObject::initClass(cx, global, debugCtor);
   7369  if (!objectProto) {
   7370    return false;
   7371  }
   7372 
   7373  envProto = DebuggerEnvironment::initClass(cx, global, debugCtor);
   7374  if (!envProto) {
   7375    return false;
   7376  }
   7377 
   7378  memoryProto = InitClass(
   7379      cx, debugCtor, nullptr, nullptr, "Memory", DebuggerMemory::construct, 0,
   7380      DebuggerMemory::properties, DebuggerMemory::methods, nullptr, nullptr);
   7381  if (!memoryProto) {
   7382    return false;
   7383  }
   7384 
   7385  debuggeeWouldRunProto = GlobalObject::getOrCreateCustomErrorPrototype(
   7386      cx, global, JSEXN_DEBUGGEEWOULDRUN);
   7387  if (!debuggeeWouldRunProto) {
   7388    return false;
   7389  }
   7390  debuggeeWouldRunCtor =
   7391      ObjectValue(global->getConstructor(JSProto_DebuggeeWouldRun));
   7392  RootedId debuggeeWouldRunId(
   7393      cx, NameToId(ClassName(JSProto_DebuggeeWouldRun, cx)));
   7394  if (!DefineDataProperty(cx, debugCtor, debuggeeWouldRunId,
   7395                          debuggeeWouldRunCtor, 0)) {
   7396    return false;
   7397  }
   7398 
   7399  debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_FRAME_PROTO,
   7400                              ObjectValue(*frameProto));
   7401  debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_OBJECT_PROTO,
   7402                              ObjectValue(*objectProto));
   7403  debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SCRIPT_PROTO,
   7404                              ObjectValue(*scriptProto));
   7405  debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SOURCE_PROTO,
   7406                              ObjectValue(*sourceProto));
   7407  debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_ENV_PROTO,
   7408                              ObjectValue(*envProto));
   7409  debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_MEMORY_PROTO,
   7410                              ObjectValue(*memoryProto));
   7411  return true;
   7412 }
   7413 
   7414 extern JS_PUBLIC_API const char* JS_GetLastOOMStackTrace(JSContext* cx) {
   7415  return cx->getOOMStackTrace();
   7416 }
   7417 
   7418 JS_PUBLIC_API bool JS::dbg::IsDebugger(JSObject& obj) {
   7419  /* We only care about debugger objects, so CheckedUnwrapStatic is OK. */
   7420  JSObject* unwrapped = CheckedUnwrapStatic(&obj);
   7421  if (!unwrapped || !unwrapped->is<DebuggerInstanceObject>()) {
   7422    return false;
   7423  }
   7424  MOZ_ASSERT(js::Debugger::fromJSObject(unwrapped));
   7425  return true;
   7426 }
   7427 
   7428 JS_PUBLIC_API bool JS::dbg::GetDebuggeeGlobals(
   7429    JSContext* cx, JSObject& dbgObj, MutableHandleObjectVector vector) {
   7430  MOZ_ASSERT(IsDebugger(dbgObj));
   7431  /* Since we know we have a debugger object, CheckedUnwrapStatic is fine. */
   7432  js::Debugger* dbg = js::Debugger::fromJSObject(CheckedUnwrapStatic(&dbgObj));
   7433 
   7434  if (!vector.reserve(vector.length() + dbg->debuggees.count())) {
   7435    JS_ReportOutOfMemory(cx);
   7436    return false;
   7437  }
   7438 
   7439  for (WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty();
   7440       r.popFront()) {
   7441    vector.infallibleAppend(static_cast<JSObject*>(r.front()));
   7442  }
   7443 
   7444  return true;
   7445 }
   7446 
   7447 #ifdef DEBUG
   7448 /* static */
   7449 bool Debugger::isDebuggerCrossCompartmentEdge(JSObject* obj,
   7450                                              const gc::Cell* target) {
   7451  MOZ_ASSERT(target);
   7452 
   7453  const gc::Cell* referent = nullptr;
   7454  if (obj->is<DebuggerScript>()) {
   7455    referent = obj->as<DebuggerScript>().getReferentCell();
   7456  } else if (obj->is<DebuggerSource>()) {
   7457    referent = obj->as<DebuggerSource>().getReferentRawObject();
   7458  } else if (obj->is<DebuggerObject>()) {
   7459    referent = obj->as<DebuggerObject>().referent();
   7460  } else if (obj->is<DebuggerEnvironment>()) {
   7461    referent = obj->as<DebuggerEnvironment>().referent();
   7462  }
   7463 
   7464  return referent == target;
   7465 }
   7466 
   7467 static void CheckDebuggeeThingRealm(Realm* realm, bool invisibleOk) {
   7468  MOZ_ASSERT_IF(!invisibleOk, !realm->creationOptions().invisibleToDebugger());
   7469 }
   7470 
   7471 void js::CheckDebuggeeThing(BaseScript* script, bool invisibleOk) {
   7472  CheckDebuggeeThingRealm(script->realm(), invisibleOk);
   7473 }
   7474 
   7475 void js::CheckDebuggeeThing(JSObject* obj, bool invisibleOk) {
   7476  if (Realm* realm = JS::GetObjectRealmOrNull(obj)) {
   7477    CheckDebuggeeThingRealm(realm, invisibleOk);
   7478  }
   7479 }
   7480 #endif  // DEBUG
   7481 
   7482 /*** JS::dbg::GarbageCollectionEvent ****************************************/
   7483 
   7484 namespace JS {
   7485 namespace dbg {
   7486 
   7487 /* static */ GarbageCollectionEvent::Ptr GarbageCollectionEvent::Create(
   7488    JSRuntime* rt, ::js::gcstats::Statistics& stats, uint64_t gcNumber) {
   7489  auto data = MakeUnique<GarbageCollectionEvent>(gcNumber);
   7490  if (!data) {
   7491    return nullptr;
   7492  }
   7493 
   7494  data->nonincrementalReason = stats.nonincrementalReason();
   7495 
   7496  for (auto& slice : stats.slices()) {
   7497    if (!data->reason) {
   7498      // There is only one GC reason for the whole cycle, but for legacy
   7499      // reasons this data is stored and replicated on each slice. Each
   7500      // slice used to have its own GCReason, but now they are all the
   7501      // same.
   7502      data->reason = ExplainGCReason(slice.reason);
   7503      MOZ_ASSERT(data->reason);
   7504    }
   7505 
   7506    if (!data->collections.growBy(1)) {
   7507      return nullptr;
   7508    }
   7509 
   7510    data->collections.back().startTimestamp = slice.start;
   7511    data->collections.back().endTimestamp = slice.end;
   7512  }
   7513 
   7514  return data;
   7515 }
   7516 
   7517 static bool DefineStringProperty(JSContext* cx, HandleObject obj,
   7518                                 PropertyName* propName, const char* strVal) {
   7519  RootedValue val(cx, UndefinedValue());
   7520  if (strVal) {
   7521    JSAtom* atomized = Atomize(cx, strVal, strlen(strVal));
   7522    if (!atomized) {
   7523      return false;
   7524    }
   7525    val = StringValue(atomized);
   7526  }
   7527  return DefineDataProperty(cx, obj, propName, val);
   7528 }
   7529 
   7530 JSObject* GarbageCollectionEvent::toJSObject(JSContext* cx) const {
   7531  RootedObject obj(cx, NewPlainObject(cx));
   7532  RootedValue gcCycleNumberVal(cx, NumberValue(majorGCNumber_));
   7533  if (!obj ||
   7534      !DefineStringProperty(cx, obj, cx->names().nonincrementalReason,
   7535                            nonincrementalReason) ||
   7536      !DefineStringProperty(cx, obj, cx->names().reason, reason) ||
   7537      !DefineDataProperty(cx, obj, cx->names().gcCycleNumber,
   7538                          gcCycleNumberVal)) {
   7539    return nullptr;
   7540  }
   7541 
   7542  Rooted<ArrayObject*> slicesArray(cx, NewDenseEmptyArray(cx));
   7543  if (!slicesArray) {
   7544    return nullptr;
   7545  }
   7546 
   7547  TimeStamp originTime = TimeStamp::ProcessCreation();
   7548 
   7549  size_t idx = 0;
   7550  for (auto range = collections.all(); !range.empty(); range.popFront()) {
   7551    Rooted<PlainObject*> collectionObj(cx, NewPlainObject(cx));
   7552    if (!collectionObj) {
   7553      return nullptr;
   7554    }
   7555 
   7556    RootedValue start(cx), end(cx);
   7557    start = NumberValue(
   7558        (range.front().startTimestamp - originTime).ToMilliseconds());
   7559    end =
   7560        NumberValue((range.front().endTimestamp - originTime).ToMilliseconds());
   7561    if (!DefineDataProperty(cx, collectionObj, cx->names().startTimestamp,
   7562                            start) ||
   7563        !DefineDataProperty(cx, collectionObj, cx->names().endTimestamp, end)) {
   7564      return nullptr;
   7565    }
   7566 
   7567    RootedValue collectionVal(cx, ObjectValue(*collectionObj));
   7568    if (!DefineDataElement(cx, slicesArray, idx++, collectionVal)) {
   7569      return nullptr;
   7570    }
   7571  }
   7572 
   7573  RootedValue slicesValue(cx, ObjectValue(*slicesArray));
   7574  if (!DefineDataProperty(cx, obj, cx->names().collections, slicesValue)) {
   7575    return nullptr;
   7576  }
   7577 
   7578  return obj;
   7579 }
   7580 
   7581 JS_PUBLIC_API bool FireOnGarbageCollectionHookRequired(JSContext* cx) {
   7582  AutoCheckCannotGC noGC;
   7583 
   7584  for (auto& dbg : cx->runtime()->onGarbageCollectionWatchers()) {
   7585    MOZ_ASSERT(dbg.getHook(Debugger::OnGarbageCollection));
   7586    if (dbg.observedGC(cx->runtime()->gc.majorGCCount())) {
   7587      return true;
   7588    }
   7589  }
   7590 
   7591  return false;
   7592 }
   7593 
   7594 JS_PUBLIC_API bool FireOnGarbageCollectionHook(
   7595    JSContext* cx, JS::dbg::GarbageCollectionEvent::Ptr&& data) {
   7596  RootedObjectVector triggered(cx);
   7597 
   7598  {
   7599    // We had better not GC (and potentially get a dangling Debugger
   7600    // pointer) while finding all Debuggers observing a debuggee that
   7601    // participated in this GC.
   7602    AutoCheckCannotGC noGC;
   7603 
   7604    for (auto& dbg : cx->runtime()->onGarbageCollectionWatchers()) {
   7605      MOZ_ASSERT(dbg.getHook(Debugger::OnGarbageCollection));
   7606      if (dbg.observedGC(data->majorGCNumber())) {
   7607        if (!triggered.append(dbg.object)) {
   7608          JS_ReportOutOfMemory(cx);
   7609          return false;
   7610        }
   7611      }
   7612    }
   7613  }
   7614 
   7615  for (; !triggered.empty(); triggered.popBack()) {
   7616    Debugger* dbg = Debugger::fromJSObject(triggered.back());
   7617 
   7618    if (dbg->getHook(Debugger::OnGarbageCollection)) {
   7619      (void)dbg->enterDebuggerHook(cx, [&]() -> bool {
   7620        return dbg->fireOnGarbageCollectionHook(cx, data);
   7621      });
   7622      MOZ_ASSERT(!cx->isExceptionPending());
   7623    }
   7624  }
   7625 
   7626  return true;
   7627 }
   7628 
   7629 bool ShouldAvoidSideEffects(JSContext* cx) {
   7630  return DebugAPI::shouldAvoidSideEffects(cx);
   7631 }
   7632 
   7633 }  // namespace dbg
   7634 }  // namespace JS