Bug 739380 - Convert the defineElement hooks to use an ECMAScript-style [[DefineOwnProperty]] interface, not the old-school SpiderMonkey defineProperty interface. r=bhackett
authorJeff Walden <jwalden@mit.edu>
Mon, 09 Apr 2012 15:15:46 -0700
changeset 91640 92e87abb646b56b804779c049cf128ce76818867
parent 91639 ec68c96fb9dc4b974190627ae482c270e2e67ec7
child 91641 4a6a021e38cab64c8910ac4fa39ae81e9b484a9a
push id22465
push usermak77@bonardo.net
push dateSat, 14 Apr 2012 11:58:29 +0000
treeherdermozilla-central@6880c195054f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs739380
milestone14.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 739380 - Convert the defineElement hooks to use an ECMAScript-style [[DefineOwnProperty]] interface, not the old-school SpiderMonkey defineProperty interface. r=bhackett
js/src/jsobj.h
js/src/vm/ObjectImpl.cpp
js/src/vm/ObjectImpl.h
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -70,28 +70,16 @@ namespace js {
 class AutoPropDescArrayRooter;
 class ProxyHandler;
 class CallObject;
 struct GCMarker;
 struct NativeIterator;
 
 namespace mjit { class Compiler; }
 
-static inline PropertyOp
-CastAsPropertyOp(JSObject *object)
-{
-    return JS_DATA_TO_FUNC_PTR(PropertyOp, object);
-}
-
-static inline StrictPropertyOp
-CastAsStrictPropertyOp(JSObject *object)
-{
-    return JS_DATA_TO_FUNC_PTR(StrictPropertyOp, object);
-}
-
 inline JSObject *
 CastAsObject(PropertyOp op)
 {
     return JS_FUNC_TO_DATA_PTR(JSObject *, op);
 }
 
 inline JSObject *
 CastAsObject(StrictPropertyOp op)
@@ -122,125 +110,16 @@ CastAsObjectJsval(StrictPropertyOp op)
      (JSPropertyOp)getter, NULL}
 #define JS_PSGS(name,getter,setter,flags)                                     \
     {name, 0, (flags) | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS,              \
      (JSPropertyOp)getter, (JSStrictPropertyOp)setter}
 #define JS_PS_END {0, 0, 0, 0, 0}
 
 /******************************************************************************/
 
