Bug 1564170: Give Debugger.Script instances their own NativeObject subclass, DebuggerScript. r=jorendorff
authorJim Blandy <jimb@mozilla.com>
Wed, 17 Jul 2019 07:13:21 +0000
changeset 483077 61c49c2a77f7f3a6078a25c0c5ed5e2d8c6f02e5
parent 483076 5fe29dc3c21e8b385aa02aafacba309f932802a6
child 483078 fe088addd88ae54284eefa8f6f06ae0d41530c6d
push id90170
push userjblandy@mozilla.com
push dateWed, 17 Jul 2019 07:15:19 +0000
treeherderautoland@0f741c61e16b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1564170
milestone70.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1564170: Give Debugger.Script instances their own NativeObject subclass, DebuggerScript. r=jorendorff Define a new class, DebuggerScript, inheriting from NativeObject. Move methods and utility functions into the class as makes sense. Tighten types where possible on code that works with DebuggerScript objects. No intended change in behavior. Differential Revision: https://phabricator.services.mozilla.com/D37509
js/src/dbg/Debugger.cpp
js/src/dbg/Debugger.h
js/src/gc/Rooting.h
--- a/js/src/dbg/Debugger.cpp
+++ b/js/src/dbg/Debugger.cpp
@@ -79,37 +79,33 @@ using mozilla::MakeScopeExit;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::Some;
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 
 /*** Forward declarations, ClassOps and Classes *****************************/
 
-static void DebuggerScript_trace(JSTracer* trc, JSObject* obj);
 static void DebuggerSource_trace(JSTracer* trc, JSObject* obj);
 
-enum { JSSLOT_DEBUGSCRIPT_OWNER, JSSLOT_DEBUGSCRIPT_COUNT };
-
-static const ClassOps DebuggerScript_classOps = {nullptr, /* addProperty */
-                                                 nullptr, /* delProperty */
-                                                 nullptr, /* enumerate   */
-                                                 nullptr, /* newEnumerate */
-                                                 nullptr, /* resolve     */
-                                                 nullptr, /* mayResolve  */
-                                                 nullptr, /* finalize    */
-                                                 nullptr, /* call        */
-                                                 nullptr, /* hasInstance */
-                                                 nullptr, /* construct   */
-                                                 DebuggerScript_trace};
-
-static const Class DebuggerScript_class = {
-    "Script",
-    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
-    &DebuggerScript_classOps};
+const ClassOps DebuggerScript::classOps_ = {nullptr, /* addProperty */
+                                            nullptr, /* delProperty */
+                                            nullptr, /* enumerate   */
+                                            nullptr, /* newEnumerate */
+                                            nullptr, /* resolve     */
+                                            nullptr, /* mayResolve  */
+                                            nullptr, /* finalize    */
+                                            nullptr, /* call        */
+                                            nullptr, /* hasInstance */
+                                            nullptr, /* construct   */
+                                            trace};
+
+const Class DebuggerScript::class_ = {
+    "Script", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
+    &classOps_};
 
 enum {
   JSSLOT_DEBUGSOURCE_OWNER,
   JSSLOT_DEBUGSOURCE_TEXT,
   JSSLOT_DEBUGSOURCE_COUNT
 };
 
 static const ClassOps DebuggerSource_classOps = {nullptr, /* addProperty */
@@ -441,28 +437,28 @@ Debugger::~Debugger() {
       onNewGlobalObjectWatchersLink.mNext ||
       cx->runtime()->onNewGlobalObjectWatchers().begin() ==
           JSRuntime::WatchersList::Iterator(this)) {
     cx->runtime()->onNewGlobalObjectWatchers().remove(this);
   }
 }
 
 JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
-                 unsigned(JSSLOT_DEBUGSCRIPT_OWNER));
+                 unsigned(DebuggerScript::OWNER_SLOT));
 JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
                  unsigned(JSSLOT_DEBUGSOURCE_OWNER));
 JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
                  unsigned(JSSLOT_DEBUGOBJECT_OWNER));
 JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
                  unsigned(DebuggerEnvironment::OWNER_SLOT));
 
 /* static */
 Debugger* Debugger::fromChildJSObject(JSObject* obj) {
   MOZ_ASSERT(obj->getClass() == &DebuggerFrame::class_ ||
-             obj->getClass() == &DebuggerScript_class ||
+             obj->getClass() == &DebuggerScript::class_ ||
              obj->getClass() == &DebuggerSource_class ||
              obj->getClass() == &DebuggerObject::class_ ||
              obj->getClass() == &DebuggerEnvironment::class_);
   JSObject* dbgobj = &obj->as<NativeObject>()
                           .getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER)
                           .toObject();
   return fromJSObject(dbgobj);
 }
@@ -3292,20 +3288,20 @@ void Debugger::removeAllocationsTracking
 }
 
 /*** Debugger JSObjects *****************************************************/
 
 void Debugger::traceCrossCompartmentEdges(JSTracer* trc) {
   generatorFrames.traceCrossCompartmentEdges<DebuggerFrame::trace>(trc);
   objects.traceCrossCompartmentEdges<DebuggerObject::trace>(trc);
   environments.traceCrossCompartmentEdges<DebuggerEnvironment::trace>(trc);
-  scripts.traceCrossCompartmentEdges<DebuggerScript_trace>(trc);
-  lazyScripts.traceCrossCompartmentEdges<DebuggerScript_trace>(trc);
+  scripts.traceCrossCompartmentEdges<DebuggerScript::trace>(trc);
+  lazyScripts.traceCrossCompartmentEdges<DebuggerScript::trace>(trc);
   sources.traceCrossCompartmentEdges<DebuggerSource_trace>(trc);
-  wasmInstanceScripts.traceCrossCompartmentEdges<DebuggerScript_trace>(trc);
+  wasmInstanceScripts.traceCrossCompartmentEdges<DebuggerScript::trace>(trc);
   wasmInstanceSources.traceCrossCompartmentEdges<DebuggerSource_trace>(trc);
 }
 
 /*
  * Ordinarily, WeakMap keys and values are marked because at some point it was
  * discovered that the WeakMap was live; that is, some object containing the
  * WeakMap was marked during mark phase.
  *
@@ -5819,61 +5815,100 @@ const JSFunctionSpec Debugger::methods[]
 
 const JSFunctionSpec Debugger::static_methods[]{
     JS_FN("isCompilableUnit", Debugger::isCompilableUnit, 1, 0),
     JS_FN("recordReplayProcessKind", Debugger::recordReplayProcessKind, 0, 0),
     JS_FS_END};
 
 /*** Debugger.Script ********************************************************/
 
