Backed out 5 changesets (bug 1577007, bug 1576776, bug 1576781, bug 1564167) for crashtest failures
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Mon, 09 Sep 2019 10:42:57 +0300
changeset 492178 4048af298d81e3b08ce0adae307eaf93d194d60f
parent 492177 0d4ffe00fdbb35f52fa0be1334e8b28a00d6be86
child 492179 c7711f68d24a98c42e64ec27ba0d5a7475cf806d
child 492272 25dba99860759e412f009985cc5d5166860d204e
push id114045
push usershindli@mozilla.com
push dateMon, 09 Sep 2019 10:00:00 +0000
treeherdermozilla-inbound@4048af298d81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1577007, 1576776, 1576781, 1564167
milestone71.0a1
backs out171d5f099ad0d22feda354c74182703ed02842a7
4b4ecd73e277680882917317f5e1235c82dfeb8b
e1be19463a5937ec7949df1fccbe465bf930cc40
da3d3d8b83c8d9003ef6d29585af10f1db8a1151
b00926690f17e839a5f6b6dc77aa2286fd42b6ca
first release with
nightly linux32
4048af298d81 / 71.0a1 / 20190909095540 / files
nightly linux64
4048af298d81 / 71.0a1 / 20190909095540 / files
nightly mac
4048af298d81 / 71.0a1 / 20190909095540 / files
nightly win32
4048af298d81 / 71.0a1 / 20190909095540 / files
nightly win64
4048af298d81 / 71.0a1 / 20190909095540 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 5 changesets (bug 1577007, bug 1576776, bug 1576781, bug 1564167) for crashtest failures Backed out changeset 171d5f099ad0 (bug 1564167) Backed out changeset 4b4ecd73e277 (bug 1577007) Backed out changeset e1be19463a59 (bug 1576781) Backed out changeset da3d3d8b83c8 (bug 1576776) Backed out changeset b00926690f17 (bug 1576776)
js/src/debugger/DebugAPI-inl.h
js/src/debugger/DebugAPI.h
js/src/debugger/Debugger.cpp
js/src/debugger/Debugger.h
js/src/debugger/DebuggerMemory.cpp
js/src/debugger/DebuggerMemory.h
js/src/debugger/Environment.cpp
js/src/debugger/Environment.h
js/src/debugger/Frame.cpp
js/src/debugger/Frame.h
js/src/debugger/Object.cpp
js/src/debugger/Object.h
js/src/debugger/Script.cpp
js/src/debugger/Script.h
js/src/debugger/Source.cpp
js/src/debugger/Source.h
js/src/doc/Debugger/Debugger.Object.md
js/src/doc/Debugger/Debugger.Script.md
js/src/doc/Debugger/Debugger.md
js/src/jit-test/tests/debug/Debugger-onNativeCall-01.js
js/src/jit-test/tests/debug/Debugger-onNativeCall-02.js
js/src/jit-test/tests/debug/Debugger-onNativeCall-03.js
js/src/jit-test/tests/debug/Debugger-onNativeCall-04.js
js/src/jit-test/tests/debug/Object-isSameNative.js
js/src/jit-test/tests/debug/Script-getEffectfulOffsets.js
js/src/jit/BaselineJIT.cpp
js/src/jit/Jit.cpp
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
js/src/vm/JSContext.cpp
js/src/vm/JSContext.h
--- a/js/src/debugger/DebugAPI-inl.h
+++ b/js/src/debugger/DebugAPI-inl.h
@@ -127,25 +127,16 @@ ResumeMode DebugAPI::onResumeFrame(JSCon
                 frame.isDebuggee());
   if (!frame.isDebuggee()) {
     return ResumeMode::Continue;
   }
   return slowPathOnResumeFrame(cx, frame);
 }
 
 /* static */
-ResumeMode DebugAPI::onNativeCall(JSContext* cx, const CallArgs& args,
-                                  CallReason reason) {
-  if (!cx->realm()->isDebuggee()) {
-    return ResumeMode::Continue;
-  }
-  return slowPathOnNativeCall(cx, args, reason);
-}
-
-/* static */
 ResumeMode DebugAPI::onDebuggerStatement(JSContext* cx,
                                          AbstractFramePtr frame) {
   if (!cx->realm()->isDebuggee()) {
     return ResumeMode::Continue;
   }
   return slowPathOnDebuggerStatement(cx, frame);
 }
 
--- a/js/src/debugger/DebugAPI.h
+++ b/js/src/debugger/DebugAPI.h
@@ -3,17 +3,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef debugger_DebugAPI_h
 #define debugger_DebugAPI_h
 
 #include "vm/GlobalObject.h"
-#include "vm/Interpreter.h"
 #include "vm/JSContext.h"
 
 namespace js {
 
 // This file contains the API which SpiderMonkey should use to interact with any
 // active Debuggers.
 
 class AbstractGeneratorObject;
@@ -209,19 +208,16 @@ class DebugAPI {
    * Unfortunately, the interpreter and the baseline JIT arrange for this to
    * be called in different ways. The interpreter calls it from JSOP_RESUME,
    * immediately after pushing the resumed frame; the JIT calls it from
    * JSOP_AFTERYIELD, just after the generator resumes. The difference
    * should not be user-visible.
    */
   static inline ResumeMode onResumeFrame(JSContext* cx, AbstractFramePtr frame);
 
-  static inline ResumeMode onNativeCall(JSContext* cx, const CallArgs& args,
-                                        CallReason reason);
-
   /*
    * Announce to the debugger a |debugger;| statement on has been
    * encountered on the youngest JS frame on |cx|. Call whatever hooks have
    * been registered to observe this.
    *
    * Note that this method is called for all |debugger;| statements,
    * regardless of the frame's debuggee-ness.
    */
@@ -376,18 +372,16 @@ class DebugAPI {
   static MOZ_MUST_USE bool slowPathOnNewGenerator(
       JSContext* cx, AbstractFramePtr frame,
       Handle<AbstractGeneratorObject*> genObj);
   static MOZ_MUST_USE bool slowPathCheckNoExecute(JSContext* cx,
                                                   HandleScript script);
   static ResumeMode slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
   static ResumeMode slowPathOnResumeFrame(JSContext* cx,
                                           AbstractFramePtr frame);
-  static ResumeMode slowPathOnNativeCall(JSContext* cx, const CallArgs& args,
-                                         CallReason reason);
   static ResumeMode slowPathOnDebuggerStatement(JSContext* cx,
                                                 AbstractFramePtr frame);
   static ResumeMode slowPathOnExceptionUnwind(JSContext* cx,
                                               AbstractFramePtr frame);
   static void slowPathOnNewWasmInstance(
       JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
   static void slowPathOnNewPromise(JSContext* cx,
                                    Handle<PromiseObject*> promise);
--- a/js/src/debugger/Debugger.cpp
+++ b/js/src/debugger/Debugger.cpp
@@ -837,50 +837,16 @@ ResumeMode DebugAPI::slowPathOnResumeFra
         }
       }
     }
   }
 
   return slowPathOnEnterFrame(cx, frame);
 }
 
-/* static */
-ResumeMode DebugAPI::slowPathOnNativeCall(JSContext* cx, const CallArgs& args,
-                                          CallReason reason) {
-  RootedValue rval(cx);
-  ResumeMode resumeMode = Debugger::dispatchHook(
-      cx,
-      [cx](Debugger* dbg) -> bool {
-        return dbg == cx->insideDebuggerEvaluationWithOnNativeCallHook &&
-          dbg->getHook(Debugger::OnNativeCall);
-      },
-      [&](Debugger* dbg) -> ResumeMode {
-        return dbg->fireNativeCall(cx, args, reason, &rval);
-      });
-
-  switch (resumeMode) {
-    case ResumeMode::Continue:
-      break;
-
-    case ResumeMode::Throw:
-      cx->setPendingExceptionAndCaptureStack(rval);
-      break;
-
-    case ResumeMode::Terminate:
-      cx->clearPendingException();
-      break;
-
-    case ResumeMode::Return:
-      args.rval().set(rval);
-      break;
-  }
-
-  return resumeMode;
-}
-
 /*
  * RAII class to mark a generator as "running" temporarily while running
  * debugger code.
  *
  * When Debugger::slowPathOnLeaveFrame is called for a frame that is yielding
  * or awaiting, its generator is in the "suspended" state. Letting script
  * observe this state, with the generator on stack yet also reenterable, would
  * be bad, so we mark it running while we fire events.
@@ -1784,17 +1750,17 @@ ResumeMode Debugger::processHandlerResul
                                           const Value& rv,
                                           AbstractFramePtr frame,
                                           jsbytecode* pc,
                                           MutableHandleValue vp) {
   JSContext* cx = ar->context();
 
   RootedValue thisv(cx);
   Maybe<HandleValue> maybeThisv;
-  if (frame && !GetThisValueForCheck(cx, frame, pc, &thisv, maybeThisv)) {
+  if (!GetThisValueForCheck(cx, frame, pc, &thisv, maybeThisv)) {
     ar.reset();
     return ResumeMode::Terminate;
   }
 
   if (!success) {
     return handleUncaughtException(ar, vp, maybeThisv, frame);
   }
 
@@ -2185,54 +2151,16 @@ ResumeMode Debugger::fireEnterFrame(JSCo
   RootedValue fval(cx, ObjectValue(*hook));
   RootedValue rv(cx);
   bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
 
   return processHandlerResult(ar, ok, rv, iter.abstractFramePtr(), iter.pc(),
                               vp);
 }
 
-ResumeMode Debugger::fireNativeCall(JSContext* cx, const CallArgs& args,
-                                    CallReason reason, MutableHandleValue vp) {
-  RootedObject hook(cx, getHook(OnNativeCall));
-  MOZ_ASSERT(hook);
-  MOZ_ASSERT(hook->isCallable());
-
-  Maybe<AutoRealm> ar;
-  ar.emplace(cx, object);
-
-  RootedValue fval(cx, ObjectValue(*hook));
-  RootedValue calleeval(cx, args.calleev());
-  if (!wrapDebuggeeValue(cx, &calleeval)) {
-    return reportUncaughtException(ar);
-  }
-
-  JSAtom* reasonAtom = nullptr;
-  switch (reason) {
-    case CallReason::Call:
-      reasonAtom = cx->names().call;
-      break;
-    case CallReason::Getter:
-      reasonAtom = cx->names().get;
-      break;
-    case CallReason::Setter:
-      reasonAtom = cx->names().set;
-      break;
-  }
-  cx->markAtom(reasonAtom);
-
-  RootedValue reasonval(cx, StringValue(reasonAtom));
-
-  RootedValue rv(cx);
-  bool ok = js::Call(cx, fval, object, calleeval, reasonval, &rv);
-
-  AbstractFramePtr frame;
-  return processHandlerResult(ar, ok, rv, frame, nullptr, vp);
-}
-
 void Debugger::fireNewScript(JSContext* cx,
                              Handle<DebuggerScriptReferent> scriptReferent) {
   RootedObject hook(cx, getHook(OnNewScript));
   MOZ_ASSERT(hook);
   MOZ_ASSERT(hook->isCallable());
 
   Maybe<AutoRealm> ar;
   ar.emplace(cx, object);
@@ -3381,23 +3309,16 @@ Debugger::IsObserving Debugger::observes
 
 Debugger::IsObserving Debugger::observesCoverage() const {
   if (collectCoverageInfo) {
     return Observing;
   }
   return NotObserving;
 }
 
-Debugger::IsObserving Debugger::observesNativeCalls() const {
-  if (getHook(Debugger::OnNativeCall)) {
-    return Observing;
-  }
-  return NotObserving;
-}
-
 // Toggle whether this Debugger's debuggees observe all execution. This is
 // called when a hook that observes all execution is set or unset. See
 // hookObservesAllExecution.
 bool Debugger::updateObservesAllExecutionOnDebuggees(JSContext* cx,
                                                      IsObserving observing) {
   ExecutionObservableRealms obs(cx);
 
   for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
@@ -4052,95 +3973,31 @@ static Debugger* Debugger_fromThisValue(
   if (!dbg) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger", fnname,
                               "prototype object");
   }
   return dbg;
 }
 
-struct MOZ_STACK_CLASS Debugger::CallData {
-  JSContext* cx;
-  const CallArgs& args;
-
-  Debugger* dbg;
-
-  CallData(JSContext* cx, const CallArgs& args, Debugger* dbg)
-      : cx(cx), args(args), dbg(dbg) {}
-
-  bool getOnDebuggerStatement();
-  bool setOnDebuggerStatement();
-  bool getOnExceptionUnwind();
-  bool setOnExceptionUnwind();
-  bool getOnNewScript();
-  bool setOnNewScript();
-  bool getOnEnterFrame();
-  bool setOnEnterFrame();
-  bool getOnNativeCall();
-  bool setOnNativeCall();
-  bool getOnNewGlobalObject();
-  bool setOnNewGlobalObject();
-  bool getOnNewPromise();
-  bool setOnNewPromise();
-  bool getOnPromiseSettled();
-  bool setOnPromiseSettled();
-  bool getUncaughtExceptionHook();
-  bool setUncaughtExceptionHook();
-  bool getAllowUnobservedAsmJS();
-  bool setAllowUnobservedAsmJS();
-  bool getCollectCoverageInfo();
-  bool setCollectCoverageInfo();
-  bool getMemory();
-  bool addDebuggee();
-  bool addAllGlobalsAsDebuggees();
-  bool removeDebuggee();
-  bool removeAllDebuggees();
-  bool hasDebuggee();
-  bool getDebuggees();
-  bool getNewestFrame();
-  bool clearAllBreakpoints();
-  bool findScripts();
-  bool findSources();
-  bool findObjects();
-  bool findAllGlobals();
-  bool findSourceURLs();
-  bool makeGlobalObjectReference();
-  bool adoptDebuggeeValue();
-  bool adoptSource();
-
-  using Method = bool (CallData::*)();
-
-  template <Method MyMethod>
-  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
-};
-
-template <Debugger::CallData::Method MyMethod>
-/* static */
-bool Debugger::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  Debugger* dbg = Debugger_fromThisValue(cx, args, "method");
-  if (!dbg) {
-    return false;
-  }
-
-  CallData data(cx, args, dbg);
-  return (data.*MyMethod)();
-}
+#define THIS_DEBUGGER(cx, argc, vp, fnname, args, dbg)      \
+  CallArgs args = CallArgsFromVp(argc, vp);                 \
+  Debugger* dbg = Debugger_fromThisValue(cx, args, fnname); \
+  if (!dbg) return false
 
 /* static */
-bool Debugger::getHookImpl(JSContext* cx, const CallArgs& args, Debugger& dbg,
+bool Debugger::getHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg,
                            Hook which) {
   MOZ_ASSERT(which >= 0 && which < HookCount);
   args.rval().set(dbg.object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + which));
   return true;
 }
 
 /* static */
-bool Debugger::setHookImpl(JSContext* cx, const CallArgs& args, Debugger& dbg,
+bool Debugger::setHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg,
                            Hook which) {
   MOZ_ASSERT(which >= 0 && which < HookCount);
   if (!args.requireAtLeast(cx, "Debugger.setHook", 1)) {
     return false;
   }
   if (args[0].isObject()) {
     if (!args[0].toObject().isCallable()) {
       return ReportIsNotFunction(cx, args[0], args.length() - 1);
@@ -4159,77 +4016,97 @@ bool Debugger::setHookImpl(JSContext* cx
       dbg.object->setReservedSlot(slot, oldHook);
       return false;
     }
   }
   args.rval().setUndefined();
   return true;
 }
 
-bool Debugger::CallData::getOnDebuggerStatement() {
+/* static */
+bool Debugger::getOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(get onDebuggerStatement)", args, dbg);
   return getHookImpl(cx, args, *dbg, OnDebuggerStatement);
 }
 
-bool Debugger::CallData::setOnDebuggerStatement() {
+/* static */
+bool Debugger::setOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(set onDebuggerStatement)", args, dbg);
   return setHookImpl(cx, args, *dbg, OnDebuggerStatement);
 }
 
-bool Debugger::CallData::getOnExceptionUnwind() {
+/* static */
+bool Debugger::getOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(get onExceptionUnwind)", args, dbg);
   return getHookImpl(cx, args, *dbg, OnExceptionUnwind);
 }
 
-bool Debugger::CallData::setOnExceptionUnwind() {
+/* static */
+bool Debugger::setOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(set onExceptionUnwind)", args, dbg);
   return setHookImpl(cx, args, *dbg, OnExceptionUnwind);
 }
 
-bool Debugger::CallData::getOnNewScript() {
+/* static */
+bool Debugger::getOnNewScript(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(get onNewScript)", args, dbg);
   return getHookImpl(cx, args, *dbg, OnNewScript);
 }
 
-bool Debugger::CallData::setOnNewScript() {
+/* static */
+bool Debugger::setOnNewScript(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(set onNewScript)", args, dbg);
   return setHookImpl(cx, args, *dbg, OnNewScript);
 }
 
-bool Debugger::CallData::getOnNewPromise() {
+/* static */
+bool Debugger::getOnNewPromise(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(get onNewPromise)", args, dbg);
   return getHookImpl(cx, args, *dbg, OnNewPromise);
 }
 
-bool Debugger::CallData::setOnNewPromise() {
+/* static */
+bool Debugger::setOnNewPromise(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(set onNewPromise)", args, dbg);
   return setHookImpl(cx, args, *dbg, OnNewPromise);
 }
 
-bool Debugger::CallData::getOnPromiseSettled() {
+/* static */
+bool Debugger::getOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(get onPromiseSettled)", args, dbg);
   return getHookImpl(cx, args, *dbg, OnPromiseSettled);
 }
 
-bool Debugger::CallData::setOnPromiseSettled() {
+/* static */
+bool Debugger::setOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(set onPromiseSettled)", args, dbg);
   return setHookImpl(cx, args, *dbg, OnPromiseSettled);
 }
 
-bool Debugger::CallData::getOnEnterFrame() {
+/* static */
+bool Debugger::getOnEnterFrame(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(get onEnterFrame)", args, dbg);
   return getHookImpl(cx, args, *dbg, OnEnterFrame);
 }
 
-bool Debugger::CallData::setOnEnterFrame() {
+/* static */
+bool Debugger::setOnEnterFrame(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(set onEnterFrame)", args, dbg);
   return setHookImpl(cx, args, *dbg, OnEnterFrame);
 }
 
-bool Debugger::CallData::getOnNativeCall() {
-  return getHookImpl(cx, args, *dbg, OnNativeCall);
-}
-
-bool Debugger::CallData::setOnNativeCall() {
-  return setHookImpl(cx, args, *dbg, OnNativeCall);
-}
-
-bool Debugger::CallData::getOnNewGlobalObject() {
+/* static */
+bool Debugger::getOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "(get onNewGlobalObject)", args, dbg);
   return getHookImpl(cx, args, *dbg, OnNewGlobalObject);
 }
 
-bool Debugger::CallData::setOnNewGlobalObject() {
+/* static */
+bool Debugger::setOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "setOnNewGlobalObject", args, dbg);
   RootedObject oldHook(cx, dbg->getHook(OnNewGlobalObject));
 
   if (!setHookImpl(cx, args, *dbg, OnNewGlobalObject)) {
     return false;
   }
 
   // Add or remove ourselves from the runtime's list of Debuggers that care
   // about new globals.
@@ -4238,80 +4115,98 @@ bool Debugger::CallData::setOnNewGlobalO
     cx->runtime()->onNewGlobalObjectWatchers().pushBack(dbg);
   } else if (oldHook && !newHook) {
     cx->runtime()->onNewGlobalObjectWatchers().remove(dbg);
   }
 
   return true;
 }
 
-bool Debugger::CallData::getUncaughtExceptionHook() {
+/* static */
+bool Debugger::getUncaughtExceptionHook(JSContext* cx, unsigned argc,
+                                        Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "get uncaughtExceptionHook", args, dbg);
   args.rval().setObjectOrNull(dbg->uncaughtExceptionHook);
   return true;
 }
 
-bool Debugger::CallData::setUncaughtExceptionHook() {
+/* static */
+bool Debugger::setUncaughtExceptionHook(JSContext* cx, unsigned argc,
+                                        Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "set uncaughtExceptionHook", args, dbg);
   if (!args.requireAtLeast(cx, "Debugger.set uncaughtExceptionHook", 1)) {
     return false;
   }
   if (!args[0].isNull() &&
       (!args[0].isObject() || !args[0].toObject().isCallable())) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_ASSIGN_FUNCTION_OR_NULL,
                               "uncaughtExceptionHook");
     return false;
   }
   dbg->uncaughtExceptionHook = args[0].toObjectOrNull();
   args.rval().setUndefined();
   return true;
 }
 
-bool Debugger::CallData::getAllowUnobservedAsmJS() {
+/* static */
+bool Debugger::getAllowUnobservedAsmJS(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "get allowUnobservedAsmJS", args, dbg);
   args.rval().setBoolean(dbg->allowUnobservedAsmJS);
   return true;
 }
 
-bool Debugger::CallData::setAllowUnobservedAsmJS() {
+/* static */
+bool Debugger::setAllowUnobservedAsmJS(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "set allowUnobservedAsmJS", args, dbg);
   if (!args.requireAtLeast(cx, "Debugger.set allowUnobservedAsmJS", 1)) {
     return false;
   }
   dbg->allowUnobservedAsmJS = ToBoolean(args[0]);
 
   for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty();
        r.popFront()) {
     GlobalObject* global = r.front();
     Realm* realm = global->realm();
     realm->updateDebuggerObservesAsmJS();
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool Debugger::CallData::getCollectCoverageInfo() {
+/* static */
+bool Debugger::getCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "get collectCoverageInfo", args, dbg);
   args.rval().setBoolean(dbg->collectCoverageInfo);
   return true;
 }
 
-bool Debugger::CallData::setCollectCoverageInfo() {
+/* static */
+bool Debugger::setCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "set collectCoverageInfo", args, dbg);
   if (!args.requireAtLeast(cx, "Debugger.set collectCoverageInfo", 1)) {
     return false;
   }
   dbg->collectCoverageInfo = ToBoolean(args[0]);
 
   IsObserving observing = dbg->collectCoverageInfo ? Observing : NotObserving;
   if (!dbg->updateObservesCoverageOnDebuggees(cx, observing)) {
     return false;
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool Debugger::CallData::getMemory() {
+/* static */
+bool Debugger::getMemory(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "get memory", args, dbg);
   Value memoryValue =
       dbg->object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE);
 
   if (!memoryValue.isObject()) {
     RootedObject memory(cx, DebuggerMemory::create(cx, dbg));
     if (!memory) {
       return false;
     }
@@ -4366,17 +4261,19 @@ GlobalObject* Debugger::unwrapDebuggeeAr
                               JSMSG_UNEXPECTED_TYPE, "argument",
                               "not a global object");
     return nullptr;
   }
 
   return &obj->as<GlobalObject>();
 }
 
-bool Debugger::CallData::addDebuggee() {
+/* static */
+bool Debugger::addDebuggee(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "addDebuggee", args, dbg);
   if (!args.requireAtLeast(cx, "Debugger.addDebuggee", 1)) {
     return false;
   }
   Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
   if (!global) {
     return false;
   }
 
@@ -4387,17 +4284,20 @@ bool Debugger::CallData::addDebuggee() {
   RootedValue v(cx, ObjectValue(*global));
   if (!dbg->wrapDebuggeeValue(cx, &v)) {
     return false;
   }
   args.rval().set(v);
   return true;
 }
 
-bool Debugger::CallData::addAllGlobalsAsDebuggees() {
+/* static */
+bool Debugger::addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc,
+                                        Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "addAllGlobalsAsDebuggees", args, dbg);
   for (CompartmentsIter comp(cx->runtime()); !comp.done(); comp.next()) {
     if (comp == dbg->object->compartment()) {
       continue;
     }
     for (RealmsInCompartmentIter r(comp); !r.done(); r.next()) {
       if (r->creationOptions().invisibleToDebugger()) {
         continue;
       }
@@ -4411,17 +4311,20 @@ bool Debugger::CallData::addAllGlobalsAs
       }
     }
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool Debugger::CallData::removeDebuggee() {
+/* static */
+bool Debugger::removeDebuggee(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "removeDebuggee", args, dbg);
+
   if (!args.requireAtLeast(cx, "Debugger.removeDebuggee", 1)) {
     return false;
   }
   Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
   if (!global) {
     return false;
   }
 
@@ -4441,17 +4344,20 @@ bool Debugger::CallData::removeDebuggee(
       return false;
     }
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool Debugger::CallData::removeAllDebuggees() {
+/* static */
+bool Debugger::removeAllDebuggees(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg);
+
   ExecutionObservableRealms obs(cx);
 
   for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
     Rooted<GlobalObject*> global(cx, e.front());
     dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, &e,
                               FromSweep::No);
 
     // See note about adding to the observable set in removeDebuggee.
@@ -4463,29 +4369,34 @@ bool Debugger::CallData::removeAllDebugg
   if (!updateExecutionObservability(cx, obs, NotObserving)) {
     return false;
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool Debugger::CallData::hasDebuggee() {
+/* static */
+bool Debugger::hasDebuggee(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "hasDebuggee", args, dbg);
   if (!args.requireAtLeast(cx, "Debugger.hasDebuggee", 1)) {
     return false;
   }
   GlobalObject* global = dbg->unwrapDebuggeeArgument(cx, args[0]);
   if (!global) {
     return false;
   }
   args.rval().setBoolean(!!dbg->debuggees.lookup(global));
   return true;
 }
 
-bool Debugger::CallData::getDebuggees() {
+/* static */
+bool Debugger::getDebuggees(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "getDebuggees", args, dbg);
+
   // Obtain the list of debuggees before wrapping each debuggee, as a GC could
   // update the debuggees set while we are iterating it.
   unsigned count = dbg->debuggees.count();
   RootedValueVector debuggees(cx);
   if (!debuggees.resize(count)) {
     return false;
   }
   unsigned i = 0;
@@ -4509,17 +4420,20 @@ bool Debugger::CallData::getDebuggees() 
     }
     arrobj->setDenseElement(i, v);
   }
 
   args.rval().setObject(*arrobj);
   return true;
 }
 
-bool Debugger::CallData::getNewestFrame() {
+/* static */
+bool Debugger::getNewestFrame(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "getNewestFrame", args, dbg);
+
   // Since there may be multiple contexts, use AllFramesIter.
   for (AllFramesIter i(cx); !i.done(); ++i) {
     if (dbg->observesFrame(i)) {
       // Ensure that Ion frames are rematerialized. Only rematerialized
       // Ion frames may be used as AbstractFramePtrs.
       if (i.isIon() && !i.ensureHasRematerializedFrame(cx)) {
         return false;
       }
@@ -4531,17 +4445,19 @@ bool Debugger::CallData::getNewestFrame(
       }
       return dbg->getFrame(cx, iter, args.rval());
     }
   }
   args.rval().setNull();
   return true;
 }
 
-bool Debugger::CallData::clearAllBreakpoints() {
+/* static */
+bool Debugger::clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "clearAllBreakpoints", args, dbg);
   for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty();
        r.popFront()) {
     DebugScript::clearBreakpointsIn(cx->runtime()->defaultFreeOp(),
                                     r.front()->realm(), dbg, nullptr);
   }
   return true;
 }
 
@@ -5429,17 +5345,20 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
     }
 
     if (!wasmInstanceVector.append(instanceObject)) {
       oom = true;
     }
   }
 };
 
-bool Debugger::CallData::findScripts() {
+/* static */
+bool Debugger::findScripts(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "findScripts", args, dbg);
+
   ScriptQuery query(cx, dbg);
 
   if (args.length() >= 1) {
     RootedObject queryObject(cx, RequireObject(cx, args[0]));
     if (!queryObject || !query.parseQuery(queryObject)) {
       return false;
     }
   } else {
@@ -5613,17 +5532,20 @@ class MOZ_STACK_CLASS Debugger::SourceQu
 
 static inline DebuggerSourceReferent AsSourceReferent(JSObject* obj) {
   if (obj->is<ScriptSourceObject>()) {
     return AsVariant(&obj->as<ScriptSourceObject>());
   }
   return AsVariant(&obj->as<WasmInstanceObject>());
 }
 
-bool Debugger::CallData::findSources() {
+/* static */
+bool Debugger::findSources(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "findSources", args, dbg);
+
   SourceQuery query(cx, dbg);
   if (!query.findSources()) {
     return false;
   }
 
   Handle<SourceQuery::SourceSet> sources(query.foundSources());
 
   size_t resultLength = sources.count();
@@ -5826,17 +5748,19 @@ class MOZ_STACK_CLASS Debugger::ObjectQu
         return false;
       }
     }
 
     return true;
   }
 };
 
