Bug 1370608 part 1 - Move newEnumerate hook from ObjectOps to ClassOps. r=evilpie,bz
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 14 Jun 2017 10:37:44 +0200
changeset 415110 f30ec8ac7239eda68629aba08416c3a40e0dea58
parent 414991 3e62702024b0b588d8b00a65d2ac7e04801326b2
child 415111 51f0acbee7c96a1bc2f1b390c37599cf30284fbd
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie, bz
bugs1370608
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1370608 part 1 - Move newEnumerate hook from ObjectOps to ClassOps. r=evilpie,bz
devtools/shared/heapsnapshot/tests/gtest/DevTools.h
dom/bindings/BindingUtils.cpp
dom/bindings/Codegen.py
dom/bindings/SimpleGlobalObject.cpp
dom/indexedDB/ActorsParent.cpp
dom/plugins/base/nsJSNPRuntime.cpp
dom/xbl/nsXBLBinding.cpp
js/public/Class.h
js/src/builtin/DataViewObject.cpp
js/src/builtin/Intl.cpp
js/src/builtin/MapObject.cpp
js/src/builtin/ModuleObject.cpp
js/src/builtin/SIMD.cpp
js/src/builtin/TestingFunctions.cpp
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/builtin/WeakMapObject.cpp
js/src/ctypes/CTypes.cpp
js/src/ctypes/Library.cpp
js/src/gdb/gdb-tests.cpp
js/src/jsapi-tests/testChromeBuffer.cpp
js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
js/src/jsapi-tests/testLookup.cpp
js/src/jsapi-tests/testNewObject.cpp
js/src/jsapi-tests/testPersistentRooted.cpp
js/src/jsapi-tests/testResolveRecursion.cpp
js/src/jsapi-tests/testWeakMap.cpp
js/src/jsapi-tests/tests.h
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsiter.cpp
js/src/jsobj.h
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/perf/jsperf.cpp
js/src/proxy/Proxy.cpp
js/src/shell/OSObject.cpp
js/src/shell/js.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/ArrayBufferObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/EnvironmentObject.cpp
js/src/vm/EnvironmentObject.h
js/src/vm/GlobalObject.cpp
js/src/vm/HelperThreads.cpp
js/src/vm/PIC.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpStatics.cpp
js/src/vm/SavedStacks.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/SharedArrayObject.cpp
js/src/vm/TypedArrayObject.cpp
js/src/vm/UnboxedObject.cpp
js/src/vm/UnboxedObject.h
js/src/wasm/WasmJS.cpp
js/xpconnect/public/xpc_make_class.h
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/XPCShellImpl.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
netwerk/base/ProxyAutoConfig.cpp
toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
xpcom/tests/gtest/TestGCPostBarriers.cpp
--- a/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
+++ b/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
@@ -69,17 +69,17 @@ struct DevTools : public ::testing::Test
             (unsigned int) report->lineno,
             message);
   }
 
   static const JSClass* getGlobalClass() {
     static const JSClassOps globalClassOps = {
       nullptr, nullptr, nullptr, nullptr,
       nullptr, nullptr, nullptr, nullptr,
-      nullptr, nullptr, nullptr,
+      nullptr, nullptr, nullptr, nullptr,
       JS_GlobalObjectTraceHook
     };
     static const JSClass globalClass = {
       "global", JSCLASS_GLOBAL_FLAGS,
       &globalClassOps
     };
     return &globalClass;
   }
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1979,16 +1979,17 @@ NativePropertyHooks sEmptyNativeProperty
 };
 
 const js::ClassOps sBoringInterfaceObjectClassClassOps = {
     nullptr,               /* addProperty */
     nullptr,               /* delProperty */
     nullptr,               /* getProperty */
     nullptr,               /* setProperty */
     nullptr,               /* enumerate */
+    nullptr,               /* newEnumerate */
     nullptr,               /* resolve */
     nullptr,               /* mayResolve */
     nullptr,               /* finalize */
     ThrowingConstructor,   /* call */
     nullptr,               /* hasInstance */
     ThrowingConstructor,   /* construct */
     nullptr,               /* trace */
 };
@@ -1999,17 +2000,16 @@ const js::ObjectOps sInterfaceObjectClas
   nullptr, /* hasProperty */
   nullptr, /* getProperty */
   nullptr, /* setProperty */
   nullptr, /* getOwnPropertyDescriptor */
   nullptr, /* deleteProperty */
   nullptr, /* watch */
   nullptr, /* unwatch */
   nullptr, /* getElements */
