Bug 1088002 part 2. Change JS_DefineElement, JS_DefineProperty, JS_DefineUCProperty, JS_DefinePropertyById, and JS_DefineProperties to default to using JSNative accessors, not JSPropertyOp accessors. r=waldo
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 29 Oct 2014 15:06:31 -0400
changeset 212919 66860992cd5ea5e12dc41ec39101de2cca4b7737
parent 212918 d0563150294baa25bb783f8bfd628c5985ee5416
child 212920 bec6973477c6f4f74591473b2c7de8fbf7f74327
push id51097
push userbzbarsky@mozilla.com
push dateWed, 29 Oct 2014 19:10:08 +0000
treeherdermozilla-inbound@bec6973477c6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs1088002
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 1088002 part 2. Change JS_DefineElement, JS_DefineProperty, JS_DefineUCProperty, JS_DefinePropertyById, and JS_DefineProperties to default to using JSNative accessors, not JSPropertyOp accessors. r=waldo
dom/base/Console.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsGlobalWindow.cpp
dom/bindings/BindingUtils.cpp
dom/bindings/Codegen.py
dom/xbl/nsXBLBinding.cpp
dom/xbl/nsXBLProtoImpl.cpp
dom/xbl/nsXBLProtoImplField.cpp
dom/xbl/nsXBLProtoImplProperty.cpp
ipc/testshell/XPCShellEnvironment.cpp
js/ipc/WrapperAnswer.cpp
js/src/builtin/TestingFunctions.cpp
js/src/ctypes/CTypes.cpp
js/src/jsapi-tests/testAddPropertyPropcache.cpp
js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp
js/src/jsapi-tests/testDefinePropertyIgnoredAttributes.cpp
js/src/jsapi-tests/testSetProperty.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsfriendapi.cpp
js/src/jsmath.cpp
js/src/jsobj.cpp
js/src/json.cpp
js/src/jsreflect.cpp
js/src/perf/jsperf.cpp
js/src/proxy/DirectProxyHandler.cpp
js/src/proxy/Proxy.cpp
js/src/shell/js.cpp
js/src/vm/NativeObject.cpp
js/src/vm/ScopeObject.cpp
js/src/vm/Shape-inl.h
js/src/vm/Shape.h
js/xpconnect/src/ExportHelpers.cpp
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/XPCShellImpl.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -1190,17 +1190,17 @@ Console::ProcessCallData(ConsoleCallData
       js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal);
       js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK,
                                     JS::PrivateValue(aData->mStack.get()));
 
       if (!JS_DefineProperty(cx, eventObj, "stacktrace",
                              JS::UndefinedHandleValue,
                              JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER |
                              JSPROP_SETTER,
-                             JS_DATA_TO_FUNC_PTR(JSPropertyOp, funObj.get()),
+                             JS_DATA_TO_FUNC_PTR(JSNative, funObj.get()),
                              nullptr)) {
         return;
       }
     }
   }
 
   if (!mStorage) {
     mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1");
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1029,18 +1029,19 @@ nsDOMClassInfo::ResolveConstructor(JSCon
 
   if (!val.isPrimitive()) {
     // If val is not an (non-null) object there either is no
     // constructor for this class, or someone messed with
     // window.classname, just fall through and let the JS engine
     // return the Object constructor.
 
     JS::Rooted<jsid> id(cx, sConstructor_id);
-    if (!::JS_DefinePropertyById(cx, obj, id, val, JSPROP_ENUMERATE,
-                                 JS_PropertyStub, JS_StrictPropertyStub)) {
+    if (!::JS_DefinePropertyById(cx, obj, id, val,
+                                 JSPROP_ENUMERATE | JSPROP_PROPOP_ACCESSORS,
+                                 JS_STUBGETTER, JS_STUBSETTER)) {
       return NS_ERROR_UNEXPECTED;
     }
 
     *objp = obj;
   }
 
   return NS_OK;
 }
@@ -1244,18 +1245,22 @@ nsDOMClassInfo::PostCreatePrototype(JSCo
   JS::Rooted<JSPropertyDescriptor> desc(cx);
   nsresult rv = ResolvePrototype(sXPConnect, win, cx, global, mData->mNameUTF16,
                                  mData, nullptr, nameSpaceManager, proto,
                                  &desc);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined() &&
       !JS_DefineUCProperty(cx, global, mData->mNameUTF16,
                            NS_strlen(mData->mNameUTF16),
-                           desc.value(), desc.attributes(),
-                           desc.getter(), desc.setter())) {
+                           desc.value(),
+                           // Descriptors never store JSNatives for accessors:
+                           // they have either JSFunctions or JSPropertyOps.
+                           desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                           JS_PROPERTYOP_GETTER(desc.getter()),
+                           JS_PROPERTYOP_SETTER(desc.setter()))) {
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
 }
 
 // static
 nsIClassInfo *
@@ -1447,18 +1452,18 @@ DefineInterfaceConstants(JSContext *cx, 
   JS::Rooted<JS::Value> v(cx);
   for (i = parent_constant_count; i < constant_count; i++) {
     nsXPIDLCString name;
     rv = if_info->GetConstant(i, &v, getter_Copies(name));
     NS_ENSURE_TRUE(NS_SUCCEEDED(rv), rv);
 
     if (!::JS_DefineProperty(cx, obj, name, v,
                              JSPROP_ENUMERATE | JSPROP_READONLY |
-                             JSPROP_PERMANENT,
-                             JS_PropertyStub, JS_StrictPropertyStub)) {
+                             JSPROP_PERMANENT | JSPROP_PROPOP_ACCESSORS,
+                             JS_STUBGETTER, JS_STUBSETTER)) {
       return NS_ERROR_UNEXPECTED;
     }
   }
 
   return NS_OK;
 }
 
 class nsDOMConstructor MOZ_FINAL : public nsIDOMDOMConstructor
@@ -2045,18 +2050,19 @@ ResolvePrototype(nsIXPConnect *aXPConnec
 
   v = OBJECT_TO_JSVAL(dot_prototype);
 
   JSAutoCompartment ac(cx, class_obj);
 
   // Per ECMA, the prototype property is {DontEnum, DontDelete, ReadOnly}
   if (!JS_WrapValue(cx, &v) ||
       !JS_DefineProperty(cx, class_obj, "prototype", v,
-                         JSPROP_PERMANENT | JSPROP_READONLY,
-                         JS_PropertyStub, JS_StrictPropertyStub)) {
+                         JSPROP_PERMANENT | JSPROP_READONLY |
+                         JSPROP_PROPOP_ACCESSORS,
+                         JS_STUBGETTER, JS_STUBSETTER)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
 }
 
 static bool
 OldBindingConstructorEnabled(const nsGlobalNameStruct *aStruct,
@@ -2504,18 +2510,19 @@ LookupComponentsShim(JSContext *cx, JS::
   JS::Rooted<JSObject*> components(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), global));
   NS_ENSURE_TRUE(components, NS_ERROR_OUT_OF_MEMORY);
 
   // Create a fake interfaces object.
   JS::Rooted<JSObject*> interfaces(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), global));
   NS_ENSURE_TRUE(interfaces, NS_ERROR_OUT_OF_MEMORY);
   bool ok =
     JS_DefineProperty(cx, components, "interfaces", interfaces,
-                      JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY,
-                      JS_PropertyStub, JS_StrictPropertyStub);
+                      JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY |
+                      JSPROP_PROPOP_ACCESSORS,
+                      JS_STUBGETTER, JS_STUBSETTER);
   NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
 
   // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
   // interfaces with constants.
   for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) {
 
     // Grab the names from the table.
     const char *geckoName = kInterfaceShimMap[i].geckoName;
@@ -2527,18 +2534,19 @@ LookupComponentsShim(JSContext *cx, JS::
     NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
     if (!v.isObject()) {
       NS_WARNING("Unable to find interface object on global");
       continue;
     }
 
     // Define the shim on the interfaces object.
     ok = JS_DefineProperty(cx, interfaces, geckoName, v,
-                           JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY,
-                           JS_PropertyStub, JS_StrictPropertyStub);
+                           JSPROP_ENUMERATE | JSPROP_PERMANENT |
+                           JSPROP_READONLY | JSPROP_PROPOP_ACCESSORS,
+                           JS_STUBGETTER, JS_STUBSETTER);
     NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   }
 
   FillPropertyDescriptor(desc, global, JS::ObjectValue(*components), false);
 
   return NS_OK;
 }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2585,18 +2585,19 @@ nsGlobalWindow::SetNewDocument(nsIDocume
 
     // Set scriptability based on the state of the docshell.
     bool allow = GetDocShell()->GetCanExecuteScripts();
     xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow);
 
     if (!aState) {
       JS::Rooted<JSObject*> rootedWrapper(cx, GetWrapperPreserveColor());
       if (!JS_DefineProperty(cx, newInnerGlobal, "window", rootedWrapper,
-                             JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,
-                             JS_PropertyStub, JS_StrictPropertyStub)) {
+                             JSPROP_ENUMERATE | JSPROP_READONLY |
+                             JSPROP_PERMANENT | JSPROP_PROPOP_ACCESSORS,
+                             JS_STUBGETTER, JS_STUBSETTER)) {
         NS_ERROR("can't create the 'window' property");
         return NS_ERROR_FAILURE;
       }
     }
   }
 
   JSAutoCompartment ac(cx, GetWrapperPreserveColor());
 
