Backed out 30 changesets (bug 949220) for assertion failure on JSObject.cpp CLOSED TREE
authorBogdan Tara <btara@mozilla.com>
Tue, 14 Apr 2020 13:17:51 +0300
changeset 523821 71f4061ed1ef409ca54176ddea93ddf04fcbe35b
parent 523820 a315c20e171362ababb9adeb6e8c093105c5ce6e
child 523822 b3608d9d7bd36a4cfb08d7c9f0525066d29d690a
push id37311
push useraciure@mozilla.com
push dateTue, 14 Apr 2020 15:44:38 +0000
treeherdermozilla-central@71f4061ed1ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs949220
milestone77.0a1
backs outb077b434866d8522790e29a10b47bd9ef46d4d2e
437dc6f1a0b13a054a239e25cb0873eafc763783
9043d3f4ea6f250ea3eeeabffd6e14e1bf7fbb42
7610053408360251ede0e4e65a93f34d57c14488
650ccbbc2e150cabecb792cb2af44fabd0af9213
b7459076dbf55418311639fd4ea4284c3aecf78c
e1ac27ee38e2559ef9d1b6f5dfb9a7384c681307
5c07df93024078656f85769dfd548a2276cde0ad
42312dec52aa1227bfdf3914ff1b201eb9ed6430
ca28434cf70f56bbea40f17824f61581714626b5
c8322158294f56971770c4048468a6475c7e8f43
817de77f7cf14fe19e07ef7b52efccee14850d62
097d29b3ed265e46ea4aa5f94fc920d8ee268a5d
a2a3821dcc6e87cca127878cc7cb7f111ed6baf2
cf4659d067916fdb2cc5038bf5822777fcdda0e7
53c4c74abb23f52989f8f9a6c2f78ca438d40f4a
a4a124e8f193051655364b09d45ed0321b9567ea
53a89cfb8c2448fe748c4ed3307c025da323b6c1
69e8d2e49ee167a3d6f1b96f86398fa1c12acf5d
afb91f3ce31ed8a26549bc1d7c9f4c0d3ef064c6
43ff0c2fc3dc6d3bfde84e13d65f6140165b280b
eda9a8e621b38f3c53214a066aed7a5304c24166
93b33661e87b02797bf086117df6113323dc6765
ddf62ab9e8ccd89543f64789327499d45b544ab2
62fff9f3a6315e997fa73724ea4fe3945712bbbc
45e22642cd49de488c141a5592648095b2517702
54f48761a664a657ac7b910ccc42a4b730c5a0ea
c0d4d9557fac9f5c5609264c9e43d9fa3bb09e47
98ac0d0d392c6099d0693cf3a77518237199b873
2d677ae71924fec4adafb4291956ed6c24196c57
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
Backed out 30 changesets (bug 949220) for assertion failure on JSObject.cpp CLOSED TREE Backed out changeset b077b434866d (bug 949220) Backed out changeset 437dc6f1a0b1 (bug 949220) Backed out changeset 9043d3f4ea6f (bug 949220) Backed out changeset 761005340836 (bug 949220) Backed out changeset 650ccbbc2e15 (bug 949220) Backed out changeset b7459076dbf5 (bug 949220) Backed out changeset e1ac27ee38e2 (bug 949220) Backed out changeset 5c07df930240 (bug 949220) Backed out changeset 42312dec52aa (bug 949220) Backed out changeset ca28434cf70f (bug 949220) Backed out changeset c8322158294f (bug 949220) Backed out changeset 817de77f7cf1 (bug 949220) Backed out changeset 097d29b3ed26 (bug 949220) Backed out changeset a2a3821dcc6e (bug 949220) Backed out changeset cf4659d06791 (bug 949220) Backed out changeset 53c4c74abb23 (bug 949220) Backed out changeset a4a124e8f193 (bug 949220) Backed out changeset 53a89cfb8c24 (bug 949220) Backed out changeset 69e8d2e49ee1 (bug 949220) Backed out changeset afb91f3ce31e (bug 949220) Backed out changeset 43ff0c2fc3dc (bug 949220) Backed out changeset eda9a8e621b3 (bug 949220) Backed out changeset 93b33661e87b (bug 949220) Backed out changeset ddf62ab9e8cc (bug 949220) Backed out changeset 62fff9f3a631 (bug 949220) Backed out changeset 45e22642cd49 (bug 949220) Backed out changeset 54f48761a664 (bug 949220) Backed out changeset c0d4d9557fac (bug 949220) Backed out changeset 98ac0d0d392c (bug 949220) Backed out changeset 2d677ae71924 (bug 949220)
dom/base/WindowNamedPropertiesHandler.cpp
dom/base/nsGlobalWindowOuter.cpp
js/public/Proxy.h
js/public/Wrapper.h
js/rust/src/glue.rs
js/rust/src/jsglue.cpp
js/src/builtin/Array.cpp
js/src/builtin/Array.h
js/src/builtin/AtomicsObject.cpp
js/src/builtin/FinalizationRegistryObject.cpp
js/src/builtin/JSON.cpp
js/src/builtin/MapObject.cpp
js/src/builtin/MapObject.h
js/src/builtin/ModuleObject.cpp
js/src/builtin/Object.cpp
js/src/builtin/Reflect.cpp
js/src/builtin/RegExp.cpp
js/src/builtin/String.cpp
js/src/builtin/TestingFunctions.cpp
js/src/builtin/TypedObject.cpp
js/src/builtin/intl/IntlObject.cpp
js/src/debugger/Debugger-inl.h
js/src/debugger/Debugger.cpp
js/src/debugger/Debugger.h
js/src/debugger/Environment.cpp
js/src/debugger/Object.cpp
js/src/debugger/Script.cpp
js/src/debugger/Source.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/Recover.cpp
js/src/jit/VMFunctionList-inl.h
js/src/jsapi-tests/testBug604087.cpp
js/src/jsfriendapi.cpp
js/src/jsmath.cpp
js/src/proxy/Proxy.cpp
js/src/proxy/Wrapper.cpp
js/src/shell/js.cpp
js/src/vm/EnvironmentObject.cpp
js/src/vm/GeneratorObject.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/Interpreter.cpp
js/src/vm/Iteration.cpp
js/src/vm/Iteration.h
js/src/vm/JSFunction.cpp
js/src/vm/JSObject-inl.h
js/src/vm/JSObject.cpp
js/src/vm/JSObject.h
js/src/vm/JSScript.cpp
js/src/vm/List-inl.h
js/src/vm/NativeObject-inl.h
js/src/vm/ObjectGroup-inl.h
js/src/vm/ObjectGroup.cpp
js/src/vm/ObjectGroup.h
js/src/vm/PIC.cpp
js/src/vm/PIC.h
js/src/vm/ProxyObject.cpp
js/src/vm/ProxyObject.h
js/src/vm/RegExpObject.cpp
js/src/vm/SavedStacks.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/StringObject-inl.h
js/src/vm/TypeInference.cpp
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmTypes.cpp
--- a/dom/base/WindowNamedPropertiesHandler.cpp
+++ b/dom/base/WindowNamedPropertiesHandler.cpp
@@ -220,26 +220,26 @@ static const DOMIfaceAndProtoJSClass Win
     0,
     &sEmptyNativePropertyHooks,
     "[object WindowProperties]",
     EventTarget_Binding::GetProtoObject};
 
 // static
 JSObject* WindowNamedPropertiesHandler::Create(JSContext* aCx,
                                                JS::Handle<JSObject*> aProto) {
-  js::ProxyOptions options;
-  options.setClass(&WindowNamedPropertiesClass.mBase);
-
   // Note: since the scope polluter proxy lives on the window's prototype
   // chain, it needs a singleton type to avoid polluting type information
   // for properties on the window.
-  JS::Rooted<JSObject*> gsp(
-      aCx, js::NewSingletonProxyObject(
-               aCx, WindowNamedPropertiesHandler::getInstance(),
-               JS::NullHandleValue, aProto, options));
+  js::ProxyOptions options;
+  options.setSingleton(true);
+  options.setClass(&WindowNamedPropertiesClass.mBase);
+
+  JS::Rooted<JSObject*> gsp(aCx);
+  gsp = js::NewProxyObject(aCx, WindowNamedPropertiesHandler::getInstance(),
+                           JS::NullHandleValue, aProto, options);
   if (!gsp) {
     return nullptr;
   }
 
   bool succeeded;
   if (!JS_SetImmutablePrototype(aCx, gsp, &succeeded)) {
     return nullptr;
   }
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -1292,21 +1292,22 @@ static JSObject* NewOuterWindowProxy(JSC
                                      JS::Handle<JSObject*> global,
                                      bool isChrome) {
   MOZ_ASSERT(JS_IsGlobalObject(global));
 
   JSAutoRealm ar(cx, global);
 
   js::WrapperOptions options;
   options.setClass(&OuterWindowProxyClass);
+  options.setSingleton(true);
   JSObject* obj =
-      js::Wrapper::NewSingleton(cx, global,
-                                isChrome ? &nsChromeOuterWindowProxy::singleton
-                                         : &nsOuterWindowProxy::singleton,
-                                options);
+      js::Wrapper::New(cx, global,
+                       isChrome ? &nsChromeOuterWindowProxy::singleton
+                                : &nsOuterWindowProxy::singleton,
+                       options);
   MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
   return obj;
 }
 
 //*****************************************************************************
 //***    nsGlobalWindowOuter: Object Management
 //*****************************************************************************
 
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -550,47 +550,52 @@ inline void SetProxyPrivate(JSObject* ob
 
 inline bool IsScriptedProxy(const JSObject* obj) {
   return IsProxy(obj) && GetProxyHandler(obj)->isScripted();
 }
 
 class MOZ_STACK_CLASS ProxyOptions {
  protected:
   /* protected constructor for subclass */
-  explicit ProxyOptions(bool lazyProtoArg)
-      : lazyProto_(lazyProtoArg), clasp_(&ProxyClass) {}
+  explicit ProxyOptions(bool singletonArg, bool lazyProtoArg = false)
+      : singleton_(singletonArg),
+        lazyProto_(lazyProtoArg),
+        clasp_(&ProxyClass) {}
 
  public:
-  ProxyOptions() : ProxyOptions(false) {}
+  ProxyOptions() : singleton_(false), lazyProto_(false), clasp_(&ProxyClass) {}
+
+  bool singleton() const { return singleton_; }
+  ProxyOptions& setSingleton(bool flag) {
+    singleton_ = flag;
+    return *this;
+  }
 
   bool lazyProto() const { return lazyProto_; }
   ProxyOptions& setLazyProto(bool flag) {
     lazyProto_ = flag;
     return *this;
   }
 
   const JSClass* clasp() const { return clasp_; }
   ProxyOptions& setClass(const JSClass* claspArg) {
     clasp_ = claspArg;
     return *this;
   }
 
  private:
+  bool singleton_;
   bool lazyProto_;
   const JSClass* clasp_;
 };
 
 JS_FRIEND_API JSObject* NewProxyObject(
     JSContext* cx, const BaseProxyHandler* handler, HandleValue priv,
     JSObject* proto, const ProxyOptions& options = ProxyOptions());
 
-JS_FRIEND_API JSObject* NewSingletonProxyObject(
-    JSContext* cx, const BaseProxyHandler* handler, HandleValue priv,
-    JSObject* proto, const ProxyOptions& options = ProxyOptions());
-
 JSObject* RenewProxyObject(JSContext* cx, JSObject* obj,
                            BaseProxyHandler* handler, const Value& priv);
 
 class JS_FRIEND_API AutoEnterPolicy {
  public:
   typedef BaseProxyHandler::Action Action;
   AutoEnterPolicy(JSContext* cx, const BaseProxyHandler* handler,
                   HandleObject wrapper, HandleId id, Action act, bool mayThrow)
--- a/js/public/Wrapper.h
+++ b/js/public/Wrapper.h
@@ -146,20 +146,16 @@ class JS_FRIEND_API Wrapper : public For
 
   using BaseProxyHandler::Action;
 
   enum Flags { CROSS_COMPARTMENT = 1 << 0, LAST_USED_FLAG = CROSS_COMPARTMENT };
 
   static JSObject* New(JSContext* cx, JSObject* obj, const Wrapper* handler,
                        const WrapperOptions& options = WrapperOptions());
 
-  static JSObject* NewSingleton(
-      JSContext* cx, JSObject* obj, const Wrapper* handler,
-      const WrapperOptions& options = WrapperOptions());
-
   static JSObject* Renew(JSObject* existing, JSObject* obj,
                          const Wrapper* handler);
 
   static inline const Wrapper* wrapperHandler(const JSObject* wrapper);
 
   static JSObject* wrappedObject(JSObject* wrapper);
 
   unsigned flags() const { return mFlags; }
--- a/js/rust/src/glue.rs
+++ b/js/rust/src/glue.rs
@@ -295,22 +295,17 @@ extern "C" {
         call: *mut JSObject,
         construct: *mut JSObject,
     ) -> *mut JSObject;
     pub fn WrapperNew(
         aCx: *mut JSContext,
         aObj: JS::HandleObject,
         aHandler: *const ::libc::c_void,
         aClass: *const JSClass,
-    ) -> *mut JSObject;
-    pub fn WrapperNewSingleton(
-        aCx: *mut JSContext,
-        aObj: JS::HandleObject,
-        aHandler: *const ::libc::c_void,
-        aClass: *const JSClass,
+        aSingleton: bool,
     ) -> *mut JSObject;
     pub fn NewWindowProxy(
         aCx: *mut JSContext,
         aObj: JS::HandleObject,
         aHandler: *const ::libc::c_void,
     ) -> *mut JSObject;
     pub fn GetWindowProxyClass() -> *const JSClass;
     pub fn GetProxyPrivate(obj: *mut JSObject) -> JS::Value;
--- a/js/rust/src/jsglue.cpp
+++ b/js/rust/src/jsglue.cpp
@@ -449,50 +449,42 @@ JSObject* NewProxyObject(JSContext* aCx,
                          JSObject* parent, JSObject* call,
                          JSObject* construct) {
   js::ProxyOptions options;
   return js::NewProxyObject(aCx, (js::BaseProxyHandler*)aHandler, aPriv, proto,
                             options);
 }
 
 JSObject* WrapperNew(JSContext* aCx, JS::HandleObject aObj,
-                     const void* aHandler, const JSClass* aClass) {
+                     const void* aHandler, const JSClass* aClass,
+                     bool aSingleton) {
   js::WrapperOptions options;
   if (aClass) {
     options.setClass(aClass);
   }
+  options.setSingleton(aSingleton);
   return js::Wrapper::New(aCx, aObj, (const js::Wrapper*)aHandler, options);
 }
 
-JSObject* WrapperNewSingleton(JSContext* aCx, JS::HandleObject aObj,
-                              const void* aHandler, const JSClass* aClass) {
-  js::WrapperOptions options;
-  if (aClass) {
-    options.setClass(aClass);
-  }
-  return js::Wrapper::NewSingleton(aCx, aObj, (const js::Wrapper*)aHandler,
-                                   options);
-}
-
 const JSClass WindowProxyClass = PROXY_CLASS_DEF(
     "Proxy", JSCLASS_HAS_RESERVED_SLOTS(1)); /* additional class flags */
 
 const JSClass* GetWindowProxyClass() { return &WindowProxyClass; }
 
 JS::Value GetProxyReservedSlot(JSObject* obj, uint32_t slot) {
   return js::GetProxyReservedSlot(obj, slot);
 }
 
 void SetProxyReservedSlot(JSObject* obj, uint32_t slot, const JS::Value* val) {
   js::SetProxyReservedSlot(obj, slot, *val);
 }
 
 JSObject* NewWindowProxy(JSContext* aCx, JS::HandleObject aObj,
                          const void* aHandler) {
-  return WrapperNewSingleton(aCx, aObj, aHandler, &WindowProxyClass);
+  return WrapperNew(aCx, aObj, aHandler, &WindowProxyClass, true);
 }
 
 JS::Value GetProxyPrivate(JSObject* obj) { return js::GetProxyPrivate(obj); }
 
 void SetProxyPrivate(JSObject* obj, const JS::Value* expando) {
   js::SetProxyPrivate(obj, *expando);
 }
 
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -45,17 +45,16 @@
 #include "vm/ArgumentsObject-inl.h"
 #include "vm/ArrayObject-inl.h"
 #include "vm/Caches-inl.h"
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/IsGivenTypeObject-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/NativeObject-inl.h"
-#include "vm/ObjectGroup-inl.h"  // JSObject::setSingleton
 
 using namespace js;
 
 using mozilla::Abs;
 using mozilla::ArrayLength;
 using mozilla::CeilingLog2;
 using mozilla::CheckedInt;
 using mozilla::DebugOnly;
@@ -3925,17 +3924,17 @@ static JSObject* CreateArrayPrototype(JS
 
   return arrayProto;
 }
 
 static bool array_proto_finish(JSContext* cx, JS::HandleObject ctor,
                                JS::HandleObject proto) {
   // Add Array.prototype[@@unscopables]. ECMA-262 draft (2016 Mar 19) 22.1.3.32.
   RootedObject unscopables(
-      cx, NewSingletonObjectWithGivenProto<PlainObject>(cx, nullptr));
+      cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, SingletonObject));
   if (!unscopables) {
     return false;
   }
 
   RootedValue value(cx, BooleanValue(true));
   if (!DefineDataProperty(cx, unscopables, cx->names().copyWithin, value) ||
       !DefineDataProperty(cx, unscopables, cx->names().entries, value) ||
       !DefineDataProperty(cx, unscopables, cx->names().fill, value) ||
@@ -4001,19 +4000,19 @@ static inline bool EnsureNewArrayElement
   }
 
   MOZ_ASSERT_IF(cap, !obj->hasDynamicElements());
 
   return true;
 }
 
 template <uint32_t maxLength>
-static MOZ_ALWAYS_INLINE ArrayObject* NewArray(JSContext* cx, uint32_t length,
-                                               HandleObject protoArg,
-                                               NewObjectKind newKind) {
+static MOZ_ALWAYS_INLINE ArrayObject* NewArray(
+    JSContext* cx, uint32_t length, HandleObject protoArg,
+    NewObjectKind newKind = GenericObject) {
   gc::AllocKind allocKind = GuessArrayGCKind(length);
   MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_));
   allocKind = ForegroundToBackgroundAllocKind(allocKind);
 
   RootedObject proto(cx, protoArg);
   if (!proto) {
     proto = GlobalObject::getOrCreateArrayPrototype(cx, cx->global());
     if (!proto) {
@@ -4095,31 +4094,34 @@ static MOZ_ALWAYS_INLINE ArrayObject* Ne
     return nullptr;
   }
 
   probes::CreateObject(cx, arr);
   return arr;
 }
 
 ArrayObject* JS_FASTCALL
-js::NewDenseEmptyArray(JSContext* cx, HandleObject proto /* = nullptr */) {
-  return NewArray<0>(cx, 0, proto, GenericObject);
-}
-
-ArrayObject* JS_FASTCALL js::NewTenuredDenseEmptyArray(
-    JSContext* cx, HandleObject proto /* = nullptr */) {
-  return NewArray<0>(cx, 0, proto, TenuredObject);
+js::NewDenseEmptyArray(JSContext* cx, HandleObject proto /* = nullptr */,
+                       NewObjectKind newKind /* = GenericObject */) {
+  return NewArray<0>(cx, 0, proto, newKind);
 }
 
 ArrayObject* JS_FASTCALL js::NewDenseFullyAllocatedArray(
     JSContext* cx, uint32_t length, HandleObject proto /* = nullptr */,
     NewObjectKind newKind /* = GenericObject */) {
   return NewArray<UINT32_MAX>(cx, length, proto, newKind);
 }
 
+ArrayObject* JS_FASTCALL js::NewDensePartlyAllocatedArray(
+    JSContext* cx, uint32_t length, HandleObject proto /* = nullptr */,
+    NewObjectKind newKind /* = GenericObject */) {
+  return NewArray<ArrayObject::EagerAllocationMaxLength>(cx, length, proto,
+                                                         newKind);
+}
+
 ArrayObject* JS_FASTCALL js::NewDenseUnallocatedArray(
     JSContext* cx, uint32_t length, HandleObject proto /* = nullptr */,
     NewObjectKind newKind /* = GenericObject */) {
   return NewArray<0>(cx, length, proto, newKind);
 }
 
 // values must point at already-rooted Value objects
 ArrayObject* js::NewDenseCopiedArray(
@@ -4137,24 +4139,24 @@ ArrayObject* js::NewDenseCopiedArray(
   if (values) {
     arr->initDenseElements(values, length);
   }
 
   return arr;
 }
 
 ArrayObject* js::NewDenseFullyAllocatedArrayWithTemplate(
-    JSContext* cx, uint32_t length, ArrayObject* templateObject) {
+    JSContext* cx, uint32_t length, JSObject* templateObject) {
   AutoSetNewObjectMetadata metadata(cx);
   gc::AllocKind allocKind = GuessArrayGCKind(length);
   MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_));
   allocKind = ForegroundToBackgroundAllocKind(allocKind);
 
   RootedObjectGroup group(cx, templateObject->group());
-  RootedShape shape(cx, templateObject->lastProperty());
+  RootedShape shape(cx, templateObject->as<ArrayObject>().lastProperty());
 
   gc::InitialHeap heap = GetInitialHeap(GenericObject, group);
   Rooted<ArrayObject*> arr(
       cx, ArrayObject::createArray(cx, allocKind, heap, shape, group, length,
                                    metadata));
   if (!arr) {
     return nullptr;
   }
--- a/js/src/builtin/Array.h
+++ b/js/src/builtin/Array.h
@@ -9,23 +9,20 @@
 #ifndef builtin_Array_h
 #define builtin_Array_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/TextUtils.h"
 
 #include "jspubtd.h"
 
+#include "vm/ArrayObject.h"
 #include "vm/JSObject.h"
-#include "vm/NativeObject.h"  // js::ShouldUpdateTypes
 
 namespace js {
-
-class ArrayObject;
-
 /* 2^32-2, inclusive */
 const uint32_t MAX_ARRAY_INDEX = 4294967294u;
 
 MOZ_ALWAYS_INLINE bool IdIsIndex(jsid id, uint32_t* indexp) {
   if (JSID_IS_INT(id)) {
     int32_t i = JSID_TO_INT(id);
     MOZ_ASSERT(i >= 0);
     *indexp = (uint32_t)i;
@@ -42,47 +39,48 @@ MOZ_ALWAYS_INLINE bool IdIsIndex(jsid id
     return false;
   }
 
   return js::StringIsArrayIndex(atom, indexp);
 }
 
 // The methods below only create dense boxed arrays.
 
-// Create a dense array with no capacity allocated, length set to 0, in the
-// normal (i.e. non-tenured) heap.
+// Create a dense array with no capacity allocated, length set to 0.
 extern ArrayObject* JS_FASTCALL
-NewDenseEmptyArray(JSContext* cx, HandleObject proto = nullptr);
-
-// Create a dense array with no capacity allocated, length set to 0, in the
-// tenured heap.
-extern ArrayObject* JS_FASTCALL
-NewTenuredDenseEmptyArray(JSContext* cx, HandleObject proto = nullptr);
+NewDenseEmptyArray(JSContext* cx, HandleObject proto = nullptr,
+                   NewObjectKind newKind = GenericObject);
 
 // Create a dense array with a set length, but without allocating space for the
 // contents. This is useful, e.g., when accepting length from the user.
 extern ArrayObject* JS_FASTCALL NewDenseUnallocatedArray(
     JSContext* cx, uint32_t length, HandleObject proto = nullptr,
     NewObjectKind newKind = GenericObject);
 
+// Create a dense array with length and capacity == |length|, initialized length
+// set to 0, but with only |EagerAllocationMaxLength| elements allocated.
+extern ArrayObject* JS_FASTCALL NewDensePartlyAllocatedArray(
+    JSContext* cx, uint32_t length, HandleObject proto = nullptr,
+    NewObjectKind newKind = GenericObject);
+
 // Create a dense array with length and capacity == 'length', initialized length
 // set to 0.
 extern ArrayObject* JS_FASTCALL NewDenseFullyAllocatedArray(
     JSContext* cx, uint32_t length, HandleObject proto = nullptr,
     NewObjectKind newKind = GenericObject);
 
 // Create a dense array from the given array values, which must be rooted.
 extern ArrayObject* NewDenseCopiedArray(JSContext* cx, uint32_t length,
                                         const Value* values,
                                         HandleObject proto = nullptr,
                                         NewObjectKind newKind = GenericObject);
 
 // Create a dense array based on templateObject with the given length.
 extern ArrayObject* NewDenseFullyAllocatedArrayWithTemplate(
-    JSContext* cx, uint32_t length, ArrayObject* templateObject);
+    JSContext* cx, uint32_t length, JSObject* templateObject);
 
 // Create a dense array with the same copy-on-write elements as another object.
 extern ArrayObject* NewDenseCopyOnWriteArray(JSContext* cx,
                                              HandleArrayObject templateObject);
 
 extern ArrayObject* NewFullyAllocatedArrayTryUseGroup(
     JSContext* cx, HandleObjectGroup group, size_t length,
     NewObjectKind newKind = GenericObject);
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -975,17 +975,18 @@ static const JSPropertySpec AtomicsPrope
     JS_STRING_SYM_PS(toStringTag, "Atomics", JSPROP_READONLY), JS_PS_END};
 
 static JSObject* CreateAtomicsObject(JSContext* cx, JSProtoKey key) {
   Handle<GlobalObject*> global = cx->global();
   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
-  return NewSingletonObjectWithGivenProto(cx, &AtomicsObject::class_, proto);
+  return NewObjectWithGivenProto(cx, &AtomicsObject::class_, proto,
+                                 SingletonObject);
 }
 
 static const ClassSpec AtomicsClassSpec = {CreateAtomicsObject, nullptr,
                                            AtomicsMethods, AtomicsProperties};
 
 const JSClass AtomicsObject::class_ = {
     "Atomics", JSCLASS_HAS_CACHED_PROTO(JSProto_Atomics), JS_NULL_CLASS_OPS,
     &AtomicsClassSpec};
--- a/js/src/builtin/FinalizationRegistryObject.cpp
+++ b/js/src/builtin/FinalizationRegistryObject.cpp
@@ -40,17 +40,17 @@ const JSClassOps FinalizationRecordObjec
 };
 
 /* static */
 FinalizationRecordObject* FinalizationRecordObject::create(
     JSContext* cx, HandleFinalizationRegistryObject registry,
     HandleValue heldValue) {
   MOZ_ASSERT(registry);
 
-  auto record = NewObjectWithGivenProto<FinalizationRecordObject>(cx, nullptr);
+  auto record = NewObjectWithNullTaggedProto<FinalizationRecordObject>(cx);
   if (!record) {
     return nullptr;
   }
 
   MOZ_ASSERT(registry->compartment() == record->compartment());
 
   record->initReservedSlot(WeakRegistrySlot, PrivateValue(registry));
   record->initReservedSlot(HeldValueSlot, heldValue);
@@ -158,17 +158,17 @@ const JSClassOps FinalizationRegistratio
 FinalizationRegistrationsObject* FinalizationRegistrationsObject::create(
     JSContext* cx) {
   auto records = cx->make_unique<FinalizationRecordVector>(cx->zone());
   if (!records) {
     return nullptr;
   }
 
   auto object =
-      NewObjectWithGivenProto<FinalizationRegistrationsObject>(cx, nullptr);
+      NewObjectWithNullTaggedProto<FinalizationRegistrationsObject>(cx);
   if (!object) {
     return nullptr;
   }
 
   InitReservedSlot(object, RecordsSlot, records.release(),
                    MemoryUse::FinalizationRecordVector);
 
   return object;
--- a/js/src/builtin/JSON.cpp
+++ b/js/src/builtin/JSON.cpp
@@ -1105,17 +1105,17 @@ static const JSPropertySpec json_static_
     JS_STRING_SYM_PS(toStringTag, "JSON", JSPROP_READONLY), JS_PS_END};
 
 static JSObject* CreateJSONObject(JSContext* cx, JSProtoKey key) {
   Handle<GlobalObject*> global = cx->global();
   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
-  return NewSingletonObjectWithGivenProto(cx, &JSONClass, proto);
+  return NewObjectWithGivenProto(cx, &JSONClass, proto, SingletonObject);
 }
 
 static const ClassSpec JSONClassSpec = {
     CreateJSONObject, nullptr, json_static_methods, json_static_properties};
 
 const JSClass js::JSONClass = {js_JSON_str,
                                JSCLASS_HAS_CACHED_PROTO(JSProto_JSON),
                                JS_NULL_CLASS_OPS, &JSONClassSpec};
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -205,48 +205,50 @@ MapIteratorObject* MapIteratorObject::cr
   Handle<MapObject*> mapobj(obj.as<MapObject>());
   Rooted<GlobalObject*> global(cx, &mapobj->global());
   Rooted<JSObject*> proto(
       cx, GlobalObject::getOrCreateMapIteratorPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
 
-  MapIteratorObject* iterobj =
-      NewObjectWithGivenProto<MapIteratorObject>(cx, proto);
-  if (!iterobj) {
-    return nullptr;
-  }
-
-  iterobj->init(mapobj, kind);
+  Nursery& nursery = cx->nursery();
 
-  constexpr size_t BufferSize =
-      RoundUp(sizeof(ValueMap::Range), gc::CellAlignBytes);
-
-  Nursery& nursery = cx->nursery();
-  void* buffer = nursery.allocateBufferSameLocation(iterobj, BufferSize);
-  if (!buffer) {
-    // Retry with |iterobj| and |buffer| forcibly tenured.
-    iterobj = NewTenuredObjectWithGivenProto<MapIteratorObject>(cx, proto);
+  MapIteratorObject* iterobj;
+  void* buffer;
+  NewObjectKind objectKind = GenericObject;
+  while (true) {
+    iterobj = NewObjectWithGivenProto<MapIteratorObject>(cx, proto, objectKind);
     if (!iterobj) {
       return nullptr;
     }
 
-    iterobj->init(mapobj, kind);
+    iterobj->setSlot(TargetSlot, ObjectValue(*mapobj));
+    iterobj->setSlot(RangeSlot, PrivateValue(nullptr));
+    iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
 
-    buffer = nursery.allocateBufferSameLocation(iterobj, BufferSize);
-    if (!buffer) {
+    const size_t size = RoundUp(sizeof(ValueMap::Range), gc::CellAlignBytes);
+    buffer = nursery.allocateBufferSameLocation(iterobj, size);
+    if (buffer) {
+      break;
+    }
+
+    if (!IsInsideNursery(iterobj)) {
       ReportOutOfMemory(cx);
       return nullptr;
     }
+
+    // There was space in the nursery for the object but not the
+    // Range. Try again in the tenured heap.
+    MOZ_ASSERT(objectKind == GenericObject);
+    objectKind = TenuredObject;
   }
 
   bool insideNursery = IsInsideNursery(iterobj);
   MOZ_ASSERT(insideNursery == nursery.isInside(buffer));
-
   if (insideNursery && !HasNurseryMemory(mapobj.get())) {
     if (!cx->nursery().addMapWithNurseryMemory(mapobj)) {
       ReportOutOfMemory(cx);
       return nullptr;
     }
     SetHasNurseryMemory(mapobj.get(), true);
   }
 
@@ -979,48 +981,50 @@ SetIteratorObject* SetIteratorObject::cr
   Handle<SetObject*> setobj(obj.as<SetObject>());
   Rooted<GlobalObject*> global(cx, &setobj->global());
   Rooted<JSObject*> proto(
       cx, GlobalObject::getOrCreateSetIteratorPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
 
-  SetIteratorObject* iterobj =
-      NewObjectWithGivenProto<SetIteratorObject>(cx, proto);
-  if (!iterobj) {
-    return nullptr;
-  }
-
-  iterobj->init(setobj, kind);
+  Nursery& nursery = cx->nursery();
 
-  constexpr size_t BufferSize =
-      RoundUp(sizeof(ValueSet::Range), gc::CellAlignBytes);
-
-  Nursery& nursery = cx->nursery();
-  void* buffer = nursery.allocateBufferSameLocation(iterobj, BufferSize);
-  if (!buffer) {
-    // Retry with |iterobj| and |buffer| forcibly tenured.
-    iterobj = NewTenuredObjectWithGivenProto<SetIteratorObject>(cx, proto);
+  SetIteratorObject* iterobj;
+  void* buffer;
+  NewObjectKind objectKind = GenericObject;
+  while (true) {
+    iterobj = NewObjectWithGivenProto<SetIteratorObject>(cx, proto, objectKind);
     if (!iterobj) {
       return nullptr;
     }
 
-    iterobj->init(setobj, kind);
+    iterobj->setSlot(TargetSlot, ObjectValue(*setobj));
+    iterobj->setSlot(RangeSlot, PrivateValue(nullptr));
+    iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
 
-    buffer = nursery.allocateBufferSameLocation(iterobj, BufferSize);
-    if (!buffer) {
+    const size_t size = RoundUp(sizeof(ValueSet::Range), gc::CellAlignBytes);
+    buffer = nursery.allocateBufferSameLocation(iterobj, size);
+    if (buffer) {
+      break;
+    }
+
+    if (!IsInsideNursery(iterobj)) {
       ReportOutOfMemory(cx);
       return nullptr;
     }
+
+    // There was space in the nursery for the object but not the
+    // Range. Try again in the tenured heap.
+    MOZ_ASSERT(objectKind == GenericObject);
+    objectKind = TenuredObject;
   }
 
   bool insideNursery = IsInsideNursery(iterobj);
   MOZ_ASSERT(insideNursery == nursery.isInside(buffer));
-
   if (insideNursery && !HasNurseryMemory(setobj.get())) {
     if (!cx->nursery().addSetWithNurseryMemory(setobj)) {
       ReportOutOfMemory(cx);
       return nullptr;
     }
     SetHasNurseryMemory(setobj.get(), true);
   }
 
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -196,22 +196,16 @@ class MapIteratorObject : public NativeO
 
   static const JSFunctionSpec methods[];
   static MapIteratorObject* create(JSContext* cx, HandleObject mapobj,
                                    ValueMap* data,
                                    MapObject::IteratorKind kind);
   static void finalize(JSFreeOp* fop, JSObject* obj);
   static size_t objectMoved(JSObject* obj, JSObject* old);
 
-  void init(MapObject* mapObj, MapObject::IteratorKind kind) {
-    initFixedSlot(TargetSlot, JS::ObjectValue(*mapObj));
-    initFixedSlot(RangeSlot, JS::PrivateValue(nullptr));
-    initFixedSlot(KindSlot, JS::Int32Value(int32_t(kind)));
-  }
-
   static MOZ_MUST_USE bool next(Handle<MapIteratorObject*> mapIterator,
                                 HandleArrayObject resultPairObj, JSContext* cx);
 
   static JSObject* createResultPair(JSContext* cx);
 
  private:
   inline MapObject::IteratorKind kind() const;
 };
@@ -315,22 +309,16 @@ class SetIteratorObject : public NativeO
 
   static const JSFunctionSpec methods[];
   static SetIteratorObject* create(JSContext* cx, HandleObject setobj,
                                    ValueSet* data,
                                    SetObject::IteratorKind kind);
   static void finalize(JSFreeOp* fop, JSObject* obj);
   static size_t objectMoved(JSObject* obj, JSObject* old);
 
-  void init(SetObject* setObj, SetObject::IteratorKind kind) {
-    initFixedSlot(TargetSlot, JS::ObjectValue(*setObj));
-    initFixedSlot(RangeSlot, JS::PrivateValue(nullptr));
-    initFixedSlot(KindSlot, JS::Int32Value(int32_t(kind)));
-  }
-
   static MOZ_MUST_USE bool next(Handle<SetIteratorObject*> setIterator,
                                 HandleArrayObject resultObj, JSContext* cx);
 
   static JSObject* createResult(JSContext* cx);
 
  private:
   inline SetObject::IteratorKind kind() const;
 };
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -380,19 +380,20 @@ bool ModuleNamespaceObject::isInstance(H
 
 /* static */
 ModuleNamespaceObject* ModuleNamespaceObject::create(
     JSContext* cx, HandleModuleObject module, HandleObject exports,
     UniquePtr<IndirectBindingMap> bindings) {
   RootedValue priv(cx, ObjectValue(*module));
   ProxyOptions options;
   options.setLazyProto(true);
+  options.setSingleton(true);
   Rooted<UniquePtr<IndirectBindingMap>> rootedBindings(cx, std::move(bindings));
   RootedObject object(
-      cx, NewSingletonProxyObject(cx, &proxyHandler, priv, nullptr, options));
+      cx, NewProxyObject(cx, &proxyHandler, priv, nullptr, options));
   if (!object) {
     return nullptr;
   }
 
   SetProxyReservedSlot(object, ExportsSlot, ObjectValue(*exports));
   SetProxyReservedSlot(object, BindingsSlot,
                        PrivateValue(rootedBindings.release()));
   AddCellMemory(object, sizeof(IndirectBindingMap),
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -993,18 +993,17 @@ PlainObject* js::ObjectCreateImpl(JSCont
       }
     }
 
     MOZ_ASSERT(!ngroup->proto().toObjectOrNull());
 
     return NewObjectWithGroup<PlainObject>(cx, ngroup, allocKind, newKind);
   }
 
-  return NewObjectWithGivenProtoAndKinds<PlainObject>(cx, proto, allocKind,
-                                                      newKind);
+  return NewObjectWithGivenProto<PlainObject>(cx, proto, allocKind, newKind);
 }
 
 PlainObject* js::ObjectCreateWithTemplate(JSContext* cx,
                                           HandlePlainObject templateObj) {
   RootedObject proto(cx, templateObj->staticPrototype());
   RootedObjectGroup group(cx, templateObj->group());
   return ObjectCreateImpl(cx, proto, GenericObject, group);
 }
@@ -2009,17 +2008,17 @@ static JSObject* CreateObjectPrototype(J
   MOZ_ASSERT(!cx->zone()->isAtomsZone());
   MOZ_ASSERT(cx->global()->isNative());
 
   /*
    * Create |Object.prototype| first, mirroring CreateBlankProto but for the
    * prototype of the created object.
    */
   RootedPlainObject objectProto(
-      cx, NewSingletonObjectWithGivenProto<PlainObject>(cx, nullptr));
+      cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, SingletonObject));
   if (!objectProto) {
     return nullptr;
   }
 
   bool succeeded;
   if (!SetImmutablePrototype(cx, objectProto, &succeeded)) {
     return nullptr;
   }
--- a/js/src/builtin/Reflect.cpp
+++ b/js/src/builtin/Reflect.cpp
@@ -215,16 +215,16 @@ static const JSFunctionSpec reflect_meth
 /*** Setup ******************************************************************/
 
 static JSObject* CreateReflectObject(JSContext* cx, JSProtoKey key) {
   Handle<GlobalObject*> global = cx->global();
   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
-  return NewSingletonObjectWithGivenProto<PlainObject>(cx, proto);
+  return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
 }
 
 static const ClassSpec ReflectClassSpec = {CreateReflectObject, nullptr,
                                            reflect_methods, nullptr};
 
 const JSClass js::ReflectClass = {"Reflect", 0, JS_NULL_CLASS_OPS,
                                   &ReflectClassSpec};
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -57,17 +57,17 @@ bool js::CreateRegExpMatchResult(JSConte
    *  0:              matched string
    *  1..pairCount-1: paren matches
    *  input:          input string
    *  index:          start index for the match
    */
 
   // Get the templateObject that defines the shape and type of the output
   // object.
-  ArrayObject* templateObject =
+  JSObject* templateObject =
       cx->realm()->regExps.getOrCreateMatchResultTemplateObject(cx);
   if (!templateObject) {
     return false;
   }
 
   size_t numPairs = matches.length();
   MOZ_ASSERT(numPairs > 0);
 
--- a/js/src/builtin/String.cpp
+++ b/js/src/builtin/String.cpp
@@ -4450,17 +4450,17 @@ static bool BuildFlatMatchArray(JSContex
                                 MutableHandleValue rval) {
   if (match < 0) {
     rval.setNull();
     return true;
   }
 
   // Get the templateObject that defines the shape and type of the output
   // object.
-  ArrayObject* templateObject =
+  JSObject* templateObject =
       cx->realm()->regExps.getOrCreateMatchResultTemplateObject(cx);
   if (!templateObject) {
     return false;
   }
 
   RootedArrayObject arr(
       cx, NewDenseFullyAllocatedArrayWithTemplate(cx, 1, templateObject));
   if (!arr) {
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -4519,41 +4519,34 @@ static bool GetConstructorName(JSContext
   if (name) {
     args.rval().setString(name);
   } else {
     args.rval().setNull();
   }
   return true;
 }
 
-class AllocationMarkerObject : public NativeObject {
- public:
-  static const JSClass class_;
-};
-
-const JSClass AllocationMarkerObject::class_ = {"AllocationMarker"};
-
 static bool AllocationMarker(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   bool allocateInsideNursery = true;
   if (args.length() > 0 && args[0].isObject()) {
     RootedObject options(cx, &args[0].toObject());
 
     RootedValue nurseryVal(cx);
     if (!JS_GetProperty(cx, options, "nursery", &nurseryVal)) {
       return false;
     }
     allocateInsideNursery = ToBoolean(nurseryVal);
   }
 
-  JSObject* obj =
-      allocateInsideNursery
-          ? NewObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr)
-          : NewTenuredObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr);
+  static const JSClass cls = {"AllocationMarker"};
+
+  auto newKind = allocateInsideNursery ? GenericObject : TenuredObject;
+  RootedObject obj(cx, NewObjectWithGivenProto(cx, &cls, nullptr, newKind));
   if (!obj) {
     return false;
   }
 
   args.rval().setObject(*obj);
   return true;
 }
 
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -501,18 +501,18 @@ bool js::ReferenceTypeDescr::call(JSCont
  */
 static TypedProto* CreatePrototypeObjectForComplexTypeInstance(
     JSContext* cx, HandleObject ctorPrototype) {
   RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype));
   if (!ctorPrototypePrototype) {
     return nullptr;
   }
 
-  return NewSingletonObjectWithGivenProto<TypedProto>(cx,
-                                                      ctorPrototypePrototype);
+  return NewObjectWithGivenProto<TypedProto>(cx, ctorPrototypePrototype,
+                                             SingletonObject);
 }
 
 static const JSClassOps ArrayTypeDescrClassOps = {
     nullptr,                 // addProperty
     nullptr,                 // delProperty
     nullptr,                 // enumerate
     nullptr,                 // newEnumerate
     nullptr,                 // resolve
@@ -599,18 +599,18 @@ static bool CreateTraceList(JSContext* c
 
 ArrayTypeDescr* ArrayMetaTypeDescr::create(JSContext* cx,
                                            HandleObject arrayTypePrototype,
                                            HandleTypeDescr elementType,
                                            HandleAtom stringRepr, int32_t size,
                                            int32_t length) {
   MOZ_ASSERT(arrayTypePrototype);
   Rooted<ArrayTypeDescr*> obj(cx);
-  obj =
-      NewSingletonObjectWithGivenProto<ArrayTypeDescr>(cx, arrayTypePrototype);
+  obj = NewObjectWithGivenProto<ArrayTypeDescr>(cx, arrayTypePrototype,
+                                                SingletonObject);
   if (!obj) {
     return nullptr;
   }
 
   obj->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(ArrayTypeDescr::Kind));
   obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
   obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT,
                         Int32Value(elementType->alignment()));
@@ -905,22 +905,22 @@ StructTypeDescr* StructMetaTypeDescr::cr
   StringBuffer stringBuffer(cx);       // Canonical string repr
   RootedValueVector fieldNames(cx);    // Name of each field.
   RootedValueVector fieldOffsets(cx);  // Offset of each field field.
   RootedValueVector fieldMuts(cx);     // Mutability of each field.
   RootedObject userFieldOffsets(cx);   // User-exposed {f:offset} object
   RootedObject userFieldTypes(cx);     // User-exposed {f:descr} object.
   Layout layout;                       // Field offsetter
 
-  userFieldOffsets = NewTenuredBuiltinClassInstance<PlainObject>(cx);
+  userFieldOffsets = NewBuiltinClassInstance<PlainObject>(cx, TenuredObject);
   if (!userFieldOffsets) {
     return nullptr;
   }
 
-  userFieldTypes = NewTenuredBuiltinClassInstance<PlainObject>(cx);
+  userFieldTypes = NewBuiltinClassInstance<PlainObject>(cx, TenuredObject);
   if (!userFieldTypes) {
     return nullptr;
   }
 
   if (!stringBuffer.append("new StructType({")) {
     return nullptr;
   }
 
@@ -999,18 +999,18 @@ StructTypeDescr* StructMetaTypeDescr::cr
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_TYPEDOBJECT_TOO_BIG);
     return nullptr;
   }
 
   // Now create the resulting type descriptor.
 
   Rooted<StructTypeDescr*> descr(cx);
-  descr = NewSingletonObjectWithGivenProto<StructTypeDescr>(
-      cx, structTypePrototype);
+  descr = NewObjectWithGivenProto<StructTypeDescr>(cx, structTypePrototype,
+                                                   SingletonObject);
   if (!descr) {
     return nullptr;
   }
 
   descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Struct));
   descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
   descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT,
                           Int32Value(AssertedCast<int32_t>(alignment)));
