Bug 1155946 part 1 - Add a mayResolve class hook to optimize objects with resolve hooks better. r=bhackett
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 23 Apr 2015 15:51:28 +0200
changeset 240670 8ed771ecfecd259a6236fbf0ad3214affb3b98aa
parent 240669 1d33044efb3163fa2fe5d06bb971969da2e98658
child 240710 27a61b918d5beedfc746b954d39c430215e0a366
push id58902
push userjandemooij@gmail.com
push dateThu, 23 Apr 2015 13:52:50 +0000
treeherdermozilla-inbound@8ed771ecfecd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs1155946
milestone40.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 1155946 part 1 - Add a mayResolve class hook to optimize objects with resolve hooks better. r=bhackett
dom/bindings/Codegen.py
dom/indexedDB/ActorsParent.cpp
dom/plugins/base/nsJSNPRuntime.cpp
dom/workers/WorkerScope.cpp
dom/xbl/nsXBLBinding.cpp
js/public/Class.h
js/src/asmjs/AsmJSModule.cpp
js/src/builtin/Intl.cpp
js/src/builtin/MapObject.cpp
js/src/builtin/Object.cpp
js/src/builtin/SIMD.cpp
js/src/builtin/SymbolObject.cpp
js/src/builtin/TestingFunctions.cpp
js/src/builtin/TypedObject.cpp
js/src/ctypes/CTypes.cpp
js/src/ctypes/Library.cpp
js/src/gdb/gdb-tests.cpp
js/src/jit/IonBuilder.cpp
js/src/jsapi-tests/testChromeBuffer.cpp
js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
js/src/jsapi-tests/testGCNursery.cpp
js/src/jsapi-tests/testNewObject.cpp
js/src/jsapi-tests/testOps.cpp
js/src/jsapi-tests/testPersistentRooted.cpp
js/src/jsapi-tests/testWeakMap.cpp
js/src/jsapi-tests/tests.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsdate.cpp
js/src/jsexn.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/jsstr.h
js/src/jsweakmap.cpp
js/src/perf/jsperf.cpp
js/src/shell/js.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/ArrayBufferObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/HelperThreads.cpp
js/src/vm/NativeObject-inl.h
js/src/vm/PIC.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpStatics.cpp
js/src/vm/SavedStacks.cpp
js/src/vm/ScopeObject.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/SharedArrayObject.cpp
js/src/vm/SharedTypedArrayObject.cpp
js/src/vm/TypedArrayObject.cpp
js/src/vm/UnboxedObject.cpp
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/XPCWrappedJSClass.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
netwerk/base/ProxyAutoConfig.cpp
toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -471,16 +471,17 @@ class CGDOMJSClass(CGThing):
               { "${name}",
                 ${flags},
                 ${addProperty}, /* addProperty */
                 nullptr,               /* delProperty */
                 nullptr,               /* getProperty */
                 nullptr,               /* setProperty */
                 ${enumerate}, /* enumerate */
                 ${resolve}, /* resolve */
+                nullptr,               /* mayResolve */
                 nullptr,               /* convert */
                 ${finalize}, /* finalize */
                 ${call}, /* call */
                 nullptr,               /* hasInstance */
                 nullptr,               /* construct */
                 ${trace}, /* trace */
                 JS_NULL_CLASS_SPEC,
                 $*{classExtensionAndObjectOps}
@@ -611,16 +612,17 @@ class CGPrototypeJSClass(CGThing):
                 "${name}Prototype",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 nullptr,               /* addProperty */
                 nullptr,               /* delProperty */
                 nullptr,               /* getProperty */
                 nullptr,               /* setProperty */
                 nullptr,               /* enumerate */
                 nullptr,               /* resolve */
+                nullptr,               /* mayResolve */
                 nullptr,               /* convert */
                 nullptr,               /* finalize */
                 nullptr,               /* call */
                 nullptr,               /* hasInstance */
                 nullptr,               /* construct */
                 nullptr,               /* trace */
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
@@ -704,16 +706,17 @@ class CGInterfaceObjectJSClass(CGThing):
                 "Function",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 nullptr,               /* addProperty */
                 nullptr,               /* delProperty */
                 nullptr,               /* getProperty */
                 nullptr,               /* setProperty */
                 nullptr,               /* enumerate */
                 nullptr,               /* resolve */
+                nullptr,               /* mayResolve */
                 nullptr,               /* convert */
                 nullptr,               /* finalize */
                 ${ctorname}, /* call */
                 ${hasInstance}, /* hasInstance */
                 ${ctorname}, /* construct */
                 nullptr,               /* trace */
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -20120,16 +20120,17 @@ const JSClass CreateIndexOp::ThreadLocal
   "IndexedDBTransactionThreadGlobal",
   JSCLASS_GLOBAL_FLAGS,
   /* addProperty */ nullptr,
   /* delProperty */ nullptr,
   /* getProperty */ nullptr,
   /* setProperty */ nullptr,
   /* enumerate */ nullptr,
   /* resolve */ nullptr,
+  /* mayResolve */ nullptr,
   /* convert */ nullptr,
   /* finalize */ nullptr,
   /* call */ nullptr,
   /* hasInstance */ nullptr,
   /* construct */ nullptr,
   /* trace */ JS_GlobalObjectTraceHook
 };
 
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -208,16 +208,17 @@ const static js::Class sNPObjectJSWrappe
     NPRUNTIME_JSCLASS_NAME,
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
     NPObjWrapper_AddProperty,
     NPObjWrapper_DelProperty,
     NPObjWrapper_GetProperty,
     NPObjWrapper_SetProperty,
     nullptr,
     NPObjWrapper_Resolve,