@@ -4521,18 +4522,19 @@ nsGlobalWindow::SetOpener(JSContext* aCx
   if (!aOpener.isNull() && !nsContentUtils::IsCallerChrome()) {
     JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
     if (!thisObj) {
       aError.Throw(NS_ERROR_UNEXPECTED);
       return;
     }
 
     if (!JS_WrapObject(aCx, &thisObj) ||
-        !JS_DefineProperty(aCx, thisObj, "opener", aOpener, JSPROP_ENUMERATE,
-                           JS_PropertyStub, JS_StrictPropertyStub)) {
+        !JS_DefineProperty(aCx, thisObj, "opener", aOpener,
+                           JSPROP_ENUMERATE | JSPROP_PROPOP_ACCESSORS,
+                           JS_STUBGETTER, JS_STUBSETTER)) {
       aError.Throw(NS_ERROR_FAILURE);
     }
 
     return;
   }
 
   if (!aOpener.isObjectOrNull()) {
     // Chrome code trying to set some random value as opener
@@ -13999,18 +14001,18 @@ nsGlobalWindow::SetConsole(JSContext* aC
 {
   JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
   if (!thisObj) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!JS_WrapObject(aCx, &thisObj) ||
       !JS_DefineProperty(aCx, thisObj, "console", aValue,
-                         JSPROP_ENUMERATE, JS_PropertyStub,
-                         JS_StrictPropertyStub)) {
+                         JSPROP_ENUMERATE | JSPROP_PROPOP_ACCESSORS,
+                         JS_STUBGETTER, JS_STUBSETTER)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 Console*
 nsGlobalWindow::GetConsole(ErrorResult& aRv)
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -523,18 +523,19 @@ CreateInterfaceObject(JSContext* cx, JS:
     int namedConstructorSlot = DOM_INTERFACE_SLOTS_BASE;
     while (namedConstructors->mName) {
       JS::Rooted<JSObject*> namedConstructor(cx,
         CreateConstructor(cx, global, namedConstructors->mName,
                           &namedConstructors->mHolder,
                           namedConstructors->mNargs));
       if (!namedConstructor ||
           !JS_DefineProperty(cx, namedConstructor, "prototype",
-                             proto, JSPROP_PERMANENT | JSPROP_READONLY,
-                             JS_PropertyStub, JS_StrictPropertyStub) ||
+                             proto,
+                             JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_PROPOP_ACCESSORS,
+                             JS_STUBGETTER, JS_STUBSETTER) ||
           (defineOnGlobal &&
            !DefineConstructor(cx, global, namedConstructors->mName,
                               namedConstructor))) {
         return nullptr;
       }
       js::SetReservedSlot(constructor, namedConstructorSlot++,
                           JS::ObjectValue(*namedConstructor));
       ++namedConstructors;
@@ -963,34 +964,34 @@ XrayResolveAttribute(JSContext* cx, JS::
       for ( ; attributeIds[i] != JSID_VOID; ++i) {
         if (id == attributeIds[i]) {
           cacheOnHolder = true;
 
           const JSPropertySpec& attrSpec = attributeSpecs[i];
           // Because of centralization, we need to make sure we fault in the
           // JitInfos as well. At present, until the JSAPI changes, the easiest
           // way to do this is wrap them up as functions ourselves.
-          desc.setAttributes(attrSpec.flags & ~JSPROP_NATIVE_ACCESSORS);
+          desc.setAttributes(attrSpec.flags);
           // They all have getters, so we can just make it.
           JS::Rooted<JSFunction*> fun(cx,
-                                      JS_NewFunctionById(cx, (JSNative)attrSpec.getter.propertyOp.op,
+                                      JS_NewFunctionById(cx, attrSpec.getter.native.op,
                                                          0, 0, wrapper, id));
           if (!fun)
             return false;
-          SET_JITINFO(fun, attrSpec.getter.propertyOp.info);
+          SET_JITINFO(fun, attrSpec.getter.native.info);
           JSObject *funobj = JS_GetFunctionObject(fun);
           desc.setGetterObject(funobj);
           desc.attributesRef() |= JSPROP_GETTER;
-          if (attrSpec.setter.propertyOp.op) {
+          if (attrSpec.setter.native.op) {
             // We have a setter! Make it.
-            fun = JS_NewFunctionById(cx, (JSNative)attrSpec.setter.propertyOp.op, 1, 0,
+            fun = JS_NewFunctionById(cx, attrSpec.setter.native.op, 1, 0,
                                      wrapper, id);
             if (!fun)
               return false;
-            SET_JITINFO(fun, attrSpec.setter.propertyOp.info);
+            SET_JITINFO(fun, attrSpec.setter.native.info);
             funobj = JS_GetFunctionObject(fun);
             desc.setSetterObject(funobj);
             desc.attributesRef() |= JSPROP_SETTER;
           } else {
             desc.setSetter(nullptr);
           }
           desc.object().set(wrapper);
           return true;
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2407,17 +2407,17 @@ class AttrDefiner(PropertyDefiner):
                 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
 
     def generateArray(self, array, name, doIdArrays):
         if len(array) == 0:
             return ""
 
         def flags(attr):
             unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
-            return ("JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS" +
+            return ("JSPROP_SHARED | JSPROP_ENUMERATE" +
                     unforgeable)
 
         def getter(attr):
             if self.static:
                 accessor = 'get_' + IDLToCIdentifier(attr.identifier.name)
                 jitinfo = "nullptr"
             else:
                 if attr.hasLenientThis():
@@ -2425,38 +2425,38 @@ class AttrDefiner(PropertyDefiner):
                 elif attr.getExtendedAttribute("CrossOriginReadable"):
                     accessor = "genericCrossOriginGetter"
                 elif self.descriptor.needsSpecialGenericOps():
                     accessor = "genericGetter"
                 else:
                     accessor = "GenericBindingGetter"
                 jitinfo = ("&%s_getterinfo" %
                            IDLToCIdentifier(attr.identifier.name))
-            return "{ { JS_CAST_NATIVE_TO(%s, JSPropertyOp), %s } }" % \
+            return "{ { %s, %s } }" % \
                    (accessor, jitinfo)
 
         def setter(attr):
             if (attr.readonly and
                 attr.getExtendedAttribute("PutForwards") is None and
                 attr.getExtendedAttribute("Replaceable") is None):
-                return "JSOP_NULLWRAPPER"
+                return "JSNATIVE_WRAPPER(nullptr)"
             if self.static:
                 accessor = 'set_' + IDLToCIdentifier(attr.identifier.name)
                 jitinfo = "nullptr"
             else:
                 if attr.hasLenientThis():
                     accessor = "genericLenientSetter"
                 elif IsCrossOriginWritable(attr, self.descriptor):
                     accessor = "genericCrossOriginSetter"
                 elif self.descriptor.needsSpecialGenericOps():
                     accessor = "genericSetter"
                 else:
                     accessor = "GenericBindingSetter"
                 jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name)
-            return "{ { JS_CAST_NATIVE_TO(%s, JSStrictPropertyOp), %s } }" % \
+            return "{ { %s, %s } }" % \
                    (accessor, jitinfo)
 
         def specData(attr):
             return (attr.identifier.name, flags(attr), getter(attr),
                     setter(attr))
 
         return self.generatePrefableArray(
             array, name,
@@ -7626,18 +7626,19 @@ class CGNewResolveHook(CGAbstractBinding
             if (!desc.object()) {
               return true;
             }
             // If desc.value() is undefined, then the DoNewResolve call
             // has already defined it on the object.  Don't try to also
             // define it.
             if (!desc.value().isUndefined() &&
                 !JS_DefinePropertyById(cx, obj, id, desc.value(),
-                                       desc.attributes(),
-                                       desc.getter(), desc.setter())) {
+                                       desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                                       JS_PROPERTYOP_GETTER(desc.getter()),
+                                       JS_PROPERTYOP_SETTER(desc.setter()))) {
               return false;
             }
             objp.set(obj);
             return true;
             """))
 
     def definition_body(self):
         if self.descriptor.isGlobal():
@@ -9598,18 +9599,19 @@ class CGResolveOwnPropertyViaNewresolve(
                 return false;
               }
               // If desc.value() is undefined, then the DoNewResolve call
               // has already defined the property on the object.  Don't
               // try to also define it.
               if (objDesc.object() &&
                   !objDesc.value().isUndefined() &&
                   !JS_DefinePropertyById(cx, obj, id, objDesc.value(),
-                                         objDesc.attributes(),
-                                         objDesc.getter(), objDesc.setter())) {
+                                         objDesc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                                         JS_PROPERTYOP_GETTER(objDesc.getter()),
+                                         JS_PROPERTYOP_SETTER(objDesc.setter()))) {
                 return false;
               }
             }
             return self->DoNewResolve(cx, wrapper, id, desc);
             """))
 
 
 class CGEnumerateOwnProperties(CGAbstractStaticMethod):
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -882,18 +882,18 @@ GetOrCreateClassObjectMap(JSContext *cx,
   if (desc.object() && desc.value().isObject() &&
       JS::IsWeakMapObject(&desc.value().toObject())) {
     return &desc.value().toObject();
   }
 
   // It's not there. Create and define it.
   JS::Rooted<JSObject*> map(cx, JS::NewWeakMapObject(cx));
   if (!map || !JS_DefineProperty(cx, scope, mapName, map,
-                                 JSPROP_PERMANENT | JSPROP_READONLY,
-                                 JS_PropertyStub, JS_StrictPropertyStub))
+                                 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_PROPOP_ACCESSORS,
+                                 JS_STUBGETTER, JS_STUBSETTER))
   {
     return nullptr;
   }
   return map;
 }
 
 static JSObject*
 GetOrCreateMapEntryForPrototype(JSContext *cx, JS::Handle<JSObject*> proto)
@@ -1031,18 +1031,18 @@ nsXBLBinding::DoInitJSClass(JSContext *c
     NS_ADDREF(docInfo);
     JS_SetReservedSlot(proto, 0, PRIVATE_TO_JSVAL(aProtoBinding));
 
     // Next, enter the compartment of the property holder, wrap the proto, and
     // stick it on.
     JSAutoCompartment ac3(cx, holder);
     if (!JS_WrapObject(cx, &proto) ||
         !JS_DefineProperty(cx, holder, aClassName.get(), proto,
-                           JSPROP_READONLY | JSPROP_PERMANENT,
-                           JS_PropertyStub, JS_StrictPropertyStub))
+                           JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_PROPOP_ACCESSORS,
+                           JS_STUBGETTER, JS_STUBSETTER))
     {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   // Whew. We have the proto. Wrap it back into the compartment of |obj|,
   // splice it in, and return it.
   JSAutoCompartment ac4(cx, obj);
--- a/dom/xbl/nsXBLProtoImpl.cpp
+++ b/dom/xbl/nsXBLProtoImpl.cpp
@@ -113,18 +113,18 @@ nsXBLProtoImpl::InstallImplementation(ns
 
     // This is just a property holder, so it doesn't need any special JSClass.
     propertyHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), scopeObject);
     NS_ENSURE_TRUE(propertyHolder, NS_ERROR_OUT_OF_MEMORY);
 
     // Define it as a property on the scopeObject, using the same name used on
     // the content side.
     bool ok = JS_DefineProperty(cx, scopeObject, className, propertyHolder,
-                                JSPROP_PERMANENT | JSPROP_READONLY,
-                                JS_PropertyStub, JS_StrictPropertyStub);
+                                JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_PROPOP_ACCESSORS,
+                                JS_STUBGETTER, JS_STUBSETTER);
     NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
   } else {
     propertyHolder = targetClassObject;
   }
 
   // Walk our member list and install each one in turn on the XBL scope object.
   if (propertyHolderIsNew) {
     for (nsXBLProtoImplMember* curr = mMembers;
--- a/dom/xbl/nsXBLProtoImplField.cpp
+++ b/dom/xbl/nsXBLProtoImplField.cpp
@@ -362,18 +362,18 @@ nsXBLProtoImplField::InstallAccessors(JS
   // them there.
   JSAutoCompartment ac2(aCx, aTargetClassObject);
   if (!JS_WrapObject(aCx, &get) || !JS_WrapObject(aCx, &set)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (!::JS_DefinePropertyById(aCx, aTargetClassObject, id, JS::UndefinedHandleValue,
                                AccessorAttributes(),
-                               JS_DATA_TO_FUNC_PTR(JSPropertyOp, get.get()),
-                               JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, set.get()))) {
+                               JS_DATA_TO_FUNC_PTR(JSNative, get.get()),
+                               JS_DATA_TO_FUNC_PTR(JSNative, set.get()))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
--- a/dom/xbl/nsXBLProtoImplProperty.cpp
+++ b/dom/xbl/nsXBLProtoImplProperty.cpp
@@ -145,18 +145,18 @@ nsXBLProtoImplProperty::InstallMember(JS
       if (!(setter = ::JS_CloneFunctionObject(aCx, setter, globalObject)))
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     nsDependentString name(mName);
     if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
                                static_cast<const char16_t*>(mName),
                                name.Length(), JS::UndefinedHandleValue, mJSAttributes,
-                               JS_DATA_TO_FUNC_PTR(JSPropertyOp, getter.get()),
-                               JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setter.get())))
+                               JS_DATA_TO_FUNC_PTR(JSNative, getter.get()),
+                               JS_DATA_TO_FUNC_PTR(JSNative, setter.get())))
       return NS_ERROR_OUT_OF_MEMORY;
   }
   return NS_OK;
 }
 
 nsresult
 nsXBLProtoImplProperty::CompileMember(AutoJSAPI& jsapi, const nsCString& aClassStr,
                                       JS::Handle<JSObject*> aClassObject)
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -553,18 +553,19 @@ XPCShellEnvironment::Init()
         return false;
     }
     JSAutoCompartment ac(cx, globalObj);
 
     backstagePass->SetGlobalObject(globalObj);
 
     JS::Rooted<Value> privateVal(cx, PrivateValue(this));
     if (!JS_DefineProperty(cx, globalObj, "__XPCShellEnvironment",
-                           privateVal, JSPROP_READONLY | JSPROP_PERMANENT,
-                           JS_PropertyStub, JS_StrictPropertyStub) ||
+                           privateVal,
+                           JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_PROPOP_ACCESSORS,
+                           JS_STUBGETTER, JS_STUBSETTER) ||
         !JS_DefineFunctions(cx, globalObj, gGlobalFunctions) ||
         !JS_DefineProfilingFunctions(cx, globalObj))
     {
         NS_ERROR("JS_DefineFunctions failed!");
         return false;
     }
 
     mGlobalHolder = globalObj;
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -175,18 +175,23 @@ WrapperAnswer::RecvDefineProperty(const 
         return fail(cx, rs);
 
     if (!js::CheckDefineProperty(cx, obj, id, desc.value(), desc.attributes(),
                                  desc.getter(), desc.setter()))
     {
         return fail(cx, rs);
     }
 
-    if (!JS_DefinePropertyById(cx, obj, id, desc.value(), desc.attributes(),
-                               desc.getter(), desc.setter()))
+    if (!JS_DefinePropertyById(cx, obj, id, desc.value(),
+                               // Descrriptors never store JSNatives for
+                               // accessors: they have either JSFunctions or
+                               // JSPropertyOps.
+                               desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                               JS_PROPERTYOP_GETTER(desc.getter()),
+                               JS_PROPERTYOP_SETTER(desc.setter())))
     {
         return fail(cx, rs);
     }
 
     return ok(rs);
 }
 
 bool
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1179,37 +1179,39 @@ ShellObjectMetadataCallback(JSContext *c
 
     RootedObject stack(cx, NewDenseEmptyArray(cx));
     if (!stack)
         return false;
 
     static int createdIndex = 0;
     createdIndex++;
 
-    if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0,
-                           JS_PropertyStub, JS_StrictPropertyStub))
+    if (!JS_DefineProperty(cx, obj, "index", createdIndex,
+                           JSPROP_PROPOP_ACCESSORS,
+                           JS_STUBGETTER, JS_STUBSETTER))
     {
         return false;
     }
 