@@ -1253,17 +1253,18 @@ static bool DefineSimpleTypeDescr(JSCont
   }
 
   RootedObject funcProto(
       cx, GlobalObject::getOrCreateFunctionPrototype(cx, global));
   if (!funcProto) {
     return false;
   }
 
-  Rooted<T*> descr(cx, NewSingletonObjectWithGivenProto<T>(cx, funcProto));
+  Rooted<T*> descr(cx);
+  descr = NewObjectWithGivenProto<T>(cx, funcProto, SingletonObject);
   if (!descr) {
     return false;
   }
 
   descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
   descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(className));
   descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT,
                           Int32Value(T::alignment(type)));
@@ -1279,17 +1280,17 @@ static bool DefineSimpleTypeDescr(JSCont
 
   if (!JS_DefineFunctions(cx, descr, T::typeObjectMethods)) {
     return false;
   }
 
   // Create the typed prototype for the scalar type. This winds up
   // not being user accessible, but we still create one for consistency.
   Rooted<TypedProto*> proto(cx);
-  proto = NewTenuredObjectWithGivenProto<TypedProto>(cx, objProto);
+  proto = NewObjectWithGivenProto<TypedProto>(cx, objProto, TenuredObject);
   if (!proto) {
     return false;
   }
   descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto));
 
   RootedValue descrValue(cx, ObjectValue(*descr));
   if (!DefineDataProperty(cx, module, className, descrValue, 0)) {
     return false;
@@ -1322,30 +1323,31 @@ static JSObject* DefineMetaTypeDescr(JSC
       cx, GlobalObject::getOrCreateFunctionPrototype(cx, global));
   if (!funcProto) {
     return nullptr;
   }
 
   // Create ctor.prototype, which inherits from Function.__proto__
 
   RootedObject proto(
-      cx, NewSingletonObjectWithGivenProto<PlainObject>(cx, funcProto));
+      cx, NewObjectWithGivenProto<PlainObject>(cx, funcProto, SingletonObject));
   if (!proto) {
     return nullptr;
   }
 
   // Create ctor.prototype.prototype, which inherits from Object.__proto__
 
   RootedObject objProto(cx,
                         GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!objProto) {
     return nullptr;
   }
   RootedObject protoProto(cx);