+    nullptr,                                                /* mayResolve */
     NPObjWrapper_Convert,
     NPObjWrapper_Finalize,
     NPObjWrapper_Call,
     nullptr,                                                /* hasInstance */
     NPObjWrapper_Construct,
     nullptr,                                                /* trace */
     JS_NULL_CLASS_SPEC,
     {
@@ -260,17 +261,17 @@ NPObjectMember_Call(JSContext *cx, unsig
 
 static void
 NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
 
 static const JSClass sNPObjectMemberClass =
   {
     "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, NPObjectMember_Convert,
+    nullptr, nullptr, nullptr, NPObjectMember_Convert,
     NPObjectMember_Finalize, NPObjectMember_Call,
     nullptr, nullptr, NPObjectMember_Trace
   };
 
 static void
 OnWrapperDestroyed();
 
 static void
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -594,16 +594,17 @@ const js::Class workerdebuggersandbox_cl
     "workerdebuggersandbox",
     JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     workerdebuggersandbox_enumerate,
     workerdebuggersandbox_resolve,
+    nullptr, /* mayResolve */
     workerdebuggersandbox_convert,
     workerdebuggersandbox_finalize,
     nullptr,
     nullptr,
     nullptr,
     JS_GlobalObjectTraceHook,
     JS_NULL_CLASS_SPEC, {
       nullptr,
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -88,17 +88,17 @@ XBLEnumerate(JSContext *cx, JS::Handle<J
 }
 
 static const JSClass gPrototypeJSClass = {
     "XBL prototype JSClass",
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
     // Our one reserved slot holds the relevant nsXBLPrototypeBinding
     JSCLASS_HAS_RESERVED_SLOTS(1),
     nullptr, nullptr, nullptr, nullptr,
-    XBLEnumerate, nullptr,
+    XBLEnumerate, nullptr, nullptr,
     nullptr, XBLFinalize,
     nullptr, nullptr, nullptr, nullptr
 };
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -19,16 +19,17 @@
 
 /*
  * A JSClass acts as a vtable for JS objects that allows JSAPI clients to
  * control various aspects of the behavior of an object like property lookup.
  * js::Class is an engine-private extension that allows more control over
  * object behavior and, e.g., allows custom slow layout.
  */
 
+struct JSAtomState;
 struct JSFreeOp;
 struct JSFunctionSpec;
 
 namespace js {
 
 struct Class;
 class FreeOp;
 class Shape;
@@ -276,16 +277,28 @@ typedef bool
 // JS looks for a property in an object, and if not found, tries to resolve
 // the given id. *resolvedp should be set to true iff the property was
 // was defined on |obj|.
 //
 typedef bool
 (* JSResolveOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                 bool* resolvedp);
 
+// A class with a resolve hook can optionally have a mayResolve hook. This hook
+// must have no side effects and must return true for a given id if the resolve
+// hook may resolve this id. This is useful when we're doing a "pure" lookup: if
+// mayResolve returns false, we know we don't have to call the effectful resolve
+// hook.
+//
+// maybeObj, if non-null, is the object on which we're doing the lookup. This
+// can be nullptr: during JIT compilation we sometimes know the Class but not
+// the object.
+typedef bool
+(* JSMayResolveOp)(const JSAtomState& names, jsid id, JSObject* maybeObj);
+
 // Convert obj to the given type, returning true with the resulting value in
 // *vp on success, and returning false on error or exception.
 typedef bool
 (* JSConvertOp)(JSContext* cx, JS::HandleObject obj, JSType type,
                 JS::MutableHandleValue vp);
 
 // Finalize obj, which the garbage collector has determined to be unreachable
 // from other live objects or from GC roots.  Obviously, finalizers must never
@@ -415,16 +428,17 @@ typedef void
                                                                               \
     /* Function pointer members (may be null). */                             \
     JSAddPropertyOp     addProperty;                                          \
     JSDeletePropertyOp  delProperty;                                          \
     JSGetterOp          getProperty;                                          \
     JSSetterOp          setProperty;                                          \
     JSEnumerateOp       enumerate;                                            \
     JSResolveOp         resolve;                                              \
+    JSMayResolveOp      mayResolve;                                           \
     JSConvertOp         convert;                                              \
     FinalizeOpType      finalize;                                             \
     JSNative            call;                                                 \
     JSHasInstanceOp     hasInstance;                                          \
     JSNative            construct;                                            \
     JSTraceOp           trace
 
 // Callback for the creation of constructor and prototype objects.
@@ -683,16 +697,18 @@ static_assert(offsetof(JSClass, delPrope
 static_assert(offsetof(JSClass, getProperty) == offsetof(Class, getProperty),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, setProperty) == offsetof(Class, setProperty),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, enumerate) == offsetof(Class, enumerate),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, resolve) == offsetof(Class, resolve),
               "Class and JSClass must be consistent");
+static_assert(offsetof(JSClass, mayResolve) == offsetof(Class, mayResolve),
+              "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, convert) == offsetof(Class, convert),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, finalize) == offsetof(Class, finalize),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, call) == offsetof(Class, call),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, construct) == offsetof(Class, construct),
               "Class and JSClass must be consistent");
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -993,16 +993,17 @@ const Class AsmJSModuleObject::class_ = 
     JSCLASS_IS_ANONYMOUS | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     AsmJSModuleObject_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     AsmJSModuleObject_trace
 };
 
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -560,16 +560,17 @@ static const Class CollatorClass = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(COLLATOR_SLOTS_COUNT),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     collator_finalize
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 collator_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -1052,16 +1053,17 @@ static const Class NumberFormatClass = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(NUMBER_FORMAT_SLOTS_COUNT),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     numberFormat_finalize
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 numberFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -1511,16 +1513,17 @@ static const Class DateTimeFormatClass =
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(DATE_TIME_FORMAT_SLOTS_COUNT),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     dateTimeFormat_finalize
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 dateTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -875,16 +875,17 @@ const Class MapIteratorObject::class_ = 
     JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     MapIteratorObject::finalize
 };
 
 const JSFunctionSpec MapIteratorObject::methods[] = {
     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_FN("next", next, 0, 0),
     JS_FS_END
@@ -1019,16 +1020,17 @@ const Class MapObject::class_ = {
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
     nullptr, // setProperty
     nullptr, // enumerate
     nullptr, // resolve
+    nullptr, // mayResolve
     nullptr, // convert
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
     mark
 };
 
@@ -1611,16 +1613,17 @@ const Class SetIteratorObject::class_ = 
     JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     SetIteratorObject::finalize
 };
 
 const JSFunctionSpec SetIteratorObject::methods[] = {
     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
     JS_FN("next", next, 0, 0),
     JS_FS_END
@@ -1751,16 +1754,17 @@ const Class SetObject::class_ = {
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
     nullptr, // addProperty
     nullptr, // delProperty
     nullptr, // getProperty
     nullptr, // setProperty
     nullptr, // enumerate
     nullptr, // resolve
+    nullptr, // mayResolve
     nullptr, // convert
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
     mark
 };
 
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1184,16 +1184,17 @@ const Class PlainObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
     nullptr,  /* addProperty */
     nullptr,  /* delProperty */
     nullptr,  /* getProperty */
     nullptr,  /* setProperty */
     nullptr,  /* enumerate */
     nullptr,  /* resolve */
+    nullptr,  /* mayResolve */
     nullptr,  /* convert */
     nullptr,  /* finalize */
     nullptr,  /* call */
     nullptr,  /* hasInstance */
     nullptr,  /* construct */
     nullptr,  /* trace */
     {
         CreateObjectConstructor,
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -175,16 +175,17 @@ const Class SimdTypeDescr::class_ = {
     "SIMD",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     TypeDescr::finalize,
     call
 };
 
 namespace {
 
 // These classes just exist to group together various properties and so on.
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -20,16 +20,17 @@ const Class SymbolObject::class_ = {
     "Symbol",
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     convert
 };
 
 SymbolObject*
 SymbolObject::create(JSContext* cx, JS::HandleSymbol symbol)
 {
     JSObject* obj = NewBuiltinClassInstance(cx, &class_);
     if (!obj)
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1149,16 +1149,17 @@ finalize_counter_finalize(JSFreeOp* fop,
 static const JSClass FinalizeCounterClass = {
     "FinalizeCounter", JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     finalize_counter_finalize
 };
 
 static bool
 MakeFinalizeObserver(JSContext* cx, unsigned argc, jsval* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1830,16 +1831,17 @@ class CloneBufferObject : public NativeO
 const Class CloneBufferObject::class_ = {
     "CloneBuffer", JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     Finalize
 };
 
 const JSPropertySpec CloneBufferObject::props_[] = {
     JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
     JS_PS_END
 };
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -219,16 +219,17 @@ const Class js::ScalarTypeDescr::class_ 
     "Scalar",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     TypeDescr::finalize,
     ScalarTypeDescr::call
 };
 
 const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
@@ -316,16 +317,17 @@ const Class js::ReferenceTypeDescr::clas
     "Reference",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     TypeDescr::finalize,
     ReferenceTypeDescr::call
 };
 
 const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
@@ -495,16 +497,17 @@ const Class ArrayTypeDescr::class_ = {
     "ArrayType",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     TypeDescr::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     TypedObject::construct
 };
 
 const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {
@@ -722,16 +725,17 @@ const Class StructTypeDescr::class_ = {
     "StructType",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     TypeDescr::finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     TypedObject::construct
 };
 
 const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {
@@ -2241,16 +2245,17 @@ OutlineTransparentTypedObject::getOrCrea
         # Name,                                          \
         Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS, \
         nullptr,        /* addProperty */                \
         nullptr,        /* delProperty */                \
         nullptr,        /* getProperty */                \
         nullptr,        /* setProperty */                \
         nullptr,        /* enumerate   */                \
         nullptr,        /* resolve     */                \
+        nullptr,        /* mayResolve  */                \
         nullptr,        /* convert     */                \
         nullptr,        /* finalize    */                \
         nullptr,        /* call        */                \
         nullptr,        /* hasInstance */                \
         nullptr,        /* construct   */                \
         Trace,                                           \
         JS_NULL_CLASS_SPEC,                              \
         JS_NULL_CLASS_EXT,                               \
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -528,49 +528,49 @@ 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 JSClass sCTypeProtoClass = {
   "CType",
   JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, nullptr,
   ConstructAbstract, nullptr, ConstructAbstract
 };
 
 // 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 JSClass sCTypeClass = {
   "CType",
   JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CType::Finalize,
   CType::ConstructData, CType::HasInstance, CType::ConstructData,
   CType::Trace
 };
 
 static const JSClass sCDataClass = {
   "CData",
   JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
   nullptr, nullptr, ArrayType::Getter, ArrayType::Setter,
-  nullptr, nullptr, nullptr, CData::Finalize,
+  nullptr, nullptr, nullptr, nullptr, CData::Finalize,
   FunctionType::Call, nullptr, FunctionType::Call
 };
 
 static const JSClass sCClosureClass = {
   "CClosure",
   JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CClosure::Finalize,
   nullptr, nullptr, nullptr, CClosure::Trace
 };
 
 /*
  * Class representing the prototype of CDataFinalizer.
  */
 static const JSClass sCDataFinalizerProtoClass = {
@@ -582,17 +582,17 @@ 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 JSClass sCDataFinalizerClass = {
   "CDataFinalizer",
   JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, CDataFinalizer::Finalize
 };
 
 
 #define CTYPESFN_FLAGS \
   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 
 #define CTYPESCTOR_FLAGS \
@@ -768,24 +768,24 @@ static const JSClass sInt64ProtoClass = 
 static const JSClass sUInt64ProtoClass = {
   "UInt64",
   0
 };
 
 static const JSClass sInt64Class = {
   "Int64",
   JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, Int64Base::Finalize
 };
 
 static const JSClass sUInt64Class = {
   "UInt64",
   JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, Int64Base::Finalize
 };
 
 static const JSFunctionSpec sInt64StaticFunctions[] = {
   JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
   JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
   JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
   // "join" is defined specially; see InitInt64Class.
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -30,17 +30,17 @@ namespace Library
 ** JSObject implementation
 *******************************************************************************/
 
 typedef Rooted<JSFlatString*>    RootedFlatString;
 
 static const JSClass sLibraryClass = {
   "Library",
   JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
-  nullptr, nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, Library::Finalize
 };
 
 #define CTYPESFN_FLAGS \
   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 
 static const JSFunctionSpec sLibraryFunctions[] = {
   JS_FN("close",   Library::Close,   0, CTYPESFN_FLAGS),
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -12,17 +12,17 @@
 
 using namespace JS;
 
 /* The class of the global object. */
 const JSClass global_class = {
     "global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 template<typename T>
 static inline T*
 checkPtr(T* ptr)
 {
   if (! ptr)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7097,28 +7097,18 @@ ObjectHasExtraOwnProperty(CompileCompart
 
     const Class* clasp = object->clasp();
 
     // Array |length| properties are not reflected in type information.
     if (clasp == &ArrayObject::class_)
         return name == comp->runtime()->names().length;
 
     // Resolve hooks can install new properties on objects on demand.
-    if (!clasp->resolve)
-        return false;
-
-    if (clasp->resolve == str_resolve) {
-        // str_resolve only resolves integers, not names.
-        return false;
-    }
-
-    if (clasp->resolve == fun_resolve)
-        return FunctionHasResolveHook(comp->runtime()->names(), NameToId(name));
-
-    return true;
+    JSObject* singleton = object->isSingleton() ? object->singleton() : nullptr;
+    return ClassMayResolveId(comp->runtime()->names(), clasp, NameToId(name), singleton);
 }
 
 void
 IonBuilder::insertRecompileCheck()
 {
     // No need for recompile checks if this is the highest optimization level.
     OptimizationLevel curLevel = optimizationInfo().level();
     if (js_IonOptimizations.isLastLevel(curLevel))
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -17,16 +17,17 @@ static const JSClass global_class = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static JS::PersistentRootedObject trusted_glob;
 static JS::PersistentRootedObject trusted_fun;
 
 static bool
 CallTrusted(JSContext* cx, unsigned argc, jsval* vp)
--- a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
+++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
@@ -20,17 +20,17 @@ GlobalResolve(JSContext* cx, JS::HandleO
 }
 
 BEGIN_TEST(testRedefineGlobalEval)
 {
     static const JSClass cls = {
         "global", JSCLASS_GLOBAL_FLAGS,
         nullptr, nullptr, nullptr, nullptr,
         GlobalEnumerate, GlobalResolve, nullptr,
-        nullptr, nullptr, nullptr, nullptr,
+        nullptr, nullptr, nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
     /* Create the global object. */
     JS::CompartmentOptions options;
     options.setVersion(JSVERSION_LATEST);
     JS::Rooted<JSObject*> g(cx, JS_NewGlobalObject(cx, &cls, nullptr, JS::FireOnNewGlobalHook, options));
     if (!g)
--- a/js/src/jsapi-tests/testGCNursery.cpp
+++ b/js/src/jsapi-tests/testGCNursery.cpp
@@ -22,16 +22,17 @@ static const js::Class TenuredClass = {
     "TenuredClass",
     0,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* ??? */
     _finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
@@ -42,16 +43,17 @@ static const js::Class NurseryClass = {
     "NurseryClass",
     JSCLASS_FINALIZE_FROM_NURSERY | JSCLASS_HAS_RESERVED_SLOTS(1),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* ??? */
     _finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
--- a/js/src/jsapi-tests/testNewObject.cpp
+++ b/js/src/jsapi-tests/testNewObject.cpp
@@ -93,17 +93,17 @@ BEGIN_TEST(testNewObject_1)
     CHECK_SAME(v, INT_TO_JSVAL(N - 1));
 
     // With JSClass.construct.
     static const JSClass cls = {
         "testNewObject_1",
         0,
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, constructHook
+        nullptr, nullptr, nullptr, constructHook
     };
     JS::RootedObject ctor(cx, JS_NewObject(cx, &cls));
     CHECK(ctor);
     JS::RootedValue rt2(cx, OBJECT_TO_JSVAL(ctor));
     obj = JS_New(cx, ctor, JS::HandleValueArray::subarray(argv, 0, 3));
     CHECK(obj);
     CHECK(JS_GetElement(cx, ctor, 0, &v));
     CHECK_SAME(v, JSVAL_ZERO);
--- a/js/src/jsapi-tests/testOps.cpp
+++ b/js/src/jsapi-tests/testOps.cpp
@@ -18,17 +18,17 @@ my_convert(JSContext* context, JS::Handl
     }
     return false;
 }
 
 static const JSClass myClass = {
     "MyClass",
     0,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, my_convert
+    nullptr, nullptr, nullptr, my_convert
 };
 
 static bool
 createMyObject(JSContext* context, unsigned argc, jsval* vp)
 {
     JS_BeginRequest(context);
 
     //JS_GC(context); //<- if we make GC here, all is ok
--- a/js/src/jsapi-tests/testPersistentRooted.cpp
+++ b/js/src/jsapi-tests/testPersistentRooted.cpp
@@ -24,16 +24,17 @@ const JSClass BarkWhenTracedClass::class
     "BarkWhenTracedClass", JSCLASS_IMPLEMENTS_BARRIERS,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     finalize,
     nullptr,
     nullptr,
     nullptr,
     trace
 };
 
 struct Kennel {
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -143,16 +143,17 @@ JSObject* newKey()
         "keyWithDelgate",
         JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1),
         nullptr, /* addProperty */
         nullptr, /* delProperty */
         nullptr, /* getProperty */
         nullptr, /* setProperty */
         nullptr, /* enumerate */
         nullptr, /* resolve */
+        nullptr, /* mayResolve */
         nullptr, /* convert */
         nullptr, /* finalize */
         nullptr, /* call */
         nullptr, /* hasInstance */
         nullptr, /* construct */
         nullptr, /* trace */
         JS_NULL_CLASS_SPEC,
         {
@@ -199,16 +200,17 @@ JSObject* newDelegate()
         "delegate",
         JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_RESERVED_SLOTS(1),
         nullptr, /* addProperty */
         nullptr, /* delProperty */
         nullptr, /* getProperty */
         nullptr, /* setProperty */
         nullptr, /* enumerate */
         nullptr, /* resolve */
+        nullptr, /* mayResolve */
         nullptr, /* convert */
         nullptr, /* finalize */
         nullptr, /* call */
         nullptr, /* hasInstance */
         nullptr, /* construct */
         JS_GlobalObjectTraceHook,
         JS_NULL_CLASS_SPEC,
         {
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -225,17 +225,17 @@ class JSAPITest
 
     JSAPITestString messages() const { return msgs; }
 
     static const JSClass * basicGlobalClass() {
         static const JSClass c = {
             "global", JSCLASS_GLOBAL_FLAGS,
             nullptr, nullptr, nullptr, nullptr,
             nullptr, nullptr, nullptr, nullptr,
-            nullptr, nullptr, nullptr,
+            nullptr, nullptr, nullptr, nullptr,
             JS_GlobalObjectTraceHook
         };
         return &c;
     }
 
   protected:
     static bool
     print(JSContext* cx, unsigned argc, jsval* vp)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1133,23 +1133,22 @@ JS_InitStandardClasses(JSContext* cx, Ha
 typedef struct JSStdName {
     size_t      atomOffset;     /* offset of atom pointer in JSAtomState */
     JSProtoKey  key;
     bool isDummy() const { return key == JSProto_Null; }
     bool isSentinel() const { return key == JSProto_LIMIT; }
 } JSStdName;
 
 static const JSStdName*
-LookupStdName(JSRuntime* rt, HandleString name, const JSStdName* table)
-{
-    MOZ_ASSERT(name->isAtom());
+LookupStdName(const JSAtomState& names, JSAtom* name, const JSStdName* table)
+{
     for (unsigned i = 0; !table[i].isSentinel(); i++) {
         if (table[i].isDummy())
             continue;
-        JSAtom* atom = AtomStateOffsetToName(*rt->commonNames, table[i].atomOffset);
+        JSAtom* atom = AtomStateOffsetToName(names, table[i].atomOffset);
         MOZ_ASSERT(atom);
         if (name == atom)
             return &table[i];
     }
 
     return nullptr;
 }
 
@@ -1215,33 +1214,32 @@ JS_ResolveStandardClass(JSContext* cx, H
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     *resolved = false;
 
     rt = cx->runtime();
     if (!rt->hasContexts() || !JSID_IS_ATOM(id))
         return true;
 
-    RootedString idstr(cx, JSID_TO_STRING(id));
-
     /* Check whether we're resolving 'undefined', and define it if so. */
+    JSAtom* idAtom = JSID_TO_ATOM(id);
     JSAtom* undefinedAtom = cx->names().undefined;
-    if (idstr == undefinedAtom) {
+    if (idAtom == undefinedAtom) {
         *resolved = true;
         return DefineProperty(cx, obj, undefinedAtom->asPropertyName(),
                               UndefinedHandleValue, nullptr, nullptr,
                               JSPROP_PERMANENT | JSPROP_READONLY);
     }
 
     /* Try for class constructors/prototypes named by well-known atoms. */
-    stdnm = LookupStdName(rt, idstr, standard_class_names);
+    stdnm = LookupStdName(cx->names(), idAtom, standard_class_names);
 
     /* Try less frequently used top-level functions and constants. */
     if (!stdnm)
-        stdnm = LookupStdName(rt, idstr, builtin_property_names);
+        stdnm = LookupStdName(cx->names(), idAtom, builtin_property_names);
 
     // If this class is anonymous, then it doesn't exist as a global
     // property, so we won't resolve anything.
     JSProtoKey key = stdnm ? stdnm->key : JSProto_Null;
     if (key != JSProto_Null && !(ProtoKeyToClass(key)->flags & JSCLASS_IS_ANONYMOUS)) {
         if (!GlobalObject::ensureConstructor(cx, global, stdnm->key))
             return false;
 
@@ -1256,16 +1254,37 @@ JS_ResolveStandardClass(JSContext* cx, H
     // Object.prototype yet. Force it now.
     if (!global->getOrCreateObjectPrototype(cx))
         return false;
 
     return true;
 }
 
 JS_PUBLIC_API(bool)
+JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj)
+{
+    MOZ_ASSERT_IF(maybeObj, maybeObj->is<GlobalObject>());
+
+    // The global object's resolve hook is special: JS_ResolveStandardClass
+    // initializes the prototype chain lazily. Only attempt to optimize here
+    // if we know the prototype chain has been initialized.
+    if (!maybeObj || !maybeObj->getProto())
+        return true;
+
+    if (!JSID_IS_ATOM(id))
+        return false;
+
+    JSAtom* atom = JSID_TO_ATOM(id);
+
+    return atom == names.undefined ||
+           LookupStdName(names, atom, standard_class_names) ||
+           LookupStdName(names, atom, builtin_property_names);
+}
+
+JS_PUBLIC_API(bool)
 JS_EnumerateStandardClasses(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     MOZ_ASSERT(obj->is<GlobalObject>());
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     return GlobalObject::initStandardClasses(cx, global);
@@ -1300,18 +1319,19 @@ ProtoKeyToId(JSContext* cx, JSProtoKey k
 JS_PUBLIC_API(JSProtoKey)
 JS_IdToProtoKey(JSContext* cx, HandleId id)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     if (!JSID_IS_ATOM(id))
         return JSProto_Null;
-    RootedString idstr(cx, JSID_TO_STRING(id));
-    const JSStdName* stdnm = LookupStdName(cx->runtime(), idstr, standard_class_names);
+
+    JSAtom* atom = JSID_TO_ATOM(id);
+    const JSStdName* stdnm = LookupStdName(cx->names(), atom, standard_class_names);
     if (!stdnm)
         return JSProto_Null;
 
     MOZ_ASSERT(MOZ_ARRAY_LENGTH(standard_class_names) == JSProto_LIMIT + 1);
     return static_cast<JSProtoKey>(stdnm - standard_class_names);
 }
 
 JS_PUBLIC_API(JSObject*)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1502,16 +1502,19 @@ JS_InitStandardClasses(JSContext* cx, JS
  * to define standard classes lazily.  The class's enumerate op should call
  * JS_EnumerateStandardClasses(cx, obj), to define eagerly during for..in
  * loops any classes not yet resolved lazily.
  */
 extern JS_PUBLIC_API(bool)
 JS_ResolveStandardClass(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolved);
 
 extern JS_PUBLIC_API(bool)
+JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj);
+
+extern JS_PUBLIC_API(bool)
 JS_EnumerateStandardClasses(JSContext* cx, JS::HandleObject obj);
 
 extern JS_PUBLIC_API(bool)
 JS_GetClassObject(JSContext* cx, JSProtoKey key, JS::MutableHandle<JSObject*> objp);
 
 extern JS_PUBLIC_API(bool)
 JS_GetClassPrototype(JSContext* cx, JSProtoKey key, JS::MutableHandle<JSObject*> objp);
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3244,16 +3244,17 @@ const Class ArrayObject::class_ = {
     "Array",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
     array_addProperty,
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     {
         GenericCreateConstructor<ArrayConstructor, 1, JSFunction::FinalizeKind>,
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -3039,16 +3039,17 @@ const Class DateObject::class_ = {
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     date_convert,
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     {
         GenericCreateConstructor<DateConstructor, MAXARGS, JSFunction::FinalizeKind>,
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -72,16 +72,17 @@ static const JSFunctionSpec exception_me
         JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \
         JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS), \
         nullptr,                 /* addProperty */ \
         nullptr,                 /* delProperty */ \
         nullptr,                 /* getProperty */ \
         nullptr,                 /* setProperty */ \
         nullptr,                 /* enumerate */ \
         nullptr,                 /* resolve */ \
+        nullptr,                 /* mayResolve */ \
         nullptr,                 /* convert */ \
         exn_finalize, \
         nullptr,                 /* call        */ \
         nullptr,                 /* hasInstance */ \
         nullptr,                 /* construct   */ \
         nullptr,                 /* trace       */ \
         { \
             ErrorObject::createConstructor, \
@@ -103,16 +104,17 @@ ErrorObject::classes[JSEXN_LIMIT] = {
         JSCLASS_HAS_CACHED_PROTO(JSProto_Error) |
         JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS),
         nullptr,                 /* addProperty */
         nullptr,                 /* delProperty */
         nullptr,                 /* getProperty */
         nullptr,                 /* setProperty */
         nullptr,                 /* enumerate */
         nullptr,                 /* resolve */
+        nullptr,                 /* mayResolve */
         nullptr,                 /* convert */
         exn_finalize,
         nullptr,                 /* call        */
         nullptr,                 /* hasInstance */
         nullptr,                 /* construct   */
         nullptr,                 /* trace       */
         {
             ErrorObject::createConstructor,
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -314,16 +314,17 @@ namespace js {
             JSCLASS_IMPLEMENTS_BARRIERS |                                               \
             flags,                                                                      \
         nullptr,                 /* addProperty */                                      \
         nullptr,                 /* delProperty */                                      \
         nullptr,                 /* getProperty */                                      \
         nullptr,                 /* setProperty */                                      \
         nullptr,                 /* enumerate */                                        \
         nullptr,                 /* resolve */                                          \
+        nullptr,                 /* mayResolve */                                       \
         js::proxy_Convert,                                                              \
         js::proxy_Finalize,      /* finalize    */                                      \
         nullptr,                 /* call        */                                      \
         js::proxy_HasInstance,   /* hasInstance */                                      \
         nullptr,                 /* construct   */                                      \
         js::proxy_Trace,         /* trace       */                                      \
         JS_NULL_CLASS_SPEC,                                                             \
         ext,                                                                            \
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -414,28 +414,28 @@ ResolveInterpretedFunctionPrototype(JSCo
         RootedValue objVal(cx, ObjectValue(*obj));
         if (!DefineProperty(cx, proto, cx->names().constructor, objVal, nullptr, nullptr, 0))
             return nullptr;
     }
 
     return proto;
 }
 
-bool
-js::FunctionHasResolveHook(const JSAtomState& atomState, jsid id)
+static bool
+fun_mayResolve(const JSAtomState& names, jsid id, JSObject*)
 {
     if (!JSID_IS_ATOM(id))
         return false;
 
     JSAtom* atom = JSID_TO_ATOM(id);
-    return atom == atomState.prototype || atom == atomState.length || atom == atomState.name;
+    return atom == names.prototype || atom == names.length || atom == names.name;
 }
 
-bool
-js::fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
+static bool
+fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
 {
     if (!JSID_IS_ATOM(id))
         return true;
 
     RootedFunction fun(cx, &obj->as<JSFunction>());
 
     if (JSID_IS_ATOM(id, cx->names().prototype)) {
         /*
@@ -886,17 +886,18 @@ const Class JSFunction::class_ = {
     js_Function_str,
     JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     fun_enumerate,
-    js::fun_resolve,
+    fun_resolve,
+    fun_mayResolve,
     nullptr,                 /* convert     */
     nullptr,                 /* finalize    */
     nullptr,                 /* call        */
     fun_hasInstance,
     nullptr,                 /* construct   */
     fun_trace,
     {
         CreateFunctionConstructor,
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -48,18 +48,18 @@ class JSFunction : public js::NativeObje
                                        Function() call (not a FunctionDeclaration or nonstandard
                                        function-statement) */
         SELF_HOSTED      = 0x0080,  /* function is self-hosted builtin and must not be
                                        decompilable nor constructible. */
         SELF_HOSTED_CTOR = 0x0100,  /* function is self-hosted builtin constructor and
                                        must be constructible but not decompilable. */
         HAS_REST         = 0x0200,  /* function has a rest (...) parameter */
         INTERPRETED_LAZY = 0x0400,  /* function is interpreted but doesn't have a script yet */
-        RESOLVED_LENGTH  = 0x0800,  /* f.length has been resolved (see js::fun_resolve). */
-        RESOLVED_NAME    = 0x1000,  /* f.name has been resolved (see js::fun_resolve). */
+        RESOLVED_LENGTH  = 0x0800,  /* f.length has been resolved (see fun_resolve). */
+        RESOLVED_NAME    = 0x1000,  /* f.name has been resolved (see fun_resolve). */
 
         FUNCTION_KIND_SHIFT = 13,
         FUNCTION_KIND_MASK  = 0x3 << FUNCTION_KIND_SHIFT,
 
         ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT,
         ARROW_KIND = Arrow << FUNCTION_KIND_SHIFT,
 
         /* Derived Flags values for convenience: */
@@ -564,19 +564,16 @@ DefineFunction(JSContext* cx, HandleObje
                unsigned nargs, unsigned flags,
                gc::AllocKind allocKind = JSFunction::FinalizeKind,
                NewObjectKind newKind = GenericObject);
 
 bool
 FunctionHasResolveHook(const JSAtomState& atomState, jsid id);
 
 extern bool
-fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
-
-extern bool
 fun_toString(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 fun_bind(JSContext* cx, unsigned argc, Value* vp);
 
 /*
  * Function extended with reserved slots for use by various kinds of functions.
  * Most functions do not have these extensions, but enough do that efficient
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -983,16 +983,17 @@ const Class PropertyIteratorObject::clas
     JSCLASS_HAS_PRIVATE |
     JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     finalize,
     nullptr, /* call        */
     nullptr, /* hasInstance */
     nullptr, /* construct   */
     trace
 };
 
@@ -1370,16 +1371,17 @@ const Class StopIterationObject::class_ 
     "StopIteration",
     JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     stopiter_hasInstance
 };
 
 /* static */ bool
 GlobalObject::initIteratorClasses(JSContext* cx, Handle<GlobalObject*> global)
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2779,29 +2779,20 @@ js::LookupPropertyPure(ExclusiveContext*
             }
 
             if (Shape* shape = obj->as<NativeObject>().lookupPure(id)) {
                 *objp = obj;
                 *propp = shape;
                 return true;
             }
 
-            // Fail if there's a resolve hook. We allow the JSFunction resolve hook
-            // if we know it will never add a property with this name or str_resolve
-            // with a non-integer property.
-            do {
-                const Class* clasp = obj->getClass();
-                if (!clasp->resolve)
-                    break;
-                if (clasp->resolve == fun_resolve && !FunctionHasResolveHook(cx->names(), id))
-                    break;
-                if (clasp->resolve == str_resolve && !JSID_IS_INT(id))
-                    break;
+            // Fail if there's a resolve hook, unless the mayResolve hook tells
+            // us the resolve hook won't define a property with this id.
+            if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
                 return false;
-            } while (0);
         } else if (obj->is<UnboxedPlainObject>()) {
             if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
                 *objp = obj;
                 MarkNonNativePropertyFound<NoGC>(propp);
                 return true;
             }
         } else if (obj->is<TypedObject>()) {
             if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1356,16 +1356,17 @@ const Class ScriptSourceObject::class_ =
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
     JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     trace
 };
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -389,18 +389,25 @@ str_enumerate(JSContext* cx, HandleObjec
         value.setString(str1);
         if (!DefineElement(cx, obj, i, value, nullptr, nullptr, STRING_ELEMENT_ATTRS))
             return false;
     }
 
     return true;
 }
 
-bool
-js::str_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
+static bool
+str_mayResolve(const JSAtomState&, jsid id, JSObject*)
+{
+    // str_resolve ignores non-integer ids.
+    return JSID_IS_INT(id);
+}
+
+static bool
+str_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
 {
     if (!JSID_IS_INT(id))
         return true;
 
     RootedString str(cx, obj->as<StringObject>().unbox());
 
     int32_t slot = JSID_TO_INT(id);
     if ((size_t)slot < str->length()) {
@@ -419,17 +426,18 @@ const Class StringObject::class_ = {
     js_String_str,
     JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_String),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     str_enumerate,
-    str_resolve
+    str_resolve,
+    str_mayResolve
 };
 
 /*
  * Returns a JSString * for the |this| value associated with 'call', or throws
  * a TypeError if |this| is null or undefined.  This algorithm is the same as
  * calling CheckObjectCoercible(this), then returning ToString(this), as all
  * String.prototype.* methods do (other than toString and valueOf).
  */
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -404,19 +404,16 @@ str_search(JSContext* cx, unsigned argc,
 
 bool
 str_split(JSContext* cx, unsigned argc, Value* vp);
 
 JSObject*
 str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep);
 
 bool
-str_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
-
-bool
 str_replace_regexp_raw(JSContext* cx, HandleString string, HandleObject regexp,
                        HandleString replacement, MutableHandleValue rval);
 
 bool
 str_replace_string_raw(JSContext* cx, HandleString string, HandleString pattern,
                        HandleString replacement, MutableHandleValue rval);
 
 extern bool
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -611,16 +611,17 @@ const Class WeakMapObject::class_ = {
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     WeakMap_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     WeakMap_mark
 };
 
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -158,17 +158,17 @@ static const struct pm_const {
 
 #undef CONSTANT
 
 static bool pm_construct(JSContext* cx, unsigned argc, jsval* vp);
 static void pm_finalize(JSFreeOp* fop, JSObject* obj);
 
 static const JSClass pm_class = {
     "PerfMeasurement", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, pm_finalize
 };
 
 // Constructor and destructor
 
 static bool
 pm_construct(JSContext* cx, unsigned argc, jsval* vp)
 {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2655,17 +2655,17 @@ sandbox_resolve(JSContext* cx, HandleObj
     return true;
 }
 
 static const JSClass sandbox_class = {
     "sandbox",
     JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     sandbox_enumerate, sandbox_resolve,
-    nullptr, nullptr,
+    nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static JSObject*
 NewSandbox(JSContext* cx, bool lazy)
 {
     RootedObject obj(cx, JS_NewGlobalObject(cx, &sandbox_class, nullptr,
@@ -5114,20 +5114,26 @@ global_resolve(JSContext* cx, HandleObje
 {
 #ifdef LAZY_STANDARD_CLASSES
     if (!JS_ResolveStandardClass(cx, obj, id, resolvedp))
         return false;
 #endif
     return true;
 }
 
+static bool
+global_mayResolve(const JSAtomState& names, jsid id, JSObject* maybeObj)
+{
+    return JS_MayResolveStandardClass(names, id, maybeObj);
+}
+
 static const JSClass global_class = {
     "global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
-    global_enumerate, global_resolve,
+    global_enumerate, global_resolve, global_mayResolve,
     nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 /*
  * Define a FakeDOMObject constructor. It returns an object with a getter,
  * setter and method with attached JitInfo. This object can be used to test
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -555,16 +555,17 @@ const Class NormalArgumentsObject::class
     JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr,                 /* addProperty */
     args_delProperty,
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     args_enumerate,
     args_resolve,
+    nullptr,                 /* mayResolve  */
     nullptr,                 /* convert     */
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
 
@@ -579,15 +580,16 @@ const Class StrictArgumentsObject::class
     JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr,                 /* addProperty */
     args_delProperty,
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     strictargs_enumerate,
     strictargs_resolve,
+    nullptr,                 /* mayResolve  */
     nullptr,                 /* convert     */
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -103,16 +103,17 @@ const Class ArrayBufferObject::class_ = 
     JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
     JSCLASS_BACKGROUND_FINALIZE,
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     nullptr,                 /* enumerate */
     nullptr,                 /* resolve */
+    nullptr,                 /* mayResolve */
     nullptr,                 /* convert */
     ArrayBufferObject::finalize,
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     ArrayBufferObject::trace,
     JS_NULL_CLASS_SPEC,
     {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2400,17 +2400,17 @@ Debugger::finalize(FreeOp* fop, JSObject
         return;
     fop->delete_(dbg);
 }
 
 const Class Debugger::jsclass = {
     "Debugger",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, Debugger::finalize,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     Debugger::traceObject
 };
 
 /* static */ Debugger*
@@ -4199,17 +4199,17 @@ DebuggerScript_trace(JSTracer* trc, JSOb
         obj->as<NativeObject>().setPrivateUnbarriered(script);
     }
 }
 
 const Class DebuggerScript_class = {
     "Script",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerScript_trace
 };
 
 JSObject*
@@ -5220,17 +5220,17 @@ DebuggerSource_trace(JSTracer* trc, JSOb
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
 const Class DebuggerSource_class = {
     "Source",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerSource_trace
 };
 
 JSObject*
@@ -5575,17 +5575,17 @@ DebuggerFrame_maybeDecrementFrameScriptS
 static void
 DebuggerFrame_finalize(FreeOp* fop, JSObject* obj)
 {
     DebuggerFrame_freeScriptFrameIterData(fop, obj);
 }
 
 const Class DebuggerFrame_class = {
     "Frame", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, DebuggerFrame_finalize
 };
 
 static NativeObject*
 CheckThisFrame(JSContext* cx, const CallArgs& args, const char* fnname, bool checkLive)
 {
     if (!args.thisv().isObject()) {
         ReportObjectRequired(cx);
@@ -6337,17 +6337,17 @@ DebuggerObject_trace(JSTracer* trc, JSOb
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
 const Class DebuggerObject_class = {
     "Object",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerObject_trace
 };
 
 static NativeObject*
@@ -7247,17 +7247,17 @@ DebuggerEnv_trace(JSTracer* trc, JSObjec
         obj->as<NativeObject>().setPrivateUnbarriered(referent);
     }
 }
 
 const Class DebuggerEnv_class = {
     "Environment",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     DebuggerEnv_trace
 };
 
 static NativeObject*
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -501,17 +501,17 @@ static void
 GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj)
 {
     fop->delete_((GlobalObject::DebuggerVector*) obj->as<NativeObject>().getPrivate());
 }
 
 static const Class
 GlobalDebuggees_class = {
     "GlobalDebuggee", JSCLASS_HAS_PRIVATE,
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, GlobalDebuggees_finalize
 };
 
 GlobalObject::DebuggerVector*
 GlobalObject::getDebuggers()
 {
     Value debuggers = getReservedSlot(DEBUGGERS);
     if (debuggers.isUndefined())
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -187,17 +187,17 @@ js::CancelOffThreadIonCompile(JSCompartm
         builder = next;
     }
 }
 
 static const JSClass parseTaskGlobalClass = {
     "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 ParseTask::ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal, JSContext* initCx,
                      const char16_t* chars, size_t length,
                      JS::OffThreadCompileCallback callback, void* callbackData)
   : cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -409,27 +409,54 @@ CallResolveOp(JSContext* cx, HandleNativ
 
     bool resolved = false;
     if (!obj->getClass()->resolve(cx, obj, id, &resolved))
         return false;
 
     if (!resolved)
         return true;
 
+    // Assert the mayResolve hook, if there is one, returns true for this
+    // property.
+    MOZ_ASSERT_IF(obj->getClass()->mayResolve,
+                  obj->getClass()->mayResolve(cx->names(), id, obj));
+
     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
         MarkDenseOrTypedArrayElementFound<CanGC>(propp);
         return true;
     }
 
     MOZ_ASSERT(!IsAnyTypedArray(obj));
 
     propp.set(obj->lookup(cx, id));
     return true;
 }
 
+static MOZ_ALWAYS_INLINE bool
+ClassMayResolveId(const JSAtomState& names, const Class* clasp, jsid id, JSObject* maybeObj)
+{
+    MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
+
+    if (!clasp->resolve) {
+        // Sanity check: we should only have a mayResolve hook if we have a
+        // resolve hook.
+        MOZ_ASSERT(!clasp->mayResolve, "Class with mayResolve hook but no resolve hook");
+        return false;
+    }
+
+    if (clasp->mayResolve) {
+        // Tell the analysis our mayResolve hooks won't trigger GC.
+        JS::AutoSuppressGCAnalysis nogc;
+        if (!clasp->mayResolve(names, id, maybeObj))
+            return false;
+    }
+
+    return true;
+}
+
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE bool
 LookupOwnPropertyInline(ExclusiveContext* cx,
                         typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
                         typename MaybeRooted<jsid, allowGC>::HandleType id,
                         typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp,
                         bool* donep)
 {
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -291,17 +291,17 @@ static void
 ForOfPIC_traceObject(JSTracer* trc, JSObject* obj)
 {
     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
         chain->mark(trc);
 }
 
 const Class ForOfPIC::jsclass = {
     "ForOfPIC", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, ForOfPIC_finalize,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
     ForOfPIC_traceObject
 };
 
 /* static */ NativeObject*
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -264,16 +264,17 @@ const Class RegExpObject::class_ = {
     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     RegExpObject::trace,
 
     // ClassSpec
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -39,16 +39,17 @@ const Class RegExpStaticsObject::class_ 
     "RegExpStatics",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     resc_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     resc_trace
 };
 
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -180,16 +180,17 @@ SavedFrame::finishSavedFrameInit(JSConte
     JSCLASS_HAS_CACHED_PROTO(JSProto_SavedFrame) |
     JSCLASS_IS_ANONYMOUS,
     nullptr,                    // addProperty
     nullptr,                    // delProperty
     nullptr,                    // getProperty
     nullptr,                    // setProperty
     nullptr,                    // enumerate
     nullptr,                    // resolve
+    nullptr,                    // mayResolve
     nullptr,                    // convert
     SavedFrame::finalize,       // finalize
     nullptr,                    // call
     nullptr,                    // hasInstance
     nullptr,                    // construct
     nullptr,                    // trace
 
     // ClassSpec
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -535,16 +535,17 @@ const Class DynamicWithObject::class_ = 
     JSCLASS_HAS_RESERVED_SLOTS(DynamicWithObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
@@ -972,16 +973,17 @@ const Class UninitializedLexicalObject::
     JSCLASS_HAS_RESERVED_SLOTS(UninitializedLexicalObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1537,17 +1537,17 @@ JSRuntime::createSelfHostingGlobal(JSCon
     JSCompartment* compartment = NewCompartment(cx, nullptr, nullptr, options);
     if (!compartment)
         return nullptr;
 
     static const Class shgClass = {
         "self-hosting-global", JSCLASS_GLOBAL_FLAGS,
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, nullptr,
+        nullptr, nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
     AutoCompartment ac(cx, compartment);
     Rooted<GlobalObject*> shg(cx, GlobalObject::createInternal(cx, &shgClass));
     if (!shg)
         return nullptr;
 
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -308,16 +308,17 @@ const Class SharedArrayBufferObject::cla
     JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     SharedArrayBufferObject::Finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT
--- a/js/src/vm/SharedTypedArrayObject.cpp
+++ b/js/src/vm/SharedTypedArrayObject.cpp
@@ -706,16 +706,17 @@ IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPP
     JSCLASS_HAS_PRIVATE |                                                      \
     JSCLASS_HAS_CACHED_PROTO(JSProto_Shared##_typedArray),                     \
     nullptr,                 /* addProperty */                                 \
     nullptr,                 /* delProperty */                                 \
     nullptr,                 /* getProperty */                                 \
     nullptr,                 /* setProperty */                                 \
     nullptr,                 /* enumerate   */                                 \
     nullptr,                 /* resolve     */                                 \
+    nullptr,                 /* mayResolve  */                                 \
     nullptr,                 /* convert     */                                 \
     nullptr,                 /* finalize    */                                 \
     nullptr,                 /* call        */                                 \
     nullptr,                 /* hasInstance */                                 \
     nullptr,                 /* construct   */                                 \
     nullptr,                 /* trace  */                                      \
     SHARED_TYPED_ARRAY_CLASS_SPEC(_typedArray)                                 \
 }
@@ -727,16 +728,17 @@ IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPP
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |                        \
     JSCLASS_HAS_CACHED_PROTO(JSProto_Shared##_typedArray),                     \
     nullptr,                 /* addProperty */                                 \
     nullptr,                 /* delProperty */                                 \
     nullptr,                 /* getProperty */                                 \
     nullptr,                 /* setProperty */                                 \
     nullptr,                 /* enumerate   */                                 \
     nullptr,                 /* resolve     */                                 \
+    nullptr,                 /* mayResolve  */                                 \
     nullptr,                 /* convert     */                                 \
     nullptr,                 /* finalize    */                                 \
     nullptr,                 /* call        */                                 \
     nullptr,                 /* hasInstance */                                 \
     nullptr,                 /* construct   */                                 \
     nullptr,                 /* trace  */                                      \
     SHARED_TYPED_ARRAY_CLASS_SPEC(_typedArray)                                 \
 }
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -834,16 +834,17 @@ TypedArrayObject::sharedTypedArrayProtot
     "???",
     JSCLASS_HAS_CACHED_PROTO(JSProto_TypedArray),
     nullptr,                /* addProperty */
     nullptr,                /* delProperty */
     nullptr,                /* getProperty */
     nullptr,                /* setProperty */
     nullptr,                /* enumerate */
     nullptr,                /* resolve */
+    nullptr,                /* mayResolve */
     nullptr,                /* convert */
     nullptr,                /* finalize */
     nullptr,                /* call */
     nullptr,                /* hasInstance */
     nullptr,                /* construct */
     nullptr,                /* trace */
     {
         GenericCreateConstructor<TypedArrayConstructor, 3, JSFunction::FinalizeKind>,
@@ -1796,16 +1797,17 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Flo
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |                        \
     JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray),                           \
     nullptr,                 /* addProperty */                                 \
     nullptr,                 /* delProperty */                                 \
     nullptr,                 /* getProperty */                                 \
     nullptr,                 /* setProperty */                                 \
     nullptr,                 /* enumerate   */                                 \
     nullptr,                 /* resolve     */                                 \
+    nullptr,                 /* mayResolve  */                                 \
     nullptr,                 /* convert     */                                 \
     nullptr,                 /* finalize    */                                 \
     nullptr,                 /* call        */                                 \
     nullptr,                 /* hasInstance */                                 \
     nullptr,                 /* construct   */                                 \
     TypedArrayObject::trace, /* trace  */                                      \
     TYPED_ARRAY_CLASS_SPEC(_typedArray)                                        \
 }
@@ -1841,16 +1843,17 @@ const Class TypedArrayObject::classes[Sc
     #typedArray "Prototype", \
     JSCLASS_HAS_CACHED_PROTO(JSProto_##typedArray), \
     nullptr, /* addProperty */ \
     nullptr, /* delProperty */ \
     nullptr, /* getProperty */ \
     nullptr, /* setProperty */ \
     nullptr, /* enumerate */ \
     nullptr, /* resolve */ \
+    nullptr, /* mayResolve */ \
     nullptr, /* convert */ \
     nullptr, /* finalize */ \
     nullptr, /* call */ \
     nullptr, /* hasInstance */ \
     nullptr, /* construct */ \
     nullptr, /* trace  */ \
     { \
         typedArray::createConstructor, \
@@ -1896,16 +1899,17 @@ const Class DataViewObject::class_ = {
     JSCLASS_HAS_RESERVED_SLOTS(TypedArrayLayout::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_DataView),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
+    nullptr, /* mayResolve */
     nullptr, /* convert */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     ArrayBufferViewObject::trace
 };
 
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -888,16 +888,17 @@ const Class UnboxedPlainObject::class_ =
     js_Object_str,
     Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS,
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate   */
     nullptr,        /* resolve     */
+    nullptr,        /* mayResolve  */
     nullptr,        /* convert     */
     nullptr,        /* finalize    */
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     UnboxedPlainObject::trace,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -463,17 +463,19 @@ sandbox_addProperty(JSContext* cx, Handl
 }
 
 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
 
 static const js::Class SandboxClass = {
     "Sandbox",
     XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
     nullptr, nullptr, nullptr, nullptr,
-    sandbox_enumerate, sandbox_resolve, sandbox_convert,  sandbox_finalize,
+    sandbox_enumerate, sandbox_resolve,
+    nullptr,        /* mayResolve */
+    sandbox_convert,  sandbox_finalize,
     nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
     JS_NULL_CLASS_SPEC,
     {
       nullptr,      /* outerObject */
       nullptr,      /* innerObject */
       false,        /* isWrappedNative */
       nullptr,      /* weakmapKeyDelegateOp */
       sandbox_moved /* objectMovedOp */
@@ -482,17 +484,19 @@ static const js::Class SandboxClass = {
 };
 
 // Note to whomever comes here to remove addProperty hooks: billm has promised
 // to do the work for this class.
 static const js::Class SandboxWriteToProtoClass = {
     "Sandbox",
     XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
     sandbox_addProperty, nullptr, nullptr, nullptr,
-    sandbox_enumerate, sandbox_resolve, sandbox_convert,  sandbox_finalize,
+    sandbox_enumerate, sandbox_resolve,
+    nullptr,        /* mayResolve */
+    sandbox_convert,  sandbox_finalize,
     nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
     JS_NULL_CLASS_SPEC,
     {
       nullptr,      /* outerObject */
       nullptr,      /* innerObject */
       false,        /* isWrappedNative */
       nullptr,      /* weakmapKeyDelegateOp */
       sandbox_moved /* objectMovedOp */
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -1451,16 +1451,17 @@ static const JSClass XPCOutParamClass = 
     "XPCOutParam",
     0,
     nullptr,   /* addProperty */
     nullptr,   /* delProperty */
     nullptr,   /* getProperty */
     nullptr,   /* setProperty */
     nullptr,   /* enumerate */
     nullptr,   /* resolve */
+    nullptr,   /* mayResolve */
     nullptr,   /* convert */
     FinalizeStub,
     nullptr,   /* call */
     nullptr,   /* hasInstance */
     nullptr,   /* construct */
     nullptr    /* trace */
 };
 
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -643,16 +643,17 @@ const XPCWrappedNativeJSClass XPC_WN_NoH
     /* Mandatory non-null function pointer members. */
     XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty
     XPC_WN_CantDeletePropertyStub,     // delProperty
     nullptr,                           // getProperty
     nullptr,                           // setProperty
 
     XPC_WN_Shared_Enumerate,           // enumerate
     XPC_WN_NoHelper_Resolve,           // resolve
+    nullptr,                           // mayResolve
     XPC_WN_Shared_Convert,             // convert
     XPC_WN_NoHelper_Finalize,          // finalize
 
     /* Optionally non-null members start here. */
     nullptr,                         // call
     nullptr,                         // construct
     nullptr,                         // hasInstance
     XPCWrappedNative::Trace,         // trace
@@ -1283,16 +1284,17 @@ const js::Class XPC_WN_ModsAllowed_WithC
 
     /* Function pointer members. */
     nullptr,                        // addProperty;
     nullptr,                        // delProperty;
     nullptr,                        // getProperty;
     nullptr,                        // setProperty;
     XPC_WN_Shared_Proto_Enumerate,  // enumerate;
     XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
+    nullptr,                        // mayResolve;
     nullptr,                        // convert;
     XPC_WN_Shared_Proto_Finalize,   // finalize;
 
     /* Optionally non-null members start here. */
     nullptr,                        // call;
     nullptr,                        // construct;
     nullptr,                        // hasInstance;
     XPC_WN_Shared_Proto_Trace,      // trace;
@@ -1308,16 +1310,17 @@ const js::Class XPC_WN_ModsAllowed_NoCal
 
     /* Function pointer members. */
     nullptr,                        // addProperty;
     nullptr,                        // delProperty;
     nullptr,                        // getProperty;
     nullptr,                        // setProperty;
     XPC_WN_Shared_Proto_Enumerate,  // enumerate;
     XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
+    nullptr,                        // mayResolve;
     nullptr,                        // convert;
     XPC_WN_Shared_Proto_Finalize,   // finalize;
 
     /* Optionally non-null members start here. */
     nullptr,                         // call;
     nullptr,                         // construct;
     nullptr,                         // hasInstance;
     XPC_WN_Shared_Proto_Trace,      // trace;
@@ -1386,16 +1389,17 @@ const js::Class XPC_WN_NoMods_WithCall_P
 
     /* Mandatory non-null function pointer members. */
     XPC_WN_OnlyIWrite_Proto_AddPropertyStub,   // addProperty;
     XPC_WN_CantDeletePropertyStub,             // delProperty;
     nullptr,                                   // getProperty;
     nullptr,                                   // setProperty;
     XPC_WN_Shared_Proto_Enumerate,             // enumerate;
     XPC_WN_NoMods_Proto_Resolve,               // resolve;
+    nullptr,                                   // mayResolve;
     nullptr,                                   // convert;
     XPC_WN_Shared_Proto_Finalize,              // finalize;
 
     /* Optionally non-null members start here. */
     nullptr,                         // call;
     nullptr,                         // construct;
     nullptr,                         // hasInstance;
     XPC_WN_Shared_Proto_Trace,      // trace;
@@ -1411,16 +1415,17 @@ const js::Class XPC_WN_NoMods_NoCall_Pro
 
     /* Mandatory non-null function pointer members. */
     XPC_WN_OnlyIWrite_Proto_AddPropertyStub,   // addProperty;
     XPC_WN_CantDeletePropertyStub,             // delProperty;
     nullptr,                                   // getProperty;
     nullptr,                                   // setProperty;
     XPC_WN_Shared_Proto_Enumerate,             // enumerate;
     XPC_WN_NoMods_Proto_Resolve,               // resolve;
+    nullptr,                                   // mayResolve;
     nullptr,                                   // convert;
     XPC_WN_Shared_Proto_Finalize,              // finalize;
 
     /* Optionally non-null members start here. */
     nullptr,                         // call;
     nullptr,                         // construct;
     nullptr,                         // hasInstance;
     XPC_WN_Shared_Proto_Trace,      // trace;
@@ -1506,16 +1511,17 @@ const js::Class XPC_WN_Tearoff_JSClass =
     WRAPPER_FLAGS |
     JSCLASS_HAS_RESERVED_SLOTS(XPC_WN_TEAROFF_RESERVED_SLOTS), // flags;
     XPC_WN_OnlyIWrite_AddPropertyStub,         // addProperty;
     XPC_WN_CantDeletePropertyStub,             // delProperty;
     nullptr,                                   // getProperty;
     nullptr,                                   // setProperty;
     XPC_WN_TearOff_Enumerate,                  // enumerate;
     XPC_WN_TearOff_Resolve,                    // resolve;
+    nullptr,                                   // mayResolve;
     XPC_WN_Shared_Convert,                     // convert;
     XPC_WN_TearOff_Finalize,                   // finalize;
 
     /* Optionally non-null members start here. */
     nullptr,                                   // call
     nullptr,                                   // construct
     nullptr,                                   // hasInstance
     nullptr,                                   // trace
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -882,17 +882,17 @@ ExpandoObjectFinalize(JSFreeOp* fop, JSO
     // Release the principal.
     nsIPrincipal* principal = GetExpandoObjectPrincipal(obj);
     NS_RELEASE(principal);
 }
 
 const JSClass ExpandoObjectClass = {
     "XrayExpandoObject",
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_EXPANDO_COUNT),
-    nullptr, nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr, 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
@@ -664,17 +664,17 @@ private:
     return NS_OK;
   }
 };
 
 const JSClass JSRuntimeWrapper::sGlobalClass = {
   "PACResolutionThreadGlobal",
   JSCLASS_GLOBAL_FLAGS,
   nullptr, nullptr, nullptr, nullptr,
-  nullptr, nullptr, nullptr,
+  nullptr, nullptr, nullptr, nullptr,
   nullptr, nullptr, nullptr, nullptr,
   JS_GlobalObjectTraceHook
 };
 
 void
 ProxyAutoConfig::SetThreadLocalIndex(uint32_t index)
 {
   sRunningIndex = index;
--- a/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
+++ b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp
@@ -115,16 +115,17 @@ static const JSClass sWitnessClass = {
   "FinalizationWitness",
   JSCLASS_HAS_RESERVED_SLOTS(WITNESS_INSTANCES_SLOTS),
   nullptr /* addProperty */,
   nullptr /* delProperty */,
   nullptr /* getProperty */,
   nullptr /* setProperty */,
   nullptr /* enumerate */,
   nullptr /* resolve */,
+  nullptr /* mayResolve */,
   nullptr /* convert */,
   Finalize /* finalize */
 };
 
 bool IsWitness(JS::Handle<JS::Value> v)
 {
   return v.isObject() && JS_GetClass(&v.toObject()) == &sWitnessClass;
 }
--- a/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
+++ b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
@@ -83,17 +83,17 @@ RunTest(JSRuntime* rt, JSContext* cx, Ar
 
 static void
 CreateGlobalAndRunTest(JSRuntime* rt, JSContext* cx)
 {
   static const JSClass GlobalClass = {
     "global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
-    nullptr, nullptr, nullptr,
+    nullptr, nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
   };
 
   JS::CompartmentOptions options;
   options.setVersion(JSVERSION_LATEST);
   JS::PersistentRootedObject global(cx);
   global = JS_NewGlobalObject(cx, &GlobalClass, nullptr, JS::FireOnNewGlobalHook, options);
   ASSERT_TRUE(global != nullptr);