Bug 1723715 part 12 - Move some builtin prototypes to GlobalObjectData. r=jonco
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 09 Aug 2021 15:25:08 +0000
changeset 588230 f73777bc287765b67d7780a09ae63d7b2419d0d6
parent 588229 71d833b689b6a6f4b3c6d91984f3d7e0eef6efa7
child 588231 eeb83afc429e8621acee3a911d698f84a7b8e8f2
push id38689
push userimoraru@mozilla.com
push dateMon, 09 Aug 2021 21:33:53 +0000
treeherdermozilla-central@0f323b67aa6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1723715
milestone92.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 1723715 part 12 - Move some builtin prototypes to GlobalObjectData. r=jonco Depends on D121991 Differential Revision: https://phabricator.services.mozilla.com/D121992
js/public/Class.h
js/src/builtin/MapObject.cpp
js/src/builtin/ModuleObject.cpp
js/src/vm/AsyncIteration.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/Iteration.cpp
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -567,17 +567,17 @@ static const uint32_t JSCLASS_FOREGROUND
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
 // previously allowed, but is now an ES5 violation and thus unsupported.
 //
 // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
 // the beginning of every global object's slots for use by the
 // application.
 static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
 static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
-    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 20;
+    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 2;
 
 static constexpr uint32_t JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(uint32_t n) {
   return JSCLASS_IS_GLOBAL |
          JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + n);
 }
 
 static constexpr uint32_t JSCLASS_GLOBAL_FLAGS =
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0);
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -178,17 +178,17 @@ bool GlobalObject::initMapIteratorProto(
       cx, GlobalObject::createBlankPrototypeInheriting<PlainObject>(cx, base));
   if (!proto) {
     return false;
   }
   if (!JS_DefineFunctions(cx, proto, MapIteratorObject::methods) ||
       !DefineToStringTag(cx, proto, cx->names().MapIterator)) {
     return false;
   }
-  global->setReservedSlot(MAP_ITERATOR_PROTO, ObjectValue(*proto));
+  global->initBuiltinProto(ProtoKind::MapIteratorProto, proto);
   return true;
 }
 
 template <typename TableObject>
 static inline bool HasNurseryMemory(TableObject* t) {
   return t->getReservedSlot(TableObject::HasNurseryMemorySlot).toBoolean();
 }
 
@@ -992,17 +992,17 @@ bool GlobalObject::initSetIteratorProto(
       cx, GlobalObject::createBlankPrototypeInheriting<PlainObject>(cx, base));
   if (!proto) {
     return false;
   }
   if (!JS_DefineFunctions(cx, proto, SetIteratorObject::methods) ||
       !DefineToStringTag(cx, proto, cx->names().SetIterator)) {
     return false;
   }
-  global->setReservedSlot(SET_ITERATOR_PROTO, ObjectValue(*proto));
+  global->initBuiltinProto(ProtoKind::SetIteratorProto, proto);
   return true;
 }
 
 SetIteratorObject* SetIteratorObject::create(JSContext* cx, HandleObject obj,
                                              ValueSet* data,
                                              SetObject::IteratorKind kind) {
   MOZ_ASSERT(kind != SetObject::Keys);
 
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -133,17 +133,17 @@ bool GlobalObject::initImportEntryProto(
   if (!proto) {
     return false;
   }
 
   if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr)) {
     return false;
   }
 
-  global->initReservedSlot(IMPORT_ENTRY_PROTO, ObjectValue(*proto));
+  global->initBuiltinProto(ProtoKind::ImportEntryProto, proto);
   return true;
 }
 
 /* static */
 ImportEntryObject* ImportEntryObject::create(
     JSContext* cx, HandleObject moduleRequest, HandleAtom maybeImportName,
     HandleAtom localName, uint32_t lineNumber, uint32_t columnNumber) {
   RootedObject proto(
@@ -212,17 +212,17 @@ bool GlobalObject::initExportEntryProto(
   if (!proto) {
     return false;
   }
 
   if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr)) {
     return false;
   }
 
-  global->initReservedSlot(EXPORT_ENTRY_PROTO, ObjectValue(*proto));
+  global->initBuiltinProto(ProtoKind::ExportEntryProto, proto);
   return true;
 }
 
 /* static */
 ExportEntryObject* ExportEntryObject::create(
     JSContext* cx, HandleAtom maybeExportName, HandleObject moduleRequest,
     HandleAtom maybeImportName, HandleAtom maybeLocalName, uint32_t lineNumber,
     uint32_t columnNumber) {
@@ -288,17 +288,17 @@ bool GlobalObject::initRequestedModulePr
   if (!proto) {
     return false;
   }
 
   if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr)) {
     return false;
   }
 
-  global->initReservedSlot(REQUESTED_MODULE_PROTO, ObjectValue(*proto));
+  global->initBuiltinProto(ProtoKind::RequestedModuleProto, proto);
   return true;
 }
 
 /* static */
 RequestedModuleObject* RequestedModuleObject::create(JSContext* cx,
                                                      HandleObject moduleRequest,
                                                      uint32_t lineNumber,
                                                      uint32_t columnNumber) {
@@ -346,17 +346,17 @@ bool GlobalObject::initModuleRequestProt
   if (!proto) {
     return false;
   }
 
   if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr)) {
     return false;
   }
 
-  global->initReservedSlot(MODULE_REQUEST_PROTO, ObjectValue(*proto));
+  global->initBuiltinProto(ProtoKind::ModuleRequestProto, proto);
   return true;
 }
 
 /* static */
 ModuleRequestObject* ModuleRequestObject::create(JSContext* cx,
                                                  HandleAtom specifier) {
   RootedObject proto(
       cx, GlobalObject::getOrCreateModuleRequestPrototype(cx, cx->global()));
@@ -1396,17 +1396,17 @@ bool GlobalObject::initModuleProto(JSCon
     return false;
   }
 
   if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors,
                                     protoFunctions)) {
     return false;
   }
 