-    if (!JS_DefineProperty(cx, obj, "stack", stack, 0,
-                           JS_PropertyStub, JS_StrictPropertyStub))
+    if (!JS_DefineProperty(cx, obj, "stack", stack, JSPROP_PROPOP_ACCESSORS,
+                           JS_STUBGETTER, JS_STUBSETTER))
     {
         return false;
     }
 
     int stackIndex = 0;
     RootedId id(cx);
     RootedValue callee(cx);
     for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) {
         if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) {
             id = INT_TO_JSID(stackIndex);
             RootedObject callee(cx, iter.callee());
-            if (!JS_DefinePropertyById(cx, stack, id, callee, 0,
-                                       JS_PropertyStub, JS_StrictPropertyStub))
+            if (!JS_DefinePropertyById(cx, stack, id, callee,
+                                       JSPROP_PROPOP_ACCESSORS,
+                                       JS_STUBGETTER, JS_STUBSETTER))
             {
                 return false;
             }
             stackIndex++;
         }
     }
 
     *pmetadata = obj;
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -1373,18 +1373,19 @@ using namespace js::ctypes;
 JS_PUBLIC_API(bool)
 JS_InitCTypesClass(JSContext* cx, HandleObject global)
 {
   // attach ctypes property to global object
   RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass, NullPtr(), NullPtr()));
   if (!ctypes)
     return false;
 
-  if (!JS_DefineProperty(cx, global, "ctypes", ctypes, JSPROP_READONLY | JSPROP_PERMANENT,
-                         JS_PropertyStub, JS_StrictPropertyStub)){
+  if (!JS_DefineProperty(cx, global, "ctypes", ctypes,
+                         JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_PROPOP_ACCESSORS,
+                         JS_STUBGETTER, JS_STUBSETTER)){
     return false;
   }
 
   if (!InitTypeClasses(cx, ctypes))
     return false;
 
   // attach API functions and properties
   if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) ||
@@ -4892,18 +4893,19 @@ StructType::DefineInternal(JSContext* cx
 
       // Add the field to the StructType's 'prototype' property.
       AutoStableStringChars nameChars(cx);
       if (!nameChars.initTwoByte(cx, name))
           return false;
 
       if (!JS_DefineUCProperty(cx, prototype,
              nameChars.twoByteChars(), name->length(), UndefinedHandleValue,
-             JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT,
-             StructType::FieldGetter, StructType::FieldSetter))
+             JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_PROPOP_ACCESSORS,
+             JS_PROPERTYOP_GETTER(StructType::FieldGetter),
+             JS_PROPERTYOP_SETTER(StructType::FieldSetter)))
         return false;
 
       size_t fieldSize = CType::GetSize(fieldType);
       size_t fieldAlign = CType::GetAlignment(fieldType);
       size_t fieldOffset = Align(structSize, fieldAlign);
       // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
       // be zero, we can safely check fieldOffset + fieldSize without first
       // checking fieldOffset for overflow.
--- a/js/src/jsapi-tests/testAddPropertyPropcache.cpp
+++ b/js/src/jsapi-tests/testAddPropertyPropcache.cpp
@@ -41,25 +41,27 @@ BEGIN_TEST(testAddPropertyHook)
     JS::RootedValue proto(cx, OBJECT_TO_JSVAL(obj));
     JS_InitClass(cx, global, obj, &AddPropertyClass, nullptr, 0, nullptr, nullptr, nullptr,
                  nullptr);
 
     obj = JS_NewArrayObject(cx, 0);
     CHECK(obj);
     JS::RootedValue arr(cx, OBJECT_TO_JSVAL(obj));
 
-    CHECK(JS_DefineProperty(cx, global, "arr", arr, JSPROP_ENUMERATE,
-                            JS_PropertyStub, JS_StrictPropertyStub));
+    CHECK(JS_DefineProperty(cx, global, "arr", arr,
+                            JSPROP_ENUMERATE | JSPROP_PROPOP_ACCESSORS,
+                            JS_STUBGETTER, JS_STUBSETTER));
 
     JS::RootedObject arrObj(cx, &arr.toObject());
     for (int i = 0; i < ExpectedCount; ++i) {
         obj = JS_NewObject(cx, &AddPropertyClass, JS::NullPtr(), JS::NullPtr());
         CHECK(obj);
-        CHECK(JS_DefineElement(cx, arrObj, i, obj, JSPROP_ENUMERATE,
-                               JS_PropertyStub, JS_StrictPropertyStub));
+        CHECK(JS_DefineElement(cx, arrObj, i, obj,
+                               JSPROP_ENUMERATE | JSPROP_PROPOP_ACCESSORS,
+                               JS_STUBGETTER, JS_STUBSETTER));
     }
 
     // Now add a prop to each of the objects, but make sure to do
     // so at the same bytecode location so we can hit the propcache.
     EXEC("'use strict';                                     \n"
          "for (var i = 0; i < arr.length; ++i)              \n"
          "  arr[i].prop = 42;                               \n"
          );
--- a/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp
+++ b/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp
@@ -31,24 +31,24 @@ BEGIN_TEST(testDefineGetterSetterNonEnum
     CHECK(funSet);
     JS::RootedObject funSetObj(cx, JS_GetFunctionObject(funSet));
     JS::RootedValue vset(cx, OBJECT_TO_JSVAL(funSetObj));
 
     JS::RootedObject vObject(cx, vobj.toObjectOrNull());
     CHECK(JS_DefineProperty(cx, vObject, PROPERTY_NAME,
                             JS::UndefinedHandleValue,
                             JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED | JSPROP_ENUMERATE,
-                            JS_DATA_TO_FUNC_PTR(JSPropertyOp, (JSObject*) funGetObj),
-                            JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, (JSObject*) funSetObj)));
+                            JS_DATA_TO_FUNC_PTR(JSNative, (JSObject*) funGetObj),
+                            JS_DATA_TO_FUNC_PTR(JSNative, (JSObject*) funSetObj)));
 
     CHECK(JS_DefineProperty(cx, vObject, PROPERTY_NAME,
                             JS::UndefinedHandleValue,
                             JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED | JSPROP_PERMANENT,
-                            JS_DATA_TO_FUNC_PTR(JSPropertyOp, (JSObject*) funGetObj),
-                            JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, (JSObject*) funSetObj)));
+                            JS_DATA_TO_FUNC_PTR(JSNative, (JSObject*) funGetObj),
+                            JS_DATA_TO_FUNC_PTR(JSNative, (JSObject*) funSetObj)));
 
     JS::Rooted<JSPropertyDescriptor> desc(cx);
     CHECK(JS_GetOwnPropertyDescriptor(cx, vObject, PROPERTY_NAME, &desc));
     CHECK(desc.object());
     CHECK(desc.hasGetterObject());
     CHECK(desc.hasSetterObject());
     CHECK(desc.isPermanent());
     CHECK(!desc.isEnumerable());
--- a/js/src/jsapi-tests/testDefinePropertyIgnoredAttributes.cpp
+++ b/js/src/jsapi-tests/testDefinePropertyIgnoredAttributes.cpp
@@ -42,40 +42,39 @@ CheckDescriptor(JS::Handle<JSPropertyDes
 BEGIN_TEST(testDefinePropertyIgnoredAttributes)
 {
     JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
     JS::Rooted<JSPropertyDescriptor> desc(cx);
     JS::RootedValue defineValue(cx);
 
     // Try a getter. Allow it to fill in the defaults.
     CHECK(JS_DefineProperty(cx, obj, "foo", defineValue,
-                            IgnoreAll | JSPROP_NATIVE_ACCESSORS | JSPROP_SHARED,
-                            (JSPropertyOp)Getter));
+                            IgnoreAll | JSPROP_SHARED,
+                            Getter));
 
     CHECK(JS_GetPropertyDescriptor(cx, obj, "foo", &desc));
 
     // Note that since JSPROP_READONLY means nothing for accessor properties, we will actually
     // claim to be writable, since the flag is not included in the mask.
     CHECK(CheckDescriptor(desc, false, true, false));
 
     // Install another configurable property, so we can futz with it.
     CHECK(JS_DefineProperty(cx, obj, "bar", defineValue,
-                            AllowConfigure | JSPROP_NATIVE_ACCESSORS | JSPROP_SHARED,
-                            (JSPropertyOp)Getter));
+                            AllowConfigure | JSPROP_SHARED,
+                            Getter));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "bar", &desc));
     CHECK(CheckDescriptor(desc, false, true, true));
 
     // Rewrite the descriptor to now be enumerable, ensuring that the lack of
     // configurablity stayed.
     CHECK(JS_DefineProperty(cx, obj, "bar", defineValue,
                             AllowEnumerate |
                             JSPROP_ENUMERATE |
-                            JSPROP_NATIVE_ACCESSORS |
                             JSPROP_SHARED,
-                            (JSPropertyOp)Getter));
+                            Getter));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "bar", &desc));
     CHECK(CheckDescriptor(desc, true, true, true));
 
     // Now try the same game with a value property
     defineValue.setObject(*obj);
     CHECK(JS_DefineProperty(cx, obj, "baz", defineValue, IgnoreWithValue));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "baz", &desc));
     CHECK(CheckDescriptor(desc, false, false, false));
