Bug 1103368, part 3 - Ban stub getter/setter arguments to JSObject::define{Generic,Property,Element} and js::SetPropertyIgnoringNamedGetter. r=bhackett.
authorJason Orendorff <jorendorff@mozilla.com>
Sat, 22 Nov 2014 08:10:20 -0600
changeset 219196 f654193f2c2ef3c92f5ea8634d99b4faac2a15e4
parent 219195 5814a172384247c021279efd6d240a0576d27173
child 219197 8d0fdbf77f280d70d0e3356111bd59658cbef8ed
push id27958
push userkwierso@gmail.com
push dateFri, 12 Dec 2014 01:30:39 +0000
treeherdermozilla-central@5288b15d22de [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs1103368
milestone37.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 1103368, part 3 - Ban stub getter/setter arguments to JSObject::define{Generic,Property,Element} and js::SetPropertyIgnoringNamedGetter. r=bhackett.
js/src/builtin/Intl.cpp
js/src/builtin/TypedObject.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsbool.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsstr.cpp
js/src/proxy/BaseProxyHandler.cpp
js/src/proxy/Proxy.cpp
js/src/vm/DebuggerMemory.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
js/src/vm/NativeObject.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/SharedTypedArrayObject.cpp
js/src/vm/TypedArrayObject.cpp
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -452,18 +452,18 @@ intl_availableLocales(JSContext *cx, Cou
         if (!lang)
             return false;
         char *p;
         while ((p = strchr(lang, '_')))
             *p = '-';
         RootedAtom a(cx, Atomize(cx, lang, strlen(lang)));
         if (!a)
             return false;
-        if (!JSObject::defineProperty(cx, locales, a->asPropertyName(), t,
-                                      JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE))
+        if (!JSObject::defineProperty(cx, locales, a->asPropertyName(), t, nullptr, nullptr,
+                                      JSPROP_ENUMERATE))
         {
             return false;
         }
     }
 #endif
     result.setObject(*locales);
     return true;
 }
@@ -715,21 +715,18 @@ InitCollatorClass(JSContext *cx, HandleO
         return nullptr;
 
     // 10.2.1 and 10.3
     if (!IntlInitialize(cx, proto, cx->names().InitializeCollator, UndefinedHandleValue, options))
         return nullptr;
 
     // 8.1
     RootedValue ctorValue(cx, ObjectValue(*ctor));
-    if (!JSObject::defineProperty(cx, Intl, cx->names().Collator, ctorValue,
-                                  JS_PropertyStub, JS_StrictPropertyStub, 0))
-    {
+    if (!JSObject::defineProperty(cx, Intl, cx->names().Collator, ctorValue, nullptr, nullptr, 0))
         return nullptr;
-    }
 
     return ctor;
 }
 
 bool
 GlobalObject::initCollatorProto(JSContext *cx, Handle<GlobalObject*> global)
 {
     RootedNativeObject proto(cx, global->createBlankPrototype(cx, &CollatorClass));
@@ -1206,18 +1203,18 @@ InitNumberFormatClass(JSContext *cx, Han
         return nullptr;
 
     // 11.2.1 and 11.3
     if (!IntlInitialize(cx, proto, cx->names().InitializeNumberFormat, UndefinedHandleValue, options))
         return nullptr;
 
     // 8.1
     RootedValue ctorValue(cx, ObjectValue(*ctor));
-    if (!JSObject::defineProperty(cx, Intl, cx->names().NumberFormat, ctorValue,
-                                  JS_PropertyStub, JS_StrictPropertyStub, 0))
+    if (!JSObject::defineProperty(cx, Intl, cx->names().NumberFormat, ctorValue, nullptr, nullptr,
+                                  0))
     {
         return nullptr;
     }
 
     return ctor;
 }
 
 bool
@@ -1663,17 +1660,17 @@ InitDateTimeFormatClass(JSContext *cx, H
 
     // 12.2.1 and 12.3
     if (!IntlInitialize(cx, proto, cx->names().InitializeDateTimeFormat, UndefinedHandleValue, options))
         return nullptr;
 
     // 8.1
     RootedValue ctorValue(cx, ObjectValue(*ctor));
     if (!JSObject::defineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue,
-                                  JS_PropertyStub, JS_StrictPropertyStub, 0))
+                                  nullptr, nullptr, 0))
     {
         return nullptr;
     }
 
     return ctor;
 }
 
 bool