-  nullptr, /* enumerate */
   InterfaceObjectToString, /* funToString */
 };
 
 bool
 GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
                        JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
                        bool* found, JS::MutableHandle<JS::Value> vp)
 {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -21,17 +21,17 @@ AUTOGENERATED_WITH_SOURCE_WARNING_COMMEN
 ADDPROPERTY_HOOK_NAME = '_addProperty'
 FINALIZE_HOOK_NAME = '_finalize'
 OBJECT_MOVED_HOOK_NAME = '_objectMoved'
 CONSTRUCT_HOOK_NAME = '_constructor'
 LEGACYCALLER_HOOK_NAME = '_legacycaller'
 HASINSTANCE_HOOK_NAME = '_hasInstance'
 RESOLVE_HOOK_NAME = '_resolve'
 MAY_RESOLVE_HOOK_NAME = '_mayResolve'
-ENUMERATE_HOOK_NAME = '_enumerate'
+NEW_ENUMERATE_HOOK_NAME = '_newEnumerate'
 ENUM_ENTRY_VARIABLE_NAME = 'strings'
 INSTANCE_RESERVED_SLOTS = 1
 
 
 def memberReservedSlot(member, descriptor):
     return ("(DOM_INSTANCE_RESERVED_SLOTS + %d)" %
             member.slotIndices[descriptor.interface.identifier.name])
 
@@ -450,60 +450,41 @@ class CGDOMJSClass(CGThing):
             reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
         else:
             classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
             traceHook = 'nullptr'
             reservedSlots = slotCount
         if self.descriptor.interface.hasProbablyShortLivingWrapper():
             classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE"
 
-        objectOps = "JS_NULL_OBJECT_OPS"
-        objectOpsString = ""
         if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
             resolveHook = RESOLVE_HOOK_NAME
             mayResolveHook = MAY_RESOLVE_HOOK_NAME
             enumerateHook = "nullptr"
-            objectOpsString = fill(
-                """
-                const js::ObjectOps sInstanceObjectOps = {
-                  nullptr, /* lookupProperty */
-                  nullptr, /* defineProperty */
-                  nullptr, /* hasProperty */
-                  nullptr, /* getProperty */
-                  nullptr, /* setProperty */
-                  nullptr, /* getOwnPropertyDescriptor */
-                  nullptr, /* deleteProperty */
-                  nullptr, /* watch */
-                  nullptr, /* unwatch */
-                  nullptr, /* getElements */
-                  ${enumerate}, /* enumerate */
-                  nullptr, /* funToString */
-                };
-
-                """,
-                enumerate=ENUMERATE_HOOK_NAME)
-            objectOps = "&sInstanceObjectOps"
+            newEnumerateHook = NEW_ENUMERATE_HOOK_NAME
         elif self.descriptor.isGlobal():
             resolveHook = "mozilla::dom::ResolveGlobal"
             mayResolveHook = "mozilla::dom::MayResolveGlobal"
             enumerateHook = "mozilla::dom::EnumerateGlobal"
+            newEnumerateHook = "nullptr"
         else:
             resolveHook = "nullptr"
             mayResolveHook = "nullptr"
             enumerateHook = "nullptr"
+            newEnumerateHook = "nullptr"
 
         return fill(
             """
-            $*{objectOpsString}
             static const js::ClassOps sClassOps = {
               ${addProperty}, /* addProperty */
               nullptr,               /* delProperty */
               nullptr,               /* getProperty */
               nullptr,               /* setProperty */
               ${enumerate}, /* enumerate */
+              ${newEnumerate}, /* newEnumerate */
               ${resolve}, /* resolve */
               ${mayResolve}, /* mayResolve */
               ${finalize}, /* finalize */
               ${call}, /* call */
               nullptr,               /* hasInstance */
               nullptr,               /* construct */
               ${trace}, /* trace */
             };
@@ -514,37 +495,36 @@ class CGDOMJSClass(CGThing):
             };
 
             static const DOMJSClass sClass = {
               { "${name}",
                 ${flags},
                 &sClassOps,
                 JS_NULL_CLASS_SPEC,
                 &sClassExtension,
-                ${objectOps}
+                JS_NULL_OBJECT_OPS
               },
               $*{descriptor}
             };
             static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS,
                           "Must have the right minimal number of reserved slots.");
             static_assert(${reservedSlots} >= ${slotCount},
                           "Must have enough reserved slots.");
             """,
-            objectOpsString=objectOpsString,
             name=self.descriptor.interface.identifier.name,
             flags=classFlags,
             addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'nullptr',
             enumerate=enumerateHook,
+            newEnumerate=newEnumerateHook,
             resolve=resolveHook,
             mayResolve=mayResolveHook,
             finalize=FINALIZE_HOOK_NAME,
             call=callHook,
             trace=traceHook,
             objectMoved=objectMovedHook,
-            objectOps=objectOps,
             descriptor=DOMClass(self.descriptor),
             instanceReservedSlots=INSTANCE_RESERVED_SLOTS,
             reservedSlots=reservedSlots,
             slotCount=slotCount)
 
 
 class CGDOMProxyJSClass(CGThing):
     """
@@ -786,16 +766,17 @@ class CGInterfaceObjectJSClass(CGThing):
             ret = fill(
                 """
                 static const js::ClassOps sInterfaceObjectClassOps = {
                     nullptr,               /* addProperty */
                     nullptr,               /* delProperty */
                     nullptr,               /* getProperty */
                     nullptr,               /* setProperty */
                     nullptr,               /* enumerate */
+                    nullptr,               /* newEnumerate */
                     nullptr,               /* resolve */
                     nullptr,               /* mayResolve */
                     nullptr,               /* finalize */
                     ${ctorname}, /* call */
                     nullptr,               /* hasInstance */
                     ${ctorname}, /* construct */
                     nullptr,               /* trace */
                 };
@@ -8919,17 +8900,17 @@ class CGEnumerateHook(CGAbstractBindingM
         assert descriptor.interface.getExtendedAttribute("NeedResolve")
 
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'obj'),
                 Argument('JS::AutoIdVector&', 'properties'),
                 Argument('bool', 'enumerableOnly')]
         # Our "self" is actually the "obj" argument in this case, not the thisval.
         CGAbstractBindingMethod.__init__(
-            self, descriptor, ENUMERATE_HOOK_NAME,
+            self, descriptor, NEW_ENUMERATE_HOOK_NAME,
             args, getThisObj="", callArgs="")
 
     def generate_code(self):
         return CGGeneric(dedent("""
             binding_detail::FastErrorResult rv;
             self->GetOwnPropertyNames(cx, properties, enumerableOnly, rv);
             if (rv.MaybeSetPendingException(cx)) {
               return false;
--- a/dom/bindings/SimpleGlobalObject.cpp
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -59,16 +59,17 @@ SimpleGlobal_moved(JSObject *obj, const 
 }
 
 static const js::ClassOps SimpleGlobalClassOps = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     JS_EnumerateStandardClasses,
+    nullptr,
     JS_ResolveStandardClass,
     JS_MayResolveStandardClass,
     SimpleGlobal_finalize,
     nullptr,
     nullptr,
     nullptr,
     JS_GlobalObjectTraceHook,
 };
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -25240,16 +25240,17 @@ CreateIndexOp::DoDatabaseWork(DatabaseCo
 }
 
 static const JSClassOps sNormalJSContextGlobalClassOps = {
   /* addProperty */ nullptr,
   /* delProperty */ nullptr,
   /* getProperty */ nullptr,
   /* setProperty */ nullptr,
   /* enumerate */ nullptr,
+  /* newEnumerate */ nullptr,
   /* resolve */ nullptr,
   /* mayResolve */ nullptr,
   /* finalize */ nullptr,
   /* call */ nullptr,
   /* hasInstance */ nullptr,
   /* construct */ nullptr,
   /* trace */ JS_GlobalObjectTraceHook
 };
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -187,18 +187,18 @@ NPObjWrapper_DelProperty(JSContext *cx, 
 static bool
 NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                          JS::MutableHandle<JS::Value> vp, JS::ObjectOpResult &result);
 
 static bool
 NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
 
 static bool
-NPObjWrapper_Enumerate(JSContext *cx, JS::Handle<JSObject*> obj, JS::AutoIdVector &properties,
-                       bool enumerableOnly);
+NPObjWrapper_NewEnumerate(JSContext *cx, JS::Handle<JSObject*> obj, JS::AutoIdVector &properties,
+                          bool enumerableOnly);
 
 static bool
 NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                      bool *resolvedp);
 
 static void
 NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj);
 
@@ -219,53 +219,40 @@ CreateNPObjectMember(NPP npp, JSContext 
                      JS::Handle<jsid> id,  NPVariant* getPropertyResult,
                      JS::MutableHandle<JS::Value> vp);
 
 const static js::ClassOps sNPObjectJSWrapperClassOps = {
     NPObjWrapper_AddProperty,
     NPObjWrapper_DelProperty,
     NPObjWrapper_GetProperty,
     NPObjWrapper_SetProperty,
-    nullptr,
+    nullptr,                    /* enumerate */
+    NPObjWrapper_NewEnumerate,
     NPObjWrapper_Resolve,
     nullptr,                    /* mayResolve */
     NPObjWrapper_Finalize,
     NPObjWrapper_Call,
     nullptr,                    /* hasInstance */
     NPObjWrapper_Construct,
     nullptr,                    /* trace */
 };
 
 const static js::ClassExtension sNPObjectJSWrapperClassExtension = {
     nullptr,                    /* weakmapKeyDelegateOp */
     NPObjWrapper_ObjectMoved
 };
 
-const static js::ObjectOps sNPObjectJSWrapperObjectOps = {
-    nullptr, // lookupProperty
-    nullptr, // defineProperty
-    nullptr, // hasProperty
-    nullptr, // getProperty
-    nullptr, // setProperty
-    nullptr, // getOwnPropertyDescriptor
-    nullptr, // deleteProperty
-    nullptr, nullptr, // watch/unwatch
-    nullptr, // getElements
-    NPObjWrapper_Enumerate,
-    nullptr,
-};
-
 const static js::Class sNPObjectJSWrapperClass = {
     NPRUNTIME_JSCLASS_NAME,
     JSCLASS_HAS_PRIVATE |
     JSCLASS_FOREGROUND_FINALIZE,
     &sNPObjectJSWrapperClassOps,
     JS_NULL_CLASS_SPEC,
     &sNPObjectJSWrapperClassExtension,
-    &sNPObjectJSWrapperObjectOps
+    JS_NULL_OBJECT_OPS
 };
 
 typedef struct NPObjectMemberPrivate {
     JS::Heap<JSObject *> npobjWrapper;
     JS::Heap<JS::Value> fieldValue;
     JS::Heap<jsid> methodName;
     NPP   npp;
 } NPObjectMemberPrivate;
@@ -283,17 +270,17 @@ NPObjectMember_Call(JSContext *cx, unsig
 static void
 NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
 
 static bool
 NPObjectMember_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
 
 static const JSClassOps sNPObjectMemberClassOps = {
   nullptr, nullptr, NPObjectMember_GetProperty, nullptr,
-  nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr,
   NPObjectMember_Finalize, NPObjectMember_Call,
   nullptr, nullptr, NPObjectMember_Trace
 };
 
 static const JSClass sNPObjectMemberClass = {
   "NPObject Ambiguous Member class",
   JSCLASS_HAS_PRIVATE |
   JSCLASS_FOREGROUND_FINALIZE,
@@ -1617,18 +1604,18 @@ CallNPMethod(JSContext *cx, unsigned arg
   JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
       return false;
 
   return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false);
 }
 
 static bool
-NPObjWrapper_Enumerate(JSContext *cx, JS::Handle<JSObject*> obj,
-                       JS::AutoIdVector &properties, bool enumerableOnly)
+NPObjWrapper_NewEnumerate(JSContext *cx, JS::Handle<JSObject*> obj,
+                          JS::AutoIdVector &properties, bool enumerableOnly)
 {
   NPObject *npobj = GetNPObject(cx, obj);
   if (!npobj || !npobj->_class) {
     ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
     return false;
   }
 
   PluginDestructionGuard pdg(LookupNPP(npobj));
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -82,17 +82,17 @@ XBLEnumerate(JSContext *cx, JS::Handle<J
     static_cast<nsXBLPrototypeBinding*>(::JS_GetReservedSlot(obj, 0).toPrivate());
   MOZ_ASSERT(protoBinding);
 
   return protoBinding->ResolveAllFields(cx, obj);
 }
 
 static const JSClassOps gPrototypeJSClassOps = {
     nullptr, nullptr, nullptr, nullptr,
-    XBLEnumerate, nullptr,
+    XBLEnumerate, nullptr, nullptr,
     nullptr, XBLFinalize,
     nullptr, nullptr, nullptr, nullptr
 };
 
 static const JSClass gPrototypeJSClass = {
     "XBL prototype JSClass",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_PRIVATE_IS_NSISUPPORTS |
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -602,16 +602,17 @@ typedef void
     uint32_t flags; \
     const ClassOpsType* cOps; \
     \
     JSAddPropertyOp    getAddProperty() const { return cOps ? cOps->addProperty : nullptr; } \
     JSDeletePropertyOp getDelProperty() const { return cOps ? cOps->delProperty : nullptr; } \
     JSGetterOp         getGetProperty() const { return cOps ? cOps->getProperty : nullptr; } \
     JSSetterOp         getSetProperty() const { return cOps ? cOps->setProperty : nullptr; } \
     JSEnumerateOp      getEnumerate()   const { return cOps ? cOps->enumerate   : nullptr; } \
+    JSNewEnumerateOp   getNewEnumerate()const { return cOps ? cOps->newEnumerate: nullptr; } \
     JSResolveOp        getResolve()     const { return cOps ? cOps->resolve     : nullptr; } \
     JSMayResolveOp     getMayResolve()  const { return cOps ? cOps->mayResolve  : nullptr; } \
     JSNative           getCall()        const { return cOps ? cOps->call        : nullptr; } \
     JSHasInstanceOp    getHasInstance() const { return cOps ? cOps->hasInstance : nullptr; } \
     JSNative           getConstruct()   const { return cOps ? cOps->construct   : nullptr; } \
     \
     bool hasFinalize() const { return cOps && cOps->finalize; } \
     bool hasTrace()    const { return cOps && cOps->trace;    } \
@@ -635,16 +636,17 @@ typedef void
 struct JS_STATIC_CLASS ClassOps
 {
     /* Function pointer members (may be null). */
     JSAddPropertyOp     addProperty;
     JSDeletePropertyOp  delProperty;
     JSGetterOp          getProperty;
     JSSetterOp          setProperty;
     JSEnumerateOp       enumerate;
+    JSNewEnumerateOp    newEnumerate;
     JSResolveOp         resolve;
     JSMayResolveOp      mayResolve;
     FinalizeOp          finalize;
     JSNative            call;
     JSHasInstanceOp     hasInstance;
     JSNative            construct;
     JSTraceOp           trace;
 };
@@ -733,17 +735,16 @@ struct JS_STATIC_CLASS ObjectOps
     HasPropertyOp    hasProperty;
     GetPropertyOp    getProperty;
     SetPropertyOp    setProperty;
     GetOwnPropertyOp getOwnPropertyDescriptor;
     DeletePropertyOp deleteProperty;
     WatchOp          watch;
     UnwatchOp        unwatch;
     GetElementsOp    getElements;
-    JSNewEnumerateOp enumerate;
     JSFunToStringOp  funToString;
 };
 
 #define JS_NULL_OBJECT_OPS nullptr
 
 } // namespace js
 
 // Classes, objects, and properties.
@@ -753,16 +754,17 @@ typedef void (*JSClassInternal)();
 struct JS_STATIC_CLASS JSClassOps
 {
     /* Function pointer members (may be null). */
     JSAddPropertyOp     addProperty;
     JSDeletePropertyOp  delProperty;
     JSGetterOp          getProperty;
     JSSetterOp          setProperty;
     JSEnumerateOp       enumerate;
+    JSNewEnumerateOp    newEnumerate;
     JSResolveOp         resolve;
     JSMayResolveOp      mayResolve;
     JSFinalizeOp        finalize;
     JSNative            call;
     JSHasInstanceOp     hasInstance;
     JSNative            construct;
     JSTraceOp           trace;
 };
@@ -968,30 +970,31 @@ struct JS_STATIC_CLASS Class
     SetPropertyOp    getOpsSetProperty()    const { return oOps ? oOps->setProperty    : nullptr; }
     GetOwnPropertyOp getOpsGetOwnPropertyDescriptor()
                                             const { return oOps ? oOps->getOwnPropertyDescriptor
                                                                                      : nullptr; }
     DeletePropertyOp getOpsDeleteProperty() const { return oOps ? oOps->deleteProperty : nullptr; }
     WatchOp          getOpsWatch()          const { return oOps ? oOps->watch          : nullptr; }
     UnwatchOp        getOpsUnwatch()        const { return oOps ? oOps->unwatch        : nullptr; }
     GetElementsOp    getOpsGetElements()    const { return oOps ? oOps->getElements    : nullptr; }
-    JSNewEnumerateOp getOpsEnumerate()      const { return oOps ? oOps->enumerate      : nullptr; }
     JSFunToStringOp  getOpsFunToString()    const { return oOps ? oOps->funToString    : nullptr; }
 };
 
 static_assert(offsetof(JSClassOps, addProperty) == offsetof(ClassOps, addProperty),
               "ClassOps and JSClassOps must be consistent");
 static_assert(offsetof(JSClassOps, delProperty) == offsetof(ClassOps, delProperty),
               "ClassOps and JSClassOps must be consistent");
 static_assert(offsetof(JSClassOps, getProperty) == offsetof(ClassOps, getProperty),
               "ClassOps and JSClassOps must be consistent");
 static_assert(offsetof(JSClassOps, setProperty) == offsetof(ClassOps, setProperty),
               "ClassOps and JSClassOps must be consistent");
 static_assert(offsetof(JSClassOps, enumerate) == offsetof(ClassOps, enumerate),
               "ClassOps and JSClassOps must be consistent");
+static_assert(offsetof(JSClassOps, newEnumerate) == offsetof(ClassOps, newEnumerate),
+              "ClassOps and JSClassOps must be consistent");
 static_assert(offsetof(JSClassOps, resolve) == offsetof(ClassOps, resolve),
               "ClassOps and JSClassOps must be consistent");
 static_assert(offsetof(JSClassOps, mayResolve) == offsetof(ClassOps, mayResolve),
               "ClassOps and JSClassOps must be consistent");
 static_assert(offsetof(JSClassOps, finalize) == offsetof(ClassOps, finalize),
               "ClassOps and JSClassOps must be consistent");
 static_assert(offsetof(JSClassOps, call) == offsetof(ClassOps, call),
               "ClassOps and JSClassOps must be consistent");
--- a/js/src/builtin/DataViewObject.cpp
+++ b/js/src/builtin/DataViewObject.cpp
@@ -929,16 +929,17 @@ DataViewObject::CreatePrototype(JSContex
 }
 
 static const ClassOps DataViewObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     ArrayBufferViewObject::trace
 };
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -1019,16 +1019,17 @@ Call(JSContext* cx, const ICUStringFunct
 /******************** Collator ********************/
 
 const ClassOps CollatorObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     CollatorObject::finalize
 };
 
 const Class CollatorObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(CollatorObject::SLOT_COUNT) |
@@ -1586,16 +1587,17 @@ js::intl_isUpperCaseFirst(JSContext* cx,
 /******************** NumberFormat ********************/
 
 const ClassOps NumberFormatObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     NumberFormatObject::finalize
 };
 
 const Class NumberFormatObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(NumberFormatObject::SLOT_COUNT) |
@@ -2479,16 +2481,17 @@ js::intl_FormatNumber(JSContext* cx, uns
 /******************** DateTimeFormat ********************/
 
 const ClassOps DateTimeFormatObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     DateTimeFormatObject::finalize
 };
 
 const Class DateTimeFormatObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(DateTimeFormatObject::SLOT_COUNT) |
@@ -3589,16 +3592,17 @@ js::intl_FormatDateTime(JSContext* cx, u
 /**************** PluralRules *****************/
 
 const ClassOps PluralRulesObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     PluralRulesObject::finalize
 };
 
 const Class PluralRulesObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(PluralRulesObject::SLOT_COUNT) |
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -127,16 +127,17 @@ namespace {
 } /* anonymous namespace */
 
 static const ClassOps MapIteratorObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     MapIteratorObject::finalize
 };
 
 const Class MapIteratorObject::class_ = {
     "Map Iterator",
     JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount) |
@@ -285,16 +286,17 @@ CreateMapPrototype(JSContext* cx, JSProt
 }
 
 const ClassOps MapObject::classOps_ = {
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
     nullptr, // setProperty
     nullptr, // enumerate
+    nullptr, // newEnumerate
     nullptr, // resolve
     nullptr, // mayResolve
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
     trace
 };
@@ -865,16 +867,17 @@ MapObject::clear(JSContext* cx, HandleOb
 /*** SetIterator *********************************************************************************/
 
 static const ClassOps SetIteratorObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     SetIteratorObject::finalize
 };
 
 const Class SetIteratorObject::class_ = {
     "Set Iterator",
     JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount) |
@@ -1010,16 +1013,17 @@ CreateSetPrototype(JSContext* cx, JSProt
 }
 
 const ClassOps SetObject::classOps_ = {
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
     nullptr, // setProperty
     nullptr, // enumerate
+    nullptr, // newEnumerate
     nullptr, // resolve
     nullptr, // mayResolve
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
     trace
 };
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -541,16 +541,17 @@ void FunctionDeclaration::trace(JSTracer
 
 /* static */ const ClassOps
 ModuleObject::classOps_ = {
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate   */
+    nullptr,        /* newEnumerate */
     nullptr,        /* resolve     */
     nullptr,        /* mayResolve  */
     ModuleObject::finalize,
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     ModuleObject::trace
 };
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -264,16 +264,17 @@ TypedObjectMemory(HandleValue v, const J
 }
 
 static const ClassOps SimdTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
     SimdTypeDescr::call
 };
 
 const Class SimdTypeDescr::class_ = {
     "SIMD",
@@ -528,16 +529,17 @@ SimdTypeDescr::call(JSContext* cx, unsig
 // SIMD class
 
 static const ClassOps SimdObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     SimdObject::resolve
 };
 
 const Class SimdObject::class_ = {
     "SIMD",
     JSCLASS_HAS_RESERVED_SLOTS(uint32_t(SimdType::Count)),
     &SimdObjectClassOps
 };
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1714,16 +1714,17 @@ finalize_counter_finalize(JSFreeOp* fop,
 }
 
 static const JSClassOps FinalizeCounterClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     finalize_counter_finalize
 };
 
 static const JSClass FinalizeCounterClass = {
     "FinalizeCounter",
     JSCLASS_IS_ANONYMOUS |
@@ -2442,16 +2443,17 @@ class CloneBufferObject : public NativeO
 };
 
 static const ClassOps CloneBufferObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     CloneBufferObject::Finalize
 };
 
 const Class CloneBufferObject::class_ = {
     "CloneBuffer",
     JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS) |
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -211,16 +211,17 @@ const Class js::TypedProto::class_ = {
  */
 
 static const ClassOps ScalarTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
     ScalarTypeDescr::call
 };
 
 const Class js::ScalarTypeDescr::class_ = {
     "Scalar",
@@ -318,16 +319,17 @@ ScalarTypeDescr::call(JSContext* cx, uns
  */
 
 static const ClassOps ReferenceTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
     ReferenceTypeDescr::call
 };
 
 const Class js::ReferenceTypeDescr::class_ = {
     "Reference",
@@ -506,16 +508,17 @@ CreatePrototypeObjectForComplexTypeInsta
 }
 
 static const ClassOps ArrayTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     TypedObject::construct
 };
 
@@ -743,16 +746,17 @@ js::IsTypedObjectArray(JSObject& obj)
  */
 
 static const ClassOps StructTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     TypedObject::construct
 };
 
@@ -2022,18 +2026,18 @@ TypedObject::obj_deleteProperty(JSContex
     RootedObject proto(cx, obj->staticPrototype());
     if (!proto)
         return result.succeed();
 
     return DeleteProperty(cx, proto, id, result);
 }
 
 bool
-TypedObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
-                           bool enumerableOnly)
+TypedObject::obj_newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
+                              bool enumerableOnly)
 {
     MOZ_ASSERT(obj->is<TypedObject>());
     Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
     Rooted<TypeDescr*> descr(cx, &typedObj->typeDescr());
 
     RootedId id(cx);
     switch (descr->kind()) {
       case type::Scalar:
@@ -2215,27 +2219,27 @@ const ObjectOps TypedObject::objectOps_ 
     TypedObject::obj_defineProperty,
     TypedObject::obj_hasProperty,
     TypedObject::obj_getProperty,
     TypedObject::obj_setProperty,
     TypedObject::obj_getOwnPropertyDescriptor,
     TypedObject::obj_deleteProperty,
     nullptr, nullptr, /* watch/unwatch */
     nullptr,   /* getElements */
-    TypedObject::obj_enumerate,
     nullptr, /* thisValue */
 };
 
 #define DEFINE_TYPEDOBJ_CLASS(Name, Trace, flag)         \
     static const ClassOps Name##ClassOps = {             \
         nullptr,        /* addProperty */                \
         nullptr,        /* delProperty */                \
         nullptr,        /* getProperty */                \
         nullptr,        /* setProperty */                \
         nullptr,        /* enumerate   */                \
+        TypedObject::obj_newEnumerate,                   \
         nullptr,        /* resolve     */                \
         nullptr,        /* mayResolve  */                \
         nullptr,        /* finalize    */                \
         nullptr,        /* call        */                \
         nullptr,        /* hasInstance */                \
         nullptr,        /* construct   */                \
         Trace,                                           \
     };                                                   \
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -531,24 +531,24 @@ class TypedObject : public ShapedObject
 
     static MOZ_MUST_USE bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj,
                                                           HandleId id,
                                                           MutableHandle<PropertyDescriptor> desc);
 
     static MOZ_MUST_USE bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                                 ObjectOpResult& result);
 
-    static MOZ_MUST_USE bool obj_enumerate(JSContext* cx, HandleObject obj,
-                                           AutoIdVector& properties, bool enumerableOnly);
-
 
     uint8_t* typedMem() const;
     uint8_t* typedMemBase() const;
 
   public:
+    static MOZ_MUST_USE bool obj_newEnumerate(JSContext* cx, HandleObject obj,
+                                              AutoIdVector& properties, bool enumerableOnly);
+
     TypedProto& typedProto() const {
         // Typed objects' prototypes can't be modified.
         return staticPrototype()->as<TypedProto>();
     }
 
     TypeDescr& typeDescr() const {
         return group()->typeDescr();
     }
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -311,16 +311,17 @@ WeakMap_construct(JSContext* cx, unsigne
 }
 
 static const ClassOps WeakMapObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     WeakMap_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     WeakMap_trace
 };
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -547,60 +547,61 @@ static const JSClass sCABIClass = {
 };
 
 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
 // This exists to give said prototypes a class of "CType", and to provide
 // reserved slots for stashing various other prototype objects.
 static const JSClassOps sCTypeProtoClassOps = {
   nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, nullptr,
-  ConstructAbstract, nullptr, ConstructAbstract
+  nullptr, ConstructAbstract, nullptr, ConstructAbstract
 };
 static const JSClass sCTypeProtoClass = {
   "CType",
   JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
   &sCTypeProtoClassOps
 };
 
 // Class representing ctypes.CData.prototype and the 'prototype' properties
 // of CTypes. This exists to give said prototypes a class of "CData".
 static const JSClass sCDataProtoClass = {
   "CData",
   0
 };
 
 static const JSClassOps sCTypeClassOps = {
   nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, CType::Finalize,
-  CType::ConstructData, CType::HasInstance, CType::ConstructData,
+  nullptr, nullptr, nullptr, nullptr,
+  CType::Finalize, CType::ConstructData, CType::HasInstance, CType::ConstructData,
   CType::Trace
 };
 static const JSClass sCTypeClass = {
   "CType",
   JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS) |
   JSCLASS_FOREGROUND_FINALIZE,
   &sCTypeClassOps
 };
 
 static const JSClassOps sCDataClassOps = {
   nullptr, nullptr, ArrayType::Getter, ArrayType::Setter,
-  nullptr, nullptr, nullptr, CData::Finalize,
-  FunctionType::Call, nullptr, FunctionType::Call
+  nullptr, nullptr, nullptr, nullptr,
+  CData::Finalize, FunctionType::Call, nullptr, FunctionType::Call
 };
 static const JSClass sCDataClass = {
   "CData",
   JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS) |
   JSCLASS_FOREGROUND_FINALIZE,
   &sCDataClassOps
 };
 
 static const JSClassOps sCClosureClassOps = {
   nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, CClosure::Finalize,
-  nullptr, nullptr, nullptr, CClosure::Trace
+  nullptr, nullptr, nullptr, nullptr,
+  CClosure::Finalize, nullptr, nullptr, nullptr,
+  CClosure::Trace
 };
 static const JSClass sCClosureClass = {
   "CClosure",
   JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS) |
   JSCLASS_FOREGROUND_FINALIZE,
   &sCClosureClassOps
 };
 
@@ -615,17 +616,18 @@ static const JSClass sCDataFinalizerProt
 /*
  * Class representing instances of CDataFinalizer.
  *
  * Instances of CDataFinalizer have both private data (with type
  * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
  */
 static const JSClassOps sCDataFinalizerClassOps = {
   nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, CDataFinalizer::Finalize
+  nullptr, nullptr, nullptr, nullptr,
+  CDataFinalizer::Finalize
 };
 static const JSClass sCDataFinalizerClass = {
   "CDataFinalizer",
   JSCLASS_HAS_PRIVATE |
   JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS) |
   JSCLASS_FOREGROUND_FINALIZE,
   &sCDataFinalizerClassOps
 };
@@ -806,17 +808,18 @@ static const JSClass sInt64ProtoClass = 
 
 static const JSClass sUInt64ProtoClass = {
   "UInt64",
   0
 };
 
 static const JSClassOps sInt64ClassOps = {
   nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, Int64Base::Finalize
+  nullptr, nullptr, nullptr, nullptr,
+  Int64Base::Finalize
 };
 
 static const JSClass sInt64Class = {
   "Int64",
   JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS) |
   JSCLASS_FOREGROUND_FINALIZE,
   &sInt64ClassOps
 };
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -29,17 +29,18 @@ namespace Library
 /*******************************************************************************
 ** JSObject implementation
 *******************************************************************************/
 
 typedef Rooted<JSFlatString*>    RootedFlatString;
 
 static const JSClassOps sLibraryClassOps = {
   nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr, Library::Finalize
+  nullptr, nullptr, nullptr, nullptr,
+  Library::Finalize
 };
 
 static const JSClass sLibraryClass = {
   "Library",
   JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS) |
   JSCLASS_FOREGROUND_FINALIZE,
   &sLibraryClassOps
 };
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -11,17 +11,17 @@
 #include "jsfriendapi.h"
 #include "js/Initialization.h"
 
 using namespace JS;
 
 static const JSClassOps global_classOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 /* The class of the global object. */
 static const JSClass global_class = {
     "global", JSCLASS_GLOBAL_FLAGS,
     &global_classOps
 };
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -15,16 +15,17 @@ static const JSClassOps global_classOps 
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static const JSClass global_class = {
     "global",
     JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS,
     &global_classOps
 };
--- a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
+++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
@@ -18,17 +18,17 @@ GlobalResolve(JSContext* cx, JS::HandleO
 {
     return JS_ResolveStandardClass(cx, obj, id, resolvedp);
 }
 
 BEGIN_TEST(testRedefineGlobalEval)
 {
     static const JSClassOps clsOps = {
         nullptr, nullptr, nullptr, nullptr,
-        GlobalEnumerate, GlobalResolve, nullptr, nullptr,
+        GlobalEnumerate, nullptr, GlobalResolve, nullptr, nullptr,
         nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
     static const JSClass cls = {
         "global", JSCLASS_GLOBAL_FLAGS,
         &clsOps
     };
--- a/js/src/jsapi-tests/testLookup.cpp
+++ b/js/src/jsapi-tests/testLookup.cpp
@@ -69,17 +69,17 @@ document_resolve(JSContext* cx, JS::Hand
         }
     }
 
     *resolvedp = false;
     return true;
 }
 
 static const JSClassOps document_classOps = {
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, document_resolve, nullptr
 };
 
 static const JSClass document_class = {
     "document", 0,
     &document_classOps
 };
 
--- a/js/src/jsapi-tests/testNewObject.cpp
+++ b/js/src/jsapi-tests/testNewObject.cpp
@@ -96,17 +96,17 @@ BEGIN_TEST(testNewObject_1)
     CHECK_EQUAL(len, N);
     CHECK(JS_GetElement(cx, obj, N - 1, &v));
     CHECK(v.isInt32(N - 1));
 
     // With JSClass.construct.
     static const JSClassOps clsOps = {
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, constructHook
+        nullptr, nullptr, nullptr, constructHook
     };
     static const JSClass cls = {
         "testNewObject_1",
         0,
         &clsOps
     };
     JS::RootedObject ctor(cx, JS_NewObject(cx, &cls));
     CHECK(ctor);
--- a/js/src/jsapi-tests/testPersistentRooted.cpp
+++ b/js/src/jsapi-tests/testPersistentRooted.cpp
@@ -23,16 +23,17 @@ int BarkWhenTracedClass::traceCount;
 static const JSClassOps BarkWhenTracedClassClassOps = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     BarkWhenTracedClass::finalize,
     nullptr,
     nullptr,
     nullptr,
     BarkWhenTracedClass::trace
 };
 
 const JSClass BarkWhenTracedClass::class_ = {
--- a/js/src/jsapi-tests/testResolveRecursion.cpp
+++ b/js/src/jsapi-tests/testResolveRecursion.cpp
@@ -14,16 +14,17 @@
 BEGIN_TEST(testResolveRecursion)
 {
     static const JSClassOps my_resolve_classOps = {
         nullptr, // add
         nullptr, // delete
         nullptr, // get
         nullptr, // set
         nullptr, // enumerate
+        nullptr, // newEnumerate
         my_resolve
     };
 
     static const JSClass my_resolve_class = {
         "MyResolve",
         JSCLASS_HAS_PRIVATE,
         &my_resolve_classOps
     };
@@ -155,16 +156,17 @@ BEGIN_TEST(testResolveRecursion_InitStan
 
 const JSClass* getGlobalClass() override {
     static const JSClassOps myGlobalClassOps = {
         nullptr, // add
         nullptr, // delete
         nullptr, // get
         nullptr, // set
         nullptr, // enumerate
+        nullptr, // newEnumerate
         my_resolve,
         nullptr, // mayResolve
         nullptr, // finalize
         nullptr, // call
         nullptr, // hasInstance
         nullptr, // construct
         JS_GlobalObjectTraceHook
     };
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -202,16 +202,17 @@ JSObject* newCCW(JS::HandleObject source
 JSObject* newDelegate()
 {
     static const js::ClassOps delegateClassOps = {
         nullptr, /* addProperty */
         nullptr, /* delProperty */
         nullptr, /* getProperty */
         nullptr, /* setProperty */
         nullptr, /* enumerate */
+        nullptr, /* newEnumerate */
         nullptr, /* resolve */
         nullptr, /* mayResolve */
         nullptr, /* finalize */
         nullptr, /* call */
         nullptr, /* hasInstance */
         nullptr, /* construct */
         JS_GlobalObjectTraceHook,
     };
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -252,17 +252,17 @@ class JSAPITest
     }
 
     JSAPITestString messages() const { return msgs; }
 
     static const JSClass * basicGlobalClass() {
         static const JSClassOps cOps = {
             nullptr, nullptr, nullptr, nullptr,
             nullptr, nullptr, nullptr, nullptr,
-            nullptr, nullptr, nullptr,
+            nullptr, nullptr, nullptr, nullptr,
             JS_GlobalObjectTraceHook
         };
         static const JSClass c = {
             "global", JSCLASS_GLOBAL_FLAGS,
             &cOps
         };
         return &c;
     }
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -175,16 +175,17 @@ ErrorObject::classSpecs[JSEXN_ERROR_LIMI
     }
 
 static const ClassOps ErrorObjectClassOps = {
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     nullptr,                 /* enumerate */
+    nullptr,                 /* newEnumerate */
     nullptr,                 /* resolve */
     nullptr,                 /* mayResolve */
     exn_finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     nullptr,                 /* trace       */
 };
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -957,16 +957,17 @@ CreateFunctionPrototype(JSContext* cx, J
 }
 
 static const ClassOps JSFunctionClassOps = {
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     fun_enumerate,
+    nullptr,                 /* newEnumerate */
     fun_resolve,
     fun_mayResolve,
     nullptr,                 /* finalize    */
     nullptr,                 /* call        */
     nullptr,
     nullptr,                 /* construct   */
     fun_trace,
 };
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -80,34 +80,37 @@ NewKeyValuePair(JSContext* cx, jsid id, 
 static inline bool
 Enumerate(JSContext* cx, HandleObject pobj, jsid id,
           bool enumerable, unsigned flags, Maybe<IdSet>& ht, AutoIdVector* props)
 {
     // Allow duplicate properties from Proxy's [[OwnPropertyKeys]].
     bool proxyOwnProperty = pobj->is<ProxyObject>() && (flags & JSITER_OWNONLY);
 
     if (!proxyOwnProperty && (!(flags & JSITER_OWNONLY) || pobj->is<ProxyObject>() ||
-        pobj->getOpsEnumerate()))
+                              pobj->getClass()->getNewEnumerate()))
     {
         if (!ht) {
             ht.emplace(cx);
             // Most of the time there are only a handful of entries.
             if (!ht->init(5))
                 return false;
         }
 
         // If we've already seen this, we definitely won't add it.
         IdSet::AddPtr p = ht->lookupForAdd(id);
         if (MOZ_UNLIKELY(!!p))
             return true;
 
         // It's not necessary to add properties to the hash table at the end of
         // the prototype chain, but custom enumeration behaviors might return
         // duplicated properties, so always add in such cases.
-        if (pobj->is<ProxyObject>() || pobj->staticPrototype() || pobj->getOpsEnumerate()) {
+        if (pobj->is<ProxyObject>() ||
+            pobj->staticPrototype() ||
+            pobj->getClass()->getNewEnumerate())
+        {
             if (!ht->add(p, id))
                 return false;
         }
     }
 
     // Symbol-keyed properties and nonenumerable properties are skipped unless
     // the caller specifically asks for them. A caller can also filter out
     // non-symbols by asking for JSITER_SYMBOLSONLY.
@@ -118,21 +121,21 @@ Enumerate(JSContext* cx, HandleObject po
 
     return props->append(id);
 }
 
 static bool
 EnumerateExtraProperties(JSContext* cx, HandleObject obj, unsigned flags, Maybe<IdSet>& ht,
                          AutoIdVector* props)
 {
-    MOZ_ASSERT(obj->getOpsEnumerate());
+    MOZ_ASSERT(obj->getClass()->getNewEnumerate());
 
     AutoIdVector properties(cx);
     bool enumerableOnly = !(flags & JSITER_HIDDEN);
-    if (!obj->getOpsEnumerate()(cx, obj, properties, enumerableOnly))
+    if (!obj->getClass()->getNewEnumerate()(cx, obj, properties, enumerableOnly))
         return false;
 
     RootedId id(cx);
     for (size_t n = 0; n < properties.length(); n++) {
         id = properties[n];
 
         // The enumerate hook does not indicate whether the properties
         // it returns are enumerable or not. Since we already passed
@@ -349,17 +352,17 @@ static bool
 Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props)
 {
     // We initialize |ht| lazily (in Enumerate()) because it ends up unused
     // anywhere from 67--99.9% of the time.
     Maybe<IdSet> ht;
     RootedObject pobj(cx, pobj_);
 
     do {
-        if (pobj->getOpsEnumerate()) {
+        if (pobj->getClass()->getNewEnumerate()) {
             if (pobj->is<UnboxedPlainObject>() && pobj->as<UnboxedPlainObject>().maybeExpando()) {
                 // Special case unboxed objects with an expando object.
                 RootedNativeObject expando(cx, pobj->as<UnboxedPlainObject>().maybeExpando());
                 if (!EnumerateNativeProperties(cx, expando, flags, ht, props,
                                                pobj.as<UnboxedPlainObject>()))
                 {
                     return false;
                 }
@@ -801,17 +804,17 @@ CanCompareIterableObjectToCache(JSObject
 static inline bool
 CanCacheIterableObject(JSContext* cx, JSObject* obj)
 {
     if (!CanCompareIterableObjectToCache(obj))
         return false;
     if (obj->isNative()) {
         if (obj->is<TypedArrayObject>() ||
             obj->hasUncacheableProto() ||
-            obj->getOpsEnumerate() ||
+            obj->getClass()->getNewEnumerate() ||
             obj->getClass()->getEnumerate() ||
             obj->as<NativeObject>().containsPure(cx->names().iteratorIntrinsic))
         {
             return false;
         }
     }
     return true;
 }
@@ -1099,16 +1102,17 @@ PropertyIteratorObject::finalize(FreeOp*
 }
 
 const ClassOps PropertyIteratorObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     finalize,
     nullptr, /* call        */
     nullptr, /* hasInstance */
     nullptr, /* construct   */
     trace
 };
@@ -1517,16 +1521,17 @@ stopiter_hasInstance(JSContext* cx, Hand
 }
 
 static const ClassOps StopIterationObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     stopiter_hasInstance
 };
 
 const Class StopIterationObject::class_ = {
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -139,17 +139,16 @@ class JSObject : public js::gc::Cell
     js::GetPropertyOp    getOpsGetProperty()    const { return getClass()->getOpsGetProperty(); }
     js::SetPropertyOp    getOpsSetProperty()    const { return getClass()->getOpsSetProperty(); }
     js::GetOwnPropertyOp getOpsGetOwnPropertyDescriptor()
                                                 const { return getClass()->getOpsGetOwnPropertyDescriptor(); }
     js::DeletePropertyOp getOpsDeleteProperty() const { return getClass()->getOpsDeleteProperty(); }
     js::WatchOp          getOpsWatch()          const { return getClass()->getOpsWatch(); }
     js::UnwatchOp        getOpsUnwatch()        const { return getClass()->getOpsUnwatch(); }
     js::GetElementsOp    getOpsGetElements()    const { return getClass()->getOpsGetElements(); }
-    JSNewEnumerateOp     getOpsEnumerate()      const { return getClass()->getOpsEnumerate(); }
     JSFunToStringOp      getOpsFunToString()    const { return getClass()->getOpsFunToString(); }
 
     js::ObjectGroup* group() const {
         MOZ_ASSERT(!hasLazyGroup());
         return groupRaw();
     }
 
     js::ObjectGroup* groupRaw() const {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1376,16 +1376,17 @@ ScriptSourceObject::finalize(FreeOp* fop
 }
 
 static const ClassOps ScriptSourceObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     ScriptSourceObject::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     ScriptSourceObject::trace
 };
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -432,16 +432,17 @@ str_resolve(JSContext* cx, HandleObject 
 }
 
 static const ClassOps StringObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     str_enumerate,
+    nullptr, /* newEnumerate */
     str_resolve,
     str_mayResolve
 };
 
 const Class StringObject::class_ = {
     js_String_str,
     JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_String),
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -162,16 +162,17 @@ static void pm_finalize(JSFreeOp* fop, J
 static const JSClassOps pm_classOps = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     pm_finalize
 };
 
 static const JSClass pm_class = {
     "PerfMeasurement",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_FOREGROUND_FINALIZE,
     &pm_classOps
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -749,16 +749,17 @@ js::proxy_Construct(JSContext* cx, unsig
 }
 
 const ClassOps js::ProxyClassOps = {
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     nullptr,                 /* enumerate   */
+    nullptr,                 /* newEnumerate */
     nullptr,                 /* resolve     */
     nullptr,                 /* mayResolve  */
     proxy_Finalize,          /* finalize    */
     nullptr,                 /* call        */
     Proxy::hasInstance,      /* hasInstance */
     nullptr,                 /* construct   */
     ProxyObject::trace,      /* trace       */
 };
@@ -772,17 +773,16 @@ const ObjectOps js::ProxyObjectOps = {
     Proxy::defineProperty,
     Proxy::has,
     Proxy::get,
     Proxy::set,
     Proxy::getOwnPropertyDescriptor,
     proxy_DeleteProperty,
     Proxy::watch, Proxy::unwatch,
     Proxy::getElements,
-    nullptr,  /* enumerate */
     Proxy::fun_toString
 };
 
 const Class js::ProxyObject::proxyClass =
     PROXY_CLASS_DEF("Proxy",
                     JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy) |
                     JSCLASS_HAS_RESERVED_SLOTS(2));
 
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -443,16 +443,17 @@ class FileObject : public JSObject {
 };
 
 static const js::ClassOps FileObjectClassOps = {
     nullptr,               /* addProperty */
     nullptr,               /* delProperty */
     nullptr,               /* getProperty */
     nullptr,               /* setProperty */
     nullptr,               /* enumerate */
+    nullptr,               /* newEnumerate */
     nullptr,               /* resolve */
     nullptr,               /* mayResolve */
     FileObject::finalize,  /* finalize */
     nullptr,               /* call */
     nullptr,               /* hasInstance */
     nullptr,               /* construct */
     nullptr                /* trace */
 };
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3090,17 +3090,17 @@ sandbox_resolve(JSContext* cx, HandleObj
 
     if (ToBoolean(v))
         return JS_ResolveStandardClass(cx, obj, id, resolvedp);
     return true;
 }
 
 static const JSClassOps sandbox_classOps = {
     nullptr, nullptr, nullptr, nullptr,
-    sandbox_enumerate, sandbox_resolve,
+    sandbox_enumerate, nullptr, sandbox_resolve,
     nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static const JSClass sandbox_class = {
     "sandbox",
     JSCLASS_GLOBAL_FLAGS,
@@ -7038,17 +7038,17 @@ global_resolve(JSContext* cx, HandleObje
 static bool
 global_mayResolve(const JSAtomState& names, jsid id, JSObject* maybeObj)
 {
     return JS_MayResolveStandardClass(names, id, maybeObj);
 }
 
 static const JSClassOps global_classOps = {
     nullptr, nullptr, nullptr, nullptr,
-    global_enumerate, global_resolve, global_mayResolve,
+    global_enumerate, nullptr, global_resolve, global_mayResolve,
     nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static const JSClass global_class = {
     "global", JSCLASS_GLOBAL_FLAGS,
     &global_classOps
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -891,16 +891,17 @@ ArgumentsObject::objectMovedDuringMinorG
  * arguments object.
  */
 const ClassOps MappedArgumentsObject::classOps_ = {
     nullptr,                 /* addProperty */
     ArgumentsObject::obj_delProperty,
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     MappedArgumentsObject::obj_enumerate,
+    nullptr,                 /* newEnumerate */
     MappedArgumentsObject::obj_resolve,
     ArgumentsObject::obj_mayResolve,
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
@@ -928,16 +929,17 @@ const Class MappedArgumentsObject::class
  * it is represented by a different class while sharing some functionality.
  */
 const ClassOps UnmappedArgumentsObject::classOps_ = {
     nullptr,                 /* addProperty */
     ArgumentsObject::obj_delProperty,
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     UnmappedArgumentsObject::obj_enumerate,
+    nullptr,                 /* newEnumerate */
     UnmappedArgumentsObject::obj_resolve,
     ArgumentsObject::obj_mayResolve,
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -107,16 +107,17 @@ CreateArrayBufferPrototype(JSContext* cx
 }
 
 static const ClassOps ArrayBufferObjectClassOps = {
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate */
+    nullptr,        /* newEnumerate */
     nullptr,        /* resolve */
     nullptr,        /* mayResolve */
     ArrayBufferObject::finalize,
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     ArrayBufferObject::trace,
 };
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -83,16 +83,17 @@ enum {
 };
 
 const ClassOps DebuggerFrame::classOps_ = {
     nullptr,    /* addProperty */
     nullptr,    /* delProperty */
     nullptr,    /* getProperty */
     nullptr,    /* setProperty */
     nullptr,    /* enumerate   */
+    nullptr,    /* newEnumerate */
     nullptr,    /* resolve     */
     nullptr,    /* mayResolve  */
     DebuggerFrame_finalize,
     nullptr,    /* call        */
     nullptr,    /* hasInstance */
     nullptr,    /* construct   */
     DebuggerFrame_trace
 };
@@ -116,16 +117,17 @@ const Class DebuggerArguments::class_ = 
 };
 
 const ClassOps DebuggerEnvironment::classOps_ = {
     nullptr,    /* addProperty */
     nullptr,    /* delProperty */
     nullptr,    /* getProperty */
     nullptr,    /* setProperty */
     nullptr,    /* enumerate   */
+    nullptr,    /* newEnumerate */
     nullptr,    /* resolve     */
     nullptr,    /* mayResolve  */
     nullptr,    /* finalize    */
     nullptr,    /* call        */
     nullptr,    /* hasInstance */
     nullptr,    /* construct   */
     DebuggerEnv_trace
 };
@@ -143,16 +145,17 @@ enum {
 };
 
 const ClassOps DebuggerObject::classOps_ = {
     nullptr,    /* addProperty */
     nullptr,    /* delProperty */
     nullptr,    /* getProperty */
     nullptr,    /* setProperty */
     nullptr,    /* enumerate   */
+    nullptr,    /* newEnumerate */
     nullptr,    /* resolve     */
     nullptr,    /* mayResolve  */
     nullptr,    /* finalize    */
     nullptr,    /* call        */
     nullptr,    /* hasInstance */
     nullptr,    /* construct   */
     DebuggerObject_trace
 };
@@ -170,16 +173,17 @@ enum {
 };
 
 static const ClassOps DebuggerScript_classOps = {
     nullptr,    /* addProperty */
     nullptr,    /* delProperty */
     nullptr,    /* getProperty */
     nullptr,    /* setProperty */
     nullptr,    /* enumerate   */
+    nullptr,    /* newEnumerate */
     nullptr,    /* resolve     */
     nullptr,    /* mayResolve  */
     nullptr,    /* finalize    */
     nullptr,    /* call        */
     nullptr,    /* hasInstance */
     nullptr,    /* construct   */
     DebuggerScript_trace
 };
@@ -198,16 +202,17 @@ enum {
 };
 
 static const ClassOps DebuggerSource_classOps = {
     nullptr,    /* addProperty */
     nullptr,    /* delProperty */
     nullptr,    /* getProperty */
     nullptr,    /* setProperty */
     nullptr,    /* enumerate   */
+    nullptr,    /* newEnumerate */
     nullptr,    /* resolve     */
     nullptr,    /* mayResolve  */
     nullptr,    /* finalize    */
     nullptr,    /* call        */
     nullptr,    /* hasInstance */
     nullptr,    /* construct   */
     DebuggerSource_trace
 };
@@ -3282,16 +3287,17 @@ Debugger::findZoneEdges(Zone* zone, js::
 }
 
 const ClassOps Debugger::classOps_ = {
     nullptr,    /* addProperty */
     nullptr,    /* delProperty */
     nullptr,    /* getProperty */
     nullptr,    /* setProperty */
     nullptr,    /* enumerate   */
+    nullptr,    /* newEnumerate */
     nullptr,    /* resolve     */
     nullptr,    /* mayResolve  */
     nullptr,    /* finalize    */
     nullptr,    /* call        */
     nullptr,    /* hasInstance */
     nullptr,    /* construct   */
     Debugger::traceObject
 };
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -403,25 +403,33 @@ const ObjectOps ModuleEnvironmentObject:
     nullptr,                                             /* defineProperty */
     ModuleEnvironmentObject::hasProperty,
     ModuleEnvironmentObject::getProperty,
     ModuleEnvironmentObject::setProperty,
     ModuleEnvironmentObject::getOwnPropertyDescriptor,
     ModuleEnvironmentObject::deleteProperty,
     nullptr, nullptr,                                    /* watch/unwatch */
     nullptr,                                             /* getElements */
-    ModuleEnvironmentObject::enumerate,
     nullptr
 };
 
+const ClassOps ModuleEnvironmentObject::classOps_ = {
+    nullptr,    /* addProperty */
+    nullptr,    /* delProperty */
+    nullptr,    /* getProperty */
+    nullptr,    /* setProperty */
+    nullptr,    /* enumerate */
+    ModuleEnvironmentObject::newEnumerate
+};
+
 const Class ModuleEnvironmentObject::class_ = {
     "ModuleEnvironmentObject",
     JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
-    JS_NULL_CLASS_OPS,
+    &ModuleEnvironmentObject::classOps_,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
     &ModuleEnvironmentObject::objectOps_
 };
 
 /* static */ ModuleEnvironmentObject*
 ModuleEnvironmentObject::create(JSContext* cx, HandleModuleObject module)
 {
@@ -598,18 +606,18 @@ ModuleEnvironmentObject::getOwnPropertyD
 /* static */ bool
 ModuleEnvironmentObject::deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                         ObjectOpResult& result)
 {
     return result.failCantDelete();
 }
 
 /* static */ bool
-ModuleEnvironmentObject::enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
-                                   bool enumerableOnly)
+ModuleEnvironmentObject::newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
+                                      bool enumerableOnly)
 {
     RootedModuleEnvironmentObject self(cx, &obj->as<ModuleEnvironmentObject>());
     const IndirectBindingMap& bs(self->importBindings());
 
     MOZ_ASSERT(properties.length() == 0);
     size_t count = bs.count() + self->slotSpan() - RESERVED_SLOTS;
     if (!properties.reserve(count)) {
         ReportOutOfMemory(cx);
@@ -816,17 +824,16 @@ static const ObjectOps WithEnvironmentOb
     with_DefineProperty,
     with_HasProperty,
     with_GetProperty,
     with_SetProperty,
     with_GetOwnPropertyDescriptor,
     with_DeleteProperty,
     nullptr, nullptr,    /* watch/unwatch */
     nullptr,             /* getElements */
-    nullptr,             /* enumerate (native enumeration of target doesn't work) */
     nullptr,
 };
 
 const Class WithEnvironmentObject::class_ = {
     "With",
     JSCLASS_HAS_RESERVED_SLOTS(WithEnvironmentObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     JS_NULL_CLASS_OPS,
@@ -1182,17 +1189,16 @@ static const ObjectOps RuntimeLexicalErr
     nullptr,             /* defineProperty */
     lexicalError_HasProperty,
     lexicalError_GetProperty,
     lexicalError_SetProperty,
     lexicalError_GetOwnPropertyDescriptor,
     lexicalError_DeleteProperty,
     nullptr, nullptr,    /* watch/unwatch */
     nullptr,             /* getElements */
-    nullptr,             /* enumerate (native enumeration of target doesn't work) */
     nullptr,             /* this */
 };
 
 const Class RuntimeLexicalErrorObject::class_ = {
     "RuntimeLexicalError",
     JSCLASS_HAS_RESERVED_SLOTS(RuntimeLexicalErrorObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     JS_NULL_CLASS_OPS,
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -382,16 +382,17 @@ class VarEnvironmentObject : public Envi
     }
 };
 
 class ModuleEnvironmentObject : public EnvironmentObject
 {
     static const uint32_t MODULE_SLOT = 1;
 
     static const ObjectOps objectOps_;
+    static const ClassOps classOps_;
 
   public:
     static const Class class_;
 
     static const uint32_t RESERVED_SLOTS = 2;
 
     static ModuleEnvironmentObject* create(JSContext* cx, HandleModuleObject module);
     ModuleObject& module();
@@ -413,18 +414,18 @@ class ModuleEnvironmentObject : public E
     static bool getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
                             MutableHandleValue vp);
     static bool setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                             HandleValue receiver, JS::ObjectOpResult& result);
     static bool getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                                          MutableHandle<PropertyDescriptor> desc);
     static bool deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                ObjectOpResult& result);
-    static bool enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
-                          bool enumerableOnly);
+    static bool newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
+                             bool enumerableOnly);
 };
 
 typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
 typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject;
 typedef MutableHandle<ModuleEnvironmentObject*> MutableHandleModuleEnvironmentObject;
 
 class WasmFunctionCallObject : public EnvironmentObject
 {
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -648,16 +648,17 @@ static const ClassOps
 GlobalDebuggees_classOps = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     GlobalDebuggees_finalize
 };
 
 static const Class
 GlobalDebuggees_class = {
     "GlobalDebuggee",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_BACKGROUND_FINALIZE,
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -283,17 +283,17 @@ js::HasOffThreadIonCompile(JSCompartment
 
     return false;
 }
 #endif
 
 static const JSClassOps parseTaskGlobalClassOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static const JSClass parseTaskGlobalClass = {
     "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
     &parseTaskGlobalClassOps
 };
 
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -284,17 +284,17 @@ ForOfPIC_finalize(FreeOp* fop, JSObject*
 static void
 ForOfPIC_traceObject(JSTracer* trc, JSObject* obj)
 {
     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
         chain->trace(trc);
 }
 
 static const ClassOps ForOfPICClassOps = {
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, ForOfPIC_finalize,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     ForOfPIC_traceObject
 };
 
 const Class ForOfPIC::class_ = {
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -193,16 +193,17 @@ CreateRegExpPrototype(JSContext* cx, JSP
 }
 
 static const ClassOps RegExpObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     RegExpObject::trace,
 };
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -36,16 +36,17 @@ resc_trace(JSTracer* trc, JSObject* obj)
 }
 
 static const ClassOps RegExpStaticsObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     resc_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     resc_trace
 };
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -305,16 +305,17 @@ SavedFrame::finishSavedFrameInit(JSConte
 }
 
 static const ClassOps SavedFrameClassOps = {
     nullptr,                    // addProperty
     nullptr,                    // delProperty
     nullptr,                    // getProperty
     nullptr,                    // setProperty
     nullptr,                    // enumerate
+    nullptr,                    // newEnumerate
     nullptr,                    // resolve
     nullptr,                    // mayResolve
     SavedFrame::finalize,       // finalize
     nullptr,                    // call
     nullptr,                    // hasInstance
     nullptr,                    // construct
     nullptr,                    // trace
 };
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2691,17 +2691,17 @@ JSRuntime::createSelfHostingGlobal(JSCon
 
     JSCompartment* compartment = NewCompartment(cx, nullptr, options);
     if (!compartment)
         return nullptr;
 
     static const ClassOps shgClassOps = {
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, nullptr,
+        nullptr, nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
     static const Class shgClass = {
         "self-hosting-global", JSCLASS_GLOBAL_FLAGS,
         &shgClassOps
     };
 
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -394,16 +394,17 @@ CreateSharedArrayBufferPrototype(JSConte
 }
 
 static const ClassOps SharedArrayBufferObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     SharedArrayBufferObject::Finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
 };
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -2039,16 +2039,17 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Flo
 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double)
 
 static const ClassOps TypedArrayClassOps = {
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     nullptr,                 /* enumerate   */
+    nullptr,                 /* newEnumerate */
     nullptr,                 /* resolve     */
     nullptr,                 /* mayResolve  */
     TypedArrayObject::finalize, /* finalize    */
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     TypedArrayObject::trace, /* trace  */
 };
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -920,18 +920,18 @@ UnboxedPlainObject::obj_deleteProperty(J
 UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
 {
     if (!convertToNative(cx, obj))
         return false;
     return WatchProperty(cx, obj, id, callable);
 }
 
 /* static */ bool
-UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
-                                  bool enumerableOnly)
+UnboxedPlainObject::newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
+                                 bool enumerableOnly)
 {
     // Ignore expando properties here, they are special-cased by the property
     // enumeration code.
 
     const UnboxedLayout::PropertyVector& unboxed = obj->as<UnboxedPlainObject>().layout().properties();
     for (size_t i = 0; i < unboxed.length(); i++) {
         if (!properties.append(NameToId(unboxed[i].name)))
             return false;
@@ -946,16 +946,17 @@ const Class UnboxedExpandoObject::class_
 };
 
 static const ClassOps UnboxedPlainObjectClassOps = {
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate   */
+    UnboxedPlainObject::newEnumerate,
     nullptr,        /* resolve     */
     nullptr,        /* mayResolve  */
     nullptr,        /* finalize    */
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     UnboxedPlainObject::trace,
 };
@@ -966,17 +967,16 @@ static const ObjectOps UnboxedPlainObjec
     UnboxedPlainObject::obj_hasProperty,
     UnboxedPlainObject::obj_getProperty,
     UnboxedPlainObject::obj_setProperty,
     UnboxedPlainObject::obj_getOwnPropertyDescriptor,
     UnboxedPlainObject::obj_deleteProperty,
     UnboxedPlainObject::obj_watch,
     nullptr,   /* No unwatch needed, as watch() converts the object to native */
     nullptr,   /* getElements */
-    UnboxedPlainObject::obj_enumerate,
     nullptr    /* funToString */
 };
 
 const Class UnboxedPlainObject::class_ = {
     js_Object_str,
     Class::NON_NATIVE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
     JSCLASS_DELAY_METADATA_BUILDER,
@@ -1622,18 +1622,18 @@ UnboxedArrayObject::obj_deleteProperty(J
 UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
 {
     if (!convertToNative(cx, obj))
         return false;
     return WatchProperty(cx, obj, id, callable);
 }
 
 /* static */ bool
-UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
-                                  bool enumerableOnly)
+UnboxedArrayObject::newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
+                                 bool enumerableOnly)
 {
     for (size_t i = 0; i < obj->as<UnboxedArrayObject>().initializedLength(); i++) {
         if (!properties.append(INT_TO_JSID(i)))
             return false;
     }
 
     if (!enumerableOnly && !properties.append(NameToId(cx->names().length)))
         return false;
@@ -1642,16 +1642,17 @@ UnboxedArrayObject::obj_enumerate(JSCont
 }
 
 static const ClassOps UnboxedArrayObjectClassOps = {
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate   */
+    UnboxedArrayObject::newEnumerate,
     nullptr,        /* resolve     */
     nullptr,        /* mayResolve  */
     UnboxedArrayObject::finalize,
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     UnboxedArrayObject::trace,
 };
@@ -1667,17 +1668,16 @@ static const ObjectOps UnboxedArrayObjec
     UnboxedArrayObject::obj_hasProperty,
     UnboxedArrayObject::obj_getProperty,
     UnboxedArrayObject::obj_setProperty,
     UnboxedArrayObject::obj_getOwnPropertyDescriptor,
     UnboxedArrayObject::obj_deleteProperty,
     UnboxedArrayObject::obj_watch,
     nullptr,   /* No unwatch needed, as watch() converts the object to native */
     nullptr,   /* getElements */
-    UnboxedArrayObject::obj_enumerate,
     nullptr    /* funToString */
 };
 
 const Class UnboxedArrayObject::class_ = {
     "Array",
     Class::NON_NATIVE |
     JSCLASS_SKIP_NURSERY_FINALIZE |
     JSCLASS_BACKGROUND_FINALIZE,
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -271,18 +271,18 @@ class UnboxedPlainObject : public Unboxe
                                 HandleValue receiver, ObjectOpResult& result);
 
     static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                                              MutableHandle<PropertyDescriptor> desc);
 
     static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    ObjectOpResult& result);
 
-    static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
-                              bool enumerableOnly);
+    static bool newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
+                             bool enumerableOnly);
     static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
 
     inline const UnboxedLayout& layout() const;
 
     const UnboxedLayout& layoutDontCheckGeneration() const {
         return group()->unboxedLayoutDontCheckGeneration();
     }
 
@@ -407,18 +407,18 @@ class UnboxedArrayObject : public Unboxe
                                 HandleValue receiver, ObjectOpResult& result);
 
     static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                                              MutableHandle<PropertyDescriptor> desc);
 
     static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    ObjectOpResult& result);
 
-    static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
-                              bool enumerableOnly);
+    static bool newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
+                             bool enumerableOnly);
     static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
 
     inline const UnboxedLayout& layout() const;
 
     const UnboxedLayout& layoutDontCheckGeneration() const {
         return group()->unboxedLayoutDontCheckGeneration();
     }
 
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -478,16 +478,17 @@ GetLimits(JSContext* cx, HandleObject ob
 
 const ClassOps WasmModuleObject::classOps_ =
 {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     WasmModuleObject::finalize
 };
 
 const Class WasmModuleObject::class_ =
 {
     "WebAssembly.Module",
@@ -923,16 +924,17 @@ WasmModuleObject::module() const
 
 const ClassOps WasmInstanceObject::classOps_ =
 {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     WasmInstanceObject::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     WasmInstanceObject::trace
 };
@@ -1283,16 +1285,17 @@ wasm::ExportedFunctionToFuncIndex(JSFunc
 
 const ClassOps WasmMemoryObject::classOps_ =
 {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     WasmMemoryObject::finalize
 };
 
 const Class WasmMemoryObject::class_ =
 {
     "WebAssembly.Memory",
@@ -1540,16 +1543,17 @@ WasmMemoryObject::grow(HandleWasmMemoryO
 
 const ClassOps WasmTableObject::classOps_ =
 {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     WasmTableObject::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     WasmTableObject::trace
 };
--- a/js/xpconnect/public/xpc_make_class.h
+++ b/js/xpconnect/public/xpc_make_class.h
@@ -46,16 +46,20 @@ XPC_WN_CannotModifySetPropertyStub(JSCon
                                    JS::ObjectOpResult& result);
 
 bool
 XPC_WN_Helper_Enumerate(JSContext* cx, JS::HandleObject obj);
 bool
 XPC_WN_Shared_Enumerate(JSContext* cx, JS::HandleObject obj);
 
 bool
+XPC_WN_NewEnumerate(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties,
+                    bool enumerableOnly);
+
+bool
 XPC_WN_Helper_Resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                       bool* resolvedp);
 
 void
 XPC_WN_Helper_Finalize(js::FreeOp* fop, JSObject* obj);
 void
 XPC_WN_NoHelper_Finalize(js::FreeOp* fop, JSObject* obj);
 
@@ -69,18 +73,16 @@ XPC_WN_Helper_HasInstance(JSContext* cx,
 bool
 XPC_WN_Helper_Construct(JSContext* cx, unsigned argc, JS::Value* vp);
 
 void
 XPCWrappedNative_Trace(JSTracer* trc, JSObject* obj);
 
 extern const js::ClassExtension XPC_WN_JSClassExtension;
 
-extern const js::ObjectOps XPC_WN_ObjectOpsWithEnumerate;
-
 #define XPC_MAKE_CLASS_OPS(_flags) { \
     /* addProperty */ \
     ((_flags) & XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY) \
     ? nullptr \
     : ((_flags) & XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) \
       ? XPC_WN_MaybeResolvingPropertyStub \
       : XPC_WN_CannotModifyPropertyStub, \
     \
@@ -102,21 +104,26 @@ extern const js::ObjectOps XPC_WN_Object
     : ((_flags) & XPC_SCRIPTABLE_USE_JSSTUB_FOR_SETPROPERTY) \
       ? nullptr \
       : ((_flags) & XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) \
         ? XPC_WN_MaybeResolvingSetPropertyStub \
         : XPC_WN_CannotModifySetPropertyStub, \
     \
     /* enumerate */ \
     ((_flags) & XPC_SCRIPTABLE_WANT_NEWENUMERATE) \
-    ? nullptr /* We will use oOps->enumerate set below in this case */ \
+    ? nullptr /* We will use newEnumerate set below in this case */ \
     : ((_flags) & XPC_SCRIPTABLE_WANT_ENUMERATE) \
       ? XPC_WN_Helper_Enumerate \
       : XPC_WN_Shared_Enumerate, \
     \
+    /* newEnumerate */ \
+    ((_flags) & XPC_SCRIPTABLE_WANT_NEWENUMERATE) \
+    ? XPC_WN_NewEnumerate \
+    : nullptr, \
+    \
     /* resolve */ \
     /* We have to figure out resolve strategy at call time */ \
     XPC_WN_Helper_Resolve, \
     \
     /* mayResolve */ \
     nullptr, \
     \
     /* finalize */ \
@@ -162,14 +169,12 @@ extern const js::ObjectOps XPC_WN_Object
     \
     /* spec */ \
     nullptr, \
     \
     /* ext */ \
     &XPC_WN_JSClassExtension, \
     \
     /* oOps */ \
-    ((_flags) & XPC_SCRIPTABLE_WANT_NEWENUMERATE) \
-    ? &XPC_WN_ObjectOpsWithEnumerate \
-    : nullptr, \
+    nullptr, \
 }
 
 #endif
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -543,17 +543,17 @@ sandbox_addProperty(JSContext* cx, Handl
 
     return true;
 }
 
 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
 
 static const js::ClassOps SandboxClassOps = {
     nullptr, nullptr, nullptr, nullptr,
-    JS_EnumerateStandardClasses, JS_ResolveStandardClass,
+    JS_EnumerateStandardClasses, nullptr, JS_ResolveStandardClass,
     JS_MayResolveStandardClass,
     sandbox_finalize,
     nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
 };
 
 static const js::ClassExtension SandboxClassExtension = {
     nullptr,      /* weakmapKeyDelegateOp */
     sandbox_moved /* objectMovedOp */
@@ -568,17 +568,17 @@ static const js::Class SandboxClass = {
     &SandboxClassExtension,
     JS_NULL_OBJECT_OPS
 };
 
 // Note to whomever comes here to remove addProperty hooks: billm has promised
 // to do the work for this class.
 static const js::ClassOps SandboxWriteToProtoClassOps = {
     sandbox_addProperty, nullptr, nullptr, nullptr,
-    JS_EnumerateStandardClasses, JS_ResolveStandardClass,
+    JS_EnumerateStandardClasses, nullptr, JS_ResolveStandardClass,
     JS_MayResolveStandardClass,
     sandbox_finalize,
     nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
 };
 
 static const js::Class SandboxWriteToProtoClass = {
     "Sandbox",
     XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1) |
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -796,17 +796,17 @@ env_resolve(JSContext* cx, HandleObject 
         }
         *resolvedp = true;
     }
     return true;
 }
 
 static const JSClassOps env_classOps = {
     nullptr, nullptr, nullptr, env_setProperty,
-    env_enumerate, env_resolve
+    env_enumerate, nullptr, env_resolve
 };
 
 static const JSClass env_class = {
     "environment", JSCLASS_HAS_PRIVATE,
     &env_classOps
 };
 
 /***************************************************************************/
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -621,16 +621,17 @@ XPC_WN_NoHelper_Resolve(JSContext* cx, H
 }
 
 static const js::ClassOps XPC_WN_NoHelper_JSClassOps = {
     XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty
     XPC_WN_CannotDeletePropertyStub,   // delProperty
     nullptr,                           // getProperty
     nullptr,                           // setProperty
     XPC_WN_Shared_Enumerate,           // enumerate
+    nullptr,                           // newEnumerate
     XPC_WN_NoHelper_Resolve,           // resolve
     nullptr,                           // mayResolve
     XPC_WN_NoHelper_Finalize,          // finalize
     nullptr,                           // call
     nullptr,                           // construct
     nullptr,                           // hasInstance
     XPCWrappedNative::Trace,           // trace
 };
@@ -869,19 +870,19 @@ XPC_WN_Helper_Enumerate(JSContext* cx, H
     nsresult rv = scr->Enumerate(wrapper, cx, obj, &retval);
     if (NS_FAILED(rv))
         return Throw(rv, cx);
     return retval;
 }
 
 /***************************************************************************/
 
-static bool
-XPC_WN_JSOp_Enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
-                      bool enumerableOnly)
+bool
+XPC_WN_NewEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
+                    bool enumerableOnly)
 {
     XPCCallContext ccx(cx, obj);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     nsCOMPtr<nsIXPCScriptable> scr = wrapper->GetScriptable();
     if (!scr || !scr->WantNewEnumerate())
         return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
@@ -892,33 +893,16 @@ XPC_WN_JSOp_Enumerate(JSContext* cx, Han
     bool retval = true;
     nsresult rv = scr->NewEnumerate(wrapper, cx, obj, properties, &retval);
     if (NS_FAILED(rv))
         return Throw(rv, cx);
     return retval;
 }
 
 /***************************************************************************/
-
-const js::ObjectOps XPC_WN_ObjectOpsWithEnumerate = {
-    nullptr,  // lookupProperty
-    nullptr,  // defineProperty
-    nullptr,  // hasProperty
-    nullptr,  // getProperty
-    nullptr,  // setProperty
-    nullptr,  // getOwnPropertyDescriptor
-    nullptr,  // deleteProperty
-    nullptr,  // watch
-    nullptr,  // unwatch
-    nullptr,  // getElements
-    XPC_WN_JSOp_Enumerate,
-    nullptr,  // funToString
-};
-
-/***************************************************************************/
 /***************************************************************************/
 
 // Compatibility hack.
 //
 // XPConnect used to do all sorts of funny tricks to find the "correct"
 // |this| object for a given method (often to the detriment of proper
 // call/apply). When these tricks were removed, a fair amount of chrome
 // code broke, because it was relying on being able to grab methods off
@@ -1108,16 +1092,17 @@ XPC_WN_ModsAllowed_Proto_Resolve(JSConte
 }
 
 static const js::ClassOps XPC_WN_ModsAllowed_Proto_JSClassOps = {
     nullptr,                            // addProperty
     nullptr,                            // delProperty
     nullptr,                            // getProperty
     nullptr,                            // setProperty
     XPC_WN_Shared_Proto_Enumerate,      // enumerate
+    nullptr,                            // newEnumerate
     XPC_WN_ModsAllowed_Proto_Resolve,   // resolve
     nullptr,                            // mayResolve
     XPC_WN_Shared_Proto_Finalize,       // finalize
     nullptr,                            // call
     nullptr,                            // construct
     nullptr,                            // hasInstance
     XPC_WN_Shared_Proto_Trace,          // trace
 };
@@ -1188,16 +1173,17 @@ XPC_WN_NoMods_Proto_Resolve(JSContext* c
 }
 
 static const js::ClassOps XPC_WN_NoMods_Proto_JSClassOps = {
     XPC_WN_OnlyIWrite_Proto_AddPropertyStub,   // addProperty
     XPC_WN_CannotDeletePropertyStub,           // delProperty
     nullptr,                                   // getProperty
     nullptr,                                   // setProperty
     XPC_WN_Shared_Proto_Enumerate,             // enumerate
+    nullptr,                                   // newEnumerate
     XPC_WN_NoMods_Proto_Resolve,               // resolve
     nullptr,                                   // mayResolve
     XPC_WN_Shared_Proto_Finalize,              // finalize
     nullptr,                                   // call
     nullptr,                                   // construct
     nullptr,                                   // hasInstance
     XPC_WN_Shared_Proto_Trace,                 // trace
 };
@@ -1284,16 +1270,17 @@ static_assert(((XPC_WRAPPER_FLAGS >> JSC
               "XPC_WRAPPER_FLAGS should not include any reserved slots");
 
 static const js::ClassOps XPC_WN_Tearoff_JSClassOps = {
     XPC_WN_OnlyIWrite_AddPropertyStub,  // addProperty
     XPC_WN_CannotDeletePropertyStub,    // delProperty
     nullptr,                            // getProperty
     nullptr,                            // setProperty
     XPC_WN_TearOff_Enumerate,           // enumerate
+    nullptr,                            // newEnumerate
     XPC_WN_TearOff_Resolve,             // resolve
     nullptr,                            // mayResolve
     XPC_WN_TearOff_Finalize,            // finalize
     nullptr,                            // call
     nullptr,                            // construct
     nullptr,                            // hasInstance
     nullptr,                            // trace
 };
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1073,17 +1073,18 @@ ExpandoObjectFinalize(JSFreeOp* fop, JSO
 {
     // Release the principal.
     nsIPrincipal* principal = GetExpandoObjectPrincipal(obj);
     NS_RELEASE(principal);
 }
 
 const JSClassOps XrayExpandoObjectClassOps = {
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr, ExpandoObjectFinalize
+    nullptr, nullptr, nullptr, nullptr,
+    ExpandoObjectFinalize
 };
 
 bool
 XrayTraits::expandoObjectMatchesConsumer(JSContext* cx,
                                          HandleObject expandoObject,
                                          nsIPrincipal* consumerOrigin,
                                          HandleObject exclusiveGlobal)
 {
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -653,17 +653,17 @@ private:
 
     return NS_OK;
   }
 };
 
 static const JSClassOps sJSContextWrapperGlobalClassOps = {
   nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr,
   JS_GlobalObjectTraceHook
 };
 
 const JSClass JSContextWrapper::sGlobalClass = {
   "PACResolutionThreadGlobal",
   JSCLASS_GLOBAL_FLAGS,
   &sJSContextWrapperGlobalClassOps
 };
--- a/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
+++ b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
@@ -118,16 +118,17 @@ void Finalize(JSFreeOp *fop, JSObject *o
 }
 
 static const JSClassOps sWitnessClassOps = {
   nullptr /* addProperty */,
   nullptr /* delProperty */,
   nullptr /* getProperty */,
   nullptr /* setProperty */,
   nullptr /* enumerate */,
+  nullptr /* newEnumerate */,
   nullptr /* resolve */,
   nullptr /* mayResolve */,
   Finalize /* finalize */
 };
 
 static const JSClass sWitnessClass = {
   "FinalizationWitness",
   JSCLASS_HAS_RESERVED_SLOTS(WITNESS_INSTANCES_SLOTS) |
--- a/xpcom/tests/gtest/TestGCPostBarriers.cpp
+++ b/xpcom/tests/gtest/TestGCPostBarriers.cpp
@@ -82,17 +82,17 @@ RunTest(JSContext* cx, ArrayT* array)
 }
 
 static void
 CreateGlobalAndRunTest(JSContext* cx)
 {
   static const JSClassOps GlobalClassOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
   };
 
   static const JSClass GlobalClass = {
     "global", JSCLASS_GLOBAL_FLAGS,
     &GlobalClassOps
   };