Bug 1564170: Give Debugger.Source instances their own NativeObject subclass, DebuggerSource. r=jorendorff
authorJim Blandy <jimb@mozilla.com>
Thu, 08 Aug 2019 23:37:54 +0000
changeset 487234 5fa9803f607068f661cb8fce513829366d99634a
parent 487233 528db640f50f3db58a2fa432fc1ee8a2eeeb6154
child 487235 2c345c044ca1ccdfccbb43ff2edfc5e8cef45554
push id36415
push userncsoregi@mozilla.com
push dateSat, 10 Aug 2019 21:28:45 +0000
treeherdermozilla-central@462d2d4ba051 [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.Source instances their own NativeObject subclass, DebuggerSource. r=jorendorff Differential Revision: https://phabricator.services.mozilla.com/D38268
js/src/debugger/Debugger.cpp
js/src/debugger/Debugger.h
js/src/gc/Rooting.h
--- a/js/src/debugger/Debugger.cpp
+++ b/js/src/debugger/Debugger.cpp
@@ -83,40 +83,31 @@ 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 DebuggerSource_trace(JSTracer* trc, JSObject* obj);
-
-enum {
-  JSSLOT_DEBUGSOURCE_OWNER,
-  JSSLOT_DEBUGSOURCE_TEXT,
-  JSSLOT_DEBUGSOURCE_COUNT
-};
-
-static const ClassOps DebuggerSource_classOps = {nullptr, /* addProperty */
-                                                 nullptr, /* delProperty */
-                                                 nullptr, /* enumerate   */
-                                                 nullptr, /* newEnumerate */
-                                                 nullptr, /* resolve     */
-                                                 nullptr, /* mayResolve  */
-                                                 nullptr, /* finalize    */
-                                                 nullptr, /* call        */
-                                                 nullptr, /* hasInstance */
-                                                 nullptr, /* construct   */
-                                                 DebuggerSource_trace};
-
-static const Class DebuggerSource_class = {
-    "Source",
-    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),
-    &DebuggerSource_classOps};
+const ClassOps DebuggerSource::classOps_ = {nullptr, /* addProperty */
+                                            nullptr, /* delProperty */
+                                            nullptr, /* enumerate   */
+                                            nullptr, /* newEnumerate */
+                                            nullptr, /* resolve     */
+                                            nullptr, /* mayResolve  */
+                                            nullptr, /* finalize    */
+                                            nullptr, /* call        */
+                                            nullptr, /* hasInstance */
+                                            nullptr, /* construct   */
+                                            trace};
+
+const Class DebuggerSource::class_ = {
+    "Source", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
+    &classOps_};
 
 /*** Utils ******************************************************************/
 
 bool js::IsInterpretedNonSelfHostedFunction(JSFunction* fun) {
   return fun->isInterpreted() && !fun->isSelfHostedBuiltin();
 }
 
 bool js::EnsureFunctionHasScript(JSContext* cx, HandleFunction fun) {
@@ -427,28 +418,28 @@ Debugger::~Debugger() {
           JSRuntime::WatchersList::Iterator(this)) {
     cx->runtime()->onNewGlobalObjectWatchers().remove(this);
   }
 }
 
 JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
                  unsigned(DebuggerScript::OWNER_SLOT));
 JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
-                 unsigned(JSSLOT_DEBUGSOURCE_OWNER));
+                 unsigned(DebuggerSource::OWNER_SLOT));
 JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
                  unsigned(JSSLOT_DEBUGOBJECT_OWNER));
 JS_STATIC_ASSERT(unsigned(DebuggerFrame::OWNER_SLOT) ==
                  unsigned(DebuggerEnvironment::OWNER_SLOT));
 
 #ifdef DEBUG
 /* static */
 bool Debugger::isChildJSObject(JSObject* obj) {
   return obj->getClass() == &DebuggerFrame::class_ ||
          obj->getClass() == &DebuggerScript::class_ ||
-         obj->getClass() == &DebuggerSource_class ||
+         obj->getClass() == &DebuggerSource::class_ ||
          obj->getClass() == &DebuggerObject::class_ ||
          obj->getClass() == &DebuggerEnvironment::class_;
 }
 #endif
 
 /* static */
 Debugger* Debugger::fromChildJSObject(JSObject* obj) {
   MOZ_ASSERT(isChildJSObject(obj));
@@ -3430,19 +3421,19 @@ 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);
-  sources.traceCrossCompartmentEdges<DebuggerSource_trace>(trc);
+  sources.traceCrossCompartmentEdges<DebuggerSource::trace>(trc);
   wasmInstanceScripts.traceCrossCompartmentEdges<DebuggerScript::trace>(trc);