@@ -2038,21 +2035,18 @@ js_InitIntlClass(JSContext *cx, HandleOb
     // called with this being "the standard built-in Intl object". The global
     // object reserves slots to track standard built-in objects, but doesn't
     // normally keep references to non-constructors. This makes sure there is one.
     RootedObject Intl(cx, global->getOrCreateIntlObject(cx));
     if (!Intl)
         return nullptr;
 
     RootedValue IntlValue(cx, ObjectValue(*Intl));
-    if (!JSObject::defineProperty(cx, global, cx->names().Intl, IntlValue,
-                                  JS_PropertyStub, JS_StrictPropertyStub, 0))
-    {
+    if (!JSObject::defineProperty(cx, global, cx->names().Intl, IntlValue, nullptr, nullptr, 0))
         return nullptr;
-    }
 
     if (!JS_DefineFunctions(cx, Intl, intl_static_methods))
         return nullptr;
 
     if (!InitCollatorClass(cx, Intl, global))
         return nullptr;
     if (!InitNumberFormatClass(cx, Intl, global))
         return nullptr;
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -550,48 +550,40 @@ const JSFunctionSpec ArrayMetaTypeDescr:
 
 bool
 js::CreateUserSizeAndAlignmentProperties(JSContext *cx, HandleTypeDescr descr)
 {
     // If data is transparent, also store the public slots.
     if (descr->transparent()) {
         // byteLength
         RootedValue typeByteLength(cx, Int32Value(descr->size()));
-        if (!JSObject::defineProperty(cx, descr, cx->names().byteLength,
-                                      typeByteLength,
-                                      nullptr, nullptr,
-                                      JSPROP_READONLY | JSPROP_PERMANENT))
+        if (!JSObject::defineProperty(cx, descr, cx->names().byteLength, typeByteLength,
+                                      nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
 
         // byteAlignment
         RootedValue typeByteAlignment(cx, Int32Value(descr->alignment()));
-        if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment,
-                                      typeByteAlignment,
-                                      nullptr, nullptr,
-                                      JSPROP_READONLY | JSPROP_PERMANENT))
+        if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment, typeByteAlignment,
+                                      nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
     } else {
         // byteLength
-        if (!JSObject::defineProperty(cx, descr, cx->names().byteLength,
-                                      UndefinedHandleValue,
-                                      nullptr, nullptr,
-                                      JSPROP_READONLY | JSPROP_PERMANENT))
+        if (!JSObject::defineProperty(cx, descr, cx->names().byteLength, UndefinedHandleValue,
+                                      nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
 
         // byteAlignment
-        if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment,
-                                      UndefinedHandleValue,
-                                      nullptr, nullptr,
-                                      JSPROP_READONLY | JSPROP_PERMANENT))
+        if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment, UndefinedHandleValue,
+                                      nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
     }
 
     return true;
 }
 
@@ -615,26 +607,28 @@ ArrayMetaTypeDescr::create(JSContext *cx
     obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
     obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(elementType->alignment()));
     obj->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(size));
     obj->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(elementType->opaque()));
     obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE, ObjectValue(*elementType));
     obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_LENGTH, Int32Value(length));
 
     RootedValue elementTypeVal(cx, ObjectValue(*elementType));