-bool Debugger::CallData::findObjects() {
+bool Debugger::findObjects(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "findObjects", args, dbg);
+
   ObjectQuery query(cx, dbg);
 
   if (args.length() >= 1) {
     RootedObject queryObject(cx, RequireObject(cx, args[0]));
     if (!queryObject || !query.parseQuery(queryObject)) {
       return false;
     }
   } else {
@@ -5862,17 +5786,20 @@ bool Debugger::CallData::findObjects() {
     }
     result->setDenseElement(i, debuggeeVal);
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-bool Debugger::CallData::findAllGlobals() {
+/* static */
+bool Debugger::findAllGlobals(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "findAllGlobals", args, dbg);
+
   RootedObjectVector globals(cx);
 
   {
     // Accumulate the list of globals before wrapping them, because
     // wrapping can GC and collect realms from under us, while iterating.
     JS::AutoCheckCannotGC nogc;
 
     for (RealmsIter r(cx->runtime()); !r.done(); r.next()) {
@@ -5920,17 +5847,20 @@ bool Debugger::CallData::findAllGlobals(
       return false;
     }
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-bool Debugger::CallData::findSourceURLs() {
+/* static */
+bool Debugger::findSourceURLs(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "findSourceURLs", args, dbg);
+
   RootedObject result(cx, NewDenseEmptyArray(cx));
   if (!result) {
     return false;
   }
 
   for (WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty();
        r.popFront()) {
     RootedObject holder(cx, r.front()->getSourceURLsHolder());
@@ -5950,17 +5880,20 @@ bool Debugger::CallData::findSourceURLs(
       }
     }
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-bool Debugger::CallData::makeGlobalObjectReference() {
+/* static */
+bool Debugger::makeGlobalObjectReference(JSContext* cx, unsigned argc,
+                                         Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "makeGlobalObjectReference", args, dbg);
   if (!args.requireAtLeast(cx, "Debugger.makeGlobalObjectReference", 1)) {
     return false;
   }
 
   Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
   if (!global) {
     return false;
   }
@@ -6054,17 +5987,18 @@ bool Debugger::recordReplayProcessKind(J
     }
     args.rval().setString(str);
   } else {
     args.rval().setUndefined();
   }
   return true;
 }
 
-bool Debugger::CallData::adoptDebuggeeValue() {
+bool Debugger::adoptDebuggeeValue(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "adoptDebuggeeValue", args, dbg);
   if (!args.requireAtLeast(cx, "Debugger.adoptDebuggeeValue", 1)) {
     return false;
   }
 
   RootedValue v(cx, args[0]);
   if (v.isObject()) {
     RootedObject obj(cx, &v.toObject());
     NativeObject* ndobj = ToNativeDebuggerObject(cx, &obj);
@@ -6107,17 +6041,18 @@ class DebuggerAdoptSourceMatcher {
       JS_ReportErrorASCII(
           cx_, "WasmInstance is in the same compartment as this debugger");
       return nullptr;
     }
     return dbg_->wrapWasmSource(cx_, wasmInstance);
   }
 };
 
-bool Debugger::CallData::adoptSource() {
+bool Debugger::adoptSource(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER(cx, argc, vp, "adoptSource", args, dbg);
   if (!args.requireAtLeast(cx, "Debugger.adoptSource", 1)) {
     return false;
   }
 
   RootedObject obj(cx, RequireObject(cx, args[0]));
   if (!obj) {
     return false;
   }
@@ -6142,53 +6077,57 @@ bool Debugger::CallData::adoptSource() {
     return false;
   }
 
   args.rval().setObject(*res);
   return true;
 }
 
 const JSPropertySpec Debugger::properties[] = {
-    JS_DEBUG_PSGS("onDebuggerStatement", getOnDebuggerStatement,
-                  setOnDebuggerStatement),
-    JS_DEBUG_PSGS("onExceptionUnwind", getOnExceptionUnwind,
-                  setOnExceptionUnwind),
-    JS_DEBUG_PSGS("onNewScript", getOnNewScript, setOnNewScript),
-    JS_DEBUG_PSGS("onNewPromise", getOnNewPromise, setOnNewPromise),
-    JS_DEBUG_PSGS("onPromiseSettled", getOnPromiseSettled, setOnPromiseSettled),
-    JS_DEBUG_PSGS("onEnterFrame", getOnEnterFrame, setOnEnterFrame),
-    JS_DEBUG_PSGS("onNativeCall", getOnNativeCall, setOnNativeCall),
-    JS_DEBUG_PSGS("onNewGlobalObject", getOnNewGlobalObject,
-                  setOnNewGlobalObject),
-    JS_DEBUG_PSGS("uncaughtExceptionHook", getUncaughtExceptionHook,
-                  setUncaughtExceptionHook),
-    JS_DEBUG_PSGS("allowUnobservedAsmJS", getAllowUnobservedAsmJS,
-                  setAllowUnobservedAsmJS),
-    JS_DEBUG_PSGS("collectCoverageInfo", getCollectCoverageInfo,
-                  setCollectCoverageInfo),
-    JS_DEBUG_PSG("memory", getMemory),
+    JS_PSGS("onDebuggerStatement", Debugger::getOnDebuggerStatement,
+            Debugger::setOnDebuggerStatement, 0),
+    JS_PSGS("onExceptionUnwind", Debugger::getOnExceptionUnwind,
+            Debugger::setOnExceptionUnwind, 0),
+    JS_PSGS("onNewScript", Debugger::getOnNewScript, Debugger::setOnNewScript,
+            0),
+    JS_PSGS("onNewPromise", Debugger::getOnNewPromise,
+            Debugger::setOnNewPromise, 0),
+    JS_PSGS("onPromiseSettled", Debugger::getOnPromiseSettled,
+            Debugger::setOnPromiseSettled, 0),
+    JS_PSGS("onEnterFrame", Debugger::getOnEnterFrame,
+            Debugger::setOnEnterFrame, 0),
+    JS_PSGS("onNewGlobalObject", Debugger::getOnNewGlobalObject,
+            Debugger::setOnNewGlobalObject, 0),
+    JS_PSGS("uncaughtExceptionHook", Debugger::getUncaughtExceptionHook,
+            Debugger::setUncaughtExceptionHook, 0),
+    JS_PSGS("allowUnobservedAsmJS", Debugger::getAllowUnobservedAsmJS,
+            Debugger::setAllowUnobservedAsmJS, 0),
+    JS_PSGS("collectCoverageInfo", Debugger::getCollectCoverageInfo,
+            Debugger::setCollectCoverageInfo, 0),
+    JS_PSG("memory", Debugger::getMemory, 0),
     JS_PS_END};
 
 const JSFunctionSpec Debugger::methods[] = {
-    JS_DEBUG_FN("addDebuggee", addDebuggee, 1),
-    JS_DEBUG_FN("addAllGlobalsAsDebuggees", addAllGlobalsAsDebuggees, 0),
-    JS_DEBUG_FN("removeDebuggee", removeDebuggee, 1),
-    JS_DEBUG_FN("removeAllDebuggees", removeAllDebuggees, 0),
-    JS_DEBUG_FN("hasDebuggee", hasDebuggee, 1),
-    JS_DEBUG_FN("getDebuggees", getDebuggees, 0),
-    JS_DEBUG_FN("getNewestFrame", getNewestFrame, 0),
-    JS_DEBUG_FN("clearAllBreakpoints", clearAllBreakpoints, 0),
-    JS_DEBUG_FN("findScripts", findScripts, 1),
-    JS_DEBUG_FN("findSources", findSources, 1),
-    JS_DEBUG_FN("findObjects", findObjects, 1),
-    JS_DEBUG_FN("findAllGlobals", findAllGlobals, 0),
-    JS_DEBUG_FN("findSourceURLs", findSourceURLs, 0),
-    JS_DEBUG_FN("makeGlobalObjectReference", makeGlobalObjectReference, 1),
-    JS_DEBUG_FN("adoptDebuggeeValue", adoptDebuggeeValue, 1),
-    JS_DEBUG_FN("adoptSource", adoptSource, 1),
+    JS_FN("addDebuggee", Debugger::addDebuggee, 1, 0),
+    JS_FN("addAllGlobalsAsDebuggees", Debugger::addAllGlobalsAsDebuggees, 0, 0),
+    JS_FN("removeDebuggee", Debugger::removeDebuggee, 1, 0),
+    JS_FN("removeAllDebuggees", Debugger::removeAllDebuggees, 0, 0),
+    JS_FN("hasDebuggee", Debugger::hasDebuggee, 1, 0),
+    JS_FN("getDebuggees", Debugger::getDebuggees, 0, 0),
+    JS_FN("getNewestFrame", Debugger::getNewestFrame, 0, 0),
+    JS_FN("clearAllBreakpoints", Debugger::clearAllBreakpoints, 0, 0),
+    JS_FN("findScripts", Debugger::findScripts, 1, 0),
+    JS_FN("findSources", Debugger::findSources, 1, 0),
+    JS_FN("findObjects", Debugger::findObjects, 1, 0),
+    JS_FN("findAllGlobals", Debugger::findAllGlobals, 0, 0),
+    JS_FN("findSourceURLs", Debugger::findSourceURLs, 0, 0),
+    JS_FN("makeGlobalObjectReference", Debugger::makeGlobalObjectReference, 1,
+          0),
+    JS_FN("adoptDebuggeeValue", Debugger::adoptDebuggeeValue, 1, 0),
+    JS_FN("adoptSource", Debugger::adoptSource, 1, 0),
     JS_FS_END};
 
 const JSFunctionSpec Debugger::static_methods[]{
     JS_FN("isCompilableUnit", Debugger::isCompilableUnit, 1, 0),
     JS_FN("recordReplayProcessKind", Debugger::recordReplayProcessKind, 0, 0),
     JS_FS_END};
 
 DebuggerScript* Debugger::newDebuggerScript(
--- a/js/src/debugger/Debugger.h
+++ b/js/src/debugger/Debugger.h
@@ -458,17 +458,16 @@ class Debugger : private mozilla::Linked
       JSContext* cx, JS::dbg::GarbageCollectionEvent::Ptr&& data);
 
  public:
   enum Hook {
     OnDebuggerStatement,
     OnExceptionUnwind,
     OnNewScript,
     OnEnterFrame,
-    OnNativeCall,
     OnNewGlobalObject,
     OnNewPromise,
     OnPromiseSettled,
     OnGarbageCollection,
     HookCount
   };
   enum {
     JSSLOT_DEBUG_PROTO_START,
@@ -836,27 +835,72 @@ class Debugger : private mozilla::Linked
 
  public:
   static const JSClass class_;
 
  private:
   template <typename F>
   void forEachWeakMap(const F& f);
 
-  static MOZ_MUST_USE bool getHookImpl(JSContext* cx, const CallArgs& args,
+  static MOZ_MUST_USE bool getHookImpl(JSContext* cx, CallArgs& args,
                                        Debugger& dbg, Hook which);
-  static MOZ_MUST_USE bool setHookImpl(JSContext* cx, const CallArgs& args,
+  static MOZ_MUST_USE bool setHookImpl(JSContext* cx, CallArgs& args,
                                        Debugger& dbg, Hook which);
 
+  static bool getOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
+  static bool setOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
+  static bool getOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
+  static bool setOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
+  static bool getOnNewScript(JSContext* cx, unsigned argc, Value* vp);
+  static bool setOnNewScript(JSContext* cx, unsigned argc, Value* vp);
+  static bool getOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
+  static bool setOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
+  static bool getOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
+  static bool setOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
+  static bool getOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
+  static bool setOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
+  static bool getOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
+  static bool setOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
+  static bool getUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
+  static bool setUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
+  static bool getAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
+  static bool setAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
+  static bool getCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
+  static bool setCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
+  static bool getMemory(JSContext* cx, unsigned argc, Value* vp);
+  static bool addDebuggee(JSContext* cx, unsigned argc, Value* vp);
+  static bool addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp);
+  static bool removeDebuggee(JSContext* cx, unsigned argc, Value* vp);
+  static bool removeAllDebuggees(JSContext* cx, unsigned argc, Value* vp);
+  static bool hasDebuggee(JSContext* cx, unsigned argc, Value* vp);
+  static bool getDebuggees(JSContext* cx, unsigned argc, Value* vp);
+  static bool getNewestFrame(JSContext* cx, unsigned argc, Value* vp);
+  static bool clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp);
+  static bool findScripts(JSContext* cx, unsigned argc, Value* vp);
+  static bool findSources(JSContext* cx, unsigned argc, Value* vp);
+  static bool findObjects(JSContext* cx, unsigned argc, Value* vp);
+  static bool findAllGlobals(JSContext* cx, unsigned argc, Value* vp);
+  static bool findSourceURLs(JSContext* cx, unsigned argc, Value* vp);
+  static bool makeGlobalObjectReference(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static bool setupTraceLoggerScriptCalls(JSContext* cx, unsigned argc,
+                                          Value* vp);
+  static bool drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc,
+                                          Value* vp);
+  static bool startTraceLogger(JSContext* cx, unsigned argc, Value* vp);
+  static bool endTraceLogger(JSContext* cx, unsigned argc, Value* vp);
   static bool isCompilableUnit(JSContext* cx, unsigned argc, Value* vp);
   static bool recordReplayProcessKind(JSContext* cx, unsigned argc, Value* vp);
+#ifdef NIGHTLY_BUILD
+  static bool setupTraceLogger(JSContext* cx, unsigned argc, Value* vp);
+  static bool drainTraceLogger(JSContext* cx, unsigned argc, Value* vp);
+#endif
+  static bool adoptDebuggeeValue(JSContext* cx, unsigned argc, Value* vp);
+  static bool adoptSource(JSContext* cx, unsigned argc, Value* vp);
   static bool construct(JSContext* cx, unsigned argc, Value* vp);
-
-  struct CallData;
-
   static const JSPropertySpec properties[];
   static const JSFunctionSpec methods[];
   static const JSFunctionSpec static_methods[];
 
   static void removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx,
                                                        AbstractFramePtr frame,
                                                        bool suspending = false);
   static bool updateExecutionObservabilityOfFrames(
@@ -893,19 +937,16 @@ class Debugger : private mozilla::Linked
   // Whether the Debugger instance needs to observe AOT-compiled asm.js
   // execution of its debuggees.
   IsObserving observesAsmJS() const;
 
   // Whether the Debugger instance needs to observe coverage of any JavaScript
   // execution.
   IsObserving observesCoverage() const;
 
-  // Whether the Debugger instance needs to observe native call invocations.
-  IsObserving observesNativeCalls() const;
-
  private:
   static MOZ_MUST_USE bool ensureExecutionObservabilityOfFrame(
       JSContext* cx, AbstractFramePtr frame);
   static MOZ_MUST_USE bool ensureExecutionObservabilityOfRealm(
       JSContext* cx, JS::Realm* realm);
 
   static bool hookObservesAllExecution(Hook which);
 
@@ -924,18 +965,16 @@ class Debugger : private mozilla::Linked
   template <typename HookIsEnabledFun /* bool (Debugger*) */,
             typename FireHookFun /* ResumeMode (Debugger*) */>
   static ResumeMode dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
                                  FireHookFun fireHook);
 
   ResumeMode fireDebuggerStatement(JSContext* cx, MutableHandleValue vp);
   ResumeMode fireExceptionUnwind(JSContext* cx, MutableHandleValue vp);
   ResumeMode fireEnterFrame(JSContext* cx, MutableHandleValue vp);
-  ResumeMode fireNativeCall(JSContext* cx, const CallArgs& args,
-                            CallReason reason, MutableHandleValue vp);
   ResumeMode fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global,
                                  MutableHandleValue vp);
   ResumeMode firePromiseHook(JSContext* cx, Hook hook, HandleObject promise,
                              MutableHandleValue vp);
 
   DebuggerScript* newVariantWrapper(JSContext* cx,
                                     Handle<DebuggerScriptReferent> referent) {
     return newDebuggerScript(cx, referent);
@@ -1410,26 +1449,16 @@ bool ParseEvalOptions(JSContext* cx, Han
 Result<Completion> DebuggerGenericEval(
     JSContext* cx, const mozilla::Range<const char16_t> chars,
     HandleObject bindings, const EvalOptions& options, Debugger* dbg,
     HandleObject envArg, FrameIter* iter);
 
 bool ParseResumptionValue(JSContext* cx, HandleValue rval,
                           ResumeMode& resumeMode, MutableHandleValue vp);
 
-#define JS_DEBUG_PSG(Name, Getter)                        \
-  JS_PSG(Name, CallData::ToNative<&CallData::Getter>, 0)
-
-#define JS_DEBUG_PSGS(Name, Getter, Setter)               \
-  JS_PSGS(Name, CallData::ToNative<&CallData::Getter>,  \
-          CallData::ToNative<&CallData::Setter>, 0)
-
-#define JS_DEBUG_FN(Name, Method, NumArgs)                \
-  JS_FN(Name, CallData::ToNative<&CallData::Method>, NumArgs, 0)
-
 } /* namespace js */
 
 namespace JS {
 
 template <>
 struct DeletePolicy<js::Debugger>
     : public js::GCManagedDeletePolicy<js::Debugger> {};
 
--- a/js/src/debugger/DebuggerMemory.cpp
+++ b/js/src/debugger/DebuggerMemory.cpp
@@ -64,102 +64,79 @@ bool DebuggerMemory::construct(JSContext
                             "Debugger.Source");
   return false;
 }
 
 /* static */ const JSClass DebuggerMemory::class_ = {
     "Memory", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_COUNT)};
 
 /* static */
-DebuggerMemory* DebuggerMemory::checkThis(JSContext* cx, CallArgs& args) {
+DebuggerMemory* DebuggerMemory::checkThis(JSContext* cx, CallArgs& args,
+                                          const char* fnName) {
   const Value& thisValue = args.thisv();
 
   if (!thisValue.isObject()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_OBJECT_REQUIRED,
                               InformalValueTypeName(thisValue));
     return nullptr;
   }
 
   JSObject& thisObject = thisValue.toObject();
   if (!thisObject.is<DebuggerMemory>()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_INCOMPATIBLE_PROTO, class_.name, "method",
+                              JSMSG_INCOMPATIBLE_PROTO, class_.name, fnName,
                               thisObject.getClass()->name);
     return nullptr;
   }
 
   // Check for Debugger.Memory.prototype, which has the same class as
   // Debugger.Memory instances, however doesn't actually represent an instance
   // of Debugger.Memory. It is the only object that is<DebuggerMemory>() but
   // doesn't have a Debugger instance.
   if (thisObject.as<DebuggerMemory>()
           .getReservedSlot(JSSLOT_DEBUGGER)
           .isUndefined()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_INCOMPATIBLE_PROTO, class_.name, "method",
+                              JSMSG_INCOMPATIBLE_PROTO, class_.name, fnName,
                               "prototype object");
     return nullptr;
   }
 
   return &thisObject.as<DebuggerMemory>();
 }
 
-struct MOZ_STACK_CLASS DebuggerMemory::CallData {
-  JSContext* cx;
-  const CallArgs& args;
-
-  Handle<DebuggerMemory*> memory;
-
-  CallData(JSContext* cx, const CallArgs& args, Handle<DebuggerMemory*> memory)
-      : cx(cx), args(args), memory(memory) {}
-
-  // Accessor properties of Debugger.Memory.prototype.
-
-  bool setTrackingAllocationSites();
-  bool getTrackingAllocationSites();
-  bool setMaxAllocationsLogLength();
-  bool getMaxAllocationsLogLength();
-  bool setAllocationSamplingProbability();
-  bool getAllocationSamplingProbability();
-  bool getAllocationsLogOverflowed();
-  bool getOnGarbageCollection();
-  bool setOnGarbageCollection();
-
-  // Function properties of Debugger.Memory.prototype.
+/**
+ * Get the |DebuggerMemory*| from the current this value and handle any errors
+ * that might occur therein.
+ *
+ * These parameters must already exist when calling this macro:
+ * - JSContext* cx
+ * - unsigned argc
+ * - Value* vp
+ * - const char* fnName
+ * These parameters will be defined after calling this macro:
+ * - CallArgs args
+ * - DebuggerMemory* memory (will be non-null)
+ */
+#define THIS_DEBUGGER_MEMORY(cx, argc, vp, fnName, args, memory)   \
+  CallArgs args = CallArgsFromVp(argc, vp);                        \
+  Rooted<DebuggerMemory*> memory(cx, checkThis(cx, args, fnName)); \
+  if (!memory) return false
 
-  bool takeCensus();
-  bool drainAllocationsLog();
-
-  using Method = bool (CallData::*)();
-
-  template <Method MyMethod>
-  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
-};
-
-template <DebuggerMemory::CallData::Method MyMethod>
-/* static */
-bool DebuggerMemory::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  Rooted<DebuggerMemory*> memory(cx, DebuggerMemory::checkThis(cx, args));
-  if (!memory) {
-    return false;
-  }
-
-  CallData data(cx, args, memory);
-  return (data.*MyMethod)();
-}
-
-static bool undefined(const CallArgs& args) {
+static bool undefined(CallArgs& args) {
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerMemory::CallData::setTrackingAllocationSites() {
+/* static */
+bool DebuggerMemory::setTrackingAllocationSites(JSContext* cx, unsigned argc,
+                                                Value* vp) {
+  THIS_DEBUGGER_MEMORY(cx, argc, vp, "(set trackingAllocationSites)", args,
+                       memory);
   if (!args.requireAtLeast(cx, "(set trackingAllocationSites)", 1)) {
     return false;
   }
 
   Debugger* dbg = memory->getDebugger();
   bool enabling = ToBoolean(args[0]);
 
   if (enabling == dbg->trackingAllocationSites) {
@@ -175,22 +152,29 @@ bool DebuggerMemory::CallData::setTracki
     }
   } else {
     dbg->removeAllocationsTrackingForAllDebuggees();
   }
 
   return undefined(args);
 }
 
-bool DebuggerMemory::CallData::getTrackingAllocationSites() {
+/* static */
+bool DebuggerMemory::getTrackingAllocationSites(JSContext* cx, unsigned argc,
+                                                Value* vp) {
+  THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get trackingAllocationSites)", args,
+                       memory);
   args.rval().setBoolean(memory->getDebugger()->trackingAllocationSites);
   return true;
 }
 
-bool DebuggerMemory::CallData::drainAllocationsLog() {
+/* static */
+bool DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc,
+                                         Value* vp) {
+  THIS_DEBUGGER_MEMORY(cx, argc, vp, "drainAllocationsLog", args, memory);
   Debugger* dbg = memory->getDebugger();
 
   if (!dbg->trackingAllocationSites) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_NOT_TRACKING_ALLOCATIONS,
                               "drainAllocationsLog");
     return false;
   }
@@ -263,22 +247,30 @@ bool DebuggerMemory::CallData::drainAllo
     dbg->allocationsLog.popFront();
   }
 
   dbg->allocationsLogOverflowed = false;
   args.rval().setObject(*result);
   return true;
 }
 
-bool DebuggerMemory::CallData::getMaxAllocationsLogLength() {
+/* static */
+bool DebuggerMemory::getMaxAllocationsLogLength(JSContext* cx, unsigned argc,
+                                                Value* vp) {
+  THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get maxAllocationsLogLength)", args,
+                       memory);
   args.rval().setInt32(memory->getDebugger()->maxAllocationsLogLength);
   return true;
 }
 
-bool DebuggerMemory::CallData::setMaxAllocationsLogLength() {
+/* static */
+bool DebuggerMemory::setMaxAllocationsLogLength(JSContext* cx, unsigned argc,
+                                                Value* vp) {
+  THIS_DEBUGGER_MEMORY(cx, argc, vp, "(set maxAllocationsLogLength)", args,
+                       memory);
   if (!args.requireAtLeast(cx, "(set maxAllocationsLogLength)", 1)) {
     return false;
   }
 
   int32_t max;
   if (!ToInt32(cx, args[0], &max)) {
     return false;
   }
@@ -296,22 +288,32 @@ bool DebuggerMemory::CallData::setMaxAll
   while (dbg->allocationsLog.length() > dbg->maxAllocationsLogLength) {
     dbg->allocationsLog.popFront();
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerMemory::CallData::getAllocationSamplingProbability() {
+/* static */
+bool DebuggerMemory::getAllocationSamplingProbability(JSContext* cx,
+                                                      unsigned argc,
+                                                      Value* vp) {
+  THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get allocationSamplingProbability)",
+                       args, memory);
   args.rval().setDouble(memory->getDebugger()->allocationSamplingProbability);
   return true;
 }
 
-bool DebuggerMemory::CallData::setAllocationSamplingProbability() {
+/* static */
+bool DebuggerMemory::setAllocationSamplingProbability(JSContext* cx,
+                                                      unsigned argc,
+                                                      Value* vp) {
+  THIS_DEBUGGER_MEMORY(cx, argc, vp, "(set allocationSamplingProbability)",
+                       args, memory);
   if (!args.requireAtLeast(cx, "(set allocationSamplingProbability)", 1)) {
     return false;
   }
 
   double probability;
   if (!ToNumber(cx, args[0], &probability)) {
     return false;
   }
@@ -337,27 +339,37 @@ bool DebuggerMemory::CallData::setAlloca
       }
     }
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerMemory::CallData::getAllocationsLogOverflowed() {
+/* static */
+bool DebuggerMemory::getAllocationsLogOverflowed(JSContext* cx, unsigned argc,
+                                                 Value* vp) {
+  THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get allocationsLogOverflowed)", args,
+                       memory);
   args.rval().setBoolean(memory->getDebugger()->allocationsLogOverflowed);
   return true;
 }
 
-bool DebuggerMemory::CallData::getOnGarbageCollection() {
+/* static */
+bool DebuggerMemory::getOnGarbageCollection(JSContext* cx, unsigned argc,
+                                            Value* vp) {
+  THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get onGarbageCollection)", args, memory);
   return Debugger::getHookImpl(cx, args, *memory->getDebugger(),
                                Debugger::OnGarbageCollection);
 }
 
-bool DebuggerMemory::CallData::setOnGarbageCollection() {
+/* static */
+bool DebuggerMemory::setOnGarbageCollection(JSContext* cx, unsigned argc,
+                                            Value* vp) {
+  THIS_DEBUGGER_MEMORY(cx, argc, vp, "(set onGarbageCollection)", args, memory);
   return Debugger::setHookImpl(cx, args, *memory->getDebugger(),
                                Debugger::OnGarbageCollection);
 }
 
 /* Debugger.Memory.prototype.takeCensus */
 
 JS_PUBLIC_API void JS::dbg::SetDebuggerMallocSizeOf(
     JSContext* cx, mozilla::MallocSizeOf mallocSizeOf) {
@@ -379,17 +391,20 @@ using JS::ubi::CountTypePtr;
 //    use that to build a CountType tree.
 //
 // 2) We create a count node for the root of our CountType tree, and then walk
 //    the heap, counting each node we find, expanding our tree of counts as we
 //    go.
 //
 // 3) We walk the tree of counts and produce JavaScript objects reporting the
 //    accumulated results.
-bool DebuggerMemory::CallData::takeCensus() {
+bool DebuggerMemory::takeCensus(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_MEMORY(cx, argc, vp, "Debugger.Memory.prototype.census", args,
+                       memory);
+
   Census census(cx);
   CountTypePtr rootType;
 
   RootedObject options(cx);
   if (args.get(0).isObject()) {
     options = &args[0].toObject();
   }
 
@@ -434,23 +449,23 @@ bool DebuggerMemory::CallData::takeCensu
   }
 
   return handler.report(cx, args.rval());
 }
 
 /* Debugger.Memory property and method tables. */
 
 /* static */ const JSPropertySpec DebuggerMemory::properties[] = {
-    JS_DEBUG_PSGS("trackingAllocationSites", getTrackingAllocationSites,
-                  setTrackingAllocationSites),
-    JS_DEBUG_PSGS("maxAllocationsLogLength", getMaxAllocationsLogLength,
-                  setMaxAllocationsLogLength),
-    JS_DEBUG_PSGS("allocationSamplingProbability",
-                  getAllocationSamplingProbability,
-                  setAllocationSamplingProbability),
-    JS_DEBUG_PSG("allocationsLogOverflowed", getAllocationsLogOverflowed),
-    JS_DEBUG_PSGS("onGarbageCollection", getOnGarbageCollection,
-                  setOnGarbageCollection),
+    JS_PSGS("trackingAllocationSites", getTrackingAllocationSites,
+            setTrackingAllocationSites, 0),
+    JS_PSGS("maxAllocationsLogLength", getMaxAllocationsLogLength,
+            setMaxAllocationsLogLength, 0),
+    JS_PSGS("allocationSamplingProbability", getAllocationSamplingProbability,
+            setAllocationSamplingProbability, 0),
+    JS_PSG("allocationsLogOverflowed", getAllocationsLogOverflowed, 0),
+
+    JS_PSGS("onGarbageCollection", getOnGarbageCollection,
+            setOnGarbageCollection, 0),
     JS_PS_END};
 
 /* static */ const JSFunctionSpec DebuggerMemory::methods[] = {
-    JS_DEBUG_FN("drainAllocationsLog", drainAllocationsLog, 0),
-    JS_DEBUG_FN("takeCensus", takeCensus, 0), JS_FS_END};
+    JS_FN("drainAllocationsLog", DebuggerMemory::drainAllocationsLog, 0, 0),
+    JS_FN("takeCensus", takeCensus, 0, 0), JS_FS_END};
--- a/js/src/debugger/DebuggerMemory.h
+++ b/js/src/debugger/DebuggerMemory.h
@@ -14,28 +14,52 @@
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 
 namespace js {
 
 class DebuggerMemory : public NativeObject {
   friend class Debugger;
 
-  static DebuggerMemory* checkThis(JSContext* cx, CallArgs& args);
+  static DebuggerMemory* checkThis(JSContext* cx, CallArgs& args,
+                                   const char* fnName);
 
   Debugger* getDebugger();
 
  public:
   static DebuggerMemory* create(JSContext* cx, Debugger* dbg);
 
   enum { JSSLOT_DEBUGGER, JSSLOT_COUNT };
 
   static bool construct(JSContext* cx, unsigned argc, Value* vp);
   static const JSClass class_;
   static const JSPropertySpec properties[];
   static const JSFunctionSpec methods[];
 
-  struct CallData;
+  // Accessor properties of Debugger.Memory.prototype.
+
+  static bool setTrackingAllocationSites(JSContext* cx, unsigned argc,
+                                         Value* vp);
+  static bool getTrackingAllocationSites(JSContext* cx, unsigned argc,
+                                         Value* vp);
+  static bool setMaxAllocationsLogLength(JSContext* cx, unsigned argc,
+                                         Value* vp);
+  static bool getMaxAllocationsLogLength(JSContext* cx, unsigned argc,
+                                         Value* vp);
+  static bool setAllocationSamplingProbability(JSContext* cx, unsigned argc,
+                                               Value* vp);
+  static bool getAllocationSamplingProbability(JSContext* cx, unsigned argc,
+                                               Value* vp);
+  static bool getAllocationsLogOverflowed(JSContext* cx, unsigned argc,
+                                          Value* vp);
+
+  static bool getOnGarbageCollection(JSContext* cx, unsigned argc, Value* vp);
+  static bool setOnGarbageCollection(JSContext* cx, unsigned argc, Value* vp);
+
+  // Function properties of Debugger.Memory.prototype.
+
+  static bool takeCensus(JSContext* cx, unsigned argc, Value* vp);
+  static bool drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp);
 };
 
 } /* namespace js */
 
 #endif /* debugger_DebuggerMemory_h */
--- a/js/src/debugger/Environment.cpp
+++ b/js/src/debugger/Environment.cpp
@@ -78,84 +78,61 @@ void DebuggerEnvironment::trace(JSTracer
     TraceManuallyBarrieredCrossCompartmentEdge(
         trc, static_cast<JSObject*>(this), &referent,
         "Debugger.Environment referent");
     setPrivateUnbarriered(referent);
   }
 }
 
 static DebuggerEnvironment* DebuggerEnvironment_checkThis(
-    JSContext* cx, const CallArgs& args) {
+    JSContext* cx, const CallArgs& args, const char* fnname,
+    bool requireDebuggee) {
   JSObject* thisobj = RequireObject(cx, args.thisv());
   if (!thisobj) {
     return nullptr;
   }
   if (thisobj->getClass() != &DebuggerEnvironment::class_) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Environment",
-                              "method", thisobj->getClass()->name);
+                              fnname, thisobj->getClass()->name);
     return nullptr;
   }
 
   // Forbid Debugger.Environment.prototype, which is of class
   // DebuggerEnvironment::class_ but isn't a real working Debugger.Environment.
   // The prototype object is distinguished by having no referent.
   DebuggerEnvironment* nthisobj = &thisobj->as<DebuggerEnvironment>();
   if (!nthisobj->getPrivate()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Environment",
-                              "method", "prototype object");
+                              fnname, "prototype object");
     return nullptr;
   }
 
+  // Forbid access to Debugger.Environment objects that are not debuggee
+  // environments.
+  if (requireDebuggee) {
+    Rooted<Env*> env(cx, static_cast<Env*>(nthisobj->getPrivate()));
+    if (!Debugger::fromChildJSObject(nthisobj)->observesGlobal(
+            &env->nonCCWGlobal())) {
+      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                JSMSG_DEBUG_NOT_DEBUGGEE,
+                                "Debugger.Environment", "environment");
+      return nullptr;
+    }
+  }
+
   return nthisobj;
 }
 
-struct MOZ_STACK_CLASS DebuggerEnvironment::CallData {
-  JSContext* cx;
-  const CallArgs& args;
-
-  HandleDebuggerEnvironment environment;
-
-  CallData(JSContext* cx, const CallArgs& args, HandleDebuggerEnvironment env)
-      : cx(cx), args(args), environment(env) {}
-
-  bool typeGetter();
-  bool scopeKindGetter();
-  bool parentGetter();
-  bool objectGetter();
-  bool calleeGetter();
-  bool inspectableGetter();
-  bool optimizedOutGetter();
-
-  bool namesMethod();
-  bool findMethod();
-  bool getVariableMethod();
-  bool setVariableMethod();
-
-  using Method = bool (CallData::*)();
-
-  template <Method MyMethod>
-  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
-};
-
-template <DebuggerEnvironment::CallData::Method MyMethod>
-/* static */
-bool DebuggerEnvironment::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  RootedDebuggerEnvironment environment(cx,
-      DebuggerEnvironment_checkThis(cx, args));
-  if (!environment) {
-    return false;
-  }
-
-  CallData data(cx, args, environment);
-  return (data.*MyMethod)();
-}
+#define THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, fnname, args, environment) \
+  CallArgs args = CallArgsFromVp(argc, vp);                                \
+  Rooted<DebuggerEnvironment*> environment(                                \
+      cx, DebuggerEnvironment_checkThis(cx, args, fnname, false));         \
+  if (!environment) return false;
 
 /* static */
 bool DebuggerEnvironment::construct(JSContext* cx, unsigned argc, Value* vp) {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                             "Debugger.Environment");
   return false;
 }
 
@@ -165,17 +142,19 @@ static bool IsDeclarative(Env* env) {
 }
 
 template <typename T>
 static bool IsDebugEnvironmentWrapper(Env* env) {
   return env->is<DebugEnvironmentProxy>() &&
          env->as<DebugEnvironmentProxy>().environment().is<T>();
 }
 