-// Get the Debugger.Script referent as bare Cell. This should only be used for
-// GC operations like tracing. Please use GetScriptReferent below.
-static inline gc::Cell* GetScriptReferentCell(JSObject* obj) {
-  MOZ_ASSERT(obj->getClass() == &DebuggerScript_class);
-  return static_cast<gc::Cell*>(obj->as<NativeObject>().getPrivate());
-}
-
-static inline DebuggerScriptReferent GetScriptReferent(JSObject* obj) {
-  MOZ_ASSERT(obj->getClass() == &DebuggerScript_class);
-  if (gc::Cell* cell = GetScriptReferentCell(obj)) {
+gc::Cell* DebuggerScript::getReferentCell() const {
+  return static_cast<gc::Cell*>(getPrivate());
+}
+
+DebuggerScriptReferent DebuggerScript::getReferent() const {
+  if (gc::Cell* cell = getReferentCell()) {
     if (cell->is<JSScript>()) {
       return AsVariant(cell->as<JSScript>());
     }
     if (cell->is<LazyScript>()) {
       return AsVariant(cell->as<LazyScript>());
     }
     MOZ_ASSERT(cell->is<JSObject>());
     return AsVariant(
         &static_cast<NativeObject*>(cell)->as<WasmInstanceObject>());
   }
   return AsVariant(static_cast<JSScript*>(nullptr));
 }
 
-void DebuggerScript_trace(JSTracer* trc, JSObject* obj) {
+/* static */
+void DebuggerScript::trace(JSTracer* trc, JSObject* obj) {
+  DebuggerScript* self = &obj->as<DebuggerScript>();
   // This comes from a private pointer, so no barrier needed.
-  gc::Cell* cell = GetScriptReferentCell(obj);
+  gc::Cell* cell = self->getReferentCell();
   if (cell) {
     if (cell->is<JSScript>()) {
       JSScript* script = cell->as<JSScript>();
       TraceManuallyBarrieredCrossCompartmentEdge(
-          trc, obj, &script, "Debugger.Script script referent");
-      obj->as<NativeObject>().setPrivateUnbarriered(script);
+          trc, self, &script, "Debugger.Script script referent");
+      self->setPrivateUnbarriered(script);
     } else if (cell->is<LazyScript>()) {
       LazyScript* lazyScript = cell->as<LazyScript>();
       TraceManuallyBarrieredCrossCompartmentEdge(
-          trc, obj, &lazyScript, "Debugger.Script lazy script referent");
-      obj->as<NativeObject>().setPrivateUnbarriered(lazyScript);
+          trc, self, &lazyScript, "Debugger.Script lazy script referent");
+      self->setPrivateUnbarriered(lazyScript);
     } else {
       JSObject* wasm = cell->as<JSObject>();
       TraceManuallyBarrieredCrossCompartmentEdge(
-          trc, obj, &wasm, "Debugger.Script wasm referent");
+          trc, self, &wasm, "Debugger.Script wasm referent");
       MOZ_ASSERT(wasm->is<WasmInstanceObject>());
-      obj->as<NativeObject>().setPrivateUnbarriered(wasm);
-    }
-  }
+      self->setPrivateUnbarriered(wasm);
+    }
+  }
+}
+
+/* static */
+NativeObject* DebuggerScript::initClass(JSContext* cx,
+                                        Handle<GlobalObject*> global,
+                                        HandleObject debugCtor) {
+  return InitClass(cx, debugCtor, nullptr, &class_, construct, 0, properties_,
+                   methods_, nullptr, nullptr);
+}
+
+class DebuggerScriptSetPrivateMatcher {
+  DebuggerScript* obj_;
+
+ public:
+  explicit DebuggerScriptSetPrivateMatcher(DebuggerScript* obj) : obj_(obj) {}
+  using ReturnType = void;
+  ReturnType match(HandleScript script) { obj_->setPrivateGCThing(script); }
+  ReturnType match(Handle<LazyScript*> lazyScript) {
+    obj_->setPrivateGCThing(lazyScript);
+  }
+  ReturnType match(Handle<WasmInstanceObject*> instance) {
+    obj_->setPrivateGCThing(instance);
+  }
+};
+
+/* static */
+DebuggerScript* DebuggerScript::create(JSContext* cx, HandleObject proto,
+                                       Handle<DebuggerScriptReferent> referent,
+                                       HandleNativeObject debugger) {
+  DebuggerScript* scriptobj =
+      NewObjectWithGivenProto<DebuggerScript>(cx, proto, TenuredObject);
+  if (!scriptobj) {
+    return nullptr;
+  }
+
+  scriptobj->setReservedSlot(DebuggerScript::OWNER_SLOT,
+                             ObjectValue(*debugger));
+  DebuggerScriptSetPrivateMatcher matcher(scriptobj);
+  referent.match(matcher);
+
+  return scriptobj;
 }
 
 static JSScript* DelazifyScript(JSContext* cx, Handle<LazyScript*> lazyScript) {
   if (lazyScript->maybeScript()) {
     return lazyScript->maybeScript();
   }
 
   // JSFunction::getOrCreateScript requires the enclosing script not to be
@@ -5902,62 +5937,41 @@ static JSScript* DelazifyScript(JSContex
   AutoRealm ar(cx, fun0);
   RootedFunction fun(cx, LazyScript::functionDelazifying(cx, lazyScript));
   if (!fun) {
     return nullptr;
   }
   return fun->getOrCreateScript(cx, fun);
 }
 
-class DebuggerScriptSetPrivateMatcher {
-  NativeObject* obj_;
-
- public:
-  explicit DebuggerScriptSetPrivateMatcher(NativeObject* obj) : obj_(obj) {}
-  using ReturnType = void;
-  ReturnType match(HandleScript script) { obj_->setPrivateGCThing(script); }
-  ReturnType match(Handle<LazyScript*> lazyScript) {
-    obj_->setPrivateGCThing(lazyScript);
-  }
-  ReturnType match(Handle<WasmInstanceObject*> instance) {
-    obj_->setPrivateGCThing(instance);
-  }
-};
-
-NativeObject* Debugger::newDebuggerScript(
+DebuggerScript* Debugger::newDebuggerScript(
     JSContext* cx, Handle<DebuggerScriptReferent> referent) {
   cx->check(object.get());
 
   RootedObject proto(
       cx, &object->getReservedSlot(JSSLOT_DEBUG_SCRIPT_PROTO).toObject());
   MOZ_ASSERT(proto);
-  NativeObject* scriptobj = NewNativeObjectWithGivenProto(
-      cx, &DebuggerScript_class, proto, TenuredObject);
-  if (!scriptobj) {
-    return nullptr;
-  }
-  scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_OWNER, ObjectValue(*object));
-  DebuggerScriptSetPrivateMatcher matcher(scriptobj);
-  referent.match(matcher);
-
-  return scriptobj;
-}
-
-template <typename ReferentVariant, typename Referent, typename Map>
-JSObject* Debugger::wrapVariantReferent(JSContext* cx, Map& map,
-                                        Handle<CrossCompartmentKey> key,
-                                        Handle<ReferentVariant> referent) {
+  RootedNativeObject debugger(cx, object);
+
+  return DebuggerScript::create(cx, proto, referent, debugger);
+}
+
+template <typename Wrapper, typename ReferentVariant, typename Referent,
+          typename Map>
+Wrapper* Debugger::wrapVariantReferent(JSContext* cx, Map& map,
+                                       Handle<CrossCompartmentKey> key,
+                                       Handle<ReferentVariant> referent) {
   cx->check(object);
 
   Handle<Referent> untaggedReferent = referent.template as<Referent>();
   MOZ_ASSERT(cx->compartment() != untaggedReferent->compartment());
 
   DependentAddPtr<Map> p(cx, map, untaggedReferent);
   if (!p) {
-    NativeObject* wrapper = newVariantWrapper(cx, referent);
+    Wrapper* wrapper = newVariantWrapper(cx, referent);
     if (!wrapper) {
       return nullptr;
     }
 
     if (!p.add(cx, map, untaggedReferent, wrapper)) {
       NukeDebuggerWrapper(wrapper);
       return nullptr;
     }
@@ -5965,22 +5979,22 @@ JSObject* Debugger::wrapVariantReferent(
     if (!object->compartment()->putWrapper(cx, key, ObjectValue(*wrapper))) {
       NukeDebuggerWrapper(wrapper);
       map.remove(untaggedReferent);
       ReportOutOfMemory(cx);
       return nullptr;
     }
   }
 
-  return p->value();
-}
-
-JSObject* Debugger::wrapVariantReferent(
+  return &p->value()->template as<Wrapper>();
+}
+
+DebuggerScript* Debugger::wrapVariantReferent(
     JSContext* cx, Handle<DebuggerScriptReferent> referent) {
-  JSObject* obj;
+  DebuggerScript* obj;
   if (referent.is<JSScript*>()) {
     Handle<JSScript*> untaggedReferent = referent.template as<JSScript*>();
     if (untaggedReferent->maybeLazyScript()) {
       // If the JSScript has corresponding LazyScript, wrap the LazyScript
       // instead.
       //
       // This is necessary for Debugger.Script identity.  If we use both
       // JSScript and LazyScript for same single script, those 2 wrapped
@@ -5990,175 +6004,181 @@ JSObject* Debugger::wrapVariantReferent(
       // If a script has corresponding LazyScript and JSScript, the
       // lifetime of the LazyScript is always longer than the JSScript.
       // So we can use the LazyScript as a proxy for the JSScript.
       Rooted<LazyScript*> lazyScript(cx, untaggedReferent->maybeLazyScript());
       Rooted<DebuggerScriptReferent> lazyScriptReferent(cx, lazyScript.get());
 
       Rooted<CrossCompartmentKey> key(cx,
                                       CrossCompartmentKey(object, lazyScript));
-      obj = wrapVariantReferent<DebuggerScriptReferent, LazyScript*,
-                                LazyScriptWeakMap>(cx, lazyScripts, key,
-                                                   lazyScriptReferent);
-      MOZ_ASSERT_IF(obj, GetScriptReferent(obj) == lazyScriptReferent);
+      obj = wrapVariantReferent<DebuggerScript, DebuggerScriptReferent,
+                                LazyScript*, LazyScriptWeakMap>(
+          cx, lazyScripts, key, lazyScriptReferent);
+      MOZ_ASSERT_IF(obj, obj->getReferent() == lazyScriptReferent);
       return obj;
     } else {
       // If the JSScript doesn't have corresponding LazyScript, the script
       // is not lazifiable, and we can safely use JSScript as referent.
       Rooted<CrossCompartmentKey> key(
           cx, CrossCompartmentKey(object, untaggedReferent));
       obj =
-          wrapVariantReferent<DebuggerScriptReferent, JSScript*, ScriptWeakMap>(
-              cx, scripts, key, referent);
+          wrapVariantReferent<DebuggerScript, DebuggerScriptReferent, JSScript*,
+                              ScriptWeakMap>(cx, scripts, key, referent);
     }
   } else if (referent.is<LazyScript*>()) {
     Handle<LazyScript*> untaggedReferent = referent.template as<LazyScript*>();
     Rooted<CrossCompartmentKey> key(
         cx, CrossCompartmentKey(object, untaggedReferent));
     obj =
-        wrapVariantReferent<DebuggerScriptReferent, LazyScript*,
+        wrapVariantReferent<DebuggerScript, DebuggerScriptReferent, LazyScript*,
                             LazyScriptWeakMap>(cx, lazyScripts, key, referent);
   } else {
     Handle<WasmInstanceObject*> untaggedReferent =
         referent.template as<WasmInstanceObject*>();
     Rooted<CrossCompartmentKey> key(
         cx, CrossCompartmentKey::DebuggeeWasmScript(object, untaggedReferent));
-    obj = wrapVariantReferent<DebuggerScriptReferent, WasmInstanceObject*,
-                              WasmInstanceWeakMap>(cx, wasmInstanceScripts, key,
-                                                   referent);
-  }
-  MOZ_ASSERT_IF(obj, GetScriptReferent(obj) == referent);
+    obj = wrapVariantReferent<DebuggerScript, DebuggerScriptReferent,
+                              WasmInstanceObject*, WasmInstanceWeakMap>(
+        cx, wasmInstanceScripts, key, referent);
+  }
+  MOZ_ASSERT_IF(obj, obj->getReferent() == referent);
   return obj;
 }
 
-JSObject* Debugger::wrapScript(JSContext* cx, HandleScript script) {
+DebuggerScript* Debugger::wrapScript(JSContext* cx, HandleScript script) {
   Rooted<DebuggerScriptReferent> referent(cx, script.get());
   return wrapVariantReferent(cx, referent);
 }
 
-JSObject* Debugger::wrapLazyScript(JSContext* cx,
-                                   Handle<LazyScript*> lazyScript) {
+DebuggerScript* Debugger::wrapLazyScript(JSContext* cx,
+                                         Handle<LazyScript*> lazyScript) {
   Rooted<DebuggerScriptReferent> referent(cx, lazyScript.get());
   return wrapVariantReferent(cx, referent);
 }
 
-JSObject* Debugger::wrapWasmScript(JSContext* cx,
-                                   Handle<WasmInstanceObject*> wasmInstance) {
+DebuggerScript* Debugger::wrapWasmScript(
+    JSContext* cx, Handle<WasmInstanceObject*> wasmInstance) {
   Rooted<DebuggerScriptReferent> referent(cx, wasmInstance.get());
   return wrapVariantReferent(cx, referent);
 }
 
-static JSObject* DebuggerScript_check(JSContext* cx, HandleValue v,
+/* static */
+DebuggerScript* DebuggerScript::check(JSContext* cx, HandleValue v,
                                       const char* fnname) {
   JSObject* thisobj = RequireObject(cx, v);
   if (!thisobj) {
     return nullptr;
   }
-  if (thisobj->getClass() != &DebuggerScript_class) {
+  if (!thisobj->is<DebuggerScript>()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Script",
                               fnname, thisobj->getClass()->name);
     return nullptr;
   }
 
-  // Check for Debugger.Script.prototype, which is of class DebuggerScript_class
-  // but whose script is null.
-  if (!GetScriptReferentCell(thisobj)) {
+  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",
                               fnname, "prototype object");
     return nullptr;
   }
 
-  return thisobj;
-}
-
-static JSObject* DebuggerScript_checkThis(JSContext* cx, const CallArgs& args,
+  return &scriptObj;
+}
+
+/* static */
+DebuggerScript* DebuggerScript::checkThis(JSContext* cx, const CallArgs& args,
                                           const char* fnname) {
-  JSObject* thisobj = DebuggerScript_check(cx, args.thisv(), fnname);
+  DebuggerScript* thisobj = DebuggerScript::check(cx, args.thisv(), fnname);
   if (!thisobj) {
     return nullptr;
   }
 
-  if (!GetScriptReferent(thisobj).is<JSScript*>() &&
-      !GetScriptReferent(thisobj).is<LazyScript*>()) {
+  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;
   }
 
   return thisobj;
 }
 
 #define THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, fnname, args, obj, referent) \
   CallArgs args = CallArgsFromVp(argc, vp);                                  \
-  RootedObject obj(cx, DebuggerScript_check(cx, args.thisv(), fnname));      \
+  RootedDebuggerScript obj(cx,                                               \
+                           DebuggerScript::check(cx, args.thisv(), fnname)); \
   if (!obj) return false;                                                    \
-  Rooted<DebuggerScriptReferent> referent(cx, GetScriptReferent(obj))
-
-#define THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, fnname, args, obj) \
-  CallArgs args = CallArgsFromVp(argc, vp);                                 \
-  RootedObject obj(cx, DebuggerScript_checkThis(cx, args, fnname));         \
+  Rooted<DebuggerScriptReferent> referent(cx, obj->getReferent())
+
+#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;
 
 #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 (GetScriptReferent(obj).is<JSScript*>()) {                               \
-    script = GetScriptReferent(obj).as<JSScript*>();                          \
+  if (obj->getReferent().is<JSScript*>()) {                                   \
+    script = obj->getReferent().as<JSScript*>();                              \
   } else {                                                                    \
-    Rooted<LazyScript*> lazyScript(cx,                                        \
-                                   GetScriptReferent(obj).as<LazyScript*>()); \
+    Rooted<LazyScript*> lazyScript(cx, obj->getReferent().as<LazyScript*>()); \
     script = DelazifyScript(cx, lazyScript);                                  \
     if (!script) return false;                                                \
   }
 
 template <typename Result>
-Result CallScriptMethod(HandleObject obj,
+Result CallScriptMethod(HandleDebuggerScript obj,
                         Result (JSScript::*ifJSScript)() const,
                         Result (LazyScript::*ifLazyScript)() const) {
-  if (GetScriptReferent(obj).is<JSScript*>()) {
-    JSScript* script = GetScriptReferent(obj).as<JSScript*>();
+  if (obj->getReferent().is<JSScript*>()) {
+    JSScript* script = obj->getReferent().as<JSScript*>();
     return (script->*ifJSScript)();
   }
 
-  LazyScript* lazyScript = GetScriptReferent(obj).as<LazyScript*>();
+  LazyScript* lazyScript = obj->getReferent().as<LazyScript*>();
   return (lazyScript->*ifLazyScript)();
 }
 
-static bool DebuggerScript_getIsGeneratorFunction(JSContext* cx, unsigned argc,
-                                                  Value* vp) {
+/* 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(
       CallScriptMethod(obj, &JSScript::isGenerator, &LazyScript::isGenerator));
   return true;
 }
 
-static bool DebuggerScript_getIsAsyncFunction(JSContext* cx, unsigned argc,
-                                              Value* vp) {
+/* 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(
       CallScriptMethod(obj, &JSScript::isAsync, &LazyScript::isAsync));
   return true;
 }
 
-static bool DebuggerScript_getIsModule(JSContext* cx, unsigned argc,
-                                       Value* vp) {
+/* static */
+bool DebuggerScript::getIsModule(JSContext* cx, unsigned argc, Value* vp) {
   THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get isModule)", args, obj);
-  DebuggerScriptReferent referent = GetScriptReferent(obj);
+  DebuggerScriptReferent referent = obj->getReferent();
   args.rval().setBoolean(referent.is<JSScript*>() &&
                          referent.as<JSScript*>()->isModule());
   return true;
 }
 
-static bool DebuggerScript_getDisplayName(JSContext* cx, unsigned argc,
-                                          Value* vp) {
+/* 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) {
@@ -6170,18 +6190,19 @@ static bool DebuggerScript_getDisplayNam
   if (!dbg->wrapDebuggeeValue(cx, &namev)) {
     return false;
   }
   args.rval().set(namev);
   return true;
 }
 
 template <typename T>
-static bool DebuggerScript_getUrlImpl(JSContext* cx, CallArgs& args,
-                                      Handle<T*> script) {
+/* static */
+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());
     }
@@ -6190,39 +6211,40 @@ static bool DebuggerScript_getUrlImpl(JS
     }
     args.rval().setString(str);
   } else {
     args.rval().setNull();
   }
   return true;
 }
 
-static bool DebuggerScript_getUrl(JSContext* cx, unsigned argc, Value* vp) {
+/* static */
+bool DebuggerScript::getUrl(JSContext* cx, unsigned argc, Value* vp) {
   THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get url)", args, obj);
-  if (GetScriptReferent(obj).is<JSScript*>()) {
-    RootedScript script(cx, GetScriptReferent(obj).as<JSScript*>());
-    return DebuggerScript_getUrlImpl<JSScript>(cx, args, script);
-  }
-
-  Rooted<LazyScript*> lazyScript(cx, GetScriptReferent(obj).as<LazyScript*>());
-  return DebuggerScript_getUrlImpl<LazyScript>(cx, args, lazyScript);
+  if (obj->getReferent().is<JSScript*>()) {
+    RootedScript script(cx, obj->getReferent().as<JSScript*>());
+    return getUrlImpl<JSScript>(cx, args, script);
+  }
+
+  Rooted<LazyScript*> lazyScript(cx, obj->getReferent().as<LazyScript*>());
+  return getUrlImpl<LazyScript>(cx, args, lazyScript);
 }
 
 struct DebuggerScriptGetStartLineMatcher {
   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; }
 };
 
-static bool DebuggerScript_getStartLine(JSContext* cx, unsigned argc,
-                                        Value* vp) {
+/* static */
+bool DebuggerScript::getStartLine(JSContext* cx, unsigned argc, Value* vp) {
   THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get startLine)", args, obj,
                             referent);
   DebuggerScriptGetStartLineMatcher matcher;
   args.rval().setNumber(referent.match(matcher));
   return true;
 }
 
 struct DebuggerScriptGetLineCountMatcher {
@@ -6250,18 +6272,18 @@ struct DebuggerScriptGetLineCountMatcher
       totalLines = double(instance.debug().bytecode().length());
     } else {
       totalLines = 0;
     }
     return true;
   }
 };
 