-/*
- * A representation of ECMA-262 ed. 5's internal Property Descriptor data
- * structure.
- */
-struct PropDesc {
-    /*
-     * Original object from which this descriptor derives, passed through for
-     * the benefit of proxies.
-     */
-    js::Value pd;
-
-    js::Value value, get, set;
-
-    /* Property descriptor boolean fields. */
-    uint8_t attrs;
-
-    /* Bits indicating which values are set. */
-    bool hasGet : 1;
-    bool hasSet : 1;
-    bool hasValue : 1;
-    bool hasWritable : 1;
-    bool hasEnumerable : 1;
-    bool hasConfigurable : 1;
-
-    friend class js::AutoPropDescArrayRooter;
-
-    PropDesc();
-
-    /*
-     * 8.10.5 ToPropertyDescriptor(Obj)
-     *
-     * If checkAccessors is false, skip steps 7.b and 8.b, which throw a
-     * TypeError if .get or .set is neither a callable object nor undefined.
-     *
-     * (DebuggerObject_defineProperty uses this: the .get and .set properties
-     * are expected to be Debugger.Object wrappers of functions, which are not
-     * themselves callable.)
-     */
-    bool initialize(JSContext* cx, const js::Value &v, bool checkAccessors=true);
-
-    /*
-     * 8.10.4 FromPropertyDescriptor(Desc)
-     *
-     * initFromPropertyDescriptor sets pd to undefined and populates all the
-     * other fields of this PropDesc from desc.
-     *
-     * makeObject populates pd based on the other fields of *this, creating a
-     * new property descriptor JSObject and defining properties on it.
-     */
-    void initFromPropertyDescriptor(const PropertyDescriptor &desc);
-    bool makeObject(JSContext *cx);
-
-    /* 8.10.1 IsAccessorDescriptor(desc) */
-    bool isAccessorDescriptor() const {
-        return hasGet || hasSet;
-    }
-
-    /* 8.10.2 IsDataDescriptor(desc) */
-    bool isDataDescriptor() const {
-        return hasValue || hasWritable;
-    }
-
-    /* 8.10.3 IsGenericDescriptor(desc) */
-    bool isGenericDescriptor() const {
-        return !isAccessorDescriptor() && !isDataDescriptor();
-    }
-
-    bool configurable() const {
-        return (attrs & JSPROP_PERMANENT) == 0;
-    }
-
-    bool enumerable() const {
-        return (attrs & JSPROP_ENUMERATE) != 0;
-    }
-
-    bool writable() const {
-        return (attrs & JSPROP_READONLY) == 0;
-    }
-
-    JSObject* getterObject() const {
-        return get.isUndefined() ? NULL : &get.toObject();
-    }
-    JSObject* setterObject() const {
-        return set.isUndefined() ? NULL : &set.toObject();
-    }
-
-    const js::Value &getterValue() const {
-        return get;
-    }
-    const js::Value &setterValue() const {
-        return set;
-    }
-
-    PropertyOp getter() const {
-        return js::CastAsPropertyOp(getterObject());
-    }
-    StrictPropertyOp setter() const {
-        return js::CastAsStrictPropertyOp(setterObject());
-    }
-
-    /*
-     * Throw a TypeError if a getter/setter is present and is neither callable
-     * nor undefined. These methods do exactly the type checks that are skipped
-     * by passing false as the checkAccessors parameter of initialize.
-     */
-    inline bool checkGetter(JSContext *cx);
-    inline bool checkSetter(JSContext *cx);
-};
-
 typedef Vector<PropDesc, 1> PropDescArray;
 
 } /* namespace js */
 
 /*
  * On success, and if id was found, return true with *objp non-null and with a
  * property of *objp stored in *propp. If successful but id was not found,
  * return true with both *objp and *propp null.
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -163,157 +163,180 @@ js::ObjectImpl::markChildren(JSTracer *t
     if (clasp->trace)
         clasp->trace(trc, obj);
 
     if (shape_->isNative())
         MarkObjectSlots(trc, obj, 0, obj->slotSpan());
 }
 
 bool
-js::SparseElementsHeader::defineElement(JSContext *cx, ObjectImpl *obj,
-                                        uint32_t index, const Value &value,
-                                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+SparseElementsHeader::defineElement(JSContext *cx, ObjectImpl *obj, uint32_t index,
+                                    const PropDesc &desc, bool shouldThrow, bool *succeeded)
 {
     MOZ_ASSERT(this == &obj->elementsHeader());
 
     MOZ_NOT_REACHED("NYI");
     return false;
 }
 
 bool
-js::DenseElementsHeader::defineElement(JSContext *cx, ObjectImpl *obj,
-                                       uint32_t index, const Value &value,
-                                       PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+DenseElementsHeader::defineElement(JSContext *cx, ObjectImpl *obj, uint32_t index,
+                                   const PropDesc &desc, bool shouldThrow, bool *succeeded)
 {
     MOZ_ASSERT(this == &obj->elementsHeader());
 
+    MOZ_ASSERT_IF(desc.hasGet || desc.hasSet, !desc.hasValue && !desc.hasWritable);
+    MOZ_ASSERT_IF(desc.hasValue || desc.hasWritable, !desc.hasGet && !desc.hasSet);
+
     /*
-     * If the new property doesn't have the right attributes, or an atypical
-     * getter or setter is being used, go sparse.
+     * If desc is an accessor descriptor or a data descriptor with atypical
+     * attributes, convert to sparse and retry.
      */
-    if (attrs != JSPROP_ENUMERATE ||
-        (attrs & (JSPROP_GETTER | JSPROP_SETTER)) || getter || setter)
+    if (desc.hasGet || desc.hasSet ||
+        (desc.hasEnumerable && !desc.enumerable()) ||
+        (desc.hasConfigurable && !desc.configurable()) ||
+        (desc.hasWritable && !desc.writable()))
     {
         if (!obj->makeElementsSparse(cx))
             return false;
         SparseElementsHeader &elts = obj->elementsHeader().asSparseElements();
-        return elts.defineElement(cx, obj, index, value, getter, setter, attrs);
+        return elts.defineElement(cx, obj, index, desc, shouldThrow, succeeded);
     }
 