-  wasmInstanceSources.traceCrossCompartmentEdges<DebuggerSource_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.
  *
  * However, during zone GC, we have to do something about cross-compartment
@@ -3473,18 +3464,16 @@ void DebugAPI::traceCrossCompartmentEdge
   for (Debugger* dbg : rt->debuggerList()) {
     Zone* zone = MaybeForwarded(dbg->object.get())->zone();
     if (!zone->isCollecting() || state == gc::State::Compact) {
       dbg->traceCrossCompartmentEdges(trc);
     }
   }
 }
 
-static inline DebuggerSourceReferent GetSourceReferent(JSObject* obj);
-
 #ifdef DEBUG
 
 static bool RuntimeHasDebugger(JSRuntime* rt, Debugger* dbg) {
   for (Debugger* d : rt->debuggerList()) {
     if (d == dbg) {
       return true;
     }
   }
@@ -3531,18 +3520,18 @@ bool DebugAPI::edgeIsInDebuggerWeakmap(J
           return dst.is<LazyScript>() && lazy == &dst.as<LazyScript>() &&
                  dbg->lazyScripts.hasEntry(lazy, src);
         },
         [=](WasmInstanceObject* instance) {
           return dst.is<JSObject>() && instance == &dst.as<JSObject>() &&
                  dbg->wasmInstanceScripts.hasEntry(instance, src);
         });
   }