-static bool DebuggerScript_getLineCount(JSContext* cx, unsigned argc,
-                                        Value* vp) {
+/* static */
+bool DebuggerScript::getLineCount(JSContext* cx, unsigned argc, Value* vp) {
   THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get lineCount)", args, obj,
                             referent);
   DebuggerScriptGetLineCountMatcher matcher(cx);
   if (!referent.match(matcher)) {
     return false;
   }
   args.rval().setNumber(matcher.totalLines);
   return true;
@@ -6291,57 +6313,59 @@ class DebuggerScriptGetSourceMatcher {
     RootedScriptSourceObject source(cx_, &lazyScript->sourceObject());
     return dbg_->wrapSource(cx_, source);
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
     return dbg_->wrapWasmSource(cx_, wasmInstance);
   }
 };
 
-static bool DebuggerScript_getSource(JSContext* cx, unsigned argc, Value* vp) {
+/* 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);
 
   DebuggerScriptGetSourceMatcher matcher(cx, dbg);
   RootedObject sourceObject(cx, referent.match(matcher));
   if (!sourceObject) {
     return false;
   }
 
   args.rval().setObject(*sourceObject);
   return true;
 }
 
-static bool DebuggerScript_getSourceStart(JSContext* cx, unsigned argc,
-                                          Value* vp) {
+/* 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(
       CallScriptMethod(obj, &JSScript::sourceStart, &LazyScript::sourceStart)));
   return true;
 }
 
-static bool DebuggerScript_getSourceLength(JSContext* cx, unsigned argc,
-                                           Value* vp) {
+/* 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(CallScriptMethod(obj, &JSScript::sourceLength,
                                                   &LazyScript::sourceLength)));
   return true;
 }
 
-static bool DebuggerScript_getMainOffset(JSContext* cx, unsigned argc,
-                                         Value* vp) {
+/* 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;
 }
 
-static bool DebuggerScript_getGlobal(JSContext* cx, unsigned argc, Value* vp) {
+/* 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;
   }
@@ -6358,25 +6382,26 @@ class DebuggerScriptGetFormatMatcher {
   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;
   }
 };
 
-static bool DebuggerScript_getFormat(JSContext* cx, unsigned argc, Value* vp) {
+/* static */
+bool DebuggerScript::getFormat(JSContext* cx, unsigned argc, Value* vp) {
   THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "(get format)", args, obj, referent);
   DebuggerScriptGetFormatMatcher matcher(cx->names());
   args.rval().setString(referent.match(matcher));
   return true;
 }
 