--- a/js/src/jsapi-tests/testSetProperty.cpp
+++ b/js/src/jsapi-tests/testSetProperty.cpp
@@ -7,21 +7,23 @@
 
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testSetProperty_NativeGetterStubSetter)
 {
     JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
     CHECK(obj);
 
-    CHECK(JS_DefineProperty(cx, global, "globalProp", obj, JSPROP_ENUMERATE,
-                            JS_PropertyStub, JS_StrictPropertyStub));
+    CHECK(JS_DefineProperty(cx, global, "globalProp", obj,
+                            JSPROP_ENUMERATE | JSPROP_PROPOP_ACCESSORS,
+                            JS_STUBGETTER, JS_STUBSETTER));
 
-    CHECK(JS_DefineProperty(cx, obj, "prop", JS::UndefinedHandleValue, JSPROP_SHARED,
-                            NativeGet, JS_StrictPropertyStub));
+    CHECK(JS_DefineProperty(cx, obj, "prop", JS::UndefinedHandleValue,
+                            JSPROP_SHARED | JSPROP_PROPOP_ACCESSORS,
+                            JS_PROPERTYOP_GETTER(NativeGet), JS_STUBSETTER));
 
     EXEC("'use strict';                                     \n"
          "var error, passed = false;                        \n"
          "try                                               \n"
          "{                                                 \n"
          "  this.globalProp.prop = 42;                      \n"
          "  throw new Error('setting property succeeded!'); \n"
          "}                                                 \n"
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2798,227 +2798,221 @@ JS_AlreadyHasOwnUCProperty(JSContext *cx
     RootedId id(cx, AtomToId(atom));
     return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
 }
 
 /*
  * Wrapper functions to create wrappers with no corresponding JSJitInfo from API
  * function arguments.
  */
-static JSPropertyOpWrapper
-GetterWrapper(JSPropertyOp getter)
-{
-    JSPropertyOpWrapper ret;
-    ret.op = getter;
-    ret.info = nullptr;
-    return ret;
-}
-
-static JSStrictPropertyOpWrapper
-SetterWrapper(JSStrictPropertyOp setter)
-{
-    JSStrictPropertyOpWrapper ret;
-    ret.op = setter;
+static JSNativeWrapper
+NativeOpWrapper(Native native)
+{
+    JSNativeWrapper ret;
+    ret.op = native;
     ret.info = nullptr;
     return ret;
 }
 
 static bool
 DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
-                   const JSPropertyOpWrapper &get, const JSStrictPropertyOpWrapper &set,
+                   const JSNativeWrapper &get, const JSNativeWrapper &set,
                    unsigned attrs, unsigned flags)
 {
-    PropertyOp getter = get.op;
-    StrictPropertyOp setter = set.op;
+    JSPropertyOp getter = JS_CAST_NATIVE_TO(get.op, JSPropertyOp);
+    JSStrictPropertyOp setter = JS_CAST_NATIVE_TO(set.op, JSStrictPropertyOp);
 
     // JSPROP_READONLY has no meaning when accessors are involved. Ideally we'd
     // throw if this happens, but we've accepted it for long enough that it's
     // not worth trying to make callers change their ways. Just flip it off on
     // its way through the API layer so that we can enforce this internally.
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
         attrs &= ~JSPROP_READONLY;
 
     // When we use DefineProperty, we need full scriptable Function objects rather
     // than JSNatives. However, we might be pulling this property descriptor off
     // of something with JSNative property descriptors. If we are, wrap them in
     // JS Function objects.
-    if (attrs & JSPROP_NATIVE_ACCESSORS) {
-        MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+    if (!(attrs & JSPROP_PROPOP_ACCESSORS)) {
         JSFunction::Flags zeroFlags = JSAPIToJSFunctionFlags(0);
 
         RootedAtom atom(cx, JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : nullptr);
-        attrs &= ~JSPROP_NATIVE_ACCESSORS;
-        if (getter) {
+        if (getter && !(attrs & JSPROP_GETTER)) {
             RootedObject global(cx, (JSObject*) &obj->global());
             JSFunction *getobj = NewFunction(cx, NullPtr(), (Native) getter, 0,
                                              zeroFlags, global, atom);
             if (!getobj)
                 return false;
 
             if (get.info)
                 getobj->setJitInfo(get.info);
 
             getter = JS_DATA_TO_FUNC_PTR(PropertyOp, getobj);
             attrs |= JSPROP_GETTER;
         }
-        if (setter) {
+        if (setter && !(attrs & JSPROP_SETTER)) {
             // Root just the getter, since the setter is not yet a JSObject.
             AutoRooterGetterSetter getRoot(cx, JSPROP_GETTER, &getter, nullptr);
             RootedObject global(cx, (JSObject*) &obj->global());
             JSFunction *setobj = NewFunction(cx, NullPtr(), (Native) setter, 1,
                                              zeroFlags, global, atom);
             if (!setobj)
                 return false;
 
             if (set.info)
                 setobj->setJitInfo(set.info);
 
             setter = JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setobj);
             attrs |= JSPROP_SETTER;
         }
+    } else {
+        attrs &= ~JSPROP_PROPOP_ACCESSORS;
     }
 
-
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id, value,
                           (attrs & JSPROP_GETTER)
                           ? JS_FUNC_TO_DATA_PTR(JSObject *, getter)
                           : nullptr,
                           (attrs & JSPROP_SETTER)
                           ? JS_FUNC_TO_DATA_PTR(JSObject *, setter)
                           : nullptr);
 
     return JSObject::defineGeneric(cx, obj, id, value, getter, setter, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
-                      unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
-{
-    return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter), SetterWrapper(setter),
+                      unsigned attrs, Native getter, Native setter)
+{
+    return DefinePropertyById(cx, obj, id, value,
+                              NativeOpWrapper(getter), NativeOpWrapper(setter),
                               attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleObject valueArg,
-                      unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+                      unsigned attrs, Native getter, Native setter)
 {
     RootedValue value(cx, ObjectValue(*valueArg));
-    return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter), SetterWrapper(setter),
+    return DefinePropertyById(cx, obj, id, value,
+                              NativeOpWrapper(getter), NativeOpWrapper(setter),
                               attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleString valueArg,
-                      unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+                      unsigned attrs, Native getter, Native setter)
 {
     RootedValue value(cx, StringValue(valueArg));
-    return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter), SetterWrapper(setter),
+    return DefinePropertyById(cx, obj, id, value,
+                              NativeOpWrapper(getter), NativeOpWrapper(setter),
                               attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, int32_t valueArg,
-                      unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+                      unsigned attrs, Native getter, Native setter)
 {
     Value value = Int32Value(valueArg);
     return DefinePropertyById(cx, obj, id, HandleValue::fromMarkedLocation(&value),
-                              GetterWrapper(getter), SetterWrapper(setter), attrs, 0);
+                              NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, uint32_t valueArg,
-                      unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+                      unsigned attrs, Native getter, Native setter)
 {
     Value value = UINT_TO_JSVAL(valueArg);
     return DefinePropertyById(cx, obj, id, HandleValue::fromMarkedLocation(&value),
-                              GetterWrapper(getter), SetterWrapper(setter), attrs, 0);
+                              NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, double valueArg,
-                      unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+                      unsigned attrs, Native getter, Native setter)
 {
     Value value = NumberValue(valueArg);
     return DefinePropertyById(cx, obj, id, HandleValue::fromMarkedLocation(&value),
-                              GetterWrapper(getter), SetterWrapper(setter), attrs, 0);
+                              NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
 }
 
 static bool
 DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue value,
-              unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+              unsigned attrs, Native getter, Native setter)
 {
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
-    return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter), SetterWrapper(setter),
+    return DefinePropertyById(cx, obj, id, value,
+                              NativeOpWrapper(getter), NativeOpWrapper(setter),
                               attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue value,
-                 unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+                 unsigned attrs, Native getter, Native setter)
 {
     return DefineElement(cx, obj, index, value, attrs, getter, setter);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleObject valueArg,
-                 unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+                 unsigned attrs, Native getter, Native setter)
 {
     RootedValue value(cx, ObjectValue(*valueArg));
     return DefineElement(cx, obj, index, value, attrs, getter, setter);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleString valueArg,
-                 unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+                 unsigned attrs, Native getter, Native setter)
 {
     RootedValue value(cx, StringValue(valueArg));
     return DefineElement(cx, obj, index, value, attrs, getter, setter);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, HandleObject obj, uint32_t index, int32_t valueArg,
-                 unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+                 unsigned attrs, Native getter, Native setter)
 {
     Value value = Int32Value(valueArg);
     return DefineElement(cx, obj, index, HandleValue::fromMarkedLocation(&value),
                          attrs, getter, setter);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, HandleObject obj, uint32_t index, uint32_t valueArg,
-                 unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+                 unsigned attrs, Native getter, Native setter)
 {
     Value value = UINT_TO_JSVAL(valueArg);
     return DefineElement(cx, obj, index, HandleValue::fromMarkedLocation(&value),
                          attrs, getter, setter);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, HandleObject obj, uint32_t index, double valueArg,
-                 unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
+                 unsigned attrs, Native getter, Native setter)
 {
     Value value = NumberValue(valueArg);
     return DefineElement(cx, obj, index, HandleValue::fromMarkedLocation(&value),
                          attrs, getter, setter);
 }
 
 static bool
 DefineProperty(JSContext *cx, HandleObject obj, const char *name, HandleValue value,
-               const JSPropertyOpWrapper &getter, const JSStrictPropertyOpWrapper &setter,
+               const JSNativeWrapper &getter, const JSNativeWrapper &setter,
                unsigned attrs, unsigned flags)
 {
-    AutoRooterGetterSetter gsRoot(cx, attrs, const_cast<JSPropertyOp *>(&getter.op),
-                                  const_cast<JSStrictPropertyOp *>(&setter.op));
+    AutoRooterGetterSetter gsRoot(cx, attrs, const_cast<JSNative *>(&getter.op),
+                                  const_cast<JSNative *>(&setter.op));
 
     RootedId id(cx);
     if (attrs & JSPROP_INDEX) {
         id.set(INT_TO_JSID(intptr_t(name)));
         attrs &= ~JSPROP_INDEX;
     } else {
         JSAtom *atom = Atomize(cx, name, strlen(name));
         if (!atom)
@@ -3042,161 +3036,161 @@ DefineSelfHostedProperty(JSContext *cx, 
     if (!name)
         return false;
 
     RootedValue getterValue(cx);
     if (!cx->global()->getSelfHostedFunction(cx, getterNameAtom, name, 0, &getterValue))
         return false;
     MOZ_ASSERT(getterValue.isObject() && getterValue.toObject().is<JSFunction>());
     RootedFunction getterFunc(cx, &getterValue.toObject().as<JSFunction>());
-    JSPropertyOp getterOp = JS_DATA_TO_FUNC_PTR(PropertyOp, getterFunc.get());
+    JSNative getterOp = JS_DATA_TO_FUNC_PTR(JSNative, getterFunc.get());
 
     RootedFunction setterFunc(cx);
     if (setterName) {
         RootedAtom setterNameAtom(cx, Atomize(cx, setterName, strlen(setterName)));
         if (!setterNameAtom)
             return false;
 
         RootedValue setterValue(cx);
         if (!cx->global()->getSelfHostedFunction(cx, setterNameAtom, name, 0, &setterValue))
             return false;
         MOZ_ASSERT(setterValue.isObject() && setterValue.toObject().is<JSFunction>());
         setterFunc = &getterValue.toObject().as<JSFunction>();
     }
-    JSStrictPropertyOp setterOp = JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setterFunc.get());
+    JSNative setterOp = JS_DATA_TO_FUNC_PTR(JSNative, setterFunc.get());
 
     return DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue,
-                              GetterWrapper(getterOp), SetterWrapper(setterOp),
+                              NativeOpWrapper(getterOp), NativeOpWrapper(setterOp),
                               attrs, flags);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, HandleObject obj, const char *name, HandleValue value,
                   unsigned attrs,
-                  PropertyOp getter /* = nullptr */, JSStrictPropertyOp setter /* = nullptr */)
-{
-    return DefineProperty(cx, obj, name, value, GetterWrapper(getter), SetterWrapper(setter),
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
+{
+    return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
                           attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, HandleObject obj, const char *name, HandleObject valueArg,
                   unsigned attrs,
-                  PropertyOp getter /* = nullptr */, JSStrictPropertyOp setter /* = nullptr */)
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
 {
     RootedValue value(cx, ObjectValue(*valueArg));
-    return DefineProperty(cx, obj, name, value, GetterWrapper(getter), SetterWrapper(setter),
+    return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
                           attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, HandleObject obj, const char *name, HandleString valueArg,
                   unsigned attrs,
-                  PropertyOp getter /* = nullptr */, JSStrictPropertyOp setter /* = nullptr */)
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
 {
     RootedValue value(cx, StringValue(valueArg));
-    return DefineProperty(cx, obj, name, value, GetterWrapper(getter), SetterWrapper(setter),
+    return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
                           attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, HandleObject obj, const char *name, int32_t valueArg,
                   unsigned attrs,
-                  PropertyOp getter /* = nullptr */, JSStrictPropertyOp setter /* = nullptr */)
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
 {
     Value value = Int32Value(valueArg);
     return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value),
-                          GetterWrapper(getter), SetterWrapper(setter), attrs, 0);
+                          NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, HandleObject obj, const char *name, uint32_t valueArg,
                   unsigned attrs,
-                  PropertyOp getter /* = nullptr */, JSStrictPropertyOp setter /* = nullptr */)
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
 {
     Value value = UINT_TO_JSVAL(valueArg);
     return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value),
-                          GetterWrapper(getter), SetterWrapper(setter), attrs, 0);
+                          NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, HandleObject obj, const char *name, double valueArg,
                   unsigned attrs,
-                  PropertyOp getter /* = nullptr */, JSStrictPropertyOp setter /* = nullptr */)
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
 {
     Value value = NumberValue(valueArg);
     return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value),
-                          GetterWrapper(getter), SetterWrapper(setter), attrs, 0);
+                          NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
 }
 
 static bool
 DefineUCProperty(JSContext *cx, HandleObject obj, const char16_t *name, size_t namelen,
-                 const Value &value_, PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
+                 const Value &value_, Native getter, Native setter, unsigned attrs,
                  unsigned flags)
 {
     RootedValue value(cx, value_);
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
     JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
-    return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter), SetterWrapper(setter),
+    return DefinePropertyById(cx, obj, id, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
                               attrs, flags);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, HandleObject obj, const char16_t *name, size_t namelen,
                     HandleValue value, unsigned attrs,
-                    JSPropertyOp getter, JSStrictPropertyOp setter)
+                    Native getter, Native setter)
 {
     return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, HandleObject obj, const char16_t *name, size_t namelen,
                     HandleObject valueArg, unsigned attrs,
-                    JSPropertyOp getter, JSStrictPropertyOp setter)
+                    Native getter, Native setter)
 {
     RootedValue value(cx, ObjectValue(*valueArg));
     return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, HandleObject obj, const char16_t *name, size_t namelen,
                     HandleString valueArg, unsigned attrs,
-                    JSPropertyOp getter, JSStrictPropertyOp setter)
+                    Native getter, Native setter)
 {
     RootedValue value(cx, StringValue(valueArg));
     return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, HandleObject obj, const char16_t *name, size_t namelen,
                     int32_t valueArg, unsigned attrs,
-                    JSPropertyOp getter, JSStrictPropertyOp setter)
+                    Native getter, Native setter)
 {
     Value value = Int32Value(valueArg);
     return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value),
                             getter, setter, attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, HandleObject obj, const char16_t *name, size_t namelen,
                     uint32_t valueArg, unsigned attrs,