-  protoProto = NewSingletonObjectWithGivenProto<PlainObject>(cx, objProto);
+  protoProto =
+      NewObjectWithGivenProto<PlainObject>(cx, objProto, SingletonObject);
   if (!protoProto) {
     return nullptr;
   }
 
   RootedValue protoProtoValue(cx, ObjectValue(*protoProto));
   if (!DefineDataProperty(cx, proto, cx->names().prototype, protoProtoValue,
                           JSPROP_READONLY | JSPROP_PERMANENT)) {
     return nullptr;
@@ -1373,18 +1375,18 @@ static JSObject* DefineMetaTypeDescr(JSC
 static JSObject* CreateTypedObjectModuleObject(JSContext* cx, JSProtoKey key) {
   Handle<GlobalObject*> global = cx->global();
   RootedObject objProto(cx,
                         GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!objProto) {
     return nullptr;
   }
 
-  return NewSingletonObjectWithGivenProto<TypedObjectModuleObject>(cx,
-                                                                   objProto);
+  return NewObjectWithGivenProto<TypedObjectModuleObject>(cx, objProto,
+                                                          SingletonObject);
 }
 
 /*  The initialization strategy for TypedObjects is mildly unusual
  * compared to other classes. Because all of the types are members
  * of a single global, `TypedObject`, we basically make the
  * initializer for the `TypedObject` class populate the
  * `TypedObject` global (which is referred to as "module" herein).
  */
--- a/js/src/builtin/intl/IntlObject.cpp
+++ b/js/src/builtin/intl/IntlObject.cpp
@@ -795,17 +795,17 @@ static JSObject* CreateIntlObject(JSCont
   Handle<GlobalObject*> global = cx->global();
   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
 
   // The |Intl| object is just a plain object with some "static" function
   // properties and some constructor properties.
-  return NewSingletonObjectWithGivenProto(cx, &IntlClass, proto);
+  return NewObjectWithGivenProto(cx, &IntlClass, proto, SingletonObject);
 }
 
 /**
  * Initializes the Intl Object and its standard built-in properties.
  * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
  */
 static bool IntlClassFinish(JSContext* cx, HandleObject intl,
                             HandleObject proto) {
--- a/js/src/debugger/Debugger-inl.h
+++ b/js/src/debugger/Debugger-inl.h
@@ -11,17 +11,17 @@
 
 #include "mozilla/Assertions.h"  // for AssertionConditionType
 
 #include "vm/JSObject.h"      // for JSObject
 #include "vm/NativeObject.h"  // for NativeObject, JSObject::is
 
 /* static */ inline js::Debugger* js::Debugger::fromJSObject(
     const JSObject* obj) {
-  MOZ_ASSERT(obj->is<DebuggerInstanceObject>());
+  MOZ_ASSERT(obj->getClass() == &class_);
   return (Debugger*)obj->as<NativeObject>().getPrivate();
 }
 
 inline bool js::Debugger::isHookCallAllowed(JSContext* cx) const {
   // If we are evaluating inside of an eval on a debugger that has an
   // onNativeCall hook, we want to _only_ call the hooks attached to that
   // specific debugger.
   return !cx->insideDebuggerEvaluationWithOnNativeCallHook ||
--- a/js/src/debugger/Debugger.cpp
+++ b/js/src/debugger/Debugger.cpp
@@ -114,17 +114,17 @@
 #include "debugger/Script-inl.h"   // for DebuggerScript::getReferent
 #include "gc/GC-inl.h"             // for ZoneCellIter
 #include "gc/Marking-inl.h"        // for MaybeForwarded
 #include "gc/WeakMap-inl.h"        // for DebuggerWeakMap::trace
 #include "vm/Compartment-inl.h"    // for Compartment::wrap
 #include "vm/GeckoProfiler-inl.h"  // for AutoSuppressProfilerSampling
 #include "vm/JSAtom-inl.h"         // for AtomToId, ValueToId
 #include "vm/JSContext-inl.h"      // for JSContext::check
-#include "vm/JSObject-inl.h"  // for JSObject::isCallable, NewTenuredObjectWithGivenProto
+#include "vm/JSObject-inl.h"       // for JSObject::isCallable
 #include "vm/JSScript-inl.h"       // for JSScript::isDebuggee, JSScript
 #include "vm/NativeObject-inl.h"  // for NativeObject::ensureDenseInitializedLength
 #include "vm/ObjectOperations-inl.h"  // for GetProperty, HasProperty
 #include "vm/Realm-inl.h"             // for AutoRealm::AutoRealm
 #include "vm/Stack-inl.h"             // for AbstractFramePtr::script
 #include "vm/TypeInference-inl.h"     // for AutoEnterAnalysis
 
 namespace js {
@@ -3914,43 +3914,42 @@ bool DebuggerWeakMap<UnbarrieredKey, Wra
         !SweepZonesInSameGroup(debuggerZone, keyZone)) {
       return false;
     }
   }
 
   return true;
 }
 
-const JSClassOps DebuggerInstanceObject::classOps_ = {
+const JSClassOps Debugger::classOps_ = {
     nullptr,                // addProperty
     nullptr,                // delProperty
     nullptr,                // enumerate
     nullptr,                // newEnumerate
     nullptr,                // resolve
     nullptr,                // mayResolve
     nullptr,                // finalize
     nullptr,                // call
     nullptr,                // hasInstance
     nullptr,                // construct
     Debugger::traceObject,  // trace
 };
 
-const JSClass DebuggerInstanceObject::class_ = {
+const JSClass Debugger::class_ = {
     "Debugger",
-    JSCLASS_HAS_PRIVATE |
-        JSCLASS_HAS_RESERVED_SLOTS(Debugger::JSSLOT_DEBUG_COUNT),
-    &classOps_};
+    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
+    &Debugger::classOps_};
 
 static Debugger* Debugger_fromThisValue(JSContext* cx, const CallArgs& args,
                                         const char* fnname) {
   JSObject* thisobj = RequireObject(cx, args.thisv());
   if (!thisobj) {
     return nullptr;
   }
-  if (!thisobj->is<DebuggerInstanceObject>()) {
+  if (thisobj->getClass() != &Debugger::class_) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger", fnname,
                               thisobj->getClass()->name);
     return nullptr;
   }
 
   // Forbid Debugger.prototype, which is of the Debugger JSClass but isn't
   // really a Debugger object. The prototype object is distinguished by
@@ -4480,23 +4479,23 @@ bool Debugger::construct(JSContext* cx, 
 
   // Get Debugger.prototype.
   RootedValue v(cx);
   RootedObject callee(cx, &args.callee());
   if (!GetProperty(cx, callee, callee, cx->names().prototype, &v)) {
     return false;
   }
   RootedNativeObject proto(cx, &v.toObject().as<NativeObject>());
-  MOZ_ASSERT(proto->is<DebuggerInstanceObject>());
+  MOZ_ASSERT(proto->getClass() == &Debugger::class_);
 
   // Make the new Debugger object. Each one has a reference to
   // Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The
   // rest of the reserved slots are for hooks; they default to undefined.
-  Rooted<DebuggerInstanceObject*> obj(
-      cx, NewTenuredObjectWithGivenProto<DebuggerInstanceObject>(cx, proto));
+  RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(
+                                 cx, &Debugger::class_, proto, TenuredObject));
   if (!obj) {
     return false;
   }
   for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP;
        slot++) {
     obj->setReservedSlot(slot, proto->getReservedSlot(slot));
   }
   obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue());
@@ -6510,19 +6509,19 @@ extern JS_PUBLIC_API bool JS_DefineDebug
   RootedNativeObject debugCtor(cx), debugProto(cx), frameProto(cx),
       scriptProto(cx), sourceProto(cx), objectProto(cx), envProto(cx),
       memoryProto(cx);
   RootedObject debuggeeWouldRunProto(cx);
   RootedValue debuggeeWouldRunCtor(cx);
   Handle<GlobalObject*> global = obj.as<GlobalObject>();
 
   debugProto =
-      InitClass(cx, global, nullptr, &DebuggerInstanceObject::class_,
-                Debugger::construct, 1, Debugger::properties, Debugger::methods,
-                nullptr, Debugger::static_methods, debugCtor.address());
+      InitClass(cx, global, nullptr, &Debugger::class_, Debugger::construct, 1,
+                Debugger::properties, Debugger::methods, nullptr,
+                Debugger::static_methods, debugCtor.address());
   if (!debugProto) {
     return false;
   }
 
   frameProto = DebuggerFrame::initClass(cx, global, debugCtor);
   if (!frameProto) {
     return false;
   }
@@ -6581,17 +6580,17 @@ extern JS_PUBLIC_API bool JS_DefineDebug
   debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_MEMORY_PROTO,
                               ObjectValue(*memoryProto));
   return true;
 }
 
 JS_PUBLIC_API bool JS::dbg::IsDebugger(JSObject& obj) {
   /* We only care about debugger objects, so CheckedUnwrapStatic is OK. */
   JSObject* unwrapped = CheckedUnwrapStatic(&obj);
-  return unwrapped && unwrapped->is<DebuggerInstanceObject>() &&
+  return unwrapped && unwrapped->getClass() == &Debugger::class_ &&
          js::Debugger::fromJSObject(unwrapped) != nullptr;
 }
 
 JS_PUBLIC_API bool JS::dbg::GetDebuggeeGlobals(
     JSContext* cx, JSObject& dbgObj, MutableHandleObjectVector vector) {
   MOZ_ASSERT(IsDebugger(dbgObj));
   /* Since we know we have a debugger object, CheckedUnwrapStatic is fine. */
   js::Debugger* dbg = js::Debugger::fromJSObject(CheckedUnwrapStatic(&dbgObj));
--- a/js/src/debugger/Debugger.h
+++ b/js/src/debugger/Debugger.h
@@ -495,30 +495,21 @@ class MOZ_RAII DebuggerList {
   void dispatchQuietHook(JSContext* cx, FireHookFun fireHook);
 
   template <typename FireHookFun /* bool (Debugger*, ResumeMode&, MutableHandleValue) */>
   MOZ_MUST_USE bool dispatchResumptionHook(JSContext* cx,
                                            AbstractFramePtr frame,
                                            FireHookFun fireHook);
 };
 
-class DebuggerInstanceObject : public NativeObject {
- private:
-  static const JSClassOps classOps_;
-
- public:
-  static const JSClass class_;
-};
-
 class Debugger : private mozilla::LinkedListElement<Debugger> {
   friend class DebugAPI;
   friend class Breakpoint;
   friend class DebuggerFrame;
   friend class DebuggerMemory;
-  friend class DebuggerInstanceObject;
 
   template <typename>
   friend class DebuggerList;
   friend struct JSRuntime::GlobalObjectWatchersLinkAccess<Debugger>;
   friend class SavedStacks;
   friend class ScriptedOnStepHandler;
   friend class ScriptedOnPopHandler;
   friend class mozilla::LinkedListElement<Debugger>;
@@ -871,16 +862,21 @@ class Debugger : private mozilla::Linked
   static void traceObject(JSTracer* trc, JSObject* obj);
 
   void trace(JSTracer* trc);
   friend struct js::GCManagedDeletePolicy<Debugger>;
 
   void traceForMovingGC(JSTracer* trc);
   void traceCrossCompartmentEdges(JSTracer* tracer);
 
+  static const JSClassOps classOps_;
+
+ public:
+  static const JSClass class_;
+
  private:
   template <typename F>
   void forEachWeakMap(const F& f);
 
   static MOZ_MUST_USE bool getHookImpl(JSContext* cx, const CallArgs& args,
                                        Debugger& dbg, Hook which);
   static MOZ_MUST_USE bool setHookImpl(JSContext* cx, const CallArgs& args,
                                        Debugger& dbg, Hook which);
--- a/js/src/debugger/Environment.cpp
+++ b/js/src/debugger/Environment.cpp
@@ -31,17 +31,17 @@
 #include "vm/NativeObject.h"       // for NativeObject, JSObject::is
 #include "vm/ObjectGroup.h"        // for GenericObject, NewObjectKind
 #include "vm/Realm.h"              // for AutoRealm, ErrorCopier
 #include "vm/Scope.h"              // for ScopeKind, ScopeKindString
 #include "vm/StringType.h"         // for JSAtom
 
 #include "vm/Compartment-inl.h"        // for Compartment::wrap
 #include "vm/EnvironmentObject-inl.h"  // for JSObject::enclosingEnvironment
-#include "vm/JSObject-inl.h"  // for IsInternalFunctionObject, NewObjectWithGivenProtoAndKind
+#include "vm/JSObject-inl.h"           // for IsInternalFunctionObject
 #include "vm/ObjectOperations-inl.h"   // for HasProperty, GetProperty
 #include "vm/Realm-inl.h"              // for AutoRealm::AutoRealm
 
 namespace js {
 class GlobalObject;
 }
 
 using namespace js;
@@ -394,20 +394,20 @@ NativeObject* DebuggerEnvironment::initC
                    construct, 0, properties_, methods_, nullptr, nullptr);
 }
 
 /* static */
 DebuggerEnvironment* DebuggerEnvironment::create(JSContext* cx,
                                                  HandleObject proto,
                                                  HandleObject referent,
                                                  HandleNativeObject debugger) {
+  NewObjectKind newKind =
+      IsInsideNursery(referent) ? GenericObject : TenuredObject;
   DebuggerEnvironment* obj =
-      IsInsideNursery(referent)
-          ? NewObjectWithGivenProto<DebuggerEnvironment>(cx, proto)
-          : NewTenuredObjectWithGivenProto<DebuggerEnvironment>(cx, proto);
+      NewObjectWithGivenProto<DebuggerEnvironment>(cx, proto, newKind);
   if (!obj) {
     return nullptr;
   }
 
   obj->setPrivateGCThing(referent);
   obj->setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));
 
   return obj;