-bool DebuggerEnvironment::CallData::typeGetter() {
+bool DebuggerEnvironment::typeGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get type", args, environment);
+
   if (!environment->requireDebuggee(cx)) {
     return false;
   }
 
   DebuggerEnvironmentType type = environment->type();
 
   const char* s;
   switch (type) {
@@ -194,17 +173,20 @@ bool DebuggerEnvironment::CallData::type
   if (!str) {
     return false;
   }
 
   args.rval().setString(str);
   return true;
 }
 
-bool DebuggerEnvironment::CallData::scopeKindGetter() {
+bool DebuggerEnvironment::scopeKindGetter(JSContext* cx, unsigned argc,
+                                          Value* vp) {
+  THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get scopeKind", args, environment);
+
   if (!environment->requireDebuggee(cx)) {
     return false;
   }
 
   Maybe<ScopeKind> kind = environment->scopeKind();
   if (kind.isSome()) {
     const char* s = ScopeKindString(*kind);
     JSAtom* str = Atomize(cx, s, strlen(s), PinAtom);
@@ -214,31 +196,39 @@ bool DebuggerEnvironment::CallData::scop
     args.rval().setString(str);
   } else {
     args.rval().setNull();
   }
 
   return true;
 }
 
-bool DebuggerEnvironment::CallData::parentGetter() {
+/* static */
+bool DebuggerEnvironment::parentGetter(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get type", args, environment);
+
   if (!environment->requireDebuggee(cx)) {
     return false;
   }
 
   RootedDebuggerEnvironment result(cx);
   if (!environment->getParent(cx, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
-bool DebuggerEnvironment::CallData::objectGetter() {
+/* static */
+bool DebuggerEnvironment::objectGetter(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get type", args, environment);
+
   if (!environment->requireDebuggee(cx)) {
     return false;
   }
 
   if (environment->type() == DebuggerEnvironmentType::Declarative) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_DEBUG_NO_ENV_OBJECT);
     return false;
@@ -248,41 +238,57 @@ bool DebuggerEnvironment::CallData::obje
   if (!environment->getObject(cx, &result)) {
     return false;
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-bool DebuggerEnvironment::CallData::calleeGetter() {
+/* static */
+bool DebuggerEnvironment::calleeGetter(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get callee", args, environment);
+
   if (!environment->requireDebuggee(cx)) {
     return false;
   }
 
   RootedDebuggerObject result(cx);
   if (!environment->getCallee(cx, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
-bool DebuggerEnvironment::CallData::inspectableGetter() {
+/* static */
+bool DebuggerEnvironment::inspectableGetter(JSContext* cx, unsigned argc,
+                                            Value* vp) {
+  THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get inspectable", args, environment);
+
   args.rval().setBoolean(environment->isDebuggee());
   return true;
 }
 
-bool DebuggerEnvironment::CallData::optimizedOutGetter() {
+/* static */
+bool DebuggerEnvironment::optimizedOutGetter(JSContext* cx, unsigned argc,
+                                             Value* vp) {
+  THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "get optimizedOut", args,
+                            environment);
+
   args.rval().setBoolean(environment->isOptimized());
   return true;
 }
 
-bool DebuggerEnvironment::CallData::namesMethod() {
+/* static */
+bool DebuggerEnvironment::namesMethod(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "names", args, environment);
+
   if (!environment->requireDebuggee(cx)) {
     return false;
   }
 
   Rooted<IdVector> ids(cx, IdVector(cx));
   if (!DebuggerEnvironment::getNames(cx, environment, &ids)) {
     return false;
   }
@@ -291,17 +297,19 @@ bool DebuggerEnvironment::CallData::name
   if (!obj) {
     return false;
   }
 
   args.rval().setObject(*obj);
   return true;
 }
 
-bool DebuggerEnvironment::CallData::findMethod() {
+/* static */
+bool DebuggerEnvironment::findMethod(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "find", args, environment);
   if (!args.requireAtLeast(cx, "Debugger.Environment.find", 1)) {
     return false;
   }
 
   if (!environment->requireDebuggee(cx)) {
     return false;
   }
 
@@ -314,34 +322,40 @@ bool DebuggerEnvironment::CallData::find
   if (!DebuggerEnvironment::find(cx, environment, id, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
-bool DebuggerEnvironment::CallData::getVariableMethod() {
+/* static */
+bool DebuggerEnvironment::getVariableMethod(JSContext* cx, unsigned argc,
+                                            Value* vp) {
+  THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "getVariable", args, environment);
   if (!args.requireAtLeast(cx, "Debugger.Environment.getVariable", 1)) {
     return false;
   }
 
   if (!environment->requireDebuggee(cx)) {
     return false;
   }
 
   RootedId id(cx);
   if (!ValueToIdentifier(cx, args[0], &id)) {
     return false;
   }
 
   return DebuggerEnvironment::getVariable(cx, environment, id, args.rval());
 }
 
-bool DebuggerEnvironment::CallData::setVariableMethod() {
+/* static */
+bool DebuggerEnvironment::setVariableMethod(JSContext* cx, unsigned argc,
+                                            Value* vp) {
+  THIS_DEBUGGER_ENVIRONMENT(cx, argc, vp, "setVariable", args, environment);
   if (!args.requireAtLeast(cx, "Debugger.Environment.setVariable", 2)) {
     return false;
   }
 
   if (!environment->requireDebuggee(cx)) {
     return false;
   }
 
@@ -366,30 +380,30 @@ bool DebuggerEnvironment::requireDebugge
 
     return false;
   }
 
   return true;
 }
 
 const JSPropertySpec DebuggerEnvironment::properties_[] = {
-    JS_DEBUG_PSG("type", typeGetter),
-    JS_DEBUG_PSG("scopeKind", scopeKindGetter),
-    JS_DEBUG_PSG("parent", parentGetter),
-    JS_DEBUG_PSG("object", objectGetter),
-    JS_DEBUG_PSG("callee", calleeGetter),
-    JS_DEBUG_PSG("inspectable", inspectableGetter),
-    JS_DEBUG_PSG("optimizedOut", optimizedOutGetter),
+    JS_PSG("type", DebuggerEnvironment::typeGetter, 0),
+    JS_PSG("scopeKind", DebuggerEnvironment::scopeKindGetter, 0),
+    JS_PSG("parent", DebuggerEnvironment::parentGetter, 0),
+    JS_PSG("object", DebuggerEnvironment::objectGetter, 0),
+    JS_PSG("callee", DebuggerEnvironment::calleeGetter, 0),
+    JS_PSG("inspectable", DebuggerEnvironment::inspectableGetter, 0),
+    JS_PSG("optimizedOut", DebuggerEnvironment::optimizedOutGetter, 0),
     JS_PS_END};
 
 const JSFunctionSpec DebuggerEnvironment::methods_[] = {
-    JS_DEBUG_FN("names", namesMethod, 0),
-    JS_DEBUG_FN("find", findMethod, 1),
-    JS_DEBUG_FN("getVariable", getVariableMethod, 1),
-    JS_DEBUG_FN("setVariable", setVariableMethod, 2),
+    JS_FN("names", DebuggerEnvironment::namesMethod, 0, 0),
+    JS_FN("find", DebuggerEnvironment::findMethod, 1, 0),
+    JS_FN("getVariable", DebuggerEnvironment::getVariableMethod, 1, 0),
+    JS_FN("setVariable", DebuggerEnvironment::setVariableMethod, 2, 0),
     JS_FS_END};
 
 /* static */
 NativeObject* DebuggerEnvironment::initClass(JSContext* cx,
                                              Handle<GlobalObject*> global,
                                              HandleObject dbgCtor) {
   return InitClass(cx, dbgCtor, nullptr, &DebuggerEnvironment::class_,
                    construct, 0, properties_, methods_, nullptr, nullptr);
--- a/js/src/debugger/Environment.h
+++ b/js/src/debugger/Environment.h
@@ -83,14 +83,33 @@ class DebuggerEnvironment : public Nativ
   }
 
   Debugger* owner() const;
 
   bool requireDebuggee(JSContext* cx) const;
 
   static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
 
-  struct CallData;
+  static MOZ_MUST_USE bool typeGetter(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool scopeKindGetter(JSContext* cx, unsigned argc,
+                                           Value* vp);
+  static MOZ_MUST_USE bool parentGetter(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static MOZ_MUST_USE bool objectGetter(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static MOZ_MUST_USE bool calleeGetter(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static MOZ_MUST_USE bool inspectableGetter(JSContext* cx, unsigned argc,
+                                             Value* vp);
+  static MOZ_MUST_USE bool optimizedOutGetter(JSContext* cx, unsigned argc,
+                                              Value* vp);
+
+  static MOZ_MUST_USE bool namesMethod(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool findMethod(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool getVariableMethod(JSContext* cx, unsigned argc,
+                                             Value* vp);
+  static MOZ_MUST_USE bool setVariableMethod(JSContext* cx, unsigned argc,
+                                             Value* vp);
 };
 
 } /* namespace js */
 
 #endif /* debugger_Environment_h */
--- a/js/src/debugger/Frame.cpp
+++ b/js/src/debugger/Frame.cpp
@@ -908,21 +908,16 @@ Result<Completion> js::DebuggerGenericEv
     RootedObject newEnv(cx);
     if (!CreateObjectsForEnvironmentChain(cx, envChain, env, &newEnv)) {
       return cx->alreadyReportedError();
     }
 
     env = newEnv;
   }
 
-  // Note whether we are in an evaluation that might invoke the OnNativeCall
-  // hook, so that the JITs will be disabled.
-  AutoNoteDebuggerEvaluationWithOnNativeCallHook noteEvaluation(
-      cx, dbg->observesNativeCalls() ? dbg : nullptr);
-
   // Run the code and produce the completion value.
   LeaveDebuggeeNoExecute nnx(cx);
   RootedValue rval(cx);
   AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr();
 
   bool ok = EvaluateInEnv(
       cx, env, frame, chars,
       options.filename() ? options.filename() : "debugger eval code",
@@ -1091,106 +1086,73 @@ void DebuggerFrame::trace(JSTracer* trc)
   }
 
   if (hasGenerator()) {
     generatorInfo()->trace(trc, *this);
   }
 }
 
 /* static */
-DebuggerFrame* DebuggerFrame::check(JSContext* cx, HandleValue thisv,
-                                    bool checkLive) {
-  JSObject* thisobj = RequireObject(cx, thisv);
+DebuggerFrame* DebuggerFrame::checkThis(JSContext* cx, const CallArgs& args,
+                                        const char* fnname, bool checkLive) {
+  JSObject* thisobj = RequireObject(cx, args.thisv());
   if (!thisobj) {
     return nullptr;
   }
   if (thisobj->getClass() != &class_) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Frame",
-                              "method", thisobj->getClass()->name);
+                              fnname, thisobj->getClass()->name);
     return nullptr;
   }
 
   RootedDebuggerFrame frame(cx, &thisobj->as<DebuggerFrame>());
 
   // Forbid Debugger.Frame.prototype, which is of class DebuggerFrame::class_
   // but isn't really a working Debugger.Frame object. The prototype object
   // is distinguished by having a nullptr private value. Also, forbid popped
   // frames.
   if (!frame->getPrivate() &&
       frame->getReservedSlot(OWNER_SLOT).isUndefined()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Frame",
-                              "method", "prototype object");
+                              fnname, "prototype object");
     return nullptr;
   }
 
   if (checkLive) {
     if (!frame->requireLive(cx)) {
       return nullptr;
     }
   }
 
   return frame;
 }
 
-struct MOZ_STACK_CLASS DebuggerFrame::CallData {
-  JSContext* cx;
-  const CallArgs& args;
-
-  HandleDebuggerFrame frame;
-
-  CallData(JSContext* cx, const CallArgs& args, HandleDebuggerFrame frame)
-      : cx(cx), args(args), frame(frame) {}
+/*
+ * Methods can use THIS_DEBUGGER_FRAME to check that `this` is a Debugger.Frame
+ * object and get it in a local Rooted.
+ *
+ * Methods that need the AbstractFramePtr should use THIS_FRAME.
+ */
+#define THIS_DEBUGGER_FRAME(cx, argc, vp, fnname, args, frame)                 \
+  CallArgs args = CallArgsFromVp(argc, vp);                                    \
+  RootedDebuggerFrame frame(cx,                                                \
+                            DebuggerFrame::checkThis(cx, args, fnname, true)); \
+  if (!frame) return false;
 
-  bool argumentsGetter();
-  bool calleeGetter();
-  bool constructingGetter();
-  bool environmentGetter();
-  bool generatorGetter();
-  bool liveGetter();
-  bool offsetGetter();
-  bool olderGetter();
-  bool getScript();
-  bool thisGetter();
-  bool typeGetter();
-  bool implementationGetter();
-  bool onStepGetter();
-  bool onStepSetter();
-  bool onPopGetter();
-  bool onPopSetter();
-  bool evalMethod();
-  bool evalWithBindingsMethod();
-
-  using Method = bool (CallData::*)();
+#define THIS_FRAME(cx, argc, vp, fnname, args, thisobj, iter, frame) \
+  THIS_DEBUGGER_FRAME(cx, argc, vp, fnname, args, thisobj);          \
+  FrameIter iter(*thisobj->frameIterData());                         \
+  AbstractFramePtr frame = iter.abstractFramePtr()
 
-  template <Method MyMethod>
-  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
-};
-
-template <DebuggerFrame::CallData::Method MyMethod>
 /* static */
-bool DebuggerFrame::CallData::ToNative(JSContext* cx, unsigned argc,
-                                       Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
+bool DebuggerFrame::typeGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get type", args, frame);
 
-  // All accessors/methods require a live frame, except for the live getter.
-  bool checkLive = MyMethod != &CallData::liveGetter;
-
-  RootedDebuggerFrame frame(cx, DebuggerFrame::check(cx, args.thisv(),
-                                                     checkLive));
-  if (!frame) {
-    return false;
-  }
-
-  CallData data(cx, args, frame);
-  return (data.*MyMethod)();
-}
-
-bool DebuggerFrame::CallData::typeGetter() {
   DebuggerFrameType type = DebuggerFrame::getType(frame);
 
   JSString* str;
   switch (type) {
     case DebuggerFrameType::Eval:
       str = cx->names().eval;
       break;
     case DebuggerFrameType::Global:
@@ -1208,17 +1170,21 @@ bool DebuggerFrame::CallData::typeGetter
     default:
       MOZ_CRASH("bad DebuggerFrameType value");
   }
 
   args.rval().setString(str);
   return true;
 }
 
-bool DebuggerFrame::CallData::implementationGetter() {
+/* static */
+bool DebuggerFrame::implementationGetter(JSContext* cx, unsigned argc,
+                                         Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get implementation", args, frame);
+
   DebuggerFrameImplementation implementation =
       DebuggerFrame::getImplementation(frame);
 
   const char* s;
   switch (implementation) {
     case DebuggerFrameImplementation::Baseline:
       s = "baseline";
       break;
@@ -1239,56 +1205,75 @@ bool DebuggerFrame::CallData::implementa
   if (!str) {
     return false;
   }
 
   args.rval().setString(str);
   return true;
 }
 
-bool DebuggerFrame::CallData::environmentGetter() {
+/* static */
+bool DebuggerFrame::environmentGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get environment", args, frame);
+
   RootedDebuggerEnvironment result(cx);
   if (!DebuggerFrame::getEnvironment(cx, frame, &result)) {
     return false;
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-bool DebuggerFrame::CallData::calleeGetter() {
+/* static */
+bool DebuggerFrame::calleeGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get callee", args, frame);
+
   RootedDebuggerObject result(cx);
   if (!DebuggerFrame::getCallee(cx, frame, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
-bool DebuggerFrame::CallData::generatorGetter() {
+/* static */
+bool DebuggerFrame::generatorGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get callee", args, frame);
+
   args.rval().setBoolean(DebuggerFrame::getIsGenerator(frame));
   return true;
 }
 
-bool DebuggerFrame::CallData::constructingGetter() {
+/* static */
+bool DebuggerFrame::constructingGetter(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get callee", args, frame);
+
   bool result;
   if (!DebuggerFrame::getIsConstructing(cx, frame, result)) {
     return false;
   }
 
   args.rval().setBoolean(result);
   return true;
 }
 
-bool DebuggerFrame::CallData::thisGetter() {
+/* static */
+bool DebuggerFrame::thisGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get this", args, frame);
+
   return DebuggerFrame::getThis(cx, frame, args.rval());
 }
 
-bool DebuggerFrame::CallData::olderGetter() {
+/* static */
+bool DebuggerFrame::olderGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get older", args, frame);
+
   RootedDebuggerFrame result(cx);
   if (!DebuggerFrame::getOlder(cx, frame, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
@@ -1306,25 +1291,21 @@ static bool DebuggerArguments_getArg(JSC
   }
   if (argsobj->getClass() != &DebuggerArguments::class_) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Arguments",
                               "getArgument", argsobj->getClass()->name);
     return false;
   }
 
-  RootedValue framev(cx,
+  // Put the Debugger.Frame into the this-value slot, then use THIS_FRAME
+  // to check that it is still live and get the fp.
+  args.setThis(
       argsobj->as<NativeObject>().getReservedSlot(JSSLOT_DEBUGARGUMENTS_FRAME));
-  RootedDebuggerFrame thisobj(cx, DebuggerFrame::check(cx, framev, true));
-  if (!thisobj) {
-    return false;
-  }
-
-  FrameIter frameIter(*thisobj->frameIterData());
-  AbstractFramePtr frame = frameIter.abstractFramePtr();
+  THIS_FRAME(cx, argc, vp, "get argument", ca2, thisobj, frameIter, frame);
 
   // TODO handle wasm frame arguments -- they are not yet reflectable.
   MOZ_ASSERT(!frame.isWasmDebugFrame(), "a wasm frame args");
 
   // Since getters can be extracted and applied to other objects,
   // there is no guarantee this object has an ith argument.
   MOZ_ASSERT(i >= 0);
   RootedValue arg(cx);
@@ -1401,80 +1382,98 @@ DebuggerArguments* DebuggerArguments::cr
       return nullptr;
     }
     getobj->setExtendedSlot(0, Int32Value(i));
   }
 
   return obj;
 }
 
-bool DebuggerFrame::CallData::argumentsGetter() {
+/* static */
+bool DebuggerFrame::argumentsGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get arguments", args, frame);
+
   RootedDebuggerArguments result(cx);
   if (!DebuggerFrame::getArguments(cx, frame, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
-bool DebuggerFrame::CallData::getScript() {
-  FrameIter iter(*frame->frameIterData());
-  AbstractFramePtr framePtr = iter.abstractFramePtr();
-  Debugger* debug = Debugger::fromChildJSObject(frame);
+/* static */
+bool DebuggerFrame::getScript(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_FRAME(cx, argc, vp, "get script", args, thisobj, frameIter, frame);
+  Debugger* debug = Debugger::fromChildJSObject(thisobj);
 
   RootedDebuggerScript scriptObject(cx);
-  if (framePtr.isWasmDebugFrame()) {
-    RootedWasmInstanceObject instance(cx, framePtr.wasmInstance()->object());
+  if (frame.isWasmDebugFrame()) {
+    RootedWasmInstanceObject instance(cx, frame.wasmInstance()->object());
     scriptObject = debug->wrapWasmScript(cx, instance);
     if (!scriptObject) {
       return false;
     }
   } else {
-    RootedScript script(cx, framePtr.script());
+    RootedScript script(cx, frame.script());
     scriptObject = debug->wrapScript(cx, script);
     if (!scriptObject) {
       return false;
     }
   }
 
   MOZ_ASSERT(scriptObject);
   args.rval().setObject(*scriptObject);
   return true;
 }
 
-bool DebuggerFrame::CallData::offsetGetter() {
+/* static */
+bool DebuggerFrame::offsetGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get offset", args, frame);
+
   size_t result;
   if (!DebuggerFrame::getOffset(cx, frame, result)) {
     return false;
   }
 
   args.rval().setNumber(double(result));
   return true;
 }
 
-bool DebuggerFrame::CallData::liveGetter() {
+/* static */
+bool DebuggerFrame::liveGetter(JSContext* cx, unsigned argc, Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+  RootedDebuggerFrame frame(cx, checkThis(cx, args, "get live", false));
+  if (!frame) {
+    return false;
+  }
+
   args.rval().setBoolean(frame->isLive());
   return true;
 }
 
 static bool IsValidHook(const Value& v) {
   return v.isUndefined() || (v.isObject() && v.toObject().isCallable());
 }
 
-bool DebuggerFrame::CallData::onStepGetter() {
+/* static */
+bool DebuggerFrame::onStepGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get onStep", args, frame);
+
   OnStepHandler* handler = frame->onStepHandler();
   RootedValue value(
       cx, handler ? ObjectOrNullValue(handler->object()) : UndefinedValue());
   MOZ_ASSERT(IsValidHook(value));
   args.rval().set(value);
   return true;
 }
 
-bool DebuggerFrame::CallData::onStepSetter() {
+/* static */
+bool DebuggerFrame::onStepSetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "set onStep", args, frame);
   if (!args.requireAtLeast(cx, "Debugger.Frame.set onStep", 1)) {
     return false;
   }
   if (!IsValidHook(args[0])) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_NOT_CALLABLE_OR_UNDEFINED);
     return false;
   }
@@ -1493,26 +1492,31 @@ bool DebuggerFrame::CallData::onStepSett
     js_delete(handler);
     return false;
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerFrame::CallData::onPopGetter() {
+/* static */
+bool DebuggerFrame::onPopGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "get onPop", args, frame);
+
   OnPopHandler* handler = frame->onPopHandler();
   RootedValue value(
       cx, handler ? ObjectValue(*handler->object()) : UndefinedValue());
   MOZ_ASSERT(IsValidHook(value));
   args.rval().set(value);
   return true;
 }
 
-bool DebuggerFrame::CallData::onPopSetter() {
+/* static */
+bool DebuggerFrame::onPopSetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "set onPop", args, frame);
   if (!args.requireAtLeast(cx, "Debugger.Frame.set onPop", 1)) {
     return false;
   }
   if (!IsValidHook(args[0])) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_NOT_CALLABLE_OR_UNDEFINED);
     return false;
   }
@@ -1526,17 +1530,19 @@ bool DebuggerFrame::CallData::onPopSette
   }
 
   frame->setOnPopHandler(cx, handler);
 
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerFrame::CallData::evalMethod() {
+/* static */
+bool DebuggerFrame::evalMethod(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "eval", args, frame);
   if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.eval", 1)) {
     return false;
   }
 
   AutoStableStringChars stableChars(cx);
   if (!ValueToStableChars(cx, "Debugger.Frame.prototype.eval", args[0],
                           stableChars)) {
     return false;
@@ -1549,17 +1555,20 @@ bool DebuggerFrame::CallData::evalMethod
   }
 
   Rooted<Completion> comp(cx);
   JS_TRY_VAR_OR_RETURN_FALSE(
       cx, comp, DebuggerFrame::eval(cx, frame, chars, nullptr, options));
   return comp.get().buildCompletionValue(cx, frame->owner(), args.rval());
 }
 
-bool DebuggerFrame::CallData::evalWithBindingsMethod() {
+/* static */
+bool DebuggerFrame::evalWithBindingsMethod(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  THIS_DEBUGGER_FRAME(cx, argc, vp, "evalWithBindings", args, frame);
   if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.evalWithBindings",
                            2)) {
     return false;
   }
 
   AutoStableStringChars stableChars(cx);
   if (!ValueToStableChars(cx, "Debugger.Frame.prototype.evalWithBindings",
                           args[0], stableChars)) {
@@ -1586,35 +1595,36 @@ bool DebuggerFrame::CallData::evalWithBi
 /* static */
 bool DebuggerFrame::construct(JSContext* cx, unsigned argc, Value* vp) {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                             "Debugger.Frame");
   return false;
 }
 
 const JSPropertySpec DebuggerFrame::properties_[] = {
-    JS_DEBUG_PSG("arguments", argumentsGetter),
-    JS_DEBUG_PSG("callee", calleeGetter),
-    JS_DEBUG_PSG("constructing", constructingGetter),
-    JS_DEBUG_PSG("environment", environmentGetter),
-    JS_DEBUG_PSG("generator", generatorGetter),
-    JS_DEBUG_PSG("live", liveGetter),
-    JS_DEBUG_PSG("offset", offsetGetter),
-    JS_DEBUG_PSG("older", olderGetter),
-    JS_DEBUG_PSG("script", getScript),
-    JS_DEBUG_PSG("this", thisGetter),
-    JS_DEBUG_PSG("type", typeGetter),
-    JS_DEBUG_PSG("implementation", implementationGetter),
-    JS_DEBUG_PSGS("onStep", onStepGetter, onStepSetter),
-    JS_DEBUG_PSGS("onPop", onPopGetter, onPopSetter),
+    JS_PSG("arguments", DebuggerFrame::argumentsGetter, 0),
+    JS_PSG("callee", DebuggerFrame::calleeGetter, 0),
+    JS_PSG("constructing", DebuggerFrame::constructingGetter, 0),
+    JS_PSG("environment", DebuggerFrame::environmentGetter, 0),
+    JS_PSG("generator", DebuggerFrame::generatorGetter, 0),
+    JS_PSG("live", DebuggerFrame::liveGetter, 0),
+    JS_PSG("offset", DebuggerFrame::offsetGetter, 0),
+    JS_PSG("older", DebuggerFrame::olderGetter, 0),
+    JS_PSG("script", DebuggerFrame::getScript, 0),
+    JS_PSG("this", DebuggerFrame::thisGetter, 0),
+    JS_PSG("type", DebuggerFrame::typeGetter, 0),
+    JS_PSG("implementation", DebuggerFrame::implementationGetter, 0),
+    JS_PSGS("onStep", DebuggerFrame::onStepGetter, DebuggerFrame::onStepSetter,
+            0),
+    JS_PSGS("onPop", DebuggerFrame::onPopGetter, DebuggerFrame::onPopSetter, 0),
     JS_PS_END};
 
 const JSFunctionSpec DebuggerFrame::methods_[] = {
-    JS_DEBUG_FN("eval", evalMethod, 1),
-    JS_DEBUG_FN("evalWithBindings", evalWithBindingsMethod, 1),
+    JS_FN("eval", DebuggerFrame::evalMethod, 1, 0),
+    JS_FN("evalWithBindings", DebuggerFrame::evalWithBindingsMethod, 1, 0),
     JS_FS_END};
 
 JSObject* js::IdVectorToArray(JSContext* cx, Handle<IdVector> ids) {
   Rooted<ValueVector> vals(cx, ValueVector(cx));
   if (!vals.growBy(ids.length())) {
     return nullptr;
   }
 
--- a/js/src/debugger/Frame.h
+++ b/js/src/debugger/Frame.h
@@ -144,16 +144,17 @@ class DebuggerFrame : public NativeObjec
   void trace(JSTracer* trc);
 
   static NativeObject* initClass(JSContext* cx, Handle<GlobalObject*> global,
                                  HandleObject dbgCtor);
   static DebuggerFrame* create(JSContext* cx, HandleObject proto,
                                const FrameIter& iter,
                                HandleNativeObject debugger);
 
+  static MOZ_MUST_USE bool getScript(JSContext* cx, unsigned argc, Value* vp);
   static MOZ_MUST_USE bool getArguments(JSContext* cx,
                                         HandleDebuggerFrame frame,
                                         MutableHandleDebuggerArguments result);
   static MOZ_MUST_USE bool getCallee(JSContext* cx, HandleDebuggerFrame frame,
                                      MutableHandleDebuggerObject result);
   static MOZ_MUST_USE bool getIsConstructing(JSContext* cx,
                                              HandleDebuggerFrame frame,
                                              bool& result);
@@ -175,18 +176,20 @@ class DebuggerFrame : public NativeObjec
                                             OnStepHandler* handler);
 
   static MOZ_MUST_USE JS::Result<Completion> eval(
       JSContext* cx, HandleDebuggerFrame frame,
       mozilla::Range<const char16_t> chars, HandleObject bindings,
       const EvalOptions& options);
 
   MOZ_MUST_USE bool requireLive(JSContext* cx);
-  static MOZ_MUST_USE DebuggerFrame* check(JSContext* cx, HandleValue thisv,
-                                           bool checkLive);
+  static MOZ_MUST_USE DebuggerFrame* checkThis(JSContext* cx,
+                                               const CallArgs& args,
+                                               const char* fnname,
+                                               bool checkLive);
 
   bool isLive() const;
   OnStepHandler* onStepHandler() const;
   OnPopHandler* onPopHandler() const;
   void setOnPopHandler(JSContext* cx, OnPopHandler* handler);
 
   inline bool hasGenerator() const;
 
@@ -258,17 +261,44 @@ class DebuggerFrame : public NativeObjec
   static MOZ_MUST_USE bool getFrameIter(JSContext* cx,
                                         HandleDebuggerFrame frame,
                                         mozilla::Maybe<FrameIter>& result);
   static MOZ_MUST_USE bool requireScriptReferent(JSContext* cx,
                                                  HandleDebuggerFrame frame);
 
   static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
 
-  struct CallData;
+  static MOZ_MUST_USE bool argumentsGetter(JSContext* cx, unsigned argc,
+                                           Value* vp);
+  static MOZ_MUST_USE bool calleeGetter(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static MOZ_MUST_USE bool constructingGetter(JSContext* cx, unsigned argc,
+                                              Value* vp);
+  static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc,
+                                             Value* vp);
+  static MOZ_MUST_USE bool generatorGetter(JSContext* cx, unsigned argc,
+                                           Value* vp);
+  static MOZ_MUST_USE bool liveGetter(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool offsetGetter(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static MOZ_MUST_USE bool olderGetter(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool thisGetter(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool typeGetter(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool implementationGetter(JSContext* cx, unsigned argc,
+                                                Value* vp);
+  static MOZ_MUST_USE bool onStepGetter(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static MOZ_MUST_USE bool onStepSetter(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static MOZ_MUST_USE bool onPopGetter(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool onPopSetter(JSContext* cx, unsigned argc, Value* vp);
+
+  static MOZ_MUST_USE bool evalMethod(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool evalWithBindingsMethod(JSContext* cx, unsigned argc,
+                                                  Value* vp);
 
   Debugger* owner() const;
 
  public:
   FrameIter::Data* frameIterData() const;
   void setFrameIterData(FrameIter::Data*);
   void freeFrameIterData(JSFreeOp* fop);
   void maybeDecrementFrameScriptStepperCount(JSFreeOp* fop,
--- a/js/src/debugger/Object.cpp
+++ b/js/src/debugger/Object.cpp
@@ -49,17 +49,16 @@
 #include "vm/JSScript.h"                 // for JSScript
 #include "vm/NativeObject.h"             // for NativeObject, JSObject::is
 #include "vm/ObjectGroup.h"              // for GenericObject, NewObjectKind
 #include "vm/ObjectOperations.h"         // for DefineProperty
 #include "vm/Realm.h"                    // for AutoRealm, ErrorCopier, Realm
 #include "vm/Runtime.h"                  // for JSAtomState
 #include "vm/SavedFrame.h"               // for SavedFrame
 #include "vm/Scope.h"                    // for PositionalFormalParameterIter
-#include "vm/SelfHosting.h"              // for GetClonedSelfHostedFunctionName
 #include "vm/Shape.h"                    // for Shape
 #include "vm/Stack.h"                    // for InvokeArgs
 #include "vm/StringType.h"               // for JSAtom, PropertyName
 #include "vm/WrapperObject.h"            // for JSObject::is, WrapperObject
 
 #include "vm/Compartment-inl.h"       // for Compartment::wrap
 #include "vm/JSAtom-inl.h"            // for ValueToId
 #include "vm/JSObject-inl.h"          // for GetObjectClassName, InitClass
@@ -99,239 +98,233 @@ void DebuggerObject::trace(JSTracer* trc
     TraceManuallyBarrieredCrossCompartmentEdge(
         trc, static_cast<JSObject*>(this), &referent,
         "Debugger.Object referent");
     setPrivateUnbarriered(referent);
   }
 }
 
 static DebuggerObject* DebuggerObject_checkThis(JSContext* cx,
-                                                const CallArgs& args) {
+                                                const CallArgs& args,
+                                                const char* fnname) {
   JSObject* thisobj = RequireObject(cx, args.thisv());
   if (!thisobj) {
     return nullptr;
   }
   if (thisobj->getClass() != &DebuggerObject::class_) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Object",
-                              "method", thisobj->getClass()->name);
+                              fnname, thisobj->getClass()->name);
     return nullptr;
   }
 
   // Forbid Debugger.Object.prototype, which is of class DebuggerObject::class_
   // but isn't a real working Debugger.Object. The prototype object is
   // distinguished by having no referent.
   DebuggerObject* nthisobj = &thisobj->as<DebuggerObject>();
   if (!nthisobj->getPrivate()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Object",
-                              "method", "prototype object");
+                              fnname, "prototype object");
     return nullptr;
   }
   return nthisobj;
 }
 
+#define THIS_DEBUGOBJECT(cx, argc, vp, fnname, args, object)                   \
+  CallArgs args = CallArgsFromVp(argc, vp);                                    \
+  RootedDebuggerObject object(cx, DebuggerObject_checkThis(cx, args, fnname)); \
+  if (!object) return false;
+
+#define THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj)  \
+  CallArgs args = CallArgsFromVp(argc, vp);                         \
+  RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname)); \
+  if (!obj) return false;                                           \
+  obj = (JSObject*)obj->as<NativeObject>().getPrivate();            \
+  MOZ_ASSERT(obj)
+
+#define THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj) \
+  CallArgs args = CallArgsFromVp(argc, vp);                                   \
+  RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname));           \
+  if (!obj) return false;                                                     \
+  Debugger* dbg = Debugger::fromChildJSObject(obj);                           \
+  obj = (JSObject*)obj->as<NativeObject>().getPrivate();                      \
+  MOZ_ASSERT(obj)
+
+#define THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, fnname, args, obj)             \
+  THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj);                 \
+  /* We only care about promises, so CheckedUnwrapStatic is OK. */            \
+  obj = CheckedUnwrapStatic(obj);                                             \
+  if (!obj) {                                                                 \
+    ReportAccessDenied(cx);                                                   \
+    return false;                                                             \
+  }                                                                           \
+  if (!obj->is<PromiseObject>()) {                                            \
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,                   \
+                              JSMSG_NOT_EXPECTED_TYPE, "Debugger", "Promise", \
+                              obj->getClass()->name);                         \
+    return false;                                                             \
+  }                                                                           \
+  Rooted<PromiseObject*> promise(cx, &obj->as<PromiseObject>());
+
+#define THIS_DEBUGOBJECT_OWNER_PROMISE(cx, argc, vp, fnname, args, dbg, obj)  \
+  THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj);      \
+  /* We only care about promises, so CheckedUnwrapStatic is OK. */            \
+  obj = CheckedUnwrapStatic(obj);                                             \
+  if (!obj) {                                                                 \
+    ReportAccessDenied(cx);                                                   \
+    return false;                                                             \
+  }                                                                           \
+  if (!obj->is<PromiseObject>()) {                                            \
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,                   \
+                              JSMSG_NOT_EXPECTED_TYPE, "Debugger", "Promise", \
+                              obj->getClass()->name);                         \
+    return false;                                                             \
+  }                                                                           \
+  Rooted<PromiseObject*> promise(cx, &obj->as<PromiseObject>());
+
 /* static */
 bool DebuggerObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                             "Debugger.Object");
   return false;
 }
 
-struct MOZ_STACK_CLASS DebuggerObject::CallData {
-  JSContext* cx;
-  const CallArgs& args;
-
-  HandleDebuggerObject object;
-  RootedObject referent;
-
-  CallData(JSContext* cx, const CallArgs& args, HandleDebuggerObject obj)
-      : cx(cx), args(args), object(obj), referent(cx, obj->referent()) {}
-
-  // JSNative properties
-  bool callableGetter();
-  bool isBoundFunctionGetter();
-  bool isArrowFunctionGetter();
-  bool isAsyncFunctionGetter();
-  bool isGeneratorFunctionGetter();
-  bool protoGetter();
-  bool classGetter();
-  bool nameGetter();
-  bool displayNameGetter();
-  bool parameterNamesGetter();
-  bool scriptGetter();
-  bool environmentGetter();
-  bool boundTargetFunctionGetter();
-  bool boundThisGetter();
-  bool boundArgumentsGetter();
-  bool allocationSiteGetter();
-  bool errorMessageNameGetter();
-  bool errorNotesGetter();
-  bool errorLineNumberGetter();
-  bool errorColumnNumberGetter();
-  bool isProxyGetter();
-  bool proxyTargetGetter();
-  bool proxyHandlerGetter();
-  bool isPromiseGetter();
-  bool promiseStateGetter();
-  bool promiseValueGetter();
-  bool promiseReasonGetter();
-  bool promiseLifetimeGetter();
-  bool promiseTimeToResolutionGetter();
-  bool promiseAllocationSiteGetter();
-  bool promiseResolutionSiteGetter();
-  bool promiseIDGetter();
-  bool promiseDependentPromisesGetter();
-
-  // JSNative methods
-  bool isExtensibleMethod();
-  bool isSealedMethod();
-  bool isFrozenMethod();
-  bool getPropertyMethod();
-  bool setPropertyMethod();
-  bool getOwnPropertyNamesMethod();
-  bool getOwnPropertySymbolsMethod();
-  bool getOwnPropertyDescriptorMethod();
-  bool preventExtensionsMethod();
-  bool sealMethod();
-  bool freezeMethod();
-  bool definePropertyMethod();
-  bool definePropertiesMethod();
-  bool deletePropertyMethod();
-  bool callMethod();
-  bool applyMethod();
-  bool asEnvironmentMethod();
-  bool forceLexicalInitializationByNameMethod();
-  bool executeInGlobalMethod();
-  bool executeInGlobalWithBindingsMethod();
-  bool createSource();
-  bool makeDebuggeeValueMethod();
-  bool makeDebuggeeNativeFunctionMethod();
-  bool isSameNativeMethod();
-  bool unsafeDereferenceMethod();
-  bool unwrapMethod();
-  bool setInstrumentationMethod();
-  bool setInstrumentationActiveMethod();
-
-  using Method = bool (CallData::*)();
-
-  template <Method MyMethod>
-  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
-};
-
-template <DebuggerObject::CallData::Method MyMethod>
 /* static */
-bool DebuggerObject::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  RootedDebuggerObject obj(cx, DebuggerObject_checkThis(cx, args));
-  if (!obj) {
-    return false;
-  }
-
-  CallData data(cx, args, obj);
-  return (data.*MyMethod)();
-}
-
-bool DebuggerObject::CallData::callableGetter() {
+bool DebuggerObject::callableGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get callable", args, object)
+
   args.rval().setBoolean(object->isCallable());
   return true;
 }
 
-bool DebuggerObject::CallData::isBoundFunctionGetter() {
+/* static */
+bool DebuggerObject::isBoundFunctionGetter(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get isBoundFunction", args, object)
+
   if (!object->isDebuggeeFunction()) {
     args.rval().setUndefined();
     return true;
   }
 
   args.rval().setBoolean(object->isBoundFunction());
   return true;
 }
 
-bool DebuggerObject::CallData::isArrowFunctionGetter() {
+/* static */
+bool DebuggerObject::isArrowFunctionGetter(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get isArrowFunction", args, object)
+
   if (!object->isDebuggeeFunction()) {
     args.rval().setUndefined();
     return true;
   }
 
   args.rval().setBoolean(object->isArrowFunction());
   return true;
 }
 
-bool DebuggerObject::CallData::isAsyncFunctionGetter() {
+/* static */
+bool DebuggerObject::isAsyncFunctionGetter(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get isAsyncFunction", args, object)
+
   if (!object->isDebuggeeFunction()) {
     args.rval().setUndefined();
     return true;
   }
 
   args.rval().setBoolean(object->isAsyncFunction());
   return true;
 }
 
-bool DebuggerObject::CallData::isGeneratorFunctionGetter() {
+/* static */
+bool DebuggerObject::isGeneratorFunctionGetter(JSContext* cx, unsigned argc,
+                                               Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get isGeneratorFunction", args, object)
+
   if (!object->isDebuggeeFunction()) {
     args.rval().setUndefined();
     return true;
   }
 
   args.rval().setBoolean(object->isGeneratorFunction());
   return true;
 }
 
-bool DebuggerObject::CallData::protoGetter() {
+/* static */
+bool DebuggerObject::protoGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get proto", args, object)
+
   RootedDebuggerObject result(cx);
   if (!DebuggerObject::getPrototypeOf(cx, object, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
-bool DebuggerObject::CallData::classGetter() {
+/* static */
+bool DebuggerObject::classGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get class", args, object)
+
   RootedString result(cx);
   if (!DebuggerObject::getClassName(cx, object, &result)) {
     return false;
   }
 
   args.rval().setString(result);
   return true;
 }
 
-bool DebuggerObject::CallData::nameGetter() {
+/* static */
+bool DebuggerObject::nameGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get name", args, object)
+
   if (!object->isFunction()) {
     args.rval().setUndefined();
     return true;
   }
 
   RootedString result(cx, object->name(cx));
   if (result) {
     args.rval().setString(result);
   } else {
     args.rval().setUndefined();
   }
   return true;
 }
 
-bool DebuggerObject::CallData::displayNameGetter() {
+/* static */
+bool DebuggerObject::displayNameGetter(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get displayName", args, object)
+
   if (!object->isFunction()) {
     args.rval().setUndefined();
     return true;
   }
 
   RootedString result(cx, object->displayName(cx));
   if (result) {
     args.rval().setString(result);
   } else {
     args.rval().setUndefined();
   }
   return true;
 }
 
-bool DebuggerObject::CallData::parameterNamesGetter() {
+/* static */
+bool DebuggerObject::parameterNamesGetter(JSContext* cx, unsigned argc,
+                                          Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get parameterNames", args, object)
+
   if (!object->isDebuggeeFunction()) {
     args.rval().setUndefined();
     return true;
   }
 
   Rooted<StringVector> names(cx, StringVector(cx));
   if (!DebuggerObject::getParameterNames(cx, object, &names)) {
     return false;
@@ -352,25 +345,26 @@ bool DebuggerObject::CallData::parameter
     }
     obj->setDenseElement(i, v);
   }
 
   args.rval().setObject(*obj);
   return true;
 }
 
-bool DebuggerObject::CallData::scriptGetter() {
-  Debugger* dbg = Debugger::fromChildJSObject(object);
-
-  if (!referent->is<JSFunction>()) {
+/* static */
+bool DebuggerObject::scriptGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get script", args, dbg, obj);
+
+  if (!obj->is<JSFunction>()) {
     args.rval().setUndefined();
     return true;
   }
 
-  RootedFunction fun(cx, &referent->as<JSFunction>());
+  RootedFunction fun(cx, &obj->as<JSFunction>());
   if (!IsInterpretedNonSelfHostedFunction(fun)) {
     args.rval().setUndefined();
     return true;
   }
 
   RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
   if (!script) {
     return false;
@@ -386,27 +380,30 @@ bool DebuggerObject::CallData::scriptGet
   if (!scriptObject) {
     return false;
   }
 
   args.rval().setObject(*scriptObject);
   return true;
 }
 
-bool DebuggerObject::CallData::environmentGetter() {
-  Debugger* dbg = Debugger::fromChildJSObject(object);
+/* static */
+bool DebuggerObject::environmentGetter(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get environment", args, dbg,
+                                  obj);
 
   // Don't bother switching compartments just to check obj's type and get its
   // env.
-  if (!referent->is<JSFunction>()) {
+  if (!obj->is<JSFunction>()) {
     args.rval().setUndefined();
     return true;
   }
 
-  RootedFunction fun(cx, &referent->as<JSFunction>());
+  RootedFunction fun(cx, &obj->as<JSFunction>());
   if (!IsInterpretedNonSelfHostedFunction(fun)) {
     args.rval().setUndefined();
     return true;
   }
 
   // Only hand out environments of debuggee functions.
   if (!dbg->observesGlobal(&fun->global())) {
     args.rval().setNull();
@@ -420,41 +417,52 @@ bool DebuggerObject::CallData::environme
     if (!env) {
       return false;
     }
   }
 
   return dbg->wrapEnvironment(cx, env, args.rval());
 }
 
-bool DebuggerObject::CallData::boundTargetFunctionGetter() {
+/* static */
+bool DebuggerObject::boundTargetFunctionGetter(JSContext* cx, unsigned argc,
+                                               Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get boundTargetFunction", args, object)
+
   if (!object->isDebuggeeFunction() || !object->isBoundFunction()) {
     args.rval().setUndefined();
     return true;
   }
 
   RootedDebuggerObject result(cx);
   if (!DebuggerObject::getBoundTargetFunction(cx, object, &result)) {
     return false;
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-bool DebuggerObject::CallData::boundThisGetter() {
+/* static */
+bool DebuggerObject::boundThisGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get boundThis", args, object)
+
   if (!object->isDebuggeeFunction() || !object->isBoundFunction()) {
     args.rval().setUndefined();
     return true;
   }
 
   return DebuggerObject::getBoundThis(cx, object, args.rval());
 }
 
-bool DebuggerObject::CallData::boundArgumentsGetter() {
+/* static */
+bool DebuggerObject::boundArgumentsGetter(JSContext* cx, unsigned argc,
+                                          Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get boundArguments", args, object)
+
   if (!object->isDebuggeeFunction() || !object->isBoundFunction()) {
     args.rval().setUndefined();
     return true;
   }
 
   Rooted<ValueVector> result(cx, ValueVector(cx));
   if (!DebuggerObject::getBoundArguments(cx, object, &result)) {
     return false;
@@ -465,95 +473,132 @@ bool DebuggerObject::CallData::boundArgu
   if (!obj) {
     return false;
   }
 
   args.rval().setObject(*obj);
   return true;
 }
 
-bool DebuggerObject::CallData::allocationSiteGetter() {
+/* static */
+bool DebuggerObject::allocationSiteGetter(JSContext* cx, unsigned argc,
+                                          Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get allocationSite", args, object)
+
   RootedObject result(cx);
   if (!DebuggerObject::getAllocationSite(cx, object, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
 // Returns the "name" field (see js.msg), which may be used as a unique
 // identifier, for any error object with a JSErrorReport or undefined
 // if the object has no JSErrorReport.
-bool DebuggerObject::CallData::errorMessageNameGetter() {
+/* static */
+bool DebuggerObject::errorMessageNameGetter(JSContext* cx, unsigned argc,
+                                            Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get errorMessageName", args, object)
+
   RootedString result(cx);
   if (!DebuggerObject::getErrorMessageName(cx, object, &result)) {
     return false;
   }
 
   if (result) {
     args.rval().setString(result);
   } else {
     args.rval().setUndefined();
   }
   return true;
 }
 
-bool DebuggerObject::CallData::errorNotesGetter() {
+/* static */
+bool DebuggerObject::errorNotesGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get errorNotes", args, object)
+
   return DebuggerObject::getErrorNotes(cx, object, args.rval());
 }
 
-bool DebuggerObject::CallData::errorLineNumberGetter() {
+/* static */
+bool DebuggerObject::errorLineNumberGetter(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get errorLineNumber", args, object)
+
   return DebuggerObject::getErrorLineNumber(cx, object, args.rval());
 }
 
-bool DebuggerObject::CallData::errorColumnNumberGetter() {
+/* static */
+bool DebuggerObject::errorColumnNumberGetter(JSContext* cx, unsigned argc,
+                                             Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get errorColumnNumber", args, object)
+
   return DebuggerObject::getErrorColumnNumber(cx, object, args.rval());
 }
 
-bool DebuggerObject::CallData::isProxyGetter() {
+/* static */
+bool DebuggerObject::isProxyGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get isProxy", args, object)
+
   args.rval().setBoolean(object->isScriptedProxy());
   return true;
 }
 
-bool DebuggerObject::CallData::proxyTargetGetter() {
+/* static */
+bool DebuggerObject::proxyTargetGetter(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get proxyTarget", args, object)
+
   if (!object->isScriptedProxy()) {
     args.rval().setUndefined();
     return true;
   }
 
   Rooted<DebuggerObject*> result(cx);
   if (!DebuggerObject::getScriptedProxyTarget(cx, object, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
-bool DebuggerObject::CallData::proxyHandlerGetter() {
+/* static */
+bool DebuggerObject::proxyHandlerGetter(JSContext* cx, unsigned argc,
+                                        Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get proxyHandler", args, object)
+
   if (!object->isScriptedProxy()) {
     args.rval().setUndefined();
     return true;
   }
   Rooted<DebuggerObject*> result(cx);
   if (!DebuggerObject::getScriptedProxyHandler(cx, object, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
-bool DebuggerObject::CallData::isPromiseGetter() {
+/* static */
+bool DebuggerObject::isPromiseGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get isPromise", args, object)
+
   args.rval().setBoolean(object->isPromise());
   return true;
 }
 
-bool DebuggerObject::CallData::promiseStateGetter() {
+/* static */
+bool DebuggerObject::promiseStateGetter(JSContext* cx, unsigned argc,
+                                        Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get promiseState", args, object);
+
   if (!DebuggerObject::requirePromise(cx, object)) {
     return false;
   }
 
   RootedValue result(cx);
   switch (object->promiseState()) {
     case JS::PromiseState::Pending:
       result.setString(cx->names().pending);
@@ -565,109 +610,110 @@ bool DebuggerObject::CallData::promiseSt
       result.setString(cx->names().rejected);
       break;
   }
 
   args.rval().set(result);
   return true;
 }
 
-bool DebuggerObject::CallData::promiseValueGetter() {
+/* static */
+bool DebuggerObject::promiseValueGetter(JSContext* cx, unsigned argc,
+                                        Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get promiseValue", args, object);
+
   if (!DebuggerObject::requirePromise(cx, object)) {
     return false;
   }
 
   if (object->promiseState() != JS::PromiseState::Fulfilled) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_DEBUG_PROMISE_NOT_FULFILLED);
     return false;
   }
 
   return DebuggerObject::getPromiseValue(cx, object, args.rval());
   ;
 }
 
-bool DebuggerObject::CallData::promiseReasonGetter() {
+/* static */
+bool DebuggerObject::promiseReasonGetter(JSContext* cx, unsigned argc,
+                                         Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get promiseReason", args, object);
+
   if (!DebuggerObject::requirePromise(cx, object)) {
     return false;
   }
 
   if (object->promiseState() != JS::PromiseState::Rejected) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_DEBUG_PROMISE_NOT_REJECTED);
     return false;
   }
 
   return DebuggerObject::getPromiseReason(cx, object, args.rval());
+  ;
 }
 
-bool DebuggerObject::CallData::promiseLifetimeGetter() {
+/* static */
+bool DebuggerObject::promiseLifetimeGetter(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get promiseLifetime", args, object);
+
   if (!DebuggerObject::requirePromise(cx, object)) {
     return false;
   }
 
   args.rval().setNumber(object->promiseLifetime());
   return true;
 }
 
-bool DebuggerObject::CallData::promiseTimeToResolutionGetter() {
+/* static */
+bool DebuggerObject::promiseTimeToResolutionGetter(JSContext* cx, unsigned argc,
+                                                   Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "get promiseTimeToResolution", args, object);
+
   if (!DebuggerObject::requirePromise(cx, object)) {
     return false;
   }
 
   if (object->promiseState() == JS::PromiseState::Pending) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_DEBUG_PROMISE_NOT_RESOLVED);
     return false;
   }
 
   args.rval().setNumber(object->promiseTimeToResolution());
   return true;
 }
 
-static PromiseObject* EnsurePromise(JSContext* cx, HandleObject referent) {
-  // We only care about promises, so CheckedUnwrapStatic is OK.
-  RootedObject obj(cx, CheckedUnwrapStatic(referent));
-  if (!obj) {
-    ReportAccessDenied(cx);
-    return nullptr;
-  }
-  if (!obj->is<PromiseObject>()) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_NOT_EXPECTED_TYPE, "Debugger", "Promise",
-                              obj->getClass()->name);
-    return nullptr;
-  }
-  return &obj->as<PromiseObject>();
-}
-
-bool DebuggerObject::CallData::promiseAllocationSiteGetter() {
-  Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
-  if (!promise) {
-    return false;
-  }
+/* static */
+bool DebuggerObject::promiseAllocationSiteGetter(JSContext* cx, unsigned argc,
+                                                 Value* vp) {
+  THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, "get promiseAllocationSite", args,
+                           refobj);
 
   RootedObject allocSite(cx, promise->allocationSite());
   if (!allocSite) {
     args.rval().setNull();
     return true;
   }
 
   if (!cx->compartment()->wrap(cx, &allocSite)) {
     return false;
   }
   args.rval().set(ObjectValue(*allocSite));
   return true;
 }
 
-bool DebuggerObject::CallData::promiseResolutionSiteGetter() {
-  Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
-  if (!promise) {
-    return false;
-  }
+/* static */
+bool DebuggerObject::promiseResolutionSiteGetter(JSContext* cx, unsigned argc,
+                                                 Value* vp) {
+  THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, "get promiseResolutionSite", args,
+                           refobj);
 
   if (promise->state() == JS::PromiseState::Pending) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_DEBUG_PROMISE_NOT_RESOLVED);
     return false;
   }
 
   RootedObject resolutionSite(cx, promise->resolutionSite());
@@ -678,33 +724,29 @@ bool DebuggerObject::CallData::promiseRe
 
   if (!cx->compartment()->wrap(cx, &resolutionSite)) {
     return false;
   }
   args.rval().set(ObjectValue(*resolutionSite));
   return true;
 }
 
-bool DebuggerObject::CallData::promiseIDGetter() {
-  Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
-  if (!promise) {
-    return false;
-  }
+/* static */
+bool DebuggerObject::promiseIDGetter(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, "get promiseID", args, refobj);
 
   args.rval().setNumber(double(promise->getID()));
   return true;
 }
 
-bool DebuggerObject::CallData::promiseDependentPromisesGetter() {
-  Debugger* dbg = Debugger::fromChildJSObject(object);
-
-  Rooted<PromiseObject*> promise(cx, EnsurePromise(cx, referent));
-  if (!promise) {
-    return false;
-  }
+/* static */
+bool DebuggerObject::promiseDependentPromisesGetter(JSContext* cx,
+                                                    unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT_OWNER_PROMISE(cx, argc, vp, "get promiseDependentPromises",
+                                 args, dbg, refobj);
 
   Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
   {
     JSAutoRealm ar(cx, promise);
     if (!promise->dependentPromises(cx, &values)) {
       return false;
     }
   }
@@ -721,118 +763,153 @@ bool DebuggerObject::CallData::promiseDe
   }
   if (!promises) {
     return false;
   }
   args.rval().setObject(*promises);
   return true;
 }
 
-bool DebuggerObject::CallData::isExtensibleMethod() {
+/* static */
+bool DebuggerObject::isExtensibleMethod(JSContext* cx, unsigned argc,
+                                        Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "isExtensible", args, object)
+
   bool result;
   if (!DebuggerObject::isExtensible(cx, object, result)) {
     return false;
   }
 
   args.rval().setBoolean(result);
   return true;
 }
 
-bool DebuggerObject::CallData::isSealedMethod() {
+/* static */
+bool DebuggerObject::isSealedMethod(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "isSealed", args, object)
+
   bool result;
   if (!DebuggerObject::isSealed(cx, object, result)) {
     return false;
   }
 
   args.rval().setBoolean(result);
   return true;
 }
 
-bool DebuggerObject::CallData::isFrozenMethod() {
+/* static */
+bool DebuggerObject::isFrozenMethod(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "isFrozen", args, object)
+
   bool result;
   if (!DebuggerObject::isFrozen(cx, object, result)) {
     return false;
   }
 
   args.rval().setBoolean(result);
   return true;
 }
 
-bool DebuggerObject::CallData::getOwnPropertyNamesMethod() {
+/* static */
+bool DebuggerObject::getOwnPropertyNamesMethod(JSContext* cx, unsigned argc,
+                                               Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "getOwnPropertyNames", args, object)
+
   Rooted<IdVector> ids(cx, IdVector(cx));
   if (!DebuggerObject::getOwnPropertyNames(cx, object, &ids)) {
     return false;
   }
 
   RootedObject obj(cx, IdVectorToArray(cx, ids));
   if (!obj) {
     return false;
   }
 
   args.rval().setObject(*obj);
   return true;
 }
 
-bool DebuggerObject::CallData::getOwnPropertySymbolsMethod() {
+/* static */
+bool DebuggerObject::getOwnPropertySymbolsMethod(JSContext* cx, unsigned argc,
+                                                 Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "getOwnPropertySymbols", args, object)
+
   Rooted<IdVector> ids(cx, IdVector(cx));
   if (!DebuggerObject::getOwnPropertySymbols(cx, object, &ids)) {
     return false;
   }
 
   RootedObject obj(cx, IdVectorToArray(cx, ids));
   if (!obj) {
     return false;
   }
 
   args.rval().setObject(*obj);
   return true;
 }
 
-bool DebuggerObject::CallData::getOwnPropertyDescriptorMethod() {
+/* static */
+bool DebuggerObject::getOwnPropertyDescriptorMethod(JSContext* cx,
+                                                    unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "getOwnPropertyDescriptor", args, object)
+
   RootedId id(cx);
   if (!ValueToId<CanGC>(cx, args.get(0), &id)) {
     return false;
   }
 
   Rooted<PropertyDescriptor> desc(cx);
   if (!DebuggerObject::getOwnPropertyDescriptor(cx, object, id, &desc)) {
     return false;
   }
 
   return JS::FromPropertyDescriptor(cx, desc, args.rval());
 }
 
-bool DebuggerObject::CallData::preventExtensionsMethod() {
+/* static */
+bool DebuggerObject::preventExtensionsMethod(JSContext* cx, unsigned argc,
+                                             Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "preventExtensions", args, object)
+
   if (!DebuggerObject::preventExtensions(cx, object)) {
     return false;
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerObject::CallData::sealMethod() {
+/* static */
+bool DebuggerObject::sealMethod(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "seal", args, object)
+
   if (!DebuggerObject::seal(cx, object)) {
     return false;
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerObject::CallData::freezeMethod() {
+/* static */
+bool DebuggerObject::freezeMethod(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "freeze", args, object)
+
   if (!DebuggerObject::freeze(cx, object)) {
     return false;
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerObject::CallData::definePropertyMethod() {
+/* static */
+bool DebuggerObject::definePropertyMethod(JSContext* cx, unsigned argc,
+                                          Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "defineProperty", args, object)
   if (!args.requireAtLeast(cx, "Debugger.Object.defineProperty", 2)) {
     return false;
   }
 
   RootedId id(cx);
   if (!ValueToId<CanGC>(cx, args[0], &id)) {
     return false;
   }
@@ -845,17 +922,20 @@ bool DebuggerObject::CallData::definePro
   if (!DebuggerObject::defineProperty(cx, object, id, desc)) {
     return false;
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerObject::CallData::definePropertiesMethod() {
+/* static */
+bool DebuggerObject::definePropertiesMethod(JSContext* cx, unsigned argc,
+                                            Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "defineProperties", args, object);
   if (!args.requireAtLeast(cx, "Debugger.Object.defineProperties", 1)) {
     return false;
   }
 
   RootedValue arg(cx, args[0]);
   RootedObject props(cx, ToObject(cx, arg));
   if (!props) {
     return false;
@@ -877,70 +957,83 @@ bool DebuggerObject::CallData::definePro
   args.rval().setUndefined();
   return true;
 }
 
 /*
  * This does a non-strict delete, as a matter of API design. The case where the
  * property is non-configurable isn't necessarily exceptional here.
  */
-bool DebuggerObject::CallData::deletePropertyMethod() {
+/* static */
+bool DebuggerObject::deletePropertyMethod(JSContext* cx, unsigned argc,
+                                          Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "deleteProperty", args, object)
+
   RootedId id(cx);
   if (!ValueToId<CanGC>(cx, args.get(0), &id)) {
     return false;
   }
 
   ObjectOpResult result;
   if (!DebuggerObject::deleteProperty(cx, object, id, result)) {
     return false;
   }
 
   args.rval().setBoolean(result.ok());
   return true;
 }
 
-bool DebuggerObject::CallData::callMethod() {
-  RootedValue thisv(cx, args.get(0));
-
-  Rooted<ValueVector> nargs(cx, ValueVector(cx));
-  if (args.length() >= 2) {
-    if (!nargs.growBy(args.length() - 1)) {
+/* static */
+bool DebuggerObject::callMethod(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "call", callArgs, object);
+
+  RootedValue thisv(cx, callArgs.get(0));
+
+  Rooted<ValueVector> args(cx, ValueVector(cx));
+  if (callArgs.length() >= 2) {
+    if (!args.growBy(callArgs.length() - 1)) {
       return false;
     }
-    for (size_t i = 1; i < args.length(); ++i) {
-      nargs[i - 1].set(args[i]);
+    for (size_t i = 1; i < callArgs.length(); ++i) {
+      args[i - 1].set(callArgs[i]);
     }
   }
 
   Rooted<Maybe<Completion>> completion(
-      cx, DebuggerObject::call(cx, object, thisv, nargs));
+      cx, DebuggerObject::call(cx, object, thisv, args));
   if (!completion.get()) {
     return false;
   }
 
-  return completion->buildCompletionValue(cx, object->owner(), args.rval());
+  return completion->buildCompletionValue(cx, object->owner(), callArgs.rval());
 }
 
-bool DebuggerObject::CallData::getPropertyMethod() {
+/* static */
+bool DebuggerObject::getPropertyMethod(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "getProperty", args, object)
   Debugger* dbg = Debugger::fromChildJSObject(object);
 
   RootedId id(cx);
   if (!ValueToId<CanGC>(cx, args.get(0), &id)) {
     return false;
   }
 
   RootedValue receiver(cx,
                        args.length() < 2 ? ObjectValue(*object) : args.get(1));
 
   Rooted<Completion> comp(cx);
   JS_TRY_VAR_OR_RETURN_FALSE(cx, comp, getProperty(cx, object, id, receiver));
   return comp.get().buildCompletionValue(cx, dbg, args.rval());
 }
 
-bool DebuggerObject::CallData::setPropertyMethod() {
+/* static */
+bool DebuggerObject::setPropertyMethod(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "setProperty", args, object)
   Debugger* dbg = Debugger::fromChildJSObject(object);
 
   RootedId id(cx);
   if (!ValueToId<CanGC>(cx, args.get(0), &id)) {
     return false;
   }
 
   RootedValue value(cx, args.get(1));
@@ -949,47 +1042,50 @@ bool DebuggerObject::CallData::setProper
                        args.length() < 3 ? ObjectValue(*object) : args.get(2));
 
   Rooted<Completion> comp(cx);
   JS_TRY_VAR_OR_RETURN_FALSE(cx, comp,
                              setProperty(cx, object, id, value, receiver));
   return comp.get().buildCompletionValue(cx, dbg, args.rval());
 }
 
-bool DebuggerObject::CallData::applyMethod() {
-  RootedValue thisv(cx, args.get(0));
-
-  Rooted<ValueVector> nargs(cx, ValueVector(cx));
-  if (args.length() >= 2 && !args[1].isNullOrUndefined()) {
-    if (!args[1].isObject()) {
+/* static */
+bool DebuggerObject::applyMethod(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "apply", callArgs, object);
+
+  RootedValue thisv(cx, callArgs.get(0));
+
+  Rooted<ValueVector> args(cx, ValueVector(cx));
+  if (callArgs.length() >= 2 && !callArgs[1].isNullOrUndefined()) {
+    if (!callArgs[1].isObject()) {
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                 JSMSG_BAD_APPLY_ARGS, js_apply_str);
       return false;
     }
 
-    RootedObject argsobj(cx, &args[1].toObject());
+    RootedObject argsobj(cx, &callArgs[1].toObject());
 
     unsigned argc = 0;
     if (!GetLengthProperty(cx, argsobj, &argc)) {
       return false;
     }
     argc = unsigned(Min(argc, ARGS_LENGTH_MAX));
 
-    if (!nargs.growBy(argc) || !GetElements(cx, argsobj, argc, nargs.begin())) {
+    if (!args.growBy(argc) || !GetElements(cx, argsobj, argc, args.begin())) {
       return false;
     }
   }
 
   Rooted<Maybe<Completion>> completion(
-      cx, DebuggerObject::call(cx, object, thisv, nargs));
+      cx, DebuggerObject::call(cx, object, thisv, args));
   if (!completion.get()) {
     return false;
   }
 
-  return completion->buildCompletionValue(cx, object->owner(), args.rval());
+  return completion->buildCompletionValue(cx, object->owner(), callArgs.rval());
 }
 
 static void EnterDebuggeeObjectRealm(JSContext* cx, Maybe<AutoRealm>& ar,
                                      JSObject* referent) {
   // |referent| may be a cross-compartment wrapper and CCWs normally
   // shouldn't be used with AutoRealm, but here we use an arbitrary realm for
   // now because we don't really have another option.
   ar.emplace(cx, referent->maybeCCWRealm()->maybeGlobal());
@@ -1023,19 +1119,21 @@ static bool RequireGlobalObject(JSContex
                        nullptr, "a global object");
     }
     return false;
   }
 
   return true;
 }
 
-bool DebuggerObject::CallData::asEnvironmentMethod() {
-  Debugger* dbg = Debugger::fromChildJSObject(object);
-
+/* static */
+bool DebuggerObject::asEnvironmentMethod(JSContext* cx, unsigned argc,
+                                         Value* vp) {
+  THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "asEnvironment", args, dbg,
+                                  referent);
   if (!RequireGlobalObject(cx, args.thisv(), referent)) {
     return false;
   }
 
   Rooted<Env*> env(cx);
   {
     AutoRealm ar(cx, referent);
     env = GetDebugEnvironmentForGlobalLexicalEnvironment(cx);
@@ -1046,17 +1144,22 @@ bool DebuggerObject::CallData::asEnviron
 
   return dbg->wrapEnvironment(cx, env, args.rval());
 }
 
 // Lookup a binding on the referent's global scope and change it to undefined
 // if it is an uninitialized lexical, otherwise do nothing. The method's
 // JavaScript return value is true _only_ when an uninitialized lexical has been
 // altered, otherwise it is false.
-bool DebuggerObject::CallData::forceLexicalInitializationByNameMethod() {
+/* static */
+bool DebuggerObject::forceLexicalInitializationByNameMethod(JSContext* cx,
+                                                            unsigned argc,
+                                                            Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "forceLexicalInitializationByName", args,
+                   object)
   if (!args.requireAtLeast(
           cx, "Debugger.Object.prototype.forceLexicalInitializationByName",
           1)) {
     return false;
   }
 
   if (!DebuggerObject::requireGlobal(cx, object)) {
     return false;
@@ -1072,17 +1175,20 @@ bool DebuggerObject::CallData::forceLexi
                                                         result)) {
     return false;
   }
 
   args.rval().setBoolean(result);
   return true;
 }
 
-bool DebuggerObject::CallData::executeInGlobalMethod() {
+/* static */
+bool DebuggerObject::executeInGlobalMethod(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "executeInGlobal", args, object);
   if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobal",
                            1)) {
     return false;
   }
 
   if (!DebuggerObject::requireGlobal(cx, object)) {
     return false;
   }
@@ -1101,17 +1207,21 @@ bool DebuggerObject::CallData::executeIn
 
   Rooted<Completion> comp(cx);
   JS_TRY_VAR_OR_RETURN_FALSE(
       cx, comp,
       DebuggerObject::executeInGlobal(cx, object, chars, nullptr, options));
   return comp.get().buildCompletionValue(cx, object->owner(), args.rval());
 }
 
-bool DebuggerObject::CallData::executeInGlobalWithBindingsMethod() {
+/* static */
+bool DebuggerObject::executeInGlobalWithBindingsMethod(JSContext* cx,
+                                                       unsigned argc,
+                                                       Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "executeInGlobalWithBindings", args, object);
   if (!args.requireAtLeast(
           cx, "Debugger.Object.prototype.executeInGlobalWithBindings", 2)) {
     return false;
   }
 
   if (!DebuggerObject::requireGlobal(cx, object)) {
     return false;
   }
@@ -1150,17 +1260,19 @@ static bool CopyStringToVector(JSContext
   }
   if (!chars.appendN(0, flat->length() + 1)) {
     return false;
   }
   CopyChars(chars.begin(), *flat);
   return true;
 }
 
-bool DebuggerObject::CallData::createSource() {
+/* static */
+bool DebuggerObject::createSource(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "createSource", args, object);
   if (!args.requireAtLeast(
           cx, "Debugger.Object.prototype.createSource", 1)) {
     return false;
   }
 
   if (!DebuggerObject::requireGlobal(cx, object)) {
     return false;
   }
@@ -1268,65 +1380,74 @@ bool DebuggerObject::CallData::createSou
   if (!wrapped) {
     return false;
   }
 
   args.rval().setObject(*wrapped);
   return true;
 }
 
-bool DebuggerObject::CallData::makeDebuggeeValueMethod() {
+/* static */
+bool DebuggerObject::makeDebuggeeValueMethod(JSContext* cx, unsigned argc,
+                                             Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "makeDebuggeeValue", args, object);
   if (!args.requireAtLeast(cx, "Debugger.Object.prototype.makeDebuggeeValue",
                            1)) {
     return false;
   }
 
   return DebuggerObject::makeDebuggeeValue(cx, object, args[0], args.rval());
 }
 
-bool DebuggerObject::CallData::makeDebuggeeNativeFunctionMethod() {
+/* static */
+bool DebuggerObject::makeDebuggeeNativeFunctionMethod(JSContext* cx,
+                                                      unsigned argc,
+                                                      Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "makeDebuggeeNativeFunction", args, object);
   if (!args.requireAtLeast(
           cx, "Debugger.Object.prototype.makeDebuggeeNativeFunction", 1)) {
     return false;
   }
 
   return DebuggerObject::makeDebuggeeNativeFunction(cx, object, args[0],
                                                     args.rval());
 }
 
-bool DebuggerObject::CallData::isSameNativeMethod() {
-  if (!args.requireAtLeast(
-          cx, "Debugger.Object.prototype.isSameNative", 1)) {
-    return false;
-  }
-
-  return DebuggerObject::isSameNative(cx, object, args[0], args.rval());
-}
-
-bool DebuggerObject::CallData::unsafeDereferenceMethod() {
+/* static */
+bool DebuggerObject::unsafeDereferenceMethod(JSContext* cx, unsigned argc,
+                                             Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "unsafeDereference", args, object);
+
   RootedObject result(cx);
   if (!DebuggerObject::unsafeDereference(cx, object, &result)) {
     return false;
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-bool DebuggerObject::CallData::unwrapMethod() {
+/* static */
+bool DebuggerObject::unwrapMethod(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "unwrap", args, object);
+
   RootedDebuggerObject result(cx);
   if (!DebuggerObject::unwrap(cx, object, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
-bool DebuggerObject::CallData::setInstrumentationMethod() {
+/* static */
+bool DebuggerObject::setInstrumentationMethod(JSContext* cx, unsigned argc,
+                                              Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "setInstrumentation", args, object);
+
   if (!args.requireAtLeast(cx, "Debugger.Object.prototype.setInstrumentation",
                            2)) {
     return false;
   }
 
   if (!DebuggerObject::requireGlobal(cx, object)) {
     return false;
   }
@@ -1378,17 +1499,21 @@ bool DebuggerObject::CallData::setInstru
       return false;
     }
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerObject::CallData::setInstrumentationActiveMethod() {
+/* static */
+bool DebuggerObject::setInstrumentationActiveMethod(JSContext* cx,
+                                                    unsigned argc, Value* vp) {
+  THIS_DEBUGOBJECT(cx, argc, vp, "setInstrumentationActive", args, object);
+
   if (!DebuggerObject::requireGlobal(cx, object)) {
     return false;
   }
 
   if (!args.requireAtLeast(
           cx, "Debugger.Object.prototype.setInstrumentationActive", 1)) {
     return false;
   }
@@ -1403,86 +1528,93 @@ bool DebuggerObject::CallData::setInstru
     }
   }
 
   args.rval().setUndefined();
   return true;
 }
 
 const JSPropertySpec DebuggerObject::properties_[] = {
-    JS_DEBUG_PSG("callable", callableGetter),
-    JS_DEBUG_PSG("isBoundFunction", isBoundFunctionGetter),
-    JS_DEBUG_PSG("isArrowFunction", isArrowFunctionGetter),
-    JS_DEBUG_PSG("isGeneratorFunction", isGeneratorFunctionGetter),
-    JS_DEBUG_PSG("isAsyncFunction", isAsyncFunctionGetter),
-    JS_DEBUG_PSG("proto", protoGetter),
-    JS_DEBUG_PSG("class", classGetter),
-    JS_DEBUG_PSG("name", nameGetter),
-    JS_DEBUG_PSG("displayName", displayNameGetter),
-    JS_DEBUG_PSG("parameterNames", parameterNamesGetter),
-    JS_DEBUG_PSG("script", scriptGetter),
-    JS_DEBUG_PSG("environment", environmentGetter),
-    JS_DEBUG_PSG("boundTargetFunction", boundTargetFunctionGetter),
-    JS_DEBUG_PSG("boundThis", boundThisGetter),
-    JS_DEBUG_PSG("boundArguments", boundArgumentsGetter),
-    JS_DEBUG_PSG("allocationSite", allocationSiteGetter),
-    JS_DEBUG_PSG("errorMessageName", errorMessageNameGetter),
-    JS_DEBUG_PSG("errorNotes", errorNotesGetter),
-    JS_DEBUG_PSG("errorLineNumber", errorLineNumberGetter),
-    JS_DEBUG_PSG("errorColumnNumber", errorColumnNumberGetter),
-    JS_DEBUG_PSG("isProxy", isProxyGetter),
-    JS_DEBUG_PSG("proxyTarget", proxyTargetGetter),
-    JS_DEBUG_PSG("proxyHandler", proxyHandlerGetter),
+    JS_PSG("callable", DebuggerObject::callableGetter, 0),
+    JS_PSG("isBoundFunction", DebuggerObject::isBoundFunctionGetter, 0),
+    JS_PSG("isArrowFunction", DebuggerObject::isArrowFunctionGetter, 0),
+    JS_PSG("isGeneratorFunction", DebuggerObject::isGeneratorFunctionGetter, 0),
+    JS_PSG("isAsyncFunction", DebuggerObject::isAsyncFunctionGetter, 0),
+    JS_PSG("proto", DebuggerObject::protoGetter, 0),
+    JS_PSG("class", DebuggerObject::classGetter, 0),
+    JS_PSG("name", DebuggerObject::nameGetter, 0),
+    JS_PSG("displayName", DebuggerObject::displayNameGetter, 0),
+    JS_PSG("parameterNames", DebuggerObject::parameterNamesGetter, 0),
+    JS_PSG("script", DebuggerObject::scriptGetter, 0),
+    JS_PSG("environment", DebuggerObject::environmentGetter, 0),
+    JS_PSG("boundTargetFunction", DebuggerObject::boundTargetFunctionGetter, 0),
+    JS_PSG("boundThis", DebuggerObject::boundThisGetter, 0),
+    JS_PSG("boundArguments", DebuggerObject::boundArgumentsGetter, 0),
+    JS_PSG("allocationSite", DebuggerObject::allocationSiteGetter, 0),
+    JS_PSG("errorMessageName", DebuggerObject::errorMessageNameGetter, 0),
+    JS_PSG("errorNotes", DebuggerObject::errorNotesGetter, 0),
+    JS_PSG("errorLineNumber", DebuggerObject::errorLineNumberGetter, 0),
+    JS_PSG("errorColumnNumber", DebuggerObject::errorColumnNumberGetter, 0),
+    JS_PSG("isProxy", DebuggerObject::isProxyGetter, 0),
+    JS_PSG("proxyTarget", DebuggerObject::proxyTargetGetter, 0),
+    JS_PSG("proxyHandler", DebuggerObject::proxyHandlerGetter, 0),
     JS_PS_END};
 
 const JSPropertySpec DebuggerObject::promiseProperties_[] = {
-    JS_DEBUG_PSG("isPromise", isPromiseGetter),
-    JS_DEBUG_PSG("promiseState", promiseStateGetter),
-    JS_DEBUG_PSG("promiseValue", promiseValueGetter),
-    JS_DEBUG_PSG("promiseReason", promiseReasonGetter),
-    JS_DEBUG_PSG("promiseLifetime", promiseLifetimeGetter),
-    JS_DEBUG_PSG("promiseTimeToResolution", promiseTimeToResolutionGetter),
-    JS_DEBUG_PSG("promiseAllocationSite", promiseAllocationSiteGetter),
-    JS_DEBUG_PSG("promiseResolutionSite", promiseResolutionSiteGetter),
-    JS_DEBUG_PSG("promiseID", promiseIDGetter),
-    JS_DEBUG_PSG("promiseDependentPromises", promiseDependentPromisesGetter),
+    JS_PSG("isPromise", DebuggerObject::isPromiseGetter, 0),
+    JS_PSG("promiseState", DebuggerObject::promiseStateGetter, 0),
+    JS_PSG("promiseValue", DebuggerObject::promiseValueGetter, 0),
+    JS_PSG("promiseReason", DebuggerObject::promiseReasonGetter, 0),
+    JS_PSG("promiseLifetime", DebuggerObject::promiseLifetimeGetter, 0),
+    JS_PSG("promiseTimeToResolution",
+           DebuggerObject::promiseTimeToResolutionGetter, 0),
+    JS_PSG("promiseAllocationSite", DebuggerObject::promiseAllocationSiteGetter,
+           0),
+    JS_PSG("promiseResolutionSite", DebuggerObject::promiseResolutionSiteGetter,
+           0),
+    JS_PSG("promiseID", DebuggerObject::promiseIDGetter, 0),
+    JS_PSG("promiseDependentPromises",
+           DebuggerObject::promiseDependentPromisesGetter, 0),
     JS_PS_END};
 
 const JSFunctionSpec DebuggerObject::methods_[] = {
-    JS_DEBUG_FN("isExtensible", isExtensibleMethod, 0),
-    JS_DEBUG_FN("isSealed", isSealedMethod, 0),
-    JS_DEBUG_FN("isFrozen", isFrozenMethod, 0),
-    JS_DEBUG_FN("getProperty", getPropertyMethod, 0),
-    JS_DEBUG_FN("setProperty", setPropertyMethod, 0),
-    JS_DEBUG_FN("getOwnPropertyNames", getOwnPropertyNamesMethod, 0),
-    JS_DEBUG_FN("getOwnPropertySymbols", getOwnPropertySymbolsMethod, 0),
-    JS_DEBUG_FN("getOwnPropertyDescriptor", getOwnPropertyDescriptorMethod, 1),
-    JS_DEBUG_FN("preventExtensions", preventExtensionsMethod, 0),
-    JS_DEBUG_FN("seal", sealMethod, 0),
-    JS_DEBUG_FN("freeze", freezeMethod, 0),
-    JS_DEBUG_FN("defineProperty", definePropertyMethod, 2),
-    JS_DEBUG_FN("defineProperties", definePropertiesMethod, 1),
-    JS_DEBUG_FN("deleteProperty", deletePropertyMethod, 1),
-    JS_DEBUG_FN("call", callMethod, 0),
-    JS_DEBUG_FN("apply", applyMethod, 0),
-    JS_DEBUG_FN("asEnvironment", asEnvironmentMethod, 0),
-    JS_DEBUG_FN("forceLexicalInitializationByName",
-                forceLexicalInitializationByNameMethod, 1),
-    JS_DEBUG_FN("executeInGlobal", executeInGlobalMethod, 1),
-    JS_DEBUG_FN("executeInGlobalWithBindings",
-                executeInGlobalWithBindingsMethod, 2),
-    JS_DEBUG_FN("createSource", createSource, 1),
-    JS_DEBUG_FN("makeDebuggeeValue", makeDebuggeeValueMethod, 1),
-    JS_DEBUG_FN("makeDebuggeeNativeFunction",
-                makeDebuggeeNativeFunctionMethod, 1),
-    JS_DEBUG_FN("isSameNative", isSameNativeMethod, 1),
-    JS_DEBUG_FN("unsafeDereference", unsafeDereferenceMethod, 0),
-    JS_DEBUG_FN("unwrap", unwrapMethod, 0),
-    JS_DEBUG_FN("setInstrumentation", setInstrumentationMethod, 2),
-    JS_DEBUG_FN("setInstrumentationActive", setInstrumentationActiveMethod, 1),
+    JS_FN("isExtensible", DebuggerObject::isExtensibleMethod, 0, 0),
+    JS_FN("isSealed", DebuggerObject::isSealedMethod, 0, 0),
+    JS_FN("isFrozen", DebuggerObject::isFrozenMethod, 0, 0),
+    JS_FN("getProperty", DebuggerObject::getPropertyMethod, 0, 0),
+    JS_FN("setProperty", DebuggerObject::setPropertyMethod, 0, 0),
+    JS_FN("getOwnPropertyNames", DebuggerObject::getOwnPropertyNamesMethod, 0,
+          0),
+    JS_FN("getOwnPropertySymbols", DebuggerObject::getOwnPropertySymbolsMethod,
+          0, 0),
+    JS_FN("getOwnPropertyDescriptor",
+          DebuggerObject::getOwnPropertyDescriptorMethod, 1, 0),
+    JS_FN("preventExtensions", DebuggerObject::preventExtensionsMethod, 0, 0),
+    JS_FN("seal", DebuggerObject::sealMethod, 0, 0),
+    JS_FN("freeze", DebuggerObject::freezeMethod, 0, 0),
+    JS_FN("defineProperty", DebuggerObject::definePropertyMethod, 2, 0),
+    JS_FN("defineProperties", DebuggerObject::definePropertiesMethod, 1, 0),
+    JS_FN("deleteProperty", DebuggerObject::deletePropertyMethod, 1, 0),
+    JS_FN("call", DebuggerObject::callMethod, 0, 0),
+    JS_FN("apply", DebuggerObject::applyMethod, 0, 0),
+    JS_FN("asEnvironment", DebuggerObject::asEnvironmentMethod, 0, 0),
+    JS_FN("forceLexicalInitializationByName",
+          DebuggerObject::forceLexicalInitializationByNameMethod, 1, 0),
+    JS_FN("executeInGlobal", DebuggerObject::executeInGlobalMethod, 1, 0),
+    JS_FN("executeInGlobalWithBindings",
+          DebuggerObject::executeInGlobalWithBindingsMethod, 2, 0),
+    JS_FN("createSource", DebuggerObject::createSource, 1, 0),
+    JS_FN("makeDebuggeeValue", DebuggerObject::makeDebuggeeValueMethod, 1, 0),
+    JS_FN("makeDebuggeeNativeFunction",
+          DebuggerObject::makeDebuggeeNativeFunctionMethod, 1, 0),
+    JS_FN("unsafeDereference", DebuggerObject::unsafeDereferenceMethod, 0, 0),
+    JS_FN("unwrap", DebuggerObject::unwrapMethod, 0, 0),
+    JS_FN("setInstrumentation", DebuggerObject::setInstrumentationMethod, 2, 0),
+    JS_FN("setInstrumentationActive",
+          DebuggerObject::setInstrumentationActiveMethod, 1, 0),
     JS_FS_END};
 
 /* static */
 NativeObject* DebuggerObject::initClass(JSContext* cx,
                                         Handle<GlobalObject*> global,
                                         HandleObject debugCtor) {
   RootedNativeObject objectProto(
       cx, InitClass(cx, debugCtor, nullptr, &class_, construct, 0, properties_,
@@ -2366,42 +2498,37 @@ bool DebuggerObject::makeDebuggeeValue(J
       return false;
     }
   }
 
   result.set(value);
   return true;
 }
 
-static JSFunction* EnsureNativeFunction(const Value& value,
-                                        bool allowExtended = true) {
-  if (!value.isObject() || !value.toObject().is<JSFunction>()) {
-    return nullptr;
-  }
-
-  JSFunction* fun = &value.toObject().as<JSFunction>();
-  if (!fun->isNative() || (fun->isExtended() && !allowExtended)) {
-    return nullptr;
-  }
-
-  return fun;
-}
-
 /* static */
 bool DebuggerObject::makeDebuggeeNativeFunction(JSContext* cx,
                                                 HandleDebuggerObject object,
                                                 HandleValue value,
                                                 MutableHandleValue result) {
   RootedObject referent(cx, object->referent());
   Debugger* dbg = object->owner();
 
-  // The logic below doesn't work with extended functions, so do not allow them.
-  RootedFunction fun(cx, EnsureNativeFunction(value,
-                                              /* allowExtended */ false));
-  if (!fun) {
+  if (!value.isObject()) {
+    JS_ReportErrorASCII(cx, "Need object");
+    return false;
+  }
+
+  RootedObject obj(cx, &value.toObject());
+  if (!obj->is<JSFunction>()) {
+    JS_ReportErrorASCII(cx, "Need function");
+    return false;
+  }
+
+  RootedFunction fun(cx, &obj->as<JSFunction>());
+  if (!fun->isNative() || fun->isExtended()) {
     JS_ReportErrorASCII(cx, "Need native function");
     return false;
   }
 
   RootedValue newValue(cx);
   {
     Maybe<AutoRealm> ar;
     EnterDebuggeeObjectRealm(cx, ar, referent);
@@ -2421,53 +2548,16 @@ bool DebuggerObject::makeDebuggeeNativeF
   if (!dbg->wrapDebuggeeValue(cx, &newValue)) {
     return false;
   }
 
   result.set(newValue);
   return true;
 }
 
-static JSAtom* MaybeGetSelfHostedFunctionName(const Value& v) {
-  if (!v.isObject() || !v.toObject().is<JSFunction>()) {
-    return nullptr;
-  }
-
-  JSFunction* fun = &v.toObject().as<JSFunction>();
-  if (!fun->isSelfHostedBuiltin()) {
-    return nullptr;
-  }
-
-  return GetClonedSelfHostedFunctionName(fun);
-}
-
-/* static */
-bool DebuggerObject::isSameNative(JSContext* cx, HandleDebuggerObject object,
-                                  HandleValue value,
-                                  MutableHandleValue result) {
-  RootedValue referentValue(cx, ObjectValue(*object->referent()));
-
-  RootedFunction fun(cx, EnsureNativeFunction(value));
-  if (!fun) {
-    RootedAtom selfHostedName(cx, MaybeGetSelfHostedFunctionName(value));
-    if (!selfHostedName) {
-      JS_ReportErrorASCII(cx, "Need native function");
-      return false;
-    }
-
-    result.setBoolean(
-        selfHostedName == MaybeGetSelfHostedFunctionName(referentValue));
-    return true;
-  }
-
-  RootedFunction referentFun(cx, EnsureNativeFunction(referentValue));
-  result.setBoolean(referentFun && referentFun->native() == fun->native());
-  return true;
-}
-
 /* static */
 bool DebuggerObject::unsafeDereference(JSContext* cx,
                                        HandleDebuggerObject object,
                                        MutableHandleObject result) {
   RootedObject referent(cx, object->referent());
 
   if (!cx->compartment()->wrap(cx, &referent)) {
     return false;
--- a/js/src/debugger/Object.h
+++ b/js/src/debugger/Object.h
@@ -141,20 +141,16 @@ class DebuggerObject : public NativeObje
       const EvalOptions& options);
   static MOZ_MUST_USE bool makeDebuggeeValue(JSContext* cx,
                                              HandleDebuggerObject object,
                                              HandleValue value,
                                              MutableHandleValue result);
   static MOZ_MUST_USE bool makeDebuggeeNativeFunction(
       JSContext* cx, HandleDebuggerObject object, HandleValue value,
       MutableHandleValue result);
-  static MOZ_MUST_USE bool isSameNative(JSContext* cx,
-                                        HandleDebuggerObject object,
-                                        HandleValue value,
-                                        MutableHandleValue result);
   static MOZ_MUST_USE bool unsafeDereference(JSContext* cx,
                                              HandleDebuggerObject object,
                                              MutableHandleObject result);
   static MOZ_MUST_USE bool unwrap(JSContext* cx, HandleDebuggerObject object,
                                   MutableHandleDebuggerObject result);
 
   // Infallible properties
   bool isCallable() const;
@@ -194,18 +190,143 @@ class DebuggerObject : public NativeObje
   PromiseObject* promise() const;
 
   static MOZ_MUST_USE bool requireGlobal(JSContext* cx,
                                          HandleDebuggerObject object);
   static MOZ_MUST_USE bool requirePromise(JSContext* cx,
                                           HandleDebuggerObject object);
   static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
 
-  struct CallData;
+  // JSNative properties
+  static MOZ_MUST_USE bool callableGetter(JSContext* cx, unsigned argc,
+                                          Value* vp);
+  static MOZ_MUST_USE bool isBoundFunctionGetter(JSContext* cx, unsigned argc,
+                                                 Value* vp);
+  static MOZ_MUST_USE bool isArrowFunctionGetter(JSContext* cx, unsigned argc,
+                                                 Value* vp);
+  static MOZ_MUST_USE bool isAsyncFunctionGetter(JSContext* cx, unsigned argc,
+                                                 Value* vp);
+  static MOZ_MUST_USE bool isGeneratorFunctionGetter(JSContext* cx,
+                                                     unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool protoGetter(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool classGetter(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool nameGetter(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool displayNameGetter(JSContext* cx, unsigned argc,
+                                             Value* vp);
+  static MOZ_MUST_USE bool parameterNamesGetter(JSContext* cx, unsigned argc,
+                                                Value* vp);
+  static MOZ_MUST_USE bool scriptGetter(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc,
+                                             Value* vp);
+  static MOZ_MUST_USE bool boundTargetFunctionGetter(JSContext* cx,
+                                                     unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool boundThisGetter(JSContext* cx, unsigned argc,
+                                           Value* vp);
+  static MOZ_MUST_USE bool boundArgumentsGetter(JSContext* cx, unsigned argc,
+                                                Value* vp);
+  static MOZ_MUST_USE bool allocationSiteGetter(JSContext* cx, unsigned argc,
+                                                Value* vp);
+  static MOZ_MUST_USE bool errorMessageNameGetter(JSContext* cx, unsigned argc,
+                                                  Value* vp);
+  static MOZ_MUST_USE bool errorNotesGetter(JSContext* cx, unsigned argc,
+                                            Value* vp);
+  static MOZ_MUST_USE bool errorLineNumberGetter(JSContext* cx, unsigned argc,
+                                                 Value* vp);
+  static MOZ_MUST_USE bool errorColumnNumberGetter(JSContext* cx, unsigned argc,
+                                                   Value* vp);
+  static MOZ_MUST_USE bool isProxyGetter(JSContext* cx, unsigned argc,
+                                         Value* vp);
+  static MOZ_MUST_USE bool proxyTargetGetter(JSContext* cx, unsigned argc,
+                                             Value* vp);
+  static MOZ_MUST_USE bool proxyHandlerGetter(JSContext* cx, unsigned argc,
+                                              Value* vp);
+  static MOZ_MUST_USE bool isPromiseGetter(JSContext* cx, unsigned argc,
+                                           Value* vp);
+  static MOZ_MUST_USE bool promiseStateGetter(JSContext* cx, unsigned argc,
+                                              Value* vp);
+  static MOZ_MUST_USE bool promiseValueGetter(JSContext* cx, unsigned argc,
+                                              Value* vp);
+  static MOZ_MUST_USE bool promiseReasonGetter(JSContext* cx, unsigned argc,
+                                               Value* vp);
+  static MOZ_MUST_USE bool promiseLifetimeGetter(JSContext* cx, unsigned argc,
+                                                 Value* vp);
+  static MOZ_MUST_USE bool promiseTimeToResolutionGetter(JSContext* cx,
+                                                         unsigned argc,
+                                                         Value* vp);
+  static MOZ_MUST_USE bool promiseAllocationSiteGetter(JSContext* cx,
+                                                       unsigned argc,
+                                                       Value* vp);
+  static MOZ_MUST_USE bool promiseResolutionSiteGetter(JSContext* cx,
+                                                       unsigned argc,
+                                                       Value* vp);
+  static MOZ_MUST_USE bool promiseIDGetter(JSContext* cx, unsigned argc,
+                                           Value* vp);
+  static MOZ_MUST_USE bool promiseDependentPromisesGetter(JSContext* cx,
+                                                          unsigned argc,
+                                                          Value* vp);
 
+  // JSNative methods
+  static MOZ_MUST_USE bool isExtensibleMethod(JSContext* cx, unsigned argc,
+                                              Value* vp);
+  static MOZ_MUST_USE bool isSealedMethod(JSContext* cx, unsigned argc,
+                                          Value* vp);
+  static MOZ_MUST_USE bool isFrozenMethod(JSContext* cx, unsigned argc,
+                                          Value* vp);
+  static MOZ_MUST_USE bool getPropertyMethod(JSContext* cx, unsigned argc,
+                                             Value* vp);
+  static MOZ_MUST_USE bool setPropertyMethod(JSContext* cx, unsigned argc,
+                                             Value* vp);
+  static MOZ_MUST_USE bool getOwnPropertyNamesMethod(JSContext* cx,
+                                                     unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool getOwnPropertySymbolsMethod(JSContext* cx,
+                                                       unsigned argc,
+                                                       Value* vp);
+  static MOZ_MUST_USE bool getOwnPropertyDescriptorMethod(JSContext* cx,
+                                                          unsigned argc,
+                                                          Value* vp);
+  static MOZ_MUST_USE bool preventExtensionsMethod(JSContext* cx, unsigned argc,
+                                                   Value* vp);
+  static MOZ_MUST_USE bool sealMethod(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool freezeMethod(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static MOZ_MUST_USE bool definePropertyMethod(JSContext* cx, unsigned argc,
+                                                Value* vp);
+  static MOZ_MUST_USE bool definePropertiesMethod(JSContext* cx, unsigned argc,
+                                                  Value* vp);
+  static MOZ_MUST_USE bool deletePropertyMethod(JSContext* cx, unsigned argc,
+                                                Value* vp);
+  static MOZ_MUST_USE bool callMethod(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool applyMethod(JSContext* cx, unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool asEnvironmentMethod(JSContext* cx, unsigned argc,
+                                               Value* vp);
+  static MOZ_MUST_USE bool forceLexicalInitializationByNameMethod(JSContext* cx,
+                                                                  unsigned argc,
+                                                                  Value* vp);
+  static MOZ_MUST_USE bool executeInGlobalMethod(JSContext* cx, unsigned argc,
+                                                 Value* vp);
+  static MOZ_MUST_USE bool executeInGlobalWithBindingsMethod(JSContext* cx,
+                                                             unsigned argc,
+                                                             Value* vp);
+  static MOZ_MUST_USE bool createSource(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static MOZ_MUST_USE bool makeDebuggeeValueMethod(JSContext* cx, unsigned argc,
+                                                   Value* vp);
+  static MOZ_MUST_USE bool makeDebuggeeNativeFunctionMethod(JSContext* cx,
+                                                            unsigned argc,
+                                                            Value* vp);
+  static MOZ_MUST_USE bool unsafeDereferenceMethod(JSContext* cx, unsigned argc,
+                                                   Value* vp);
+  static MOZ_MUST_USE bool unwrapMethod(JSContext* cx, unsigned argc,
+                                        Value* vp);
+  static MOZ_MUST_USE bool setInstrumentationMethod(JSContext* cx,
+                                                    unsigned argc, Value* vp);
+  static MOZ_MUST_USE bool setInstrumentationActiveMethod(JSContext* cx,
+                                                          unsigned argc,
+                                                          Value* vp);
   static MOZ_MUST_USE bool getErrorReport(JSContext* cx,
                                           HandleObject maybeError,
                                           JSErrorReport*& report);
 };
 
 } /* namespace js */
 
 #endif /* debugger_Object_h */
--- a/js/src/debugger/Script.cpp
+++ b/js/src/debugger/Script.cpp
@@ -173,187 +173,142 @@ static JSScript* DelazifyScript(JSContex
   RootedFunction fun(cx, LazyScript::functionDelazifying(cx, lazyScript));
   if (!fun) {
     return nullptr;
   }
   return fun->getOrCreateScript(cx, fun);
 }
 
 /* static */
-DebuggerScript* DebuggerScript::check(JSContext* cx, HandleValue v) {
+DebuggerScript* DebuggerScript::check(JSContext* cx, HandleValue v,
+                                      const char* fnname) {
   JSObject* thisobj = RequireObject(cx, v);
   if (!thisobj) {
     return nullptr;
   }
   if (!thisobj->is<DebuggerScript>()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Script",
-                              "method", thisobj->getClass()->name);
+                              fnname, thisobj->getClass()->name);
     return nullptr;
   }
 
   DebuggerScript& scriptObj = thisobj->as<DebuggerScript>();
 
   // Check for Debugger.Script.prototype, which is of class
   // DebuggerScript::class but whose script is null.
   if (!scriptObj.getReferentCell()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Script",
-                              "method", "prototype object");
+                              fnname, "prototype object");
     return nullptr;
   }
 
   return &scriptObj;
 }
 
-struct MOZ_STACK_CLASS DebuggerScript::CallData {
-  JSContext* cx;
-  const CallArgs& args;
-
-  HandleDebuggerScript obj;
-  Rooted<DebuggerScriptReferent> referent;
-  RootedScript script;
-
-  CallData(JSContext* cx, const CallArgs& args, HandleDebuggerScript obj)
-      : cx(cx), args(args), obj(obj), referent(cx, obj->getReferent()),
-        script(cx) {}
-
-  MOZ_MUST_USE bool ensureScriptMaybeLazy() {
-    if (!referent.is<JSScript*>() && !referent.is<LazyScript*>()) {
-      ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
-                       args.thisv(), nullptr, "a JS script");
-      return false;
-    }
-    return true;
+/* static */
+DebuggerScript* DebuggerScript::checkThis(JSContext* cx, const CallArgs& args,
+                                          const char* fnname) {
+  DebuggerScript* thisobj = DebuggerScript::check(cx, args.thisv(), fnname);
+  if (!thisobj) {
+    return nullptr;
   }
 
-  MOZ_MUST_USE bool ensureScript() {
-    if (!ensureScriptMaybeLazy()) {
-      return false;
-    }
-    if (referent.is<JSScript*>()) {
-      script = referent.as<JSScript*>();
-    } else {
-      Rooted<LazyScript*> lazyScript(cx, referent.as<LazyScript*>());
-      script = DelazifyScript(cx, lazyScript);
-      if (!script) {
-        return false;
-      }
-    }
-    return true;
+  if (!thisobj->getReferent().is<JSScript*>() &&
+      !thisobj->getReferent().is<LazyScript*>()) {
+    ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
+                     args.thisv(), nullptr, "a JS script");
+    return nullptr;
   }
 
-  bool getIsGeneratorFunction();
-  bool getIsAsyncFunction();
-  bool getIsFunction();
-  bool getIsModule();
-  bool getDisplayName();
-  bool getUrl();
-  bool getStartLine();
-  bool getStartColumn();
-  bool getLineCount();
-  bool getSource();
-  bool getSourceStart();
-  bool getSourceLength();
-  bool getMainOffset();
-  bool getGlobal();
-  bool getFormat();
-  bool getChildScripts();
-  bool getPossibleBreakpoints();
-  bool getPossibleBreakpointOffsets();
-  bool getOffsetMetadata();
-  bool getOffsetLocation();
-  template <bool Successor>
-  bool getSuccessorOrPredecessorOffsets();
-  bool getEffectfulOffsets();
-  bool getAllOffsets();
-  bool getAllColumnOffsets();
-  bool getLineOffsets();
-  bool setBreakpoint();
-  bool getBreakpoints();
-  bool clearBreakpoint();
-  bool clearAllBreakpoints();
-  bool isInCatchScope();
-  bool getOffsetsCoverage();
-  bool setInstrumentationId();
+  return thisobj;
+}
+
+#define THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, fnname, args, obj, referent) \
+  CallArgs args = CallArgsFromVp(argc, vp);                                  \
+  RootedDebuggerScript obj(cx,                                               \
+                           DebuggerScript::check(cx, args.thisv(), fnname)); \
+  if (!obj) return false;                                                    \
+  Rooted<DebuggerScriptReferent> referent(cx, obj->getReferent())
 
-  using Method = bool (CallData::*)();
-
-  template <Method MyMethod>
-  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
-};
+#define THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, fnname, args, obj)  \
+  CallArgs args = CallArgsFromVp(argc, vp);                                  \
+  RootedDebuggerScript obj(cx, DebuggerScript::checkThis(cx, args, fnname)); \
+  if (!obj) return false;
 
-template <DebuggerScript::CallData::Method MyMethod>
-/* static */
-bool DebuggerScript::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  RootedDebuggerScript obj(cx, DebuggerScript::check(cx, args.thisv()));
-  if (!obj) {
-    return false;
+#define THIS_DEBUGSCRIPT_SCRIPT_DELAZIFY(cx, argc, vp, fnname, args, obj,     \
+                                         script)                              \
+  THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, fnname, args, obj);        \
+  RootedScript script(cx);                                                    \
+  if (obj->getReferent().is<JSScript*>()) {                                   \
+    script = obj->getReferent().as<JSScript*>();                              \
+  } else {                                                                    \
+    Rooted<LazyScript*> lazyScript(cx, obj->getReferent().as<LazyScript*>()); \
+    script = DelazifyScript(cx, lazyScript);                                  \
+    if (!script) return false;                                                \
   }
 
-  CallData data(cx, args, obj);
-  return (data.*MyMethod)();
-}
-
 template <typename Result>
 Result CallScriptMethod(HandleDebuggerScript obj,
                         Result (JSScript::*ifJSScript)() const,
                         Result (LazyScript::*ifLazyScript)() const) {
   if (obj->getReferent().is<JSScript*>()) {
     JSScript* script = obj->getReferent().as<JSScript*>();
     return (script->*ifJSScript)();
   }
 
   MOZ_ASSERT(obj->getReferent().is<LazyScript*>());
   LazyScript* lazyScript = obj->getReferent().as<LazyScript*>();
   return (lazyScript->*ifLazyScript)();
 }
 
-bool DebuggerScript::CallData::getIsGeneratorFunction() {
-  if (!ensureScriptMaybeLazy()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getIsGeneratorFunction(JSContext* cx, unsigned argc,
+                                            Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get isGeneratorFunction)",
+                                     args, obj);
   args.rval().setBoolean(obj->getReferentScript()->isGenerator());
   return true;
 }
 
-bool DebuggerScript::CallData::getIsAsyncFunction() {
-  if (!ensureScriptMaybeLazy()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getIsAsyncFunction(JSContext* cx, unsigned argc,
+                                        Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get isAsyncFunction)",
+                                     args, obj);
   args.rval().setBoolean(obj->getReferentScript()->isAsync());
   return true;
 }
 
-bool DebuggerScript::CallData::getIsFunction() {
-  if (!ensureScriptMaybeLazy()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getIsFunction(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get isFunction)",
+                                     args, obj);
+  DebuggerScriptReferent referent = obj->getReferent();
 
   // Note: LazyScripts always have functions.
   args.rval().setBoolean(!referent.is<JSScript*>() ||
                          referent.as<JSScript*>()->functionNonDelazifying());
   return true;
 }
 
-bool DebuggerScript::CallData::getIsModule() {
-  if (!ensureScriptMaybeLazy()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getIsModule(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get isModule)", args, obj);
+  DebuggerScriptReferent referent = obj->getReferent();
   args.rval().setBoolean(referent.is<JSScript*>() &&
                          referent.as<JSScript*>()->isModule());
   return true;
 }
 
-bool DebuggerScript::CallData::getDisplayName() {
-  if (!ensureScriptMaybeLazy()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getDisplayName(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get displayName)", args,
+                                     obj);
   JSFunction* func = CallScriptMethod(obj, &JSScript::functionNonDelazifying,
                                       &LazyScript::functionNonDelazifying);
   Debugger* dbg = Debugger::fromChildJSObject(obj);
 
   JSString* name = func ? func->displayAtom() : nullptr;
   if (!name) {
     args.rval().setUndefined();
     return true;
@@ -364,17 +319,17 @@ bool DebuggerScript::CallData::getDispla
     return false;
   }
   args.rval().set(namev);
   return true;
 }
 
 template <typename T>
 /* static */
-bool DebuggerScript::getUrlImpl(JSContext* cx, const CallArgs& args,
+bool DebuggerScript::getUrlImpl(JSContext* cx, CallArgs& args,
                                 Handle<T*> script) {
   if (script->filename()) {
     JSString* str;
     if (script->scriptSource()->introducerFilename()) {
       str = NewStringCopyZ<CanGC>(cx,
                                   script->scriptSource()->introducerFilename());
     } else {
       str = NewStringCopyZ<CanGC>(cx, script->filename());
@@ -384,57 +339,61 @@ bool DebuggerScript::getUrlImpl(JSContex
     }
     args.rval().setString(str);
   } else {
     args.rval().setNull();
   }
   return true;
 }
 
-bool DebuggerScript::CallData::getUrl() {
-  if (!ensureScriptMaybeLazy()) {
-    return false;
-  }
-
-  if (referent.is<JSScript*>()) {
-    RootedScript script(cx, referent.as<JSScript*>());
+/* static */
+bool DebuggerScript::getUrl(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get url)", args, obj);
+  if (obj->getReferent().is<JSScript*>()) {
+    RootedScript script(cx, obj->getReferent().as<JSScript*>());
     return getUrlImpl<JSScript>(cx, args, script);
   }
 
-  Rooted<LazyScript*> lazyScript(cx, referent.as<LazyScript*>());
+  Rooted<LazyScript*> lazyScript(cx, obj->getReferent().as<LazyScript*>());
   return getUrlImpl<LazyScript>(cx, args, lazyScript);
 }
 
 struct DebuggerScript::GetStartLineMatcher {
   using ReturnType = uint32_t;
 
   ReturnType match(HandleScript script) { return script->lineno(); }
   ReturnType match(Handle<LazyScript*> lazyScript) {
     return lazyScript->lineno();
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return 1; }
 };
 
-bool DebuggerScript::CallData::getStartLine() {
+/* static */
+bool DebuggerScript::getStartLine(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get startLine)", args, obj,
+                            referent);
   GetStartLineMatcher matcher;
   args.rval().setNumber(referent.match(matcher));
   return true;
 }
 
 struct DebuggerScript::GetStartColumnMatcher {
   using ReturnType = uint32_t;
 
   ReturnType match(HandleScript script) { return script->column(); }
   ReturnType match(Handle<LazyScript*> lazyScript) {
     return lazyScript->column();
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return 0; }
 };
 
-bool DebuggerScript::CallData::getStartColumn() {
+/* static */
+bool DebuggerScript::getStartColumn(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get startColumn)", args, obj,
+                            referent);
   GetStartColumnMatcher matcher;
   args.rval().setNumber(referent.match(matcher));
   return true;
 }
 
 struct DebuggerScript::GetLineCountMatcher {
   JSContext* cx_;
   double totalLines;
@@ -459,17 +418,20 @@ struct DebuggerScript::GetLineCountMatch
       totalLines = double(instance.debug().bytecode().length());
     } else {
       totalLines = 0;
     }
     return true;
   }
 };
 
-bool DebuggerScript::CallData::getLineCount() {
+/* static */
+bool DebuggerScript::getLineCount(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get lineCount)", args, obj,
+                            referent);
   GetLineCountMatcher matcher(cx);
   if (!referent.match(matcher)) {
     return false;
   }
   args.rval().setNumber(matcher.totalLines);
   return true;
 }
 
@@ -496,57 +458,59 @@ class DebuggerScript::GetSourceMatcher {
     RootedScriptSourceObject source(cx_, lazyScript->sourceObject());
     return dbg_->wrapSource(cx_, source);
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
     return dbg_->wrapWasmSource(cx_, wasmInstance);
   }
 };
 
-bool DebuggerScript::CallData::getSource() {
+/* static */
+bool DebuggerScript::getSource(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get source)", args, obj, referent);
   Debugger* dbg = Debugger::fromChildJSObject(obj);
 
   GetSourceMatcher matcher(cx, dbg);
   RootedDebuggerSource sourceObject(cx, referent.match(matcher));
   if (!sourceObject) {
     return false;
   }
 
   args.rval().setObject(*sourceObject);
   return true;
 }
 
-bool DebuggerScript::CallData::getSourceStart() {
-  if (!ensureScriptMaybeLazy()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getSourceStart(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get sourceStart)", args,
+                                     obj);
   args.rval().setNumber(uint32_t(obj->getReferentScript()->sourceStart()));
   return true;
 }
 
-bool DebuggerScript::CallData::getSourceLength() {
-  if (!ensureScriptMaybeLazy()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getSourceLength(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get sourceEnd)", args,
+                                     obj);
   args.rval().setNumber(uint32_t(obj->getReferentScript()->sourceLength()));
   return true;
 }
 
-bool DebuggerScript::CallData::getMainOffset() {
-  if (!ensureScript()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getMainOffset(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_DELAZIFY(cx, argc, vp, "(get mainOffset)", args, obj,
+                                   script);
   args.rval().setNumber(uint32_t(script->mainOffset()));
   return true;
 }
 
-bool DebuggerScript::CallData::getGlobal() {
-  if (!ensureScript()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getGlobal(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_DELAZIFY(cx, argc, vp, "(get global)", args, obj,
+                                   script);
   Debugger* dbg = Debugger::fromChildJSObject(obj);
 
   RootedValue v(cx, ObjectValue(script->global()));
   if (!dbg->wrapDebuggeeValue(cx, &v)) {
     return false;
   }
   args.rval().set(v);
   return true;
@@ -560,17 +524,19 @@ class DebuggerScript::GetFormatMatcher {
   using ReturnType = JSAtom*;
   ReturnType match(HandleScript script) { return names_.js; }
   ReturnType match(Handle<LazyScript*> lazyScript) { return names_.js; }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
     return names_.wasm;
   }
 };
 
-bool DebuggerScript::CallData::getFormat() {
+/* static */
+bool DebuggerScript::getFormat(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get format)", args, obj, referent);
   GetFormatMatcher matcher(cx->names());
   args.rval().setString(referent.match(matcher));
   return true;
 }
 
 static bool PushFunctionScript(JSContext* cx, Debugger* dbg, HandleFunction fun,
                                HandleObject array) {
   // Ignore asm.js natives.
@@ -586,20 +552,20 @@ static bool PushFunctionScript(JSContext
   } else {
     RootedScript script(cx, fun->nonLazyScript());
     wrapped = dbg->wrapScript(cx, script);
   }
 
   return wrapped && NewbornArrayPush(cx, array, ObjectValue(*wrapped));
 }
 
-bool DebuggerScript::CallData::getChildScripts() {
-  if (!ensureScriptMaybeLazy()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getChildScripts(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "getChildScripts", args,
+                                     obj);
   Debugger* dbg = Debugger::fromChildJSObject(obj);
 
   RootedObject result(cx, NewDenseEmptyArray(cx));
   if (!result) {
     return false;
   }
 
   RootedFunction fun(cx);
@@ -966,34 +932,44 @@ class DebuggerScript::GetPossibleBreakpo
       if (!maybeAppendEntry(offset, lineno, column, true)) {
         return false;
       }
     }
     return true;
   }
 };
 
-bool DebuggerScript::CallData::getPossibleBreakpoints() {
+/* static */
+bool DebuggerScript::getPossibleBreakpoints(JSContext* cx, unsigned argc,
+                                            Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getPossibleBreakpoints", args, obj,
+                            referent);
+
   RootedObject result(cx);
   GetPossibleBreakpointsMatcher<false> matcher(cx, &result);
   if (args.length() >= 1 && !args[0].isUndefined()) {
     RootedObject queryObject(cx, RequireObject(cx, args[0]));
     if (!queryObject || !matcher.parseQuery(queryObject)) {
       return false;
     }
   }
   if (!referent.match(matcher)) {
     return false;
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-bool DebuggerScript::CallData::getPossibleBreakpointOffsets() {
+/* static */
+bool DebuggerScript::getPossibleBreakpointOffsets(JSContext* cx, unsigned argc,
+                                                  Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getPossibleBreakpointOffsets", args,
+                            obj, referent);
+
   RootedObject result(cx);
   GetPossibleBreakpointsMatcher<true> matcher(cx, &result);
   if (args.length() >= 1 && !args[0].isUndefined()) {
     RootedObject queryObject(cx, RequireObject(cx, args[0]));
     if (!queryObject || !matcher.parseQuery(queryObject)) {
       return false;
     }
   }
@@ -1099,17 +1075,21 @@ class DebuggerScript::GetOffsetMetadataM
     if (!DefineDataProperty(cx_, result_, cx_->names().isStepStart, value)) {
       return false;
     }
 
     return true;
   }
 };
 
-bool DebuggerScript::CallData::getOffsetMetadata() {
+/* static */
+bool DebuggerScript::getOffsetMetadata(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getOffsetMetadata", args, obj,
+                            referent);
   if (!args.requireAtLeast(cx, "Debugger.Script.getOffsetMetadata", 1)) {
     return false;
   }
   size_t offset;
   if (!ScriptOffset(cx, args[0], &offset)) {
     return false;
   }
 
@@ -1408,17 +1388,21 @@ class DebuggerScript::GetOffsetLocationM
     if (!DefineDataProperty(cx_, result_, cx_->names().isEntryPoint, value)) {
       return false;
     }
 
     return true;
   }
 };
 
-bool DebuggerScript::CallData::getOffsetLocation() {
+/* static */
+bool DebuggerScript::getOffsetLocation(JSContext* cx, unsigned argc,
+                                       Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getOffsetLocation", args, obj,
+                            referent);
   if (!args.requireAtLeast(cx, "Debugger.Script.getOffsetLocation", 1)) {
     return false;
   }
   size_t offset;
   if (!ScriptOffset(cx, args[0], &offset)) {
     return false;
   }
 
@@ -1488,322 +1472,60 @@ class DebuggerScript::GetSuccessorOrPred
 
   ReturnType match(Handle<WasmInstanceObject*> instance) {
     JS_ReportErrorASCII(
         cx_, "getSuccessorOrPredecessorOffsets NYI on wasm instances");
     return false;
   }
 };
 
-template <bool Successor>
-bool DebuggerScript::CallData::getSuccessorOrPredecessorOffsets() {
-  if (!args.requireAtLeast(cx, "successorOrPredecessorOffsets", 1)) {
+/* static */
+bool DebuggerScript::getSuccessorOrPredecessorOffsets(JSContext* cx,
+                                                      unsigned argc, Value* vp,
+                                                      const char* name,
+                                                      bool successor) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, name, args, obj, referent);
+
+  if (!args.requireAtLeast(cx, name, 1)) {
     return false;
   }
   size_t offset;
   if (!ScriptOffset(cx, args[0], &offset)) {
     return false;
   }
 
   RootedObject result(cx);
-  GetSuccessorOrPredecessorOffsetsMatcher matcher(cx, offset, Successor,
+  GetSuccessorOrPredecessorOffsetsMatcher matcher(cx, offset, successor,
                                                   &result);
   if (!referent.match(matcher)) {
     return false;
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-// Return whether an opcode is considered effectful: it can have direct side
-// effects that can be observed outside of the current frame. Opcodes are not
-// effectful if they only modify the current frame's state, modify objects
-// created by the current frame, or can potentially call other scripts or
-// natives which could have side effects.
-static bool BytecodeIsEffectful(JSOp op) {
-  switch (op) {
-    case JSOP_SETPROP:
-    case JSOP_STRICTSETPROP:
-    case JSOP_SETPROP_SUPER:
-    case JSOP_STRICTSETPROP_SUPER:
-    case JSOP_SETELEM:
-    case JSOP_STRICTSETELEM:
-    case JSOP_SETELEM_SUPER:
-    case JSOP_STRICTSETELEM_SUPER:
-    case JSOP_SETNAME:
-    case JSOP_STRICTSETNAME:
-    case JSOP_SETGNAME:
-    case JSOP_STRICTSETGNAME:
-    case JSOP_DELPROP:
-    case JSOP_STRICTDELPROP:
-    case JSOP_DELELEM:
-    case JSOP_STRICTDELELEM:
-    case JSOP_DELNAME:
-    case JSOP_SETALIASEDVAR:
-    case JSOP_INITHOMEOBJECT:
-    case JSOP_INITALIASEDLEXICAL:
-    case JSOP_SETINTRINSIC:
-    case JSOP_INITGLEXICAL:
-    case JSOP_DEFVAR:
-    case JSOP_DEFLET:
-    case JSOP_DEFCONST:
-    case JSOP_DEFFUN:
-    case JSOP_SETFUNNAME:
-    case JSOP_MUTATEPROTO:
-    case JSOP_DYNAMIC_IMPORT:
-      // Treat async functions as effectful so that microtask checkpoints
-      // won't run.
-    case JSOP_INITIALYIELD:
-    case JSOP_YIELD:
-      return true;
-
-    case JSOP_NOP:
-    case JSOP_NOP_DESTRUCTURING:
-    case JSOP_TRY_DESTRUCTURING:
-    case JSOP_LINENO:
-    case JSOP_JUMPTARGET:
-    case JSOP_LABEL:
-    case JSOP_UNDEFINED:
-    case JSOP_IFNE:
-    case JSOP_IFEQ:
-    case JSOP_RETURN:
-    case JSOP_RETRVAL:
-    case JSOP_AND:
-    case JSOP_OR:
-    case JSOP_TRY:
-    case JSOP_THROW:
-    case JSOP_GOTO:
-    case JSOP_CONDSWITCH:
-    case JSOP_TABLESWITCH:
-    case JSOP_CASE:
-    case JSOP_DEFAULT:
-    case JSOP_BITNOT:
-    case JSOP_BITAND:
-    case JSOP_BITOR:
-    case JSOP_BITXOR:
-    case JSOP_LSH:
-    case JSOP_RSH:
-    case JSOP_URSH:
-    case JSOP_ADD:
-    case JSOP_SUB:
-    case JSOP_MUL:
-    case JSOP_DIV:
-    case JSOP_MOD:
-    case JSOP_POW:
-    case JSOP_POS:
-    case JSOP_TONUMERIC:
-    case JSOP_NEG:
-    case JSOP_INC:
-    case JSOP_DEC:
-    case JSOP_TOSTRING:
-    case JSOP_EQ:
-    case JSOP_NE:
-    case JSOP_STRICTEQ:
-    case JSOP_STRICTNE:
-    case JSOP_LT:
-    case JSOP_LE:
-    case JSOP_GT:
-    case JSOP_GE:
-    case JSOP_DOUBLE:
-    case JSOP_BIGINT:
-    case JSOP_STRING:
-    case JSOP_SYMBOL:
-    case JSOP_ZERO:
-    case JSOP_ONE:
-    case JSOP_NULL:
-    case JSOP_VOID:
-    case JSOP_HOLE:
-    case JSOP_FALSE:
-    case JSOP_TRUE:
-    case JSOP_ARGUMENTS:
-    case JSOP_REST:
-    case JSOP_GETARG:
-    case JSOP_SETARG:
-    case JSOP_GETLOCAL:
-    case JSOP_SETLOCAL:
-    case JSOP_THROWSETCONST:
-    case JSOP_THROWSETALIASEDCONST:
-    case JSOP_THROWSETCALLEE:
-    case JSOP_CHECKLEXICAL:
-    case JSOP_INITLEXICAL:
-    case JSOP_CHECKALIASEDLEXICAL:
-    case JSOP_UNINITIALIZED:
-    case JSOP_POP:
-    case JSOP_POPN:
-    case JSOP_DUPAT:
-    case JSOP_NEWARRAY:
-    case JSOP_NEWARRAY_COPYONWRITE:
-    case JSOP_NEWINIT:
-    case JSOP_NEWOBJECT:
-    case JSOP_INITELEM:
-    case JSOP_INITHIDDENELEM:
-    case JSOP_INITELEM_INC:
-    case JSOP_INITELEM_ARRAY:
-    case JSOP_INITPROP:
-    case JSOP_INITLOCKEDPROP:
-    case JSOP_INITHIDDENPROP:
-    case JSOP_INITPROP_GETTER:
-    case JSOP_INITHIDDENPROP_GETTER:
-    case JSOP_INITPROP_SETTER:
-    case JSOP_INITHIDDENPROP_SETTER:
-    case JSOP_INITELEM_GETTER:
-    case JSOP_INITHIDDENELEM_GETTER:
-    case JSOP_INITELEM_SETTER:
-    case JSOP_INITHIDDENELEM_SETTER:
-    case JSOP_FUNCALL:
-    case JSOP_FUNAPPLY:
-    case JSOP_SPREADCALL:
-    case JSOP_CALL:
-    case JSOP_CALL_IGNORES_RV:
-    case JSOP_CALLITER:
-    case JSOP_NEW:
-    case JSOP_EVAL:
-    case JSOP_STRICTEVAL:
-    case JSOP_INT8:
-    case JSOP_UINT16:
-    case JSOP_GETGNAME:
-    case JSOP_GETNAME:
-    case JSOP_GETINTRINSIC:
-    case JSOP_GETIMPORT:
-    case JSOP_BINDGNAME:
-    case JSOP_BINDNAME:
-    case JSOP_BINDVAR:
-    case JSOP_DUP:
-    case JSOP_DUP2:
-    case JSOP_SWAP:
-    case JSOP_PICK:
-    case JSOP_UNPICK:
-    case JSOP_GETALIASEDVAR:
-    case JSOP_UINT24:
-    case JSOP_RESUMEINDEX:
-    case JSOP_INT32:
-    case JSOP_LOOPHEAD:
-    case JSOP_GETELEM:
-    case JSOP_CALLELEM:
-    case JSOP_LENGTH:
-    case JSOP_NOT:
-    case JSOP_FUNCTIONTHIS:
-    case JSOP_GLOBALTHIS:
-    case JSOP_CALLEE:
-    case JSOP_ENVCALLEE:
-    case JSOP_SUPERBASE:
-    case JSOP_GETPROP_SUPER:
-    case JSOP_GETELEM_SUPER:
-    case JSOP_GETPROP:
-    case JSOP_CALLPROP:
-    case JSOP_REGEXP:
-    case JSOP_CALLSITEOBJ:
-    case JSOP_OBJECT:
-    case JSOP_CLASSCONSTRUCTOR:
-    case JSOP_TYPEOF:
-    case JSOP_TYPEOFEXPR:
-    case JSOP_TOASYNCITER:
-    case JSOP_TOID:
-    case JSOP_ITERNEXT:
-    case JSOP_LAMBDA:
-    case JSOP_LAMBDA_ARROW:
-    case JSOP_PUSHLEXICALENV:
-    case JSOP_POPLEXICALENV:
-    case JSOP_FRESHENLEXICALENV:
-    case JSOP_RECREATELEXICALENV:
-    case JSOP_ITER:
-    case JSOP_MOREITER:
-    case JSOP_ISNOITER:
-    case JSOP_ENDITER:
-    case JSOP_IN:
-    case JSOP_HASOWN:
-    case JSOP_SETRVAL:
-    case JSOP_INSTANCEOF:
-    case JSOP_DEBUGLEAVELEXICALENV:
-    case JSOP_DEBUGGER:
-    case JSOP_GIMPLICITTHIS:
-    case JSOP_IMPLICITTHIS:
-    case JSOP_NEWTARGET:
-    case JSOP_CHECKISOBJ:
-    case JSOP_CHECKISCALLABLE:
-    case JSOP_CHECKOBJCOERCIBLE:
-    case JSOP_DEBUGCHECKSELFHOSTED:
-    case JSOP_IS_CONSTRUCTING:
-    case JSOP_OPTIMIZE_SPREADCALL:
-    case JSOP_IMPORTMETA:
-    case JSOP_LOOPENTRY:
-    case JSOP_INSTRUMENTATION_ACTIVE:
-    case JSOP_INSTRUMENTATION_CALLBACK:
-    case JSOP_INSTRUMENTATION_SCRIPT_ID:
-    case JSOP_ENTERWITH:
-    case JSOP_LEAVEWITH:
-    case JSOP_SPREADNEW:
-    case JSOP_SPREADEVAL:
-    case JSOP_STRICTSPREADEVAL:
-    case JSOP_CHECKCLASSHERITAGE:
-    case JSOP_FUNWITHPROTO:
-    case JSOP_OBJWITHPROTO:
-    case JSOP_BUILTINPROTO:
-    case JSOP_DERIVEDCONSTRUCTOR:
-    case JSOP_CHECKTHIS:
-    case JSOP_CHECKRETURN:
-    case JSOP_CHECKTHISREINIT:
-    case JSOP_SUPERFUN:
-    case JSOP_SPREADSUPERCALL:
-    case JSOP_SUPERCALL:
-    case JSOP_PUSHVARENV:
-    case JSOP_POPVARENV:
-    case JSOP_GETBOUNDNAME:
-    case JSOP_EXCEPTION:
-    case JSOP_ISGENCLOSING:
-    case JSOP_FINALYIELDRVAL:
-    case JSOP_RESUME:
-    case JSOP_AFTERYIELD:
-    case JSOP_AWAIT:
-    case JSOP_TRYSKIPAWAIT:
-    case JSOP_GENERATOR:
-    case JSOP_ASYNCAWAIT:
-    case JSOP_ASYNCRESOLVE:
-    case JSOP_FINALLY:
-    case JSOP_GETRVAL:
-    case JSOP_GOSUB:
-    case JSOP_RETSUB:
-    case JSOP_THROWMSG:
-    case JSOP_FORCEINTERPRETER:
-    case JSOP_UNUSED71:
-    case JSOP_UNUSED149:
-    case JSOP_LIMIT:
-      return false;
-  }
-
-  MOZ_ASSERT_UNREACHABLE("Invalid opcode");
-  return false;
+/* static */
+bool DebuggerScript::getSuccessorOffsets(JSContext* cx, unsigned argc,
+                                         Value* vp) {
+  return DebuggerScript::getSuccessorOrPredecessorOffsets(
+      cx, argc, vp, "getSuccessorOffsets", true);
 }
 
-bool DebuggerScript::CallData::getEffectfulOffsets() {
-  if (!ensureScript()) {
-    return false;
-  }
-
-  RootedObject result(cx, NewDenseEmptyArray(cx));
-  if (!result) {
-    return false;
-  }
-  for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) {
-    if (BytecodeIsEffectful(r.frontOpcode())) {
-      if (!NewbornArrayPush(cx, result, NumberValue(r.frontOffset()))) {
-        return false;
-      }
-    }
-  }
-
-  args.rval().setObject(*result);
-  return true;
+/* static */
+bool DebuggerScript::getPredecessorOffsets(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  return DebuggerScript::getSuccessorOrPredecessorOffsets(
+      cx, argc, vp, "getPredecessorOffsets", false);
 }
 
-bool DebuggerScript::CallData::getAllOffsets() {
-  if (!ensureScript()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getAllOffsets(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_DELAZIFY(cx, argc, vp, "getAllOffsets", args, obj,
+                                   script);
 
   // First pass: determine which offsets in this script are jump targets and
   // which line numbers jump to them.
   FlowGraphSummary flowData(cx);
   if (!flowData.populate(cx, script)) {
     return false;
   }
 
@@ -1959,17 +1681,22 @@ class DebuggerScript::GetAllColumnOffset
       if (!appendColumnOffsetEntry(lineno, column, offset)) {
         return false;
       }
     }
     return true;
   }
 };
 
-bool DebuggerScript::CallData::getAllColumnOffsets() {
+/* static */
+bool DebuggerScript::getAllColumnOffsets(JSContext* cx, unsigned argc,
+                                         Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getAllColumnOffsets", args, obj,
+                            referent);
+
   RootedObject result(cx);
   GetAllColumnOffsetsMatcher matcher(cx, &result);
   if (!referent.match(matcher)) {
     return false;
   }
 
   args.rval().setObject(*result);
   return true;
@@ -2042,17 +1769,20 @@ class DebuggerScript::GetLineOffsetsMatc
       if (!NewbornArrayPush(cx_, result_, NumberValue(offsets[i]))) {
         return false;
       }
     }
     return true;
   }
 };
 
-bool DebuggerScript::CallData::getLineOffsets() {
+/* static */
+bool DebuggerScript::getLineOffsets(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getLineOffsets", args, obj,
+                            referent);
   if (!args.requireAtLeast(cx, "Debugger.Script.getLineOffsets", 1)) {
     return false;
   }
 
   // Parse lineno argument.
   RootedValue linenoValue(cx, args[0]);
   size_t lineno;
   if (!ToNumber(cx, &linenoValue)) {
@@ -2152,17 +1882,19 @@ struct DebuggerScript::SetBreakpointMatc
       return true;
     }
     site->dec(cx_->runtime()->defaultFreeOp());
     site->destroyIfEmpty(cx_->runtime()->defaultFreeOp());
     return false;
   }
 };
 
-bool DebuggerScript::CallData::setBreakpoint() {
+/* static */
+bool DebuggerScript::setBreakpoint(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "setBreakpoint", args, obj, referent);
   if (!args.requireAtLeast(cx, "Debugger.Script.setBreakpoint", 2)) {
     return false;
   }
   Debugger* dbg = Debugger::fromChildJSObject(obj);
 
   size_t offset;
   if (!ScriptOffset(cx, args[0], &offset)) {
     return false;
@@ -2176,20 +1908,20 @@ bool DebuggerScript::CallData::setBreakp
   SetBreakpointMatcher matcher(cx, dbg, offset, handler);
   if (!referent.match(matcher)) {
     return false;
   }
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerScript::CallData::getBreakpoints() {
-  if (!ensureScript()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getBreakpoints(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_DELAZIFY(cx, argc, vp, "getBreakpoints", args, obj,
+                                   script);
   Debugger* dbg = Debugger::fromChildJSObject(obj);
 
   jsbytecode* pc;
   if (args.length() > 0) {
     size_t offset;
     if (!ScriptOffset(cx, args[0], &offset) ||
         !EnsureScriptOffsetIsValid(cx, script, offset)) {
       return false;
@@ -2253,17 +1985,20 @@ class DebuggerScript::ClearBreakpointMat
       return true;
     }
     instance.debug().clearBreakpointsIn(cx_->runtime()->defaultFreeOp(),
                                         instanceObj, dbg_, handler_);
     return true;
   }
 };
 
-bool DebuggerScript::CallData::clearBreakpoint() {
+/* static */
+bool DebuggerScript::clearBreakpoint(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "clearBreakpoint", args, obj,
+                            referent);
   if (!args.requireAtLeast(cx, "Debugger.Script.clearBreakpoint", 1)) {
     return false;
   }
   Debugger* dbg = Debugger::fromChildJSObject(obj);
 
   JSObject* handler = RequireObject(cx, args[0]);
   if (!handler) {
     return false;
@@ -2273,17 +2008,21 @@ bool DebuggerScript::CallData::clearBrea
   if (!referent.match(matcher)) {
     return false;
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-bool DebuggerScript::CallData::clearAllBreakpoints() {
+/* static */
+bool DebuggerScript::clearAllBreakpoints(JSContext* cx, unsigned argc,
+                                         Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "clearAllBreakpoints", args, obj,
+                            referent);
   Debugger* dbg = Debugger::fromChildJSObject(obj);
   ClearBreakpointMatcher matcher(cx, dbg, nullptr);
   if (!referent.match(matcher)) {
     return false;
   }
   args.rval().setUndefined();
   return true;
 }
@@ -2324,17 +2063,20 @@ class DebuggerScript::IsInCatchScopeMatc
     return match(script);
   }
   ReturnType match(Handle<WasmInstanceObject*> instance) {
     isInCatch_ = false;
     return true;
   }
 };
 
-bool DebuggerScript::CallData::isInCatchScope() {
+/* static */
+bool DebuggerScript::isInCatchScope(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "isInCatchScope", args, obj,
+                            referent);
   if (!args.requireAtLeast(cx, "Debugger.Script.isInCatchScope", 1)) {
     return false;
   }
 
   size_t offset;
   if (!ScriptOffset(cx, args[0], &offset)) {
     return false;
   }
@@ -2342,20 +2084,21 @@ bool DebuggerScript::CallData::isInCatch
   IsInCatchScopeMatcher matcher(cx, offset);
   if (!referent.match(matcher)) {
     return false;
   }
   args.rval().setBoolean(matcher.isInCatch());
   return true;
 }
 
-bool DebuggerScript::CallData::getOffsetsCoverage() {
-  if (!ensureScript()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::getOffsetsCoverage(JSContext* cx, unsigned argc,
+                                        Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_DELAZIFY(cx, argc, vp, "getOffsetsCoverage", args,
+                                   obj, script);
 
   // If the script has no coverage information, then skip this and return null
   // instead.
   if (!script->hasScriptCounts()) {
     args.rval().setNull();
     return true;
   }
 
@@ -2426,20 +2169,21 @@ bool DebuggerScript::CallData::getOffset
       hits -= counts->numExec();
     }
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-bool DebuggerScript::CallData::setInstrumentationId() {
-  if (!ensureScriptMaybeLazy()) {
-    return false;
-  }
+/* static */
+bool DebuggerScript::setInstrumentationId(JSContext* cx, unsigned argc,
+                                          Value* vp) {
+  THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "setInstrumentationId", args,
+                                     obj);
 
   if (!obj->getInstrumentationId().isUndefined()) {
     JS_ReportErrorASCII(cx, "Script instrumentation ID is already set");
     return false;
   }
 
   if (!args.get(0).isNumber()) {
     JS_ReportErrorASCII(cx, "Script instrumentation ID must be a number");
@@ -2455,50 +2199,47 @@ bool DebuggerScript::CallData::setInstru
 /* static */
 bool DebuggerScript::construct(JSContext* cx, unsigned argc, Value* vp) {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                             "Debugger.Script");
   return false;
 }
 
 const JSPropertySpec DebuggerScript::properties_[] = {
-    JS_DEBUG_PSG("isGeneratorFunction", getIsGeneratorFunction),
-    JS_DEBUG_PSG("isAsyncFunction", getIsAsyncFunction),
-    JS_DEBUG_PSG("isFunction",  getIsFunction),
-    JS_DEBUG_PSG("isModule", getIsModule),
-    JS_DEBUG_PSG("displayName", getDisplayName),
-    JS_DEBUG_PSG("url", getUrl),
-    JS_DEBUG_PSG("startLine", getStartLine),
-    JS_DEBUG_PSG("startColumn", getStartColumn),
-    JS_DEBUG_PSG("lineCount", getLineCount),
-    JS_DEBUG_PSG("source", getSource),
-    JS_DEBUG_PSG("sourceStart", getSourceStart),
-    JS_DEBUG_PSG("sourceLength", getSourceLength),
-    JS_DEBUG_PSG("mainOffset", getMainOffset),
-    JS_DEBUG_PSG("global", getGlobal),
-    JS_DEBUG_PSG("format", getFormat),
+    JS_PSG("isGeneratorFunction", getIsGeneratorFunction, 0),
+    JS_PSG("isAsyncFunction", getIsAsyncFunction, 0),
+    JS_PSG("isFunction",  getIsFunction, 0),
+    JS_PSG("isModule", getIsModule, 0),
+    JS_PSG("displayName", getDisplayName, 0),
+    JS_PSG("url", getUrl, 0),
+    JS_PSG("startLine", getStartLine, 0),
+    JS_PSG("startColumn", getStartColumn, 0),
+    JS_PSG("lineCount", getLineCount, 0),
+    JS_PSG("source", getSource, 0),
+    JS_PSG("sourceStart", getSourceStart, 0),
+    JS_PSG("sourceLength", getSourceLength, 0),
+    JS_PSG("mainOffset", getMainOffset, 0),
+    JS_PSG("global", getGlobal, 0),
+    JS_PSG("format", getFormat, 0),
     JS_PS_END};
 
 const JSFunctionSpec DebuggerScript::methods_[] = {
-    JS_DEBUG_FN("getChildScripts", getChildScripts, 0),
-    JS_DEBUG_FN("getPossibleBreakpoints", getPossibleBreakpoints, 0),
-    JS_DEBUG_FN("getPossibleBreakpointOffsets", getPossibleBreakpointOffsets, 0),
-    JS_DEBUG_FN("setBreakpoint", setBreakpoint, 2),
-    JS_DEBUG_FN("getBreakpoints", getBreakpoints, 1),
-    JS_DEBUG_FN("clearBreakpoint", clearBreakpoint, 1),
-    JS_DEBUG_FN("clearAllBreakpoints", clearAllBreakpoints, 0),
-    JS_DEBUG_FN("isInCatchScope", isInCatchScope, 1),
-    JS_DEBUG_FN("getOffsetMetadata", getOffsetMetadata, 1),
-    JS_DEBUG_FN("getOffsetsCoverage", getOffsetsCoverage, 0),
-    JS_DEBUG_FN("getSuccessorOffsets",
-                getSuccessorOrPredecessorOffsets<true>, 1),
-    JS_DEBUG_FN("getPredecessorOffsets",
-                getSuccessorOrPredecessorOffsets<false>, 1),
-    JS_DEBUG_FN("getEffectfulOffsets", getEffectfulOffsets, 1),
-    JS_DEBUG_FN("setInstrumentationId", setInstrumentationId, 1),
+    JS_FN("getChildScripts", getChildScripts, 0, 0),
+    JS_FN("getPossibleBreakpoints", getPossibleBreakpoints, 0, 0),
+    JS_FN("getPossibleBreakpointOffsets", getPossibleBreakpointOffsets, 0, 0),
+    JS_FN("setBreakpoint", setBreakpoint, 2, 0),
+    JS_FN("getBreakpoints", getBreakpoints, 1, 0),
+    JS_FN("clearBreakpoint", clearBreakpoint, 1, 0),
+    JS_FN("clearAllBreakpoints", clearAllBreakpoints, 0, 0),
+    JS_FN("isInCatchScope", isInCatchScope, 1, 0),
+    JS_FN("getOffsetMetadata", getOffsetMetadata, 1, 0),
+    JS_FN("getOffsetsCoverage", getOffsetsCoverage, 0, 0),
+    JS_FN("getSuccessorOffsets", getSuccessorOffsets, 1, 0),
+    JS_FN("getPredecessorOffsets", getPredecessorOffsets, 1, 0),
+    JS_FN("setInstrumentationId", setInstrumentationId, 1, 0),
 
     // The following APIs are deprecated due to their reliance on the
     // under-defined 'entrypoint' concept. Make use of getPossibleBreakpoints,
     // getPossibleBreakpointOffsets, or getOffsetMetadata instead.
-    JS_DEBUG_FN("getAllOffsets", getAllOffsets, 0),
-    JS_DEBUG_FN("getAllColumnOffsets", getAllColumnOffsets, 0),
-    JS_DEBUG_FN("getLineOffsets", getLineOffsets, 1),
-    JS_DEBUG_FN("getOffsetLocation", getOffsetLocation, 0), JS_FS_END};
+    JS_FN("getAllOffsets", getAllOffsets, 0, 0),
+    JS_FN("getAllColumnOffsets", getAllColumnOffsets, 0, 0),
+    JS_FN("getLineOffsets", getLineOffsets, 1, 0),
+    JS_FN("getOffsetLocation", getOffsetLocation, 0, 0), JS_FS_END};
--- a/js/src/debugger/Script.h
+++ b/js/src/debugger/Script.h
@@ -47,25 +47,59 @@ class DebuggerScript : public NativeObje
   void trace(JSTracer* trc);
 
   using ReferentVariant = DebuggerScriptReferent;
 
   inline gc::Cell* getReferentCell() const;
   inline js::BaseScript* getReferentScript() const;
   inline DebuggerScriptReferent getReferent() const;
 
-  static DebuggerScript* check(JSContext* cx, HandleValue v);
+  static DebuggerScript* check(JSContext* cx, HandleValue v,
+                               const char* fnname);
+  static DebuggerScript* checkThis(JSContext* cx, const CallArgs& args,
+                                   const char* fnname);
 
+  // JS methods
+  static bool getIsGeneratorFunction(JSContext* cx, unsigned argc, Value* vp);
+  static bool getIsAsyncFunction(JSContext* cx, unsigned argc, Value* vp);
+  static bool getIsFunction(JSContext* cx, unsigned argc, Value* vp);
+  static bool getIsModule(JSContext* cx, unsigned argc, Value* vp);
+  static bool getDisplayName(JSContext* cx, unsigned argc, Value* vp);
+  static bool getUrl(JSContext* cx, unsigned argc, Value* vp);
+  static bool getStartLine(JSContext* cx, unsigned argc, Value* vp);
+  static bool getStartColumn(JSContext* cx, unsigned argc, Value* vp);
+  static bool getLineCount(JSContext* cx, unsigned argc, Value* vp);
+  static bool getSource(JSContext* cx, unsigned argc, Value* vp);
+  static bool getSourceStart(JSContext* cx, unsigned argc, Value* vp);
+  static bool getSourceLength(JSContext* cx, unsigned argc, Value* vp);
+  static bool getMainOffset(JSContext* cx, unsigned argc, Value* vp);
+  static bool getGlobal(JSContext* cx, unsigned argc, Value* vp);
+  static bool getFormat(JSContext* cx, unsigned argc, Value* vp);
+  static bool getChildScripts(JSContext* cx, unsigned argc, Value* vp);
+  static bool getPossibleBreakpoints(JSContext* cx, unsigned argc, Value* vp);
+  static bool getPossibleBreakpointOffsets(JSContext* cx, unsigned argc,
+                                           Value* vp);
+  static bool getOffsetMetadata(JSContext* cx, unsigned argc, Value* vp);
+  static bool getOffsetLocation(JSContext* cx, unsigned argc, Value* vp);
+  static bool getSuccessorOffsets(JSContext* cx, unsigned argc, Value* vp);
+  static bool getPredecessorOffsets(JSContext* cx, unsigned argc, Value* vp);
+  static bool getAllOffsets(JSContext* cx, unsigned argc, Value* vp);
+  static bool getAllColumnOffsets(JSContext* cx, unsigned argc, Value* vp);
+  static bool getLineOffsets(JSContext* cx, unsigned argc, Value* vp);
+  static bool setBreakpoint(JSContext* cx, unsigned argc, Value* vp);
+  static bool getBreakpoints(JSContext* cx, unsigned argc, Value* vp);
+  static bool clearBreakpoint(JSContext* cx, unsigned argc, Value* vp);
+  static bool clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp);
+  static bool isInCatchScope(JSContext* cx, unsigned argc, Value* vp);
+  static bool getOffsetsCoverage(JSContext* cx, unsigned argc, Value* vp);
+  static bool setInstrumentationId(JSContext* cx, unsigned argc, Value* vp);
   static bool construct(JSContext* cx, unsigned argc, Value* vp);
 
-  struct CallData;
-
   template <typename T>
-  static bool getUrlImpl(JSContext* cx, const CallArgs& args,
-                         Handle<T*> script);
+  static bool getUrlImpl(JSContext* cx, CallArgs& args, Handle<T*> script);
 
   static bool getSuccessorOrPredecessorOffsets(JSContext* cx, unsigned argc,
                                                Value* vp, const char* name,
                                                bool successor);
 
   Value getInstrumentationId() const {
     return getSlot(INSTRUMENTATION_ID_SLOT);
   }
--- a/js/src/debugger/Source.cpp
+++ b/js/src/debugger/Source.cpp
@@ -122,85 +122,75 @@ void DebuggerSource::trace(JSTracer* trc
 /* static */
 bool DebuggerSource::construct(JSContext* cx, unsigned argc, Value* vp) {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                             "Debugger.Source");
   return false;
 }
 
 /* static */
-DebuggerSource* DebuggerSource::check(JSContext* cx, HandleValue thisv) {
+DebuggerSource* DebuggerSource::check(JSContext* cx, HandleValue thisv,
+                                      const char* fnname) {
   JSObject* thisobj = RequireObject(cx, thisv);
   if (!thisobj) {
     return nullptr;
   }
   if (!thisobj->is<DebuggerSource>()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Source",
-                              "method", thisobj->getClass()->name);
+                              fnname, thisobj->getClass()->name);
     return nullptr;
   }
 
   DebuggerSource* thisSourceObj = &thisobj->as<DebuggerSource>();
 
   if (!thisSourceObj->getReferentRawObject()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Source",
-                              "method", "prototype object");
+                              fnname, "prototype object");
     return nullptr;
   }
 
   return thisSourceObj;
 }
 
-struct MOZ_STACK_CLASS DebuggerSource::CallData {
-  JSContext* cx;
-  const CallArgs& args;
-
-  HandleDebuggerSource obj;
-  Rooted<DebuggerSourceReferent> referent;
-
-  CallData(JSContext* cx, const CallArgs& args, HandleDebuggerSource obj)
-      : cx(cx), args(args), obj(obj), referent(cx, obj->getReferent()) {}
+template <typename ReferentT>
+/* static */
+DebuggerSource* DebuggerSource::checkThis(JSContext* cx, const CallArgs& args,
+                                          const char* fnname,
+                                          const char* refname) {
+  DebuggerSource* thisobj = check(cx, args.thisv(), fnname);
+  if (!thisobj) {
+    return nullptr;
+  }
 
-  bool getText();
-  bool getBinary();
-  bool getURL();
-  bool getStartLine();
-  bool getId();
-  bool getDisplayURL();
-  bool getElement();
-  bool getElementProperty();
-  bool getIntroductionScript();
-  bool getIntroductionOffset();
-  bool getIntroductionType();
-  bool setSourceMapURL();
-  bool getSourceMapURL();
-  bool reparse();
-
-  using Method = bool (CallData::*)();
-
-  template <Method MyMethod>
-  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
-};
-
-template <DebuggerSource::CallData::Method MyMethod>
-/* static */
-bool DebuggerSource::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  RootedDebuggerSource obj(cx, DebuggerSource::check(cx, args.thisv()));
-  if (!obj) {
-    return false;
+  if (!thisobj->getReferent().is<ReferentT>()) {
+    ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
+                     args.thisv(), nullptr, refname);
+    return nullptr;
   }
 
-  CallData data(cx, args, obj);
-  return (data.*MyMethod)();
+  return thisobj;
 }
 
+#define THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, fnname, args, obj, referent) \
+  CallArgs args = CallArgsFromVp(argc, vp);                                  \
+  RootedDebuggerSource obj(cx,                                               \
+                           DebuggerSource::check(cx, args.thisv(), fnname)); \
+  if (!obj) return false;                                                    \
+  Rooted<DebuggerSourceReferent> referent(cx, obj->getReferent())
+
+#define THIS_DEBUGSOURCE_SOURCE(cx, argc, vp, fnname, args, obj, sourceObject) \
+  CallArgs args = CallArgsFromVp(argc, vp);                                    \
+  RootedDebuggerSource obj(cx, DebuggerSource::checkThis<ScriptSourceObject*>( \
+                                   cx, args, fnname, "a JS source"));          \
+  if (!obj) return false;                                                      \
+  RootedScriptSourceObject sourceObject(                                       \
+      cx, obj->getReferent().as<ScriptSourceObject*>())
+
 class DebuggerSourceGetTextMatcher {
   JSContext* cx_;
 
  public:
   explicit DebuggerSourceGetTextMatcher(JSContext* cx) : cx_(cx) {}
 
   using ReturnType = JSString*;
 
@@ -228,17 +218,19 @@ class DebuggerSourceGetTextMatcher {
       msg = "Restart with developer tools open to view WebAssembly source.";
     } else {
       msg = "[debugger missing wasm binary-to-text conversion]";
     }
     return NewStringCopyZ<CanGC>(cx_, msg);
   }
 };
 
-bool DebuggerSource::CallData::getText() {
+/* static */
+bool DebuggerSource::getText(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get text)", args, obj, referent);
   Value textv = obj->getReservedSlot(TEXT_SLOT);
   if (!textv.isUndefined()) {
     MOZ_ASSERT(textv.isString());
     args.rval().set(textv);
     return true;
   }
 
   DebuggerSourceGetTextMatcher matcher(cx);
@@ -247,17 +239,20 @@ bool DebuggerSource::CallData::getText()
     return false;
   }
 
   args.rval().setString(str);
   obj->setReservedSlot(TEXT_SLOT, args.rval());
   return true;
 }
 
-bool DebuggerSource::CallData::getBinary() {
+/* static */
+bool DebuggerSource::getBinary(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get binary)", args, obj, referent);
+
   if (!referent.is<WasmInstanceObject*>()) {
     ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
                      args.thisv(), nullptr, "a wasm source");
     return false;
   }
 
   RootedWasmInstanceObject instanceObj(cx, referent.as<WasmInstanceObject*>());
   wasm::Instance& instance = instanceObj->instance();
@@ -298,17 +293,20 @@ class DebuggerSourceGetURLMatcher {
     }
     return Nothing();
   }
   ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
     return Some(instanceObj->instance().createDisplayURL(cx_));
   }
 };
 
-bool DebuggerSource::CallData::getURL() {
+/* static */
+bool DebuggerSource::getURL(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get url)", args, obj, referent);
+
   DebuggerSourceGetURLMatcher matcher(cx);
   Maybe<JSString*> str = referent.match(matcher);
   if (str.isSome()) {
     if (!*str) {
       return false;
     }
     args.rval().setString(*str);
   } else {
@@ -323,17 +321,21 @@ class DebuggerSourceGetStartLineMatcher 
 
   ReturnType match(HandleScriptSourceObject sourceObject) {
     ScriptSource* ss = sourceObject->source();
     return ss->startLine();
   }
   ReturnType match(Handle<WasmInstanceObject*> instanceObj) { return 0; }
 };
 
-bool DebuggerSource::CallData::getStartLine() {
+/* static */
+bool DebuggerSource::getStartLine(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get startLine)", args, obj,
+                            referent);
+
   DebuggerSourceGetStartLineMatcher matcher;
   uint32_t line = referent.match(matcher);
   args.rval().setNumber(line);
   return true;
 }
 
 class DebuggerSourceGetIdMatcher {
  public:
@@ -341,17 +343,20 @@ class DebuggerSourceGetIdMatcher {
 
   ReturnType match(HandleScriptSourceObject sourceObject) {
     ScriptSource* ss = sourceObject->source();
     return ss->id();
   }
   ReturnType match(Handle<WasmInstanceObject*> instanceObj) { return 0; }
 };
 
-bool DebuggerSource::CallData::getId() {
+/* static */
+bool DebuggerSource::getId(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get id)", args, obj, referent);
+
   DebuggerSourceGetIdMatcher matcher;
   uint32_t id = referent.match(matcher);
   args.rval().setNumber(id);
   return true;
 }
 
 struct DebuggerSourceGetDisplayURLMatcher {
   using ReturnType = const char16_t*;
@@ -360,17 +365,20 @@ struct DebuggerSourceGetDisplayURLMatche
     MOZ_ASSERT(ss);
     return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
     return wasmInstance->instance().metadata().displayURL();
   }
 };
 
-bool DebuggerSource::CallData::getDisplayURL() {
+/* static */
+bool DebuggerSource::getDisplayURL(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get url)", args, obj, referent);
+
   DebuggerSourceGetDisplayURLMatcher matcher;
   if (const char16_t* displayURL = referent.match(matcher)) {
     JSString* str = JS_NewUCStringCopyZ(cx, displayURL);
     if (!str) {
       return false;
     }
     args.rval().setString(str);
   } else {
@@ -382,17 +390,20 @@ bool DebuggerSource::CallData::getDispla
 struct DebuggerSourceGetElementMatcher {
   using ReturnType = JSObject*;
   ReturnType match(HandleScriptSourceObject sourceObject) {
     return sourceObject->unwrappedElement();
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return nullptr; }
 };
 
-bool DebuggerSource::CallData::getElement() {
+/* static */
+bool DebuggerSource::getElement(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get element)", args, obj, referent);
+
   DebuggerSourceGetElementMatcher matcher;
   if (JSObject* element = referent.match(matcher)) {
     args.rval().setObjectOrNull(element);
     if (!Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval())) {
       return false;
     }
   } else {
     args.rval().setUndefined();
@@ -405,17 +416,21 @@ struct DebuggerSourceGetElementPropertyM
   ReturnType match(HandleScriptSourceObject sourceObject) {
     return sourceObject->unwrappedElementAttributeName();
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
     return UndefinedValue();
   }
 };
 
-bool DebuggerSource::CallData::getElementProperty() {
+/* static */
+bool DebuggerSource::getElementProperty(JSContext* cx, unsigned argc,
+                                        Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get elementAttributeName)", args,
+                            obj, referent);
   DebuggerSourceGetElementPropertyMatcher matcher;
   args.rval().set(referent.match(matcher));
   return Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval());
 }
 
 class DebuggerSourceGetIntroductionScriptMatcher {
   JSContext* cx_;
   Debugger* dbg_;
@@ -447,17 +462,21 @@ class DebuggerSourceGetIntroductionScrip
     if (!ds) {
       return false;
     }
     rval_.setObject(*ds);
     return true;
   }
 };
 
-bool DebuggerSource::CallData::getIntroductionScript() {
+/* static */
+bool DebuggerSource::getIntroductionScript(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionScript)", args, obj,
+                            referent);
   Debugger* dbg = Debugger::fromChildJSObject(obj);
   DebuggerSourceGetIntroductionScriptMatcher matcher(cx, dbg, args.rval());
   return referent.match(matcher);
 }
 
 struct DebuggerGetIntroductionOffsetMatcher {
   using ReturnType = Value;
   ReturnType match(HandleScriptSourceObject sourceObject) {
@@ -471,66 +490,62 @@ struct DebuggerGetIntroductionOffsetMatc
     }
     return UndefinedValue();
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
     return UndefinedValue();
   }
 };
 
-bool DebuggerSource::CallData::getIntroductionOffset() {
+/* static */
+bool DebuggerSource::getIntroductionOffset(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionOffset)", args, obj,
+                            referent);
   DebuggerGetIntroductionOffsetMatcher matcher;
   args.rval().set(referent.match(matcher));
   return true;
 }
 
 struct DebuggerSourceGetIntroductionTypeMatcher {
   using ReturnType = const char*;
   ReturnType match(HandleScriptSourceObject sourceObject) {
     ScriptSource* ss = sourceObject->source();
     MOZ_ASSERT(ss);
     return ss->hasIntroductionType() ? ss->introductionType() : nullptr;
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return "wasm"; }
 };
 
-bool DebuggerSource::CallData::getIntroductionType() {
+/* static */
+bool DebuggerSource::getIntroductionType(JSContext* cx, unsigned argc,
+                                         Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionType)", args, obj,
+                            referent);
+
   DebuggerSourceGetIntroductionTypeMatcher matcher;
   if (const char* introductionType = referent.match(matcher)) {
     JSString* str = NewStringCopyZ<CanGC>(cx, introductionType);
     if (!str) {
       return false;
     }
     args.rval().setString(str);
   } else {
     args.rval().setUndefined();
   }
 
   return true;
 }
 
-ScriptSourceObject* EnsureSourceObject(JSContext* cx,
-                                       HandleDebuggerSource obj) {
-  if (!obj->getReferent().is<ScriptSourceObject*>()) {
-    RootedValue v(cx, ObjectValue(*obj));
-    ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
-                     v, nullptr, "a JS source");
-    return nullptr;
-  }
-  return obj->getReferent().as<ScriptSourceObject*>();
-}
-
-bool DebuggerSource::CallData::setSourceMapURL() {
-  RootedScriptSourceObject sourceObject(cx, EnsureSourceObject(cx, obj));
-  if (!sourceObject) {
-    return false;
-  }
+/* static */
+bool DebuggerSource::setSourceMapURL(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSOURCE_SOURCE(cx, argc, vp, "set sourceMapURL", args, obj,
+                          sourceObject);
   ScriptSource* ss = sourceObject->source();
   MOZ_ASSERT(ss);
-
   if (!args.requireAtLeast(cx, "set sourceMapURL", 1)) {
     return false;
   }
 
   JSString* str = ToString<CanGC>(cx, args[0]);
   if (!str) {
     return false;
   }
@@ -584,17 +599,21 @@ class DebuggerSourceGetSourceMapURLMatch
       return false;
     }
 
     result_.set(str);
     return true;
   }
 };
 
-bool DebuggerSource::CallData::getSourceMapURL() {
+/* static */
+bool DebuggerSource::getSourceMapURL(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get sourceMapURL)", args, obj,
+                            referent);
+
   RootedString result(cx);
   DebuggerSourceGetSourceMapURLMatcher matcher(cx, &result);
   if (!referent.match(matcher)) {
     return false;
   }
   if (result) {
     args.rval().setString(result);
   } else {
@@ -623,32 +642,37 @@ static JSScript* ReparseSource(JSContext
   if (!srcBuf.init(cx, units.get(), ss->length(),
                    JS::SourceOwnership::Borrowed)) {
     return nullptr;
   }
 
   return JS::Compile(cx, options, srcBuf);
 }
 
-bool DebuggerSource::CallData::reparse() {
-  RootedScriptSourceObject sourceObject(cx, EnsureSourceObject(cx, obj));
-  if (!sourceObject) {
+/* static */
+bool DebuggerSource::reparse(JSContext* cx, unsigned argc, Value* vp) {
+  THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "reparse", args, obj,
+                            referent);
+  if (!referent.is<ScriptSourceObject*>()) {
+    JS_ReportErrorASCII(cx, "Source object required");
     return false;
   }
 
-  if (!sourceObject->source()->hasSourceText()) {
+  RootedScriptSourceObject sso(cx, referent.as<ScriptSourceObject*>());
+
+  if (!sso->source()->hasSourceText()) {
     JS_ReportErrorASCII(cx, "Source object missing text");
     return false;
   }
 
   RootedScript script(cx);
-  if (sourceObject->source()->hasSourceType<mozilla::Utf8Unit>()) {
-    script = ReparseSource<mozilla::Utf8Unit>(cx, sourceObject);
+  if (sso->source()->hasSourceType<mozilla::Utf8Unit>()) {
+    script = ReparseSource<mozilla::Utf8Unit>(cx, sso);
   } else {
-    script = ReparseSource<char16_t>(cx, sourceObject);
+    script = ReparseSource<char16_t>(cx, sso);
   }
 
   if (!script) {
     return false;
   }
 
   Debugger* dbg = Debugger::fromChildJSObject(obj);
   RootedObject scriptDO(cx, dbg->wrapScript(cx, script));
@@ -656,25 +680,25 @@ bool DebuggerSource::CallData::reparse()
     return false;
   }
 
   args.rval().setObject(*scriptDO);
   return true;
 }
 
 const JSPropertySpec DebuggerSource::properties_[] = {
-    JS_DEBUG_PSG("text", getText),
-    JS_DEBUG_PSG("binary", getBinary),
-    JS_DEBUG_PSG("url", getURL),
-    JS_DEBUG_PSG("startLine", getStartLine),
-    JS_DEBUG_PSG("id", getId),
-    JS_DEBUG_PSG("element", getElement),
-    JS_DEBUG_PSG("displayURL", getDisplayURL),
-    JS_DEBUG_PSG("introductionScript", getIntroductionScript),
-    JS_DEBUG_PSG("introductionOffset", getIntroductionOffset),
-    JS_DEBUG_PSG("introductionType", getIntroductionType),
-    JS_DEBUG_PSG("elementAttributeName", getElementProperty),
-    JS_DEBUG_PSGS("sourceMapURL", getSourceMapURL, setSourceMapURL),
+    JS_PSG("text", getText, 0),
+    JS_PSG("binary", getBinary, 0),
+    JS_PSG("url", getURL, 0),
+    JS_PSG("startLine", getStartLine, 0),
+    JS_PSG("id", getId, 0),
+    JS_PSG("element", getElement, 0),
+    JS_PSG("displayURL", getDisplayURL, 0),
+    JS_PSG("introductionScript", getIntroductionScript, 0),
+    JS_PSG("introductionOffset", getIntroductionOffset, 0),
+    JS_PSG("introductionType", getIntroductionType, 0),
+    JS_PSG("elementAttributeName", getElementProperty, 0),
+    JS_PSGS("sourceMapURL", getSourceMapURL, setSourceMapURL, 0),
     JS_PS_END};
 
 const JSFunctionSpec DebuggerSource::methods_[] = {
-    JS_DEBUG_FN("reparse", reparse, 0),
+    JS_FN("reparse", reparse, 0, 0),
     JS_FS_END};
--- a/js/src/debugger/Source.h
+++ b/js/src/debugger/Source.h
@@ -38,20 +38,38 @@ class DebuggerSource : public NativeObje
 
   void trace(JSTracer* trc);
 
   using ReferentVariant = DebuggerSourceReferent;
 
   NativeObject* getReferentRawObject() const;
   DebuggerSourceReferent getReferent() const;
 
-  static DebuggerSource* check(JSContext* cx, HandleValue v);
+  static DebuggerSource* check(JSContext* cx, HandleValue v,
+                               const char* fnname);
+  template <typename ReferentT>
+  static DebuggerSource* checkThis(JSContext* cx, const CallArgs& args,
+                                   const char* fnname, const char* refname);
+
+  // JS methods
   static bool construct(JSContext* cx, unsigned argc, Value* vp);
-
-  struct CallData;
+  static bool getText(JSContext* cx, unsigned argc, Value* vp);
+  static bool getBinary(JSContext* cx, unsigned argc, Value* vp);
+  static bool getURL(JSContext* cx, unsigned argc, Value* vp);
+  static bool getStartLine(JSContext* cx, unsigned argc, Value* vp);
+  static bool getId(JSContext* cx, unsigned argc, Value* vp);
+  static bool getDisplayURL(JSContext* cx, unsigned argc, Value* vp);
+  static bool getElement(JSContext* cx, unsigned argc, Value* vp);
+  static bool getElementProperty(JSContext* cx, unsigned argc, Value* vp);
+  static bool getIntroductionScript(JSContext* cx, unsigned argc, Value* vp);
+  static bool getIntroductionOffset(JSContext* cx, unsigned argc, Value* vp);
+  static bool getIntroductionType(JSContext* cx, unsigned argc, Value* vp);
+  static bool setSourceMapURL(JSContext* cx, unsigned argc, Value* vp);
+  static bool getSourceMapURL(JSContext* cx, unsigned argc, Value* vp);
+  static bool reparse(JSContext* cx, unsigned argc, Value* vp);
 
  private:
   static const JSClassOps classOps_;
 
   static const JSPropertySpec properties_[];
   static const JSFunctionSpec methods_[];
 };
 
--- a/js/src/doc/Debugger/Debugger.Object.md
+++ b/js/src/doc/Debugger/Debugger.Object.md
@@ -455,20 +455,16 @@ of exotic object like an opaque wrapper.
 
 <code>makeDebuggeeNativeFunction(<i>value</i>)</code>
 :   If <i>value</i> is a native function in the debugger's compartment, create
     an equivalent function for the same native in the debuggee's realm, and
     return a `Debugger.Object` instance for the new function.  The new function
     can be accessed by code in the debuggee without going through a cross
     compartment wrapper.
 
-<code>isSameNative(<i>value</i>)</code>
-:   If <i>value</i> is a native function in the debugger's compartment, return
-    whether the referent is a native function for the same C++ native.
-
 <code>decompile([<i>pretty</i>])</code>
 :   If the referent is a function that is debuggee code, return the
     JavaScript source code for a function definition equivalent to the
     referent function in its effect and result, as a string. If
     <i>pretty</i> is present and true, produce indented code with line
     breaks. If the referent is not a function that is debuggee code, return
     `undefined`.
 
--- a/js/src/doc/Debugger/Debugger.Script.md
+++ b/js/src/doc/Debugger/Debugger.Script.md
@@ -343,23 +343,16 @@ methods of other kinds of objects.
     flow paths.
 
 <code>getPredecessorOffsets(<i>offset</i>)</code>
 :   **If the instance refers to a `JSScript`**, return an array
     containing the offsets of all bytecodes in the script for which
     <i>offset</i> is an immediate successor via non-exceptional
     control flow paths.
 
-<code>getEffectfulOffsets()</code>
-:   **If the instance refers to a `JSScript`**, return an array
-    containing the offsets of all bytecodes in the script which can have direct
-    side effects that are visible outside the currently executing frame.  This
-    includes, for example, operations that set properties or elements on
-    objects, or that may set names in environments created outside the frame.
-
 `getOffsetsCoverage()`:
 :   **If the instance refers to a `JSScript`**, return `null` or an array which
     contains information about the coverage of all opcodes. The elements of
     the array are objects, each of which describes a single opcode, and
     contains the following properties:
 
     * lineNumber: the line number of the current opcode.
 
--- a/js/src/doc/Debugger/Debugger.md
+++ b/js/src/doc/Debugger/Debugger.md
@@ -157,34 +157,16 @@ compartment.
     (Naturally, <i>frame</i> is currently the youngest
     [visible frame][vf].) This method should return
     a [resumption value][rv] specifying how the debuggee's execution should
     proceed.
 
     SpiderMonkey only calls `onEnterFrame` to report
     [visible][vf], non-`"debugger"` frames.
 
-<code>onNativeCall(<i>callee</i>, <i>reason</i>)</code>
-:   A call to a native function is being made from a debuggee realm.
-    <i>callee</i> is a [`Debugger.Object`] for the function being called, and
-    <i>reason</i> is a string describing the reason the call was made, and
-    has one of the following values:
-
-    `get`: The native is the getter for a property which is being accessed.
-    `set`: The native is the setter for a property being written to.
-    `call`: Any call not fitting into the above categories.
-
-    This method should return a [resumption value][rv] specifying how the
-    debuggee's execution should proceed.
-
-    SpiderMonkey only calls `onNativeCall` hooks when execution is inside a
-    debugger evaluation associated with the debugger that has the `onNativeCall`
-    hook.  Such evaluation methods include `Debugger.Object.executeInGlobal`,
-    `Debugger.Frame.eval`, and associated methods.
-
 <code>onExceptionUnwind(<i>frame</i>, <i>value</i>)</code>
 :   The exception <i>value</i> has been thrown, and has propagated to
     <i>frame</i>; <i>frame</i> is the youngest remaining stack frame, and is a
     debuggee frame. This method should return a [resumption value][rv]
     specifying how the debuggee's execution should proceed. If it returns
     `undefined`, the exception continues to propagate as normal: if control in
     `frame` is in a `try` block, control jumps to the corresponding `catch` or
     `finally` block; otherwise, <i>frame</i> is popped, and the exception
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Debugger-onNativeCall-01.js
+++ /dev/null
@@ -1,64 +0,0 @@
-// Test that the onNativeCall hook is called when expected.
-
-load(libdir + 'eqArrayHelper.js');
-
-var g = newGlobal({newCompartment: true});
-var dbg = Debugger(g);
-var gdbg = dbg.addDebuggee(g);
-
-g.eval(`
-const x = [];
-Object.defineProperty(x, "a", {
-  get: print,
-  set: print,
-});
-function f() {
-  x.a++;
-  x.push(4);
-}
-`);
-
-for (let i = 0; i < 5; i++) {
-  g.f();
-}
-
-const rv = [];
-dbg.onNativeCall = (callee, reason) => { rv.push(callee.name, reason); };
-
-var dbg2 = Debugger(g);
-var gdbg2 = dbg2.addDebuggee(g);
-
-const fscript = gdbg.getOwnPropertyDescriptor('f').value.script;
-
-for (let i = 0; i < 5; i++) {
-  // The onNativeCall hook is called when doing global evaluations.
-  rv.length = 0;
-  gdbg.executeInGlobal(`f()`);
-  assertEqArray(rv, ["print", "get", "print", "set", "push", "call"]);
-
-  // The onNativeCall hook is called when doing frame evaluations.
-  let handlerCalled = false;
-  const handler = {
-    hit(frame) {
-      fscript.clearBreakpoint(handler);
-      rv.length = 0;
-      frame.eval(`f()`);
-      assertEqArray(rv, ["print", "get", "print", "set", "push", "call"]);
-      handlerCalled = true;
-    },
-  };
-  fscript.setBreakpoint(fscript.mainOffset, handler);
-  g.f();
-  assertEq(handlerCalled, true);
-
-  // The onNativeCall hook is *not* called when not in a debugger evaluation.
-  rv.length = 0;
-  g.f();
-  assertEqArray(rv, []);
-
-  // The onNativeCall hook is *not* called when in a debugger evaluation
-  // associated with a different debugger.
-  rv.length = 0;
-  gdbg2.executeInGlobal(`f()`);
-  assertEqArray(rv, []);
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Debugger-onNativeCall-02.js
+++ /dev/null
@@ -1,61 +0,0 @@
-// Test that the onNativeCall hook can control the call's behavior.
-
-var g = newGlobal({newCompartment: true});
-var dbg = Debugger(g);
-var gdbg = dbg.addDebuggee(g);
-
-g.eval(`
-var x = [];
-Object.defineProperty(x, "a", {
-  get: print,
-  set: print,
-});
-var rv;
-function f() {
-  x.a++;
-  try {
-    rv = x.push(4);
-  } catch (e) {
-    throw "rethrowing";
-  }
-}
-`);
-
-for (let i = 0; i < 5; i++) {
-  g.f();
-}
-
-for (let i = 0; i < 5; i++) {
-  // Test terminating execution.
-  dbg.onNativeCall = (callee, reason) => {
-    return null;
-  };
-  const len = g.x.length;
-  let v = gdbg.executeInGlobal(`f()`);
-  assertEq(v, null);
-  assertEq(g.x.length, len);
-
-  // Test throwing an exception.
-  dbg.onNativeCall = (callee, reason) => {
-    return { throw: "throwing" };
-  };
-  v = gdbg.executeInGlobal(`f()`);
-  assertEq(v.throw, "throwing");
-
-  // Test throwing an exception #2.
-  dbg.onNativeCall = (callee, reason) => {
-    if (callee.name == "push") {
-      return { throw: "throwing" };
-    }
-  };
-  v = gdbg.executeInGlobal(`f()`);
-  assertEq(v.throw, "rethrowing");
-
-  // Test returning a different value from the native.
-  dbg.onNativeCall = (callee, reason) => {
-    return { return: "value" };
-  };
-  v = gdbg.executeInGlobal(`f()`);
-  assertEq(v.return, undefined);
-  assertEq(g.rv, "value");
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Debugger-onNativeCall-03.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// Test onNativeCall's behavior when used with self-hosted functions.
-
-load(libdir + 'eqArrayHelper.js');
-
-var g = newGlobal({newCompartment: true});
-var dbg = Debugger(g);
-var gdbg = dbg.addDebuggee(g);
-
-const rv = [];
-
-dbg.onEnterFrame = f => {
-  rv.push("EnterFrame");
-};
-
-dbg.onNativeCall = f => {
-  rv.push(f.displayName);
-};
-
-gdbg.executeInGlobal(`
-  var x = [1,3,2];
-  x.sort((a, b) => {print(a)});
-`);
-
-// When running self-hosted code, we will see native calls to internal
-// self-hosted JS functions and intrinsic natives. Drop these from the result
-// array.
-const validNames = ["EnterFrame", "sort", "print"];
-const filtered = rv.filter(name => validNames.includes(name));
-
-assertEq(filtered.length < rv.length, true);
-assertEqArray(filtered, ["EnterFrame", "sort", "EnterFrame", "print", "EnterFrame", "print"]);
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Debugger-onNativeCall-04.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// Test that onNativeCall behaves correctly when a debugger eval might enter the
-// JIT via OSR.
-
-var g = newGlobal({newCompartment: true});
-var dbg = Debugger(g);
-var gdbg = dbg.addDebuggee(g);
-
-g.eval(`
-const x = [];
-function f() {
-  for (let i = 0; i < 5; i++) {
-    x.push(i);
-  }
-}
-`);
-
-let numCalls = 0;
-dbg.onNativeCall = callee => { assertEq(callee.name, "push"); numCalls++; };
-
-var dbg2 = Debugger(g);
-
-for (let i = 0; i < 5; i++) {
-  numCalls = 0;
-  gdbg.executeInGlobal(`f()`);
-  assertEq(numCalls, 5);
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Object-isSameNative.js
+++ /dev/null
@@ -1,46 +0,0 @@
-// Test that the onNativeCall hook is called when expected.
-
-load(libdir + 'eqArrayHelper.js');
-
-var g = newGlobal({newCompartment: true});
-var dbg = Debugger(g);
-var gdbg = dbg.addDebuggee(g);
-
-assertEq(gdbg.getProperty("print").return.isSameNative(print), true);
-assertEq(gdbg.getProperty("print").return.isSameNative(newGlobal), false);
-
-g.eval(`
-const x = [];
-Object.defineProperty(x, "a", {
-  get: print,
-  set: print,
-});
-function f() {
-  x.a++;
-  x.length = 0;
-  x.push(4, 5, 6);
-  x.sort(print);
-}
-`);
-
-const comparisons = [
-  print,
-  Array.prototype.push,
-  Array.prototype.sort, // Note: self-hosted
-  newGlobal
-];
-
-const rv = [];
-dbg.onNativeCall = (callee, reason) => {
-  for (const fn of comparisons) {
-    if (callee.isSameNative(fn)) {
-      rv.push(fn.name);
-    }
-  }
-}
-
-for (let i = 0; i < 5; i++) {
-  rv.length = 0;
-  gdbg.executeInGlobal(`f()`);
-  assertEqArray(rv, ["print", "print", "push", "sort", "print", "print"]);
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Script-getEffectfulOffsets.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// Check that Script.getEffectfulOffsets behaves sensibly.
-
-var g = newGlobal({newCompartment: true});
-var dbg = Debugger(g);
-var numEffectfulOperations;
-
-function onNewScript(script) {
-  script.getChildScripts().forEach(onNewScript);
-  numEffectfulOperations += script.getEffectfulOffsets().length;
-}
-dbg.onNewScript = onNewScript;
-
-function test(code, expected) {
-  numEffectfulOperations = 0;
-  g.eval(`
-function f(a, b, c) {
-${code}
-}
-`);
-  assertEq(numEffectfulOperations, expected);
-}
-
-test("a.f = 0", 1);
-test("a.f++", 1);
-test("return a.f", 0);
-test("a[b] = 0", 1);
-test("a[b]++", 1);
-test("return a[b]", 0);
-test("a = 0", 0);
-test("d = 0", 1);
-test("with (a) { b = 0; }", 1);
-test("let o = {}; ({x: o.x} = { x: 10 })", 1);
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -354,22 +354,16 @@ static MethodStatus CanEnterBaselineInte
 }
 
 MethodStatus jit::CanEnterBaselineInterpreterAtBranch(JSContext* cx,
                                                       InterpreterFrame* fp) {
   if (!CheckFrame(fp)) {
     return Method_CantCompile;
   }
 
-  // JITs do not respect the debugger's OnNativeCall hook, so JIT execution is
-  // disabled if this hook might need to be called.
-  if (cx->insideDebuggerEvaluationWithOnNativeCallHook) {
-    return Method_CantCompile;
-  }
-
   return CanEnterBaselineInterpreter(cx, fp->script());
 }
 
 template <BaselineTier Tier>
 MethodStatus jit::CanEnterBaselineMethod(JSContext* cx, RunState& state) {
   if (state.isInvoke()) {
     InvokeState& invoke = *state.asInvoke();
     if (invoke.args().length() > BASELINE_MAX_ARGS_LENGTH) {
--- a/js/src/jit/Jit.cpp
+++ b/js/src/jit/Jit.cpp
@@ -133,22 +133,16 @@ static EnterJitStatus JS_HAZ_JSNATIVE_CA
 }
 
 EnterJitStatus js::jit::MaybeEnterJit(JSContext* cx, RunState& state) {
   if (!IsBaselineInterpreterEnabled()) {
     // All JITs are disabled.
     return EnterJitStatus::NotEntered;
   }
 
-  // JITs do not respect the debugger's OnNativeCall hook, so JIT execution is
-  // disabled if this hook might need to be called.
-  if (cx->insideDebuggerEvaluationWithOnNativeCallHook) {
-    return EnterJitStatus::NotEntered;
-  }
-
   JSScript* script = state.script();
 
   uint8_t* code = script->jitCodeRaw();
   do {
     // Make sure we can enter Baseline Interpreter code. Note that the prologue
     // has warm-up checks to tier up if needed.
     if (script->hasJitScript()) {
       break;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -424,35 +424,24 @@ bool js::RunScript(JSContext* cx, RunSta
   return Interpret(cx, state);
 }
 #ifdef _MSC_VER
 #  pragma optimize("", on)
 #endif
 
 STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
 MOZ_ALWAYS_INLINE bool CallJSNative(JSContext* cx, Native native,
-                                    CallReason reason,
                                     const CallArgs& args) {
   TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
   AutoTraceLog traceLog(logger, TraceLogger_Call);
 
   if (!CheckRecursionLimit(cx)) {
     return false;
   }
 
-  switch (DebugAPI::onNativeCall(cx, args, reason)) {
-    case ResumeMode::Continue:
-      break;
-    case ResumeMode::Throw:
-    case ResumeMode::Terminate:
-      return false;
-    case ResumeMode::Return:
-      return true;
-  }
-
 #ifdef DEBUG
   bool alreadyThrowing = cx->isExceptionPending();
 #endif
   cx->check(args);
   MOZ_ASSERT(!args.callee().is<ProxyObject>());
 
   AutoRealm ar(cx, &args.callee());
   bool ok = native(cx, args.length(), args.base());
@@ -466,17 +455,17 @@ MOZ_ALWAYS_INLINE bool CallJSNative(JSCo
 STATIC_PRECONDITION(ubound(args.argv_) >= argc)
 MOZ_ALWAYS_INLINE bool CallJSNativeConstructor(JSContext* cx, Native native,
                                                const CallArgs& args) {
 #ifdef DEBUG
   RootedObject callee(cx, &args.callee());
 #endif
 
   MOZ_ASSERT(args.thisv().isMagic());
-  if (!CallJSNative(cx, native, CallReason::Call, args)) {
+  if (!CallJSNative(cx, native, args)) {
     return false;
   }
 
   /*
    * Native constructors must return non-primitive values on success.
    * Although it is legal, if a constructor returns the callee, there is a
    * 99.9999% chance it is a bug. If any valid code actually wants the
    * constructor to return the callee, the assertion can be removed or
@@ -497,18 +486,17 @@ MOZ_ALWAYS_INLINE bool CallJSNativeConst
  * required arguments, allocate declared local variables, and pop everything
  * when done.  Then push the return value.
  *
  * Note: This function DOES NOT call GetThisValue to munge |args.thisv()| if
  *       necessary.  The caller (usually the interpreter) must have performed
  *       this step already!
  */
 bool js::InternalCallOrConstruct(JSContext* cx, const CallArgs& args,
-                                 MaybeConstruct construct,
-                                 CallReason reason) {
+                                 MaybeConstruct construct) {
   MOZ_ASSERT(args.length() <= ARGS_LENGTH_MAX);
   MOZ_ASSERT(!cx->zone()->types.activeAnalysis);
 
   unsigned skipForCallee = args.length() + 1 + (construct == CONSTRUCT);
   if (args.calleev().isPrimitive()) {
     return ReportIsNotFunction(cx, args.calleev(), skipForCallee, construct);
   }
 
@@ -523,17 +511,17 @@ bool js::InternalCallOrConstruct(JSConte
     if (args.callee().is<ProxyObject>()) {
       RootedObject proxy(cx, &args.callee());
       return Proxy::call(cx, proxy, args);
     }
 
     JSNative call = args.callee().callHook();
     MOZ_ASSERT(call, "isCallable without a callHook?");
 
-    return CallJSNative(cx, call, reason, args);
+    return CallJSNative(cx, call, args);
   }
 
   /* Invoke native functions. */
   RootedFunction fun(cx, &args.callee().as<JSFunction>());
   if (construct != CONSTRUCT && fun->isClassConstructor()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_CANT_CALL_CLASS_CONSTRUCTOR);
     return false;
@@ -543,30 +531,17 @@ bool js::InternalCallOrConstruct(JSConte
     MOZ_ASSERT_IF(construct, !fun->isConstructor());
     JSNative native = fun->native();
     if (!construct && args.ignoresReturnValue() && fun->hasJitInfo()) {
       const JSJitInfo* jitInfo = fun->jitInfo();
       if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) {
         native = jitInfo->ignoresReturnValueMethod;
       }
     }
-    return CallJSNative(cx, native, reason, args);
-  }
-
-  // Self-hosted builtins are considered native by the onNativeCall hook.
-  if (fun->isSelfHostedBuiltin()) {
-    switch (DebugAPI::onNativeCall(cx, args, reason)) {
-      case ResumeMode::Continue:
-        break;
-      case ResumeMode::Throw:
-      case ResumeMode::Terminate:
-        return false;
-      case ResumeMode::Return:
-        return true;
-    }
+    return CallJSNative(cx, native, args);
   }
 
   if (!JSFunction::getOrCreateScript(cx, fun)) {
     return false;
   }
 
   /* Run function until JSOP_RETRVAL, JSOP_RETURN or error. */
   InvokeState state(cx, args, construct);
@@ -590,18 +565,17 @@ bool js::InternalCallOrConstruct(JSConte
   }
 
   bool ok = RunScript(cx, state);
 
   MOZ_ASSERT_IF(ok && construct, args.rval().isObject());
   return ok;
 }
 
-static bool InternalCall(JSContext* cx, const AnyInvokeArgs& args,
-                         CallReason reason = CallReason::Call) {
+static bool InternalCall(JSContext* cx, const AnyInvokeArgs& args) {
   MOZ_ASSERT(args.array() + args.length() == args.end(),
              "must pass calling arguments to a calling attempt");
 
   if (args.thisv().isObject()) {
     // We must call the thisValue hook in case we are not called from the
     // interpreter, where a prior bytecode has computed an appropriate
     // |this| already.  But don't do that if fval is a DOM function.
     HandleValue fval = args.calleev();
@@ -612,34 +586,33 @@ static bool InternalCall(JSContext* cx, 
             .as<JSFunction>()
             .jitInfo()
             ->needsOuterizedThisObject()) {
       JSObject* thisObj = &args.thisv().toObject();
       args.mutableThisv().set(GetThisValue(thisObj));
     }
   }
 
-  return InternalCallOrConstruct(cx, args, NO_CONSTRUCT, reason);
+  return InternalCallOrConstruct(cx, args, NO_CONSTRUCT);
 }
 
 bool js::CallFromStack(JSContext* cx, const CallArgs& args) {
   return InternalCall(cx, static_cast<const AnyInvokeArgs&>(args));
 }
 
 // ES7 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
 // 7.3.12 Call.
 bool js::Call(JSContext* cx, HandleValue fval, HandleValue thisv,
-              const AnyInvokeArgs& args, MutableHandleValue rval,
-              CallReason reason) {
+              const AnyInvokeArgs& args, MutableHandleValue rval) {
   // Explicitly qualify these methods to bypass AnyInvokeArgs's deliberate
   // shadowing.
   args.CallArgs::setCallee(fval);
   args.CallArgs::setThis(thisv);
 
-  if (!InternalCall(cx, args, reason)) {
+  if (!InternalCall(cx, args)) {
     return false;
   }
 
   rval.set(args.rval());
   return true;
 }
 
 static bool InternalConstruct(JSContext* cx, const AnyConstructArgs& args) {
@@ -753,31 +726,31 @@ bool js::CallGetter(JSContext* cx, Handl
   // Invoke could result in another try to get or set the same id again, see
   // bug 355497.
   if (!CheckRecursionLimit(cx)) {
     return false;
   }
 
   FixedInvokeArgs<0> args(cx);
 
-  return Call(cx, getter, thisv, args, rval, CallReason::Getter);
+  return Call(cx, getter, thisv, args, rval);
 }
 
 bool js::CallSetter(JSContext* cx, HandleValue thisv, HandleValue setter,
                     HandleValue v) {
   if (!CheckRecursionLimit(cx)) {
     return false;
   }
 
   FixedInvokeArgs<1> args(cx);
 
   args[0].set(v);
 
   RootedValue ignored(cx);
-  return Call(cx, setter, thisv, args, &ignored, CallReason::Setter);
+  return Call(cx, setter, thisv, args, &ignored);
 }
 
 bool js::ExecuteKernel(JSContext* cx, HandleScript script,
                        JSObject& envChainArg, const Value& newTargetValue,
                        AbstractFramePtr evalInFrame, Value* result) {
   MOZ_ASSERT_IF(script->isGlobalCode(),
                 IsGlobalLexicalEnvironment(&envChainArg) ||
                     !IsSyntacticEnvironment(&envChainArg));
@@ -3088,23 +3061,21 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_
 
       MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc));
       CallArgs args =
           CallArgsFromSp(argStackSlots, REGS.sp, construct, ignoresReturnValue);
 
       JSFunction* maybeFun;
       bool isFunction = IsFunctionObject(args.calleev(), &maybeFun);
 
-      // Use the slow path if the callee is not an interpreted function, if we
-      // have to throw an exception, or if we might have to invoke the
-      // OnNativeCall hook for a self-hosted builtin.
+      // Use the slow path if the callee is not an interpreted function or if we
+      // have to throw an exception.
       if (!isFunction || !maybeFun->isInterpreted() ||
           (construct && !maybeFun->isConstructor()) ||
-          (!construct && maybeFun->isClassConstructor()) ||
-          cx->insideDebuggerEvaluationWithOnNativeCallHook) {
+          (!construct && maybeFun->isClassConstructor())) {
         if (construct) {
           if (!ConstructFromStack(cx, args)) {
             goto error;
           }
         } else {
           if (*REGS.pc == JSOP_CALLITER && args.calleev().isPrimitive()) {
             MOZ_ASSERT(args.length() == 0, "thisv must be on top of the stack");
             ReportValueError(cx, JSMSG_NOT_ITERABLE, -1, args.thisv(), nullptr);
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -40,34 +40,26 @@ extern void GetNonSyntacticGlobalThis(JS
 extern bool ReportIsNotFunction(JSContext* cx, HandleValue v, int numToSkip,
                                 MaybeConstruct construct = NO_CONSTRUCT);
 
 /* See ReportIsNotFunction comment for the meaning of numToSkip. */
 extern JSObject* ValueToCallable(JSContext* cx, HandleValue v,
                                  int numToSkip = -1,
                                  MaybeConstruct construct = NO_CONSTRUCT);
 
-// Reasons why a call could be performed, for passing onto the debugger.
-enum class CallReason {
-  Call,
-  Getter,
-  Setter
-};
-
 /*
  * Call or construct arguments that are stored in rooted memory.
  *
  * NOTE: Any necessary |GetThisValue| computation must have been performed on
  *       |args.thisv()|, likely by the interpreter when pushing |this| onto the
  *       stack.  If you're not sure whether |GetThisValue| processing has been
  *       performed, use |Invoke|.
  */
 extern bool InternalCallOrConstruct(JSContext* cx, const CallArgs& args,
-                                    MaybeConstruct construct,
-                                    CallReason reason = CallReason::Call);
+                                    MaybeConstruct construct);
 
 /*
  * These helpers take care of the infinite-recursion check necessary for
  * getter/setter calls.
  */
 extern bool CallGetter(JSContext* cx, HandleValue thisv, HandleValue getter,
                        MutableHandleValue rval);
 
@@ -79,18 +71,17 @@ extern bool CallSetter(JSContext* cx, Ha
 // All parameters are required, hopefully forcing callers to be careful not to
 // (say) blindly pass callee as |newTarget| when a different value should have
 // been passed.  Behavior is unspecified if any element of |args| isn't
 // initialized.
 //
 // |rval| is written to *only* after |fval| and |thisv| have been consumed, so
 // |rval| *may* alias either argument.
 extern bool Call(JSContext* cx, HandleValue fval, HandleValue thisv,
-                 const AnyInvokeArgs& args, MutableHandleValue rval,
-                 CallReason reason = CallReason::Call);
+                 const AnyInvokeArgs& args, MutableHandleValue rval);
 
 inline bool Call(JSContext* cx, HandleValue fval, HandleValue thisv,
                  MutableHandleValue rval) {
   FixedInvokeArgs<0> args(cx);
   return Call(cx, fval, thisv, args, rval);
 }
 
 inline bool Call(JSContext* cx, HandleValue fval, JSObject* thisObj,
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -1289,21 +1289,21 @@ JSContext::JSContext(JSRuntime* runtime,
       osrTempData_(this, nullptr),
       ionReturnOverride_(this, MagicValue(JS_ARG_POISON)),
       jitStackLimit(UINTPTR_MAX),
       jitStackLimitNoInterrupt(this, UINTPTR_MAX),
       jobQueue(this, nullptr),
       internalJobQueue(this),
       canSkipEnqueuingJobs(this, false),
       promiseRejectionTrackerCallback(this, nullptr),
-      promiseRejectionTrackerCallbackData(this, nullptr),
+      promiseRejectionTrackerCallbackData(this, nullptr)
 #ifdef JS_STRUCTURED_SPEW
-      structuredSpewer_(),
+      ,
+      structuredSpewer_()
 #endif
-      insideDebuggerEvaluationWithOnNativeCallHook(this, nullptr)
 {
   MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
              JS::RootingContext::get(this));
 }
 
 JSContext::~JSContext() {
   // Clear the ContextKind first, so that ProtectedData checks will allow us to
   // destroy this context even if the runtime is already gone.
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -993,22 +993,16 @@ struct JSContext : public JS::RootingCon
  private:
   // Spewer for this thread
   js::UnprotectedData<js::StructuredSpewer> structuredSpewer_;
 
  public:
   js::StructuredSpewer& spewer() { return structuredSpewer_.ref(); }
 #endif
 
-  // During debugger evaluations which need to observe native calls, JITs are
-  // completely disabled. This flag indicates whether we are in this state, and
-  // the debugger which initiated the evaluation. This debugger has other
-  // references on the stack and does not need to be traced.
-  js::ContextData<js::Debugger*> insideDebuggerEvaluationWithOnNativeCallHook;
-
 }; /* struct JSContext */
 
 inline JS::Result<> JSContext::boolToResult(bool ok) {
   if (MOZ_LIKELY(ok)) {
     MOZ_ASSERT(!isExceptionPending());
     MOZ_ASSERT(!isPropagatingForcedReturn());
     return JS::Ok();
   }
@@ -1244,30 +1238,16 @@ class MOZ_RAII AutoKeepAtoms {
   JSContext* cx;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
  public:
   explicit inline AutoKeepAtoms(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
   inline ~AutoKeepAtoms();
 };
 
-class MOZ_RAII AutoNoteDebuggerEvaluationWithOnNativeCallHook {
-  JSContext* cx;
-  Debugger* oldValue;
- public:
-  AutoNoteDebuggerEvaluationWithOnNativeCallHook(JSContext* cx, Debugger* dbg)
-      : cx(cx), oldValue(cx->insideDebuggerEvaluationWithOnNativeCallHook) {
-    cx->insideDebuggerEvaluationWithOnNativeCallHook = dbg;
-  }
-
-  ~AutoNoteDebuggerEvaluationWithOnNativeCallHook() {
-    cx->insideDebuggerEvaluationWithOnNativeCallHook = oldValue;
-  }
-};
-
 enum UnsafeABIStrictness {
   NoExceptions,
   AllowPendingExceptions,
   AllowThrownExceptions
 };
 
 // Should be used in functions called directly from JIT code (with
 // masm.callWithABI) to assert invariants in debug builds.