-  if (src->getClass() == &DebuggerSource_class) {
-    return GetSourceReferent(src).match(
+  if (src->is<DebuggerSource>()) {
+    return src->as<DebuggerSource>().getReferent().match(
         [=](ScriptSourceObject* sso) {
           return dst.is<JSObject>() && sso == &dst.as<JSObject>() &&
                  dbg->sources.hasEntry(sso, src);
         },
         [=](WasmInstanceObject* instance) {
           return dst.is<JSObject>() && instance == &dst.as<JSObject>() &&
                  dbg->wasmInstanceSources.hasEntry(instance, src);
         });
@@ -4878,27 +4867,27 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
 
     // Check for a 'source' property
     RootedValue debuggerSource(cx);
     if (!GetProperty(cx, query, query, cx->names().source, &debuggerSource)) {
       return false;
     }
     if (!debuggerSource.isUndefined()) {
       if (!debuggerSource.isObject() ||
-          debuggerSource.toObject().getClass() != &DebuggerSource_class) {
+          !debuggerSource.toObject().is<DebuggerSource>()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_UNEXPECTED_TYPE,
                                   "query object's 'source' property",
                                   "not undefined nor a Debugger.Source object");
         return false;
       }
 
       Value owner =
-          debuggerSource.toObject().as<NativeObject>().getReservedSlot(
-              JSSLOT_DEBUGSOURCE_OWNER);
+          debuggerSource.toObject().as<DebuggerSource>().getReservedSlot(
+              DebuggerSource::OWNER_SLOT);
 
       // The given source must have an owner. Otherwise, it's a
       // Debugger.Source.prototype, which would match no scripts, and is
       // probably a mistake.
       if (!owner.isObject()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_DEBUG_PROTO, "Debugger.Source",
                                   "Debugger.Source");
@@ -4910,17 +4899,17 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
       // but mixing Debugger.Sources is probably a sign of confusion.
       if (&owner.toObject() != debugger->object) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_DEBUG_WRONG_OWNER, "Debugger.Source");
         return false;
       }
 
       hasSource = true;
-      source = GetSourceReferent(&debuggerSource.toObject());
+      source = debuggerSource.toObject().as<DebuggerSource>().getReferent();
     }
 
     // Check for a 'displayURL' property.
     RootedValue displayURL(cx);
     if (!GetProperty(cx, query, query, cx->names().displayURL, &displayURL)) {
       return false;
     }
     if (!displayURL.isUndefined() && !displayURL.isString()) {
@@ -5949,17 +5938,17 @@ bool Debugger::adoptDebuggeeValue(JSCont
 class DebuggerAdoptSourceMatcher {
   JSContext* cx_;
   Debugger* dbg_;
 
  public:
   explicit DebuggerAdoptSourceMatcher(JSContext* cx, Debugger* dbg)
       : cx_(cx), dbg_(dbg) {}
 
-  using ReturnType = JSObject*;
+  using ReturnType = DebuggerSource*;
 
   ReturnType match(HandleScriptSourceObject source) {
     if (source->compartment() == cx_->compartment()) {
       JS_ReportErrorASCII(cx_,
                           "Source is in the same compartment as this debugger");
       return nullptr;
     }
     return dbg_->wrapSource(cx_, source);
@@ -5969,45 +5958,43 @@ class DebuggerAdoptSourceMatcher {
       JS_ReportErrorASCII(
           cx_, "WasmInstance is in the same compartment as this debugger");
       return nullptr;
     }
     return dbg_->wrapWasmSource(cx_, wasmInstance);
   }
 };
 
-static inline NativeObject* GetSourceReferentRawObject(JSObject* obj);
-static inline DebuggerSourceReferent GetSourceReferent(JSObject* obj);
-
 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;
   }
 
   obj = UncheckedUnwrap(obj);
-  if (obj->getClass() != &DebuggerSource_class) {
+  if (!obj->is<DebuggerSource>()) {
     JS_ReportErrorASCII(cx, "Argument is not a Debugger.Source");
     return false;
   }
 
-  if (!GetSourceReferentRawObject(obj)) {
+  RootedDebuggerSource sourceObj(cx, &obj->as<DebuggerSource>());
+  if (!sourceObj->getReferentRawObject()) {
     JS_ReportErrorASCII(cx, "Argument is Debugger.Source.prototype");
     return false;
   }
 
-  Rooted<DebuggerSourceReferent> referent(cx, GetSourceReferent(obj));
+  Rooted<DebuggerSourceReferent> referent(cx, sourceObj->getReferent());
 
   DebuggerAdoptSourceMatcher matcher(cx, dbg);
-  JSObject* res = referent.match(matcher);
+  DebuggerSource* res = referent.match(matcher);
   if (!res) {
     return false;
   }
 
   args.rval().setObject(*res);
   return true;
 }
 
@@ -6383,98 +6370,103 @@ void DebugAPI::propagateForcedReturn(JSC
   // check for the special propagating-forced-return flag.
   MOZ_ASSERT(!cx->isExceptionPending());
   cx->setPropagatingForcedReturn();
   frame.setReturnValue(rval);
 }
 
 /*** Debugger.Source ********************************************************/
 
+/* static */
+NativeObject* DebuggerSource::initClass(JSContext* cx,
+                                        Handle<GlobalObject*> global,
+                                        HandleObject debugCtor) {
+  return InitClass(cx, debugCtor, nullptr, &class_, construct, 0, properties_,
+                   methods_, nullptr, nullptr);
+}
+
+/* static */
+DebuggerSource* DebuggerSource::create(JSContext* cx, HandleObject proto,
+                                       Handle<DebuggerSourceReferent> referent,
+                                       HandleNativeObject debugger) {
+  NativeObject* obj =
+      NewNativeObjectWithGivenProto(cx, &class_, proto, TenuredObject);
+  if (!obj) {
+    return nullptr;
+  }
+  RootedDebuggerSource sourceObj(cx, &obj->as<DebuggerSource>());
+  sourceObj->setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));
+  referent.get().match(
+      [&](auto sourceHandle) { sourceObj->setPrivateGCThing(sourceHandle); });
+
+  return sourceObj;
+}
+
 // For internal use only.