-static bool DebuggerScript_getChildScripts(JSContext* cx, unsigned argc,
-                                           Value* vp) {
+/* static */
+bool DebuggerScript::getChildScripts(JSContext* cx, unsigned argc, Value* vp) {
   THIS_DEBUGSCRIPT_SCRIPT_DELAZIFY(cx, argc, vp, "getChildScripts", args, obj,
                                    script);
   Debugger* dbg = Debugger::fromChildJSObject(obj);
 
   RootedObject result(cx, NewDenseEmptyArray(cx));
   if (!result) {
     return false;
   }
@@ -6743,18 +6768,19 @@ class DebuggerScriptGetPossibleBreakpoin
       if (!maybeAppendEntry(offset, lineno, column, true)) {
         return false;
       }
     }
     return true;
   }
 };
 
-static bool DebuggerScript_getPossibleBreakpoints(JSContext* cx, unsigned argc,
-                                                  Value* vp) {
+/* static */
+bool DebuggerScript::getPossibleBreakpoints(JSContext* cx, unsigned argc,
+                                            Value* vp) {
   THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getPossibleBreakpoints", args, obj,
                             referent);
 
   RootedObject result(cx);
   DebuggerScriptGetPossibleBreakpointsMatcher<false> matcher(cx, &result);
   if (args.length() >= 1 && !args[0].isUndefined()) {
     RootedObject queryObject(cx, RequireObject(cx, args[0]));
     if (!queryObject || !matcher.parseQuery(queryObject)) {
@@ -6764,19 +6790,19 @@ static bool DebuggerScript_getPossibleBr
   if (!referent.match(matcher)) {
     return false;
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-static bool DebuggerScript_getPossibleBreakpointOffsets(JSContext* cx,
-                                                        unsigned argc,
-                                                        Value* vp) {
+/* static */
+bool DebuggerScript::getPossibleBreakpointOffsets(JSContext* cx, unsigned argc,
+                                                  Value* vp) {
   THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getPossibleBreakpointOffsets", args,
                             obj, referent);
 
   RootedObject result(cx);
   DebuggerScriptGetPossibleBreakpointsMatcher<true> matcher(cx, &result);
   if (args.length() >= 1 && !args[0].isUndefined()) {
     RootedObject queryObject(cx, RequireObject(cx, args[0]));
     if (!queryObject || !matcher.parseQuery(queryObject)) {
@@ -6885,18 +6911,19 @@ class DebuggerScriptGetOffsetMetadataMat
     if (!DefineDataProperty(cx_, result_, cx_->names().isStepStart, value)) {
       return false;
     }
 
     return true;
   }
 };
 
-static bool DebuggerScript_getOffsetMetadata(JSContext* cx, unsigned argc,
-                                             Value* vp) {
+/* 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;
@@ -7197,18 +7224,19 @@ class DebuggerScriptGetOffsetLocationMat
     if (!DefineDataProperty(cx_, result_, cx_->names().isEntryPoint, value)) {
       return false;
     }
 
     return true;
   }
 };
 
-static bool DebuggerScript_getOffsetLocation(JSContext* cx, unsigned argc,
-                                             Value* vp) {
+/* 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;
@@ -7279,18 +7307,21 @@ class DebuggerScriptGetSuccessorOrPredec
 
   ReturnType match(Handle<WasmInstanceObject*> instance) {
     JS_ReportErrorASCII(
         cx_, "getSuccessorOrPredecessorOffsets NYI on wasm instances");
     return false;
   }
 };
 
-static bool DebuggerScript_getSuccessorOrPredecessorOffsets(
-    JSContext* cx, unsigned argc, Value* vp, const char* name, bool successor) {
+/* 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;
@@ -7302,30 +7333,32 @@ static bool DebuggerScript_getSuccessorO
   if (!referent.match(matcher)) {
     return false;
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-static bool DebuggerScript_getSuccessorOffsets(JSContext* cx, unsigned argc,
-                                               Value* vp) {
-  return DebuggerScript_getSuccessorOrPredecessorOffsets(
+/* static */
+bool DebuggerScript::getSuccessorOffsets(JSContext* cx, unsigned argc,
+                                         Value* vp) {
+  return DebuggerScript::getSuccessorOrPredecessorOffsets(
       cx, argc, vp, "getSuccessorOffsets", true);
 }
 
-static bool DebuggerScript_getPredecessorOffsets(JSContext* cx, unsigned argc,
-                                                 Value* vp) {
-  return DebuggerScript_getSuccessorOrPredecessorOffsets(
+/* static */
+bool DebuggerScript::getPredecessorOffsets(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  return DebuggerScript::getSuccessorOrPredecessorOffsets(
       cx, argc, vp, "getPredecessorOffsets", false);
 }
 
-static bool DebuggerScript_getAllOffsets(JSContext* cx, unsigned argc,
-                                         Value* vp) {
+/* 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;
@@ -7484,18 +7517,19 @@ class DebuggerScriptGetAllColumnOffsetsM
       if (!appendColumnOffsetEntry(lineno, column, offset)) {
         return false;
       }
     }
     return true;
   }
 };
 
-static bool DebuggerScript_getAllColumnOffsets(JSContext* cx, unsigned argc,
-                                               Value* vp) {
+/* static */
+bool DebuggerScript::getAllColumnOffsets(JSContext* cx, unsigned argc,
+                                         Value* vp) {
   THIS_DEBUGSCRIPT_REFERENT(cx, argc, vp, "getAllColumnOffsets", args, obj,
                             referent);
 
   RootedObject result(cx);
   DebuggerScriptGetAllColumnOffsetsMatcher matcher(cx, &result);
   if (!referent.match(matcher)) {
     return false;
   }
@@ -7571,18 +7605,18 @@ class DebuggerScriptGetLineOffsetsMatche
       if (!NewbornArrayPush(cx_, result_, NumberValue(offsets[i]))) {
         return false;
       }
     }
     return true;
   }
 };
 
-static bool DebuggerScript_getLineOffsets(JSContext* cx, unsigned argc,
-                                          Value* vp) {
+/* 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]);
@@ -7908,18 +7942,18 @@ struct DebuggerScriptSetBreakpointMatche
       return true;
     }
     site->dec(cx_->runtime()->defaultFreeOp());
     site->destroyIfEmpty(cx_->runtime()->defaultFreeOp());
     return false;
   }
 };
 
-static bool DebuggerScript_setBreakpoint(JSContext* cx, unsigned argc,
-                                         Value* vp) {
+/* 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)) {
@@ -7934,18 +7968,18 @@ static bool DebuggerScript_setBreakpoint
   DebuggerScriptSetBreakpointMatcher matcher(cx, dbg, offset, handler);
   if (!referent.match(matcher)) {
     return false;
   }
   args.rval().setUndefined();
   return true;
 }
 
-static bool DebuggerScript_getBreakpoints(JSContext* cx, unsigned argc,
-                                          Value* vp) {
+/* 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) ||
@@ -8010,18 +8044,18 @@ class DebuggerScriptClearBreakpointMatch
       return true;
     }
     instance.debug().clearBreakpointsIn(cx_->runtime()->defaultFreeOp(),
                                         instanceObj, dbg_, handler_);
     return true;
   }
 };
 
-static bool DebuggerScript_clearBreakpoint(JSContext* cx, unsigned argc,
-                                           Value* vp) {
+/* 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]);
@@ -8033,18 +8067,19 @@ static bool DebuggerScript_clearBreakpoi
   if (!referent.match(matcher)) {
     return false;
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-static bool DebuggerScript_clearAllBreakpoints(JSContext* cx, unsigned argc,
-                                               Value* vp) {
+/* 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);
   DebuggerScriptClearBreakpointMatcher matcher(cx, dbg, nullptr);
   if (!referent.match(matcher)) {
     return false;
   }
   args.rval().setUndefined();
@@ -8087,18 +8122,18 @@ class DebuggerScriptIsInCatchScopeMatche
     return match(script);
   }
   ReturnType match(Handle<WasmInstanceObject*> instance) {
     isInCatch_ = false;
     return true;
   }
 };
 
-static bool DebuggerScript_isInCatchScope(JSContext* cx, unsigned argc,
-                                          Value* vp) {
+/* 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)) {
@@ -8108,18 +8143,19 @@ static bool DebuggerScript_isInCatchScop
   DebuggerScriptIsInCatchScopeMatcher matcher(cx, offset);
   if (!referent.match(matcher)) {
     return false;
   }
   args.rval().setBoolean(matcher.isInCatch());
   return true;
 }
 
-static bool DebuggerScript_getOffsetsCoverage(JSContext* cx, unsigned argc,
-                                              Value* vp) {
+/* 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;
@@ -8192,62 +8228,60 @@ static bool DebuggerScript_getOffsetsCov
       hits -= counts->numExec();
     }
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
-static bool DebuggerScript_construct(JSContext* cx, unsigned argc, Value* vp) {
+/* static */
+bool DebuggerScript::construct(JSContext* cx, unsigned argc, Value* vp) {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                             "Debugger.Script");
   return false;
 }
 
-static const JSPropertySpec DebuggerScript_properties[] = {
-    JS_PSG("isGeneratorFunction", DebuggerScript_getIsGeneratorFunction, 0),
-    JS_PSG("isAsyncFunction", DebuggerScript_getIsAsyncFunction, 0),
-    JS_PSG("isModule", DebuggerScript_getIsModule, 0),
-    JS_PSG("displayName", DebuggerScript_getDisplayName, 0),
-    JS_PSG("url", DebuggerScript_getUrl, 0),
-    JS_PSG("startLine", DebuggerScript_getStartLine, 0),
-    JS_PSG("lineCount", DebuggerScript_getLineCount, 0),
-    JS_PSG("source", DebuggerScript_getSource, 0),
-    JS_PSG("sourceStart", DebuggerScript_getSourceStart, 0),
-    JS_PSG("sourceLength", DebuggerScript_getSourceLength, 0),
-    JS_PSG("mainOffset", DebuggerScript_getMainOffset, 0),
-    JS_PSG("global", DebuggerScript_getGlobal, 0),
-    JS_PSG("format", DebuggerScript_getFormat, 0),
+const JSPropertySpec DebuggerScript::properties_[] = {
+    JS_PSG("isGeneratorFunction", getIsGeneratorFunction, 0),
+    JS_PSG("isAsyncFunction", getIsAsyncFunction, 0),
+    JS_PSG("isModule", getIsModule, 0),
+    JS_PSG("displayName", getDisplayName, 0),
+    JS_PSG("url", getUrl, 0),
+    JS_PSG("startLine", getStartLine, 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};
 
-static const JSFunctionSpec DebuggerScript_methods[] = {
-    JS_FN("getChildScripts", DebuggerScript_getChildScripts, 0, 0),
-    JS_FN("getPossibleBreakpoints", DebuggerScript_getPossibleBreakpoints, 0,
-          0),
-    JS_FN("getPossibleBreakpointOffsets",
-          DebuggerScript_getPossibleBreakpointOffsets, 0, 0),
-    JS_FN("setBreakpoint", DebuggerScript_setBreakpoint, 2, 0),
-    JS_FN("getBreakpoints", DebuggerScript_getBreakpoints, 1, 0),
-    JS_FN("clearBreakpoint", DebuggerScript_clearBreakpoint, 1, 0),
-    JS_FN("clearAllBreakpoints", DebuggerScript_clearAllBreakpoints, 0, 0),
-    JS_FN("isInCatchScope", DebuggerScript_isInCatchScope, 1, 0),
-    JS_FN("getOffsetMetadata", DebuggerScript_getOffsetMetadata, 1, 0),
-    JS_FN("getOffsetsCoverage", DebuggerScript_getOffsetsCoverage, 0, 0),
-    JS_FN("getSuccessorOffsets", DebuggerScript_getSuccessorOffsets, 1, 0),
-    JS_FN("getPredecessorOffsets", DebuggerScript_getPredecessorOffsets, 1, 0),
+const JSFunctionSpec DebuggerScript::methods_[] = {
+    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),
 
     // The following APIs are deprecated due to their reliance on the
     // under-defined 'entrypoint' concept. Make use of getPossibleBreakpoints,
     // getPossibleBreakpointOffsets, or getOffsetMetadata instead.
-    JS_FN("getAllOffsets", DebuggerScript_getAllOffsets, 0, 0),
-    JS_FN("getAllColumnOffsets", DebuggerScript_getAllColumnOffsets, 0, 0),
-    JS_FN("getLineOffsets", DebuggerScript_getLineOffsets, 1, 0),
-    JS_FN("getOffsetLocation", DebuggerScript_getOffsetLocation, 0, 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};
 
 /*** Debugger.Source ********************************************************/
 
 // For internal use only.
 static inline NativeObject* GetSourceReferentRawObject(JSObject* obj) {
   MOZ_ASSERT(obj->getClass() == &DebuggerSource_class);
   return static_cast<NativeObject*>(obj->as<NativeObject>().getPrivate());
 }
@@ -8308,26 +8342,27 @@ NativeObject* Debugger::newDebuggerSourc
 JSObject* Debugger::wrapVariantReferent(
     JSContext* cx, Handle<DebuggerSourceReferent> referent) {
   JSObject* obj;
   if (referent.is<ScriptSourceObject*>()) {
     Handle<ScriptSourceObject*> untaggedReferent =
         referent.template as<ScriptSourceObject*>();
     Rooted<CrossCompartmentKey> key(
         cx, CrossCompartmentKey::DebuggeeSource(object, untaggedReferent));
-    obj = wrapVariantReferent<DebuggerSourceReferent, ScriptSourceObject*,
-                              SourceWeakMap>(cx, sources, key, referent);
+    obj = wrapVariantReferent<NativeObject, DebuggerSourceReferent,
+                              ScriptSourceObject*, SourceWeakMap>(
+        cx, sources, key, referent);
   } else {
     Handle<WasmInstanceObject*> untaggedReferent =
         referent.template as<WasmInstanceObject*>();
     Rooted<CrossCompartmentKey> key(
         cx, CrossCompartmentKey::DebuggeeSource(object, untaggedReferent));
-    obj = wrapVariantReferent<DebuggerSourceReferent, WasmInstanceObject*,
-                              WasmInstanceWeakMap>(cx, wasmInstanceSources, key,
-                                                   referent);
+    obj = wrapVariantReferent<NativeObject, DebuggerSourceReferent,
+                              WasmInstanceObject*, WasmInstanceWeakMap>(
+        cx, wasmInstanceSources, key, referent);
   }
   MOZ_ASSERT_IF(obj, GetSourceReferent(obj) == referent);
   return obj;
 }
 
 JSObject* Debugger::wrapSource(JSContext* cx, HandleScriptSourceObject source) {
   Rooted<DebuggerSourceReferent> referent(cx, source.get());
   return wrapVariantReferent(cx, referent);
@@ -8918,19 +8953,17 @@ extern JS_PUBLIC_API bool JS_DefineDebug
     return false;
   }
 
   frameProto = DebuggerFrame::initClass(cx, global, debugCtor);
   if (!frameProto) {
     return false;
   }
 
-  scriptProto = InitClass(
-      cx, debugCtor, nullptr, &DebuggerScript_class, DebuggerScript_construct,
-      0, DebuggerScript_properties, DebuggerScript_methods, nullptr, nullptr);
+  scriptProto = DebuggerScript::initClass(cx, global, debugCtor);
   if (!scriptProto) {
     return false;
   }
 
   sourceProto = InitClass(
       cx, debugCtor, nullptr, &DebuggerSource_class, DebuggerSource_construct,
       0, DebuggerSource_properties, DebuggerSource_methods, nullptr, nullptr);
   if (!sourceProto) {
@@ -9012,18 +9045,18 @@ JS_PUBLIC_API bool JS::dbg::GetDebuggeeG
 #ifdef DEBUG
 /* static */
 bool Debugger::isDebuggerCrossCompartmentEdge(JSObject* obj,
                                               const gc::Cell* target) {
   MOZ_ASSERT(target);
 
   auto cls = obj->getClass();
   const gc::Cell* referent = nullptr;
-  if (cls == &DebuggerScript_class) {
-    referent = GetScriptReferentCell(obj);
+  if (obj->is<DebuggerScript>()) {
+    referent = obj->as<DebuggerScript>().getReferentCell();
   } else if (cls == &DebuggerSource_class) {
     referent = GetSourceReferentRawObject(obj);
   } else if (obj->is<DebuggerObject>()) {
     referent = static_cast<gc::Cell*>(obj->as<DebuggerObject>().getPrivate());
   } else if (obj->is<DebuggerEnvironment>()) {
     referent =
         static_cast<gc::Cell*>(obj->as<DebuggerEnvironment>().getPrivate());
   }
--- a/js/src/dbg/Debugger.h
+++ b/js/src/dbg/Debugger.h
@@ -412,29 +412,46 @@ class MOZ_RAII EvalOptions {
 /*
  * Env is the type of what ES5 calls "lexical environments" (runtime activations
  * of lexical scopes). This is currently just JSObject, and is implemented by
  * CallObject, LexicalEnvironmentObject, and WithEnvironmentObject, among
  * others--but environments and objects are really two different concepts.
  */
 typedef JSObject Env;
 
-// One of a real JSScript, a real LazyScript, or synthesized.
+// The referent of a Debugger.Script.
 //
-// If synthesized, the referent is one of the following:
+// - For most scripts, we point at their LazyScript, because that address
+//   doesn't change as the script is lazified/delazified.
+//
+// - For scripts that cannot be lazified, and thus have no LazyScript, we point
+//   directly to their JSScript.
 //
-//   1. A WasmInstanceObject, denoting a synthesized toplevel wasm module
-//      script.
-//   2. A wasm JSFunction, denoting a synthesized wasm function script.
-//      NYI!
+// - For Web Assembly instances for which we are presenting a script-like
+//   interface, we point at their WasmInstanceObject.
+//
+// The DebuggerScript object itself simply stores a Cell* in its private
+// pointer, but when we're working with that pointer in C++ code, we'd rather
+// not pass around a Cell* and be constantly asserting that, yes, this really
+// does point to something okay. Instead, we immediately build an instance of
+// this type from the Cell* and use that instead, so we can benefit from
+// Variant's static checks.
 typedef mozilla::Variant<JSScript*, LazyScript*, WasmInstanceObject*>
     DebuggerScriptReferent;
 
-// Either a ScriptSourceObject, for ordinary JS, or a WasmInstanceObject,
-// denoting the synthesized source of a wasm module.
+// The referent of a Debugger.Source.
+//
+// - For most sources, this is a ScriptSourceObject.
+//
+// - For Web Assembly instances for which we are presenting a source-like
+//   interface, we point at their WasmInstanceObject.
+//
+// The DebuggerSource object actually simply stores a Cell* in its private
+// pointer. See the comments for DebuggerScriptReferent for the rationale for
+// this type.
 typedef mozilla::Variant<ScriptSourceObject*, WasmInstanceObject*>
     DebuggerSourceReferent;
 
 class Debugger : private mozilla::LinkedListElement<Debugger> {
   friend class Breakpoint;
   friend class DebuggerFrame;
   friend class DebuggerMemory;
   friend struct JSRuntime::GlobalObjectWatchersLinkAccess<Debugger>;
@@ -927,17 +944,17 @@ class Debugger : private mozilla::Linked
   using DebuggerFrameVector = GCVector<DebuggerFrame*>;
   static MOZ_MUST_USE bool getDebuggerFrames(
       AbstractFramePtr frame, MutableHandle<DebuggerFrameVector> frames);
 
  public:
   static MOZ_MUST_USE bool ensureExecutionObservabilityOfOsrFrame(
       JSContext* cx, AbstractFramePtr osrSourceFrame);
 
-  // Public for DebuggerScript_setBreakpoint.
+  // Public for DebuggerScript::setBreakpoint.
   static MOZ_MUST_USE bool ensureExecutionObservabilityOfScript(
       JSContext* cx, JSScript* script);
 
   // Whether the Debugger instance needs to observe all non-AOT JS
   // execution of its debugees.
   IsObserving observesAllExecution() const;
 
   // Whether the Debugger instance needs to observe AOT-compiled asm.js
@@ -999,48 +1016,49 @@ class Debugger : private mozilla::Linked
   ResumeMode fireDebuggerStatement(JSContext* cx, MutableHandleValue vp);
   ResumeMode fireExceptionUnwind(JSContext* cx, MutableHandleValue vp);
   ResumeMode fireEnterFrame(JSContext* cx, MutableHandleValue vp);
   ResumeMode fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global,
                                  MutableHandleValue vp);
   ResumeMode firePromiseHook(JSContext* cx, Hook hook, HandleObject promise,
                              MutableHandleValue vp);
 
-  NativeObject* newVariantWrapper(JSContext* cx,
-                                  Handle<DebuggerScriptReferent> referent) {
+  DebuggerScript* newVariantWrapper(JSContext* cx,
+                                    Handle<DebuggerScriptReferent> referent) {
     return newDebuggerScript(cx, referent);
   }
   NativeObject* newVariantWrapper(JSContext* cx,
                                   Handle<DebuggerSourceReferent> referent) {
     return newDebuggerSource(cx, referent);
   }
 
   /*
    * Helper function to help wrap Debugger objects whose referents may be
    * variants. Currently Debugger.Script and Debugger.Source referents may
    * be variants.
    *
    * Prefer using wrapScript, wrapWasmScript, wrapSource, and wrapWasmSource
    * whenever possible.
    */
-  template <typename ReferentVariant, typename Referent, typename Map>
-  JSObject* wrapVariantReferent(JSContext* cx, Map& map,
-                                Handle<CrossCompartmentKey> key,
-                                Handle<ReferentVariant> referent);
-  JSObject* wrapVariantReferent(JSContext* cx,
-                                Handle<DebuggerScriptReferent> referent);
+  template <typename Wrapper, typename ReferentVariant, typename Referent,
+            typename Map>
+  Wrapper* wrapVariantReferent(JSContext* cx, Map& map,
+                               Handle<CrossCompartmentKey> key,
+                               Handle<ReferentVariant> referent);
+  DebuggerScript* wrapVariantReferent(JSContext* cx,
+                                      Handle<DebuggerScriptReferent> referent);
   JSObject* wrapVariantReferent(JSContext* cx,
                                 Handle<DebuggerSourceReferent> referent);
 
   /*
    * Allocate and initialize a Debugger.Script instance whose referent is
    * |referent|.
    */
-  NativeObject* newDebuggerScript(JSContext* cx,
-                                  Handle<DebuggerScriptReferent> referent);
+  DebuggerScript* newDebuggerScript(JSContext* cx,
+                                    Handle<DebuggerScriptReferent> referent);
 
   /*
    * Allocate and initialize a Debugger.Source instance whose referent is
    * |referent|.
    */
   NativeObject* newDebuggerSource(JSContext* cx,
                                   Handle<DebuggerSourceReferent> referent);
 
@@ -1314,28 +1332,28 @@ class Debugger : private mozilla::Linked
   MOZ_MUST_USE bool getFrame(JSContext* cx, const FrameIter& iter,
                              MutableHandleDebuggerFrame result);
 
   /*
    * Return the Debugger.Script object for |script|, or create a new one if
    * needed. The context |cx| must be in the debugger realm; |script| must be
    * a script in a debuggee realm.
    */
-  JSObject* wrapScript(JSContext* cx, HandleScript script);
+  DebuggerScript* wrapScript(JSContext* cx, HandleScript script);
 
-  JSObject* wrapLazyScript(JSContext* cx, Handle<LazyScript*> script);
+  DebuggerScript* wrapLazyScript(JSContext* cx, Handle<LazyScript*> script);
 
   /*
    * Return the Debugger.Script object for |wasmInstance| (the toplevel
    * script), synthesizing a new one if needed. The context |cx| must be in
    * the debugger compartment; |wasmInstance| must be a WasmInstanceObject in
    * the debuggee realm.
    */
-  JSObject* wrapWasmScript(JSContext* cx,
-                           Handle<WasmInstanceObject*> wasmInstance);
+  DebuggerScript* wrapWasmScript(JSContext* cx,
+                                 Handle<WasmInstanceObject*> wasmInstance);
 
   /*
    * Return the Debugger.Source object for |source|, or create a new one if
    * needed. The context |cx| must be in the debugger compartment; |source|
    * must be a script source object in a debuggee realm.
    */
   JSObject* wrapSource(JSContext* cx, js::HandleScriptSourceObject source);
 
@@ -1348,16 +1366,88 @@ class Debugger : private mozilla::Linked
   JSObject* wrapWasmSource(JSContext* cx,
                            Handle<WasmInstanceObject*> wasmInstance);
 
  private:
   Debugger(const Debugger&) = delete;
   Debugger& operator=(const Debugger&) = delete;
 };
 
+class DebuggerScript : public NativeObject {
+ public:
+  static const Class class_;
+
+  enum {
+    OWNER_SLOT,
+    RESERVED_SLOTS,
+  };
+
+  static NativeObject* initClass(JSContext* cx, Handle<GlobalObject*> global,
+                                 HandleObject debugCtor);
+  static DebuggerScript* create(JSContext* cx, HandleObject proto,
+                                Handle<DebuggerScriptReferent> referent,
+                                HandleNativeObject debugger);
+
+  static void trace(JSTracer* trc, JSObject* obj);
+
+  inline gc::Cell* getReferentCell() const;
+  inline DebuggerScriptReferent getReferent() const;
+
+  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 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 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 construct(JSContext* cx, unsigned argc, Value* vp);
+
+  template <typename T>
+  static bool getUrlImpl(JSContext* cx, CallArgs& args, Handle<T*> script);
+
+  static bool getSuccessorOrPredecessorOffsets(JSContext* cx, unsigned argc,
+                                               Value* vp, const char* name,
+                                               bool successor);
+
+ private:
+  static const ClassOps classOps_;
+
+  static const JSPropertySpec properties_[];
+  static const JSFunctionSpec methods_[];
+};
+
 /*
  * A Handler represents a reference to a handler function. These handler
  * functions are called by the Debugger API to notify the user of certain
  * events. For each event type, we define a separate subclass of Handler. This
  * allows users to define a single reference to an object that implements
  * multiple handlers, by inheriting from the appropriate subclasses.
  *
  * A Handler can be stored on a reflection object, in which case the reflection
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -24,16 +24,17 @@ class PlainObject;
 class ScriptSourceObject;
 class SavedFrame;
 class Shape;
 class ObjectGroup;
 class DebuggerArguments;
 class DebuggerEnvironment;
 class DebuggerFrame;
 class DebuggerObject;
+class DebuggerScript;
 class Scope;
 class ModuleObject;
 
 // These are internal counterparts to the public types such as HandleObject.
 
 typedef JS::Handle<NativeObject*> HandleNativeObject;
 typedef JS::Handle<Shape*> HandleShape;
 typedef JS::Handle<ObjectGroup*> HandleObjectGroup;
@@ -43,29 +44,31 @@ typedef JS::Handle<PropertyName*> Handle
 typedef JS::Handle<ArrayObject*> HandleArrayObject;
 typedef JS::Handle<PlainObject*> HandlePlainObject;
 typedef JS::Handle<SavedFrame*> HandleSavedFrame;
 typedef JS::Handle<ScriptSourceObject*> HandleScriptSourceObject;
 typedef JS::Handle<DebuggerArguments*> HandleDebuggerArguments;
 typedef JS::Handle<DebuggerEnvironment*> HandleDebuggerEnvironment;
 typedef JS::Handle<DebuggerFrame*> HandleDebuggerFrame;
 typedef JS::Handle<DebuggerObject*> HandleDebuggerObject;
+typedef JS::Handle<DebuggerScript*> HandleDebuggerScript;
 typedef JS::Handle<Scope*> HandleScope;
 typedef JS::Handle<ModuleObject*> HandleModuleObject;
 
 typedef JS::MutableHandle<Shape*> MutableHandleShape;
 typedef JS::MutableHandle<JSAtom*> MutableHandleAtom;
 typedef JS::MutableHandle<NativeObject*> MutableHandleNativeObject;
 typedef JS::MutableHandle<PlainObject*> MutableHandlePlainObject;
 typedef JS::MutableHandle<SavedFrame*> MutableHandleSavedFrame;
 typedef JS::MutableHandle<DebuggerArguments*> MutableHandleDebuggerArguments;
 typedef JS::MutableHandle<DebuggerEnvironment*>
     MutableHandleDebuggerEnvironment;
 typedef JS::MutableHandle<DebuggerFrame*> MutableHandleDebuggerFrame;
 typedef JS::MutableHandle<DebuggerObject*> MutableHandleDebuggerObject;
+typedef JS::MutableHandle<DebuggerScript*> MutableHandleDebuggerScript;
 typedef JS::MutableHandle<Scope*> MutableHandleScope;
 typedef JS::MutableHandle<ModuleObject*> MutableHandleModuleObject;
 
 typedef JS::Rooted<NativeObject*> RootedNativeObject;
 typedef JS::Rooted<Shape*> RootedShape;
 typedef JS::Rooted<ObjectGroup*> RootedObjectGroup;
 typedef JS::Rooted<JSAtom*> RootedAtom;
 typedef JS::Rooted<JSLinearString*> RootedLinearString;
@@ -74,16 +77,17 @@ typedef JS::Rooted<ArrayObject*> RootedA
 typedef JS::Rooted<GlobalObject*> RootedGlobalObject;
 typedef JS::Rooted<PlainObject*> RootedPlainObject;
 typedef JS::Rooted<SavedFrame*> RootedSavedFrame;
 typedef JS::Rooted<ScriptSourceObject*> RootedScriptSourceObject;
 typedef JS::Rooted<DebuggerArguments*> RootedDebuggerArguments;
 typedef JS::Rooted<DebuggerEnvironment*> RootedDebuggerEnvironment;
 typedef JS::Rooted<DebuggerFrame*> RootedDebuggerFrame;
 typedef JS::Rooted<DebuggerObject*> RootedDebuggerObject;
+typedef JS::Rooted<DebuggerScript*> RootedDebuggerScript;
 typedef JS::Rooted<Scope*> RootedScope;
 typedef JS::Rooted<ModuleObject*> RootedModuleObject;
 
 typedef JS::GCVector<JSFunction*> FunctionVector;
 typedef JS::GCVector<PropertyName*> PropertyNameVector;
 typedef JS::GCVector<Shape*> ShapeVector;
 typedef JS::GCVector<JSString*> StringVector;