-    /* If space for the dense element already exists, we only need set it. */
+    /* Does the element exist?  All behavior depends upon this. */
     uint32_t initLen = initializedLength();
     if (index < initLen) {
-        obj->elements[index].set(obj->asObjectPtr(), index, value);
-        return true;
+        HeapSlot &slot = obj->elements[index];
+        if (!slot.isMagic(JS_ARRAY_HOLE)) {
+            /*
+             * The element exists with attributes { [[Enumerable]]: true,
+             * [[Configurable]]: true, [[Writable]]: true, [[Value]]: slot }.
+             */
+            // XXX jwalden fill this in!
+        }
+    }
+
+    /*
+     * If the element doesn't exist, we can only add it if the object is
+     * extensible.
+     */
+    if (!obj->isExtensible()) {
+        *succeeded = false;
+        if (!shouldThrow)
+            return true;
+        MOZ_ALWAYS_FALSE(js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_OBJECT_NOT_EXTENSIBLE,
+                                                  JSDVG_IGNORE_STACK,
+                                                  ObjectValue(*obj->asObjectPtr()),
+                                                  NULL, NULL, NULL));
+        return false;
     }
 
     /* Otherwise we ensure space for it exists and that it's initialized. */
     ObjectImpl::DenseElementsResult res = obj->ensureDenseElementsInitialized(cx, index, 0);
 
     /* Propagate any error. */
     if (res == ObjectImpl::Failure)
         return false;
 
     /* Otherwise, if the index was too far out of range, go sparse. */
     if (res == ObjectImpl::ConvertToSparse) {
         if (!obj->makeElementsSparse(cx))
             return false;
         SparseElementsHeader &elts = obj->elementsHeader().asSparseElements();
-        return elts.defineElement(cx, obj, index, value, getter, setter, attrs);
+        return elts.defineElement(cx, obj, index, desc, shouldThrow, succeeded);
     }
 
     /* But if we were able to ensure the element's existence, we're good. */
     MOZ_ASSERT(res == ObjectImpl::Succeeded);
-    obj->elements[index].set(obj->asObjectPtr(), index, value);
+    obj->elements[index].set(obj->asObjectPtr(), index, desc.value);
+    *succeeded = true;
     return true;
 }
 
 static JSObject *
 ArrayBufferDelegate(JSContext *cx, ObjectImpl *obj)
 {
     MOZ_ASSERT(obj->hasClass(&ArrayBufferClass));
     if (obj->getPrivate())
         return static_cast<JSObject *>(obj->getPrivate());
     JSObject *delegate = NewObjectWithGivenProto(cx, &ObjectClass, obj->getProto(), NULL);
     obj->setPrivate(delegate);
     return delegate;
 }
 
 template <typename T>
 bool
-js::TypedElementsHeader<T>::defineElement(JSContext *cx, ObjectImpl *obj,
-                                       uint32_t index, const Value &value,
-                                       PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+TypedElementsHeader<T>::defineElement(JSContext *cx, ObjectImpl *obj,
+                                      uint32_t index, const PropDesc &desc, bool shouldThrow,
+                                      bool *succeeded)
 {
-    /*
-     * XXX This isn't really a good error message, if this is even how typed
-     *     arrays should behave...
-     */
+    /* XXX jwalden This probably isn't how typed arrays should behave... */
+    *succeeded = false;
     js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_OBJECT_NOT_EXTENSIBLE,
-                             JSDVG_IGNORE_STACK, ObjectValue(*(JSObject*)obj), // XXX
+                             JSDVG_IGNORE_STACK,
+                             ObjectValue(*(JSObject*)obj), // XXX jwalden dodgy cast
                              NULL, NULL, NULL);
     return false;
 }
 
 bool