-                    JSPropertyOp getter, JSStrictPropertyOp setter)
+                    Native getter, Native setter)
 {
     Value value = UINT_TO_JSVAL(valueArg);
     return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value),
                             getter, setter, attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, HandleObject obj, const char16_t *name, size_t namelen,
                     double valueArg, unsigned attrs,
-                    JSPropertyOp getter, JSStrictPropertyOp setter)
+                    Native getter, Native setter)
 {
     Value value = NumberValue(valueArg);
     return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value),
                             getter, setter, attrs, 0);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_DefineObject(JSContext *cx, HandleObject obj, const char *name, const JSClass *jsclasp,
@@ -3210,17 +3204,17 @@ JS_DefineObject(JSContext *cx, HandleObj
     if (!clasp)
         clasp = &JSObject::class_;    /* default class is Object */
 
     RootedObject nobj(cx, NewObjectWithClassProto(cx, clasp, proto, obj));
     if (!nobj)
         return nullptr;
 
     RootedValue nobjValue(cx, ObjectValue(*nobj));
-    if (!DefineProperty(cx, obj, name, nobjValue, GetterWrapper(nullptr), SetterWrapper(nullptr),
+    if (!DefineProperty(cx, obj, name, nobjValue, NativeOpWrapper(nullptr), NativeOpWrapper(nullptr),
                         attrs, 0)) {
         return nullptr;
     }
 
     return nobj;
 }
 
 static inline Value
@@ -3235,18 +3229,18 @@ ValueFromScalar(int32_t x)
 }
 
 template<typename T>
 static bool
 DefineConstScalar(JSContext *cx, HandleObject obj, const JSConstScalarSpec<T> *cds)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    JSPropertyOpWrapper noget = GetterWrapper(nullptr);
-    JSStrictPropertyOpWrapper noset = SetterWrapper(nullptr);
+    JSNativeWrapper noget = NativeOpWrapper(nullptr);
+    JSNativeWrapper noset = NativeOpWrapper(nullptr);
     unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
     for (; cds->name; cds++) {
         RootedValue value(cx, ValueFromScalar(cds->val));
         if (!DefineProperty(cx, obj, cds->name, value, noget, noset, attrs, 0))
             return false;
     }
     return true;
 }
@@ -3301,37 +3295,23 @@ JS_PUBLIC_API(bool)
 JS_DefineProperties(JSContext *cx, HandleObject obj, const JSPropertySpec *ps)
 {
     RootedId id(cx);
 
     for (; ps->name; ps++) {
         if (!PropertySpecNameToId(cx, ps->name, &id))
             return false;
 
-        if (ps->flags & JSPROP_NATIVE_ACCESSORS) {
-            // If you declare native accessors, then you should have a native
-            // getter.
-            MOZ_ASSERT(ps->getter.propertyOp.op);
-
-            // If you do not have a self-hosted getter, you should not have a
-            // self-hosted setter. This is the closest approximation to that
-            // assertion we can have with our setup.
-            MOZ_ASSERT_IF(ps->setter.propertyOp.info, ps->setter.propertyOp.op);
-
+        if (!ps->isSelfHosted()) {
             if (!DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue,
-                                    ps->getter.propertyOp, ps->setter.propertyOp, ps->flags, 0))
+                                    ps->getter.native, ps->setter.native, ps->flags, 0))
             {
                 return false;
             }
         } else {
-            // If you have self-hosted getter/setter, you can't have a
-            // native one.
-            MOZ_ASSERT(!ps->getter.propertyOp.op && !ps->setter.propertyOp.op);
-            MOZ_ASSERT(ps->flags & JSPROP_GETTER);
-
             if (!DefineSelfHostedProperty(cx, obj, id,
                                           ps->getter.selfHosted.funname,
                                           ps->setter.selfHosted.funname,
                                           ps->flags, 0))
             {
                 return false;
             }
         }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -959,18 +959,19 @@ class MOZ_STACK_CLASS SourceBufferHolder
  *     trying to reorder or change the the first byte worth of flags.
  */
 #define JSPROP_ENUMERATE        0x01    /* property is visible to for/in loop */
 #define JSPROP_READONLY         0x02    /* not settable: assignment is no-op.
                                            This flag is only valid when neither
                                            JSPROP_GETTER nor JSPROP_SETTER is
                                            set. */
 #define JSPROP_PERMANENT        0x04    /* property cannot be deleted */
-#define JSPROP_NATIVE_ACCESSORS 0x08    /* set in JSPropertyDescriptor.flags
-                                           if getters/setters are JSNatives */
+#define JSPROP_PROPOP_ACCESSORS 0x08    /* Passed to JS_Define(UC)Property* and
+                                           JS_DefineElement if getters/setters
+                                           are JSPropertyOp/JSStrictPropertyOp */
 #define JSPROP_GETTER           0x10    /* property holds getter function */
 #define JSPROP_SETTER           0x20    /* property holds setter function */
 #define JSPROP_SHARED           0x40    /* don't allocate a value slot for this
                                            property; don't copy the property on
                                            set of the same-named property in an
                                            object that delegates to a prototype
                                            containing this property */
 #define JSPROP_INDEX            0x80    /* name is actually (int) index */
@@ -2385,131 +2386,157 @@ struct JSConstScalarSpec {
 };
 
 typedef JSConstScalarSpec<double> JSConstDoubleSpec;
 typedef JSConstScalarSpec<int32_t> JSConstIntegerSpec;
 
 struct JSJitInfo;
 
 /*
- * Wrappers to replace {Strict,}PropertyOp for JSPropertySpecs. This will allow
- * us to pass one JSJitInfo per function with the property spec, without
- * additional field overhead.
- */
-typedef struct JSStrictPropertyOpWrapper {
-    JSStrictPropertyOp  op;
-    const JSJitInfo     *info;
-} JSStrictPropertyOpWrapper;
-
-typedef struct JSPropertyOpWrapper {
-    JSPropertyOp        op;
-    const JSJitInfo     *info;
-} JSPropertyOpWrapper;
-
-/*
- * Wrapper to do as above, but for JSNatives for JSFunctionSpecs.
+ * Wrapper to relace JSNative for JSPropertySpecs and JSFunctionSpecs. This will
+ * allow us to pass one JSJitInfo per function with the property/function spec,
+ * without additional field overhead.
  */
 typedef struct JSNativeWrapper {
     JSNative        op;
     const JSJitInfo *info;
 } JSNativeWrapper;
 
 /*
  * Macro static initializers which make it easy to pass no JSJitInfo as part of a
  * JSPropertySpec or JSFunctionSpec.
  */
-#define JSOP_WRAPPER(op) { {op, nullptr} }
-#define JSOP_NULLWRAPPER JSOP_WRAPPER(nullptr)
+#define JSNATIVE_WRAPPER(native) { {native, nullptr} }
 
 /*
  * To define an array element rather than a named property member, cast the
  * element's index to (const char *) and initialize name with it, and set the
  * JSPROP_INDEX bit in flags.
  */
 struct JSPropertySpec {
     struct SelfHostedWrapper {
         void       *unused;
         const char *funname;
     };
 
     const char                  *name;
     uint8_t                     flags;
     union {
-        JSPropertyOpWrapper propertyOp;
+        JSNativeWrapper     native;
         SelfHostedWrapper   selfHosted;
     } getter;
     union {
-        JSStrictPropertyOpWrapper propertyOp;
+        JSNativeWrapper           native;
         SelfHostedWrapper         selfHosted;
     } setter;
 
-    static_assert(sizeof(SelfHostedWrapper) == sizeof(JSPropertyOpWrapper),
-                  "JSPropertySpec::getter must be compact");
-    static_assert(sizeof(SelfHostedWrapper) == sizeof(JSStrictPropertyOpWrapper),
-                  "JSPropertySpec::setter must be compact");
-    static_assert(offsetof(SelfHostedWrapper, funname) == offsetof(JSPropertyOpWrapper, info),
+    bool isSelfHosted() const {
+#ifdef DEBUG
+        // Verify that our accessors match our JSPROP_GETTER flag.
+        if (flags & JSPROP_GETTER)
+            checkAccessorsAreSelfHosted();
+        else
+            checkAccessorsAreNative();
+#endif
+        return (flags & JSPROP_GETTER);
+    }
+
+    static_assert(sizeof(SelfHostedWrapper) == sizeof(JSNativeWrapper),
+                  "JSPropertySpec::getter/setter must be compact");
+    static_assert(offsetof(SelfHostedWrapper, funname) == offsetof(JSNativeWrapper, info),
                   "JS_SELF_HOSTED* macros below require that "
                   "SelfHostedWrapper::funname overlay "
-                  "JSPropertyOpWrapper::info");
+                  "JSNativeWrapper::info");
+private:
+    void checkAccessorsAreNative() const {
+        MOZ_ASSERT(getter.native.op);
+        // We may not have a setter at all.  So all we can assert here, for the
+        // native case is that if we have a jitinfo for the setter then we have
+        // a setter op too.  This is good enough to make sure we don't have a
+        // SelfHostedWrapper for the setter.
+        MOZ_ASSERT_IF(setter.native.info, setter.native.op);
+    }
+
+    void checkAccessorsAreSelfHosted() const {
+        MOZ_ASSERT(!getter.selfHosted.unused);
+        MOZ_ASSERT(!setter.selfHosted.unused);
+    }
 };
 
 namespace JS {
 namespace detail {
 
 /* NEVER DEFINED, DON'T USE.  For use by JS_CAST_NATIVE_TO only. */
 inline int CheckIsNative(JSNative native);
 
 /* NEVER DEFINED, DON'T USE.  For use by JS_CAST_STRING_TO only. */
 template<size_t N>
 inline int
 CheckIsCharacterLiteral(const char (&arr)[N]);
 
+/* NEVER DEFINED, DON'T USE.  For use by JS_PROPERTYOP_GETTER only. */
+inline int CheckIsPropertyOp(JSPropertyOp op);
+
+/* NEVER DEFINED, DON'T USE.  For use by JS_PROPERTYOP_SETTER only. */
+inline int CheckIsStrictPropertyOp(JSStrictPropertyOp op);
+
+
 } // namespace detail
 } // namespace JS
 
 #define JS_CAST_NATIVE_TO(v, To) \
   (static_cast<void>(sizeof(JS::detail::CheckIsNative(v))), \
    reinterpret_cast<To>(v))
 
 #define JS_CAST_STRING_TO(s, To) \
   (static_cast<void>(sizeof(JS::detail::CheckIsCharacterLiteral(s))), \
    reinterpret_cast<To>(s))
 
 #define JS_CHECK_ACCESSOR_FLAGS(flags) \
-  (static_cast<mozilla::EnableIf<!((flags) & (JSPROP_READONLY | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS))>::Type>(0), \
+  (static_cast<mozilla::EnableIf<!((flags) & (JSPROP_READONLY | JSPROP_SHARED | JSPROP_PROPOP_ACCESSORS))>::Type>(0), \
    (flags))
 
+#define JS_PROPERTYOP_GETTER(v) \
+  (static_cast<void>(sizeof(JS::detail::CheckIsPropertyOp(v))), \
+   reinterpret_cast<JSNative>(v))
+
+#define JS_PROPERTYOP_SETTER(v) \
+  (static_cast<void>(sizeof(JS::detail::CheckIsStrictPropertyOp(v))), \
+   reinterpret_cast<JSNative>(v))
+
+#define JS_STUBGETTER JS_PROPERTYOP_GETTER(JS_PropertyStub)
+
+#define JS_STUBSETTER JS_PROPERTYOP_SETTER(JS_StrictPropertyStub)
+
 /*
- * JSPropertySpec uses JSAPI JSPropertyOp and JSStrictPropertyOp in function
- * signatures.  These macros encapsulate the definition of JSNative-backed
- * JSPropertySpecs, performing type-safe casts on the getter/setter functions
- * and adding the necessary property flags to trigger interpretation as
- * JSNatives.
+ * JSPropertySpec uses JSNativeWrapper.  These macros encapsulate the definition
+ * of JSNative-backed JSPropertySpecs, by defining the JSNativeWrappers for
+ * them.
  */
 #define JS_PSG(name, getter, flags) \
     {name, \
-     uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS), \
-     JSOP_WRAPPER(JS_CAST_NATIVE_TO(getter, JSPropertyOp)), \
-     JSOP_NULLWRAPPER}
+     uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED), \
+     JSNATIVE_WRAPPER(getter), \
+     JSNATIVE_WRAPPER(nullptr)}
 #define JS_PSGS(name, getter, setter, flags) \
     {name, \
-     uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS), \
-     JSOP_WRAPPER(JS_CAST_NATIVE_TO(getter, JSPropertyOp)), \
-     JSOP_WRAPPER(JS_CAST_NATIVE_TO(setter, JSStrictPropertyOp))}
+     uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED), \
+     JSNATIVE_WRAPPER(getter), \
+     JSNATIVE_WRAPPER(setter)}
 #define JS_SELF_HOSTED_GET(name, getterName, flags) \
     {name, \
      uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED | JSPROP_GETTER), \
      { nullptr, JS_CAST_STRING_TO(getterName, const JSJitInfo *) }, \