-  global->setReservedSlot(MODULE_PROTO, ObjectValue(*proto));
+  global->initBuiltinProto(ProtoKind::ModuleProto, proto);
   return true;
 }
 
 #undef DEFINE_GETTER_FUNCTIONS
 #undef DEFINE_STRING_ACCESSOR_METHOD
 #undef DEFINE_ARRAY_SLOT_ACCESSOR
 
 ///////////////////////////////////////////////////////////////////////////
--- a/js/src/vm/AsyncIteration.cpp
+++ b/js/src/vm/AsyncIteration.cpp
@@ -441,38 +441,38 @@ static const JSFunctionSpec async_from_s
 
 static const JSFunctionSpec async_generator_methods[] = {
     JS_FN("next", js::AsyncGeneratorNext, 1, 0),
     JS_FN("throw", js::AsyncGeneratorThrow, 1, 0),
     JS_FN("return", js::AsyncGeneratorReturn, 1, 0), JS_FS_END};
 
 bool GlobalObject::initAsyncIteratorProto(JSContext* cx,
                                           Handle<GlobalObject*> global) {
-  if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject()) {
+  if (global->hasBuiltinProto(ProtoKind::AsyncIteratorProto)) {
     return true;
   }
 
   // 25.1.3 The %AsyncIteratorPrototype% Object
   RootedObject asyncIterProto(
       cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
   if (!asyncIterProto) {
     return false;
   }
   if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr,
                                     async_iterator_proto_methods)) {
     return false;
   }
 