-js::ArrayBufferElementsHeader::defineElement(JSContext *cx, ObjectImpl *obj,
-                                             uint32_t index, const Value &value,
-                                             PropertyOp getter, StrictPropertyOp setter,
-                                             unsigned attrs)
+ArrayBufferElementsHeader::defineElement(JSContext *cx, ObjectImpl *obj,
+                                         uint32_t index, const PropDesc &desc, bool shouldThrow,
+                                         bool *succeeded)
 {
     MOZ_ASSERT(this == &obj->elementsHeader());
 
     JSObject *delegate = ArrayBufferDelegate(cx, obj);
     if (!delegate)
         return false;
-    return DefineElement(cx, delegate, index, value, getter, setter, attrs);
+    return DefineElement(cx, delegate, index, desc, shouldThrow, succeeded);
 }
 
 bool
-js::DefineElement(JSContext *cx, ObjectImpl *obj, uint32_t index, const Value &value,
-                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+js::DefineElement(JSContext *cx, ObjectImpl *obj, uint32_t index, const PropDesc &desc,
+                  bool shouldThrow, bool *succeeded)
 {
     NEW_OBJECT_REPRESENTATION_ONLY();
 
     ElementsHeader &header = obj->elementsHeader();
 
     switch (header.kind()) {
       case DenseElements:
-        return header.asDenseElements().defineElement(cx, obj, index, value, getter, setter,
-                                                      attrs);
+        return header.asDenseElements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                      succeeded);
       case SparseElements:
-        return header.asSparseElements().defineElement(cx, obj, index, value, getter, setter,
-                                                       attrs);
+        return header.asSparseElements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                       succeeded);
       case Uint8Elements:
-        return header.asUint8Elements().defineElement(cx, obj, index, value, getter, setter,
-                                                      attrs);
+        return header.asUint8Elements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                      succeeded);
       case Int8Elements:
-        return header.asInt8Elements().defineElement(cx, obj, index, value, getter, setter,
-                                                     attrs);
+        return header.asInt8Elements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                     succeeded);
       case Uint16Elements:
-        return header.asUint16Elements().defineElement(cx, obj, index, value, getter, setter,
-                                                       attrs);
+        return header.asUint16Elements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                       succeeded);
       case Int16Elements:
-        return header.asInt16Elements().defineElement(cx, obj, index, value, getter, setter,
-                                                      attrs);
+        return header.asInt16Elements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                      succeeded);
       case Uint32Elements:
-        return header.asUint32Elements().defineElement(cx, obj, index, value, getter, setter,
-                                                       attrs);
+        return header.asUint32Elements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                       succeeded);
       case Int32Elements:
-        return header.asInt32Elements().defineElement(cx, obj, index, value, getter, setter,
-                                                      attrs);
+        return header.asInt32Elements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                      succeeded);
       case Uint8ClampedElements:
-        return header.asUint8ClampedElements().defineElement(cx, obj, index, value,
-                                                             getter, setter, attrs);
+        return header.asUint8ClampedElements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                             succeeded);
       case Float32Elements:
-        return header.asFloat32Elements().defineElement(cx, obj, index, value, getter, setter,
-                                                        attrs);
+        return header.asFloat32Elements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                        succeeded);
       case Float64Elements:
-        return header.asFloat64Elements().defineElement(cx, obj, index, value, getter, setter,
-                                                        attrs);
+        return header.asFloat64Elements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                        succeeded);
       case ArrayBufferElements:
-        return header.asArrayBufferElements().defineElement(cx, obj, index, value, getter, setter,
-                                                            attrs);
+        return header.asArrayBufferElements().defineElement(cx, obj, index, desc, shouldThrow,
+                                                            succeeded);
     }
 
     MOZ_NOT_REACHED("bad elements kind!");
     return false;
 }
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -16,16 +16,139 @@
 #include "jsval.h"
 
 #include "gc/Barrier.h"
 
 namespace js {
 
 class ObjectImpl;
 
+class AutoPropDescArrayRooter;
+
+static inline PropertyOp
+CastAsPropertyOp(JSObject *object)
+{
+    return JS_DATA_TO_FUNC_PTR(PropertyOp, object);
+}
+
+static inline StrictPropertyOp
+CastAsStrictPropertyOp(JSObject *object)
+{
+    return JS_DATA_TO_FUNC_PTR(StrictPropertyOp, object);
+}
+
+/*
+ * A representation of ECMA-262 ed. 5's internal Property Descriptor data
+ * structure.
+ */
+struct PropDesc {
+    /*
+     * Original object from which this descriptor derives, passed through for
+     * the benefit of proxies.
+     */
+    Value pd;
+
+    Value value, get, set;
+
+    /* Property descriptor boolean fields. */
+    uint8_t attrs;
+
+    /* Bits indicating which values are set. */
+    bool hasGet : 1;
+    bool hasSet : 1;
+    bool hasValue : 1;
+    bool hasWritable : 1;
+    bool hasEnumerable : 1;
+    bool hasConfigurable : 1;
+
+    friend class AutoPropDescArrayRooter;
+
+    PropDesc();
+
+    /*
+     * 8.10.5 ToPropertyDescriptor(Obj)
+     *
+     * If checkAccessors is false, skip steps 7.b and 8.b, which throw a
+     * TypeError if .get or .set is neither a callable object nor undefined.
+     *
+     * (DebuggerObject_defineProperty uses this: the .get and .set properties
+     * are expected to be Debugger.Object wrappers of functions, which are not
+     * themselves callable.)
+     */
+    bool initialize(JSContext* cx, const Value &v, bool checkAccessors = true);
+
+    /*
+     * 8.10.4 FromPropertyDescriptor(Desc)
+     *
+     * initFromPropertyDescriptor sets pd to undefined and populates all the
+     * other fields of this PropDesc from desc.
+     *
+     * makeObject populates pd based on the other fields of *this, creating a
+     * new property descriptor JSObject and defining properties on it.
+     */
+    void initFromPropertyDescriptor(const PropertyDescriptor &desc);
+    bool makeObject(JSContext *cx);
+
+    /* 8.10.1 IsAccessorDescriptor(desc) */
+    bool isAccessorDescriptor() const {
+        return hasGet || hasSet;
+    }
+
+    /* 8.10.2 IsDataDescriptor(desc) */
+    bool isDataDescriptor() const {
+        return hasValue || hasWritable;
+    }
+
+    /* 8.10.3 IsGenericDescriptor(desc) */
+    bool isGenericDescriptor() const {
+        return !isAccessorDescriptor() && !isDataDescriptor();
+    }
+
+    bool configurable() const {
+        return (attrs & JSPROP_PERMANENT) == 0;
+    }
+
+    bool enumerable() const {
+        return (attrs & JSPROP_ENUMERATE) != 0;
+    }
+
+    bool writable() const {
+        return (attrs & JSPROP_READONLY) == 0;
+    }
+
+    JSObject* getterObject() const {
+        return get.isUndefined() ? NULL : &get.toObject();
+    }
+    JSObject* setterObject() const {
+        return set.isUndefined() ? NULL : &set.toObject();
+    }
+
+    const Value &getterValue() const {
+        return get;
+    }
+    const Value &setterValue() const {
+        return set;
+    }
+
+    PropertyOp getter() const {
+        return CastAsPropertyOp(getterObject());
+    }
+    StrictPropertyOp setter() const {
+        return CastAsStrictPropertyOp(setterObject());
+    }
+
+    /*
+     * Throw a TypeError if a getter/setter is present and is neither callable
+     * nor undefined. These methods do exactly the type checks that are skipped
+     * by passing false as the checkAccessors parameter of initialize.
+     */
+    inline bool checkGetter(JSContext *cx);
+    inline bool checkSetter(JSContext *cx);
+};
+
 class DenseElementsHeader;
 class SparseElementsHeader;
 class Uint8ElementsHeader;
 class Int8ElementsHeader;
 class Uint16ElementsHeader;
 class Int16ElementsHeader;
 class Uint32ElementsHeader;
 class Int32ElementsHeader;
@@ -124,19 +247,18 @@ class DenseElementsHeader : public Eleme
         return dense.initializedLength;
     }
 
     uint32_t length() const {
         MOZ_ASSERT(ElementsHeader::isDenseElements());
         return ElementsHeader::length;
     }
 