-     JSOP_NULLWRAPPER }
+     JSNATIVE_WRAPPER(nullptr) }
 #define JS_SELF_HOSTED_GETSET(name, getterName, setterName, flags) \
     {name, \
      uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED | JSPROP_GETTER | JSPROP_SETTER), \
      { nullptr, JS_CAST_STRING_TO(getterName, const JSJitInfo *) },  \
      { nullptr, JS_CAST_STRING_TO(setterName, const JSJitInfo *) } }
-#define JS_PS_END { nullptr, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+#define JS_PS_END { nullptr, 0, JSNATIVE_WRAPPER(nullptr), JSNATIVE_WRAPPER(nullptr) }
 
 /*
  * To define a native function, set call to a JSNativeWrapper. To define a
  * self-hosted function, set selfHostedName to the name of a function
  * compiled during JSRuntime::initSelfHosting.
  */
 struct JSFunctionSpec {
     const char      *name;
@@ -2875,72 +2902,72 @@ extern JS_PUBLIC_API(bool)
 JS_DefineConstIntegers(JSContext *cx, JS::HandleObject obj, const JSConstIntegerSpec *cis);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineProperties(JSContext *cx, JS::HandleObject obj, const JSPropertySpec *ps);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, JS::HandleValue value,
                   unsigned attrs,
-                  JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                  JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, JS::HandleObject value,
                   unsigned attrs,
-                  JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                  JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, JS::HandleString value,
                   unsigned attrs,
-                  JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                  JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, int32_t value,
                   unsigned attrs,
-                  JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                  JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, uint32_t value,
                   unsigned attrs,
-                  JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                  JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, double value,
                   unsigned attrs,
-                  JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                  JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value,
                       unsigned attrs,
-                      JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                      JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject value,
                       unsigned attrs,
-                      JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                      JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleString value,
                       unsigned attrs,
-                      JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                      JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, int32_t value,
                       unsigned attrs,
-                      JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                      JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, uint32_t value,
                       unsigned attrs,
-                      JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                      JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, double value,
                       unsigned attrs,
-                      JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                      JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_AlreadyHasOwnProperty(JSContext *cx, JS::HandleObject obj, const char *name,
                          bool *foundp);
 
 extern JS_PUBLIC_API(bool)
 JS_AlreadyHasOwnPropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
                              bool *foundp);
@@ -2980,17 +3007,16 @@ template <typename Outer>
 class PropertyDescriptorOperations
 {
     const JSPropertyDescriptor * desc() const { return static_cast<const Outer*>(this)->extract(); }
 
   public:
     bool isEnumerable() const { return desc()->attrs & JSPROP_ENUMERATE; }
     bool isReadonly() const { return desc()->attrs & JSPROP_READONLY; }
     bool isPermanent() const { return desc()->attrs & JSPROP_PERMANENT; }
-    bool hasNativeAccessors() const { return desc()->attrs & JSPROP_NATIVE_ACCESSORS; }
     bool hasGetterObject() const { return desc()->attrs & JSPROP_GETTER; }
     bool hasSetterObject() const { return desc()->attrs & JSPROP_SETTER; }
     bool hasGetterOrSetterObject() const { return desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER); }
     bool hasGetterOrSetter() const { return desc()->getter || desc()->setter; }
     bool isShared() const { return desc()->attrs & JSPROP_SHARED; }
     bool isIndex() const { return desc()->attrs & JSPROP_INDEX; }
     bool hasAttributes(unsigned attrs) const { return desc()->attrs & attrs; }
 
@@ -3180,42 +3206,42 @@ extern JS_PUBLIC_API(bool)
 JS_DeletePropertyById(JSContext *cx, JS::HandleObject obj, jsid id);
 
 extern JS_PUBLIC_API(bool)
 JS_DeletePropertyById2(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, JS::HandleObject obj, const char16_t *name, size_t namelen,
                     JS::HandleValue value, unsigned attrs,
-                    JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                    JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, JS::HandleObject obj, const char16_t *name, size_t namelen,
                     JS::HandleObject value, unsigned attrs,
-                    JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                    JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, JS::HandleObject obj, const char16_t *name, size_t namelen,
                     JS::HandleString value, unsigned attrs,
-                    JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                    JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, JS::HandleObject obj, const char16_t *name, size_t namelen,
                     int32_t value, unsigned attrs,
-                    JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                    JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, JS::HandleObject obj, const char16_t *name, size_t namelen,
                     uint32_t value, unsigned attrs,
-                    JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                    JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, JS::HandleObject obj, const char16_t *name, size_t namelen,
                     double value, unsigned attrs,
-                    JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                    JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_AlreadyHasOwnUCProperty(JSContext *cx, JS::HandleObject obj, const char16_t *name,
                            size_t namelen, bool *foundp);
 
 extern JS_PUBLIC_API(bool)
 JS_HasUCProperty(JSContext *cx, JS::HandleObject obj,
                  const char16_t *name, size_t namelen,
@@ -3256,42 +3282,42 @@ extern JS_PUBLIC_API(bool)
 JS_GetArrayLength(JSContext *cx, JS::Handle<JSObject*> obj, uint32_t *lengthp);
 
 extern JS_PUBLIC_API(bool)
 JS_SetArrayLength(JSContext *cx, JS::Handle<JSObject*> obj, uint32_t length);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::HandleValue value,
                  unsigned attrs,
-                 JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                 JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::HandleObject value,
                  unsigned attrs,
-                 JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                 JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::HandleString value,
                  unsigned attrs,
-                 JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                 JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, JS::HandleObject obj, uint32_t index, int32_t value,
                  unsigned attrs,
-                 JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                 JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, JS::HandleObject obj, uint32_t index, uint32_t value,
                  unsigned attrs,
-                 JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                 JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, JS::HandleObject obj, uint32_t index, double value,
                  unsigned attrs,
-                 JSPropertyOp getter = nullptr, JSStrictPropertyOp setter = nullptr);
+                 JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_AlreadyHasOwnElement(JSContext *cx, JS::HandleObject obj, uint32_t index, bool *foundp);
 
 extern JS_PUBLIC_API(bool)
 JS_HasElement(JSContext *cx, JS::HandleObject obj, uint32_t index, bool *foundp);
 
 extern JS_PUBLIC_API(bool)
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -283,18 +283,19 @@ JS_TraceShapeCycleCollectorChildren(JSTr
 }
 
 static bool
 DefineHelpProperty(JSContext *cx, HandleObject obj, const char *prop, const char *value)
 {
     RootedAtom atom(cx, Atomize(cx, value, strlen(value)));
     if (!atom)
         return false;
-    return JS_DefineProperty(cx, obj, prop, atom, JSPROP_READONLY | JSPROP_PERMANENT,
-                             JS_PropertyStub, JS_StrictPropertyStub);
+    return JS_DefineProperty(cx, obj, prop, atom,
+                             JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_PROPOP_ACCESSORS,
+                             JS_STUBGETTER, JS_STUBSETTER);
 }
 
 JS_FRIEND_API(bool)
 JS_DefineFunctionsWithHelp(JSContext *cx, HandleObject obj, const JSFunctionSpecWithHelp *fs)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
 
     CHECK_REQUEST(cx);
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -1562,18 +1562,18 @@ js_InitMathClass(JSContext *cx, HandleOb
 {
     RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx));
     if (!proto)
         return nullptr;
     RootedObject Math(cx, NewObjectWithGivenProto(cx, &MathClass, proto, obj, SingletonObject));
     if (!Math)
         return nullptr;
 
-    if (!JS_DefineProperty(cx, obj, js_Math_str, Math, 0,
-                           JS_PropertyStub, JS_StrictPropertyStub))
+    if (!JS_DefineProperty(cx, obj, js_Math_str, Math, JSPROP_PROPOP_ACCESSORS,
+                           JS_STUBGETTER, JS_STUBSETTER))
     {
         return nullptr;
     }
 
     if (!JS_DefineFunctions(cx, Math, math_static_methods))
         return nullptr;
     if (!JS_DefineConstDoubles(cx, Math, math_constants))
         return nullptr;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3004,17 +3004,17 @@ JSObject::constructHook() const
     return nullptr;
 }
 
 /* static */ bool
 JSObject::defineGeneric(ExclusiveContext *cx, HandleObject obj,
                         HandleId id, HandleValue value,
                         JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
-    MOZ_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
+    MOZ_ASSERT(!(attrs & JSPROP_PROPOP_ACCESSORS));
     js::DefineGenericOp op = obj->getOps()->defineGeneric;
     if (op) {
         if (!cx->shouldBeJSContext())
             return false;
         return op(cx->asJSContext(), obj, id, value, getter, setter, attrs);
     }
     return baseops::DefineGeneric(cx, obj.as<NativeObject>(), id, value, getter, setter, attrs);
 }
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -908,18 +908,19 @@ js_InitJSONClass(JSContext *cx, HandleOb
     if (!GlobalObject::getOrCreateBooleanPrototype(cx, global))
         return nullptr;
 
     RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx));
     RootedObject JSON(cx, NewObjectWithClassProto(cx, &JSONClass, proto, global, SingletonObject));
     if (!JSON)
         return nullptr;
 
-    if (!JS_DefineProperty(cx, global, js_JSON_str, JSON, 0,
-                           JS_PropertyStub, JS_StrictPropertyStub))
+    if (!JS_DefineProperty(cx, global, js_JSON_str, JSON,
+                           JSPROP_PROPOP_ACCESSORS,
+                           JS_STUBGETTER, JS_STUBSETTER))
         return nullptr;
 
     if (!JS_DefineFunctions(cx, JSON, json_static_methods))
         return nullptr;
 
     global->setConstructor(JSProto_JSON, ObjectValue(*JSON));
 
     return JSON;
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -3536,18 +3536,18 @@ JS_InitReflect(JSContext *cx, HandleObje
     RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx));
     if (!proto)
         return nullptr;
     RootedObject Reflect(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto,
                                                      obj, SingletonObject));
     if (!Reflect)
         return nullptr;
 
-    if (!JS_DefineProperty(cx, obj, "Reflect", Reflect, 0,
-                           JS_PropertyStub, JS_StrictPropertyStub)) {
+    if (!JS_DefineProperty(cx, obj, "Reflect", Reflect, JSPROP_PROPOP_ACCESSORS,
+                           JS_STUBGETTER, JS_STUBSETTER)) {
         return nullptr;
     }
 
     if (!JS_DefineFunctions(cx, Reflect, static_methods))
         return nullptr;
 
     return Reflect;
 }
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -127,18 +127,16 @@ static const JSPropertySpec pm_props[] =
     GETTER(eventsMeasured),
     JS_PS_END
 };
 
 #undef GETTER
 
 // If this were C++ these would be "static const" members.
 