-    if (!JSObject::defineProperty(cx, obj, cx->names().elementType,
-                                  elementTypeVal, nullptr, nullptr,
-                                  JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!JSObject::defineProperty(cx, obj, cx->names().elementType, elementTypeVal,
+                                  nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    {
         return nullptr;
+    }
 
     RootedValue lengthValue(cx, NumberValue(length));
-    if (!JSObject::defineProperty(cx, obj, cx->names().length,
-                                  lengthValue, nullptr, nullptr,
-                                  JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!JSObject::defineProperty(cx, obj, cx->names().length, lengthValue,
+                                  nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    {
         return nullptr;
+    }
 
     if (!CreateUserSizeAndAlignmentProperties(cx, obj))
         return nullptr;
 
     Rooted<TypedProto*> prototypeObj(cx);
     prototypeObj = CreatePrototypeObjectForComplexTypeInstance(cx, obj, arrayTypePrototype);
     if (!prototypeObj)
         return nullptr;
@@ -971,26 +965,24 @@ StructMetaTypeDescr::create(JSContext *c
     }
 
     // Create data properties fieldOffsets and fieldTypes
     if (!JSObject::freeze(cx, userFieldOffsets))
         return nullptr;
     if (!JSObject::freeze(cx, userFieldTypes))
         return nullptr;
     RootedValue userFieldOffsetsValue(cx, ObjectValue(*userFieldOffsets));
-    if (!JSObject::defineProperty(cx, descr, cx->names().fieldOffsets,
-                                  userFieldOffsetsValue, nullptr, nullptr,
-                                  JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!JSObject::defineProperty(cx, descr, cx->names().fieldOffsets, userFieldOffsetsValue,
+                                  nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
     RootedValue userFieldTypesValue(cx, ObjectValue(*userFieldTypes));
-    if (!JSObject::defineProperty(cx, descr, cx->names().fieldTypes,
-                                  userFieldTypesValue, nullptr, nullptr,
-                                  JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!JSObject::defineProperty(cx, descr, cx->names().fieldTypes, userFieldTypesValue,
+                                  nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
     if (!CreateUserSizeAndAlignmentProperties(cx, descr))
         return nullptr;
 
     Rooted<TypedProto*> prototypeObj(cx);
@@ -1238,21 +1230,21 @@ DefineMetaTypeDescr(JSContext *cx,
         return nullptr;
     RootedObject protoProto(cx);
     protoProto = NewObjectWithProto<PlainObject>(cx, objProto,
                                                  global, SingletonObject);
     if (!protoProto)
         return nullptr;
 
     RootedValue protoProtoValue(cx, ObjectValue(*protoProto));
-    if (!JSObject::defineProperty(cx, proto, cx->names().prototype,
-                                  protoProtoValue,
-                                  nullptr, nullptr,
-                                  JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!JSObject::defineProperty(cx, proto, cx->names().prototype, protoProtoValue,
+                                  nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    {
         return nullptr;
+    }
 
     // Create ctor itself
 
     const int constructorLength = 2;
     RootedFunction ctor(cx);
     ctor = global->createConstructor(cx, T::construct, className, constructorLength);
     if (!ctor ||
         !LinkConstructorAndPrototype(cx, ctor, proto) ||
@@ -1312,44 +1304,42 @@ GlobalObject::initTypedObjectModule(JSCo
 
     RootedObject arrayType(cx);
     arrayType = DefineMetaTypeDescr<ArrayMetaTypeDescr>(
         cx, "ArrayType", global, module, TypedObjectModuleObject::ArrayTypePrototype);
     if (!arrayType)
         return false;
 
     RootedValue arrayTypeValue(cx, ObjectValue(*arrayType));
-    if (!JSObject::defineProperty(cx, module, cx->names().ArrayType,
-                                  arrayTypeValue,
-                                  nullptr, nullptr,
-                                  JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!JSObject::defineProperty(cx, module, cx->names().ArrayType, arrayTypeValue,
+                                  nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    {
         return false;
+    }
 
     // StructType.
 
     RootedObject structType(cx);
     structType = DefineMetaTypeDescr<StructMetaTypeDescr>(
         cx, "StructType", global, module, TypedObjectModuleObject::StructTypePrototype);
     if (!structType)
         return false;
 
     RootedValue structTypeValue(cx, ObjectValue(*structType));
-    if (!JSObject::defineProperty(cx, module, cx->names().StructType,
-                                  structTypeValue,
-                                  nullptr, nullptr,
-                                  JSPROP_READONLY | JSPROP_PERMANENT))
+    if (!JSObject::defineProperty(cx, module, cx->names().StructType, structTypeValue,
+                                  nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
+    {
         return false;
+    }
 
     // Everything is setup, install module on the global object:
     RootedValue moduleValue(cx, ObjectValue(*module));
     global->setConstructor(JSProto_TypedObject, moduleValue);
-    if (!JSObject::defineProperty(cx, global, cx->names().TypedObject,
-                                  moduleValue,
-                                  nullptr, nullptr,
-                                  0))
+    if (!JSObject::defineProperty(cx, global, cx->names().TypedObject, moduleValue,
+                                  nullptr, nullptr, 0))
     {
         return false;
     }
 
     return module;
 }
 
 JSObject *
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1247,18 +1247,17 @@ JS_ResolveStandardClass(JSContext *cx, H
 
     RootedString idstr(cx, JSID_TO_STRING(id));
 
     /* Check whether we're resolving 'undefined', and define it if so. */
     JSAtom *undefinedAtom = cx->names().undefined;
     if (idstr == undefinedAtom) {
         *resolved = true;
         return JSObject::defineProperty(cx, obj, undefinedAtom->asPropertyName(),
-                                        UndefinedHandleValue,
-                                        JS_PropertyStub, JS_StrictPropertyStub,
+                                        UndefinedHandleValue, nullptr, nullptr,
                                         JSPROP_PERMANENT | JSPROP_READONLY);
     }
 
     /* Try for class constructors/prototypes named by well-known atoms. */
     stdnm = LookupStdName(rt, idstr, standard_class_names);
 
     /* Try less frequently used top-level functions and constants. */
     if (!stdnm)
@@ -2857,16 +2856,29 @@ DefinePropertyById(JSContext *cx, Handle
     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);
 
+    // In most places throughout the engine, a property with null getter and
+    // not JSPROP_GETTER/SETTER/SHARED has no getter, and the same for setters:
+    // it's just a plain old data property. However the JS_Define* APIs use
+    // null getter and setter to mean "default to the Class getProperty and
+    // setProperty ops".
+    if (!getter)
+        getter = obj->getClass()->getProperty;
+    if (!setter)
+        setter = obj->getClass()->setProperty;
+    if (getter == JS_PropertyStub)
+        getter = nullptr;
+    if (setter == JS_StrictPropertyStub)
+        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, Native getter, Native setter)
 {
     return DefinePropertyById(cx, obj, id, value,
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -458,18 +458,29 @@ array_length_getter(JSContext *cx, Handl
     } while (obj);
     return true;
 }
 
 static bool
 array_length_setter(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp)
 {
     if (!obj->is<ArrayObject>()) {
+        // This array .length property was found on the prototype
+        // chain. Ideally the setter should not have been called, but since
+        // we're here, do an impression of SetPropertyByDefining.
+        const Class *clasp = obj->getClass();
+        JSPropertyOp getter = clasp->getProperty;
+        if (getter == JS_PropertyStub)
+            getter = nullptr;
+        JSStrictPropertyOp setter = clasp->setProperty;
+        if (setter == JS_StrictPropertyStub)
+            setter = nullptr;
+
         return JSObject::defineProperty(cx, obj, cx->names().length, vp,
-                                        nullptr, nullptr, JSPROP_ENUMERATE);
+                                        getter, setter, JSPROP_ENUMERATE);
     }
 
     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
     MOZ_ASSERT(arr->lengthIsWritable(),
                "setter shouldn't be called if property is non-writable");
     return ArraySetLength<SequentialExecution>(cx, arr, id, JSPROP_PERMANENT, vp, strict);
 }
 
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -155,21 +155,18 @@ js_InitBooleanClass(JSContext *cx, Handl
     Handle<PropertyName*> valueOfName = cx->names().valueOf;
     RootedFunction
         valueOf(cx, NewFunction(cx, NullPtr(), bool_valueOf, 0, JSFunction::NATIVE_FUN,
                                 global, valueOfName));
     if (!valueOf)
         return nullptr;
 
     RootedValue value(cx, ObjectValue(*valueOf));
-    if (!JSObject::defineProperty(cx, booleanProto, valueOfName, value,
-                                  JS_PropertyStub, JS_StrictPropertyStub, 0))
-    {
+    if (!JSObject::defineProperty(cx, booleanProto, valueOfName, value, nullptr, nullptr, 0))
         return nullptr;
-    }
 
     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Boolean, ctor, booleanProto))
         return nullptr;
 
     return booleanProto;
 }
 
 JSString *
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -503,21 +503,18 @@ ErrorObject::createProto(JSContext *cx, 
     JSExnType type = ExnTypeFromProtoKey(key);
     if (!ErrorObject::init(cx, err, type, nullptr, emptyStr, emptyStr, 0, 0, emptyStr))
         return nullptr;
 
     // The various prototypes also have .name in addition to the normal error
     // instance properties.
     RootedPropertyName name(cx, ClassName(key, cx));
     RootedValue nameValue(cx, StringValue(name));
-    if (!JSObject::defineProperty(cx, err, cx->names().name, nameValue,
-                                  JS_PropertyStub, JS_StrictPropertyStub, 0))
-    {
+    if (!JSObject::defineProperty(cx, err, cx->names().name, nameValue, nullptr, nullptr, 0))
         return nullptr;
-    }
 
     return errorProto;
 }
 
 /* static */ JSObject *
 ErrorObject::createConstructor(JSContext *cx, JSProtoKey key)
 {
     RootedObject ctor(cx);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -414,31 +414,30 @@ ResolveInterpretedFunctionPrototype(JSCo
     RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, objProto,
                                                                      nullptr, SingletonObject));
     if (!proto)
         return nullptr;
 
     // Per ES5 15.3.5.2 a user-defined function's .prototype property is
     // initially non-configurable, non-enumerable, and writable.
     RootedValue protoVal(cx, ObjectValue(*proto));
-    if (!JSObject::defineProperty(cx, obj, cx->names().prototype,
-                                  protoVal, JS_PropertyStub, JS_StrictPropertyStub,
+    if (!JSObject::defineProperty(cx, obj, cx->names().prototype, protoVal, nullptr, nullptr,
                                   JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
     // Per ES5 13.2 the prototype's .constructor property is configurable,
     // non-enumerable, and writable.  However, per the 15 July 2013 ES6 draft,
     // section 15.19.3, the .prototype of a generator function does not link
     // back with a .constructor.
     if (!isStarGenerator) {
         RootedValue objVal(cx, ObjectValue(*obj));
-        if (!JSObject::defineProperty(cx, proto, cx->names().constructor,
-                                      objVal, JS_PropertyStub, JS_StrictPropertyStub, 0))
+        if (!JSObject::defineProperty(cx, proto, cx->names().constructor, objVal, nullptr, nullptr,
+                                      0))
         {
             return nullptr;
         }
     }
 
     return proto;
 }
 
@@ -2115,21 +2114,25 @@ js::DefineFunction(JSContext *cx, Handle
     if (flags & JSFUN_STUB_GSOPS) {
         /*
          * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
          * the defined property's attributes. This allows us to encode another,
          * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
          * for more on this.
          */
         flags &= ~JSFUN_STUB_GSOPS;
-        gop = JS_PropertyStub;
-        sop = JS_StrictPropertyStub;
-    } else {
         gop = nullptr;
         sop = nullptr;
+    } else {
+        gop = obj->getClass()->getProperty;
+        sop = obj->getClass()->setProperty;
+        if (gop == JS_PropertyStub)
+            gop = nullptr;
+        if (sop == JS_StrictPropertyStub)
+            sop = nullptr;
     }
 
     JSFunction::Flags funFlags;
     if (!native)
         funFlags = JSFunction::INTERPRETED_LAZY;
     else
         funFlags = JSAPIToJSFunctionFlags(flags);
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2421,18 +2421,17 @@ DefineStandardSlot(JSContext *cx, Handle
             if (!NativeObject::addProperty(cx, global, id, nullptr, nullptr, slot, attrs, 0))
                 return false;
 
             named = true;
             return true;
         }
     }
 
-    named = JSObject::defineGeneric(cx, obj, id,
-                                    v, JS_PropertyStub, JS_StrictPropertyStub, attrs);
+    named = JSObject::defineGeneric(cx, obj, id, v, nullptr, nullptr, attrs);
     return named;
 }
 
 static void
 SetClassObject(JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
 {
     MOZ_ASSERT(!obj->getParent());
     if (!obj->is<GlobalObject>())
@@ -2882,32 +2881,26 @@ 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(getter != JS_PropertyStub);
+    MOZ_ASSERT(setter != JS_StrictPropertyStub);
     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);
     }
-
-    if (getter == nullptr)
-        getter = obj->getClass()->getProperty;
-    if (setter == nullptr)
-        setter = obj->getClass()->setProperty;
-    if (getter == JS_PropertyStub)
-        getter = nullptr;
-    if (setter == JS_StrictPropertyStub)
-        setter = nullptr;
     return baseops::DefineGeneric(cx, obj.as<NativeObject>(), id, value, getter, setter, attrs);
 }
 
 /* static */ bool
 JSObject::defineProperty(ExclusiveContext *cx, HandleObject obj,
                          PropertyName *name, HandleValue value,
                          JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
@@ -2915,31 +2908,25 @@ JSObject::defineProperty(ExclusiveContex
     return defineGeneric(cx, obj, id, value, getter, setter, attrs);
 }
 
 /* static */ bool
 JSObject::defineElement(ExclusiveContext *cx, HandleObject obj,
                         uint32_t index, HandleValue value,
                         JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
+    MOZ_ASSERT(getter != JS_PropertyStub);
+    MOZ_ASSERT(setter != JS_StrictPropertyStub);
+
     js::DefineElementOp op = obj->getOps()->defineElement;
     if (op) {
         if (!cx->shouldBeJSContext())
             return false;
         return op(cx->asJSContext(), obj, index, value, getter, setter, attrs);
     }
-
-    if (!getter)
-        getter = obj->getClass()->getProperty;
-    if (!setter)
-        setter = obj->getClass()->setProperty;
-    if (getter == JS_PropertyStub)
-        getter = nullptr;
-    if (setter == JS_StrictPropertyStub)
-        setter = nullptr;
     return baseops::DefineElement(cx, obj.as<NativeObject>(), index, value, getter, setter, attrs);
 }
 
 /* static */ bool
 JSObject::lookupGeneric(JSContext *cx, HandleObject obj, js::HandleId id,
                         MutableHandleObject objp, MutableHandleShape propp)
 {
     /* NB: The logic of lookupGeneric is implicitly reflected in
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -593,30 +593,30 @@ class JSObject : public js::gc::Cell
         return lookupGeneric(cx, obj, id, objp, propp);
     }
 
     static inline bool lookupElement(JSContext *cx, js::HandleObject obj, uint32_t index,
                                      js::MutableHandleObject objp, js::MutableHandleShape propp);
 
     static bool defineGeneric(js::ExclusiveContext *cx, js::HandleObject obj,
                               js::HandleId id, js::HandleValue value,
-                              JSPropertyOp getter = JS_PropertyStub,
-                              JSStrictPropertyOp setter = JS_StrictPropertyStub,
+                              JSPropertyOp getter = nullptr,
+                              JSStrictPropertyOp setter = nullptr,
                               unsigned attrs = JSPROP_ENUMERATE);
 
     static bool defineProperty(js::ExclusiveContext *cx, js::HandleObject obj,
                                js::PropertyName *name, js::HandleValue value,
-                               JSPropertyOp getter = JS_PropertyStub,
-                               JSStrictPropertyOp setter = JS_StrictPropertyStub,
+                               JSPropertyOp getter = nullptr,
+                               JSStrictPropertyOp setter = nullptr,
                                unsigned attrs = JSPROP_ENUMERATE);
 
     static bool defineElement(js::ExclusiveContext *cx, js::HandleObject obj,
                               uint32_t index, js::HandleValue value,
-                              JSPropertyOp getter = JS_PropertyStub,
-                              JSStrictPropertyOp setter = JS_StrictPropertyStub,
+                              JSPropertyOp getter = nullptr,
+                              JSStrictPropertyOp setter = nullptr,
                               unsigned attrs = JSPROP_ENUMERATE);
 
     static inline bool getGeneric(JSContext *cx, js::HandleObject obj, js::HandleObject receiver,
                                   js::HandleId id, js::MutableHandleValue vp);
 
     static inline bool getGenericNoGC(JSContext *cx, JSObject *obj, JSObject *receiver,
                                       jsid id, js::Value *vp);
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -382,22 +382,18 @@ str_enumerate(JSContext *cx, HandleObjec
 {
     RootedString str(cx, obj->as<StringObject>().unbox());
     RootedValue value(cx);
     for (size_t i = 0, length = str->length(); i < length; i++) {
         JSString *str1 = NewDependentString(cx, str, i, 1);
         if (!str1)
             return false;
         value.setString(str1);
-        if (!JSObject::defineElement(cx, obj, i, value,
-                                     JS_PropertyStub, JS_StrictPropertyStub,
-                                     STRING_ELEMENT_ATTRS))
-        {
+        if (!JSObject::defineElement(cx, obj, i, value, nullptr, nullptr, STRING_ELEMENT_ATTRS))
             return false;
-        }
     }
 
     return true;
 }
 
 bool
 js::str_resolve(JSContext *cx, HandleObject obj, HandleId id, bool *resolvedp)
 {
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -127,17 +127,27 @@ BaseProxyHandler::set(JSContext *cx, Han
         if (!HasOwnProperty(cx, receiver, id, &existingDescriptor))
             return false;
 
         // Steps 5.e-f.
         unsigned attrs =
             existingDescriptor
             ? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT
             : JSPROP_ENUMERATE;
-        return JSObject::defineGeneric(cx, receiver, id, vp, nullptr, nullptr, attrs);
+
+        // A very old nonstandard SpiderMonkey extension: default to the Class
+        // getter and setter ops.
+        const Class *clasp = receiver->getClass();
+        PropertyOp getter = clasp->getProperty;
+        if (getter == JS_PropertyStub)
+            getter = nullptr;
+        setter = clasp->setProperty;
+        if (setter == JS_StrictPropertyStub)
+            setter = nullptr;
+        return JSObject::defineGeneric(cx, receiver, id, vp, getter, setter, attrs);
     }
 
     // Step 6.
     MOZ_ASSERT(ownDesc.isAccessorDescriptor());
     RootedObject setter(cx);
     if (ownDesc.hasSetterObject())
         setter = ownDesc.setterObject();
     if (!setter)
@@ -148,66 +158,39 @@ BaseProxyHandler::set(JSContext *cx, Han
 
 bool
 js::SetPropertyIgnoringNamedGetter(JSContext *cx, const BaseProxyHandler *handler,
                                    HandleObject proxy, HandleObject receiver,
                                    HandleId id, MutableHandle<PropertyDescriptor> desc,
                                    bool descIsOwn, bool strict, MutableHandleValue vp)
 {
     /* The control-flow here differs from ::get() because of the fall-through case below. */
-    if (descIsOwn) {
-        MOZ_ASSERT(desc.object());
-
+    MOZ_ASSERT_IF(descIsOwn, desc.object());
+    if (desc.object()) {
         // Check for read-only properties.
-        if (desc.isReadonly())
-            return strict ? Throw(cx, id, JSMSG_READ_ONLY) : true;
-        if (!desc.setter()) {
-            // Be wary of the odd explicit undefined setter case possible through
-            // Object.defineProperty.
-            if (!desc.hasSetterObject())
-                desc.setSetter(JS_StrictPropertyStub);
-        } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) {
+        if (desc.isReadonly()) {
+            if (strict)
+                return Throw(cx, id, descIsOwn ? JSMSG_READ_ONLY : JSMSG_CANT_REDEFINE_PROP);
+            return true;
+        }
+
+        MOZ_ASSERT(desc.getter() != JS_PropertyStub);
+        MOZ_ASSERT(desc.setter() != JS_StrictPropertyStub);
+        if (desc.hasSetterObject() || desc.setter()) {
             if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp))
                 return false;
             if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != handler)
                 return true;
             if (desc.isShared())
                 return true;
         }
-        if (!desc.getter()) {
-            // Same as above for the null setter case.
-            if (!desc.hasGetterObject())
-                desc.setGetter(JS_PropertyStub);
-        }
         desc.value().set(vp.get());
-        return handler->defineProperty(cx, receiver, id, desc);
-    }
-    if (desc.object()) {
-        // Check for read-only properties.
-        if (desc.isReadonly())
-            return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true;
-        if (!desc.setter()) {
-            // Be wary of the odd explicit undefined setter case possible through
-            // Object.defineProperty.
-            if (!desc.hasSetterObject())
-                desc.setSetter(JS_StrictPropertyStub);
-        } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) {
-            if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp))
-                return false;
-            if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != handler)
-                return true;
-            if (desc.isShared())
-                return true;
-        }
-        if (!desc.getter()) {
-            // Same as above for the null setter case.
-            if (!desc.hasGetterObject())
-                desc.setGetter(JS_PropertyStub);
-        }
-        desc.value().set(vp.get());
+
+        if (descIsOwn)
+            return handler->defineProperty(cx, receiver, id, desc);
         return JSObject::defineGeneric(cx, receiver, id, desc.value(),
                                        desc.getter(), desc.setter(), desc.attributes());
     }
     desc.object().set(receiver);
     desc.value().set(vp.get());
     desc.setAttributes(JSPROP_ENUMERATE);
     desc.setGetter(nullptr);
     desc.setSetter(nullptr); // Pick up the class getter/setter.
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -347,17 +347,24 @@ Proxy::set(JSContext *cx, HandleObject p
     }
 
     // Ok. Either there was no pre-existing property, or it was a value prop
     // that we're going to shadow. Either way, define a new own property.
     unsigned attrs =
         (desc.object() == proxy)
         ? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT
         : JSPROP_ENUMERATE;
-    return JSObject::defineGeneric(cx, receiver, id, vp, nullptr, nullptr, attrs);
+    const Class *clasp = receiver->getClass();
+    JSPropertyOp getter = clasp->getProperty;
+    if (getter == JS_PropertyStub)
+        getter = nullptr;
+    JSStrictPropertyOp setter = clasp->setProperty;
+    if (setter == JS_StrictPropertyStub)
+        setter = nullptr;
+    return JSObject::defineGeneric(cx, receiver, id, vp, getter, setter, attrs);
 }
 
 bool
 Proxy::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
     JS_CHECK_RECURSION(cx, return false);
     const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -448,17 +448,17 @@ class ByJSType {
 
         RootedValue stringsReport(cx);
         if (!strings.report(census, &stringsReport) ||
             !JSObject::defineProperty(cx, obj, cx->names().strings, stringsReport))
             return false;
 
         RootedValue otherReport(cx);
         if (!other.report(census, &otherReport) ||
-            !JSObject::defineProperty(cx, obj, cx->names().other,   otherReport))
+            !JSObject::defineProperty(cx, obj, cx->names().other, otherReport))
             return false;
 
         report.setObject(*obj);
         return true;
     }
 };
 
 // An assorter that categorizes nodes that are JSObjects by their class, and
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -274,17 +274,17 @@ GlobalObject::valueIsEval(Value val)
     return eval.isObject() && eval == val;
 }
 
 /* static */ bool
 GlobalObject::initStandardClasses(JSContext *cx, Handle<GlobalObject*> global)
 {
     /* Define a top-level property 'undefined' with the undefined value. */
     if (!JSObject::defineProperty(cx, global, cx->names().undefined, UndefinedHandleValue,
-                                  JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY))
+                                  nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY))
     {
         return false;
     }
 
     for (size_t k = 0; k < JSProto_LIMIT; ++k) {
         if (!ensureConstructor(cx, global, static_cast<JSProtoKey>(k)))
             return false;
     }
@@ -321,18 +321,17 @@ InitBareBuiltinCtor(JSContext *cx, Handl
  * or functions.
  */
 /* static */ bool
 GlobalObject::initSelfHostingBuiltins(JSContext *cx, Handle<GlobalObject*> global,
                                       const JSFunctionSpec *builtins)
 {
     // Define a top-level property 'undefined' with the undefined value.
     if (!JSObject::defineProperty(cx, global, cx->names().undefined, UndefinedHandleValue,
-                                  JS_PropertyStub, JS_StrictPropertyStub,
-                                  JSPROP_PERMANENT | JSPROP_READONLY))
+                                  nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY))
     {
         return false;
     }
 
     // Define a top-level property 'std_iterator' with the name of the method
     // used by for-of loops to create an iterator.
     RootedValue std_iterator(cx);
 #ifdef JS_HAS_SYMBOLS
@@ -430,21 +429,20 @@ GlobalObject::createBlankPrototypeInheri
 bool
 js::LinkConstructorAndPrototype(JSContext *cx, JSObject *ctor_, JSObject *proto_)
 {
     RootedObject ctor(cx, ctor_), proto(cx, proto_);
 
     RootedValue protoVal(cx, ObjectValue(*proto));
     RootedValue ctorVal(cx, ObjectValue(*ctor));
 
-    return JSObject::defineProperty(cx, ctor, cx->names().prototype,
-                                    protoVal, JS_PropertyStub, JS_StrictPropertyStub,
-                                    JSPROP_PERMANENT | JSPROP_READONLY) &&
-           JSObject::defineProperty(cx, proto, cx->names().constructor,
-                                    ctorVal, JS_PropertyStub, JS_StrictPropertyStub, 0);
+    return JSObject::defineProperty(cx, ctor, cx->names().prototype, protoVal,
+                                    nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY) &&
+           JSObject::defineProperty(cx, proto, cx->names().constructor, ctorVal,
+                                    nullptr, nullptr, 0);
 }
 
 bool
 js::DefinePropertiesAndFunctions(JSContext *cx, HandleObject obj,
                                  const JSPropertySpec *ps, const JSFunctionSpec *fs)
 {
     if (ps && !JS_DefineProperties(cx, obj, ps))
         return false;
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -341,18 +341,19 @@ DefVarOrConstOperation(JSContext *cx, Ha
 
     RootedShape prop(cx);
     RootedObject obj2(cx);
     if (!JSObject::lookupProperty(cx, varobj, dn, &obj2, &prop))
         return false;
 
     /* Steps 8c, 8d. */
     if (!prop || (obj2 != varobj && varobj->is<GlobalObject>())) {
-        if (!JSObject::defineProperty(cx, varobj, dn, UndefinedHandleValue, JS_PropertyStub,
-                                      JS_StrictPropertyStub, attrs)) {
+        if (!JSObject::defineProperty(cx, varobj, dn, UndefinedHandleValue, nullptr, nullptr,
+                                      attrs))
+        {
             return false;
         }
     } else if (attrs & JSPROP_READONLY) {
         /*
          * Extension: ordinarily we'd be done here -- but for |const|.  If we
          * see a redeclaration that's |const|, we consider it a conflict.
          */
         unsigned oldAttrs;
@@ -552,16 +553,18 @@ TypeOfObjectOperation(JSObject *obj, JSR
     JSType type = js::TypeOfObject(obj);
     return TypeName(type, *rt->commonNames);
 }
 
 static MOZ_ALWAYS_INLINE bool
 InitElemOperation(JSContext *cx, HandleObject obj, HandleValue idval, HandleValue val)
 {
     MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
+    MOZ_ASSERT(obj->getClass()->getProperty == JS_PropertyStub);
+    MOZ_ASSERT(obj->getClass()->setProperty == JS_StrictPropertyStub);
 
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, idval, &id))
         return false;
 
     return JSObject::defineGeneric(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE);
 }
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3694,28 +3694,24 @@ js::DefFunOperation(JSContext *cx, Handl
      * ECMA requires functions defined when entering Eval code to be
      * impermanent.
      */
     unsigned attrs = script->isActiveEval()
                      ? JSPROP_ENUMERATE
                      : JSPROP_ENUMERATE | JSPROP_PERMANENT;
 
     /* Steps 5d, 5f. */
-    if (!shape || pobj != parent) {
-        return JSObject::defineProperty(cx, parent, name, rval, JS_PropertyStub,
-                                        JS_StrictPropertyStub, attrs);
-    }
+    if (!shape || pobj != parent)
+        return JSObject::defineProperty(cx, parent, name, rval, nullptr, nullptr, attrs);
 
     /* Step 5e. */
     MOZ_ASSERT(parent->isNative());
     if (parent->is<GlobalObject>()) {
-        if (shape->configurable()) {
-            return JSObject::defineProperty(cx, parent, name, rval, JS_PropertyStub,
-                                            JS_StrictPropertyStub, attrs);
-        }
+        if (shape->configurable())
+            return JSObject::defineProperty(cx, parent, name, rval, nullptr, nullptr, attrs);
 
         if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) {
             JSAutoByteString bytes;
             if (AtomToPrintableString(cx, name, &bytes)) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REDEFINE_PROP,
                                      bytes.ptr());
             }
 
@@ -3954,21 +3950,21 @@ js::InitGetterSetterOperation(JSContext 
     PropertyOp getter;
     StrictPropertyOp setter;
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
 
     JSOp op = JSOp(*pc);
 
     if (op == JSOP_INITPROP_GETTER || op == JSOP_INITELEM_GETTER) {
         getter = CastAsPropertyOp(val);
-        setter = JS_StrictPropertyStub;
+        setter = nullptr;
         attrs |= JSPROP_GETTER;
     } else {
         MOZ_ASSERT(op == JSOP_INITPROP_SETTER || op == JSOP_INITELEM_SETTER);
-        getter = JS_PropertyStub;
+        getter = nullptr;
         setter = CastAsStrictPropertyOp(val);
         attrs |= JSPROP_SETTER;
     }
 
     RootedValue scratch(cx);
     return JSObject::defineGeneric(cx, obj, id, scratch, getter, setter, attrs);
 }
 
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -391,18 +391,17 @@ InitGetterSetterOperation(JSContext *cx,
 
 bool
 SpreadCallOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue thisv,
                     HandleValue callee, HandleValue arr, MutableHandleValue res);
 
 inline bool
 SetConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName name, HandleValue rval)
 {
-    return JSObject::defineProperty(cx, varobj, name, rval,
-                                    JS_PropertyStub, JS_StrictPropertyStub,
+    return JSObject::defineProperty(cx, varobj, name, rval, nullptr, nullptr,
                                     JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
 }
 
 void
 ReportUninitializedLexical(JSContext *cx, HandlePropertyName name);
 
 void
 ReportUninitializedLexical(JSContext *cx, HandleScript script, jsbytecode *pc);
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -2020,29 +2020,29 @@ SetPropertyByDefining(typename Execution
             return false;
     } else {
         // Purge the property cache of now-shadowed id in receiver's scope chain.
         if (!PurgeScopeChain(cxArg->asJSContext(), receiver, id))
             return false;
     }
 
     // Define the new data property.
-    if (!receiver->is<NativeObject>()) {
-        if (mode == ParallelExecution)
-            return false;
-        return JSObject::defineGeneric(cxArg->asJSContext(), receiver, id, v,
-                                       clasp->getProperty, clasp->setProperty, JSPROP_ENUMERATE);
-    }
-    Rooted<NativeObject*> nativeReceiver(cxArg, &receiver->as<NativeObject>());
     JSPropertyOp getter = clasp->getProperty;
     if (getter == JS_PropertyStub)
         getter = nullptr;
     JSStrictPropertyOp setter = clasp->setProperty;
     if (setter == JS_StrictPropertyStub)
         setter = nullptr;
+    if (!receiver->is<NativeObject>()) {
+        if (mode == ParallelExecution)
+            return false;
+        return JSObject::defineGeneric(cxArg->asJSContext(), receiver, id, v, getter, setter,
+                                       JSPROP_ENUMERATE);
+    }
+    Rooted<NativeObject*> nativeReceiver(cxArg, &receiver->as<NativeObject>());
     return DefinePropertyOrElement<mode>(cxArg, nativeReceiver, id, getter, setter,
                                          JSPROP_ENUMERATE, v, true, strict);
 }
 
 /*
  * Implement "the rest of" assignment to a property when no property receiver[id]
  * was found anywhere on the prototype chain.
  *
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -212,17 +212,17 @@ intrinsic_MakeConstructible(JSContext *c
     MOZ_ASSERT(args[0].isObject());
     MOZ_ASSERT(args[0].toObject().is<JSFunction>());
     MOZ_ASSERT(args[1].isObject());
 
     // Normal .prototype properties aren't enumerable.  But for this to clone
     // correctly, it must be enumerable.
     RootedObject ctor(cx, &args[0].toObject());
     if (!JSObject::defineProperty(cx, ctor, cx->names().prototype, args[1],
-                                  JS_PropertyStub, JS_StrictPropertyStub,
+                                  nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT))
     {
         return false;
     }
 
     ctor->as<JSFunction>().setIsSelfHostedConstructor();
     args.rval().setUndefined();
     return true;
--- a/js/src/vm/SharedTypedArrayObject.cpp
+++ b/js/src/vm/SharedTypedArrayObject.cpp
@@ -743,24 +743,20 @@ IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPP
 template<typename NativeType>
 bool
 SharedTypedArrayObjectTemplate<NativeType>::FinishClassInit(JSContext *cx,
                                                             HandleObject ctor,
                                                             HandleObject proto)
 {
     RootedValue bytesValue(cx, Int32Value(BYTES_PER_ELEMENT));
 
-    if (!JSObject::defineProperty(cx, ctor,
-                                  cx->names().BYTES_PER_ELEMENT, bytesValue,
-                                  JS_PropertyStub, JS_StrictPropertyStub,
-                                  JSPROP_PERMANENT | JSPROP_READONLY) ||
-        !JSObject::defineProperty(cx, proto,
-                                  cx->names().BYTES_PER_ELEMENT, bytesValue,
-                                  JS_PropertyStub, JS_StrictPropertyStub,
-                                  JSPROP_PERMANENT | JSPROP_READONLY))
+    if (!JSObject::defineProperty(cx, ctor, cx->names().BYTES_PER_ELEMENT, bytesValue,
+                                  nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY) ||
+        !JSObject::defineProperty(cx, proto, cx->names().BYTES_PER_ELEMENT, bytesValue,
+                                  nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY))
     {
         return false;
     }
 
     return true;
 };
 
 IMPL_SHARED_TYPED_ARRAY_STATICS(Int8Array)
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -237,24 +237,20 @@ class TypedArrayObjectTemplate : public 
         return NewFunction(cx, ctorObj, class_constructor, 3, JSFunction::NATIVE_CTOR, global,
                            ClassName(key, cx), JSFunction::FinalizeKind);
     }
 
     static bool
     finishClassInit(JSContext *cx, HandleObject ctor, HandleObject proto)
     {
         RootedValue bytesValue(cx, Int32Value(BYTES_PER_ELEMENT));
-        if (!JSObject::defineProperty(cx, ctor,
-                                      cx->names().BYTES_PER_ELEMENT, bytesValue,
-                                      JS_PropertyStub, JS_StrictPropertyStub,
-                                      JSPROP_PERMANENT | JSPROP_READONLY) ||
-            !JSObject::defineProperty(cx, proto,
-                                      cx->names().BYTES_PER_ELEMENT, bytesValue,
-                                      JS_PropertyStub, JS_StrictPropertyStub,
-                                      JSPROP_PERMANENT | JSPROP_READONLY))
+        if (!JSObject::defineProperty(cx, ctor, cx->names().BYTES_PER_ELEMENT, bytesValue,
+                                      nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY) ||
+            !JSObject::defineProperty(cx, proto, cx->names().BYTES_PER_ELEMENT, bytesValue,
+                                      nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY))
         {
             return false;
         }
 
         RootedFunction fun(cx);
         fun = NewFunction(cx, NullPtr(), ArrayBufferObject::createTypedArrayFromBuffer<NativeType>,
                           0, JSFunction::NATIVE_FUN, cx->global(), NullPtr());
         if (!fun)