Bug 993026 - Simplify JS resolve hook. r=jorendorff
authorTom Schuster <evilpies@gmail.com>
Sat, 08 Nov 2014 01:07:12 +0100
changeset 214664 2c6e370c940caf077f1a0b41140cf49b17c42455
parent 214663 b22c5e82467b22bb162129fff9d549da760e34dc
child 214665 466732e6be01fac4b758ad54f542ee9d0960f1a0
push id51546
push userevilpies@gmail.com
push dateSat, 08 Nov 2014 00:07:48 +0000
treeherdermozilla-inbound@3c8f81efb9a7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs993026
milestone36.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 993026 - Simplify JS resolve hook. r=jorendorff
js/public/Class.h
js/src/jit/IonBuilder.cpp
js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
js/src/jsapi-tests/testLookup.cpp
js/src/jsapi-tests/testResolveRecursion.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsobj.cpp
js/src/jsstr.cpp
js/src/jsstr.h
js/src/shell/js.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/NativeObject-inl.h
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -114,34 +114,22 @@ typedef bool
 typedef bool
 (* JSEnumerateOp)(JSContext *cx, JS::HandleObject obj);
 
 // Resolve a lazy property named by id in obj by defining it directly in obj.
 // Lazy properties are those reflected from some peer native property space
 // (e.g., the DOM attributes for a given node reflected as obj) on demand.
 //
 // JS looks for a property in an object, and if not found, tries to resolve
-// the given id.  If resolve succeeds, the engine looks again in case resolve
-// defined obj[id].  If no such property exists directly in obj, the process
-// is repeated with obj's prototype, etc.
+// the given id. *resolvedp should be set to true iff the property was
+// was defined on |obj|.
 //
-// NB: JSNewResolveOp provides a cheaper way to resolve lazy properties.
 typedef bool