--- a/js/src/debugger/Object.cpp
+++ b/js/src/debugger/Object.cpp
@@ -63,17 +63,17 @@
 #include "vm/SelfHosting.h"               // for GetClonedSelfHostedFunctionName
 #include "vm/Shape.h"                     // for Shape
 #include "vm/Stack.h"                     // for InvokeArgs
 #include "vm/StringType.h"                // for JSAtom, PropertyName
 #include "vm/WrapperObject.h"             // for JSObject::is, WrapperObject
 
 #include "vm/Compartment-inl.h"       // for Compartment::wrap
 #include "vm/JSAtom-inl.h"            // for ValueToId
-#include "vm/JSObject-inl.h"  // for GetObjectClassName, InitClass, NewObjectWithGivenProtoAndKind
+#include "vm/JSObject-inl.h"          // for GetObjectClassName, InitClass
 #include "vm/NativeObject-inl.h"      // for NativeObject::global
 #include "vm/ObjectOperations-inl.h"  // for DeleteProperty, GetProperty
 #include "vm/Realm-inl.h"             // for AutoRealm::AutoRealm
 
 using namespace js;
 
 using JS::AutoStableStringChars;
 using mozilla::Maybe;
@@ -1613,20 +1613,20 @@ NativeObject* DebuggerObject::initClass(
 
   return objectProto;
 }
 
 /* static */
 DebuggerObject* DebuggerObject::create(JSContext* cx, HandleObject proto,
                                        HandleObject referent,
                                        HandleNativeObject debugger) {
+  NewObjectKind newKind =
+      IsInsideNursery(referent) ? GenericObject : TenuredObject;
   DebuggerObject* obj =
-      IsInsideNursery(referent)
-          ? NewObjectWithGivenProto<DebuggerObject>(cx, proto)
-          : NewTenuredObjectWithGivenProto<DebuggerObject>(cx, proto);
+      NewObjectWithGivenProto<DebuggerObject>(cx, proto, newKind);
   if (!obj) {
     return nullptr;
   }
 
   obj->setPrivateGCThing(referent);
   obj->setReservedSlot(JSSLOT_DEBUGOBJECT_OWNER, ObjectValue(*debugger));
 
   return obj;
--- a/js/src/debugger/Script.cpp
+++ b/js/src/debugger/Script.cpp
@@ -31,27 +31,28 @@
 #include "js/HeapAPI.h"        // for GCCellPtr
 #include "js/Wrapper.h"        // for UncheckedUnwrap
 #include "vm/ArrayObject.h"    // for ArrayObject
 #include "vm/BytecodeUtil.h"   // for GET_JUMP_OFFSET
 #include "vm/GlobalObject.h"   // for GlobalObject
 #include "vm/JSContext.h"      // for JSContext, ReportValueError
 #include "vm/JSFunction.h"     // for JSFunction
 #include "vm/JSObject.h"       // for RequireObject, JSObject
+#include "vm/ObjectGroup.h"    // for TenuredObject
 #include "vm/ObjectOperations.h"  // for DefineDataProperty, HasOwnProperty
 #include "vm/Realm.h"             // for AutoRealm
 #include "vm/Runtime.h"           // for JSAtomState, JSRuntime
 #include "vm/StringType.h"        // for NameToId, PropertyName, JSAtom
 #include "wasm/WasmDebug.h"       // for ExprLoc, DebugState
 #include "wasm/WasmInstance.h"    // for Instance
 #include "wasm/WasmTypes.h"       // for Bytes
 
-#include "vm/BytecodeUtil-inl.h"  // for BytecodeRangeWithPosition
-#include "vm/JSAtom-inl.h"        // for ValueToId
-#include "vm/JSObject-inl.h"  // for NewBuiltinClassInstance, NewObjectWithGivenProto, NewTenuredObjectWithGivenProto
+#include "vm/BytecodeUtil-inl.h"      // for BytecodeRangeWithPosition
+#include "vm/JSAtom-inl.h"            // for ValueToId
+#include "vm/JSObject-inl.h"          // for NewBuiltinClassInstance
 #include "vm/ObjectOperations-inl.h"  // for GetProperty
 #include "vm/Realm-inl.h"             // for AutoRealm::AutoRealm
 
 using namespace js;
 
 using mozilla::Maybe;
 using mozilla::Some;
 
@@ -101,17 +102,17 @@ NativeObject* DebuggerScript::initClass(
                    methods_, nullptr, nullptr);
 }
 
 /* static */
 DebuggerScript* DebuggerScript::create(JSContext* cx, HandleObject proto,
                                        Handle<DebuggerScriptReferent> referent,
                                        HandleNativeObject debugger) {
   DebuggerScript* scriptobj =
-      NewTenuredObjectWithGivenProto<DebuggerScript>(cx, proto);
+      NewObjectWithGivenProto<DebuggerScript>(cx, proto, TenuredObject);
   if (!scriptobj) {
     return nullptr;
   }
 
   scriptobj->setReservedSlot(DebuggerScript::OWNER_SLOT,
                              ObjectValue(*debugger));
   referent.get().match(
       [&](auto& scriptHandle) { scriptobj->setPrivateGCThing(scriptHandle); });
--- a/js/src/debugger/Source.cpp
+++ b/js/src/debugger/Source.cpp
@@ -30,17 +30,17 @@
 #include "vm/TypedArrayObject.h"  // for TypedArrayObject, JSObject::is
 #include "wasm/WasmCode.h"        // for Metadata
 #include "wasm/WasmDebug.h"       // for DebugState
 #include "wasm/WasmInstance.h"    // for Instance
 #include "wasm/WasmJS.h"          // for WasmInstanceObject
 #include "wasm/WasmTypes.h"       // for Bytes, RootedWasmInstanceObject
 
 #include "vm/JSObject-inl.h"      // for InitClass
-#include "vm/NativeObject-inl.h"  // for NewTenuredObjectWithGivenProto
+#include "vm/NativeObject-inl.h"  // for NewNativeObjectWithGivenProto
 
 namespace js {
 class GlobalObject;
 }
 
 using namespace js;
 
 using mozilla::AsVariant;
@@ -73,21 +73,22 @@ NativeObject* DebuggerSource::initClass(
   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) {
-  Rooted<DebuggerSource*> sourceObj(
-      cx, NewTenuredObjectWithGivenProto<DebuggerSource>(cx, proto));
-  if (!sourceObj) {
+  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.
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -5328,27 +5328,27 @@ bool CallIRGenerator::getTemplateObjectF
         return true;
       }
       RootedObject proto(cx_, args_[0].toObjectOrNull());
       res.set(ObjectCreateImpl(cx_, proto, TenuredObject));
       return !!res;
     }
 
     case InlinableNative::IntrinsicNewArrayIterator: {
-      res.set(NewArrayIteratorTemplate(cx_));
+      res.set(NewArrayIteratorObject(cx_, TenuredObject));
       return !!res;
     }
 
     case InlinableNative::IntrinsicNewStringIterator: {
-      res.set(NewStringIteratorTemplate(cx_));
+      res.set(NewStringIteratorObject(cx_, TenuredObject));
       return !!res;
     }
 
     case InlinableNative::IntrinsicNewRegExpStringIterator: {
-      res.set(NewRegExpStringIteratorTemplate(cx_));
+      res.set(NewRegExpStringIteratorObject(cx_, TenuredObject));
       return !!res;
     }
 
     case InlinableNative::TypedArrayConstructor: {
       return TypedArrayObject::GetTemplateObjectForNative(
           cx_, calleeFunc->native(), args_, res);
     }
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3731,19 +3731,20 @@ void CodeGenerator::visitTableSwitchV(LT
   masm.unboxInt32(value, index);
 
   masm.bind(&isInt);
 
   emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer()));
 }
 
 void CodeGenerator::visitCloneLiteral(LCloneLiteral* lir) {
+  pushArg(ImmWord(TenuredObject));
   pushArg(ToRegister(lir->getObjectLiteral()));
 
-  using Fn = JSObject* (*)(JSContext*, HandleObject);
+  using Fn = JSObject* (*)(JSContext*, HandleObject, NewObjectKind);
   callVM<Fn, DeepCloneObjectLiteral>(lir);
 }
 
 void CodeGenerator::visitParameter(LParameter* lir) {}
 
 void CodeGenerator::visitCallee(LCallee* lir) {
   Register callee = ToRegister(lir->output());
   Address ptr(masm.getStackPointer(),
@@ -6733,31 +6734,31 @@ void CodeGenerator::visitNewArrayDynamic
 
 void CodeGenerator::visitNewIterator(LNewIterator* lir) {
   Register objReg = ToRegister(lir->output());
   Register tempReg = ToRegister(lir->temp());
 
   OutOfLineCode* ool;
   switch (lir->mir()->type()) {
     case MNewIterator::ArrayIterator: {
-      using Fn = ArrayIteratorObject* (*)(JSContext*);
-      ool = oolCallVM<Fn, NewArrayIterator>(lir, ArgList(),
-                                            StoreRegisterTo(objReg));
+      using Fn = ArrayIteratorObject* (*)(JSContext*, NewObjectKind);
+      ool = oolCallVM<Fn, NewArrayIteratorObject>(
+          lir, ArgList(Imm32(GenericObject)), StoreRegisterTo(objReg));
       break;
     }
     case MNewIterator::StringIterator: {
-      using Fn = StringIteratorObject* (*)(JSContext*);
-      ool = oolCallVM<Fn, NewStringIterator>(lir, ArgList(),
-                                             StoreRegisterTo(objReg));
+      using Fn = StringIteratorObject* (*)(JSContext*, NewObjectKind);
+      ool = oolCallVM<Fn, NewStringIteratorObject>(
+          lir, ArgList(Imm32(GenericObject)), StoreRegisterTo(objReg));
       break;
     }
     case MNewIterator::RegExpStringIterator: {
-      using Fn = RegExpStringIteratorObject* (*)(JSContext*);
-      ool = oolCallVM<Fn, NewRegExpStringIterator>(lir, ArgList(),
-                                                   StoreRegisterTo(objReg));
+      using Fn = RegExpStringIteratorObject* (*)(JSContext*, NewObjectKind);
+      ool = oolCallVM<Fn, NewRegExpStringIteratorObject>(
+          lir, ArgList(Imm32(GenericObject)), StoreRegisterTo(objReg));
       break;
     }
     default:
       MOZ_CRASH("unexpected iterator type");
   }
 
   TemplateObject templateObject(lir->mir()->templateObject());
   masm.createGCObject(objReg, tempReg, templateObject, gc::DefaultHeap,
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1291,23 +1291,23 @@ RNewIterator::RNewIterator(CompactBuffer
 
 bool RNewIterator::recover(JSContext* cx, SnapshotIterator& iter) const {
   RootedObject templateObject(cx, &iter.read().toObject());
   RootedValue result(cx);
 
   JSObject* resultObject = nullptr;
   switch (MNewIterator::Type(type_)) {
     case MNewIterator::ArrayIterator:
-      resultObject = NewArrayIterator(cx);
+      resultObject = NewArrayIteratorObject(cx);
       break;
     case MNewIterator::StringIterator:
-      resultObject = NewStringIterator(cx);
+      resultObject = NewStringIteratorObject(cx);
       break;
     case MNewIterator::RegExpStringIterator:
-      resultObject = NewRegExpStringIterator(cx);
+      resultObject = NewRegExpStringIteratorObject(cx);
       break;
   }
 
   if (!resultObject) {
     return false;
   }
 
   result.setObject(*resultObject);
--- a/js/src/jit/VMFunctionList-inl.h
+++ b/js/src/jit/VMFunctionList-inl.h
@@ -184,25 +184,25 @@ namespace jit {
   _(LooselyNotEqual, js::jit::LooselyEqual<js::jit::EqualityKind::NotEqual>)   \
   _(MakeDefaultConstructor, js::MakeDefaultConstructor)                        \
   _(MutatePrototype, js::jit::MutatePrototype)                                 \
   _(NamedLambdaObjectCreateTemplateObject,                                     \
     js::NamedLambdaObject::createTemplateObject)                               \
   _(NativeGetElement, js::NativeGetElement)                                    \
   _(NewArgumentsObject, js::jit::NewArgumentsObject)                           \
   _(NewArrayCopyOnWriteOperation, js::NewArrayCopyOnWriteOperation)            \
-  _(NewArrayIterator, js::NewArrayIterator)                                    \
+  _(NewArrayIteratorObject, js::NewArrayIteratorObject)                        \
   _(NewArrayOperation, js::NewArrayOperation)                                  \
   _(NewArrayWithGroup, js::NewArrayWithGroup)                                  \
   _(NewCallObject, js::jit::NewCallObject)                                     \
   _(NewDenseCopyOnWriteArray, js::NewDenseCopyOnWriteArray)                    \
   _(NewObjectOperation, js::NewObjectOperation)                                \
   _(NewObjectOperationWithTemplate, js::NewObjectOperationWithTemplate)        \
-  _(NewRegExpStringIterator, js::NewRegExpStringIterator)                      \
-  _(NewStringIterator, js::NewStringIterator)                                  \
+  _(NewRegExpStringIteratorObject, js::NewRegExpStringIteratorObject)          \
+  _(NewStringIteratorObject, js::NewStringIteratorObject)                      \
   _(NewStringObject, js::jit::NewStringObject)                                 \
   _(NewTypedArrayWithTemplateAndArray, js::NewTypedArrayWithTemplateAndArray)  \
   _(NewTypedArrayWithTemplateAndBuffer,                                        \
     js::NewTypedArrayWithTemplateAndBuffer)                                    \
   _(NewTypedArrayWithTemplateAndLength,                                        \
     js::NewTypedArrayWithTemplateAndLength)                                    \
   _(NormalSuspend, js::jit::NormalSuspend)                                     \
   _(NumberToString, js::NumberToString<CanGC>)                                 \
--- a/js/src/jsapi-tests/testBug604087.cpp
+++ b/js/src/jsapi-tests/testBug604087.cpp
@@ -40,19 +40,19 @@ static JSObject* Wrap(JSContext* cx, JS:
 
 static const JSWrapObjectCallbacks WrapObjectCallbacks = {Wrap, PreWrap};
 
 BEGIN_TEST(testBug604087) {
   js::SetWindowProxyClass(cx, &OuterWrapperClass);
 
   js::WrapperOptions options;
   options.setClass(&OuterWrapperClass);
+  options.setSingleton(true);
   JS::RootedObject outerObj(
-      cx,
-      js::Wrapper::NewSingleton(cx, global, &js::Wrapper::singleton, options));
+      cx, js::Wrapper::New(cx, global, &js::Wrapper::singleton, options));
   JS::RealmOptions globalOptions;
   JS::RootedObject compartment2(
       cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
                              JS::FireOnNewGlobalHook, globalOptions));
   CHECK(compartment2 != nullptr);
   JS::RootedObject compartment3(
       cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
                              JS::FireOnNewGlobalHook, globalOptions));
@@ -73,18 +73,17 @@ BEGIN_TEST(testBug604087) {
   JS::RootedObject c4wrapper(cx, wrap(cx, outerObj, compartment4));
   CHECK(c4wrapper);
   c4wrapper->as<js::ProxyObject>().setReservedSlot(0, js::Int32Value(4));
   compartment4 = c4wrapper = nullptr;
 
   JS::RootedObject next(cx);
   {
     JSAutoRealm ar(cx, compartment2);
-    next = js::Wrapper::NewSingleton(cx, compartment2, &js::Wrapper::singleton,
-                                     options);
+    next = js::Wrapper::New(cx, compartment2, &js::Wrapper::singleton, options);
     CHECK(next);
   }
 
   JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
   CHECK(JS_TransplantObject(cx, outerObj, next));
   return true;
 }
 END_TEST(testBug604087)
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -120,17 +120,18 @@ JS_FRIEND_API JSObject* JS_NewObjectWith
                                                    const JSClass* clasp,
                                                    HandleObject proto) {
   /*
    * Create our object with a null proto and then splice in the correct proto
    * after we setSingleton, so that we don't pollute the default
    * ObjectGroup attached to our proto with information about our object, since
    * we're not going to be using that ObjectGroup anyway.
    */
-  RootedObject obj(cx, NewSingletonObjectWithGivenProto(cx, clasp, nullptr));
+  RootedObject obj(
+      cx, NewObjectWithGivenProto(cx, clasp, nullptr, SingletonObject));
   if (!obj) {
     return nullptr;
   }
   if (!JS_SplicePrototype(cx, obj, proto)) {
     return nullptr;
   }
   return obj;
 }
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -937,17 +937,17 @@ static const JSPropertySpec math_static_
     JS_STRING_SYM_PS(toStringTag, "Math", JSPROP_READONLY), JS_PS_END};
 
 static JSObject* CreateMathObject(JSContext* cx, JSProtoKey key) {
   Handle<GlobalObject*> global = cx->global();
   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
-  return NewSingletonObjectWithGivenProto(cx, &MathClass, proto);
+  return NewObjectWithGivenProto(cx, &MathClass, proto, SingletonObject);
 }
 
 static bool MathClassFinish(JSContext* cx, HandleObject ctor,
                             HandleObject proto) {
   return JS_DefineConstDoubles(cx, ctor, math_constants);
 }
 
 static const ClassSpec MathClassSpec = {
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -787,42 +787,17 @@ JS_FRIEND_API JSObject* js::NewProxyObje
     cx->check(proto_);  // |priv| might be cross-compartment.
   }
 
   if (options.lazyProto()) {
     MOZ_ASSERT(!proto_);
     proto_ = TaggedProto::LazyProto;
   }
 
-  return ProxyObject::New(cx, handler, priv, TaggedProto(proto_),
-                          options.clasp());
-}
-
-JS_FRIEND_API JSObject* js::NewSingletonProxyObject(
-    JSContext* cx, const BaseProxyHandler* handler, HandleValue priv,
-    JSObject* proto_, const ProxyOptions& options) {
-  AssertHeapIsIdle();
-  CHECK_THREAD(cx);
-
-  // This can be called from the compartment wrap hooks while in a realm with a
-  // gray global. Trigger the read barrier on the global to ensure this is
-  // unmarked.
-  cx->realm()->maybeGlobal();
-
-  if (proto_ != TaggedProto::LazyProto) {
-    cx->check(proto_);  // |priv| might be cross-compartment.
-  }
-
-  if (options.lazyProto()) {
-    MOZ_ASSERT(!proto_);
-    proto_ = TaggedProto::LazyProto;
-  }
-
-  return ProxyObject::NewSingleton(cx, handler, priv, TaggedProto(proto_),
-                                   options.clasp());
+  return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), options);
 }
 
 void ProxyObject::renew(const BaseProxyHandler* handler, const Value& priv) {
   MOZ_ASSERT(!IsInsideNursery(this));
   MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
   MOZ_ASSERT(getClass() == &ProxyClass);
   MOZ_ASSERT(!IsWindowProxy(this));
   MOZ_ASSERT(hasDynamicPrototype());
--- a/js/src/proxy/Wrapper.cpp
+++ b/js/src/proxy/Wrapper.cpp
@@ -277,29 +277,16 @@ JSObject* Wrapper::New(JSContext* cx, JS
   mozilla::Maybe<AutoRealm> ar;
   if (handler->isCrossCompartmentWrapper()) {
     ar.emplace(cx, &cx->compartment()->globalForNewCCW());
   }
   RootedValue priv(cx, ObjectValue(*obj));
   return NewProxyObject(cx, handler, priv, options.proto(), options);
 }
 
-JSObject* Wrapper::NewSingleton(JSContext* cx, JSObject* obj,
-                                const Wrapper* handler,
-                                const WrapperOptions& options) {
-  // If this is a cross-compartment wrapper allocate it in the compartment's
-  // first global. See Compartment::globalForNewCCW.
-  mozilla::Maybe<AutoRealm> ar;
-  if (handler->isCrossCompartmentWrapper()) {
-    ar.emplace(cx, &cx->compartment()->globalForNewCCW());
-  }
-  RootedValue priv(cx, ObjectValue(*obj));
-  return NewSingletonProxyObject(cx, handler, priv, options.proto(), options);
-}
-
 JSObject* Wrapper::Renew(JSObject* existing, JSObject* obj,
                          const Wrapper* handler) {
   existing->as<ProxyObject>().renew(handler, ObjectValue(*obj));
   return existing;
 }
 
 JSObject* Wrapper::wrappedObject(JSObject* wrapper) {
   MOZ_ASSERT(wrapper->is<WrapperObject>());
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -557,20 +557,21 @@ static JSObject* NewGlobalObject(JSConte
 const JSClass ShellWindowProxyClass =
     PROXY_CLASS_DEF("ShellWindowProxy", JSCLASS_HAS_RESERVED_SLOTS(1));
 
 JSObject* NewShellWindowProxy(JSContext* cx, JS::HandleObject global) {
   MOZ_ASSERT(global->is<GlobalObject>());
 
   js::WrapperOptions options;
   options.setClass(&ShellWindowProxyClass);
+  options.setSingleton(true);
 
   JSAutoRealm ar(cx, global);
   JSObject* obj =
-      js::Wrapper::NewSingleton(cx, global, &js::Wrapper::singleton, options);
+      js::Wrapper::New(cx, global, &js::Wrapper::singleton, options);
   MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
   return obj;
 }
 
 /*
  * A toy principals type for the shell.
  *
  * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the
@@ -4984,18 +4985,17 @@ class XDRBufferObject : public NativeObj
 /*static */ const JSClass XDRBufferObject::class_ = {
     "XDRBufferObject",
     JSCLASS_HAS_RESERVED_SLOTS(XDRBufferObject::RESERVED_SLOTS) |
         JSCLASS_BACKGROUND_FINALIZE,
     &XDRBufferObject::classOps_};
 
 XDRBufferObject* XDRBufferObject::create(JSContext* cx,
                                          JS::TranscodeBuffer* buf) {
-  XDRBufferObject* bufObj =
-      NewObjectWithGivenProto<XDRBufferObject>(cx, nullptr);
+  XDRBufferObject* bufObj = NewObjectWithNullTaggedProto<XDRBufferObject>(cx);
   if (!bufObj) {
     return nullptr;
   }
 
   auto heapBuf = cx->make_unique<JS::TranscodeBuffer>();
   if (!heapBuf) {
     return nullptr;
   }
@@ -7840,17 +7840,17 @@ static bool EnsureGrayRoot(JSContext* cx
   CallArgs args = CallArgsFromVp(argc, vp);
 
   auto priv = EnsureShellCompartmentPrivate(cx);
   if (!priv) {
     return false;
   }
 
   if (!priv->grayRoot) {
-    if (!(priv->grayRoot = NewTenuredDenseEmptyArray(cx, nullptr))) {
+    if (!(priv->grayRoot = NewDenseEmptyArray(cx, nullptr, TenuredObject))) {
       return false;
     }
   }
 
   // Barrier to enforce the invariant that JS does not touch gray objects.
   JSObject* obj = priv->grayRoot;
   JS::ExposeObjectToActiveJS(obj);
 
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -23,17 +23,16 @@
 #  include "vm/BytecodeIterator.h"
 #  include "vm/BytecodeLocation.h"
 #endif
 
 #include "gc/Marking-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/NativeObject-inl.h"
-#include "vm/ObjectGroup-inl.h"  // JSObject::setSingleton
 #include "vm/Stack-inl.h"
 #include "vm/TypeInference-inl.h"
 
 #ifdef DEBUG
 #  include "vm/BytecodeIterator-inl.h"
 #  include "vm/BytecodeLocation-inl.h"
 #endif
 
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -246,18 +246,18 @@ static const JSFunctionSpec generator_me
 
 JSObject* js::NewSingletonObjectWithFunctionPrototype(
     JSContext* cx, Handle<GlobalObject*> global) {
   RootedObject proto(cx,
                      GlobalObject::getOrCreateFunctionPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
-  RootedObject obj(cx,
-                   NewSingletonObjectWithGivenProto<PlainObject>(cx, proto));
+  RootedObject obj(
+      cx, NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject));
   if (!obj) {
     return nullptr;
   }
   if (!JSObject::setDelegate(cx, obj)) {
     return nullptr;
   }
   return obj;
 }
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -596,17 +596,17 @@ JSObject* GlobalObject::getOrCreateThrow
   return throwTypeError;
 }
 
 GlobalObject* GlobalObject::createInternal(JSContext* cx,
                                            const JSClass* clasp) {
   MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
   MOZ_ASSERT(clasp->isTrace(JS_GlobalObjectTraceHook));
 
-  JSObject* obj = NewSingletonObjectWithGivenProto(cx, clasp, nullptr);
+  JSObject* obj = NewObjectWithGivenProto(cx, clasp, nullptr, SingletonObject);
   if (!obj) {
     return nullptr;
   }
 
   Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
   MOZ_ASSERT(global->isUnqualifiedVarObj());
 
   // Initialize the private slot to null if present, as GC can call class
@@ -851,23 +851,23 @@ JSFunction* GlobalObject::createConstruc
 
   return fun;
 }
 
 static NativeObject* CreateBlankProto(JSContext* cx, const JSClass* clasp,
                                       HandleObject proto) {
   MOZ_ASSERT(clasp != &JSFunction::class_);
 
-  RootedObject blankProto(cx,
-                          NewSingletonObjectWithGivenProto(cx, clasp, proto));
+  RootedNativeObject blankProto(
+      cx, NewNativeObjectWithGivenProto(cx, clasp, proto, SingletonObject));
   if (!blankProto || !JSObject::setDelegate(cx, blankProto)) {
     return nullptr;
   }
 
-  return &blankProto->as<NativeObject>();
+  return blankProto;
 }
 
 /* static */
 NativeObject* GlobalObject::createBlankPrototype(JSContext* cx,
                                                  Handle<GlobalObject*> global,
                                                  const JSClass* clasp) {
   RootedObject objectProto(cx, getOrCreateObjectPrototype(cx, global));
   if (!objectProto) {
@@ -964,17 +964,18 @@ NativeObject* GlobalObject::getIntrinsic
     return &slot.toObject().as<NativeObject>();
   }
 
   Rooted<NativeObject*> intrinsicsHolder(cx);
   bool isSelfHostingGlobal = cx->runtime()->isSelfHostingGlobal(global);
   if (isSelfHostingGlobal) {
     intrinsicsHolder = global;
   } else {
-    intrinsicsHolder = NewTenuredObjectWithGivenProto<PlainObject>(cx, nullptr);
+    intrinsicsHolder =
+        NewObjectWithGivenProto<PlainObject>(cx, nullptr, TenuredObject);
     if (!intrinsicsHolder) {
       return nullptr;
     }
   }
 
   /* Define a property 'global' with the current global as its value. */
   RootedValue globalValue(cx, ObjectValue(*global));
   if (!DefineDataProperty(cx, intrinsicsHolder, cx->names().global, globalValue,
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -4925,17 +4925,17 @@ bool js::DefFunOperation(JSContext* cx, 
 
 JSObject* js::SingletonObjectLiteralOperation(JSContext* cx,
                                               HandleScript script,
                                               jsbytecode* pc) {
   MOZ_ASSERT(JSOp(*pc) == JSOp::Object);
 
   RootedObject obj(cx, script->getObject(pc));
   if (cx->realm()->creationOptions().cloneSingletons()) {
-    return DeepCloneObjectLiteral(cx, obj);
+    return DeepCloneObjectLiteral(cx, obj, TenuredObject);
   }
 
   cx->realm()->behaviors().setSingletonsAsValues();
   return obj;
 }
 
 JSObject* js::ImportMetaOperation(JSContext* cx, HandleScript script) {
   RootedObject module(cx, GetModuleObjectForScript(script));
@@ -5440,17 +5440,17 @@ JSObject* js::NewObjectOperation(JSConte
 
   RootedPlainObject obj(cx);
 
   // Actually allocate the object.
   if (withTemplate) {
     obj = CopyInitializerObject(cx, baseObject, newKind);
   } else {
     MOZ_ASSERT(JSOp(*pc) == JSOp::NewInit);
-    obj = NewBuiltinClassInstanceWithKind<PlainObject>(cx, newKind);
+    obj = NewBuiltinClassInstance<PlainObject>(cx, newKind);
   }
 
   if (!obj) {
     return nullptr;
   }
 
   if (newKind == SingletonObject) {
     MOZ_ASSERT(obj->isSingleton());
--- a/js/src/vm/Iteration.cpp
+++ b/js/src/vm/Iteration.cpp
@@ -1005,18 +1005,18 @@ PlainObject* Realm::getOrCreateIterResul
   return iterResultWithoutPrototypeTemplate_;
 }
 
 PlainObject* Realm::createIterResultTemplateObject(
     JSContext* cx, WithObjectPrototype withProto) {
   // Create template plain object
   Rooted<PlainObject*> templateObject(
       cx, withProto == WithObjectPrototype::Yes
-              ? NewTenuredBuiltinClassInstance<PlainObject>(cx)
-              : NewObjectWithGivenProto<PlainObject>(cx, nullptr));
+              ? NewBuiltinClassInstance<PlainObject>(cx, TenuredObject)
+              : NewObjectWithNullTaggedProto<PlainObject>(cx));
   if (!templateObject) {
     return nullptr;
   }
 
   // Create a new group for the template.
   Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
   RootedObjectGroup group(
       cx, ObjectGroupRealm::makeGroup(cx, templateObject->realm(),
@@ -1106,34 +1106,25 @@ enum {
   ArrayIteratorSlotNextIndex,
   ArrayIteratorSlotItemKind,
   ArrayIteratorSlotCount
 };
 
 const JSClass ArrayIteratorObject::class_ = {
     "Array Iterator", JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount)};
 
-ArrayIteratorObject* js::NewArrayIteratorTemplate(JSContext* cx) {
+ArrayIteratorObject* js::NewArrayIteratorObject(JSContext* cx,
+                                                NewObjectKind newKind) {
   RootedObject proto(
       cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
   if (!proto) {
     return nullptr;
   }
 
-  return NewTenuredObjectWithGivenProto<ArrayIteratorObject>(cx, proto);
-}
-
-ArrayIteratorObject* js::NewArrayIterator(JSContext* cx) {
-  RootedObject proto(
-      cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
-  if (!proto) {
-    return nullptr;
-  }
-
-  return NewObjectWithGivenProto<ArrayIteratorObject>(cx, proto);
+  return NewObjectWithGivenProto<ArrayIteratorObject>(cx, proto, newKind);
 }
 
 static const JSFunctionSpec array_iterator_methods[] = {
     JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0), JS_FS_END};
 
 static const JSClass StringIteratorPrototypeClass = {"String Iterator", 0};
 
 enum {
@@ -1143,34 +1134,25 @@ enum {
 };
 
 const JSClass StringIteratorObject::class_ = {
     "String Iterator", JSCLASS_HAS_RESERVED_SLOTS(StringIteratorSlotCount)};
 
 static const JSFunctionSpec string_iterator_methods[] = {
     JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0), JS_FS_END};
 
-StringIteratorObject* js::NewStringIteratorTemplate(JSContext* cx) {
+StringIteratorObject* js::NewStringIteratorObject(JSContext* cx,
+                                                  NewObjectKind newKind) {
   RootedObject proto(
       cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global()));
   if (!proto) {
     return nullptr;
   }
 
-  return NewTenuredObjectWithGivenProto<StringIteratorObject>(cx, proto);
-}
-
-StringIteratorObject* js::NewStringIterator(JSContext* cx) {
-  RootedObject proto(
-      cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global()));
-  if (!proto) {
-    return nullptr;
-  }
-
-  return NewObjectWithGivenProto<StringIteratorObject>(cx, proto);
+  return NewObjectWithGivenProto<StringIteratorObject>(cx, proto, newKind);
 }
 
 static const JSClass RegExpStringIteratorPrototypeClass = {
     "RegExp String Iterator", 0};
 
 enum {
   // The regular expression used for iteration. May hold the original RegExp
   // object when it is reused instead of a new RegExp object.
@@ -1221,34 +1203,26 @@ const JSClass RegExpStringIteratorObject
     "RegExp String Iterator",
     JSCLASS_HAS_RESERVED_SLOTS(RegExpStringIteratorSlotCount)};
 
 static const JSFunctionSpec regexp_string_iterator_methods[] = {
     JS_SELF_HOSTED_FN("next", "RegExpStringIteratorNext", 0, 0),
 
     JS_FS_END};
 
-RegExpStringIteratorObject* js::NewRegExpStringIteratorTemplate(JSContext* cx) {
+RegExpStringIteratorObject* js::NewRegExpStringIteratorObject(
+    JSContext* cx, NewObjectKind newKind) {
   RootedObject proto(cx, GlobalObject::getOrCreateRegExpStringIteratorPrototype(
                              cx, cx->global()));
   if (!proto) {
     return nullptr;
   }
 
-  return NewTenuredObjectWithGivenProto<RegExpStringIteratorObject>(cx, proto);
-}
-
-RegExpStringIteratorObject* js::NewRegExpStringIterator(JSContext* cx) {
-  RootedObject proto(cx, GlobalObject::getOrCreateRegExpStringIteratorPrototype(
-                             cx, cx->global()));
-  if (!proto) {
-    return nullptr;
-  }
-
-  return NewObjectWithGivenProto<RegExpStringIteratorObject>(cx, proto);
+  return NewObjectWithGivenProto<RegExpStringIteratorObject>(cx, proto,
+                                                             newKind);
 }
 
 JSObject* js::ValueToIterator(JSContext* cx, HandleValue vp) {
   RootedObject obj(cx);
   if (vp.isObject()) {
     /* Common case. */
     obj = &vp.toObject();
   } else if (vp.isNullOrUndefined()) {
--- a/js/src/vm/Iteration.h
+++ b/js/src/vm/Iteration.h
@@ -371,34 +371,34 @@ class PropertyIteratorObject : public Na
   static void finalize(JSFreeOp* fop, JSObject* obj);
 };
 
 class ArrayIteratorObject : public NativeObject {
  public:
   static const JSClass class_;
 };
 
-ArrayIteratorObject* NewArrayIteratorTemplate(JSContext* cx);
-ArrayIteratorObject* NewArrayIterator(JSContext* cx);
+ArrayIteratorObject* NewArrayIteratorObject(
+    JSContext* cx, NewObjectKind newKind = GenericObject);
 
 class StringIteratorObject : public NativeObject {
  public:
   static const JSClass class_;
 };
 
-StringIteratorObject* NewStringIteratorTemplate(JSContext* cx);
-StringIteratorObject* NewStringIterator(JSContext* cx);
+StringIteratorObject* NewStringIteratorObject(
+    JSContext* cx, NewObjectKind newKind = GenericObject);
 
 class RegExpStringIteratorObject : public NativeObject {
  public:
   static const JSClass class_;
 };
 
-RegExpStringIteratorObject* NewRegExpStringIteratorTemplate(JSContext* cx);
-RegExpStringIteratorObject* NewRegExpStringIterator(JSContext* cx);
+RegExpStringIteratorObject* NewRegExpStringIteratorObject(
+    JSContext* cx, NewObjectKind newKind = GenericObject);
 
 MOZ_MUST_USE bool EnumerateProperties(JSContext* cx, HandleObject obj,
                                       MutableHandleIdVector props);
 
 PropertyIteratorObject* LookupInIteratorCache(JSContext* cx, HandleObject obj);
 
 JSObject* ValueToIterator(JSContext* cx, HandleValue vp);
 
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -365,17 +365,17 @@ static bool ResolveInterpretedFunctionPr
   } else {
     objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
   }
   if (!objProto) {
     return false;
   }
 
   RootedPlainObject proto(
-      cx, NewSingletonObjectWithGivenProto<PlainObject>(cx, objProto));
+      cx, NewObjectWithGivenProto<PlainObject>(cx, objProto, SingletonObject));
   if (!proto) {
     return false;
   }
 
   // Per ES5 13.2 the prototype's .constructor property is configurable,
   // non-enumerable, and writable.  However, per the 15 July 2013 ES6 draft,
   // section 15.19.3, the .prototype of a generator function does not link
   // back with a .constructor.
--- a/js/src/vm/JSObject-inl.h
+++ b/js/src/vm/JSObject-inl.h
@@ -112,16 +112,31 @@ js::NativeObject::updateDictionaryListPo
 
   // Dictionary objects can be allocated in the nursery and when they are
   // tenured the shape's pointer to the object needs to be updated.
   if (shape()->dictNext == DictionaryShapeLink(old)) {
     shape()->dictNext = DictionaryShapeLink(this);
   }
 }
 
+/* static */ inline bool JSObject::setSingleton(JSContext* cx,
+                                                js::HandleObject obj) {
+  MOZ_ASSERT(!IsInsideNursery(obj));
+  MOZ_ASSERT(!obj->isSingleton());
+
+  js::ObjectGroup* group = js::ObjectGroup::lazySingletonGroup(
+      cx, obj->groupRaw(), obj->getClass(), obj->taggedProto());
+  if (!group) {
+    return false;
+  }
+
+  obj->setGroupRaw(group);
+  return true;
+}
+
 /* static */ inline js::ObjectGroup* JSObject::getGroup(JSContext* cx,
                                                         js::HandleObject obj) {
   MOZ_ASSERT(cx->compartment() == obj->compartment());
   if (obj->hasLazyGroup()) {
     if (cx->compartment() != obj->compartment()) {
       MOZ_CRASH();
     }
     return makeLazyGroup(cx, obj);
@@ -356,16 +371,22 @@ MOZ_ALWAYS_INLINE bool ToPropertyKey(JSC
  */
 inline bool IsInternalFunctionObject(JSObject& funobj) {
   JSFunction& fun = funobj.as<JSFunction>();
   return fun.isInterpreted() && !fun.environment();
 }
 
 inline gc::InitialHeap GetInitialHeap(NewObjectKind newKind,
                                       const JSClass* clasp) {
+  if (newKind == NurseryAllocatedProxy) {
+    MOZ_ASSERT(clasp->isProxy());
+    MOZ_ASSERT(clasp->hasFinalize());
+    MOZ_ASSERT(!CanNurseryAllocateFinalizedClass(clasp));
+    return gc::DefaultHeap;
+  }
   if (newKind != GenericObject) {
     return gc::TenuredHeap;
   }
   if (clasp->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp)) {
     return gc::TenuredHeap;
   }
   return gc::DefaultHeap;
 }
@@ -414,92 +435,77 @@ inline T* NewObjectWithGivenTaggedProto(
                                         gc::AllocKind allocKind,
                                         NewObjectKind newKind = GenericObject,
                                         uint32_t initialShapeFlags = 0) {
   JSObject* obj = NewObjectWithGivenTaggedProto(
       cx, &T::class_, proto, allocKind, newKind, initialShapeFlags);
   return obj ? &obj->as<T>() : nullptr;
 }
 
+template <typename T>
+inline T* NewObjectWithNullTaggedProto(JSContext* cx,
+                                       NewObjectKind newKind = GenericObject,
+                                       uint32_t initialShapeFlags = 0) {
+  Handle<TaggedProto> nullProto = AsTaggedProto(nullptr);
+  return NewObjectWithGivenTaggedProto<T>(cx, nullProto, newKind,
+                                          initialShapeFlags);
+}
+
 inline JSObject* NewObjectWithGivenProto(
     JSContext* cx, const JSClass* clasp, HandleObject proto,
     gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) {
   return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(proto),
                                        allocKind, newKind);
 }
 
-inline JSObject* NewObjectWithGivenProto(JSContext* cx, const JSClass* clasp,
-                                         HandleObject proto) {
+inline JSObject* NewObjectWithGivenProto(
+    JSContext* cx, const JSClass* clasp, HandleObject proto,
+    NewObjectKind newKind = GenericObject) {
   return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(proto),
-                                       GenericObject);
-}
-
-inline JSObject* NewTenuredObjectWithGivenProto(JSContext* cx,
-                                                const JSClass* clasp,
-                                                HandleObject proto) {
-  return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(proto),
-                                       TenuredObject);
-}
-
-inline JSObject* NewSingletonObjectWithGivenProto(JSContext* cx,
-                                                  const JSClass* clasp,
-                                                  HandleObject proto) {
-  return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(proto),
-                                       SingletonObject);
+                                       newKind);
 }
 
 template <typename T>
-inline T* NewObjectWithGivenProto(JSContext* cx, HandleObject proto) {
-  return NewObjectWithGivenTaggedProto<T>(cx, AsTaggedProto(proto),
-                                          GenericObject);
+inline T* NewObjectWithGivenProto(JSContext* cx, HandleObject proto,
+                                  NewObjectKind newKind = GenericObject) {
+  return NewObjectWithGivenTaggedProto<T>(cx, AsTaggedProto(proto), newKind);
 }
 
 template <typename T>
-inline T* NewSingletonObjectWithGivenProto(JSContext* cx, HandleObject proto) {
-  return NewObjectWithGivenTaggedProto<T>(cx, AsTaggedProto(proto),
-                                          SingletonObject);
-}
-
-template <typename T>
-inline T* NewTenuredObjectWithGivenProto(JSContext* cx, HandleObject proto) {
-  return NewObjectWithGivenTaggedProto<T>(cx, AsTaggedProto(proto),
-                                          TenuredObject);
-}
-
-template <typename T>
-inline T* NewObjectWithGivenProtoAndKinds(JSContext* cx, HandleObject proto,
-                                          gc::AllocKind allocKind,
-                                          NewObjectKind newKind) {
+inline T* NewObjectWithGivenProto(JSContext* cx, HandleObject proto,
+                                  gc::AllocKind allocKind,
+                                  NewObjectKind newKind = GenericObject) {
   JSObject* obj = NewObjectWithGivenTaggedProto(
       cx, &T::class_, AsTaggedProto(proto), allocKind, newKind);
   return obj ? &obj->as<T>() : nullptr;
 }
 
 // Make an object with the prototype set according to the cached prototype or
 // Object.prototype.
-JSObject* NewObjectWithClassProto(JSContext* cx, const JSClass* clasp,
-                                  HandleObject proto, gc::AllocKind allocKind,
-                                  NewObjectKind newKind = GenericObject);
+JSObject* NewObjectWithClassProtoCommon(JSContext* cx, const JSClass* clasp,
+                                        HandleObject proto,
+                                        gc::AllocKind allocKind,
+                                        NewObjectKind newKind);
+
+inline JSObject* NewObjectWithClassProto(
+    JSContext* cx, const JSClass* clasp, HandleObject proto,
+    gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) {
+  return NewObjectWithClassProtoCommon(cx, clasp, proto, allocKind, newKind);
+}
 
 inline JSObject* NewObjectWithClassProto(
     JSContext* cx, const JSClass* clasp, HandleObject proto,
     NewObjectKind newKind = GenericObject) {
   gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
   return NewObjectWithClassProto(cx, clasp, proto, allocKind, newKind);
 }
 
 template <class T>
-inline T* NewObjectWithClassProto(JSContext* cx, HandleObject proto) {
-  JSObject* obj = NewObjectWithClassProto(cx, &T::class_, proto, GenericObject);
-  return obj ? &obj->as<T>() : nullptr;
-}
-
-template <class T>
-inline T* NewObjectWithClassProtoAndKind(JSContext* cx, HandleObject proto,
-                                         NewObjectKind newKind) {
+inline T* NewObjectWithClassProto(JSContext* cx, HandleObject proto,
+                                  NewObjectKind newKind = GenericObject) {
   JSObject* obj = NewObjectWithClassProto(cx, &T::class_, proto, newKind);
   return obj ? &obj->as<T>() : nullptr;
 }
 
 template <class T>
 inline T* NewObjectWithClassProto(JSContext* cx, HandleObject proto,
                                   gc::AllocKind allocKind,
                                   NewObjectKind newKind = GenericObject) {
@@ -521,30 +527,18 @@ inline JSObject* NewBuiltinClassInstance
 inline JSObject* NewBuiltinClassInstance(
     JSContext* cx, const JSClass* clasp,
     NewObjectKind newKind = GenericObject) {
   gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
   return NewBuiltinClassInstance(cx, clasp, allocKind, newKind);
 }
 
 template <typename T>
-inline T* NewBuiltinClassInstance(JSContext* cx) {
-  JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, GenericObject);
-  return obj ? &obj->as<T>() : nullptr;
-}
-
-template <typename T>
-inline T* NewTenuredBuiltinClassInstance(JSContext* cx) {
-  JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, TenuredObject);
-  return obj ? &obj->as<T>() : nullptr;
-}
-
-template <typename T>
-inline T* NewBuiltinClassInstanceWithKind(JSContext* cx,
-                                          NewObjectKind newKind) {
+inline T* NewBuiltinClassInstance(JSContext* cx,
+                                  NewObjectKind newKind = GenericObject) {
   JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, newKind);
   return obj ? &obj->as<T>() : nullptr;
 }
 
 template <typename T>
 inline T* NewBuiltinClassInstance(JSContext* cx, gc::AllocKind allocKind,
                                   NewObjectKind newKind = GenericObject) {
   JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, allocKind, newKind);
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -880,20 +880,20 @@ JSObject* js::NewObjectWithGivenTaggedPr
 }
 
 static bool NewObjectIsCachable(JSContext* cx, NewObjectKind newKind,
                                 const JSClass* clasp) {
   return !cx->isHelperThreadContext() && newKind == GenericObject &&
          clasp->isNative();
 }
 
-JSObject* js::NewObjectWithClassProto(JSContext* cx, const JSClass* clasp,
-                                      HandleObject protoArg,
-                                      gc::AllocKind allocKind,
-                                      NewObjectKind newKind) {
+JSObject* js::NewObjectWithClassProtoCommon(JSContext* cx, const JSClass* clasp,
+                                            HandleObject protoArg,
+                                            gc::AllocKind allocKind,
+                                            NewObjectKind newKind) {
   if (protoArg) {
     return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(protoArg),
                                          allocKind, newKind);
   }
 
   if (CanChangeToBackgroundAllocKind(allocKind, clasp)) {
     allocKind = ForegroundToBackgroundAllocKind(allocKind);
   }
@@ -1142,17 +1142,17 @@ JSObject* js::CreateThisForFunctionWithP
           AutoSweepObjectGroup sweepNewGroup(group);
           MOZ_ASSERT(group && group->newScript(sweepNewGroup));
         }
       }
     }
 
     res = CreateThisForFunctionWithGroup(cx, group, newKind);
   } else {
-    res = NewBuiltinClassInstanceWithKind<PlainObject>(cx, newKind);
+    res = NewBuiltinClassInstance<PlainObject>(cx, newKind);
   }
 
   if (res) {
     MOZ_ASSERT(res->nonCCWRealm() == callee->realm());
     JSScript* script = JSFunction::getOrCreateScript(cx, callee);
     if (!script) {
       return nullptr;
     }
@@ -1373,28 +1373,30 @@ JSObject* js::CloneObject(JSContext* cx,
       return nullptr;
     }
 
     if (obj->as<NativeObject>().hasPrivate()) {
       clone->as<NativeObject>().setPrivate(
           obj->as<NativeObject>().getPrivate());
     }
   } else {
+    ProxyOptions options;
+    options.setClass(obj->getClass());
+
     auto* handler = GetProxyHandler(obj);
 
     // Same as above, require tenure allocation of the clone. This means for
     // proxy objects we need to reject nursery allocatable proxies.
     if (handler->canNurseryAllocate()) {
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                 JSMSG_CANT_CLONE_OBJECT);
       return nullptr;
     }
 
-    clone = ProxyObject::New(cx, handler, JS::NullHandleValue, proto,
-                             obj->getClass());
+    clone = ProxyObject::New(cx, handler, JS::NullHandleValue, proto, options);
     if (!clone) {
       return nullptr;
     }
 
     if (!CopyProxyObject(cx, obj.as<ProxyObject>(), clone.as<ProxyObject>())) {
       return nullptr;
     }
   }
@@ -1443,72 +1445,77 @@ static bool GetScriptPlainObjectProperti
         !properties.emplaceBack(INT_TO_JSID(i), v)) {
       return false;
     }
   }
 
   return true;
 }
 
-static bool DeepCloneValue(JSContext* cx, Value* vp) {
+static bool DeepCloneValue(JSContext* cx, Value* vp, NewObjectKind newKind) {
   if (vp->isObject()) {
     RootedObject obj(cx, &vp->toObject());
-    obj = DeepCloneObjectLiteral(cx, obj);
+    obj = DeepCloneObjectLiteral(cx, obj, newKind);
     if (!obj) {
       return false;
     }
     vp->setObject(*obj);
   } else {
     cx->markAtomValue(*vp);
   }
   return true;
 }
 
-JSObject* js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj) {
+JSObject* js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj,
+                                     NewObjectKind newKind) {
   /* NB: Keep this in sync with XDRObjectLiteral. */
   MOZ_ASSERT_IF(obj->isSingleton(),
                 cx->realm()->behaviors().getSingletonsAsTemplates());
   MOZ_ASSERT(obj->is<PlainObject>() || obj->is<ArrayObject>());
+  MOZ_ASSERT(newKind != SingletonObject);
 
   if (obj->is<ArrayObject>()) {
     Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
     if (!GetScriptArrayObjectElements(obj.as<ArrayObject>(), &values)) {
       return nullptr;
     }
 
     // Deep clone any elements.
     for (uint32_t i = 0; i < values.length(); ++i) {
-      if (!DeepCloneValue(cx, values[i].address())) {
+      if (!DeepCloneValue(cx, values[i].address(), newKind)) {
         return nullptr;
       }
     }
 
     ObjectGroup::NewArrayKind arrayKind = ObjectGroup::NewArrayKind::Normal;
     if (obj->is<ArrayObject>() &&
         obj->as<ArrayObject>().denseElementsAreCopyOnWrite()) {
       arrayKind = ObjectGroup::NewArrayKind::CopyOnWrite;
     }
 
     return ObjectGroup::newArrayObject(cx, values.begin(), values.length(),
-                                       TenuredObject, arrayKind);
+                                       newKind, arrayKind);
   }
 
   Rooted<IdValueVector> properties(cx, IdValueVector(cx));
   if (!GetScriptPlainObjectProperties(obj, &properties)) {
     return nullptr;
   }
 
   for (size_t i = 0; i < properties.length(); i++) {
     cx->markId(properties[i].get().id);
-    if (!DeepCloneValue(cx, &properties[i].get().value)) {
+    if (!DeepCloneValue(cx, &properties[i].get().value, newKind)) {
       return nullptr;
     }
   }
 
-  NewObjectKind newKind = obj->isSingleton() ? SingletonObject : TenuredObject;
+  if (obj->isSingleton()) {
+    newKind = SingletonObject;
+  }
+
   return ObjectGroup::newPlainObject(cx, properties.begin(),
                                      properties.length(), newKind);
 }
 
 static bool InitializePropertiesFromCompatibleNativeObject(
     JSContext* cx, HandleNativeObject dst, HandleNativeObject src) {
   cx->check(src, dst);
   MOZ_ASSERT(src->getClass() == dst->getClass());
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -878,17 +878,18 @@ extern JSObject* CreateThisForFunction(J
 
 // Generic call for constructing |this|.
 extern JSObject* CreateThis(JSContext* cx, const JSClass* clasp,
                             js::HandleObject callee);
 
 extern JSObject* CloneObject(JSContext* cx, HandleObject obj,
                              Handle<js::TaggedProto> proto);
 
-extern JSObject* DeepCloneObjectLiteral(JSContext* cx, HandleObject obj);
+extern JSObject* DeepCloneObjectLiteral(JSContext* cx, HandleObject obj,
+                                        NewObjectKind newKind = GenericObject);
 
 /* ES6 draft rev 32 (2015 Feb 2) 6.2.4.5 ToPropertyDescriptor(Obj) */
 bool ToPropertyDescriptor(JSContext* cx, HandleValue descval,
                           bool checkAccessors,
                           MutableHandle<JS::PropertyDescriptor> desc);
 
 /*
  * Throw a TypeError if desc.getterObject() or setterObject() is not
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -4789,17 +4789,17 @@ static JSObject* CloneScriptObject(JSCon
 
     Scope* enclosing = innerFun->enclosingScope();
     uint32_t scopeIndex = FindScopeIndex(srcData->gcthings(), *enclosing);
     RootedScope enclosingClone(cx, &gcThings[scopeIndex].get().as<Scope>());
     return CloneInnerInterpretedFunction(cx, enclosingClone, innerFun,
                                          sourceObject);
   }
 
-  return DeepCloneObjectLiteral(cx, obj);
+  return DeepCloneObjectLiteral(cx, obj, TenuredObject);
 }
 
 /* static */
 bool PrivateScriptData::Clone(JSContext* cx, HandleScript src, HandleScript dst,
                               MutableHandle<GCVector<Scope*>> scopes) {
   PrivateScriptData* srcData = src->data_;
   uint32_t ngcthings = srcData->gcthings().size();
 
--- a/js/src/vm/List-inl.h
+++ b/js/src/vm/List-inl.h
@@ -15,23 +15,23 @@
 #include <stdint.h>  // uint32_t
 
 #include "js/RootingAPI.h"    // JS::Handle, JS::Rooted
 #include "js/Value.h"         // JS::Value, JS::ObjectValue
 #include "vm/JSContext.h"     // JSContext
 #include "vm/NativeObject.h"  // js::NativeObject
 
 #include "vm/Compartment-inl.h"    // JS::Compartment::wrap
-#include "vm/JSObject-inl.h"       // js::NewObjectWithGivenProto
+#include "vm/JSObject-inl.h"       // js::NewObjectWithNullTaggedProto
 #include "vm/NativeObject-inl.h"   // js::NativeObject::*
 #include "vm/Realm-inl.h"          // js::AutoRealm
 #include "vm/TypeInference-inl.h"  // js::MarkObjectGroupUnknownProperties
 
 inline /* static */ js::ListObject* js::ListObject::create(JSContext* cx) {
-  js::ListObject* obj = NewObjectWithGivenProto<ListObject>(cx, nullptr);
+  js::ListObject* obj = NewObjectWithNullTaggedProto<ListObject>(cx);
   if (!obj) {
     return nullptr;
   }
 
   // Internal object and may contain exotic MagicValues so don't track property
   // types.
   MarkObjectGroupUnknownProperties(cx, obj->group());
   return obj;
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -650,16 +650,58 @@ static inline PlainObject* CopyInitializ
 
   if (!obj->setLastProperty(cx, baseobj->lastProperty())) {
     return nullptr;
   }
 
   return obj;
 }
 
+inline NativeObject* NewNativeObjectWithGivenTaggedProto(
+    JSContext* cx, const JSClass* clasp, Handle<TaggedProto> proto,
+    gc::AllocKind allocKind, NewObjectKind newKind) {
+  return MaybeNativeObject(
+      NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind, newKind));
+}
+
+inline NativeObject* NewNativeObjectWithGivenTaggedProto(
+    JSContext* cx, const JSClass* clasp, Handle<TaggedProto> proto,
+    NewObjectKind newKind = GenericObject) {
+  return MaybeNativeObject(
+      NewObjectWithGivenTaggedProto(cx, clasp, proto, newKind));
+}
+
+inline NativeObject* NewNativeObjectWithGivenProto(JSContext* cx,
+                                                   const JSClass* clasp,
+                                                   HandleObject proto,
+                                                   gc::AllocKind allocKind,
+                                                   NewObjectKind newKind) {
+  return MaybeNativeObject(
+      NewObjectWithGivenProto(cx, clasp, proto, allocKind, newKind));
+}
+
+inline NativeObject* NewNativeObjectWithGivenProto(
+    JSContext* cx, const JSClass* clasp, HandleObject proto,
+    NewObjectKind newKind = GenericObject) {
+  return MaybeNativeObject(NewObjectWithGivenProto(cx, clasp, proto, newKind));
+}
+
+inline NativeObject* NewNativeObjectWithClassProto(
+    JSContext* cx, const JSClass* clasp, HandleObject proto,
+    gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) {
+  return MaybeNativeObject(
+      NewObjectWithClassProto(cx, clasp, proto, allocKind, newKind));
+}
+
+inline NativeObject* NewNativeObjectWithClassProto(
+    JSContext* cx, const JSClass* clasp, HandleObject proto,
+    NewObjectKind newKind = GenericObject) {
+  return MaybeNativeObject(NewObjectWithClassProto(cx, clasp, proto, newKind));
+}
+
 /*
  * Call obj's resolve hook.
  *
  * cx and id are the parameters initially passed to the ongoing lookup;
  * propp and recursedp are its out parameters.
  *
  * There are four possible outcomes:
  *
--- a/js/src/vm/ObjectGroup-inl.h
+++ b/js/src/vm/ObjectGroup-inl.h
@@ -86,35 +86,11 @@ inline TypeNewScript* ObjectGroup::newSc
 }
 
 inline PreliminaryObjectArrayWithTemplate* ObjectGroup::maybePreliminaryObjects(
     const AutoSweepObjectGroup& sweep) {
   MOZ_ASSERT(sweep.group() == this);
   return maybePreliminaryObjectsDontCheckGeneration();
 }
 
-/* static */ inline ObjectGroup* ObjectGroup::lazySingletonGroup(
-    JSContext* cx, ObjectGroup* oldGroup, const JSClass* clasp,
-    TaggedProto proto) {
-  ObjectGroupRealm& realm = oldGroup ? ObjectGroupRealm::get(oldGroup)
-                                     : ObjectGroupRealm::getForNewObject(cx);
-  JS::Realm* objectRealm = oldGroup ? oldGroup->realm() : cx->realm();
-  return lazySingletonGroup(cx, realm, objectRealm, clasp, proto);
-}
-
 }  // namespace js
 
-/* static */ inline bool JSObject::setSingleton(JSContext* cx,
-                                                js::HandleObject obj) {
-  MOZ_ASSERT(!IsInsideNursery(obj));
-  MOZ_ASSERT(!obj->isSingleton());
-
-  js::ObjectGroup* group = js::ObjectGroup::lazySingletonGroup(
-      cx, obj->groupRaw(), obj->getClass(), obj->taggedProto());
-  if (!group) {
-    return false;
-  }
-
-  obj->setGroupRaw(group);
-  return true;
-}
-
 #endif /* vm_ObjectGroup_inl_h */
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -636,20 +636,22 @@ ObjectGroup* ObjectGroup::defaultNewGrou
   }
 
   groups.defaultNewGroupCache.put(group, associated);
   return group;
 }
 
 /* static */
 ObjectGroup* ObjectGroup::lazySingletonGroup(JSContext* cx,
-                                             ObjectGroupRealm& realm,
-                                             JS::Realm* objectRealm,
+                                             ObjectGroup* oldGroup,
                                              const JSClass* clasp,
                                              TaggedProto proto) {
+  ObjectGroupRealm& realm = oldGroup ? ObjectGroupRealm::get(oldGroup)
+                                     : ObjectGroupRealm::getForNewObject(cx);
+
   MOZ_ASSERT_IF(proto.isObject(),
                 cx->compartment() == proto.toObject()->compartment());
 
   ObjectGroupRealm::NewTable*& table = realm.lazyTable;
 
   if (!table) {
     table = cx->new_<ObjectGroupRealm::NewTable>(cx->zone());
     if (!table) {
@@ -665,17 +667,17 @@ ObjectGroup* ObjectGroup::lazySingletonG
 
     return group;
   }
 
   AutoEnterAnalysis enter(cx);
 
   Rooted<TaggedProto> protoRoot(cx, proto);
   ObjectGroup* group = ObjectGroupRealm::makeGroup(
-      cx, objectRealm, clasp, protoRoot,
+      cx, oldGroup ? oldGroup->realm() : cx->realm(), clasp, protoRoot,
       OBJECT_FLAG_SINGLETON | OBJECT_FLAG_LAZY_SINGLETON);
   if (!group) {
     return nullptr;
   }
 
   if (!table->add(p, ObjectGroupRealm::NewEntry(group, nullptr))) {
     ReportOutOfMemory(cx);
     return nullptr;
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -44,16 +44,22 @@ enum NewObjectKind {
   /*
    * Singleton objects are treated specially by the type system. This flag
    * ensures that the new object is automatically set up correctly as a
    * singleton and is allocated in the tenured heap.
    */
   SingletonObject,
 
   /*
+   * CrossCompartmentWrappers use the common Proxy class, but are allowed
+   * to have nursery lifetime.
+   */
+  NurseryAllocatedProxy,
+
+  /*
    * Objects which will not benefit from being allocated in the nursery
    * (e.g. because they are known to have a long lifetime) may be allocated
    * with this kind to place them immediately into the tenured generation.
    */
   TenuredObject
 };
 
 /*
@@ -455,36 +461,25 @@ class ObjectGroup : public gc::TenuredCe
   // Whether to make a singleton when calling 'new' at script/pc.
   static bool useSingletonForNewObject(JSContext* cx, JSScript* script,
                                        jsbytecode* pc);
 
   // Whether to make a singleton object at an allocation site.
   static bool useSingletonForAllocationSite(JSScript* script, jsbytecode* pc,
                                             JSProtoKey key);
 
- public:
   // Static accessors for ObjectGroupRealm NewTable.
 
   static ObjectGroup* defaultNewGroup(JSContext* cx, const JSClass* clasp,
                                       TaggedProto proto,
                                       JSObject* associated = nullptr);
-
-  // For use in creating a singleton group without needing to replace an
-  // existing group.
-  static ObjectGroup* lazySingletonGroup(JSContext* cx, ObjectGroupRealm& realm,
-                                         JS::Realm* objectRealm,
+  static ObjectGroup* lazySingletonGroup(JSContext* cx, ObjectGroup* oldGroup,
                                          const JSClass* clasp,
                                          TaggedProto proto);
 
-  // For use in replacing an already-existing group with a singleton group.
-  static inline ObjectGroup* lazySingletonGroup(JSContext* cx,
-                                                ObjectGroup* oldGroup,
-                                                const JSClass* clasp,
-                                                TaggedProto proto);
-
   static void setDefaultNewGroupUnknown(JSContext* cx, ObjectGroupRealm& realm,
                                         const JSClass* clasp,
                                         JS::HandleObject obj);
 
   // Static accessors for ObjectGroupRealm ArrayObjectTable and
   // PlainObjectTable.
 
   enum class NewArrayKind {
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -332,26 +332,26 @@ static const JSClassOps ForOfPICClassOps
     nullptr,               // mayResolve
     ForOfPIC_finalize,     // finalize
     nullptr,               // call
     nullptr,               // hasInstance
     nullptr,               // construct
     ForOfPIC_traceObject,  // trace
 };
 
-const JSClass ForOfPICObject::class_ = {
+const JSClass ForOfPIC::class_ = {
     "ForOfPIC", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
     &ForOfPICClassOps};
 
 /* static */
 NativeObject* js::ForOfPIC::createForOfPICObject(JSContext* cx,
                                                  Handle<GlobalObject*> global) {
   cx->check(global);
-  ForOfPICObject* obj =
-      NewTenuredObjectWithGivenProto<ForOfPICObject>(cx, nullptr);
+  NativeObject* obj = NewNativeObjectWithGivenProto(cx, &ForOfPIC::class_,
+                                                    nullptr, TenuredObject);
   if (!obj) {
     return nullptr;
   }
   ForOfPIC::Chain* chain = cx->new_<ForOfPIC::Chain>(obj);
   if (!chain) {
     return nullptr;
   }
   InitObjectPrivate(obj, chain, MemoryUse::ForOfPIC);
--- a/js/src/vm/PIC.h
+++ b/js/src/vm/PIC.h
@@ -3,17 +3,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_PIC_h
 #define vm_PIC_h
 
 #include "vm/GlobalObject.h"
-#include "vm/NativeObject.h"
 
 namespace js {
 
 class Shape;
 
 template <typename Category>
 class PICChain;
 
@@ -71,22 +70,16 @@ class PICChain {
     unsigned count = 0;
     for (CatStub* stub = stubs_; stub; stub = stub->next()) {
       count++;
     }
     return count;
   }
 };
 
-// Class for object that holds ForOfPIC chain.
-class ForOfPICObject : public NativeObject {
- public:
-  static const JSClass class_;
-};
-
 /*
  *  ForOfPIC defines a PIC category for optimizing for-of operations.
  */
 struct ForOfPIC {
   /* Forward declarations so template-substitution works. */
   class Stub;
   class Chain;
 
@@ -216,21 +209,24 @@ struct ForOfPIC {
     void reset(JSContext* cx);
 
     // Erase the stub chain.
     void eraseChain(JSContext* cx);
 
     void freeAllStubs(JSFreeOp* fop);
   };
 
+  // Class for object that holds ForOfPIC chain.
+  static const JSClass class_;
+
   static NativeObject* createForOfPICObject(JSContext* cx,
                                             Handle<GlobalObject*> global);
 
   static inline Chain* fromJSObject(NativeObject* obj) {
-    MOZ_ASSERT(obj->is<ForOfPICObject>());
+    MOZ_ASSERT(obj->getClass() == &ForOfPIC::class_);
     return (ForOfPIC::Chain*)obj->getPrivate();
   }
   static inline Chain* getOrCreate(JSContext* cx) {
     NativeObject* obj = cx->global()->getForOfPICObject();
     if (obj) {
       return fromJSObject(obj);
     }
     return create(cx);
--- a/js/src/vm/ProxyObject.cpp
+++ b/js/src/vm/ProxyObject.cpp
@@ -40,209 +40,100 @@ static gc::AllocKind GetProxyGCObjectKin
   gc::AllocKind kind = gc::GetGCObjectKind(nslots);
   if (handler->finalizeInBackground(priv)) {
     kind = ForegroundToBackgroundAllocKind(kind);
   }
 
   return kind;
 }
 
-void ProxyObject::init(const BaseProxyHandler* handler, HandleValue priv,
-                       JSContext* cx) {
-  setInlineValueArray();
-
-  detail::ProxyValueArray* values = detail::GetProxyDataLayout(this)->values();
-  values->init(numReservedSlots());
-
-  data.handler = handler;
-
-  if (IsCrossCompartmentWrapper(this)) {
-    MOZ_ASSERT(cx->global() == &cx->compartment()->globalForNewCCW());
-    setCrossCompartmentPrivate(priv);
-  } else {
-    setSameCompartmentPrivate(priv);
-  }
-}
-
 /* static */
 ProxyObject* ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler,
                               HandleValue priv, TaggedProto proto_,
-                              const JSClass* clasp) {
+                              const ProxyOptions& options) {
   Rooted<TaggedProto> proto(cx, proto_);
 
-  MOZ_ASSERT(clasp->isProxy());
+  const JSClass* clasp = options.clasp();
+
+#ifdef DEBUG
   MOZ_ASSERT(isValidProxyClass(clasp));
   MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
   MOZ_ASSERT_IF(proto.isObject(),
                 cx->compartment() == proto.toObject()->compartment());
   MOZ_ASSERT(clasp->hasFinalize());
-
-#ifdef DEBUG
   if (priv.isGCThing()) {
     JS::AssertCellIsNotGray(priv.toGCThing());
   }
 #endif
 
   /*
    * Eagerly mark properties unknown for proxies, so we don't try to track
    * their properties and so that we don't need to walk the compartment if
    * their prototype changes later.  But don't do this for DOM proxies,
    * because we want to be able to keep track of them in typesets in useful
    * ways.
    */
-  if (proto.isObject() && !clasp->isDOMClass()) {
+  if (proto.isObject() && !options.singleton() && !clasp->isDOMClass()) {
     ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
     RootedObject protoObj(cx, proto.toObject());
     if (!JSObject::setNewGroupUnknown(cx, realm, clasp, protoObj)) {
       return nullptr;
     }
   }
 
-  gc::AllocKind allocKind = GetProxyGCObjectKind(clasp, handler, priv);
-
-  Realm* realm = cx->realm();
-
-  AutoSetNewObjectMetadata metadata(cx);
-  // Try to look up the group and shape in the NewProxyCache.
-  RootedObjectGroup group(cx);
-  RootedShape shape(cx);
-  if (!realm->newProxyCache.lookup(clasp, proto, group.address(),
-                                   shape.address())) {
-    group = ObjectGroup::defaultNewGroup(cx, clasp, proto, nullptr);
-    if (!group) {
-      return nullptr;
-    }
-
-    shape = EmptyShape::getInitialShape(cx, clasp, proto, /* nfixed = */ 0);
-    if (!shape) {
-      return nullptr;
-    }
-
-    realm->newProxyCache.add(group, shape);
+  // Ensure that the wrapper has the same lifetime assumptions as the
+  // wrappee. Prefer to allocate in the nursery, when possible.
+  NewObjectKind newKind = NurseryAllocatedProxy;
+  if (options.singleton()) {
+    MOZ_ASSERT(priv.isNull() ||
+               (priv.isGCThing() && priv.toGCThing()->isTenured()));
+    newKind = SingletonObject;
+  } else if ((priv.isGCThing() && priv.toGCThing()->isTenured()) ||
+             !handler->canNurseryAllocate()) {
+    newKind = TenuredObject;
   }
 
-  MOZ_ASSERT(group->realm() == realm);
-  MOZ_ASSERT(shape->zone() == cx->zone());
-  MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(group.address()));
-  MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(shape.address()));
+  gc::AllocKind allocKind = GetProxyGCObjectKind(clasp, handler, priv);
+
+  AutoSetNewObjectMetadata metadata(cx);
+  // Note: this will initialize the object's |data| to strange values, but we
+  // will immediately overwrite those below.
+  ProxyObject* proxy;
+  JS_TRY_VAR_OR_RETURN_NULL(cx, proxy,
+                            create(cx, clasp, proto, allocKind, newKind));
 
-  // Ensure that the wrapper has the same lifetime assumptions as the
-  // wrappee. Prefer to allocate in the nursery, when possible.
-  bool privIsTenured = priv.isGCThing() && priv.toGCThing()->isTenured();
-  gc::InitialHeap heap = privIsTenured || !handler->canNurseryAllocate()
-                             ? gc::TenuredHeap
-                             : gc::DefaultHeap;
+  proxy->setInlineValueArray();
+
+  detail::ProxyValueArray* values = detail::GetProxyDataLayout(proxy)->values();
+  values->init(proxy->numReservedSlots());
 
-  debugCheckNewObject(group, shape, allocKind, heap);
-
-  JSObject* obj =
-      AllocateObject(cx, allocKind, /* nDynamicSlots = */ 0, heap, clasp);
-  if (!obj) {
-    return nullptr;
+  proxy->data.handler = handler;
+  if (IsCrossCompartmentWrapper(proxy)) {
+    MOZ_ASSERT(cx->global() == &cx->compartment()->globalForNewCCW());
+    proxy->setCrossCompartmentPrivate(priv);
+  } else {
+    proxy->setSameCompartmentPrivate(priv);
   }
 
-  ProxyObject* proxy = static_cast<ProxyObject*>(obj);
-  proxy->initGroup(group);
-  proxy->initShape(shape);
+  if (newKind == SingletonObject) {
+    Rooted<ProxyObject*> rootedProxy(cx, proxy);
+    if (!JSObject::setSingleton(cx, rootedProxy)) {
+      return nullptr;
+    }
+    return rootedProxy;
+  }
 
-  MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
-  realm->setObjectPendingMetadata(cx, proxy);
-
-  js::gc::gcTracer.traceCreateObject(proxy);
-
-  proxy->init(handler, priv, cx);
-
-  // Don't track types of properties of non-DOM and non-singleton proxies.
+  /* Don't track types of properties of non-DOM and non-singleton proxies. */
   if (!clasp->isDOMClass()) {
     MarkObjectGroupUnknownProperties(cx, proxy->group());
   }
 
   return proxy;
 }
 
-/* static */
-ProxyObject* ProxyObject::NewSingleton(JSContext* cx,
-                                       const BaseProxyHandler* handler,
-                                       HandleValue priv, TaggedProto proto_,
-                                       const JSClass* clasp) {
-  Rooted<TaggedProto> proto(cx, proto_);
-
-  MOZ_ASSERT(clasp->isProxy());
-  MOZ_ASSERT(isValidProxyClass(clasp));
-  MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
-  MOZ_ASSERT_IF(proto.isObject(),
-                cx->compartment() == proto.toObject()->compartment());
-  MOZ_ASSERT(clasp->hasFinalize());
-
-#ifdef DEBUG
-  if (priv.isGCThing()) {
-    JS::AssertCellIsNotGray(priv.toGCThing());
-  }
-#endif
-
-  gc::AllocKind allocKind = GetProxyGCObjectKind(clasp, handler, priv);
-
-  AutoSetNewObjectMetadata metadata(cx);
-  Rooted<ProxyObject*> proxy(cx);
-  {
-    Realm* realm = cx->realm();
-
-    // We're creating a singleton, so go straight to getting a singleton group,
-    // from the singleton group cache (or creating it freshly if needed).
-    RootedObjectGroup group(cx, ObjectGroup::lazySingletonGroup(
-                                    cx, ObjectGroupRealm::getForNewObject(cx),
-                                    realm, clasp, proto));
-    if (!group) {
-      return nullptr;
-    }
-
-    MOZ_ASSERT(group->realm() == realm);
-    MOZ_ASSERT(group->singleton());
-    MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(group.address()));
-
-    // Also retrieve an empty shape.  Unlike for non-singleton proxies, this
-    // shape lookup is not cached in |realm->newProxyCache|.  We could cache it
-    // there, but distinguishing group/shape for singleton and non-singleton
-    // proxies would increase contention on the cache (and might end up evicting
-    // non-singleton cases where performance really matters).  Assume that
-    // singleton proxies are rare, and don't bother caching their shapes/groups.
-    RootedShape shape(
-        cx, EmptyShape::getInitialShape(cx, clasp, proto, /* nfixed = */ 0));
-    if (!shape) {
-      return nullptr;
-    }
-
-    MOZ_ASSERT(shape->zone() == cx->zone());
-    MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(shape.address()));
-
-    gc::InitialHeap heap = gc::TenuredHeap;
-    debugCheckNewObject(group, shape, allocKind, heap);
-
-    JSObject* obj =
-        AllocateObject(cx, allocKind, /* nDynamicSlots = */ 0, heap, clasp);
-    if (!obj) {
-      return nullptr;
-    }
-
-    proxy = static_cast<ProxyObject*>(obj);
-    proxy->initGroup(group);
-    proxy->initShape(shape);
-
-    MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
-    realm->setObjectPendingMetadata(cx, proxy);
-
-    js::gc::gcTracer.traceCreateObject(proxy);
-  }
-
-  proxy->init(handler, priv, cx);
-
-  MOZ_ASSERT(proxy->isSingleton());
-  return proxy;
-}
-
 gc::AllocKind ProxyObject::allocKindForTenure() const {
   MOZ_ASSERT(usingInlineValueArray());
   Value priv = private_();
   return GetProxyGCObjectKind(getClass(), data.handler, priv);
 }
 
 void ProxyObject::setCrossCompartmentPrivate(const Value& priv) {
   setPrivate(priv);
@@ -270,14 +161,65 @@ void ProxyObject::nuke() {
   // The proxy's reserved slots are not cleared and will continue to be
   // traced. This avoids the possibility of triggering write barriers while
   // nuking proxies in dead compartments which could otherwise cause those
   // compartments to be kept alive. Note that these are slots cannot hold
   // cross compartment pointers, so this cannot cause the target compartment
   // to leak.
 }
 
+/* static */ JS::Result<ProxyObject*, JS::OOM&> ProxyObject::create(
+    JSContext* cx, const JSClass* clasp, Handle<TaggedProto> proto,
+    gc::AllocKind allocKind, NewObjectKind newKind) {
+  MOZ_ASSERT(clasp->isProxy());
+
+  Realm* realm = cx->realm();
+  RootedObjectGroup group(cx);
+  RootedShape shape(cx);
+
+  // Try to look up the group and shape in the NewProxyCache.
+  if (!realm->newProxyCache.lookup(clasp, proto, group.address(),
+                                   shape.address())) {
+    group = ObjectGroup::defaultNewGroup(cx, clasp, proto, nullptr);
+    if (!group) {
+      return cx->alreadyReportedOOM();
+    }
+
+    shape = EmptyShape::getInitialShape(cx, clasp, proto, /* nfixed = */ 0);
+    if (!shape) {
+      return cx->alreadyReportedOOM();
+    }
+
+    realm->newProxyCache.add(group, shape);
+  }
+
+  MOZ_ASSERT(group->realm() == realm);
+  MOZ_ASSERT(shape->zone() == cx->zone());
+  MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(group.address()));
+  MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(shape.address()));
+
+  gc::InitialHeap heap = GetInitialHeap(newKind, group);
+  debugCheckNewObject(group, shape, allocKind, heap);
+
+  JSObject* obj =
+      js::AllocateObject(cx, allocKind, /* nDynamicSlots = */ 0, heap, clasp);
+  if (!obj) {
+    return cx->alreadyReportedOOM();
+  }
+
+  ProxyObject* pobj = static_cast<ProxyObject*>(obj);
+  pobj->initGroup(group);
+  pobj->initShape(shape);
+
+  MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
+  cx->realm()->setObjectPendingMetadata(cx, pobj);
+
+  js::gc::gcTracer.traceCreateObject(pobj);
+
+  return pobj;
+}
+
 JS_FRIEND_API void js::detail::SetValueInProxy(Value* slot,
                                                const Value& value) {
   // Slots in proxies are not GCPtrValues, so do a cast whenever assigning
   // values to them which might trigger a barrier.
   *reinterpret_cast<GCPtrValue*>(slot) = value;
 }
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -32,43 +32,41 @@ class ProxyObject : public JSObject {
                   "proxy object size must match GC thing size");
     static_assert(offsetof(ProxyObject, data) == detail::ProxyDataOffset,
                   "proxy object layout must match shadow interface");
     static_assert(offsetof(ProxyObject, data.reservedSlots) ==
                       offsetof(shadow::Object, slots),
                   "Proxy reservedSlots must overlay native object slots field");
   }
 
+  static JS::Result<ProxyObject*, JS::OOM&> create(JSContext* cx,
+                                                   const JSClass* clasp,
+                                                   Handle<TaggedProto> proto,
+                                                   js::gc::AllocKind allocKind,
+                                                   js::NewObjectKind newKind);
+
  public:
   static ProxyObject* New(JSContext* cx, const BaseProxyHandler* handler,
                           HandleValue priv, TaggedProto proto_,
-                          const JSClass* clasp);
-
-  static ProxyObject* NewSingleton(JSContext* cx,
-                                   const BaseProxyHandler* handler,
-                                   HandleValue priv, TaggedProto proto_,
-                                   const JSClass* clasp);
-
-  void init(const BaseProxyHandler* handler, HandleValue priv, JSContext* cx);
+                          const ProxyOptions& options);
 
   // Proxies usually store their ProxyValueArray inline in the object.
   // There's one unfortunate exception: when a proxy is swapped with another
   // object, and the sizes don't match, we malloc the ProxyValueArray.
   void* inlineDataStart() const {
     return (void*)(uintptr_t(this) + sizeof(ProxyObject));
   }
   bool usingInlineValueArray() const {
     return data.values() == inlineDataStart();
   }
   void setInlineValueArray() {
     data.reservedSlots =
         &reinterpret_cast<detail::ProxyValueArray*>(inlineDataStart())
              ->reservedSlots;
   }
-
   MOZ_MUST_USE bool initExternalValueArrayAfterSwap(JSContext* cx,
                                                     HandleValueVector values);
 
   const Value& private_() const { return GetProxyPrivate(this); }
 
   void setCrossCompartmentPrivate(const Value& priv);
   void setSameCompartmentPrivate(const Value& priv);
 
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -67,17 +67,17 @@ static_assert(RegExpFlag::DotAll == REGE
 static_assert(RegExpFlag::Unicode == REGEXP_UNICODE_FLAG,
               "self-hosted JS and /u flag bits must agree");
 static_assert(RegExpFlag::Sticky == REGEXP_STICKY_FLAG,
               "self-hosted JS and /y flag bits must agree");
 
 RegExpObject* js::RegExpAlloc(JSContext* cx, NewObjectKind newKind,
                               HandleObject proto /* = nullptr */) {
   Rooted<RegExpObject*> regexp(
-      cx, NewObjectWithClassProtoAndKind<RegExpObject>(cx, proto, newKind));
+      cx, NewObjectWithClassProto<RegExpObject>(cx, proto, newKind));
   if (!regexp) {
     return nullptr;
   }
 
   regexp->initPrivate(nullptr);
 
   if (!EmptyShape::ensureInitialCustomShape<RegExpObject>(cx, regexp)) {
     return nullptr;
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -554,17 +554,17 @@ SavedFrame* SavedFrame::create(JSContext
 
   RootedObject proto(cx,
                      GlobalObject::getOrCreateSavedFramePrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
   cx->check(proto);
 
-  return NewTenuredObjectWithGivenProto<SavedFrame>(cx, proto);
+  return NewObjectWithGivenProto<SavedFrame>(cx, proto, TenuredObject);
 }
 
 bool SavedFrame::isSelfHosted(JSContext* cx) {
   JSAtom* source = getSource();
   return source == cx->names().selfHosted;
 }
 
 bool SavedFrame::isWasm() {
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -795,17 +795,17 @@ static bool intrinsic_IsPackedArray(JSCo
   args.rval().setBoolean(IsPackedArray(&args[0].toObject()));
   return true;
 }
 
 bool js::intrinsic_NewArrayIterator(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   MOZ_ASSERT(args.length() == 0);
 
-  JSObject* obj = NewArrayIterator(cx);
+  JSObject* obj = NewArrayIteratorObject(cx);
   if (!obj) {
     return false;
   }
 
   args.rval().setObject(*obj);
   return true;
 }
 
@@ -885,31 +885,31 @@ static bool intrinsic_CreateSetIteration
   args.rval().setObject(*result);
   return true;
 }
 
 bool js::intrinsic_NewStringIterator(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   MOZ_ASSERT(args.length() == 0);
 
-  JSObject* obj = NewStringIterator(cx);
+  JSObject* obj = NewStringIteratorObject(cx);
   if (!obj) {
     return false;
   }
 
   args.rval().setObject(*obj);
   return true;
 }
 
 bool js::intrinsic_NewRegExpStringIterator(JSContext* cx, unsigned argc,
                                            Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   MOZ_ASSERT(args.length() == 0);
 
-  JSObject* obj = NewRegExpStringIterator(cx);
+  JSObject* obj = NewRegExpStringIteratorObject(cx);
   if (!obj) {
     return false;
   }
 
   args.rval().setObject(*obj);
   return true;
 }
 
@@ -2949,17 +2949,17 @@ static JSObject* CloneObject(JSContext* 
       MOZ_CRASH();
     }
     RootedString str(cx, CloneString(cx, &selfHostedString->asLinear()));
     if (!str) {
       return nullptr;
     }
     clone = StringObject::create(cx, str);
   } else if (selfHostedObject->is<ArrayObject>()) {
-    clone = NewTenuredDenseEmptyArray(cx, nullptr);
+    clone = NewDenseEmptyArray(cx, nullptr, TenuredObject);
   } else {
     MOZ_ASSERT(selfHostedObject->isNative());
     clone = NewObjectWithGivenProto(
         cx, selfHostedObject->getClass(), nullptr,
         selfHostedObject->asTenured().getAllocKind(), SingletonObject);
   }
   if (!clone) {
     return nullptr;
--- a/js/src/vm/StringObject-inl.h
+++ b/js/src/vm/StringObject-inl.h
@@ -31,17 +31,17 @@ namespace js {
   return true;
 }
 
 /* static */ inline StringObject* StringObject::create(JSContext* cx,
                                                        HandleString str,
                                                        HandleObject proto,
                                                        NewObjectKind newKind) {
   Rooted<StringObject*> obj(
-      cx, NewObjectWithClassProtoAndKind<StringObject>(cx, proto, newKind));
+      cx, NewObjectWithClassProto<StringObject>(cx, proto, newKind));
   if (!obj) {
     return nullptr;
   }
   if (!StringObject::init(cx, obj, str)) {
     return nullptr;
   }
   return obj;
 }
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -40,17 +40,16 @@
 #include "vm/Shape.h"
 #include "vm/Time.h"
 
 #include "gc/GC-inl.h"
 #include "gc/Marking-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/NativeObject-inl.h"
-#include "vm/ObjectGroup-inl.h"  // JSObject::setSingleton
 
 using namespace js;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
 using mozilla::PodArrayZero;
 using mozilla::PodCopy;
 
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -4050,17 +4050,17 @@ static const JSFunctionSpec WebAssembly_
 static JSObject* CreateWebAssemblyObject(JSContext* cx, JSProtoKey key) {
   MOZ_RELEASE_ASSERT(HasSupport(cx));
 
   Handle<GlobalObject*> global = cx->global();
   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
-  return NewSingletonObjectWithGivenProto(cx, &WebAssemblyClass, proto);
+  return NewObjectWithGivenProto(cx, &WebAssemblyClass, proto, SingletonObject);
 }
 
 static bool WebAssemblyClassFinish(JSContext* cx, HandleObject wasm,
                                    HandleObject proto) {
   struct NameAndProtoKey {
     const char* const name;
     JSProtoKey key;
   };
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -101,17 +101,18 @@ void AnyRef::trace(JSTracer* trc) {
     TraceManuallyBarrieredEdge(trc, &value_, "wasm anyref referent");
   }
 }
 
 const JSClass WasmValueBox::class_ = {
     "WasmValueBox", JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS)};
 
 WasmValueBox* WasmValueBox::create(JSContext* cx, HandleValue val) {
-  WasmValueBox* obj = NewObjectWithGivenProto<WasmValueBox>(cx, nullptr);
+  WasmValueBox* obj = (WasmValueBox*)NewObjectWithGivenProto(
+      cx, &WasmValueBox::class_, nullptr);
   if (!obj) {
     return nullptr;
   }
   obj->setFixedSlot(VALUE_SLOT, val);
   return obj;
 }
 
 bool wasm::BoxAnyRef(JSContext* cx, HandleValue val, MutableHandleAnyRef addr) {