tor-browser

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

Script.cpp (75529B)


      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/Script-inl.h"
      8 
      9 #include "mozilla/Maybe.h"   // for Some, Maybe
     10 #include "mozilla/Span.h"    // for Span
     11 #include "mozilla/Vector.h"  // for Vector
     12 
     13 #include <stddef.h>  // for ptrdiff_t
     14 #include <stdint.h>  // for uint32_t, UINT32_MAX, SIZE_MAX, int32_t
     15 
     16 #include "jsnum.h"             // for ToNumber
     17 #include "NamespaceImports.h"  // for CallArgs, RootedValue
     18 
     19 #include "builtin/Array.h"         // for NewDenseEmptyArray
     20 #include "debugger/Debugger.h"     // for DebuggerScriptReferent, Debugger
     21 #include "debugger/DebugScript.h"  // for DebugScript
     22 #include "debugger/Source.h"       // for DebuggerSource
     23 #include "gc/GC.h"                 // for MemoryUse, MemoryUse::Breakpoint
     24 #include "gc/Tracer.h"         // for TraceManuallyBarrieredCrossCompartmentEdge
     25 #include "gc/Zone.h"           // for Zone
     26 #include "gc/ZoneAllocator.h"  // for AddCellMemory
     27 #include "js/CallArgs.h"       // for CallArgs, CallArgsFromVp
     28 #include "js/ColumnNumber.h"  // JS::LimitedColumnNumberOneOrigin, JS::WasmFunctionIndex
     29 #include "js/friend/ErrorMessages.h"  // for GetErrorMessage, JSMSG_*
     30 #include "js/GCVariant.h"             // for GCVariant
     31 #include "js/HeapAPI.h"               // for GCCellPtr
     32 #include "js/RootingAPI.h"            // for Rooted
     33 #include "js/Wrapper.h"               // for UncheckedUnwrap
     34 #include "vm/ArrayObject.h"           // for ArrayObject
     35 #include "vm/BytecodeUtil.h"          // for GET_JUMP_OFFSET
     36 #include "vm/Compartment.h"           // for JS::Compartment
     37 #include "vm/EnvironmentObject.h"     // for EnvironmentCoordinateNameSlow
     38 #include "vm/GlobalObject.h"          // for GlobalObject
     39 #include "vm/JSContext.h"             // for JSContext, ReportValueError
     40 #include "vm/JSFunction.h"            // for JSFunction
     41 #include "vm/JSObject.h"              // for RequireObject, JSObject
     42 #include "vm/JSScript.h"              // for BaseScript
     43 #include "vm/ObjectOperations.h"      // for DefineDataProperty, HasOwnProperty
     44 #include "vm/PlainObject.h"           // for js::PlainObject
     45 #include "vm/Realm.h"                 // for AutoRealm
     46 #include "vm/Runtime.h"               // for JSAtomState, JSRuntime
     47 #include "vm/StringType.h"            // for NameToId, PropertyName, JSAtom
     48 #include "wasm/WasmDebug.h"           // for ExprLoc, DebugState
     49 #include "wasm/WasmInstance.h"        // for Instance
     50 #include "wasm/WasmJS.h"              // for WasmInstanceObject
     51 #include "wasm/WasmTypeDecls.h"       // for Bytes
     52 
     53 #include "gc/Marking-inl.h"       // for MaybeForwardedObjectIs
     54 #include "vm/BytecodeUtil-inl.h"  // for BytecodeRangeWithPosition
     55 #include "vm/JSAtomUtils-inl.h"   // for PrimitiveValueToId
     56 #include "vm/JSObject-inl.h"  // for NewBuiltinClassInstance, NewObjectWithGivenProto, NewTenuredObjectWithGivenProto
     57 #include "vm/JSScript-inl.h"  // for JSScript::global
     58 #include "vm/ObjectOperations-inl.h"  // for GetProperty
     59 #include "vm/Realm-inl.h"             // for AutoRealm::AutoRealm
     60 
     61 using namespace js;
     62 
     63 using mozilla::Maybe;
     64 using mozilla::Some;
     65 
     66 const JSClassOps DebuggerScript::classOps_ = {
     67    nullptr,                          // addProperty
     68    nullptr,                          // delProperty
     69    nullptr,                          // enumerate
     70    nullptr,                          // newEnumerate
     71    nullptr,                          // resolve
     72    nullptr,                          // mayResolve
     73    nullptr,                          // finalize
     74    nullptr,                          // call
     75    nullptr,                          // construct
     76    CallTraceMethod<DebuggerScript>,  // trace
     77 };
     78 
     79 const JSClass DebuggerScript::class_ = {
     80    "Script",
     81    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
     82    &classOps_,
     83 };
     84 
     85 void DebuggerScript::trace(JSTracer* trc) {
     86  // This comes from a private pointer, so no barrier needed.
     87  gc::Cell* cell = getReferentCell();
     88  if (cell) {
     89    if (cell->is<BaseScript>()) {
     90      BaseScript* script = cell->as<BaseScript>();
     91      TraceManuallyBarrieredCrossCompartmentEdge(
     92          trc, this, &script, "Debugger.Script script referent");
     93      if (script != cell->as<BaseScript>()) {
     94        setReservedSlotGCThingAsPrivateUnbarriered(SCRIPT_SLOT, script);
     95      }
     96    } else {
     97      JSObject* wasm = cell->as<JSObject>();
     98      TraceManuallyBarrieredCrossCompartmentEdge(
     99          trc, this, &wasm, "Debugger.Script wasm referent");
    100      if (wasm != cell->as<JSObject>()) {
    101        MOZ_ASSERT(gc::MaybeForwardedObjectIs<WasmInstanceObject>(wasm));
    102        setReservedSlotGCThingAsPrivateUnbarriered(SCRIPT_SLOT, wasm);
    103      }
    104    }
    105  }
    106 }
    107 
    108 /* static */
    109 NativeObject* DebuggerScript::initClass(JSContext* cx,
    110                                        Handle<GlobalObject*> global,
    111                                        HandleObject debugCtor) {
    112  return InitClass(cx, debugCtor, nullptr, nullptr, "Script", construct, 0,
    113                   properties_, methods_, nullptr, nullptr);
    114 }
    115 
    116 /* static */
    117 DebuggerScript* DebuggerScript::create(JSContext* cx, HandleObject proto,
    118                                       Handle<DebuggerScriptReferent> referent,
    119                                       Handle<NativeObject*> debugger) {
    120  DebuggerScript* scriptobj =
    121      NewTenuredObjectWithGivenProto<DebuggerScript>(cx, proto);
    122  if (!scriptobj) {
    123    return nullptr;
    124  }
    125 
    126  scriptobj->setReservedSlot(DebuggerScript::OWNER_SLOT,
    127                             ObjectValue(*debugger));
    128  referent.get().match([&](auto& scriptHandle) {
    129    scriptobj->setReservedSlotGCThingAsPrivate(SCRIPT_SLOT, scriptHandle);
    130  });
    131 
    132  return scriptobj;
    133 }
    134 
    135 static JSScript* DelazifyScript(JSContext* cx, Handle<BaseScript*> script) {
    136  if (script->hasBytecode()) {
    137    return script->asJSScript();
    138  }
    139  MOZ_ASSERT(script->isFunction());
    140 
    141  // JSFunction::getOrCreateScript requires an enclosing scope. This requires
    142  // the enclosing script to be non-lazy.
    143  if (script->hasEnclosingScript()) {
    144    Rooted<BaseScript*> enclosingScript(cx, script->enclosingScript());
    145    if (!DelazifyScript(cx, enclosingScript)) {
    146      return nullptr;
    147    }
    148 
    149    if (!script->isReadyForDelazification()) {
    150      // It didn't work! Delazifying the enclosing script still didn't
    151      // delazify this script. This happens when the function
    152      // corresponding to this script was removed by constant folding.
    153      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    154                                JSMSG_DEBUG_OPTIMIZED_OUT_FUN);
    155      return nullptr;
    156    }
    157  }
    158 
    159  MOZ_ASSERT(script->enclosingScope());
    160 
    161  RootedFunction fun(cx, script->function());
    162  AutoRealm ar(cx, fun);
    163  return JSFunction::getOrCreateScript(cx, fun);
    164 }
    165 
    166 /* static */
    167 DebuggerScript* DebuggerScript::check(JSContext* cx, HandleValue v) {
    168  JSObject* thisobj = RequireObject(cx, v);
    169  if (!thisobj) {
    170    return nullptr;
    171  }
    172  if (!thisobj->is<DebuggerScript>()) {
    173    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    174                              JSMSG_INCOMPATIBLE_PROTO, "Debugger.Script",
    175                              "method", thisobj->getClass()->name);
    176    return nullptr;
    177  }
    178 
    179  return &thisobj->as<DebuggerScript>();
    180 }
    181 
    182 struct MOZ_STACK_CLASS DebuggerScript::CallData {
    183  JSContext* cx;
    184  const CallArgs& args;
    185 
    186  Handle<DebuggerScript*> obj;
    187  Rooted<DebuggerScriptReferent> referent;
    188  RootedScript script;
    189 
    190  CallData(JSContext* cx, const CallArgs& args, Handle<DebuggerScript*> obj)
    191      : cx(cx),
    192        args(args),
    193        obj(obj),
    194        referent(cx, obj->getReferent()),
    195        script(cx) {}
    196 
    197  [[nodiscard]] bool ensureScriptMaybeLazy() {
    198    if (!referent.is<BaseScript*>()) {
    199      ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
    200                       args.thisv(), nullptr, "a JS script");
    201      return false;
    202    }
    203    return true;
    204  }
    205 
    206  [[nodiscard]] bool ensureScript() {
    207    if (!ensureScriptMaybeLazy()) {
    208      return false;
    209    }
    210    script = DelazifyScript(cx, referent.as<BaseScript*>());
    211    if (!script) {
    212      return false;
    213    }
    214    return true;
    215  }
    216 
    217  bool getIsGeneratorFunction();
    218  bool getIsAsyncFunction();
    219  bool getIsFunction();
    220  bool getIsModule();
    221  bool getDisplayName();
    222  bool getParameterNames();
    223  bool getUrl();
    224  bool getStartLine();
    225  bool getStartColumn();
    226  bool getLineCount();
    227  bool getSource();
    228  bool getSourceStart();
    229  bool getSourceLength();
    230  bool getMainOffset();
    231  bool getGlobal();
    232  bool getFormat();
    233  bool getChildScripts();
    234  bool getPossibleBreakpoints();
    235  bool getPossibleBreakpointOffsets();
    236  bool getOffsetMetadata();
    237  bool getOffsetLocation();
    238  bool getEffectfulOffsets();
    239  bool getAllOffsets();
    240  bool getAllColumnOffsets();
    241  bool getLineOffsets();
    242  bool setBreakpoint();
    243  bool getBreakpoints();
    244  bool clearBreakpoint();
    245  bool clearAllBreakpoints();
    246  bool isInCatchScope();
    247  bool getOffsetsCoverage();
    248 
    249  using Method = bool (CallData::*)();
    250 
    251  template <Method MyMethod>
    252  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
    253 };
    254 
    255 template <DebuggerScript::CallData::Method MyMethod>
    256 /* static */
    257 bool DebuggerScript::CallData::ToNative(JSContext* cx, unsigned argc,
    258                                        Value* vp) {
    259  CallArgs args = CallArgsFromVp(argc, vp);
    260 
    261  Rooted<DebuggerScript*> obj(cx, DebuggerScript::check(cx, args.thisv()));
    262  if (!obj) {
    263    return false;
    264  }
    265 
    266  CallData data(cx, args, obj);
    267  return (data.*MyMethod)();
    268 }
    269 
    270 bool DebuggerScript::CallData::getIsGeneratorFunction() {
    271  if (!ensureScriptMaybeLazy()) {
    272    return false;
    273  }
    274  args.rval().setBoolean(obj->getReferentScript()->isGenerator());
    275  return true;
    276 }
    277 
    278 bool DebuggerScript::CallData::getIsAsyncFunction() {
    279  if (!ensureScriptMaybeLazy()) {
    280    return false;
    281  }
    282  args.rval().setBoolean(obj->getReferentScript()->isAsync());
    283  return true;
    284 }
    285 
    286 bool DebuggerScript::CallData::getIsFunction() {
    287  if (!ensureScriptMaybeLazy()) {
    288    return false;
    289  }
    290 
    291  args.rval().setBoolean(obj->getReferentScript()->function());
    292  return true;
    293 }
    294 
    295 bool DebuggerScript::CallData::getIsModule() {
    296  if (!ensureScriptMaybeLazy()) {
    297    return false;
    298  }
    299  BaseScript* script = referent.as<BaseScript*>();
    300 
    301  args.rval().setBoolean(script->isModule());
    302  return true;
    303 }
    304 
    305 bool DebuggerScript::CallData::getDisplayName() {
    306  if (!ensureScriptMaybeLazy()) {
    307    return false;
    308  }
    309 
    310  JSFunction* func = obj->getReferentScript()->function();
    311  if (!func) {
    312    args.rval().setUndefined();
    313    return true;
    314  }
    315 
    316  JSAtom* name = func->fullDisplayAtom();
    317  if (!name) {
    318    args.rval().setUndefined();
    319    return true;
    320  }
    321 
    322  RootedValue namev(cx, StringValue(name));
    323  Debugger* dbg = obj->owner();
    324  if (!dbg->wrapDebuggeeValue(cx, &namev)) {
    325    return false;
    326  }
    327  args.rval().set(namev);
    328  return true;
    329 }
    330 
    331 bool DebuggerScript::CallData::getParameterNames() {
    332  if (!ensureScript()) {
    333    return false;
    334  }
    335 
    336  RootedFunction fun(cx, referent.as<BaseScript*>()->function());
    337  if (!fun) {
    338    args.rval().setUndefined();
    339    return true;
    340  }
    341 
    342  ArrayObject* arr = GetFunctionParameterNamesArray(cx, fun);
    343  if (!arr) {
    344    return false;
    345  }
    346 
    347  args.rval().setObject(*arr);
    348  return true;
    349 }
    350 
    351 bool DebuggerScript::CallData::getUrl() {
    352  if (!ensureScriptMaybeLazy()) {
    353    return false;
    354  }
    355 
    356  Rooted<BaseScript*> script(cx, referent.as<BaseScript*>());
    357 
    358  if (script->filename()) {
    359    JSString* str;
    360    if (const char* introducer = script->scriptSource()->introducerFilename()) {
    361      str =
    362          NewStringCopyUTF8N(cx, JS::UTF8Chars(introducer, strlen(introducer)));
    363    } else {
    364      const char* filename = script->filename();
    365      str = NewStringCopyUTF8N(cx, JS::UTF8Chars(filename, strlen(filename)));
    366    }
    367    if (!str) {
    368      return false;
    369    }
    370    args.rval().setString(str);
    371  } else {
    372    args.rval().setNull();
    373  }
    374  return true;
    375 }
    376 
    377 bool DebuggerScript::CallData::getStartLine() {
    378  args.rval().setNumber(
    379      referent.get().match([](BaseScript*& s) { return s->lineno(); },
    380                           [](WasmInstanceObject*&) { return (uint32_t)1; }));
    381  return true;
    382 }
    383 
    384 bool DebuggerScript::CallData::getStartColumn() {
    385  JS::LimitedColumnNumberOneOrigin column = referent.get().match(
    386      [](BaseScript*& s) { return s->column(); },
    387      [](WasmInstanceObject*&) {
    388        return JS::LimitedColumnNumberOneOrigin(
    389            JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin);
    390      });
    391  args.rval().setNumber(column.oneOriginValue());
    392  return true;
    393 }
    394 
    395 struct DebuggerScript::GetLineCountMatcher {
    396  JSContext* cx_;
    397  double totalLines;
    398 
    399  explicit GetLineCountMatcher(JSContext* cx) : cx_(cx), totalLines(0.0) {}
    400  using ReturnType = bool;
    401 
    402  ReturnType match(Handle<BaseScript*> base) {
    403    RootedScript script(cx_, DelazifyScript(cx_, base));
    404    if (!script) {
    405      return false;
    406    }
    407    totalLines = double(GetScriptLineExtent(script));
    408    return true;
    409  }
    410  ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
    411    wasm::Instance& instance = instanceObj->instance();
    412    if (instance.debugEnabled()) {
    413      totalLines = double(instance.debug().bytecode().length());
    414    } else {
    415      totalLines = 0;
    416    }
    417    return true;
    418  }
    419 };
    420 
    421 bool DebuggerScript::CallData::getLineCount() {
    422  GetLineCountMatcher matcher(cx);
    423  if (!referent.match(matcher)) {
    424    return false;
    425  }
    426  args.rval().setNumber(matcher.totalLines);
    427  return true;
    428 }
    429 
    430 class DebuggerScript::GetSourceMatcher {
    431  JSContext* cx_;
    432  Debugger* dbg_;
    433 
    434 public:
    435  GetSourceMatcher(JSContext* cx, Debugger* dbg) : cx_(cx), dbg_(dbg) {}
    436 
    437  using ReturnType = DebuggerSource*;
    438 
    439  ReturnType match(Handle<BaseScript*> script) {
    440    Rooted<ScriptSourceObject*> source(cx_, script->sourceObject());
    441    return dbg_->wrapSource(cx_, source);
    442  }
    443  ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
    444    return dbg_->wrapWasmSource(cx_, wasmInstance);
    445  }
    446 };
    447 
    448 bool DebuggerScript::CallData::getSource() {
    449  Debugger* dbg = obj->owner();
    450 
    451  GetSourceMatcher matcher(cx, dbg);
    452  Rooted<DebuggerSource*> sourceObject(cx, referent.match(matcher));
    453  if (!sourceObject) {
    454    return false;
    455  }
    456 
    457  args.rval().setObject(*sourceObject);
    458  return true;
    459 }
    460 
    461 bool DebuggerScript::CallData::getSourceStart() {
    462  if (!ensureScriptMaybeLazy()) {
    463    return false;
    464  }
    465  args.rval().setNumber(uint32_t(obj->getReferentScript()->sourceStart()));
    466  return true;
    467 }
    468 
    469 bool DebuggerScript::CallData::getSourceLength() {
    470  if (!ensureScriptMaybeLazy()) {
    471    return false;
    472  }
    473  args.rval().setNumber(uint32_t(obj->getReferentScript()->sourceLength()));
    474  return true;
    475 }
    476 
    477 bool DebuggerScript::CallData::getMainOffset() {
    478  if (!ensureScript()) {
    479    return false;
    480  }
    481  args.rval().setNumber(uint32_t(script->mainOffset()));
    482  return true;
    483 }
    484 
    485 bool DebuggerScript::CallData::getGlobal() {
    486  if (!ensureScript()) {
    487    return false;
    488  }
    489  Debugger* dbg = obj->owner();
    490 
    491  RootedValue v(cx, ObjectValue(script->global()));
    492  if (!dbg->wrapDebuggeeValue(cx, &v)) {
    493    return false;
    494  }
    495  args.rval().set(v);
    496  return true;
    497 }
    498 
    499 bool DebuggerScript::CallData::getFormat() {
    500  args.rval().setString(referent.get().match(
    501      [this](BaseScript*&) { return cx->names().js.get(); },
    502      [this](WasmInstanceObject*&) { return cx->names().wasm.get(); }));
    503  return true;
    504 }
    505 
    506 static bool PushFunctionScript(JSContext* cx, Debugger* dbg, HandleFunction fun,
    507                               HandleObject array) {
    508  // Ignore asm.js natives.
    509  if (!IsInterpretedNonSelfHostedFunction(fun)) {
    510    return true;
    511  }
    512 
    513  Rooted<BaseScript*> script(cx, fun->baseScript());
    514  MOZ_ASSERT(script);
    515  if (!script) {
    516    // If the function doesn't have script, ignore it.
    517    return true;
    518  }
    519  RootedObject wrapped(cx, dbg->wrapScript(cx, script));
    520  if (!wrapped) {
    521    return false;
    522  }
    523 
    524  return NewbornArrayPush(cx, array, ObjectValue(*wrapped));
    525 }
    526 
    527 static bool PushInnerFunctions(JSContext* cx, Debugger* dbg, HandleObject array,
    528                               mozilla::Span<const JS::GCCellPtr> gcThings) {
    529  RootedFunction fun(cx);
    530 
    531  for (JS::GCCellPtr gcThing : gcThings) {
    532    if (!gcThing.is<JSObject>()) {
    533      continue;
    534    }
    535 
    536    JSObject* obj = &gcThing.as<JSObject>();
    537    if (obj->is<JSFunction>()) {
    538      fun = &obj->as<JSFunction>();
    539 
    540      // Ignore any delazification placeholder functions. These should not be
    541      // exposed to debugger in any way.
    542      if (fun->isGhost()) {
    543        continue;
    544      }
    545 
    546      if (!PushFunctionScript(cx, dbg, fun, array)) {
    547        return false;
    548      }
    549    }
    550  }
    551 
    552  return true;
    553 }
    554 
    555 bool DebuggerScript::CallData::getChildScripts() {
    556  if (!ensureScriptMaybeLazy()) {
    557    return false;
    558  }
    559  Debugger* dbg = obj->owner();
    560 
    561  RootedObject result(cx, NewDenseEmptyArray(cx));
    562  if (!result) {
    563    return false;
    564  }
    565 
    566  Rooted<BaseScript*> script(cx, obj->getReferent().as<BaseScript*>());
    567  if (!PushInnerFunctions(cx, dbg, result, script->gcthings())) {
    568    return false;
    569  }
    570 
    571  args.rval().setObject(*result);
    572  return true;
    573 }
    574 
    575 static bool ScriptOffset(JSContext* cx, const Value& v, size_t* offsetp) {
    576  double d;
    577  size_t off;
    578 
    579  bool ok = v.isNumber();
    580  if (ok) {
    581    d = v.toNumber();
    582    off = size_t(d);
    583  }
    584  if (!ok || off != d) {
    585    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    586                              JSMSG_DEBUG_BAD_OFFSET);
    587    return false;
    588  }
    589  *offsetp = off;
    590  return true;
    591 }
    592 
    593 static bool EnsureScriptOffsetIsValid(JSContext* cx, JSScript* script,
    594                                      size_t offset) {
    595  if (IsValidBytecodeOffset(cx, script, offset)) {
    596    return true;
    597  }
    598  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    599                            JSMSG_DEBUG_BAD_OFFSET);
    600  return false;
    601 }
    602 
    603 static bool IsGeneratorSlotInitialization(JSScript* script, size_t offset,
    604                                          JSContext* cx) {
    605  jsbytecode* pc = script->offsetToPC(offset);
    606  if (JSOp(*pc) != JSOp::SetAliasedVar) {
    607    return false;
    608  }
    609 
    610  PropertyName* name = EnvironmentCoordinateNameSlow(script, pc);
    611  return name == cx->names().dot_generator_;
    612 }
    613 
    614 static bool EnsureBreakpointIsAllowed(JSContext* cx, JSScript* script,
    615                                      size_t offset) {
    616  // Disallow breakpoint for `JSOp::SetAliasedVar` after `JSOp::Generator`.
    617  // Those 2 instructions are supposed to be atomic, and nothing should happen
    618  // in between them.
    619  //
    620  // Hitting a breakpoint there breaks the assumption around the existence of
    621  // the frame's `GeneratorInfo`.
    622  // (see `DebugAPI::slowPathOnNewGenerator` and `DebuggerFrame::create`)
    623  if (IsGeneratorSlotInitialization(script, offset, cx)) {
    624    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    625                              JSMSG_DEBUG_BREAKPOINT_NOT_ALLOWED);
    626    return false;
    627  }
    628 
    629  return true;
    630 }
    631 
    632 template <bool OnlyOffsets>
    633 class DebuggerScript::GetPossibleBreakpointsMatcher {
    634  JSContext* cx_;
    635  MutableHandleObject result_;
    636 
    637  Maybe<size_t> minOffset;
    638  Maybe<size_t> maxOffset;
    639 
    640  Maybe<uint32_t> minLine;
    641  JS::LimitedColumnNumberOneOrigin minColumn;
    642  Maybe<uint32_t> maxLine;
    643  JS::LimitedColumnNumberOneOrigin maxColumn;
    644 
    645  bool passesQuery(size_t offset, uint32_t lineno,
    646                   JS::LimitedColumnNumberOneOrigin colno) {
    647    // [minOffset, maxOffset) - Inclusive minimum and exclusive maximum.
    648    if ((minOffset && offset < *minOffset) ||
    649        (maxOffset && offset >= *maxOffset)) {
    650      return false;
    651    }
    652 
    653    if (minLine) {
    654      if (lineno < *minLine || (lineno == *minLine && colno < minColumn)) {
    655        return false;
    656      }
    657    }
    658 
    659    if (maxLine) {
    660      if (lineno > *maxLine || (lineno == *maxLine && colno >= maxColumn)) {
    661        return false;
    662      }
    663    }
    664 
    665    return true;
    666  }
    667 
    668  bool maybeAppendEntry(size_t offset, uint32_t lineno,
    669                        JS::LimitedColumnNumberOneOrigin colno,
    670                        bool isStepStart) {
    671    if (!passesQuery(offset, lineno, colno)) {
    672      return true;
    673    }
    674 
    675    if (OnlyOffsets) {
    676      if (!NewbornArrayPush(cx_, result_, NumberValue(offset))) {
    677        return false;
    678      }
    679 
    680      return true;
    681    }
    682 
    683    Rooted<PlainObject*> entry(cx_, NewPlainObject(cx_));
    684    if (!entry) {
    685      return false;
    686    }
    687 
    688    RootedValue value(cx_, NumberValue(offset));
    689    if (!DefineDataProperty(cx_, entry, cx_->names().offset, value)) {
    690      return false;
    691    }
    692 
    693    value = NumberValue(lineno);
    694    if (!DefineDataProperty(cx_, entry, cx_->names().lineNumber, value)) {
    695      return false;
    696    }
    697 
    698    value = NumberValue(colno.oneOriginValue());
    699    if (!DefineDataProperty(cx_, entry, cx_->names().columnNumber, value)) {
    700      return false;
    701    }
    702 
    703    value = BooleanValue(isStepStart);
    704    if (!DefineDataProperty(cx_, entry, cx_->names().isStepStart, value)) {
    705      return false;
    706    }
    707 
    708    if (!NewbornArrayPush(cx_, result_, ObjectValue(*entry))) {
    709      return false;
    710    }
    711    return true;
    712  }
    713 
    714  template <typename T>
    715  bool parseIntValueImpl(HandleValue value, T* result) {
    716    if (!value.isNumber()) {
    717      return false;
    718    }
    719 
    720    double doubleOffset = value.toNumber();
    721    if (doubleOffset < 0 || (unsigned int)doubleOffset != doubleOffset) {
    722      return false;
    723    }
    724 
    725    *result = doubleOffset;
    726    return true;
    727  }
    728 
    729  bool parseUint32Value(HandleValue value, uint32_t* result) {
    730    return parseIntValueImpl(value, result);
    731  }
    732  bool parseColumnValue(HandleValue value,
    733                        JS::LimitedColumnNumberOneOrigin* result) {
    734    uint32_t tmp;
    735    if (!parseIntValueImpl(value, &tmp)) {
    736      return false;
    737    }
    738    if (tmp == 0) {
    739      return false;
    740    }
    741    *result->addressOfValueForTranscode() = tmp;
    742    if (!result->valid()) {
    743      return false;
    744    }
    745    return true;
    746  }
    747  bool parseSizeTValue(HandleValue value, size_t* result) {
    748    return parseIntValueImpl(value, result);
    749  }
    750 
    751  template <typename T>
    752  bool parseIntValueMaybeImpl(HandleValue value, Maybe<T>* result) {
    753    T result_;
    754    if (!parseIntValueImpl(value, &result_)) {
    755      return false;
    756    }
    757 
    758    *result = Some(result_);
    759    return true;
    760  }
    761 
    762  bool parseUint32Value(HandleValue value, Maybe<uint32_t>* result) {
    763    return parseIntValueMaybeImpl(value, result);
    764  }
    765  bool parseSizeTValue(HandleValue value, Maybe<size_t>* result) {
    766    return parseIntValueMaybeImpl(value, result);
    767  }
    768 
    769 public:
    770  explicit GetPossibleBreakpointsMatcher(JSContext* cx,
    771                                         MutableHandleObject result)
    772      : cx_(cx), result_(result) {}
    773 
    774  bool parseQuery(HandleObject query) {
    775    RootedValue lineValue(cx_);
    776    if (!GetProperty(cx_, query, query, cx_->names().line, &lineValue)) {
    777      return false;
    778    }
    779 
    780    RootedValue minLineValue(cx_);
    781    if (!GetProperty(cx_, query, query, cx_->names().minLine, &minLineValue)) {
    782      return false;
    783    }
    784 
    785    RootedValue minColumnValue(cx_);
    786    if (!GetProperty(cx_, query, query, cx_->names().minColumn,
    787                     &minColumnValue)) {
    788      return false;
    789    }
    790 
    791    RootedValue minOffsetValue(cx_);
    792    if (!GetProperty(cx_, query, query, cx_->names().minOffset,
    793                     &minOffsetValue)) {
    794      return false;
    795    }
    796 
    797    RootedValue maxLineValue(cx_);
    798    if (!GetProperty(cx_, query, query, cx_->names().maxLine, &maxLineValue)) {
    799      return false;
    800    }
    801 
    802    RootedValue maxColumnValue(cx_);
    803    if (!GetProperty(cx_, query, query, cx_->names().maxColumn,
    804                     &maxColumnValue)) {
    805      return false;
    806    }
    807 
    808    RootedValue maxOffsetValue(cx_);
    809    if (!GetProperty(cx_, query, query, cx_->names().maxOffset,
    810                     &maxOffsetValue)) {
    811      return false;
    812    }
    813 
    814    if (!minOffsetValue.isUndefined()) {
    815      if (!parseSizeTValue(minOffsetValue, &minOffset)) {
    816        JS_ReportErrorNumberASCII(
    817            cx_, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
    818            "getPossibleBreakpoints' 'minOffset'", "not an integer");
    819        return false;
    820      }
    821    }
    822    if (!maxOffsetValue.isUndefined()) {
    823      if (!parseSizeTValue(maxOffsetValue, &maxOffset)) {
    824        JS_ReportErrorNumberASCII(
    825            cx_, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
    826            "getPossibleBreakpoints' 'maxOffset'", "not an integer");
    827        return false;
    828      }
    829    }
    830 
    831    if (!lineValue.isUndefined()) {
    832      if (!minLineValue.isUndefined() || !maxLineValue.isUndefined()) {
    833        JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
    834                                  JSMSG_UNEXPECTED_TYPE,
    835                                  "getPossibleBreakpoints' 'line'",
    836                                  "not allowed alongside 'minLine'/'maxLine'");
    837        return false;
    838      }
    839 
    840      uint32_t line;
    841      if (!parseUint32Value(lineValue, &line)) {
    842        JS_ReportErrorNumberASCII(
    843            cx_, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
    844            "getPossibleBreakpoints' 'line'", "not an integer");
    845        return false;
    846      }
    847 
    848      // If no end column is given, we use the default of 0 and wrap to
    849      // the next line.
    850      minLine = Some(line);
    851      maxLine = Some(line + (maxColumnValue.isUndefined() ? 1 : 0));
    852    }
    853 
    854    if (!minLineValue.isUndefined()) {
    855      if (!parseUint32Value(minLineValue, &minLine)) {
    856        JS_ReportErrorNumberASCII(
    857            cx_, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
    858            "getPossibleBreakpoints' 'minLine'", "not an integer");
    859        return false;
    860      }
    861    }
    862 
    863    if (!minColumnValue.isUndefined()) {
    864      if (!minLine) {
    865        JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
    866                                  JSMSG_UNEXPECTED_TYPE,
    867                                  "getPossibleBreakpoints' 'minColumn'",
    868                                  "not allowed without 'line' or 'minLine'");
    869        return false;
    870      }
    871 
    872      if (!parseColumnValue(minColumnValue, &minColumn)) {
    873        JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
    874                                  JSMSG_UNEXPECTED_TYPE,
    875                                  "getPossibleBreakpoints' 'minColumn'",
    876                                  "not a positive integer in valid range");
    877        return false;
    878      }
    879    }
    880 
    881    if (!maxLineValue.isUndefined()) {
    882      if (!parseUint32Value(maxLineValue, &maxLine)) {
    883        JS_ReportErrorNumberASCII(
    884            cx_, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
    885            "getPossibleBreakpoints' 'maxLine'", "not an integer");
    886        return false;
    887      }
    888    }
    889 
    890    if (!maxColumnValue.isUndefined()) {
    891      if (!maxLine) {
    892        JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
    893                                  JSMSG_UNEXPECTED_TYPE,
    894                                  "getPossibleBreakpoints' 'maxColumn'",
    895                                  "not allowed without 'line' or 'maxLine'");
    896        return false;
    897      }
    898 
    899      if (!parseColumnValue(maxColumnValue, &maxColumn)) {
    900        JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
    901                                  JSMSG_UNEXPECTED_TYPE,
    902                                  "getPossibleBreakpoints' 'maxColumn'",
    903                                  "not a positive integer in valid range");
    904        return false;
    905      }
    906    }
    907 
    908    return true;
    909  }
    910 
    911  using ReturnType = bool;
    912  ReturnType match(Handle<BaseScript*> base) {
    913    RootedScript script(cx_, DelazifyScript(cx_, base));
    914    if (!script) {
    915      return false;
    916    }
    917 
    918    // Second pass: build the result array.
    919    result_.set(NewDenseEmptyArray(cx_));
    920    if (!result_) {
    921      return false;
    922    }
    923 
    924    for (BytecodeRangeWithPosition r(cx_, script, SkipPrologueOps::Yes);
    925         !r.empty(); r.popFront()) {
    926      if (!r.frontIsBreakablePoint()) {
    927        continue;
    928      }
    929 
    930      size_t offset = r.frontOffset();
    931      uint32_t lineno = r.frontLineNumber();
    932      JS::LimitedColumnNumberOneOrigin colno = r.frontColumnNumber();
    933 
    934      if (!maybeAppendEntry(offset, lineno, colno,
    935                            r.frontIsBreakableStepPoint())) {
    936        return false;
    937      }
    938    }
    939 
    940    return true;
    941  }
    942  ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
    943    wasm::Instance& instance = instanceObj->instance();
    944 
    945    Vector<wasm::ExprLoc> offsets(cx_);
    946    if (instance.debugEnabled() &&
    947        !instance.debug().getAllColumnOffsets(&offsets)) {
    948      return false;
    949    }
    950 
    951    result_.set(NewDenseEmptyArray(cx_));
    952    if (!result_) {
    953      return false;
    954    }
    955 
    956    for (uint32_t i = 0; i < offsets.length(); i++) {
    957      uint32_t lineno = offsets[i].lineno;
    958      JS::LimitedColumnNumberOneOrigin column(offsets[i].column);
    959      size_t offset = offsets[i].offset;
    960      if (!maybeAppendEntry(offset, lineno, column, true)) {
    961        return false;
    962      }
    963    }
    964    return true;
    965  }
    966 };
    967 
    968 bool DebuggerScript::CallData::getPossibleBreakpoints() {
    969  RootedObject result(cx);
    970  GetPossibleBreakpointsMatcher<false> matcher(cx, &result);
    971  if (args.length() >= 1 && !args[0].isUndefined()) {
    972    RootedObject queryObject(cx, RequireObject(cx, args[0]));
    973    if (!queryObject || !matcher.parseQuery(queryObject)) {
    974      return false;
    975    }
    976  }
    977  if (!referent.match(matcher)) {
    978    return false;
    979  }
    980 
    981  args.rval().setObject(*result);
    982  return true;
    983 }
    984 
    985 bool DebuggerScript::CallData::getPossibleBreakpointOffsets() {
    986  RootedObject result(cx);
    987  GetPossibleBreakpointsMatcher<true> matcher(cx, &result);
    988  if (args.length() >= 1 && !args[0].isUndefined()) {
    989    RootedObject queryObject(cx, RequireObject(cx, args[0]));
    990    if (!queryObject || !matcher.parseQuery(queryObject)) {
    991      return false;
    992    }
    993  }
    994  if (!referent.match(matcher)) {
    995    return false;
    996  }
    997 
    998  args.rval().setObject(*result);
    999  return true;
   1000 }
   1001 
   1002 class DebuggerScript::GetOffsetMetadataMatcher {
   1003  JSContext* cx_;
   1004  size_t offset_;
   1005  MutableHandle<PlainObject*> result_;
   1006 
   1007 public:
   1008  explicit GetOffsetMetadataMatcher(JSContext* cx, size_t offset,
   1009                                    MutableHandle<PlainObject*> result)
   1010      : cx_(cx), offset_(offset), result_(result) {}
   1011  using ReturnType = bool;
   1012  ReturnType match(Handle<BaseScript*> base) {
   1013    RootedScript script(cx_, DelazifyScript(cx_, base));
   1014    if (!script) {
   1015      return false;
   1016    }
   1017 
   1018    if (!EnsureScriptOffsetIsValid(cx_, script, offset_)) {
   1019      return false;
   1020    }
   1021 
   1022    result_.set(NewPlainObject(cx_));
   1023    if (!result_) {
   1024      return false;
   1025    }
   1026 
   1027    // Use SkipPrologueOps::No to ensure we return isBreakpoint = false and
   1028    // isStepStart = false for prologue ops, instead of the metadata for the
   1029    // first 'main' op.
   1030    BytecodeRangeWithPosition r(cx_, script, SkipPrologueOps::No);
   1031    while (!r.empty() && r.frontOffset() < offset_) {
   1032      r.popFront();
   1033    }
   1034    MOZ_ASSERT(r.frontOffset() == offset_);
   1035 
   1036    RootedValue value(cx_, NumberValue(r.frontLineNumber()));
   1037    if (!DefineDataProperty(cx_, result_, cx_->names().lineNumber, value)) {
   1038      return false;
   1039    }
   1040 
   1041    value = NumberValue(r.frontColumnNumber().oneOriginValue());
   1042    if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value)) {
   1043      return false;
   1044    }
   1045 
   1046    value = BooleanValue(r.frontIsBreakablePoint());
   1047    if (!DefineDataProperty(cx_, result_, cx_->names().isBreakpoint, value)) {
   1048      return false;
   1049    }
   1050 
   1051    value = BooleanValue(r.frontIsBreakableStepPoint());
   1052    if (!DefineDataProperty(cx_, result_, cx_->names().isStepStart, value)) {
   1053      return false;
   1054    }
   1055 
   1056    return true;
   1057  }
   1058  ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
   1059    wasm::Instance& instance = instanceObj->instance();
   1060    if (!instance.debugEnabled()) {
   1061      JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
   1062                                JSMSG_DEBUG_BAD_OFFSET);
   1063      return false;
   1064    }
   1065 
   1066    uint32_t lineno;
   1067    JS::LimitedColumnNumberOneOrigin column;
   1068    if (!instance.debug().getOffsetLocation(offset_, &lineno, &column)) {
   1069      JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
   1070                                JSMSG_DEBUG_BAD_OFFSET);
   1071      return false;
   1072    }
   1073 
   1074    result_.set(NewPlainObject(cx_));
   1075    if (!result_) {
   1076      return false;
   1077    }
   1078 
   1079    RootedValue value(cx_, NumberValue(lineno));
   1080    if (!DefineDataProperty(cx_, result_, cx_->names().lineNumber, value)) {
   1081      return false;
   1082    }
   1083 
   1084    value = NumberValue(column.oneOriginValue());
   1085    if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value)) {
   1086      return false;
   1087    }
   1088 
   1089    value.setBoolean(true);
   1090    if (!DefineDataProperty(cx_, result_, cx_->names().isBreakpoint, value)) {
   1091      return false;
   1092    }
   1093 
   1094    value.setBoolean(true);
   1095    if (!DefineDataProperty(cx_, result_, cx_->names().isStepStart, value)) {
   1096      return false;
   1097    }
   1098 
   1099    return true;
   1100  }
   1101 };
   1102 
   1103 bool DebuggerScript::CallData::getOffsetMetadata() {
   1104  if (!args.requireAtLeast(cx, "Debugger.Script.getOffsetMetadata", 1)) {
   1105    return false;
   1106  }
   1107  size_t offset;
   1108  if (!ScriptOffset(cx, args[0], &offset)) {
   1109    return false;
   1110  }
   1111 
   1112  Rooted<PlainObject*> result(cx);
   1113  GetOffsetMetadataMatcher matcher(cx, offset, &result);
   1114  if (!referent.match(matcher)) {
   1115    return false;
   1116  }
   1117 
   1118  args.rval().setObject(*result);
   1119  return true;
   1120 }
   1121 
   1122 namespace {
   1123 
   1124 /*
   1125 * FlowGraphSummary::populate(cx, script) computes a summary of script's
   1126 * control flow graph used by DebuggerScript_{getAllOffsets,getLineOffsets}.
   1127 *
   1128 * An instruction on a given line is an entry point for that line if it can be
   1129 * reached from (an instruction on) a different line. We distinguish between the
   1130 * following cases:
   1131 *   - hasNoEdges:
   1132 *       The instruction cannot be reached, so the instruction is not an entry
   1133 *       point for the line it is on.
   1134 *   - hasSingleEdge:
   1135 *       The instruction can be reached from a single line. If this line is
   1136 *       different from the line the instruction is on, the instruction is an
   1137 *       entry point for that line.
   1138 *
   1139 * Similarly, an instruction on a given position (line/column pair) is an
   1140 * entry point for that position if it can be reached from (an instruction on) a
   1141 * different position. Again, we distinguish between the following cases:
   1142 *   - hasNoEdges:
   1143 *       The instruction cannot be reached, so the instruction is not an entry
   1144 *       point for the position it is on.
   1145 *   - hasSingleEdge:
   1146 *       The instruction can be reached from a single position. If this line is
   1147 *       different from the position the instruction is on, the instruction is
   1148 *       an entry point for that position.
   1149 */
   1150 class FlowGraphSummary {
   1151 public:
   1152  class Entry {
   1153   public:
   1154    static constexpr uint32_t Line_HasNoEdge = UINT32_MAX;
   1155    static constexpr uint32_t Column_HasMultipleEdge = UINT32_MAX;
   1156 
   1157    // NOTE: column can be Column_HasMultipleEdge.
   1158    static Entry createWithSingleEdgeOrMultipleEdge(uint32_t lineno,
   1159                                                    uint32_t column) {
   1160      return Entry(lineno, column);
   1161    }
   1162 
   1163    static Entry createWithMultipleEdgesFromSingleLine(uint32_t lineno) {
   1164      return Entry(lineno, Column_HasMultipleEdge);
   1165    }
   1166 
   1167    static Entry createWithMultipleEdgesFromMultipleLines() {
   1168      return Entry(Line_HasNoEdge, Column_HasMultipleEdge);
   1169    }
   1170 
   1171    Entry() : lineno_(Line_HasNoEdge), column_(1) {}
   1172 
   1173    bool hasNoEdges() const {
   1174      return lineno_ == Line_HasNoEdge && column_ != Column_HasMultipleEdge;
   1175    }
   1176 
   1177    bool hasSingleEdge() const {
   1178      return lineno_ != Line_HasNoEdge && column_ != Column_HasMultipleEdge;
   1179    }
   1180 
   1181    uint32_t lineno() const { return lineno_; }
   1182 
   1183    // Returns 1-origin column number or the sentinel value
   1184    // Column_HasMultipleEdge.
   1185    uint32_t columnOrSentinel() const { return column_; }
   1186 
   1187    JS::LimitedColumnNumberOneOrigin column() const {
   1188      MOZ_ASSERT(column_ != Column_HasMultipleEdge);
   1189      return JS::LimitedColumnNumberOneOrigin(column_);
   1190    }
   1191 
   1192   private:
   1193    Entry(uint32_t lineno, uint32_t column)
   1194        : lineno_(lineno), column_(column) {}
   1195 
   1196    // Line number (1-origin).
   1197    // Line_HasNoEdge for no edge.
   1198    uint32_t lineno_;
   1199 
   1200    // Column number in UTF-16 code units (1-origin).
   1201    // Column_HasMultipleEdge for multiple edge.
   1202    uint32_t column_;
   1203  };
   1204 
   1205  explicit FlowGraphSummary(JSContext* cx) : entries_(cx) {}
   1206 
   1207  Entry& operator[](size_t index) { return entries_[index]; }
   1208 
   1209  bool populate(JSContext* cx, JSScript* script) {
   1210    if (!entries_.growBy(script->length())) {
   1211      return false;
   1212    }
   1213    unsigned mainOffset = script->pcToOffset(script->main());
   1214    entries_[mainOffset] = Entry::createWithMultipleEdgesFromMultipleLines();
   1215 
   1216    // The following code uses uint32_t for column numbers.
   1217    // The value is either 1-origin column number,
   1218    // or Entry::Column_HasMultipleEdge.
   1219 
   1220    uint32_t prevLineno = script->lineno();
   1221    uint32_t prevColumn = 1;
   1222    JSOp prevOp = JSOp::Nop;
   1223    for (BytecodeRangeWithPosition r(cx, script, SkipPrologueOps::Yes);
   1224         !r.empty(); r.popFront()) {
   1225      uint32_t lineno = prevLineno;
   1226      uint32_t column = prevColumn;
   1227      JSOp op = r.frontOpcode();
   1228 
   1229      if (BytecodeFallsThrough(prevOp)) {
   1230        addEdge(prevLineno, prevColumn, r.frontOffset());
   1231      }
   1232 
   1233      // If we visit the branch target before we visit the
   1234      // branch op itself, just reuse the previous location.
   1235      // This is reasonable for the time being because this
   1236      // situation can currently only arise from loop heads,
   1237      // where this assumption holds.
   1238      if (BytecodeIsJumpTarget(op) && !entries_[r.frontOffset()].hasNoEdges()) {
   1239        lineno = entries_[r.frontOffset()].lineno();
   1240        column = entries_[r.frontOffset()].columnOrSentinel();
   1241      }
   1242 
   1243      if (r.frontIsEntryPoint()) {
   1244        lineno = r.frontLineNumber();
   1245        column = r.frontColumnNumber().oneOriginValue();
   1246      }
   1247 
   1248      if (IsJumpOpcode(op)) {
   1249        addEdge(lineno, column, r.frontOffset() + GET_JUMP_OFFSET(r.frontPC()));
   1250      } else if (op == JSOp::TableSwitch) {
   1251        jsbytecode* const switchPC = r.frontPC();
   1252        jsbytecode* pc = switchPC;
   1253        size_t offset = r.frontOffset();
   1254        ptrdiff_t step = JUMP_OFFSET_LEN;
   1255        size_t defaultOffset = offset + GET_JUMP_OFFSET(pc);
   1256        pc += step;
   1257        addEdge(lineno, column, defaultOffset);
   1258 
   1259        int32_t low = GET_JUMP_OFFSET(pc);
   1260        pc += JUMP_OFFSET_LEN;
   1261        int ncases = GET_JUMP_OFFSET(pc) - low + 1;
   1262        pc += JUMP_OFFSET_LEN;
   1263 
   1264        for (int i = 0; i < ncases; i++) {
   1265          size_t target = script->tableSwitchCaseOffset(switchPC, i);
   1266          addEdge(lineno, column, target);
   1267        }
   1268      } else if (op == JSOp::Try) {
   1269        // As there is no literal incoming edge into the catch block, we
   1270        // make a fake one by copying the JSOp::Try location, as-if this
   1271        // was an incoming edge of the catch block. This is needed
   1272        // because we only report offsets of entry points which have
   1273        // valid incoming edges.
   1274        for (const TryNote& tn : script->trynotes()) {
   1275          if (tn.start == r.frontOffset() + JSOpLength_Try) {
   1276            uint32_t catchOffset = tn.start + tn.length;
   1277            if (tn.kind() == TryNoteKind::Catch ||
   1278                tn.kind() == TryNoteKind::Finally) {
   1279              addEdge(lineno, column, catchOffset);
   1280            }
   1281          }
   1282        }
   1283      }
   1284 
   1285      prevLineno = lineno;
   1286      prevColumn = column;
   1287      prevOp = op;
   1288    }
   1289 
   1290    return true;
   1291  }
   1292 
   1293 private:
   1294  // sourceColumn is either 1-origin column number,
   1295  // or Entry::Column_HasMultipleEdge.
   1296  void addEdge(uint32_t sourceLineno, uint32_t sourceColumn,
   1297               size_t targetOffset) {
   1298    if (entries_[targetOffset].hasNoEdges()) {
   1299      entries_[targetOffset] =
   1300          Entry::createWithSingleEdgeOrMultipleEdge(sourceLineno, sourceColumn);
   1301    } else if (entries_[targetOffset].lineno() != sourceLineno) {
   1302      entries_[targetOffset] =
   1303          Entry::createWithMultipleEdgesFromMultipleLines();
   1304    } else if (entries_[targetOffset].columnOrSentinel() != sourceColumn) {
   1305      entries_[targetOffset] =
   1306          Entry::createWithMultipleEdgesFromSingleLine(sourceLineno);
   1307    }
   1308  }
   1309 
   1310  Vector<Entry> entries_;
   1311 };
   1312 
   1313 } /* anonymous namespace */
   1314 
   1315 class DebuggerScript::GetOffsetLocationMatcher {
   1316  JSContext* cx_;
   1317  size_t offset_;
   1318  MutableHandle<PlainObject*> result_;
   1319 
   1320 public:
   1321  explicit GetOffsetLocationMatcher(JSContext* cx, size_t offset,
   1322                                    MutableHandle<PlainObject*> result)
   1323      : cx_(cx), offset_(offset), result_(result) {}
   1324  using ReturnType = bool;
   1325  ReturnType match(Handle<BaseScript*> base) {
   1326    RootedScript script(cx_, DelazifyScript(cx_, base));
   1327    if (!script) {
   1328      return false;
   1329    }
   1330 
   1331    if (!EnsureScriptOffsetIsValid(cx_, script, offset_)) {
   1332      return false;
   1333    }
   1334 
   1335    FlowGraphSummary flowData(cx_);
   1336    if (!flowData.populate(cx_, script)) {
   1337      return false;
   1338    }
   1339 
   1340    result_.set(NewPlainObject(cx_));
   1341    if (!result_) {
   1342      return false;
   1343    }
   1344 
   1345    // Use SkipPrologueOps::No to ensure we return isEntryPoint = false for
   1346    // prologue ops, instead of the value for the first 'main' op.
   1347    BytecodeRangeWithPosition r(cx_, script, SkipPrologueOps::No);
   1348    while (!r.empty() && r.frontOffset() < offset_) {
   1349      r.popFront();
   1350    }
   1351    MOZ_ASSERT(r.frontOffset() == offset_);
   1352 
   1353    bool isEntryPoint = r.frontIsEntryPoint();
   1354 
   1355    // Line numbers are only correctly defined on entry points. Thus looks
   1356    // either for the next valid offset in the flowData, being the last entry
   1357    // point flowing into the current offset, or for the next valid entry point.
   1358    while (!r.frontIsEntryPoint() &&
   1359           !flowData[r.frontOffset()].hasSingleEdge()) {
   1360      r.popFront();
   1361      MOZ_ASSERT(!r.empty());
   1362    }
   1363 
   1364    // If this is an entry point, take the line number associated with the entry
   1365    // point, otherwise settle on the next instruction and take the incoming
   1366    // edge position.
   1367    uint32_t lineno;
   1368    JS::LimitedColumnNumberOneOrigin column;
   1369    if (r.frontIsEntryPoint()) {
   1370      lineno = r.frontLineNumber();
   1371      column = r.frontColumnNumber();
   1372    } else {
   1373      MOZ_ASSERT(flowData[r.frontOffset()].hasSingleEdge());
   1374      lineno = flowData[r.frontOffset()].lineno();
   1375      column = flowData[r.frontOffset()].column();
   1376    }
   1377 
   1378    RootedValue value(cx_, NumberValue(lineno));
   1379    if (!DefineDataProperty(cx_, result_, cx_->names().lineNumber, value)) {
   1380      return false;
   1381    }
   1382 
   1383    value = NumberValue(column.oneOriginValue());
   1384    if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value)) {
   1385      return false;
   1386    }
   1387 
   1388    // The same entry point test that is used by getAllColumnOffsets.
   1389    isEntryPoint = (isEntryPoint && !flowData[offset_].hasNoEdges() &&
   1390                    (flowData[offset_].lineno() != r.frontLineNumber() ||
   1391                     flowData[offset_].columnOrSentinel() !=
   1392                         r.frontColumnNumber().oneOriginValue()));
   1393    value.setBoolean(isEntryPoint);
   1394    if (!DefineDataProperty(cx_, result_, cx_->names().isEntryPoint, value)) {
   1395      return false;
   1396    }
   1397 
   1398    return true;
   1399  }
   1400  ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
   1401    wasm::Instance& instance = instanceObj->instance();
   1402    if (!instance.debugEnabled()) {
   1403      JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
   1404                                JSMSG_DEBUG_BAD_OFFSET);
   1405      return false;
   1406    }
   1407 
   1408    uint32_t lineno;
   1409    JS::LimitedColumnNumberOneOrigin column;
   1410    if (!instance.debug().getOffsetLocation(offset_, &lineno, &column)) {
   1411      JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
   1412                                JSMSG_DEBUG_BAD_OFFSET);
   1413      return false;
   1414    }
   1415 
   1416    result_.set(NewPlainObject(cx_));
   1417    if (!result_) {
   1418      return false;
   1419    }
   1420 
   1421    RootedValue value(cx_, NumberValue(lineno));
   1422    if (!DefineDataProperty(cx_, result_, cx_->names().lineNumber, value)) {
   1423      return false;
   1424    }
   1425 
   1426    value = NumberValue(column.oneOriginValue());
   1427    if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value)) {
   1428      return false;
   1429    }
   1430 
   1431    value.setBoolean(true);
   1432    if (!DefineDataProperty(cx_, result_, cx_->names().isEntryPoint, value)) {
   1433      return false;
   1434    }
   1435 
   1436    return true;
   1437  }
   1438 };
   1439 
   1440 bool DebuggerScript::CallData::getOffsetLocation() {
   1441  if (!args.requireAtLeast(cx, "Debugger.Script.getOffsetLocation", 1)) {
   1442    return false;
   1443  }
   1444  size_t offset;
   1445  if (!ScriptOffset(cx, args[0], &offset)) {
   1446    return false;
   1447  }
   1448 
   1449  Rooted<PlainObject*> result(cx);
   1450  GetOffsetLocationMatcher matcher(cx, offset, &result);
   1451  if (!referent.match(matcher)) {
   1452    return false;
   1453  }
   1454 
   1455  args.rval().setObject(*result);
   1456  return true;
   1457 }
   1458 
   1459 // Return whether an opcode is considered effectful: it can have direct side
   1460 // effects that can be observed outside of the current frame. Opcodes are not
   1461 // effectful if they only modify the current frame's state, modify objects
   1462 // created by the current frame, or can potentially call other scripts or
   1463 // natives which could have side effects.
   1464 static bool BytecodeIsEffectful(JSScript* script, size_t offset) {
   1465  jsbytecode* pc = script->offsetToPC(offset);
   1466  JSOp op = JSOp(*pc);
   1467  switch (op) {
   1468    case JSOp::SetProp:
   1469    case JSOp::StrictSetProp:
   1470    case JSOp::SetPropSuper:
   1471    case JSOp::StrictSetPropSuper:
   1472    case JSOp::SetElem:
   1473    case JSOp::StrictSetElem:
   1474    case JSOp::SetElemSuper:
   1475    case JSOp::StrictSetElemSuper:
   1476    case JSOp::SetName:
   1477    case JSOp::StrictSetName:
   1478    case JSOp::SetGName:
   1479    case JSOp::StrictSetGName:
   1480    case JSOp::DelProp:
   1481    case JSOp::StrictDelProp:
   1482    case JSOp::DelElem:
   1483    case JSOp::StrictDelElem:
   1484    case JSOp::DelName:
   1485    case JSOp::SetAliasedVar:
   1486    case JSOp::InitHomeObject:
   1487    case JSOp::SetIntrinsic:
   1488    case JSOp::InitGLexical:
   1489    case JSOp::GlobalOrEvalDeclInstantiation:
   1490    case JSOp::SetFunName:
   1491    case JSOp::MutateProto:
   1492    case JSOp::DynamicImport:
   1493    case JSOp::InitialYield:
   1494    case JSOp::Yield:
   1495    case JSOp::Await:
   1496    case JSOp::CanSkipAwait:
   1497 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
   1498    case JSOp::AddDisposable:
   1499 #endif
   1500      return true;
   1501 
   1502    case JSOp::Nop:
   1503    case JSOp::NopDestructuring:
   1504    case JSOp::NopIsAssignOp:
   1505    case JSOp::TryDestructuring:
   1506    case JSOp::Lineno:
   1507    case JSOp::JumpTarget:
   1508    case JSOp::Undefined:
   1509    case JSOp::JumpIfTrue:
   1510    case JSOp::JumpIfFalse:
   1511    case JSOp::Return:
   1512    case JSOp::RetRval:
   1513    case JSOp::And:
   1514    case JSOp::Or:
   1515    case JSOp::Coalesce:
   1516    case JSOp::Try:
   1517    case JSOp::Throw:
   1518    case JSOp::ThrowWithStack:
   1519 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
   1520    case JSOp::TakeDisposeCapability:
   1521    case JSOp::CreateSuppressedError:
   1522 #endif
   1523    case JSOp::Goto:
   1524    case JSOp::TableSwitch:
   1525    case JSOp::Case:
   1526    case JSOp::Default:
   1527    case JSOp::BitNot:
   1528    case JSOp::BitAnd:
   1529    case JSOp::BitOr:
   1530    case JSOp::BitXor:
   1531    case JSOp::Lsh:
   1532    case JSOp::Rsh:
   1533    case JSOp::Ursh:
   1534    case JSOp::Add:
   1535    case JSOp::Sub:
   1536    case JSOp::Mul:
   1537    case JSOp::Div:
   1538    case JSOp::Mod:
   1539    case JSOp::Pow:
   1540    case JSOp::Pos:
   1541    case JSOp::ToNumeric:
   1542    case JSOp::Neg:
   1543    case JSOp::Inc:
   1544    case JSOp::Dec:
   1545    case JSOp::ToString:
   1546    case JSOp::Eq:
   1547    case JSOp::Ne:
   1548    case JSOp::StrictEq:
   1549    case JSOp::StrictNe:
   1550    case JSOp::StrictConstantEq:
   1551    case JSOp::StrictConstantNe:
   1552    case JSOp::Lt:
   1553    case JSOp::Le:
   1554    case JSOp::Gt:
   1555    case JSOp::Ge:
   1556    case JSOp::Double:
   1557    case JSOp::BigInt:
   1558    case JSOp::String:
   1559    case JSOp::Symbol:
   1560    case JSOp::Zero:
   1561    case JSOp::One:
   1562    case JSOp::Null:
   1563    case JSOp::Void:
   1564    case JSOp::Hole:
   1565    case JSOp::False:
   1566    case JSOp::True:
   1567    case JSOp::Arguments:
   1568    case JSOp::Rest:
   1569    case JSOp::GetArg:
   1570    case JSOp::GetFrameArg:
   1571    case JSOp::SetArg:
   1572    case JSOp::GetLocal:
   1573    case JSOp::SetLocal:
   1574    case JSOp::GetActualArg:
   1575    case JSOp::ArgumentsLength:
   1576    case JSOp::ThrowSetConst:
   1577    case JSOp::CheckLexical:
   1578    case JSOp::CheckAliasedLexical:
   1579    case JSOp::InitLexical:
   1580    case JSOp::Uninitialized:
   1581    case JSOp::Pop:
   1582    case JSOp::PopN:
   1583    case JSOp::DupAt:
   1584    case JSOp::NewArray:
   1585    case JSOp::NewInit:
   1586    case JSOp::NewObject:
   1587    case JSOp::InitElem:
   1588    case JSOp::InitHiddenElem:
   1589    case JSOp::InitLockedElem:
   1590    case JSOp::InitElemInc:
   1591    case JSOp::InitElemArray:
   1592    case JSOp::InitProp:
   1593    case JSOp::InitLockedProp:
   1594    case JSOp::InitHiddenProp:
   1595    case JSOp::InitPropGetter:
   1596    case JSOp::InitHiddenPropGetter:
   1597    case JSOp::InitPropSetter:
   1598    case JSOp::InitHiddenPropSetter:
   1599    case JSOp::InitElemGetter:
   1600    case JSOp::InitHiddenElemGetter:
   1601    case JSOp::InitElemSetter:
   1602    case JSOp::InitHiddenElemSetter:
   1603    case JSOp::SpreadCall:
   1604    case JSOp::Call:
   1605    case JSOp::CallContent:
   1606    case JSOp::CallIgnoresRv:
   1607    case JSOp::CallIter:
   1608    case JSOp::CallContentIter:
   1609    case JSOp::New:
   1610    case JSOp::NewContent:
   1611    case JSOp::Eval:
   1612    case JSOp::StrictEval:
   1613    case JSOp::Int8:
   1614    case JSOp::Uint16:
   1615    case JSOp::ResumeKind:
   1616    case JSOp::GetGName:
   1617    case JSOp::GetName:
   1618    case JSOp::GetIntrinsic:
   1619    case JSOp::GetImport:
   1620    case JSOp::BindName:
   1621    case JSOp::BindUnqualifiedName:
   1622    case JSOp::BindUnqualifiedGName:
   1623    case JSOp::BindVar:
   1624    case JSOp::Dup:
   1625    case JSOp::Dup2:
   1626    case JSOp::Swap:
   1627    case JSOp::Pick:
   1628    case JSOp::Unpick:
   1629    case JSOp::GetAliasedDebugVar:
   1630    case JSOp::GetAliasedVar:
   1631    case JSOp::Uint24:
   1632    case JSOp::Int32:
   1633    case JSOp::LoopHead:
   1634    case JSOp::GetElem:
   1635    case JSOp::Not:
   1636    case JSOp::FunctionThis:
   1637    case JSOp::GlobalThis:
   1638    case JSOp::NonSyntacticGlobalThis:
   1639    case JSOp::Callee:
   1640    case JSOp::EnvCallee:
   1641    case JSOp::SuperBase:
   1642    case JSOp::GetPropSuper:
   1643    case JSOp::GetElemSuper:
   1644    case JSOp::GetProp:
   1645    case JSOp::RegExp:
   1646    case JSOp::CallSiteObj:
   1647    case JSOp::Object:
   1648    case JSOp::Typeof:
   1649    case JSOp::TypeofExpr:
   1650    case JSOp::TypeofEq:
   1651    case JSOp::ToAsyncIter:
   1652    case JSOp::ToPropertyKey:
   1653    case JSOp::Lambda:
   1654    case JSOp::PushLexicalEnv:
   1655    case JSOp::PopLexicalEnv:
   1656    case JSOp::FreshenLexicalEnv:
   1657    case JSOp::RecreateLexicalEnv:
   1658    case JSOp::PushClassBodyEnv:
   1659    case JSOp::Iter:
   1660    case JSOp::MoreIter:
   1661    case JSOp::IsNoIter:
   1662    case JSOp::EndIter:
   1663    case JSOp::CloseIter:
   1664    case JSOp::OptimizeGetIterator:
   1665    case JSOp::IsNullOrUndefined:
   1666    case JSOp::In:
   1667    case JSOp::HasOwn:
   1668    case JSOp::CheckPrivateField:
   1669    case JSOp::NewPrivateName:
   1670    case JSOp::SetRval:
   1671    case JSOp::Instanceof:
   1672    case JSOp::DebugLeaveLexicalEnv:
   1673    case JSOp::Debugger:
   1674    case JSOp::ImplicitThis:
   1675    case JSOp::NewTarget:
   1676    case JSOp::CheckIsObj:
   1677    case JSOp::CheckObjCoercible:
   1678    case JSOp::DebugCheckSelfHosted:
   1679    case JSOp::IsConstructing:
   1680    case JSOp::OptimizeSpreadCall:
   1681    case JSOp::ImportMeta:
   1682    case JSOp::EnterWith:
   1683    case JSOp::LeaveWith:
   1684    case JSOp::SpreadNew:
   1685    case JSOp::SpreadEval:
   1686    case JSOp::StrictSpreadEval:
   1687    case JSOp::CheckClassHeritage:
   1688    case JSOp::FunWithProto:
   1689    case JSOp::ObjWithProto:
   1690    case JSOp::BuiltinObject:
   1691    case JSOp::CheckThis:
   1692    case JSOp::CheckReturn:
   1693    case JSOp::CheckThisReinit:
   1694    case JSOp::SuperFun:
   1695    case JSOp::SpreadSuperCall:
   1696    case JSOp::SuperCall:
   1697    case JSOp::PushVarEnv:
   1698    case JSOp::GetBoundName:
   1699    case JSOp::Exception:
   1700    case JSOp::ExceptionAndStack:
   1701    case JSOp::IsGenClosing:
   1702    case JSOp::FinalYieldRval:
   1703    case JSOp::Resume:
   1704    case JSOp::CheckResumeKind:
   1705    case JSOp::AfterYield:
   1706    case JSOp::MaybeExtractAwaitValue:
   1707    case JSOp::Generator:
   1708    case JSOp::AsyncAwait:
   1709    case JSOp::AsyncResolve:
   1710    case JSOp::AsyncReject:
   1711    case JSOp::Finally:
   1712    case JSOp::GetRval:
   1713    case JSOp::ThrowMsg:
   1714    case JSOp::ForceInterpreter:
   1715      return false;
   1716 
   1717    case JSOp::InitAliasedLexical: {
   1718      uint32_t hops = EnvironmentCoordinate(pc).hops();
   1719      if (hops == 0) {
   1720        // Initializing aliased lexical in the current scope is almost same
   1721        // as JSOp::InitLexical.
   1722        return false;
   1723      }
   1724 
   1725      // Otherwise this can touch an environment outside of the current scope.
   1726      return true;
   1727    }
   1728  }
   1729 
   1730  MOZ_ASSERT_UNREACHABLE("Invalid opcode");
   1731  return false;
   1732 }
   1733 
   1734 bool DebuggerScript::CallData::getEffectfulOffsets() {
   1735  if (!ensureScript()) {
   1736    return false;
   1737  }
   1738 
   1739  RootedObject result(cx, NewDenseEmptyArray(cx));
   1740  if (!result) {
   1741    return false;
   1742  }
   1743  for (BytecodeRange r(cx, script); !r.empty(); r.popFront()) {
   1744    size_t offset = r.frontOffset();
   1745    if (!BytecodeIsEffectful(script, offset)) {
   1746      continue;
   1747    }
   1748 
   1749    if (IsGeneratorSlotInitialization(script, offset, cx)) {
   1750      // This is engine-internal operation and not visible outside the
   1751      // currently executing frame.
   1752      //
   1753      // Also this offset is not allowed for setting breakpoint.
   1754      continue;
   1755    }
   1756 
   1757    if (!NewbornArrayPush(cx, result, NumberValue(offset))) {
   1758      return false;
   1759    }
   1760  }
   1761 
   1762  args.rval().setObject(*result);
   1763  return true;
   1764 }
   1765 
   1766 bool DebuggerScript::CallData::getAllOffsets() {
   1767  if (!ensureScript()) {
   1768    return false;
   1769  }
   1770 
   1771  // First pass: determine which offsets in this script are jump targets and
   1772  // which line numbers jump to them.
   1773  FlowGraphSummary flowData(cx);
   1774  if (!flowData.populate(cx, script)) {
   1775    return false;
   1776  }
   1777 
   1778  // Second pass: build the result array.
   1779  RootedObject result(cx, NewDenseEmptyArray(cx));
   1780  if (!result) {
   1781    return false;
   1782  }
   1783  for (BytecodeRangeWithPosition r(cx, script, SkipPrologueOps::Yes);
   1784       !r.empty(); r.popFront()) {
   1785    if (!r.frontIsEntryPoint()) {
   1786      continue;
   1787    }
   1788 
   1789    size_t offset = r.frontOffset();
   1790    uint32_t lineno = r.frontLineNumber();
   1791 
   1792    // Make a note, if the current instruction is an entry point for the current
   1793    // line.
   1794    if (!flowData[offset].hasNoEdges() && flowData[offset].lineno() != lineno) {
   1795      // Get the offsets array for this line.
   1796      RootedObject offsets(cx);
   1797      RootedValue offsetsv(cx);
   1798 
   1799      RootedId id(cx, PropertyKey::Int(lineno));
   1800 
   1801      bool found;
   1802      if (!HasOwnProperty(cx, result, id, &found)) {
   1803        return false;
   1804      }
   1805      if (found && !GetProperty(cx, result, result, id, &offsetsv)) {
   1806        return false;
   1807      }
   1808 
   1809      if (offsetsv.isObject()) {
   1810        offsets = &offsetsv.toObject();
   1811      } else {
   1812        MOZ_ASSERT(offsetsv.isUndefined());
   1813 
   1814        // Create an empty offsets array for this line.
   1815        // Store it in the result array.
   1816        RootedId id(cx);
   1817        RootedValue v(cx, NumberValue(lineno));
   1818        offsets = NewDenseEmptyArray(cx);
   1819        if (!offsets || !PrimitiveValueToId<CanGC>(cx, v, &id)) {
   1820          return false;
   1821        }
   1822 
   1823        RootedValue value(cx, ObjectValue(*offsets));
   1824        if (!DefineDataProperty(cx, result, id, value)) {
   1825          return false;
   1826        }
   1827      }
   1828 
   1829      // Append the current offset to the offsets array.
   1830      if (!NewbornArrayPush(cx, offsets, NumberValue(offset))) {
   1831        return false;
   1832      }
   1833    }
   1834  }
   1835 
   1836  args.rval().setObject(*result);
   1837  return true;
   1838 }
   1839 
   1840 class DebuggerScript::GetAllColumnOffsetsMatcher {
   1841  JSContext* cx_;
   1842  MutableHandleObject result_;
   1843 
   1844  bool appendColumnOffsetEntry(uint32_t lineno,
   1845                               JS::LimitedColumnNumberOneOrigin column,
   1846                               size_t offset) {
   1847    Rooted<PlainObject*> entry(cx_, NewPlainObject(cx_));
   1848    if (!entry) {
   1849      return false;
   1850    }
   1851 
   1852    RootedValue value(cx_, NumberValue(lineno));
   1853    if (!DefineDataProperty(cx_, entry, cx_->names().lineNumber, value)) {
   1854      return false;
   1855    }
   1856 
   1857    value = NumberValue(column.oneOriginValue());
   1858    if (!DefineDataProperty(cx_, entry, cx_->names().columnNumber, value)) {
   1859      return false;
   1860    }
   1861 
   1862    value = NumberValue(offset);
   1863    if (!DefineDataProperty(cx_, entry, cx_->names().offset, value)) {
   1864      return false;
   1865    }
   1866 
   1867    return NewbornArrayPush(cx_, result_, ObjectValue(*entry));
   1868  }
   1869 
   1870 public:
   1871  explicit GetAllColumnOffsetsMatcher(JSContext* cx, MutableHandleObject result)
   1872      : cx_(cx), result_(result) {}
   1873  using ReturnType = bool;
   1874  ReturnType match(Handle<BaseScript*> base) {
   1875    RootedScript script(cx_, DelazifyScript(cx_, base));
   1876    if (!script) {
   1877      return false;
   1878    }
   1879 
   1880    // First pass: determine which offsets in this script are jump targets
   1881    // and which positions jump to them.
   1882    FlowGraphSummary flowData(cx_);
   1883    if (!flowData.populate(cx_, script)) {
   1884      return false;
   1885    }
   1886 
   1887    // Second pass: build the result array.
   1888    result_.set(NewDenseEmptyArray(cx_));
   1889    if (!result_) {
   1890      return false;
   1891    }
   1892 
   1893    for (BytecodeRangeWithPosition r(cx_, script, SkipPrologueOps::Yes);
   1894         !r.empty(); r.popFront()) {
   1895      uint32_t lineno = r.frontLineNumber();
   1896      JS::LimitedColumnNumberOneOrigin column = r.frontColumnNumber();
   1897      size_t offset = r.frontOffset();
   1898 
   1899      // Make a note, if the current instruction is an entry point for
   1900      // the current position.
   1901      if (r.frontIsEntryPoint() && !flowData[offset].hasNoEdges() &&
   1902          (flowData[offset].lineno() != lineno ||
   1903           flowData[offset].columnOrSentinel() != column.oneOriginValue())) {
   1904        if (!appendColumnOffsetEntry(lineno, column, offset)) {
   1905          return false;
   1906        }
   1907      }
   1908    }
   1909    return true;
   1910  }
   1911  ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
   1912    wasm::Instance& instance = instanceObj->instance();
   1913 
   1914    Vector<wasm::ExprLoc> offsets(cx_);
   1915    if (instance.debugEnabled() &&
   1916        !instance.debug().getAllColumnOffsets(&offsets)) {
   1917      return false;
   1918    }
   1919 
   1920    result_.set(NewDenseEmptyArray(cx_));
   1921    if (!result_) {
   1922      return false;
   1923    }
   1924 
   1925    for (uint32_t i = 0; i < offsets.length(); i++) {
   1926      uint32_t lineno = offsets[i].lineno;
   1927      JS::LimitedColumnNumberOneOrigin column(offsets[i].column);
   1928      size_t offset = offsets[i].offset;
   1929      if (!appendColumnOffsetEntry(lineno, column, offset)) {
   1930        return false;
   1931      }
   1932    }
   1933    return true;
   1934  }
   1935 };
   1936 
   1937 bool DebuggerScript::CallData::getAllColumnOffsets() {
   1938  RootedObject result(cx);
   1939  GetAllColumnOffsetsMatcher matcher(cx, &result);
   1940  if (!referent.match(matcher)) {
   1941    return false;
   1942  }
   1943 
   1944  args.rval().setObject(*result);
   1945  return true;
   1946 }
   1947 
   1948 class DebuggerScript::GetLineOffsetsMatcher {
   1949  JSContext* cx_;
   1950  uint32_t lineno_;
   1951  MutableHandleObject result_;
   1952 
   1953 public:
   1954  explicit GetLineOffsetsMatcher(JSContext* cx, uint32_t lineno,
   1955                                 MutableHandleObject result)
   1956      : cx_(cx), lineno_(lineno), result_(result) {}
   1957  using ReturnType = bool;
   1958  ReturnType match(Handle<BaseScript*> base) {
   1959    RootedScript script(cx_, DelazifyScript(cx_, base));
   1960    if (!script) {
   1961      return false;
   1962    }
   1963 
   1964    // First pass: determine which offsets in this script are jump targets and
   1965    // which line numbers jump to them.
   1966    FlowGraphSummary flowData(cx_);
   1967    if (!flowData.populate(cx_, script)) {
   1968      return false;
   1969    }
   1970 
   1971    result_.set(NewDenseEmptyArray(cx_));
   1972    if (!result_) {
   1973      return false;
   1974    }
   1975 
   1976    // Second pass: build the result array.
   1977    for (BytecodeRangeWithPosition r(cx_, script, SkipPrologueOps::Yes);
   1978         !r.empty(); r.popFront()) {
   1979      if (!r.frontIsEntryPoint()) {
   1980        continue;
   1981      }
   1982 
   1983      size_t offset = r.frontOffset();
   1984 
   1985      // If the op at offset is an entry point, append offset to result.
   1986      if (r.frontLineNumber() == lineno_ && !flowData[offset].hasNoEdges() &&
   1987          flowData[offset].lineno() != lineno_) {
   1988        if (!NewbornArrayPush(cx_, result_, NumberValue(offset))) {
   1989          return false;
   1990        }
   1991      }
   1992    }
   1993 
   1994    return true;
   1995  }
   1996  ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
   1997    wasm::Instance& instance = instanceObj->instance();
   1998 
   1999    Vector<uint32_t> offsets(cx_);
   2000    if (instance.debugEnabled() &&
   2001        !instance.debug().getLineOffsets(lineno_, &offsets)) {
   2002      return false;
   2003    }
   2004 
   2005    result_.set(NewDenseEmptyArray(cx_));
   2006    if (!result_) {
   2007      return false;
   2008    }
   2009 
   2010    for (uint32_t i = 0; i < offsets.length(); i++) {
   2011      if (!NewbornArrayPush(cx_, result_, NumberValue(offsets[i]))) {
   2012        return false;
   2013      }
   2014    }
   2015    return true;
   2016  }
   2017 };
   2018 
   2019 bool DebuggerScript::CallData::getLineOffsets() {
   2020  if (!args.requireAtLeast(cx, "Debugger.Script.getLineOffsets", 1)) {
   2021    return false;
   2022  }
   2023 
   2024  // Parse lineno argument.
   2025  RootedValue linenoValue(cx, args[0]);
   2026  uint32_t lineno;
   2027  if (!ToNumber(cx, &linenoValue)) {
   2028    return false;
   2029  }
   2030  {
   2031    double d = linenoValue.toNumber();
   2032    lineno = uint32_t(d);
   2033    if (lineno != d) {
   2034      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2035                                JSMSG_DEBUG_BAD_LINE);
   2036      return false;
   2037    }
   2038  }
   2039 
   2040  RootedObject result(cx);
   2041  GetLineOffsetsMatcher matcher(cx, lineno, &result);
   2042  if (!referent.match(matcher)) {
   2043    return false;
   2044  }
   2045 
   2046  args.rval().setObject(*result);
   2047  return true;
   2048 }
   2049 
   2050 struct DebuggerScript::SetBreakpointMatcher {
   2051  JSContext* cx_;
   2052  Debugger* dbg_;
   2053  size_t offset_;
   2054  RootedObject handler_;
   2055  RootedObject debuggerObject_;
   2056 
   2057  bool wrapCrossCompartmentEdges() {
   2058    if (!cx_->compartment()->wrap(cx_, &handler_) ||
   2059        !cx_->compartment()->wrap(cx_, &debuggerObject_)) {
   2060      return false;
   2061    }
   2062 
   2063    // If the Debugger's compartment has killed incoming wrappers, we may not
   2064    // have gotten usable results from the 'wrap' calls. Treat it as a
   2065    // failure.
   2066    if (IsDeadProxyObject(handler_) || IsDeadProxyObject(debuggerObject_)) {
   2067      ReportAccessDenied(cx_);
   2068      return false;
   2069    }
   2070 
   2071    return true;
   2072  }
   2073 
   2074 public:
   2075  explicit SetBreakpointMatcher(JSContext* cx, Debugger* dbg, size_t offset,
   2076                                HandleObject handler)
   2077      : cx_(cx),
   2078        dbg_(dbg),
   2079        offset_(offset),
   2080        handler_(cx, handler),
   2081        debuggerObject_(cx_, dbg_->toJSObject()) {}
   2082 
   2083  using ReturnType = bool;
   2084 
   2085  ReturnType match(Handle<BaseScript*> base) {
   2086    RootedScript script(cx_, DelazifyScript(cx_, base));
   2087    if (!script) {
   2088      return false;
   2089    }
   2090 
   2091    if (!dbg_->observesScript(script)) {
   2092      JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
   2093                                JSMSG_DEBUG_NOT_DEBUGGING);
   2094      return false;
   2095    }
   2096 
   2097    if (!EnsureScriptOffsetIsValid(cx_, script, offset_)) {
   2098      return false;
   2099    }
   2100 
   2101    if (!EnsureBreakpointIsAllowed(cx_, script, offset_)) {
   2102      return false;
   2103    }
   2104 
   2105    // Ensure observability *before* setting the breakpoint. If the script is
   2106    // not already a debuggee, trying to ensure observability after setting
   2107    // the breakpoint (and thus marking the script as a debuggee) will skip
   2108    // actually ensuring observability.
   2109    if (!dbg_->ensureExecutionObservabilityOfScript(cx_, script)) {
   2110      return false;
   2111    }
   2112 
   2113    // A Breakpoint belongs logically to its script's compartment, so its
   2114    // references to its Debugger and handler must be properly wrapped.
   2115    AutoRealm ar(cx_, script);
   2116    if (!wrapCrossCompartmentEdges()) {
   2117      return false;
   2118    }
   2119 
   2120    jsbytecode* pc = script->offsetToPC(offset_);
   2121    JSBreakpointSite* site =
   2122        DebugScript::getOrCreateBreakpointSite(cx_, script, pc);
   2123    if (!site) {
   2124      return false;
   2125    }
   2126 
   2127    if (!cx_->zone()->new_<Breakpoint>(dbg_, debuggerObject_, site, handler_)) {
   2128      site->destroyIfEmpty(cx_->runtime()->gcContext());
   2129      ReportOutOfMemory(cx_);
   2130      return false;
   2131    }
   2132    AddCellMemory(script, sizeof(Breakpoint), MemoryUse::Breakpoint);
   2133 
   2134    return true;
   2135  }
   2136  ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
   2137    wasm::Instance& instance = wasmInstance->instance();
   2138    if (!instance.debugEnabled() ||
   2139        !instance.debug().hasBreakpointTrapAtOffset(offset_)) {
   2140      JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
   2141                                JSMSG_DEBUG_BAD_OFFSET);
   2142      return false;
   2143    }
   2144 
   2145    // A Breakpoint belongs logically to its Instance's compartment, so its
   2146    // references to its Debugger and handler must be properly wrapped.
   2147    AutoRealm ar(cx_, wasmInstance);
   2148    if (!wrapCrossCompartmentEdges()) {
   2149      return false;
   2150    }
   2151 
   2152    WasmBreakpointSite* site = instance.getOrCreateBreakpointSite(cx_, offset_);
   2153    if (!site) {
   2154      return false;
   2155    }
   2156 
   2157    if (!cx_->zone()->new_<Breakpoint>(dbg_, debuggerObject_, site, handler_)) {
   2158      site->destroyIfEmpty(cx_->runtime()->gcContext());
   2159      ReportOutOfMemory(cx_);
   2160      return false;
   2161    }
   2162    AddCellMemory(wasmInstance, sizeof(Breakpoint), MemoryUse::Breakpoint);
   2163 
   2164    return true;
   2165  }
   2166 };
   2167 
   2168 bool DebuggerScript::CallData::setBreakpoint() {
   2169  if (!args.requireAtLeast(cx, "Debugger.Script.setBreakpoint", 2)) {
   2170    return false;
   2171  }
   2172  Debugger* dbg = obj->owner();
   2173 
   2174  size_t offset;
   2175  if (!ScriptOffset(cx, args[0], &offset)) {
   2176    return false;
   2177  }
   2178 
   2179  RootedObject handler(cx, RequireObject(cx, args[1]));
   2180  if (!handler) {
   2181    return false;
   2182  }
   2183 
   2184  SetBreakpointMatcher matcher(cx, dbg, offset, handler);
   2185  if (!referent.match(matcher)) {
   2186    return false;
   2187  }
   2188  args.rval().setUndefined();
   2189  return true;
   2190 }
   2191 
   2192 bool DebuggerScript::CallData::getBreakpoints() {
   2193  if (!ensureScript()) {
   2194    return false;
   2195  }
   2196  Debugger* dbg = obj->owner();
   2197 
   2198  jsbytecode* pc;
   2199  if (args.length() > 0) {
   2200    size_t offset;
   2201    if (!ScriptOffset(cx, args[0], &offset) ||
   2202        !EnsureScriptOffsetIsValid(cx, script, offset)) {
   2203      return false;
   2204    }
   2205    pc = script->offsetToPC(offset);
   2206  } else {
   2207    pc = nullptr;
   2208  }
   2209 
   2210  RootedObject arr(cx, NewDenseEmptyArray(cx));
   2211  if (!arr) {
   2212    return false;
   2213  }
   2214 
   2215  for (unsigned i = 0; i < script->length(); i++) {
   2216    JSBreakpointSite* site =
   2217        DebugScript::getBreakpointSite(script, script->offsetToPC(i));
   2218    if (!site) {
   2219      continue;
   2220    }
   2221    if (!pc || site->pc == pc) {
   2222      for (Breakpoint* bp = site->firstBreakpoint(); bp;
   2223           bp = bp->nextInSite()) {
   2224        if (bp->debugger == dbg) {
   2225          RootedObject handler(cx, bp->getHandler());
   2226          if (!cx->compartment()->wrap(cx, &handler) ||
   2227              !NewbornArrayPush(cx, arr, ObjectValue(*handler))) {
   2228            return false;
   2229          }
   2230        }
   2231      }
   2232    }
   2233  }
   2234  args.rval().setObject(*arr);
   2235  return true;
   2236 }
   2237 
   2238 class DebuggerScript::ClearBreakpointMatcher {
   2239  JSContext* cx_;
   2240  Debugger* dbg_;
   2241  RootedObject handler_;
   2242 
   2243 public:
   2244  ClearBreakpointMatcher(JSContext* cx, Debugger* dbg, JSObject* handler)
   2245      : cx_(cx), dbg_(dbg), handler_(cx, handler) {}
   2246  using ReturnType = bool;
   2247 
   2248  ReturnType match(Handle<BaseScript*> base) {
   2249    RootedScript script(cx_, DelazifyScript(cx_, base));
   2250    if (!script) {
   2251      return false;
   2252    }
   2253 
   2254    // A Breakpoint belongs logically to its script's compartment, so it holds
   2255    // its handler via a cross-compartment wrapper. But the handler passed to
   2256    // `clearBreakpoint` is same-compartment with the Debugger. Wrap it here,
   2257    // so that `DebugScript::clearBreakpointsIn` gets the right value to
   2258    // search for.
   2259    AutoRealm ar(cx_, script);
   2260    if (!cx_->compartment()->wrap(cx_, &handler_)) {
   2261      return false;
   2262    }
   2263 
   2264    DebugScript::clearBreakpointsIn(cx_->runtime()->gcContext(), script, dbg_,
   2265                                    handler_);
   2266    return true;
   2267  }
   2268  ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
   2269    wasm::Instance& instance = instanceObj->instance();
   2270    if (!instance.debugEnabled()) {
   2271      return true;
   2272    }
   2273 
   2274    // A Breakpoint belongs logically to its instance's compartment, so it
   2275    // holds its handler via a cross-compartment wrapper. But the handler
   2276    // passed to `clearBreakpoint` is same-compartment with the Debugger. Wrap
   2277    // it here, so that `DebugState::clearBreakpointsIn` gets the right value
   2278    // to search for.
   2279    AutoRealm ar(cx_, instanceObj);
   2280    if (!cx_->compartment()->wrap(cx_, &handler_)) {
   2281      return false;
   2282    }
   2283 
   2284    instance.debug().clearBreakpointsIn(cx_->runtime()->gcContext(),
   2285                                        instanceObj, dbg_, handler_);
   2286    return true;
   2287  }
   2288 };
   2289 
   2290 bool DebuggerScript::CallData::clearBreakpoint() {
   2291  if (!args.requireAtLeast(cx, "Debugger.Script.clearBreakpoint", 1)) {
   2292    return false;
   2293  }
   2294  Debugger* dbg = obj->owner();
   2295 
   2296  JSObject* handler = RequireObject(cx, args[0]);
   2297  if (!handler) {
   2298    return false;
   2299  }
   2300 
   2301  ClearBreakpointMatcher matcher(cx, dbg, handler);
   2302  if (!referent.match(matcher)) {
   2303    return false;
   2304  }
   2305 
   2306  args.rval().setUndefined();
   2307  return true;
   2308 }
   2309 
   2310 bool DebuggerScript::CallData::clearAllBreakpoints() {
   2311  Debugger* dbg = obj->owner();
   2312  ClearBreakpointMatcher matcher(cx, dbg, nullptr);
   2313  if (!referent.match(matcher)) {
   2314    return false;
   2315  }
   2316  args.rval().setUndefined();
   2317  return true;
   2318 }
   2319 
   2320 class DebuggerScript::IsInCatchScopeMatcher {
   2321  JSContext* cx_;
   2322  size_t offset_;
   2323  bool isInCatch_;
   2324 
   2325 public:
   2326  explicit IsInCatchScopeMatcher(JSContext* cx, size_t offset)
   2327      : cx_(cx), offset_(offset), isInCatch_(false) {}
   2328  using ReturnType = bool;
   2329 
   2330  inline bool isInCatch() const { return isInCatch_; }
   2331 
   2332  ReturnType match(Handle<BaseScript*> base) {
   2333    RootedScript script(cx_, DelazifyScript(cx_, base));
   2334    if (!script) {
   2335      return false;
   2336    }
   2337 
   2338    if (!EnsureScriptOffsetIsValid(cx_, script, offset_)) {
   2339      return false;
   2340    }
   2341 
   2342    MOZ_ASSERT(!isInCatch_);
   2343    for (const TryNote& tn : script->trynotes()) {
   2344      bool inRange = tn.start <= offset_ && offset_ < tn.start + tn.length;
   2345      if (inRange && tn.kind() == TryNoteKind::Catch) {
   2346        isInCatch_ = true;
   2347      } else if (isInCatch_) {
   2348        // For-of loops generate a synthetic catch block to handle
   2349        // closing the iterator when throwing an exception. The
   2350        // debugger should ignore these synthetic catch blocks, so
   2351        // we skip any Catch trynote that is immediately followed
   2352        // by a ForOf trynote.
   2353        if (inRange && tn.kind() == TryNoteKind::ForOf) {
   2354          isInCatch_ = false;
   2355          continue;
   2356        }
   2357        return true;
   2358      }
   2359    }
   2360 
   2361    return true;
   2362  }
   2363  ReturnType match(Handle<WasmInstanceObject*> instance) {
   2364    isInCatch_ = false;
   2365    return true;
   2366  }
   2367 };
   2368 
   2369 bool DebuggerScript::CallData::isInCatchScope() {
   2370  if (!args.requireAtLeast(cx, "Debugger.Script.isInCatchScope", 1)) {
   2371    return false;
   2372  }
   2373 
   2374  size_t offset;
   2375  if (!ScriptOffset(cx, args[0], &offset)) {
   2376    return false;
   2377  }
   2378 
   2379  IsInCatchScopeMatcher matcher(cx, offset);
   2380  if (!referent.match(matcher)) {
   2381    return false;
   2382  }
   2383  args.rval().setBoolean(matcher.isInCatch());
   2384  return true;
   2385 }
   2386 
   2387 bool DebuggerScript::CallData::getOffsetsCoverage() {
   2388  if (!ensureScript()) {
   2389    return false;
   2390  }
   2391 
   2392  Debugger* dbg = obj->owner();
   2393  if (dbg->observesCoverage() != Debugger::Observing) {
   2394    args.rval().setNull();
   2395    return true;
   2396  }
   2397 
   2398  // If the script has no coverage information, then skip this and return null
   2399  // instead.
   2400  if (!script->hasScriptCounts()) {
   2401    args.rval().setNull();
   2402    return true;
   2403  }
   2404 
   2405  ScriptCounts* sc = &script->getScriptCounts();
   2406 
   2407  // If the main ever got visited, then assume that any code before main got
   2408  // visited once.
   2409  uint64_t hits = 0;
   2410  const PCCounts* counts =
   2411      sc->maybeGetPCCounts(script->pcToOffset(script->main()));
   2412  if (counts->numExec()) {
   2413    hits = 1;
   2414  }
   2415 
   2416  // Build an array of objects which are composed of 4 properties:
   2417  //  - offset          PC offset of the current opcode.
   2418  //  - lineNumber      Line of the current opcode.
   2419  //  - columnNumber    Column of the current opcode.
   2420  //  - count           Number of times the instruction got executed.
   2421  RootedObject result(cx, NewDenseEmptyArray(cx));
   2422  if (!result) {
   2423    return false;
   2424  }
   2425 
   2426  RootedId offsetId(cx, NameToId(cx->names().offset));
   2427  RootedId lineNumberId(cx, NameToId(cx->names().lineNumber));
   2428  RootedId columnNumberId(cx, NameToId(cx->names().columnNumber));
   2429  RootedId countId(cx, NameToId(cx->names().count));
   2430 
   2431  RootedObject item(cx);
   2432  RootedValue offsetValue(cx);
   2433  RootedValue lineNumberValue(cx);
   2434  RootedValue columnNumberValue(cx);
   2435  RootedValue countValue(cx);
   2436 
   2437  // Iterate linearly over the bytecode.
   2438  for (BytecodeRangeWithPosition r(cx, script, SkipPrologueOps::Yes);
   2439       !r.empty(); r.popFront()) {
   2440    size_t offset = r.frontOffset();
   2441 
   2442    // The beginning of each non-branching sequences of instruction set the
   2443    // number of execution of the current instruction and any following
   2444    // instruction.
   2445    counts = sc->maybeGetPCCounts(offset);
   2446    if (counts) {
   2447      hits = counts->numExec();
   2448    }
   2449 
   2450    offsetValue.setNumber(double(offset));
   2451    lineNumberValue.setNumber(double(r.frontLineNumber()));
   2452    columnNumberValue.setNumber(double(r.frontColumnNumber().oneOriginValue()));
   2453    countValue.setNumber(double(hits));
   2454 
   2455    // Create a new object with the offset, line number, column number, the
   2456    // number of hit counts, and append it to the array.
   2457    item = NewPlainObjectWithProto(cx, nullptr);
   2458    if (!item || !DefineDataProperty(cx, item, offsetId, offsetValue) ||
   2459        !DefineDataProperty(cx, item, lineNumberId, lineNumberValue) ||
   2460        !DefineDataProperty(cx, item, columnNumberId, columnNumberValue) ||
   2461        !DefineDataProperty(cx, item, countId, countValue) ||
   2462        !NewbornArrayPush(cx, result, ObjectValue(*item))) {
   2463      return false;
   2464    }
   2465 
   2466    // If the current instruction has thrown, then decrement the hit counts
   2467    // with the number of throws.
   2468    counts = sc->maybeGetThrowCounts(offset);
   2469    if (counts) {
   2470      hits -= counts->numExec();
   2471    }
   2472  }
   2473 
   2474  args.rval().setObject(*result);
   2475  return true;
   2476 }
   2477 
   2478 /* static */
   2479 bool DebuggerScript::construct(JSContext* cx, unsigned argc, Value* vp) {
   2480  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
   2481                            "Debugger.Script");
   2482  return false;
   2483 }
   2484 
   2485 const JSPropertySpec DebuggerScript::properties_[] = {
   2486    JS_DEBUG_PSG("isGeneratorFunction", getIsGeneratorFunction),
   2487    JS_DEBUG_PSG("isAsyncFunction", getIsAsyncFunction),
   2488    JS_DEBUG_PSG("isFunction", getIsFunction),
   2489    JS_DEBUG_PSG("isModule", getIsModule),
   2490    JS_DEBUG_PSG("displayName", getDisplayName),
   2491    JS_DEBUG_PSG("parameterNames", getParameterNames),
   2492    JS_DEBUG_PSG("url", getUrl),
   2493    JS_DEBUG_PSG("startLine", getStartLine),
   2494    JS_DEBUG_PSG("startColumn", getStartColumn),
   2495    JS_DEBUG_PSG("lineCount", getLineCount),
   2496    JS_DEBUG_PSG("source", getSource),
   2497    JS_DEBUG_PSG("sourceStart", getSourceStart),
   2498    JS_DEBUG_PSG("sourceLength", getSourceLength),
   2499    JS_DEBUG_PSG("mainOffset", getMainOffset),
   2500    JS_DEBUG_PSG("global", getGlobal),
   2501    JS_DEBUG_PSG("format", getFormat),
   2502    JS_PS_END,
   2503 };
   2504 
   2505 const JSFunctionSpec DebuggerScript::methods_[] = {
   2506    JS_DEBUG_FN("getChildScripts", getChildScripts, 0),
   2507    JS_DEBUG_FN("getPossibleBreakpoints", getPossibleBreakpoints, 0),
   2508    JS_DEBUG_FN("getPossibleBreakpointOffsets", getPossibleBreakpointOffsets,
   2509                0),
   2510    JS_DEBUG_FN("setBreakpoint", setBreakpoint, 2),
   2511    JS_DEBUG_FN("getBreakpoints", getBreakpoints, 1),
   2512    JS_DEBUG_FN("clearBreakpoint", clearBreakpoint, 1),
   2513    JS_DEBUG_FN("clearAllBreakpoints", clearAllBreakpoints, 0),
   2514    JS_DEBUG_FN("isInCatchScope", isInCatchScope, 1),
   2515    JS_DEBUG_FN("getOffsetMetadata", getOffsetMetadata, 1),
   2516    JS_DEBUG_FN("getOffsetsCoverage", getOffsetsCoverage, 0),
   2517    JS_DEBUG_FN("getEffectfulOffsets", getEffectfulOffsets, 1),
   2518 
   2519    // The following APIs are deprecated due to their reliance on the
   2520    // under-defined 'entrypoint' concept. Make use of getPossibleBreakpoints,
   2521    // getPossibleBreakpointOffsets, or getOffsetMetadata instead.
   2522    JS_DEBUG_FN("getAllOffsets", getAllOffsets, 0),
   2523    JS_DEBUG_FN("getAllColumnOffsets", getAllColumnOffsets, 0),
   2524    JS_DEBUG_FN("getLineOffsets", getLineOffsets, 1),
   2525    JS_DEBUG_FN("getOffsetLocation", getOffsetLocation, 0),
   2526    JS_FS_END,
   2527 };