-(* JSResolveOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
-
-// Like JSResolveOp, except the *objp out parameter, on success, should be null
-// to indicate that id was not resolved; and non-null, referring to obj or one
-// of its prototypes, if id was resolved.  The hook may assume *objp is null on
-// entry.
-//
-// This hook instead of JSResolveOp is called via the JSClass.resolve member
-// if JSCLASS_NEW_RESOLVE is set in JSClass.flags.
-typedef bool
-(* JSNewResolveOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
-                   JS::MutableHandleObject objp);
+(* JSResolveOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
+                bool *resolvedp);
 
 // 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
@@ -395,17 +383,16 @@ typedef void (*JSClassInternal)();
 struct JSClass {
     JS_CLASS_MEMBERS(JSFinalizeOp);
 
     void                *reserved[32];
 };
 
 #define JSCLASS_HAS_PRIVATE             (1<<0)  // objects have private slot
 #define JSCLASS_NEW_ENUMERATE           (1<<1)  // has JSNewEnumerateOp hook
-#define JSCLASS_NEW_RESOLVE             (1<<2)  // has JSNewResolveOp hook
 #define JSCLASS_PRIVATE_IS_NSISUPPORTS  (1<<3)  // private is (nsISupports *)
 #define JSCLASS_IS_DOMJSCLASS           (1<<4)  // objects are DOM
 #define JSCLASS_IMPLEMENTS_BARRIERS     (1<<5)  // Correctly implements GC read
                                                 // and write barriers
 #define JSCLASS_EMULATES_UNDEFINED      (1<<6)  // objects of this class act
                                                 // like the value undefined,
                                                 // in some contexts
 #define JSCLASS_USERBIT1                (1<<7)  // Reserved for embeddings.
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6528,22 +6528,22 @@ ClassHasResolveHook(CompileCompartment *
     // properties are not reflected in type information, so pretend there is a
     // resolve hook for this property.
     if (clasp == &ArrayObject::class_)
         return name == comp->runtime()->names().length;
 
     if (clasp->resolve == JS_ResolveStub)
         return false;
 
-    if (clasp->resolve == (JSResolveOp)str_resolve) {
+    if (clasp->resolve == str_resolve) {
         // str_resolve only resolves integers, not names.
         return false;
     }
 
-    if (clasp->resolve == (JSResolveOp)fun_resolve)
+    if (clasp->resolve == fun_resolve)
         return FunctionHasResolveHook(comp->runtime()->names(), name);
 
     return true;
 }
 
 void
 IonBuilder::insertRecompileCheck()
 {
--- a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
+++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
@@ -9,20 +9,19 @@
 
 static bool
 GlobalEnumerate(JSContext *cx, JS::Handle<JSObject*> obj)
 {
     return JS_EnumerateStandardClasses(cx, obj);
 }
 
 static bool
-GlobalResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id)
+GlobalResolve(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *resolvedp)
 {
-    bool resolved = false;
-    return JS_ResolveStandardClass(cx, obj, id, &resolved);
+    return JS_ResolveStandardClass(cx, obj, id, resolvedp);
 }
 
 BEGIN_TEST(testRedefineGlobalEval)
 {
     static const JSClass cls = {
         "global", JSCLASS_GLOBAL_FLAGS,
         JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
         GlobalEnumerate, GlobalResolve, JS_ConvertStub,
--- a/js/src/jsapi-tests/testLookup.cpp
+++ b/js/src/jsapi-tests/testLookup.cpp
@@ -45,47 +45,51 @@ static const JSClass DocumentAllClass = 
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
 bool
-document_resolve(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
-                 JS::MutableHandleObject objp)
+document_resolve(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *resolvedp)
 {
     // If id is "all", resolve document.all=true.
     JS::RootedValue v(cx);
     if (!JS_IdToValue(cx, id, &v))
         return false;
+
     if (v.isString()) {
         JSString *str = v.toString();
         JSFlatString *flatStr = JS_FlattenString(cx, str);
         if (!flatStr)
             return false;
         if (JS_FlatStringEqualsAscii(flatStr, "all")) {
             JS::Rooted<JSObject*> docAll(cx,
                                          JS_NewObject(cx, &DocumentAllClass, JS::NullPtr(), JS::NullPtr()));
             if (!docAll)
                 return false;
+
             JS::Rooted<JS::Value> allValue(cx, JS::ObjectValue(*docAll));
-            bool ok = JS_DefinePropertyById(cx, obj, id, allValue, 0);
-            objp.set(ok ? obj.get() : nullptr);
-            return ok;
+            if (!JS_DefinePropertyById(cx, obj, id, allValue, 0))
+                return false;
+
+            *resolvedp = true;
+            return true;
         }
     }
-    objp.set(nullptr);
+
+    *resolvedp = false;
     return true;
 }
 
 static const JSClass document_class = {
-    "document", JSCLASS_NEW_RESOLVE,
+    "document", 0,
     JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-    JS_EnumerateStub, (JSResolveOp) document_resolve, JS_ConvertStub
+    JS_EnumerateStub, document_resolve, JS_ConvertStub
 };
 
 BEGIN_TEST(testLookup_bug570195)
 {
     JS::RootedObject obj(cx, JS_NewObject(cx, &document_class, JS::NullPtr(), JS::NullPtr()));
     CHECK(obj);
     CHECK(JS_DefineProperty(cx, global, "document", obj, 0));
     JS::RootedValue v(cx);
--- a/js/src/jsapi-tests/testResolveRecursion.cpp
+++ b/js/src/jsapi-tests/testResolveRecursion.cpp
@@ -11,24 +11,23 @@
  * Test that resolve hook recursion for the same object and property is
  * prevented.
  */
 
 BEGIN_TEST(testResolveRecursion)
 {
     static const JSClass my_resolve_class = {
         "MyResolve",
-        JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE,
-
+        JSCLASS_HAS_PRIVATE,
         JS_PropertyStub,       // add
         JS_DeletePropertyStub, // delete
         JS_PropertyStub,       // get
         JS_StrictPropertyStub, // set
         JS_EnumerateStub,
-        (JSResolveOp) my_resolve,
+        my_resolve,
         JS_ConvertStub
     };
 
     obj1 = obj2 = nullptr;
     JS::AddObjectRoot(cx, &obj1);
     JS::AddObjectRoot(cx, &obj2);
 
     obj1 = JS_NewObject(cx, &my_resolve_class, JS::NullPtr(), JS::NullPtr());
@@ -74,17 +73,17 @@ struct AutoIncrCounters {
     ~AutoIncrCounters() {
         t->resolveExitCount++;
     }
 
     cls_testResolveRecursion *t;
 };
 
 bool
-doResolve(JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp)
+doResolve(JS::HandleObject obj, JS::HandleId id, bool *resolvedp)
 {
     CHECK_EQUAL(resolveExitCount, 0);
     AutoIncrCounters incr(this);
     CHECK(obj == obj1 || obj == obj2);
 
     CHECK(JSID_IS_STRING(id));
 
     JSFlatString *str = JS_FlattenString(cx, JSID_TO_STRING(id));
@@ -92,55 +91,55 @@ doResolve(JS::HandleObject obj, JS::Hand
     JS::RootedValue v(cx);
     if (JS_FlatStringEqualsAscii(str, "x")) {
         if (obj == obj1) {
             /* First resolve hook invocation. */
             CHECK_EQUAL(resolveEntryCount, 1);
             EVAL("obj2.y = true", &v);
             CHECK_SAME(v, JSVAL_TRUE);
             CHECK(JS_DefinePropertyById(cx, obj, id, JS::FalseHandleValue, 0));
-            objp.set(obj);
+            *resolvedp = true;
             return true;
         }
         if (obj == obj2) {
             CHECK_EQUAL(resolveEntryCount, 4);
-            objp.set(nullptr);
+            *resolvedp = false;
             return true;
         }
     } else if (JS_FlatStringEqualsAscii(str, "y")) {
         if (obj == obj2) {
             CHECK_EQUAL(resolveEntryCount, 2);
             CHECK(JS_DefinePropertyById(cx, obj, id, JS::NullHandleValue, 0));
             EVAL("obj1.x", &v);
             CHECK(v.isUndefined());
             EVAL("obj1.y", &v);
             CHECK_SAME(v, JSVAL_ZERO);
-            objp.set(obj);
+            *resolvedp = true;
             return true;
         }
         if (obj == obj1) {
             CHECK_EQUAL(resolveEntryCount, 3);
             EVAL("obj1.x", &v);
             CHECK(v.isUndefined());
             EVAL("obj1.y", &v);
             CHECK(v.isUndefined());
             EVAL("obj2.y", &v);
             CHECK(v.isNull());
             EVAL("obj2.x", &v);
             CHECK(v.isUndefined());
             EVAL("obj1.y = 0", &v);
             CHECK_SAME(v, JSVAL_ZERO);
-            objp.set(obj);
+            *resolvedp = true;
             return true;
         }
     }
     CHECK(false);
     return false;
 }
 
 static bool
-my_resolve(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleObject objp)
+my_resolve(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *resolvedp)
 {
     return static_cast<cls_testResolveRecursion *>(JS_GetPrivate(obj))->
-           doResolve(obj, id, objp);
+           doResolve(obj, id, resolvedp);
 }
 
 END_TEST(testResolveRecursion)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2176,18 +2176,19 @@ JS_DeletePropertyStub(JSContext *cx, Han
 
 JS_PUBLIC_API(bool)
 JS_EnumerateStub(JSContext *cx, HandleObject obj)
 {
     return true;
 }
 
 JS_PUBLIC_API(bool)
-JS_ResolveStub(JSContext *cx, HandleObject obj, HandleId id)
-{
+JS_ResolveStub(JSContext *cx, HandleObject obj, HandleId id, bool *resolvedp)
+{
+    MOZ_ASSERT(*resolvedp == false);
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_ConvertStub(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
 {
     MOZ_ASSERT(type != JSTYPE_OBJECT && type != JSTYPE_FUNCTION);
     MOZ_ASSERT(obj);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2368,17 +2368,17 @@ JS_StrictPropertyStub(JSContext *cx, JS:
 extern JS_PUBLIC_API(bool)
 JS_DeletePropertyStub(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
                       bool *succeeded);
 
 extern JS_PUBLIC_API(bool)
 JS_EnumerateStub(JSContext *cx, JS::HandleObject obj);
 
 extern JS_PUBLIC_API(bool)
-JS_ResolveStub(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
+JS_ResolveStub(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *resolvedp);
 
 extern JS_PUBLIC_API(bool)
 JS_ConvertStub(JSContext *cx, JS::HandleObject obj, JSType type,
                JS::MutableHandleValue vp);
 
 template<typename T>
 struct JSConstScalarSpec {
     const char *name;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -444,17 +444,17 @@ ResolveInterpretedFunctionPrototype(JSCo
 
 bool
 js::FunctionHasResolveHook(const JSAtomState &atomState, PropertyName *name)
 {
     return name == atomState.prototype || name == atomState.length || name == atomState.name;
 }
 
 bool
-js::fun_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
+js::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)) {
         /*
@@ -471,17 +471,18 @@ js::fun_resolve(JSContext *cx, HandleObj
          *
          * ES6 19.2.4.3: arrow functions also don't have a prototype property.
          */
         if (fun->isBuiltin() || fun->isArrow() || fun->isFunctionPrototype())
             return true;
 
         if (!ResolveInterpretedFunctionPrototype(cx, fun))
             return false;
-        objp.set(fun);
+
+        *resolvedp = true;
         return true;
     }
 
     if (JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().name)) {
         MOZ_ASSERT(!IsInternalFunctionObject(obj));
 
         RootedValue v(cx);
         if (JSID_IS_ATOM(id, cx->names().length)) {
@@ -493,17 +494,18 @@ js::fun_resolve(JSContext *cx, HandleObj
         } else {
             v.setString(fun->atom() == nullptr ? cx->runtime()->emptyString : fun->atom());
         }
 
         if (!DefineNativeProperty(cx, fun, id, v, JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_PERMANENT | JSPROP_READONLY)) {
             return false;
         }
-        objp.set(fun);
+
+        *resolvedp = true;
         return true;
     }
 
     return true;
 }
 
 template<XDRMode mode>
 bool
@@ -881,24 +883,24 @@ CreateFunctionPrototype(JSContext *cx, J
 
     self->setThrowTypeError(throwTypeError);
 
     return functionProto;
 }
 
 const Class JSFunction::class_ = {
     js_Function_str,
-    JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
+    JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     fun_enumerate,
-    (JSResolveOp)js::fun_resolve,
+    js::fun_resolve,
     JS_ConvertStub,
     nullptr,                 /* finalize    */
     nullptr,                 /* call        */
     fun_hasInstance,
     nullptr,                 /* construct   */
     fun_trace,
     {
         CreateFunctionConstructor,
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -529,17 +529,17 @@ DefineFunction(JSContext *cx, HandleObje
                unsigned nargs, unsigned flags,
                gc::AllocKind allocKind = JSFunction::FinalizeKind,
                NewObjectKind newKind = GenericObject);
 
 bool
 FunctionHasResolveHook(const JSAtomState &atomState, PropertyName *name);
 
 extern bool
-fun_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp);
+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);
 
 /*
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4215,17 +4215,17 @@ JSObject::hasIdempotentProtoChain() cons
     // Return false if obj (or an object on its proto chain) is non-native or
     // has a resolve or lookup hook.
     JSObject *obj = const_cast<JSObject *>(this);
     while (true) {
         if (!obj->isNative())
             return false;
 
         JSResolveOp resolve = obj->getClass()->resolve;
-        if (resolve != JS_ResolveStub && resolve != (JSResolveOp) js::fun_resolve)
+        if (resolve != JS_ResolveStub && resolve != js::fun_resolve)
             return false;
 
         if (obj->getOps()->lookupProperty || obj->getOps()->lookupGeneric || obj->getOps()->lookupElement)
             return false;
 
         obj = obj->getProto();
         if (!obj)
             return true;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -394,17 +394,17 @@ str_enumerate(JSContext *cx, HandleObjec
             return false;
         }
     }
 
     return true;
 }
 
 bool
-js::str_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
+js::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()) {
@@ -412,31 +412,31 @@ js::str_resolve(JSContext *cx, HandleObj
         if (!str1)
             return false;
         RootedValue value(cx, StringValue(str1));
         if (!JSObject::defineElement(cx, obj, uint32_t(slot), value, nullptr, nullptr,
                                      STRING_ELEMENT_ATTRS))
         {
             return false;
         }
-        objp.set(obj);
+        *resolvedp = true;
     }
     return true;
 }
 
 const Class StringObject::class_ = {
     js_String_str,
     JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
-    JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_String),
+    JSCLASS_HAS_CACHED_PROTO(JSProto_String),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     str_enumerate,
-    (JSResolveOp)str_resolve,
+    str_resolve,
     JS_ConvertStub
 };
 
 /*
  * 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
@@ -403,17 +403,17 @@ str_search(JSContext *cx, unsigned argc,
 
 bool
 str_split(JSContext *cx, unsigned argc, Value *vp);
 
 JSObject *
 str_split_string(JSContext *cx, HandleTypeObject type, HandleString str, HandleString sep);
 
 bool
-str_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp);
+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);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2508,41 +2508,33 @@ sandbox_enumerate(JSContext *cx, HandleO
 
     if (!ToBoolean(v))
         return true;
 
     return JS_EnumerateStandardClasses(cx, obj);
 }
 
 static bool
-sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
+sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id, bool *resolvedp)
 {
     RootedValue v(cx);
     if (!JS_GetProperty(cx, obj, "lazy", &v))
         return false;
 
-    if (ToBoolean(v)) {
-        bool resolved;
-        if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
-            return false;
-        if (resolved) {
-            objp.set(obj);
-            return true;
-        }
-    }
-    objp.set(nullptr);
+    if (ToBoolean(v))
+        return JS_ResolveStandardClass(cx, obj, id, resolvedp);
     return true;
 }
 
 static const JSClass sandbox_class = {
     "sandbox",
-    JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
+    JSCLASS_GLOBAL_FLAGS,
     JS_PropertyStub,   JS_DeletePropertyStub,
     JS_PropertyStub,   JS_StrictPropertyStub,
-    sandbox_enumerate, (JSResolveOp)sandbox_resolve,
+    sandbox_enumerate, sandbox_resolve,
     JS_ConvertStub, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static JSObject *
 NewSandbox(JSContext *cx, bool lazy)
 {
@@ -4819,37 +4811,30 @@ global_enumerate(JSContext *cx, HandleOb
 #ifdef LAZY_STANDARD_CLASSES
     return JS_EnumerateStandardClasses(cx, obj);
 #else
     return true;
 #endif
 }
 
 static bool
-global_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
+global_resolve(JSContext *cx, HandleObject obj, HandleId id, bool *resolvedp)
 {
 #ifdef LAZY_STANDARD_CLASSES
-    bool resolved;
-
-    if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
-        return false;
-    if (resolved) {
-        objp.set(obj);
-        return true;
-    }
+    if (!JS_ResolveStandardClass(cx, obj, id, resolvedp))
+        return false;
 #endif
-
     return true;
 }
 
 static const JSClass global_class = {
-    "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
+    "global", JSCLASS_GLOBAL_FLAGS,
     JS_PropertyStub,  JS_DeletePropertyStub,
     JS_PropertyStub,  JS_StrictPropertyStub,
-    global_enumerate, (JSResolveOp) global_resolve,
+    global_enumerate, global_resolve,
     JS_ConvertStub,   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
@@ -354,20 +354,18 @@ ArgSetter(JSContext *cx, HandleObject ob
      * that has a setter for this id.
      */
     bool succeeded;
     return baseops::DeleteGeneric(cx, argsobj, id, &succeeded) &&
            baseops::DefineGeneric(cx, argsobj, id, vp, nullptr, nullptr, attrs);
 }
 
 static bool
-args_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
+args_resolve(JSContext *cx, HandleObject obj, HandleId id, bool *resolvedp)
 {
-    objp.set(nullptr);
-
     Rooted<NormalArgumentsObject*> argsobj(cx, &obj->as<NormalArgumentsObject>());
 
     unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
     if (JSID_IS_INT(id)) {
         uint32_t arg = uint32_t(JSID_TO_INT(id));
         if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg))
             return true;
 
@@ -381,17 +379,17 @@ args_resolve(JSContext *cx, HandleObject
 
         if (argsobj->callee().isMagic(JS_OVERWRITTEN_CALLEE))
             return true;
     }
 
     if (!baseops::DefineGeneric(cx, argsobj, id, UndefinedHandleValue, ArgGetter, ArgSetter, attrs))
         return false;
 
-    objp.set(argsobj);
+    *resolvedp = true;
     return true;
 }
 
 static bool
 args_enumerate(JSContext *cx, HandleObject obj)
 {
     Rooted<NormalArgumentsObject*> argsobj(cx, &obj->as<NormalArgumentsObject>());
     RootedId id(cx);
@@ -470,20 +468,18 @@ StrictArgSetter(JSContext *cx, HandleObj
      * collect its value.
      */
     bool succeeded;
     return baseops::DeleteGeneric(cx, argsobj, id, &succeeded) &&
            baseops::DefineGeneric(cx, argsobj, id, vp, nullptr, nullptr, attrs);
 }
 
 static bool
-strictargs_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
+strictargs_resolve(JSContext *cx, HandleObject obj, HandleId id, bool *resolvedp)
 {
-    objp.set(nullptr);
-
     Rooted<StrictArgumentsObject*> argsobj(cx, &obj->as<StrictArgumentsObject>());
 
     unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
     PropertyOp getter = StrictArgGetter;
     StrictPropertyOp setter = StrictArgSetter;
 
     if (JSID_IS_INT(id)) {
         uint32_t arg = uint32_t(JSID_TO_INT(id));
@@ -501,17 +497,17 @@ strictargs_resolve(JSContext *cx, Handle
         attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
         getter = CastAsPropertyOp(argsobj->global().getThrowTypeError());
         setter = CastAsStrictPropertyOp(argsobj->global().getThrowTypeError());
     }
 
     if (!baseops::DefineGeneric(cx, argsobj, id, UndefinedHandleValue, getter, setter, attrs))
         return false;
 
-    objp.set(argsobj);
+    *resolvedp = true;
     return true;
 }
 
 static bool
 strictargs_enumerate(JSContext *cx, HandleObject obj)
 {
     Rooted<StrictArgumentsObject*> argsobj(cx, &obj->as<StrictArgumentsObject>());
 
@@ -566,48 +562,48 @@ ArgumentsObject::trace(JSTracer *trc, JS
 /*
  * The classes below collaborate to lazily reflect and synchronize actual
  * argument values, argument count, and callee function object stored in a
  * stack frame with their corresponding property values in the frame's
  * arguments object.
  */
 const Class NormalArgumentsObject::class_ = {
     "Arguments",
-    JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
+    JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE,
     JS_PropertyStub,         /* addProperty */
     args_delProperty,
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     args_enumerate,
-    reinterpret_cast<JSResolveOp>(args_resolve),
+    args_resolve,
     JS_ConvertStub,
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
 
 /*
  * Strict mode arguments is significantly less magical than non-strict mode
  * arguments, so it is represented by a different class while sharing some
  * functionality.
  */
 const Class StrictArgumentsObject::class_ = {
     "Arguments",
-    JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
+    JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE,
     JS_PropertyStub,         /* addProperty */
     args_delProperty,
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     strictargs_enumerate,
-    reinterpret_cast<JSResolveOp>(strictargs_resolve),
+    strictargs_resolve,
     JS_ConvertStub,
     ArgumentsObject::finalize,
     nullptr,                 /* call        */
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     ArgumentsObject::trace
 };
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -479,75 +479,47 @@ NewNativeObjectWithType(JSContext *cx, H
  *
  *   - Otherwise no property was resolved. Set *propp = nullptr and
  *     *recursedp = false and return true.
  */
 static MOZ_ALWAYS_INLINE bool
 CallResolveOp(JSContext *cx, HandleNativeObject obj, HandleId id, MutableHandleObject objp,
               MutableHandleShape propp, bool *recursedp)
 {
-    const Class *clasp = obj->getClass();
-    JSResolveOp resolve = clasp->resolve;
-
     /*
      * Avoid recursion on (obj, id) already being resolved on cx.
      *
      * Once we have successfully added an entry for (obj, key) to
      * cx->resolvingTable, control must go through cleanup: before
      * returning.  But note that JS_DHASH_ADD may find an existing
      * entry, in which case we bail to suppress runaway recursion.
      */
     AutoResolving resolving(cx, obj, id);
     if (resolving.alreadyStarted()) {
         /* Already resolving id in obj -- suppress recursion. */
         *recursedp = true;
         return true;
     }
     *recursedp = false;
 
-    propp.set(nullptr);
-
-    if (clasp->flags & JSCLASS_NEW_RESOLVE) {
-        JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
-        RootedObject obj2(cx, nullptr);
-        if (!newresolve(cx, obj, id, &obj2))
-            return false;
-
-        /*
-         * We trust the new style resolve hook to set obj2 to nullptr when
-         * the id cannot be resolved. But, when obj2 is not null, we do
-         * not assume that id must exist and do full nativeLookup for
-         * compatibility.
-         */
-        if (!obj2)
-            return true;
+    bool resolved = false;
+    if (!obj->getClass()->resolve(cx, obj, id, &resolved))
+        return false;
 
-        if (!obj2->isNative()) {
-            /* Whoops, newresolve handed back a foreign obj2. */
-            MOZ_ASSERT(obj2 != obj);
-            return JSObject::lookupGeneric(cx, obj2, id, objp, propp);
-        }
+    if (!resolved)
+        return true;
 
-        objp.set(obj2);
-    } else {
-        if (!resolve(cx, obj, id))
-            return false;
+    objp.set(obj);
 
-        objp.set(obj);
-    }
-
-    NativeObject *nobjp = &objp->as<NativeObject>();
-
-    if (JSID_IS_INT(id) && nobjp->containsDenseElement(JSID_TO_INT(id))) {
+    if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
         MarkDenseOrTypedArrayElementFound<CanGC>(propp);
         return true;
     }
 
-    Shape *shape;
-    if (!nobjp->empty() && (shape = nobjp->lookup(cx, id)))
+    if (Shape *shape = obj->lookup(cx, id))
         propp.set(shape);
     else
         objp.set(nullptr);
 
     return true;
 }
 
 template <AllowGC allowGC>