-static const uint8_t PM_CATTRS = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT;
-
 #define CONSTANT(name) { #name, PerfMeasurement::name }
 
 static const struct pm_const {
     const char *name;
     PerfMeasurement::EventMask value;
 } pm_consts[] = {
     CONSTANT(CPU_CYCLES),
     CONSTANT(INSTRUCTIONS),
@@ -228,32 +226,35 @@ GetPM(JSContext* cx, JS::HandleValue val
     return nullptr;
 }
 
 namespace JS {
 
 JSObject*
 RegisterPerfMeasurement(JSContext *cx, HandleObject globalArg)
 {
+    static const uint8_t PM_CATTRS =
+        JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_PROPOP_ACCESSORS;
+
     RootedObject global(cx, globalArg);
     RootedObject prototype(cx);
     prototype = JS_InitClass(cx, global, js::NullPtr() /* parent */,
                              &pm_class, pm_construct, 1,
                              pm_props, pm_fns, 0, 0);
     if (!prototype)
         return 0;
 
     RootedObject ctor(cx);
     ctor = JS_GetConstructor(cx, prototype);
     if (!ctor)
         return 0;
 
     for (const pm_const *c = pm_consts; c->name; c++) {
         if (!JS_DefineProperty(cx, ctor, c->name, c->value, PM_CATTRS,
-                               JS_PropertyStub, JS_StrictPropertyStub))
+                               JS_STUBGETTER, JS_STUBSETTER))
             return 0;
     }
 
     if (!JS_FreezeObject(cx, prototype) ||
         !JS_FreezeObject(cx, ctor)) {
         return 0;
     }
 
--- a/js/src/proxy/DirectProxyHandler.cpp
+++ b/js/src/proxy/DirectProxyHandler.cpp
@@ -34,18 +34,25 @@ DirectProxyHandler::getOwnPropertyDescri
 
 bool
 DirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                    MutableHandle<PropertyDescriptor> desc) const
 {
     assertEnteredPolicy(cx, proxy, id, SET);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     RootedValue v(cx, desc.value());
-    return CheckDefineProperty(cx, target, id, v, desc.attributes(), desc.getter(), desc.setter()) &&
-           JS_DefinePropertyById(cx, target, id, v, desc.attributes(), desc.getter(), desc.setter());
+    return CheckDefineProperty(cx, target, id, v, desc.attributes(),
+                               desc.getter(), desc.setter()) &&
+           JS_DefinePropertyById(cx, target, id, v,
+                                 // Descriptors never store JSNatives for
+                                 // accessors: they have either JSFunctions or
+                                 // JSPropertyOps.
+                                 desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                                 JS_PROPERTYOP_GETTER(desc.getter()),
+                                 JS_PROPERTYOP_SETTER(desc.setter()));
 }
 
 bool
 DirectProxyHandler::ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                     AutoIdVector &props) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -888,16 +888,16 @@ js_InitProxyClass(JSContext *cx, HandleO
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     RootedFunction ctor(cx);
     ctor = global->createConstructor(cx, proxy, cx->names().Proxy, 2);
     if (!ctor)
         return nullptr;
 
     if (!JS_DefineFunctions(cx, ctor, static_methods))
         return nullptr;
-    if (!JS_DefineProperty(cx, obj, "Proxy", ctor, 0,
-                           JS_PropertyStub, JS_StrictPropertyStub)) {
+    if (!JS_DefineProperty(cx, obj, "Proxy", ctor, JSPROP_PROPOP_ACCESSORS,
+                           JS_STUBGETTER, JS_STUBSETTER)) {
         return nullptr;
     }
 
     global->setConstructor(JSProto_Proxy, ObjectValue(*ctor));
     return ctor;
 }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5066,19 +5066,19 @@ static const JSJitInfo doFoo_methodinfo 
     false,    /* isAlwaysInSlot */
     false,    /* isLazilyCachedInSlot */
     false,    /* isTypedMethod */
     0         /* slotIndex */
 };
 
 static const JSPropertySpec dom_props[] = {
     {"x",
-     JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS,
-     { { (JSPropertyOp)dom_genericGetter, &dom_x_getterinfo } },
-     { { (JSStrictPropertyOp)dom_genericSetter, &dom_x_setterinfo } }
+     JSPROP_SHARED | JSPROP_ENUMERATE,
+     { { dom_genericGetter, &dom_x_getterinfo } },
+     { { dom_genericSetter, &dom_x_setterinfo } }
     },
     JS_PS_END
 };
 
 static const JSFunctionSpec dom_methods[] = {
     JS_FNINFO("doFoo", dom_genericMethod, &doFoo_methodinfo, 3, JSPROP_ENUMERATE),
     JS_FS_END
 };
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1387,17 +1387,17 @@ PurgeScopeChain(ExclusiveContext *cx, JS
         return PurgeScopeChainHelper(cx, obj, id);
     return true;
 }
 
 bool
 js::DefineNativeProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, HandleValue value,
                          PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