-  global->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*asyncIterProto));
+  global->initBuiltinProto(ProtoKind::AsyncIteratorProto, asyncIterProto);
   return true;
 }
 
 bool GlobalObject::initAsyncFromSyncIteratorProto(
     JSContext* cx, Handle<GlobalObject*> global) {
-  if (global->getReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO).isObject()) {
+  if (global->hasBuiltinProto(ProtoKind::AsyncFromSyncIteratorProto)) {
     return true;
   }
 
   RootedObject asyncIterProto(
       cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
   if (!asyncIterProto) {
     return false;
   }
@@ -486,18 +486,18 @@ bool GlobalObject::initAsyncFromSyncIter
   }
   if (!DefinePropertiesAndFunctions(cx, asyncFromSyncIterProto, nullptr,
                                     async_from_sync_iter_methods) ||
       !DefineToStringTag(cx, asyncFromSyncIterProto,
                          cx->names().AsyncFromSyncIterator)) {
     return false;
   }
 
-  global->setReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO,
-                          ObjectValue(*asyncFromSyncIterProto));
+  global->initBuiltinProto(ProtoKind::AsyncFromSyncIteratorProto,
+                           asyncFromSyncIterProto);
   return true;
 }
 
 static JSObject* CreateAsyncGeneratorFunction(JSContext* cx, JSProtoKey key) {
   RootedObject proto(
       cx, GlobalObject::getOrCreateFunctionConstructor(cx, cx->global()));
   if (!proto) {
     return nullptr;
@@ -651,24 +651,25 @@ static const JSClass AsyncIteratorHelper
 const JSClass AsyncIteratorHelperObject::class_ = {
     "Async Iterator Helper",
     JSCLASS_HAS_RESERVED_SLOTS(AsyncIteratorHelperObject::SlotCount),
 };
 
 /* static */
 NativeObject* GlobalObject::getOrCreateAsyncIteratorHelperPrototype(
     JSContext* cx, Handle<GlobalObject*> global) {
-  return MaybeNativeObject(getOrCreateObject(
-      cx, global, ASYNC_ITERATOR_HELPER_PROTO, initAsyncIteratorHelperProto));
+  return MaybeNativeObject(
+      getOrCreateBuiltinProto(cx, global, ProtoKind::AsyncIteratorHelperProto,
+                              initAsyncIteratorHelperProto));
 }
 
 /* static */
 bool GlobalObject::initAsyncIteratorHelperProto(JSContext* cx,
                                                 Handle<GlobalObject*> global) {
-  if (global->getReservedSlot(ASYNC_ITERATOR_HELPER_PROTO).isObject()) {
+  if (global->hasBuiltinProto(ProtoKind::AsyncIteratorHelperProto)) {
     return true;
   }
 
   RootedObject asyncIterProto(
       cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
   if (!asyncIterProto) {
     return false;
   }
@@ -679,18 +680,18 @@ bool GlobalObject::initAsyncIteratorHelp
   if (!asyncIteratorHelperProto) {
     return false;
   }
   if (!DefinePropertiesAndFunctions(cx, asyncIteratorHelperProto, nullptr,
                                     async_iterator_helper_methods)) {
     return false;
   }
 
-  global->setReservedSlot(ASYNC_ITERATOR_HELPER_PROTO,
-                          ObjectValue(*asyncIteratorHelperProto));
+  global->initBuiltinProto(ProtoKind::AsyncIteratorHelperProto,
+                           asyncIteratorHelperProto);
   return true;
 }
 
 AsyncIteratorHelperObject* js::NewAsyncIteratorHelper(JSContext* cx) {
   RootedObject proto(cx, GlobalObject::getOrCreateAsyncIteratorHelperPrototype(
                              cx, cx->global()));
   if (!proto) {
     return nullptr;
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -280,17 +280,17 @@ bool GlobalObject::resolveConstructor(JS
                               IfClassIsDisabled::DoNothing);
   }
 
   // %IteratorPrototype%.map.[[Prototype]] is %Generator% and
   // %Generator%.prototype.[[Prototype]] is %IteratorPrototype%.
   // A workaround in initIteratorProto prevents runaway mutual recursion while
   // setting these up. Ensure the workaround is triggered already:
   if (key == JSProto_GeneratorFunction &&
-      !global->getReservedSlot(ITERATOR_PROTO).isObject()) {
+      !global->hasBuiltinProto(ProtoKind::IteratorProto)) {
     if (!getOrCreateIteratorPrototype(cx, global)) {
       return false;
     }
 
     // If iterator helpers are enabled, populating %IteratorPrototype% will
     // have recursively gone through here.
     if (global->isStandardClassResolved(key)) {
       return true;
@@ -436,65 +436,80 @@ bool GlobalObject::maybeResolveGlobalThi
     *resolved = true;
     global->data().globalThisResolved = true;
   }
 
   return true;
 }
 
 /* static */
-JSObject* GlobalObject::createObject(JSContext* cx,
-                                     Handle<GlobalObject*> global,
-                                     unsigned slot, ObjectInitOp init) {
+JSObject* GlobalObject::createBuiltinProto(JSContext* cx,
+                                           Handle<GlobalObject*> global,
+                                           ProtoKind kind, ObjectInitOp init) {
   if (global->zone()->createdForHelperThread()) {
-    return createOffThreadObject(cx, global, slot);
+    return createOffThreadBuiltinProto(cx, global, kind);
   }
 
   MOZ_ASSERT(!cx->isHelperThreadContext());
   if (!init(cx, global)) {
     return nullptr;
   }
 
-  return &global->getSlot(slot).toObject();
+  return &global->getBuiltinProto(kind);
 }
 
-JSObject* GlobalObject::createObject(JSContext* cx,
-                                     Handle<GlobalObject*> global,
-                                     unsigned slot, HandleAtom tag,
-                                     ObjectInitWithTagOp init) {
+JSObject* GlobalObject::createBuiltinProto(JSContext* cx,
+                                           Handle<GlobalObject*> global,
+                                           ProtoKind kind, HandleAtom tag,
+                                           ObjectInitWithTagOp init) {
   if (global->zone()->createdForHelperThread()) {
-    return createOffThreadObject(cx, global, slot);
+    return createOffThreadBuiltinProto(cx, global, kind);
   }
 
   MOZ_ASSERT(!cx->isHelperThreadContext());
   if (!init(cx, global, tag)) {
     return nullptr;
   }
 
-  return &global->getSlot(slot).toObject();
+  return &global->getBuiltinProto(kind);
 }
 
 const JSClass GlobalObject::OffThreadPlaceholderObject::class_ = {
     "off-thread-prototype-placeholder", JSCLASS_HAS_RESERVED_SLOTS(1)};
 
 /* static */ GlobalObject::OffThreadPlaceholderObject*
 GlobalObject::OffThreadPlaceholderObject::New(JSContext* cx, unsigned slot) {
   Rooted<OffThreadPlaceholderObject*> placeholder(cx);
   placeholder =
       NewObjectWithGivenProto<OffThreadPlaceholderObject>(cx, nullptr);
   if (!placeholder) {
     return nullptr;
   }
 
-  placeholder->setReservedSlot(SlotIndexSlot, Int32Value(slot));
+  placeholder->setReservedSlot(SlotIndexOrProtoKindSlot, Int32Value(slot));
   return placeholder;
 }
 
-inline int32_t GlobalObject::OffThreadPlaceholderObject::getSlotIndex() const {
-  return getReservedSlot(SlotIndexSlot).toInt32();
+/* static */ GlobalObject::OffThreadPlaceholderObject*
+GlobalObject::OffThreadPlaceholderObject::New(JSContext* cx, ProtoKind kind) {
+  Rooted<OffThreadPlaceholderObject*> placeholder(cx);
+  placeholder =
+      NewObjectWithGivenProto<OffThreadPlaceholderObject>(cx, nullptr);
+  if (!placeholder) {
+    return nullptr;
+  }
+
+  placeholder->setReservedSlot(SlotIndexOrProtoKindSlot,
+                               Int32Value(-int32_t(kind)));
+  return placeholder;
+}
+
+inline int32_t
+GlobalObject::OffThreadPlaceholderObject::getSlotIndexOrProtoKind() const {
+  return getReservedSlot(SlotIndexOrProtoKindSlot).toInt32();
 }
 
 /* static */
 bool GlobalObject::resolveOffThreadConstructor(JSContext* cx,
                                                Handle<GlobalObject*> global,
                                                JSProtoKey key) {
   // Don't resolve constructors for off-thread parse globals. Instead create a
   // placeholder object for the prototype which we can use to find the real
@@ -519,40 +534,46 @@ bool GlobalObject::resolveOffThreadConst
   }
 
   global->setPrototype(key, ObjectValue(*placeholder));
   global->setConstructor(key, MagicValue(JS_OFF_THREAD_CONSTRUCTOR));
   return true;
 }
 
 /* static */
-JSObject* GlobalObject::createOffThreadObject(JSContext* cx,
-                                              Handle<GlobalObject*> global,
-                                              unsigned slot) {
+JSObject* GlobalObject::createOffThreadBuiltinProto(
+    JSContext* cx, Handle<GlobalObject*> global, ProtoKind kind) {
   // Don't create prototype objects for off-thread parse globals. Instead
   // create a placeholder object which we can use to find the real prototype
   // when the off-thread compartment is merged back into the target
   // compartment.
 
   MOZ_ASSERT(global->zone()->createdForHelperThread());
-  MOZ_ASSERT(slot == MODULE_PROTO || slot == IMPORT_ENTRY_PROTO ||
-             slot == EXPORT_ENTRY_PROTO || slot == REQUESTED_MODULE_PROTO);
+  MOZ_ASSERT(kind == ProtoKind::ModuleProto ||
+             kind == ProtoKind::ImportEntryProto ||
+             kind == ProtoKind::ExportEntryProto ||
+             kind == ProtoKind::RequestedModuleProto);
 
-  auto placeholder = OffThreadPlaceholderObject::New(cx, slot);
+  auto placeholder = OffThreadPlaceholderObject::New(cx, kind);
   if (!placeholder) {
     return nullptr;
   }
 
-  global->setSlot(slot, ObjectValue(*placeholder));
+  global->initBuiltinProto(kind, placeholder);
   return placeholder;
 }
 
 JSObject* GlobalObject::getPrototypeForOffThreadPlaceholder(JSObject* obj) {
   auto placeholder = &obj->as<OffThreadPlaceholderObject>();
-  return &getSlot(placeholder->getSlotIndex()).toObject();
+  int32_t value = placeholder->getSlotIndexOrProtoKind();
+  if (value >= 0) {
+    return &getSlot(value).toObject();
+  }
+  MOZ_ASSERT(-value < int32_t(ProtoKind::Limit));
+  return &getBuiltinProto(ProtoKind(-value));
 }
 
 /* static */
 bool GlobalObject::initBuiltinConstructor(JSContext* cx,
                                           Handle<GlobalObject*> global,
                                           JSProtoKey key, HandleObject ctor,
                                           HandleObject proto) {
   MOZ_ASSERT(!global->empty());  // reserved slots already allocated
@@ -1093,50 +1114,55 @@ bool GlobalObject::ensureModulePrototype
 
   return true;
 }
 
 /* static */
 JSObject* GlobalObject::createIteratorPrototype(JSContext* cx,
                                                 Handle<GlobalObject*> global) {
   if (!cx->realm()->creationOptions().getIteratorHelpersEnabled()) {
-    return getOrCreateObject(cx, global, ITERATOR_PROTO, initIteratorProto);
+    return getOrCreateBuiltinProto(cx, global, ProtoKind::IteratorProto,
+                                   initIteratorProto);
   }
 
   if (!ensureConstructor(cx, global, JSProto_Iterator)) {
     return nullptr;
   }
   JSObject* proto = &global->getPrototype(JSProto_Iterator).toObject();
-  global->setReservedSlot(ITERATOR_PROTO, ObjectValue(*proto));
+  global->initBuiltinProto(ProtoKind::IteratorProto, proto);
   return proto;
 }
 
 /* static */
 JSObject* GlobalObject::createAsyncIteratorPrototype(
     JSContext* cx, Handle<GlobalObject*> global) {
   if (!cx->realm()->creationOptions().getIteratorHelpersEnabled()) {
-    return getOrCreateObject(cx, global, ASYNC_ITERATOR_PROTO,
-                             initAsyncIteratorProto);
+    return getOrCreateBuiltinProto(cx, global, ProtoKind::AsyncIteratorProto,
+                                   initAsyncIteratorProto);
   }
 
   if (!ensureConstructor(cx, global, JSProto_AsyncIterator)) {
     return nullptr;
   }
   JSObject* proto = &global->getPrototype(JSProto_AsyncIterator).toObject();
-  global->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*proto));
+  global->initBuiltinProto(ProtoKind::AsyncIteratorProto, proto);
   return proto;
 }
 
 void GlobalObject::releaseData(JSFreeOp* fop) {
   GlobalObjectData* data = maybeData();
   setReservedSlot(GLOBAL_DATA_SLOT, PrivateValue(nullptr));
   fop->delete_(this, data, MemoryUse::GlobalObjectData);
 }
 
 void GlobalObjectData::trace(JSTracer* trc) {
+  for (auto& proto : builtinProtos) {
+    TraceNullableEdge(trc, &proto, "global-builtin-proto");
+  }
+
   TraceEdge(trc, &emptyGlobalScope, "global-empty-scope");
 
   TraceNullableEdge(trc, &regExpStatics, "global-regexp-statics");
   TraceNullableEdge(trc, &intrinsicsHolder, "global-intrinsics-holder");
   TraceNullableEdge(trc, &forOfPICChain, "global-for-of-pic");
   TraceNullableEdge(trc, &sourceURLsHolder, "global-source-urls");
   TraceNullableEdge(trc, &realmKeyObject, "global-realm-key");
   TraceNullableEdge(trc, &throwTypeError, "global-throw-type-error");
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_GlobalObject_h
 #define vm_GlobalObject_h
 
 #include "mozilla/Assertions.h"
+#include "mozilla/EnumeratedArray.h"
 
 #include <stdint.h>
 #include <type_traits>
 
 #include "jsapi.h"
 #include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jspubtd.h"
@@ -62,16 +63,44 @@ class GlobalObjectData {
   friend class js::GlobalObject;
 
   GlobalObjectData(const GlobalObjectData&) = delete;
   void operator=(const GlobalObjectData&) = delete;
 
  public:
   GlobalObjectData() = default;
 
+  // Built-in prototypes for this global. Note that this is different from the
+  // set of built-in constructors/prototypes based on JSProtoKey.
+  enum class ProtoKind {
+    IteratorProto,
+    ArrayIteratorProto,
+    StringIteratorProto,
+    RegExpStringIteratorProto,
+    GeneratorObjectProto,
+    AsyncIteratorProto,
+    AsyncFromSyncIteratorProto,
+    AsyncGeneratorProto,
+    MapIteratorProto,
+    SetIteratorProto,
+    WrapForValidIteratorProto,
+    IteratorHelperProto,
+    AsyncIteratorHelperProto,
+    ModuleProto,
+    ImportEntryProto,
+    ExportEntryProto,
+    RequestedModuleProto,
+    ModuleRequestProto,
+
+    Limit
+  };
+  using ProtoArray =
+      mozilla::EnumeratedArray<ProtoKind, ProtoKind::Limit, HeapPtr<JSObject*>>;
+  ProtoArray builtinProtos;
+
   HeapPtr<GlobalScope*> emptyGlobalScope;
 
   // Global state for regular expressions.
   HeapPtr<RegExpStaticsObject*> regExpStatics;
 
   // Functions and other top-level values for self-hosted code.
   HeapPtr<NativeObject*> intrinsicsHolder;
 
@@ -134,48 +163,32 @@ class GlobalObject : public NativeObject
    * Count of slots to store built-in prototypes and initial visible
    * properties for the constructors.
    */
   static const unsigned STANDARD_CLASS_SLOTS = JSProto_LIMIT * 2;
 
   enum : unsigned {
     /* One-off properties stored after slots for built-ins. */
     GLOBAL_DATA_SLOT = APPLICATION_SLOTS + STANDARD_CLASS_SLOTS,
-    ITERATOR_PROTO,
-    ARRAY_ITERATOR_PROTO,
-    STRING_ITERATOR_PROTO,
-    REGEXP_STRING_ITERATOR_PROTO,
-    GENERATOR_OBJECT_PROTO,
-    ASYNC_ITERATOR_PROTO,
-    ASYNC_FROM_SYNC_ITERATOR_PROTO,
-    ASYNC_GENERATOR_PROTO,
-    MAP_ITERATOR_PROTO,
-    SET_ITERATOR_PROTO,
-    WRAP_FOR_VALID_ITERATOR_PROTO,
-    ITERATOR_HELPER_PROTO,
-    ASYNC_ITERATOR_HELPER_PROTO,
-    MODULE_PROTO,
-    IMPORT_ENTRY_PROTO,
-    EXPORT_ENTRY_PROTO,
-    REQUESTED_MODULE_PROTO,
-    MODULE_REQUEST_PROTO,
     WINDOW_PROXY,
 
     /* Total reserved-slot count for global objects. */
     RESERVED_SLOTS
   };
 
   /*
    * The slot count must be in the public API for JSCLASS_GLOBAL_FLAGS, and
    * we won't expose GlobalObject, so just assert that the two values are
    * synchronized.
    */
   static_assert(JSCLASS_GLOBAL_SLOT_COUNT == RESERVED_SLOTS,
                 "global object slot counts are inconsistent");
 
+  using ProtoKind = GlobalObjectData::ProtoKind;
+
   static unsigned constructorSlot(JSProtoKey key) {
     MOZ_ASSERT(key < JSProto_LIMIT);
     return APPLICATION_SLOTS + key;
   }
 
   static unsigned prototypeSlot(JSProtoKey key) {
     MOZ_ASSERT(key < JSProto_LIMIT);
     return APPLICATION_SLOTS + JSProto_LIMIT + key;
@@ -188,16 +201,31 @@ class GlobalObject : public NativeObject
   const GlobalObjectData* maybeData() const {
     Value v = getReservedSlot(GLOBAL_DATA_SLOT);
     return static_cast<const GlobalObjectData*>(v.toPrivate());
   }
 
   GlobalObjectData& data() { return *maybeData(); }
   const GlobalObjectData& data() const { return *maybeData(); }
 
+  void initBuiltinProto(ProtoKind kind, JSObject* proto) {
+    MOZ_ASSERT(proto);
+    data().builtinProtos[kind].init(proto);
+  }
+  bool hasBuiltinProto(ProtoKind kind) const {
+    return bool(data().builtinProtos[kind]);
+  }
+  JSObject* maybeBuiltinProto(ProtoKind kind) const {
+    return data().builtinProtos[kind];
+  }
+  JSObject& getBuiltinProto(ProtoKind kind) const {
+    MOZ_ASSERT(hasBuiltinProto(kind));
+    return *data().builtinProtos[kind];
+  }
+
  public:
   GlobalLexicalEnvironmentObject& lexicalEnvironment() const;
   GlobalScope& emptyGlobalScope() const;
 
   void traceData(JSTracer* trc) { data().trace(trc); }
   void releaseData(JSFreeOp* fop);
 
   size_t sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const {
@@ -545,41 +573,42 @@ class GlobalObject : public NativeObject
   }
 
   static bool ensureModulePrototypesCreated(JSContext* cx,
                                             Handle<GlobalObject*> global,
                                             bool setUsedAsPrototype = false);
 
   static JSObject* getOrCreateModulePrototype(JSContext* cx,
                                               Handle<GlobalObject*> global) {
-    return getOrCreateObject(cx, global, MODULE_PROTO, initModuleProto);
+    return getOrCreateBuiltinProto(cx, global, ProtoKind::ModuleProto,
+                                   initModuleProto);
   }
 
   static JSObject* getOrCreateImportEntryPrototype(
       JSContext* cx, Handle<GlobalObject*> global) {
-    return getOrCreateObject(cx, global, IMPORT_ENTRY_PROTO,
-                             initImportEntryProto);
+    return getOrCreateBuiltinProto(cx, global, ProtoKind::ImportEntryProto,
+                                   initImportEntryProto);
   }
 
   static JSObject* getOrCreateExportEntryPrototype(
       JSContext* cx, Handle<GlobalObject*> global) {
-    return getOrCreateObject(cx, global, EXPORT_ENTRY_PROTO,
-                             initExportEntryProto);
+    return getOrCreateBuiltinProto(cx, global, ProtoKind::ExportEntryProto,
+                                   initExportEntryProto);
   }
 
   static JSObject* getOrCreateRequestedModulePrototype(
       JSContext* cx, Handle<GlobalObject*> global) {
-    return getOrCreateObject(cx, global, REQUESTED_MODULE_PROTO,
-                             initRequestedModuleProto);
+    return getOrCreateBuiltinProto(cx, global, ProtoKind::RequestedModuleProto,
+                                   initRequestedModuleProto);
   }
 
   static JSObject* getOrCreateModuleRequestPrototype(
       JSContext* cx, Handle<GlobalObject*> global) {
-    return getOrCreateObject(cx, global, MODULE_REQUEST_PROTO,
-                             initModuleRequestProto);
+    return getOrCreateBuiltinProto(cx, global, ProtoKind::ModuleRequestProto,
+                                   initModuleRequestProto);
   }
 
   static JSFunction* getOrCreateTypedArrayConstructor(
       JSContext* cx, Handle<GlobalObject*> global) {
     if (!ensureConstructor(cx, global, JSProto_TypedArray)) {
       return nullptr;
     }
     return &global->getConstructor(JSProto_TypedArray)
@@ -595,84 +624,83 @@ class GlobalObject : public NativeObject
     return &global->getPrototype(JSProto_TypedArray).toObject();
   }
 
  private:
   using ObjectInitOp = bool (*)(JSContext*, Handle<GlobalObject*>);
   using ObjectInitWithTagOp = bool (*)(JSContext*, Handle<GlobalObject*>,
                                        HandleAtom);
 
-  static JSObject* getOrCreateObject(JSContext* cx,
-                                     Handle<GlobalObject*> global,
-                                     unsigned slot, ObjectInitOp init) {
-    Value v = global->getReservedSlot(slot);
-    if (v.isObject()) {
-      return &v.toObject();
+  static JSObject* getOrCreateBuiltinProto(JSContext* cx,
+                                           Handle<GlobalObject*> global,
+                                           ProtoKind kind, ObjectInitOp init) {
+    if (JSObject* proto = global->maybeBuiltinProto(kind)) {
+      return proto;
     }
 
-    return createObject(cx, global, slot, init);
+    return createBuiltinProto(cx, global, kind, init);
   }
 
-  static JSObject* getOrCreateObject(JSContext* cx,
-                                     Handle<GlobalObject*> global,
-                                     unsigned slot, HandleAtom tag,
-                                     ObjectInitWithTagOp init) {
-    Value v = global->getReservedSlot(slot);
-    if (v.isObject()) {
-      return &v.toObject();
+  static JSObject* getOrCreateBuiltinProto(JSContext* cx,
+                                           Handle<GlobalObject*> global,
+                                           ProtoKind kind, HandleAtom tag,
+                                           ObjectInitWithTagOp init) {
+    if (JSObject* proto = global->maybeBuiltinProto(kind)) {
+      return proto;
     }
 
-    return createObject(cx, global, slot, tag, init);
+    return createBuiltinProto(cx, global, kind, tag, init);
   }
 
-  static JSObject* createObject(JSContext* cx, Handle<GlobalObject*> global,
-                                unsigned slot, ObjectInitOp init);
-  static JSObject* createObject(JSContext* cx, Handle<GlobalObject*> global,
-                                unsigned slot, HandleAtom tag,
-                                ObjectInitWithTagOp init);
+  static JSObject* createBuiltinProto(JSContext* cx,
+                                      Handle<GlobalObject*> global,
+                                      ProtoKind kind, ObjectInitOp init);
+  static JSObject* createBuiltinProto(JSContext* cx,
+                                      Handle<GlobalObject*> global,
+                                      ProtoKind kind, HandleAtom tag,
+                                      ObjectInitWithTagOp init);
 
   static JSObject* createIteratorPrototype(JSContext* cx,
                                            Handle<GlobalObject*> global);
 
  public:
   static JSObject* getOrCreateIteratorPrototype(JSContext* cx,
                                                 Handle<GlobalObject*> global) {
-    if (global->getReservedSlot(ITERATOR_PROTO).isObject()) {
-      return &global->getReservedSlot(ITERATOR_PROTO).toObject();
+    if (JSObject* proto = global->maybeBuiltinProto(ProtoKind::IteratorProto)) {
+      return proto;
     }
     return createIteratorPrototype(cx, global);
   }
 
   static NativeObject* getOrCreateArrayIteratorPrototype(
       JSContext* cx, Handle<GlobalObject*> global);
 
   NativeObject* maybeGetArrayIteratorPrototype() {
-    Value v = getReservedSlot(ARRAY_ITERATOR_PROTO);
-    if (v.isObject()) {
-      return &v.toObject().as<NativeObject>();
+    if (JSObject* obj = maybeBuiltinProto(ProtoKind::ArrayIteratorProto)) {
+      return &obj->as<NativeObject>();
     }
     return nullptr;
   }
 
   static JSObject* getOrCreateStringIteratorPrototype(
       JSContext* cx, Handle<GlobalObject*> global);
 
   static JSObject* getOrCreateRegExpStringIteratorPrototype(
       JSContext* cx, Handle<GlobalObject*> global);
 
   void setGeneratorObjectPrototype(JSObject* obj) {
-    setReservedSlot(GENERATOR_OBJECT_PROTO, ObjectValue(*obj));
+    initBuiltinProto(ProtoKind::GeneratorObjectProto, obj);
   }
 
   static JSObject* getOrCreateGeneratorObjectPrototype(
       JSContext* cx, Handle<GlobalObject*> global) {
     if (!ensureConstructor(cx, global, JSProto_GeneratorFunction)) {
       return nullptr;
     }
-    return &global->getReservedSlot(GENERATOR_OBJECT_PROTO).toObject();
+    return &global->getBuiltinProto(ProtoKind::GeneratorObjectProto);
   }
 
   static JSObject* getOrCreateGeneratorFunctionPrototype(
       JSContext* cx, Handle<GlobalObject*> global) {
     if (!ensureConstructor(cx, global, JSProto_GeneratorFunction)) {
       return nullptr;
     }
     return &global->getPrototype(JSProto_GeneratorFunction).toObject();
@@ -702,28 +730,28 @@ class GlobalObject : public NativeObject
     return &global->getConstructor(JSProto_AsyncFunction).toObject();
   }
 
   static JSObject* createAsyncIteratorPrototype(JSContext* cx,
                                                 Handle<GlobalObject*> global);
 
   static JSObject* getOrCreateAsyncIteratorPrototype(
       JSContext* cx, Handle<GlobalObject*> global) {
-    if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject()) {
-      return &global->getReservedSlot(ASYNC_ITERATOR_PROTO).toObject();
+    if (JSObject* proto =
+            global->maybeBuiltinProto(ProtoKind::AsyncIteratorProto)) {
+      return proto;
     }
     return createAsyncIteratorPrototype(cx, global);
-    // return getOrCreateObject(cx, global, ASYNC_ITERATOR_PROTO,
-    //                         initAsyncIteratorProto);
   }
 
   static JSObject* getOrCreateAsyncFromSyncIteratorPrototype(
       JSContext* cx, Handle<GlobalObject*> global) {
-    return getOrCreateObject(cx, global, ASYNC_FROM_SYNC_ITERATOR_PROTO,
-                             initAsyncFromSyncIteratorProto);
+    return getOrCreateBuiltinProto(cx, global,
+                                   ProtoKind::AsyncFromSyncIteratorProto,
+                                   initAsyncFromSyncIteratorProto);
   }
 
   static JSObject* getOrCreateAsyncGenerator(JSContext* cx,
                                              Handle<GlobalObject*> global) {
     if (!ensureConstructor(cx, global, JSProto_AsyncGeneratorFunction)) {
       return nullptr;
     }
     return &global->getPrototype(JSProto_AsyncGeneratorFunction).toObject();
@@ -733,37 +761,37 @@ class GlobalObject : public NativeObject
       JSContext* cx, Handle<GlobalObject*> global) {
     if (!ensureConstructor(cx, global, JSProto_AsyncGeneratorFunction)) {
       return nullptr;
     }
     return &global->getConstructor(JSProto_AsyncGeneratorFunction).toObject();
   }
 
   void setAsyncGeneratorPrototype(JSObject* obj) {
-    setReservedSlot(ASYNC_GENERATOR_PROTO, ObjectValue(*obj));
+    initBuiltinProto(ProtoKind::AsyncGeneratorProto, obj);
   }
 
   static JSObject* getOrCreateAsyncGeneratorPrototype(
       JSContext* cx, Handle<GlobalObject*> global) {
     if (!ensureConstructor(cx, global, JSProto_AsyncGeneratorFunction)) {
       return nullptr;
     }
-    return &global->getReservedSlot(ASYNC_GENERATOR_PROTO).toObject();
+    return &global->getBuiltinProto(ProtoKind::AsyncGeneratorProto);
   }
 
   static JSObject* getOrCreateMapIteratorPrototype(
       JSContext* cx, Handle<GlobalObject*> global) {
-    return getOrCreateObject(cx, global, MAP_ITERATOR_PROTO,
-                             initMapIteratorProto);
+    return getOrCreateBuiltinProto(cx, global, ProtoKind::MapIteratorProto,
+                                   initMapIteratorProto);
   }
 
   static JSObject* getOrCreateSetIteratorPrototype(
       JSContext* cx, Handle<GlobalObject*> global) {
-    return getOrCreateObject(cx, global, SET_ITERATOR_PROTO,
-                             initSetIteratorProto);
+    return getOrCreateBuiltinProto(cx, global, ProtoKind::SetIteratorProto,
+                                   initSetIteratorProto);
   }
 
   static JSObject* getOrCreateDataViewPrototype(JSContext* cx,
                                                 Handle<GlobalObject*> global) {
     if (!ensureConstructor(cx, global, JSProto_DataView)) {
       return nullptr;
     }
     return &global->getPrototype(JSProto_DataView).toObject();
@@ -870,17 +898,17 @@ class GlobalObject : public NativeObject
                               MutableHandleObject eval);
 
   // Infallibly test whether the given value is the eval function for this
   // global.
   bool valueIsEval(const Value& val);
 
   // Implemented in vm/Iteration.cpp.
   static bool initIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
-  template <unsigned Slot, const JSClass* ProtoClass,
+  template <ProtoKind Kind, const JSClass* ProtoClass,
             const JSFunctionSpec* Methods>
   static bool initObjectIteratorProto(JSContext* cx,
                                       Handle<GlobalObject*> global,
                                       HandleAtom tag);
 
   // Implemented in vm/AsyncIteration.cpp.
   static bool initAsyncIteratorProto(JSContext* cx,
                                      Handle<GlobalObject*> global);
@@ -939,35 +967,38 @@ class GlobalObject : public NativeObject
   Shape* maybeArrayShape() const { return data().arrayShape; }
 
   // Returns an object that represents the realm, used by embedder.
   static JSObject* getOrCreateRealmKeyObject(JSContext* cx,
                                              Handle<GlobalObject*> global);
 
   // A class used in place of a prototype during off-thread parsing.
   struct OffThreadPlaceholderObject : public NativeObject {
-    static const int32_t SlotIndexSlot = 0;
+    // The slot either stores a slot index (Int32Value >= 0) or a ProtoKind
+    // (Int32Value < 0).
+    static const int32_t SlotIndexOrProtoKindSlot = 0;
     static const JSClass class_;
     static OffThreadPlaceholderObject* New(JSContext* cx, unsigned slot);
-    inline int32_t getSlotIndex() const;
+    static OffThreadPlaceholderObject* New(JSContext* cx, ProtoKind kind);
+    inline int32_t getSlotIndexOrProtoKind() const;
   };
 
   static bool isOffThreadPrototypePlaceholder(JSObject* obj) {
     return obj->is<OffThreadPlaceholderObject>();
   }
 
   JSObject* getPrototypeForOffThreadPlaceholder(JSObject* placeholder);
 
  private:
   static bool resolveOffThreadConstructor(JSContext* cx,
                                           Handle<GlobalObject*> global,
                                           JSProtoKey key);
-  static JSObject* createOffThreadObject(JSContext* cx,
-                                         Handle<GlobalObject*> global,
-                                         unsigned slot);
+  static JSObject* createOffThreadBuiltinProto(JSContext* cx,
+                                               Handle<GlobalObject*> global,
+                                               ProtoKind kind);
 };
 
 /*
  * Unless otherwise specified, define ctor.prototype = proto as non-enumerable,
  * non-configurable, and non-writable; and define proto.constructor = ctor as
  * non-enumerable but configurable and writable.
  */
 extern bool LinkConstructorAndPrototype(
--- a/js/src/vm/Iteration.cpp
+++ b/js/src/vm/Iteration.cpp
@@ -1572,95 +1572,97 @@ static const JSFunctionSpec iterator_met
     JS_SELF_HOSTED_FN("every", "IteratorEvery", 1, 0),
     JS_SELF_HOSTED_FN("find", "IteratorFind", 1, 0),
     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_FS_END};
 
 /* static */
 bool GlobalObject::initIteratorProto(JSContext* cx,
                                      Handle<GlobalObject*> global) {
-  if (global->getReservedSlot(ITERATOR_PROTO).isObject()) {
+  if (global->hasBuiltinProto(ProtoKind::IteratorProto)) {
     return true;
   }
 
   RootedObject proto(
       cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
   if (!proto) {
     return false;
   }
 
   // %IteratorPrototype%.map.[[Prototype]] is %Generator% and
   // %Generator%.prototype.[[Prototype]] is %IteratorPrototype%.
   // Populate the slot early, to prevent runaway mutual recursion.
-  global->setReservedSlot(ITERATOR_PROTO, ObjectValue(*proto));
+  global->initBuiltinProto(ProtoKind::IteratorProto, proto);
 
   if (!DefinePropertiesAndFunctions(cx, proto, nullptr, iterator_methods)) {
     // In this case, we leave a partially initialized object in the
     // slot. There's no obvious way to do better, since this object may already
     // be in the prototype chain of %GeneratorPrototype%.
     return false;
   }
 
   return true;
 }
 
 /* static */
-template <unsigned Slot, const JSClass* ProtoClass,
+template <GlobalObject::ProtoKind Kind, const JSClass* ProtoClass,
           const JSFunctionSpec* Methods>
 bool GlobalObject::initObjectIteratorProto(JSContext* cx,
                                            Handle<GlobalObject*> global,
                                            HandleAtom tag) {
-  if (global->getReservedSlot(Slot).isObject()) {
+  if (global->hasBuiltinProto(Kind)) {
     return true;
   }
 
   RootedObject iteratorProto(
       cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
   if (!iteratorProto) {
     return false;
   }
 
   RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(
                              cx, ProtoClass, iteratorProto));
   if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, Methods) ||
       (tag && !DefineToStringTag(cx, proto, tag))) {
     return false;
   }
 
-  global->setReservedSlot(Slot, ObjectValue(*proto));
+  global->initBuiltinProto(Kind, proto);
   return true;
 }
 
 /* static */
 NativeObject* GlobalObject::getOrCreateArrayIteratorPrototype(
     JSContext* cx, Handle<GlobalObject*> global) {
-  return MaybeNativeObject(getOrCreateObject(
-      cx, global, ARRAY_ITERATOR_PROTO, cx->names().ArrayIterator.toHandle(),
-      initObjectIteratorProto<ARRAY_ITERATOR_PROTO,
+  return MaybeNativeObject(getOrCreateBuiltinProto(
+      cx, global, ProtoKind::ArrayIteratorProto,
+      cx->names().ArrayIterator.toHandle(),
+      initObjectIteratorProto<ProtoKind::ArrayIteratorProto,
                               &ArrayIteratorPrototypeClass,
                               array_iterator_methods>));
 }
 
 /* static */
 JSObject* GlobalObject::getOrCreateStringIteratorPrototype(
     JSContext* cx, Handle<GlobalObject*> global) {
-  return getOrCreateObject(
-      cx, global, STRING_ITERATOR_PROTO, cx->names().StringIterator.toHandle(),
-      initObjectIteratorProto<STRING_ITERATOR_PROTO,
+  return getOrCreateBuiltinProto(
+      cx, global, ProtoKind::StringIteratorProto,
+      cx->names().StringIterator.toHandle(),
+      initObjectIteratorProto<ProtoKind::StringIteratorProto,
                               &StringIteratorPrototypeClass,
                               string_iterator_methods>);
 }
 
 /* static */
 JSObject* GlobalObject::getOrCreateRegExpStringIteratorPrototype(
     JSContext* cx, Handle<GlobalObject*> global) {
-  return getOrCreateObject(
-      cx, global, REGEXP_STRING_ITERATOR_PROTO,
+  return getOrCreateBuiltinProto(
+      cx, global, ProtoKind::RegExpStringIteratorProto,
       cx->names().RegExpStringIterator.toHandle(),
-      initObjectIteratorProto<REGEXP_STRING_ITERATOR_PROTO,
+      initObjectIteratorProto<ProtoKind::RegExpStringIteratorProto,
                               &RegExpStringIteratorPrototypeClass,
                               regexp_string_iterator_methods>);
 }
 
 // Iterator Helper Proposal 2.1.3.1 Iterator()
 // https://tc39.es/proposal-iterator-helpers/#sec-iterator as of revision
 // ed6e15a
 static bool IteratorConstructor(JSContext* cx, unsigned argc, Value* vp) {
@@ -1731,19 +1733,19 @@ static const JSClass WrapForValidIterato
 const JSClass WrapForValidIteratorObject::class_ = {
     "Wrap For Valid Iterator",
     JSCLASS_HAS_RESERVED_SLOTS(WrapForValidIteratorObject::SlotCount),
 };
 
 /* static */
 NativeObject* GlobalObject::getOrCreateWrapForValidIteratorPrototype(
     JSContext* cx, Handle<GlobalObject*> global) {
-  return MaybeNativeObject(getOrCreateObject(
-      cx, global, WRAP_FOR_VALID_ITERATOR_PROTO, HandleAtom(nullptr),
-      initObjectIteratorProto<WRAP_FOR_VALID_ITERATOR_PROTO,
+  return MaybeNativeObject(getOrCreateBuiltinProto(
+      cx, global, ProtoKind::WrapForValidIteratorProto, HandleAtom(nullptr),
+      initObjectIteratorProto<ProtoKind::WrapForValidIteratorProto,
                               &WrapForValidIteratorPrototypeClass,
                               wrap_for_valid_iterator_methods>));
 }
 
 WrapForValidIteratorObject* js::NewWrapForValidIterator(JSContext* cx) {
   RootedObject proto(cx, GlobalObject::getOrCreateWrapForValidIteratorPrototype(
                              cx, cx->global()));
   if (!proto) {
@@ -1763,21 +1765,21 @@ static const JSClass IteratorHelperProto
 const JSClass IteratorHelperObject::class_ = {
     "Iterator Helper",
     JSCLASS_HAS_RESERVED_SLOTS(IteratorHelperObject::SlotCount),
 };
 
 /* static */
 NativeObject* GlobalObject::getOrCreateIteratorHelperPrototype(
     JSContext* cx, Handle<GlobalObject*> global) {
-  return MaybeNativeObject(
-      getOrCreateObject(cx, global, ITERATOR_HELPER_PROTO, HandleAtom(nullptr),
-                        initObjectIteratorProto<ITERATOR_HELPER_PROTO,
-                                                &IteratorHelperPrototypeClass,
-                                                iterator_helper_methods>));
+  return MaybeNativeObject(getOrCreateBuiltinProto(
+      cx, global, ProtoKind::IteratorHelperProto, HandleAtom(nullptr),
+      initObjectIteratorProto<ProtoKind::IteratorHelperProto,
+                              &IteratorHelperPrototypeClass,
+                              iterator_helper_methods>));
 }
 
 IteratorHelperObject* js::NewIteratorHelper(JSContext* cx) {
   RootedObject proto(
       cx, GlobalObject::getOrCreateIteratorHelperPrototype(cx, cx->global()));
   if (!proto) {
     return nullptr;
   }