-    bool defineElement(JSContext *cx, ObjectImpl *obj,
-                       uint32_t index, const Value &value,
-                       PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+    bool defineElement(JSContext *cx, ObjectImpl *obj, uint32_t index, const PropDesc &desc,
+                       bool shouldThrow, bool *succeeded);
 
   private:
     inline bool isDenseElements() const MOZ_DELETE;
     inline DenseElementsHeader & asDenseElements() MOZ_DELETE;
 
     DenseElementsHeader(const DenseElementsHeader &other) MOZ_DELETE;
     void operator=(const DenseElementsHeader &other) MOZ_DELETE;
 };
@@ -149,19 +271,18 @@ class SparseElementsHeader : public Elem
         return sparse.shape;
     }
 
     uint32_t length() const {
         MOZ_ASSERT(ElementsHeader::isSparseElements());
         return ElementsHeader::length;
     }
 
-    bool defineElement(JSContext *cx, ObjectImpl *obj,
-                       uint32_t index, const Value &value,
-                       PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+    bool defineElement(JSContext *cx, ObjectImpl *obj, uint32_t index, const PropDesc &desc,
+                       bool shouldThrow, bool *succeeded);
 
   private:
     inline bool isSparseElements() const MOZ_DELETE;
     inline SparseElementsHeader & asSparseElements() MOZ_DELETE;
 
     SparseElementsHeader(const SparseElementsHeader &other) MOZ_DELETE;
     void operator=(const SparseElementsHeader &other) MOZ_DELETE;
 };
@@ -258,19 +379,18 @@ template<> inline const bool TypeIsUint8
 template <typename T>
 class TypedElementsHeader : public ElementsHeader
 {
   public:
     uint32_t byteLength() const {
         return ElementsHeader::length;
     }
 
-    bool defineElement(JSContext *cx, ObjectImpl *obj,
-                       uint32_t index, const Value &value,
-                       PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+    bool defineElement(JSContext *cx, ObjectImpl *obj, uint32_t index, const PropDesc &desc,
+                       bool shouldThrow, bool *succeeded);
 
   private:
     TypedElementsHeader(const TypedElementsHeader &other) MOZ_DELETE;
     void operator=(const TypedElementsHeader &other) MOZ_DELETE;
 };
 
 class Uint8ElementsHeader : public TypedElementsHeader<uint8_t>
 {
@@ -344,19 +464,18 @@ class Uint8ClampedElementsHeader : publi
     inline Uint8ClampedElementsHeader & asUint8ClampedElements() MOZ_DELETE;
     Uint8ClampedElementsHeader(const Uint8ClampedElementsHeader &other) MOZ_DELETE;
     void operator=(const Uint8ClampedElementsHeader &other) MOZ_DELETE;
 };
 
 class ArrayBufferElementsHeader : public ElementsHeader
 {
   public:
-    bool defineElement(JSContext *cx, ObjectImpl *obj,
-                       uint32_t index, const Value &value,
-                       PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+    bool defineElement(JSContext *cx, ObjectImpl *obj, uint32_t index, const PropDesc &desc,
+                       bool shouldThrow, bool *succeeded);
 
   private:
     inline bool isArrayBufferElements() const MOZ_DELETE;
     inline ArrayBufferElementsHeader & asArrayBufferElements() MOZ_DELETE;
 
     ArrayBufferElementsHeader(const ArrayBufferElementsHeader &other) MOZ_DELETE;
     void operator=(const ArrayBufferElementsHeader &other) MOZ_DELETE;
 };
@@ -904,14 +1023,14 @@ class ObjectImpl : public gc::Cell
     static size_t getFixedSlotOffset(size_t slot) {
         return sizeof(ObjectImpl) + slot * sizeof(Value);
     }
     static size_t getPrivateDataOffset(size_t nfixed) { return getFixedSlotOffset(nfixed); }
     static size_t offsetOfSlots() { return offsetof(ObjectImpl, slots); }
 };
 
 extern bool
-DefineElement(JSContext *cx, ObjectImpl *obj, uint32_t index, const Value &value,
-              PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+DefineElement(JSContext *cx, ObjectImpl *obj, uint32_t index, const PropDesc &desc,
+              bool shouldThrow, bool *succeeded);
 
 } /* namespace js */
 
 #endif /* ObjectImpl_h__ */