-    MOZ_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
+    MOZ_ASSERT(!(attrs & JSPROP_PROPOP_ACCESSORS));
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     RootedShape shape(cx);
     RootedValue updateValue(cx, value);
     bool shouldDefine = true;
 
     /*
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1672,17 +1672,23 @@ class DebugScopeProxy : public BaseProxy
         Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
 
         bool found;
         if (!has(cx, proxy, id, &found))
             return false;
         if (found)
             return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
 
-        return JS_DefinePropertyById(cx, scope, id, desc.value(), desc.attributes(), desc.getter(), desc.setter());
+        return JS_DefinePropertyById(cx, scope, id, desc.value(),
+                                     // Descriptors never store JSNatives for
+                                     // accessors: they have either JSFunctions
+                                     // or JSPropertyOps.
+                                     desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                                     JS_PROPERTYOP_GETTER(desc.getter()),
+                                     JS_PROPERTYOP_SETTER(desc.setter()));
     }
 
     bool getScopePropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props,
                                unsigned flags) const
     {
         Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
 
         if (isMissingArgumentsBinding(*scope)) {
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -217,16 +217,28 @@ AutoRooterGetterSetter::AutoRooterGetter
                                                PropertyOp *pgetter, StrictPropertyOp *psetter
                                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
         inner.emplace(cx, attrs, pgetter, psetter);
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
+inline
+AutoRooterGetterSetter::AutoRooterGetterSetter(ThreadSafeContext *cx, uint8_t attrs,
+                                               JSNative *pgetter, JSNative *psetter
+                                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
+{
+    if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
+        inner.emplace(cx, attrs, reinterpret_cast<PropertyOp *>(pgetter),
+                      reinterpret_cast<StrictPropertyOp *>(psetter));
+    }
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+}
+
 static inline uint8_t
 GetShapeAttributes(JSObject *obj, Shape *shape)
 {
     MOZ_ASSERT(obj->isNative());
 
     if (IsImplicitDenseOrTypedArrayElement(shape)) {
         if (IsAnyTypedArray(obj))
             return JSPROP_ENUMERATE | JSPROP_PERMANENT;
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -1079,16 +1079,19 @@ class AutoRooterGetterSetter
         PropertyOp *pgetter;
         StrictPropertyOp *psetter;
     };
 
   public:
     inline AutoRooterGetterSetter(ThreadSafeContext *cx, uint8_t attrs,
                                   PropertyOp *pgetter, StrictPropertyOp *psetter
                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+    inline AutoRooterGetterSetter(ThreadSafeContext *cx, uint8_t attrs,
+                                  JSNative *pgetter, JSNative *psetter
+                                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
   private:
     mozilla::Maybe<Inner> inner;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 struct EmptyShape : public js::Shape
 {
--- a/js/xpconnect/src/ExportHelpers.cpp
+++ b/js/xpconnect/src/ExportHelpers.cpp
@@ -419,18 +419,19 @@ ExportFunction(JSContext *cx, HandleValu
             JS_ReportError(cx, "Exporting function failed");
             return false;
         }
 
         // We have the forwarder function in the target compartment. If
         // defineAs was set, we also need to define it as a property on
         // the target.
         if (!JSID_IS_VOID(options.defineAs)) {
-            if (!JS_DefinePropertyById(cx, targetScope, id, rval, JSPROP_ENUMERATE,
-                                       JS_PropertyStub, JS_StrictPropertyStub)) {
+            if (!JS_DefinePropertyById(cx, targetScope, id, rval,
+                                       JSPROP_ENUMERATE | JSPROP_PROPOP_ACCESSORS,
+                                       JS_STUBGETTER, JS_STUBSETTER)) {
                 return false;
             }
         }
     }
 
     // Finally we have to re-wrap the exported function back to the caller compartment.
     if (!JS_WrapValue(cx, rval))
         return false;
@@ -463,18 +464,19 @@ CreateObjectIn(JSContext *cx, HandleValu
     RootedObject obj(cx);
     {
         JSAutoCompartment ac(cx, scope);
         obj = JS_NewObject(cx, nullptr, JS::NullPtr(), scope);
         if (!obj)
             return false;
 
         if (define) {
-            if (!JS_DefinePropertyById(cx, scope, options.defineAs, obj, JSPROP_ENUMERATE,
-                                       JS_PropertyStub, JS_StrictPropertyStub))
+            if (!JS_DefinePropertyById(cx, scope, options.defineAs, obj,
+                                       JSPROP_ENUMERATE | JSPROP_PROPOP_ACCESSORS,
+                                       JS_STUBGETTER, JS_STUBSETTER))
                 return false;
         }
     }
 
     rval.setObject(*obj);
     if (!WrapperFactory::WaiveXrayAndWrap(cx, rval))
         return false;
 
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -438,18 +438,20 @@ sandbox_addProperty(JSContext *cx, Handl
     } else {
         if (!JS_CopyPropertyFrom(cx, id, unwrappedProto, obj))
             return false;
     }
 
     if (!JS_GetPropertyDescriptorById(cx, obj, id, &pd))
         return false;
     unsigned attrs = pd.attributes() & ~(JSPROP_GETTER | JSPROP_SETTER);
-    if (!JS_DefinePropertyById(cx, obj, id, vp, attrs,
-                               writeToProto_getProperty, writeToProto_setProperty))
+    if (!JS_DefinePropertyById(cx, obj, id, vp,
+                               attrs | JSPROP_PROPOP_ACCESSORS,
+                               JS_PROPERTYOP_GETTER(writeToProto_getProperty),
+                               JS_PROPERTYOP_SETTER(writeToProto_setProperty)))
         return false;
 
     return true;
 }
 
 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
 
 static const js::Class SandboxClass = {
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1498,18 +1498,18 @@ XRE_XPCShellMain(int argc, char **argv, 
 
             JS_SetPrivate(envobj, envp);
 
             nsAutoString workingDirectory;
             if (GetCurrentWorkingDirectory(workingDirectory))
                 gWorkingDirectory = &workingDirectory;
 
             JS_DefineProperty(cx, glob, "__LOCATION__", JS::UndefinedHandleValue,
-                              JSPROP_NATIVE_ACCESSORS | JSPROP_SHARED,
-                              JS_CAST_NATIVE_TO(GetLocationProperty, JSPropertyOp),
+                              JSPROP_SHARED,
+                              GetLocationProperty,
                               nullptr);
 
             // We are almost certainly going to run script here, so we need an
             // AutoEntryScript. This is Gecko-specific and not in any spec.
             dom::AutoEntryScript aes(backstagePass);
             result = ProcessArgs(aes.cx(), glob, argv, argc, &dirprovider);
 
             JS_DropPrincipals(rt, gJSPrincipals);
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -309,17 +309,17 @@ DefinePropertyIfFound(XPCCallContext& cc
 
             propFlags |= JSPROP_GETTER | JSPROP_SHARED;
             propFlags &= ~JSPROP_ENUMERATE;
 
             AutoResolveName arn(ccx, id);
             if (resolved)
                 *resolved = true;
             return JS_DefinePropertyById(ccx, obj, id, UndefinedHandleValue, propFlags,
-                                         JS_DATA_TO_FUNC_PTR(JSPropertyOp, funobj.get()),
+                                         JS_DATA_TO_FUNC_PTR(JSNative, funobj.get()),
                                          nullptr);
         }
 
         if (resolved)
             *resolved = false;
         return true;
     }
 
@@ -358,18 +358,23 @@ DefinePropertyIfFound(XPCCallContext& cc
         Rooted<JSPropertyDescriptor> desc(ccx);
         if (!xpc::Interpose(ccx, obj, iface->GetIID(), id, &desc))
             return false;
 
         if (desc.object()) {
             AutoResolveName arn(ccx, id);
             if (resolved)
                 *resolved = true;
-            return JS_DefinePropertyById(ccx, obj, id, desc.value(), desc.attributes(),
-                                         desc.getter(), desc.setter());
+            return JS_DefinePropertyById(ccx, obj, id, desc.value(),
+                                         // Descriptors never store JSNatives
+                                         // for accessors: they have either
+                                         // JSFunctions or JSPropertyOps.
+                                         desc.attributes(),
+                                         JS_PROPERTYOP_GETTER(desc.getter()),
+                                         JS_PROPERTYOP_SETTER(desc.setter()));
         }
     }
 
     if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_STRING) ||
         id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE) ||
         (scriptableInfo &&
          scriptableInfo->GetFlags().DontEnumQueryInterface() &&
          id == rt->GetStringID(XPCJSRuntime::IDX_QUERY_INTERFACE)))
@@ -388,21 +393,21 @@ DefinePropertyIfFound(XPCCallContext& cc
 
     // else...
 
     MOZ_ASSERT(member->IsAttribute(), "way broken!");
 
     propFlags |= JSPROP_GETTER | JSPROP_SHARED;
     propFlags &= ~JSPROP_READONLY;
     JSObject* funobj = funval.toObjectOrNull();
-    JSPropertyOp getter = JS_DATA_TO_FUNC_PTR(JSPropertyOp, funobj);
-    JSStrictPropertyOp setter;
+    JSNative getter = JS_DATA_TO_FUNC_PTR(JSNative, funobj);
+    JSNative setter;
     if (member->IsWritableAttribute()) {
         propFlags |= JSPROP_SETTER;
-        setter = JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, funobj);
+        setter = JS_DATA_TO_FUNC_PTR(JSNative, funobj);
     } else {
         setter = nullptr;
     }
 
     AutoResolveName arn(ccx, id);
     if (resolved)
         *resolved = true;
 
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -496,26 +496,25 @@ JSXrayTraits::resolveOwnProperty(JSConte
     for (const JSPropertySpec *ps = clasp->spec.prototypeProperties; ps && ps->name; ++ps) {
         if (PropertySpecNameEqualsId(ps->name, id)) {
             psMatch = ps;
             break;
         }
     }
     if (psMatch) {
         desc.value().setUndefined();
-        // Note that this is also kind of an abuse of JSPROP_NATIVE_ACCESSORS.
-        // See bug 992977.
         RootedFunction getterObj(cx);
         RootedFunction setterObj(cx);
         unsigned flags = psMatch->flags;
-        if (flags & JSPROP_NATIVE_ACCESSORS) {
-            desc.setGetter(psMatch->getter.propertyOp.op);
-            desc.setSetter(psMatch->setter.propertyOp.op);
+        if (!psMatch->isSelfHosted()) {
+            desc.setGetter(JS_CAST_NATIVE_TO(psMatch->getter.native.op,
+                                             JSPropertyOp));
+            desc.setSetter(JS_CAST_NATIVE_TO(psMatch->setter.native.op,
+                                             JSStrictPropertyOp));
         } else {
-            MOZ_ASSERT(flags & JSPROP_GETTER);
             getterObj = JS::GetSelfHostedFunction(cx, psMatch->getter.selfHosted.funname, id, 0);
             if (!getterObj)
                 return false;
             desc.setGetterObject(JS_GetFunctionObject(getterObj));
             if (psMatch->setter.selfHosted.funname) {
                 MOZ_ASSERT(flags & JSPROP_SETTER);
                 setterObj = JS::GetSelfHostedFunction(cx, psMatch->setter.selfHosted.funname, id, 0);
                 if (!setterObj)
@@ -529,18 +528,24 @@ JSXrayTraits::resolveOwnProperty(JSConte
         // This is broken, and will be fixed at some point, but for now we need to
         // cache the value explicitly. See the corresponding call to
         // JS_GetPropertyById at the top of this function.
         //
         // Note also that the public-facing API here doesn't give us a way to
         // pass along JITInfo. It's probably ok though, since Xrays are already
         // pretty slow.
         return JS_DefinePropertyById(cx, holder, id,
-                                     UndefinedHandleValue, desc.attributes(),
-                                     desc.getter(), desc.setter()) &&
+                                     UndefinedHandleValue,
+                                     // This particular descriptor, unlike most,
+                                     // actually stores JSNatives directly,
+                                     // since we just set it up.  Do NOT pass
+                                     // JSPROP_PROPOP_ACCESSORS here!
+                                     desc.attributes(),
+                                     JS_PROPERTYOP_GETTER(desc.getter()),
+                                     JS_PROPERTYOP_SETTER(desc.setter())) &&
                JS_GetPropertyDescriptorById(cx, holder, id, desc);
     }
 
     return true;
 }
 
 bool
 JSXrayTraits::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp)
@@ -608,18 +613,19 @@ JSXrayTraits::defineProperty(JSContext *
         }
         if (existingDesc.object() && existingDesc.object() != wrapper) {
             JS_ReportError(cx, "Not allowed to shadow non-own Xray-resolved property on [Object] or [Array] XrayWrapper");
             return false;
         }
 
         JSAutoCompartment ac(cx, target);
         if (!JS_WrapPropertyDescriptor(cx, desc) ||
-            !JS_DefinePropertyById(cx, target, id, desc.value(), desc.attributes(),
-                                   JS_PropertyStub, JS_StrictPropertyStub))
+            !JS_DefinePropertyById(cx, target, id, desc.value(),
+                                   desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                                   JS_STUBGETTER, JS_STUBSETTER))
         {
             return false;
         }
         *defined = true;
         return true;
     }
 
     return true;
@@ -721,17 +727,17 @@ JSXrayTraits::enumerateNames(JSContext *
         if (!MaybeAppend(id, flags, props))
             return false;
     }
     for (const JSPropertySpec *ps = clasp->spec.prototypeProperties; ps && ps->name; ++ps) {
         // We have code to Xray self-hosted accessors. But at present, there don't appear
         // to be any self-hosted accessors anywhere in SpiderMonkey, let alone in on an
         // Xrayable class, so we can't test it. Assert against it to make sure that we get
         // test coverage in test_XrayToJS.xul when the time comes.
-        MOZ_ASSERT(ps->flags & JSPROP_NATIVE_ACCESSORS,
+        MOZ_ASSERT(!ps->isSelfHosted(),
                    "Self-hosted accessor added to Xrayable class - ping the XPConnect "
                    "module owner about adding test coverage");
 
         jsid id;
         if (!PropertySpecNameToPermanentId(cx, ps->name, &id))
             return false;
         if (!MaybeAppend(id, flags, props))
             return false;
@@ -1176,18 +1182,23 @@ XPCWrappedNativeXrayTraits::resolveNativ
 
         JSFunction *toString = JS_NewFunction(cx, XrayToString, 0, 0, holder, "toString");
         if (!toString)
             return false;
 
         FillPropertyDescriptor(desc, wrapper, 0,
                                ObjectValue(*JS_GetFunctionObject(toString)));
 
-        return JS_DefinePropertyById(cx, holder, id, desc.value(), desc.attributes(),
-                                     desc.getter(), desc.setter()) &&
+        return JS_DefinePropertyById(cx, holder, id, desc.value(),
+                                     // Descriptors never store JSNatives for
+                                     // accessors: they have either JSFunctions
+                                     // or JSPropertyOps.
+                                     desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                                     JS_PROPERTYOP_GETTER(desc.getter()),
+                                     JS_PROPERTYOP_SETTER(desc.setter())) &&
                JS_GetPropertyDescriptorById(cx, holder, id, desc);
     }
 
     desc.object().set(holder);
     desc.setAttributes(JSPROP_ENUMERATE);
     desc.setGetter(nullptr);
     desc.setSetter(nullptr);
     desc.value().set(JSVAL_VOID);
@@ -1233,18 +1244,23 @@ XPCWrappedNativeXrayTraits::resolveNativ
         return false;
 
     if (desc.hasGetterObject())
         desc.setGetterObject(&fval.toObject());
     if (desc.hasSetterObject())
         desc.setSetterObject(&fval.toObject());
 
     // Define the property.
-    return JS_DefinePropertyById(cx, holder, id, desc.value(), desc.attributes(),
-                                 desc.getter(), desc.setter());
+    return JS_DefinePropertyById(cx, holder, id, desc.value(),
+                                 // Descriptors never store JSNatives for
+                                 // accessors: they have either JSFunctions or
+                                 // JSPropertyOps.
+                                 desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                                 JS_PROPERTYOP_GETTER(desc.getter()),
+                                 JS_PROPERTYOP_SETTER(desc.setter()));
 }
 
 static bool
 wrappedJSObject_getter(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.thisv().isObject()) {
         JS_ReportError(cx, "This value not an object");
@@ -1315,20 +1331,18 @@ XrayTraits::resolveOwnProperty(JSContext
     // Handle .wrappedJSObject for subsuming callers. This should move once we
     // sort out own-ness for the holder.
     if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT) &&
         AccessCheck::wrapperSubsumes(wrapper))
     {
         if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found))
             return false;
         if (!found && !JS_DefinePropertyById(cx, holder, id, UndefinedHandleValue,
-                                             JSPROP_ENUMERATE | JSPROP_SHARED |
-                                             JSPROP_NATIVE_ACCESSORS,
-                                             JS_CAST_NATIVE_TO(wrappedJSObject_getter,
-                                                               JSPropertyOp))) {
+                                             JSPROP_ENUMERATE | JSPROP_SHARED,
+                                             wrappedJSObject_getter)) {
             return false;
         }
         if (!JS_GetPropertyDescriptorById(cx, holder, id, desc))
             return false;
         desc.object().set(wrapper);
         return true;
     }
 
@@ -1529,18 +1543,23 @@ DOMXrayTraits::resolveOwnProperty(JSCont
     if (!XrayResolveOwnProperty(cx, wrapper, obj, id, desc, cacheOnHolder))
         return false;
 
     MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?");
 
     if (!desc.object() || !cacheOnHolder)
         return true;
 
-    return JS_DefinePropertyById(cx, holder, id, desc.value(), desc.attributes(),
-                                 desc.getter(), desc.setter()) &&
+    return JS_DefinePropertyById(cx, holder, id, desc.value(),
+                                 // Descriptors never store JSNatives for
+                                 // accessors: they have either JSFunctions or
+                                 // JSPropertyOps.
+                                 desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                                 JS_PROPERTYOP_GETTER(desc.getter()),
+                                 JS_PROPERTYOP_SETTER(desc.setter())) &&
            JS_GetPropertyDescriptorById(cx, holder, id, desc);
 }
 
 bool
 DOMXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                               MutableHandle<JSPropertyDescriptor> desc,
                               Handle<JSPropertyDescriptor> existingDesc, bool *defined)
 {
@@ -1849,18 +1868,23 @@ XrayWrapper<Base, Traits>::getPropertyDe
             return JS_WrapPropertyDescriptor(cx, desc);
         }
     }
 
     // If we still have nothing, we're done.
     if (!desc.object())
         return true;
 
-    if (!JS_DefinePropertyById(cx, holder, id, desc.value(), desc.attributes(),
-                               desc.getter(), desc.setter()) ||
+    if (!JS_DefinePropertyById(cx, holder, id, desc.value(),
+                               // Descriptors never store JSNatives for
+                               // accessors: they have either JSFunctions or
+                               // JSPropertyOps.
+                               desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                               JS_PROPERTYOP_GETTER(desc.getter()),
+                               JS_PROPERTYOP_SETTER(desc.setter())) ||
         !JS_GetPropertyDescriptorById(cx, holder, id, desc))
     {
         return false;
     }
     MOZ_ASSERT(desc.object());
     desc.object().set(wrapper);
     return true;
 }
@@ -1996,18 +2020,22 @@ XrayWrapper<Base, Traits>::definePropert
     if (!JS_WrapPropertyDescriptor(cx, &wrappedDesc))
         return false;
 
     // Fix up Xray waivers.
     if (!RecreateLostWaivers(cx, desc.address(), &wrappedDesc))
         return false;
 
     return JS_DefinePropertyById(cx, expandoObject, id, wrappedDesc.value(),
-                                 wrappedDesc.get().attrs,
-                                 wrappedDesc.getter(), wrappedDesc.setter());
+                                 // Descriptors never store JSNatives for
+                                 // accessors: they have either JSFunctions
+                                 // or JSPropertyOps.
+                                 wrappedDesc.get().attrs | JSPROP_PROPOP_ACCESSORS,
+                                 JS_PROPERTYOP_GETTER(wrappedDesc.getter()),
+                                 JS_PROPERTYOP_SETTER(wrappedDesc.setter()));
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::ownPropertyKeys(JSContext *cx, HandleObject wrapper,
                                            AutoIdVector &props) const
 {
     assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);