-static inline NativeObject* GetSourceReferentRawObject(JSObject* obj) {
-  MOZ_ASSERT(obj->getClass() == &DebuggerSource_class);
-  return static_cast<NativeObject*>(obj->as<NativeObject>().getPrivate());
-}
-
-static inline DebuggerSourceReferent GetSourceReferent(JSObject* obj) {
-  if (NativeObject* referent = GetSourceReferentRawObject(obj)) {
+NativeObject* DebuggerSource::getReferentRawObject() const {
+  return static_cast<NativeObject*>(getPrivate());
+}
+
+DebuggerSourceReferent DebuggerSource::getReferent() const {
+  if (NativeObject* referent = getReferentRawObject()) {
     if (referent->is<ScriptSourceObject>()) {
       return AsVariant(&referent->as<ScriptSourceObject>());
     }
     return AsVariant(&referent->as<WasmInstanceObject>());
   }
   return AsVariant(static_cast<ScriptSourceObject*>(nullptr));
 }
 
-void DebuggerSource_trace(JSTracer* trc, JSObject* obj) {
+/* static */
+void DebuggerSource::trace(JSTracer* trc, JSObject* obj) {
+  DebuggerSource* sourceObj = &obj->as<DebuggerSource>();
   // There is a barrier on private pointers, so the Unbarriered marking
   // is okay.
-  if (JSObject* referent = GetSourceReferentRawObject(obj)) {
-    TraceManuallyBarrieredCrossCompartmentEdge(trc, obj, &referent,
+  if (JSObject* referent = sourceObj->getReferentRawObject()) {
+    TraceManuallyBarrieredCrossCompartmentEdge(trc, sourceObj, &referent,
                                                "Debugger.Source referent");
-    obj->as<NativeObject>().setPrivateUnbarriered(referent);
-  }
-}
-
-class SetDebuggerSourcePrivateMatcher {
-  NativeObject* obj_;
-
- public:
-  explicit SetDebuggerSourcePrivateMatcher(NativeObject* obj) : obj_(obj) {}
-  using ReturnType = void;
-  ReturnType match(HandleScriptSourceObject source) {
-    obj_->setPrivateGCThing(source);
-  }
-  ReturnType match(Handle<WasmInstanceObject*> instance) {
-    obj_->setPrivateGCThing(instance);
-  }
-};
-
-NativeObject* Debugger::newDebuggerSource(
+    sourceObj->setPrivateUnbarriered(referent);
+  }
+}
+
+DebuggerSource* Debugger::newDebuggerSource(
     JSContext* cx, Handle<DebuggerSourceReferent> referent) {
   cx->check(object.get());
 
   RootedObject proto(
       cx, &object->getReservedSlot(JSSLOT_DEBUG_SOURCE_PROTO).toObject());
   MOZ_ASSERT(proto);
-  NativeObject* sourceobj = NewNativeObjectWithGivenProto(
-      cx, &DebuggerSource_class, proto, TenuredObject);
-  if (!sourceobj) {
-    return nullptr;
-  }
-  sourceobj->setReservedSlot(JSSLOT_DEBUGSOURCE_OWNER, ObjectValue(*object));
-  SetDebuggerSourcePrivateMatcher matcher(sourceobj);
-  referent.match(matcher);
-
-  return sourceobj;
-}
-
-JSObject* Debugger::wrapVariantReferent(
+  RootedNativeObject debugger(cx, object);
+  return DebuggerSource::create(cx, proto, referent, debugger);
+}
+
+DebuggerSource* Debugger::wrapVariantReferent(
     JSContext* cx, Handle<DebuggerSourceReferent> referent) {
-  JSObject* obj;
+  DebuggerSource* obj;
   if (referent.is<ScriptSourceObject*>()) {
-    obj = wrapVariantReferent<NativeObject, DebuggerSourceReferent,
+    obj = wrapVariantReferent<DebuggerSource, DebuggerSourceReferent,
                               ScriptSourceObject*, SourceWeakMap>(cx, sources,
                                                                   referent);
   } else {
-    obj = wrapVariantReferent<NativeObject, DebuggerSourceReferent,
+    obj = wrapVariantReferent<DebuggerSource, DebuggerSourceReferent,
                               WasmInstanceObject*, WasmInstanceWeakMap>(
         cx, wasmInstanceSources, referent);
   }
-  MOZ_ASSERT_IF(obj, GetSourceReferent(obj) == referent);
+  MOZ_ASSERT_IF(obj, obj->getReferent() == referent);
   return obj;
 }
 
-JSObject* Debugger::wrapSource(JSContext* cx, HandleScriptSourceObject source) {
+DebuggerSource* Debugger::wrapSource(JSContext* cx,
+                                     HandleScriptSourceObject source) {
   Rooted<DebuggerSourceReferent> referent(cx, source.get());
   return wrapVariantReferent(cx, referent);
 }
 
-JSObject* Debugger::wrapWasmSource(JSContext* cx,
-                                   Handle<WasmInstanceObject*> wasmInstance) {
+DebuggerSource* Debugger::wrapWasmSource(
+    JSContext* cx, Handle<WasmInstanceObject*> wasmInstance) {
   Rooted<DebuggerSourceReferent> referent(cx, wasmInstance.get());
   return wrapVariantReferent(cx, referent);
 }
 
 bool DebugAPI::getScriptInstrumentationId(JSContext* cx,
                                           HandleObject dbgObject,
                                           HandleScript script,
                                           MutableHandleValue rval) {
@@ -6482,79 +6474,81 @@ bool DebugAPI::getScriptInstrumentationI
   DebuggerScript* dbgScript = dbg->wrapScript(cx, script);
   if (!dbgScript) {
     return false;
   }
   rval.set(dbgScript->getInstrumentationId());
   return true;
 }
 
-static bool DebuggerSource_construct(JSContext* cx, unsigned argc, Value* vp) {
+/* static */
+bool DebuggerSource::construct(JSContext* cx, unsigned argc, Value* vp) {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                             "Debugger.Source");
   return false;
 }
 
-static NativeObject* DebuggerSource_check(JSContext* cx, HandleValue thisv,
-                                          const char* fnname) {
+/* static */
+DebuggerSource* DebuggerSource::check(JSContext* cx, HandleValue thisv,
+                                      const char* fnname) {
   JSObject* thisobj = RequireObject(cx, thisv);
   if (!thisobj) {
     return nullptr;
   }
-  if (thisobj->getClass() != &DebuggerSource_class) {
+  if (!thisobj->is<DebuggerSource>()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Source",
                               fnname, thisobj->getClass()->name);
     return nullptr;
   }
 
-  NativeObject* nthisobj = &thisobj->as<NativeObject>();
-
-  if (!GetSourceReferentRawObject(thisobj)) {
+  DebuggerSource* thisSourceObj = &thisobj->as<DebuggerSource>();
+
+  if (!thisSourceObj->getReferentRawObject()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Source",
                               fnname, "prototype object");
     return nullptr;
   }
 
-  return nthisobj;
+  return thisSourceObj;
 }
 
 template <typename ReferentT>
-static NativeObject* DebuggerSource_checkThis(JSContext* cx,
-                                              const CallArgs& args,
-                                              const char* fnname,
-                                              const char* refname) {
-  NativeObject* thisobj = DebuggerSource_check(cx, args.thisv(), fnname);
+/* 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;
   }
 
-  if (!GetSourceReferent(thisobj).is<ReferentT>()) {
+  if (!thisobj->getReferent().is<ReferentT>()) {
     ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
                      args.thisv(), nullptr, refname);
     return nullptr;
   }
 
   return thisobj;
 }
 
-#define THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, fnname, args, obj, referent)  \
-  CallArgs args = CallArgsFromVp(argc, vp);                                   \
-  RootedNativeObject obj(cx, DebuggerSource_check(cx, args.thisv(), fnname)); \
-  if (!obj) return false;                                                     \
-  Rooted<DebuggerSourceReferent> referent(cx, GetSourceReferent(obj))
+#define THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, fnname, args, obj, referent) \
+  CallArgs args = CallArgsFromVp(argc, vp);                                  \
+  RootedDebuggerSource obj(cx, 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);                                    \
-  RootedNativeObject obj(cx, DebuggerSource_checkThis<ScriptSourceObject*>(    \
-                                 cx, args, fnname, "a JS source"));            \
+  RootedDebuggerSource obj(                                                    \
+      cx, checkThis<ScriptSourceObject*>(cx, args, fnname, "a JS source"));    \
   if (!obj) return false;                                                      \
   RootedScriptSourceObject sourceObject(                                       \
-      cx, GetSourceReferent(obj).as<ScriptSourceObject*>())
+      cx, obj->getReferent().as<ScriptSourceObject*>())
 
 class DebuggerSourceGetTextMatcher {
   JSContext* cx_;
 
  public:
   explicit DebuggerSourceGetTextMatcher(JSContext* cx) : cx_(cx) {}
 
   using ReturnType = JSString*;
@@ -6583,37 +6577,39 @@ 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);
   }
 };
 
-static bool DebuggerSource_getText(JSContext* cx, unsigned argc, Value* vp) {
+/* 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(JSSLOT_DEBUGSOURCE_TEXT);
+  Value textv = obj->getReservedSlot(TEXT_SLOT);
   if (!textv.isUndefined()) {
     MOZ_ASSERT(textv.isString());
     args.rval().set(textv);
     return true;
   }
 
   DebuggerSourceGetTextMatcher matcher(cx);
   JSString* str = referent.match(matcher);
   if (!str) {
     return false;
   }
 
   args.rval().setString(str);
-  obj->setReservedSlot(JSSLOT_DEBUGSOURCE_TEXT, args.rval());
+  obj->setReservedSlot(TEXT_SLOT, args.rval());
   return true;
 }
 
-static bool DebuggerSource_getBinary(JSContext* cx, unsigned argc, Value* vp) {
+/* 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;
   }
 
@@ -6656,17 +6652,18 @@ class DebuggerSourceGetURLMatcher {
     }
     return Nothing();
   }
   ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
     return Some(instanceObj->instance().createDisplayURL(cx_));
   }
 };
 
-static bool DebuggerSource_getURL(JSContext* cx, unsigned argc, Value* vp) {
+/* 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;
     }
@@ -6683,17 +6680,18 @@ class DebuggerSourceGetIdMatcher {
 
   ReturnType match(HandleScriptSourceObject sourceObject) {
     ScriptSource* ss = sourceObject->source();
     return ss->id();
   }
   ReturnType match(Handle<WasmInstanceObject*> instanceObj) { return 0; }
 };
 
-static bool DebuggerSource_getId(JSContext* cx, unsigned argc, Value* vp) {
+/* 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;
 }
 
@@ -6704,18 +6702,18 @@ struct DebuggerSourceGetDisplayURLMatche
     MOZ_ASSERT(ss);
     return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
     return wasmInstance->instance().metadata().displayURL();
   }
 };
 
-static bool DebuggerSource_getDisplayURL(JSContext* cx, unsigned argc,
-                                         Value* vp) {
+/* 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;
     }
@@ -6729,17 +6727,18 @@ static bool DebuggerSource_getDisplayURL
 struct DebuggerSourceGetElementMatcher {
   using ReturnType = JSObject*;
   ReturnType match(HandleScriptSourceObject sourceObject) {
     return sourceObject->unwrappedElement();
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return nullptr; }
 };
 
-static bool DebuggerSource_getElement(JSContext* cx, unsigned argc, Value* vp) {
+/* 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;
     }
@@ -6754,18 +6753,19 @@ struct DebuggerSourceGetElementPropertyM
   ReturnType match(HandleScriptSourceObject sourceObject) {
     return sourceObject->unwrappedElementAttributeName();
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
     return UndefinedValue();
   }
 };
 
-static bool DebuggerSource_getElementProperty(JSContext* cx, unsigned argc,
-                                              Value* vp) {
+/* 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 {
@@ -6799,18 +6799,19 @@ class DebuggerSourceGetIntroductionScrip
     if (!ds) {
       return false;
     }
     rval_.setObject(*ds);
     return true;
   }
 };
 
-static bool DebuggerSource_getIntroductionScript(JSContext* cx, unsigned argc,
-                                                 Value* vp) {
+/* 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 {
@@ -6826,18 +6827,19 @@ struct DebuggerGetIntroductionOffsetMatc
     }
     return UndefinedValue();
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
     return UndefinedValue();
   }
 };
 
-static bool DebuggerSource_getIntroductionOffset(JSContext* cx, unsigned argc,
-                                                 Value* vp) {
+/* 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 {
@@ -6845,18 +6847,19 @@ struct DebuggerSourceGetIntroductionType
   ReturnType match(HandleScriptSourceObject sourceObject) {
     ScriptSource* ss = sourceObject->source();
     MOZ_ASSERT(ss);
     return ss->hasIntroductionType() ? ss->introductionType() : nullptr;
   }
   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return "wasm"; }
 };
 
-static bool DebuggerSource_getIntroductionType(JSContext* cx, unsigned argc,
-                                               Value* vp) {
+/* 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;
@@ -6864,18 +6867,18 @@ static bool DebuggerSource_getIntroducti
     args.rval().setString(str);
   } else {
     args.rval().setUndefined();
   }
 
   return true;
 }
 
-static bool DebuggerSource_setSourceMapURL(JSContext* cx, unsigned argc,
-                                           Value* vp) {
+/* 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;
   }
 
@@ -6933,50 +6936,49 @@ class DebuggerSourceGetSourceMapURLMatch
       return false;
     }
 
     result_.set(str);
     return true;
   }
 };
 
-static bool DebuggerSource_getSourceMapURL(JSContext* cx, unsigned argc,
-                                           Value* vp) {
+/* 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 {
     args.rval().setNull();
   }
   return true;
 }
 
-static const JSPropertySpec DebuggerSource_properties[] = {
-    JS_PSG("text", DebuggerSource_getText, 0),
-    JS_PSG("binary", DebuggerSource_getBinary, 0),
-    JS_PSG("url", DebuggerSource_getURL, 0),
-    JS_PSG("id", DebuggerSource_getId, 0),
-    JS_PSG("element", DebuggerSource_getElement, 0),
-    JS_PSG("displayURL", DebuggerSource_getDisplayURL, 0),
-    JS_PSG("introductionScript", DebuggerSource_getIntroductionScript, 0),
-    JS_PSG("introductionOffset", DebuggerSource_getIntroductionOffset, 0),
-    JS_PSG("introductionType", DebuggerSource_getIntroductionType, 0),
-    JS_PSG("elementAttributeName", DebuggerSource_getElementProperty, 0),
-    JS_PSGS("sourceMapURL", DebuggerSource_getSourceMapURL,
-            DebuggerSource_setSourceMapURL, 0),
+const JSPropertySpec DebuggerSource::properties_[] = {
+    JS_PSG("text", getText, 0),
+    JS_PSG("binary", getBinary, 0),
+    JS_PSG("url", getURL, 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};
 
-static const JSFunctionSpec DebuggerSource_methods[] = {JS_FS_END};
+const JSFunctionSpec DebuggerSource::methods_[] = {JS_FS_END};
 
 /*** JS::dbg::Builder *******************************************************/
 
 Builder::Builder(JSContext* cx, js::Debugger* debugger)
     : debuggerObject(cx, debugger->toJSObject().get()), debugger(debugger) {}
 
 #if DEBUG
 void Builder::assertBuilt(JSObject* obj) {
@@ -7071,19 +7073,17 @@ extern JS_PUBLIC_API bool JS_DefineDebug
     return false;
   }
 
   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);
+  sourceProto = DebuggerSource::initClass(cx, global, debugCtor);
   if (!sourceProto) {
     return false;
   }
 
   objectProto = DebuggerObject::initClass(cx, global, debugCtor);
   if (!objectProto) {
     return false;
   }
@@ -7156,22 +7156,21 @@ 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 (obj->is<DebuggerScript>()) {
     referent = obj->as<DebuggerScript>().getReferentCell();
-  } else if (cls == &DebuggerSource_class) {
-    referent = GetSourceReferentRawObject(obj);
+  } else if (obj->is<DebuggerSource>()) {
+    referent = obj->as<DebuggerSource>().getReferentRawObject();
   } 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());
   }
 
   return referent == target;
--- a/js/src/debugger/Debugger.h
+++ b/js/src/debugger/Debugger.h
@@ -39,16 +39,17 @@
  */
 #undef Yield
 
 namespace js {
 
 class Breakpoint;
 class DebuggerFrame;
 class DebuggerScript;
+class DebuggerSource;
 class DebuggerMemory;
 class ScriptedOnStepHandler;
 class ScriptedOnPopHandler;
 class WasmInstanceObject;
 
 /**
  * A completion value, describing how some sort of JavaScript evaluation
  * completed. This is used to tell an onPop handler what's going on with the
@@ -925,18 +926,18 @@ class Debugger : private mozilla::Linked
                                  MutableHandleValue vp);
   ResumeMode firePromiseHook(JSContext* cx, Hook hook, HandleObject promise,
                              MutableHandleValue vp);
 
   DebuggerScript* newVariantWrapper(JSContext* cx,
                                     Handle<DebuggerScriptReferent> referent) {
     return newDebuggerScript(cx, referent);
   }
-  NativeObject* newVariantWrapper(JSContext* cx,
-                                  Handle<DebuggerSourceReferent> referent) {
+  DebuggerSource* 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.
    *
@@ -944,32 +945,32 @@ class Debugger : private mozilla::Linked
    * whenever possible.
    */
   template <typename Wrapper, typename ReferentVariant, typename Referent,
             typename Map>
   Wrapper* wrapVariantReferent(JSContext* cx, Map& map,
                                Handle<ReferentVariant> referent);
   DebuggerScript* wrapVariantReferent(JSContext* cx,
                                       Handle<DebuggerScriptReferent> referent);
-  JSObject* wrapVariantReferent(JSContext* cx,
-                                Handle<DebuggerSourceReferent> referent);
+  DebuggerSource* wrapVariantReferent(JSContext* cx,
+                                      Handle<DebuggerSourceReferent> referent);
 
   /*
    * Allocate and initialize a Debugger.Script instance whose referent is
    * |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);
+  DebuggerSource* newDebuggerSource(JSContext* cx,
+                                    Handle<DebuggerSourceReferent> referent);
 
   /*
    * Receive a "new script" event from the engine. A new script was compiled
    * or deserialized.
    */
   void fireNewScript(JSContext* cx,
                      Handle<DebuggerScriptReferent> scriptReferent);
 
@@ -1123,32 +1124,82 @@ class Debugger : private mozilla::Linked
   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);
+  DebuggerSource* wrapSource(JSContext* cx,
+                             js::HandleScriptSourceObject source);
 
   /*
    * Return the Debugger.Source object for |wasmInstance| (the entire module),
    * 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* wrapWasmSource(JSContext* cx,
-                           Handle<WasmInstanceObject*> wasmInstance);
+  DebuggerSource* wrapWasmSource(JSContext* cx,
+                                 Handle<WasmInstanceObject*> wasmInstance);
 
  private:
   Debugger(const Debugger&) = delete;
   Debugger& operator=(const Debugger&) = delete;
 };
 
+class DebuggerSource : public NativeObject {
+ public:
+  static const Class class_;
+
+  enum {
+    OWNER_SLOT,
+    TEXT_SLOT,
+    RESERVED_SLOTS,
+  };
+
+  static NativeObject* initClass(JSContext* cx, Handle<GlobalObject*> global,
+                                 HandleObject debugCtor);
+  static DebuggerSource* create(JSContext* cx, HandleObject proto,
+                                Handle<DebuggerSourceReferent> referent,
+                                HandleNativeObject debugger);
+
+  static void trace(JSTracer* trc, JSObject* obj);
+
+  NativeObject* getReferentRawObject() const;
+  DebuggerSourceReferent getReferent() const;
+
+  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);
+  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 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);
+
+ private:
+  static const ClassOps classOps_;
+
+  static const JSPropertySpec properties_[];
+  static const JSFunctionSpec methods_[];
+};
+
 /*
  * A Handler represents a Debugger API reflection object's handler function,
  * like a Debugger.Frame's onStep handler. 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.
  *
  * When a reflection object accepts a Handler, it calls its 'hold' method; and
  * if the Handler is replaced by another, or the reflection object is finalized,
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -25,16 +25,17 @@ class ScriptSourceObject;
 class SavedFrame;
 class Shape;
 class ObjectGroup;
 class DebuggerArguments;
 class DebuggerEnvironment;
 class DebuggerFrame;
 class DebuggerObject;
 class DebuggerScript;
+class DebuggerSource;
 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;
@@ -45,30 +46,32 @@ typedef JS::Handle<ArrayObject*> HandleA
 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<DebuggerSource*> HandleDebuggerSource;
 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<DebuggerSource*> MutableHandleDebuggerSource;
 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;
@@ -78,16 +81,17 @@ typedef JS::Rooted<GlobalObject*> Rooted
 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<DebuggerSource*> RootedDebuggerSource;
 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;