Bug 739380 - Begin to implement element definition. r=bhackett
authorJeff Walden <jwalden@mit.edu>
Wed, 21 Mar 2012 13:32:55 -0700
changeset 94176 e86e048872d6f2bf2f8cd34124c90d9f9336e572
parent 94175 c7ce0c86d5e86497deae66b8824dd51d2d6bbecd
child 94177 3c1d6080a98f082afb0369318192aef7bf1e5ed3
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [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 - Begin to implement element definition. r=bhackett
js/src/vm/ObjectImpl.cpp
js/src/vm/ObjectImpl.h
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -152,8 +152,159 @@ js::ObjectImpl::markChildren(JSTracer *t
     Class *clasp = shape_->getObjectClass();
     JSObject *obj = asObjectPtr();
     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)
+{
+    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)
+{
+    MOZ_ASSERT(this == &obj->elementsHeader());
+
+    /*
+     * If the new property doesn't have the right attributes, or an atypical
+     * getter or setter is being used, go sparse.
+     */
+    if (attrs != JSPROP_ENUMERATE ||
+        (attrs & (JSPROP_GETTER | JSPROP_SETTER)) || getter || setter)
+    {
+        if (!obj->makeElementsSparse(cx))
+            return false;
+        SparseElementsHeader &elts = obj->elementsHeader().asSparseElements();
+        return elts.defineElement(cx, obj, index, value, getter, setter, attrs);
+    }
+
+    /* If space for the dense element already exists, we only need set it. */
+    uint32_t initLen = initializedLength();
+    if (index < initLen) {
+        obj->elements[index].set(obj->asObjectPtr(), index, value);
+        return true;
+    }
+
+    /* 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);
+    }
+
+    /* 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);
+    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)
+{
+    /*
+     * XXX This isn't really a good error message, if this is even how typed
+     *     arrays should behave...
+     */
+    js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_OBJECT_NOT_EXTENSIBLE,
+                             JSDVG_IGNORE_STACK, ObjectValue(*(JSObject*)obj), // XXX
+                             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)
+{
+    MOZ_ASSERT(this == &obj->elementsHeader());
+
+    JSObject *delegate = ArrayBufferDelegate(cx, obj);
+    if (!delegate)
+        return false;
+    return DefineElement(cx, delegate, index, value, getter, setter, attrs);
+}
+
+bool
+js::DefineElement(JSContext *cx, ObjectImpl *obj, uint32_t index, const Value &value,
+                  PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+{
+    NEW_OBJECT_REPRESENTATION_ONLY();
+
+    ElementsHeader &header = obj->elementsHeader();
+
+    switch (header.kind()) {
+      case DenseElements:
+        return header.asDenseElements().defineElement(cx, obj, index, value, getter, setter,
+                                                      attrs);
+      case SparseElements:
+        return header.asSparseElements().defineElement(cx, obj, index, value, getter, setter,
+                                                       attrs);
+      case Uint8Elements:
+        return header.asUint8Elements().defineElement(cx, obj, index, value, getter, setter,
+                                                      attrs);
+      case Int8Elements:
+        return header.asInt8Elements().defineElement(cx, obj, index, value, getter, setter,
+                                                     attrs);
+      case Uint16Elements:
+        return header.asUint16Elements().defineElement(cx, obj, index, value, getter, setter,
+                                                       attrs);
+      case Int16Elements:
+        return header.asInt16Elements().defineElement(cx, obj, index, value, getter, setter,
+                                                      attrs);
+      case Uint32Elements:
+        return header.asUint32Elements().defineElement(cx, obj, index, value, getter, setter,
+                                                       attrs);
+      case Int32Elements:
+        return header.asInt32Elements().defineElement(cx, obj, index, value, getter, setter,
+                                                      attrs);
+      case Uint8ClampedElements:
+        return header.asUint8ClampedElements().defineElement(cx, obj, index, value,
+                                                             getter, setter, attrs);
+      case Float32Elements:
+        return header.asFloat32Elements().defineElement(cx, obj, index, value, getter, setter,
+                                                        attrs);
+      case Float64Elements:
+        return header.asFloat64Elements().defineElement(cx, obj, index, value, getter, setter,
+                                                        attrs);
+      case ArrayBufferElements:
+        return header.asArrayBufferElements().defineElement(cx, obj, index, value, getter, setter,
+                                                            attrs);
+    }
+
+    MOZ_NOT_REACHED("bad elements kind!");
+    return false;
+}
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -124,16 +124,20 @@ 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);
+
   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;
 };
 
@@ -145,58 +149,131 @@ 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);
+
   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;
 };
 
 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);
+
   private:
     TypedElementsHeader(const TypedElementsHeader &other) MOZ_DELETE;
     void operator=(const TypedElementsHeader &other) MOZ_DELETE;
 };
 
-class Uint8ElementsHeader : public TypedElementsHeader<uint8_t> { };
-class Int8ElementsHeader : public TypedElementsHeader<int8_t> { };
-class Uint16ElementsHeader : public TypedElementsHeader<uint16_t> { };
-class Int16ElementsHeader : public TypedElementsHeader<int16_t> { };
-class Uint32ElementsHeader : public TypedElementsHeader<uint32_t> { };
-class Int32ElementsHeader : public TypedElementsHeader<int32_t> { };
-class Float32ElementsHeader : public TypedElementsHeader<float> { };
-class Float64ElementsHeader : public TypedElementsHeader<double> { };
+class Uint8ElementsHeader : public TypedElementsHeader<uint8_t>
+{
+  private:
+    inline bool isUint8Elements() const MOZ_DELETE;
+    inline Uint8ElementsHeader & asUint8Elements() MOZ_DELETE;
+    Uint8ElementsHeader(const Uint8ElementsHeader &other) MOZ_DELETE;
+    void operator=(const Uint8ElementsHeader &other) MOZ_DELETE;
+};
+class Int8ElementsHeader : public TypedElementsHeader<int8_t>
+{
+  private:
+    bool isInt8Elements() const MOZ_DELETE;
+    Int8ElementsHeader & asInt8Elements() MOZ_DELETE;
+    Int8ElementsHeader(const Int8ElementsHeader &other) MOZ_DELETE;
+    void operator=(const Int8ElementsHeader &other) MOZ_DELETE;
+};
+class Uint16ElementsHeader : public TypedElementsHeader<uint16_t>
+{
+  private:
+    bool isUint16Elements() const MOZ_DELETE;
+    Uint16ElementsHeader & asUint16Elements() MOZ_DELETE;
+    Uint16ElementsHeader(const Uint16ElementsHeader &other) MOZ_DELETE;
+    void operator=(const Uint16ElementsHeader &other) MOZ_DELETE;
+};
+class Int16ElementsHeader : public TypedElementsHeader<int16_t>
+{
+  private:
+    bool isInt16Elements() const MOZ_DELETE;
+    Int16ElementsHeader & asInt16Elements() MOZ_DELETE;
+    Int16ElementsHeader(const Int16ElementsHeader &other) MOZ_DELETE;
+    void operator=(const Int16ElementsHeader &other) MOZ_DELETE;
+};
+class Uint32ElementsHeader : public TypedElementsHeader<uint32_t>
+{
+  private:
+    bool isUint32Elements() const MOZ_DELETE;
+    Uint32ElementsHeader & asUint32Elements() MOZ_DELETE;
+    Uint32ElementsHeader(const Uint32ElementsHeader &other) MOZ_DELETE;
+    void operator=(const Uint32ElementsHeader &other) MOZ_DELETE;
+};
+class Int32ElementsHeader : public TypedElementsHeader<int32_t>
+{
+  private:
+    bool isInt32Elements() const MOZ_DELETE;
+    Int32ElementsHeader & asInt32Elements() MOZ_DELETE;
+    Int32ElementsHeader(const Int32ElementsHeader &other) MOZ_DELETE;
+    void operator=(const Int32ElementsHeader &other) MOZ_DELETE;
+};
+class Float32ElementsHeader : public TypedElementsHeader<float>
+{
+  private:
+    bool isFloat32Elements() const MOZ_DELETE;
+    Float32ElementsHeader & asFloat32Elements() MOZ_DELETE;
+    Float32ElementsHeader(const Float32ElementsHeader &other) MOZ_DELETE;
+    void operator=(const Float32ElementsHeader &other) MOZ_DELETE;
+};
+class Float64ElementsHeader : public TypedElementsHeader<double>
+{
+  private:
+    bool isFloat64Elements() const MOZ_DELETE;
+    Float64ElementsHeader & asFloat64Elements() MOZ_DELETE;
+    Float64ElementsHeader(const Float64ElementsHeader &other) MOZ_DELETE;
+    void operator=(const Float64ElementsHeader &other) MOZ_DELETE;
+};
 
 class Uint8ClampedElementsHeader : public TypedElementsHeader<uint8_t>
 {
+  public:
+    bool defineElement(JSContext *cx, ObjectImpl *obj,
+                       uint32_t index, const Value &value,
+                       PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
+
   private:
     inline bool isUint8Clamped() const MOZ_DELETE;
     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);
+
   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;
 };
 
@@ -453,16 +530,23 @@ class ObjectImpl : public gc::Cell
     inline bool isDenseArray() const;
     inline bool isSlowArray() const;
     inline bool isArray() const;
 
     inline HeapSlotArray getDenseArrayElements();
     inline const Value & getDenseArrayElement(uint32_t idx);
     inline uint32_t getDenseArrayInitializedLength();
 
+    bool makeElementsSparse(JSContext *cx) {
+        NEW_OBJECT_REPRESENTATION_ONLY();
+
+        MOZ_NOT_REACHED("NYI");
+        return false;
+    }
+
   protected:
 #ifdef DEBUG
     void checkShapeConsistency();
 #else
     void checkShapeConsistency() { }
 #endif
 
   private:
@@ -514,16 +598,35 @@ class ObjectImpl : public gc::Cell
 
     /* Minimum size for dynamically allocated slots. */
     static const uint32_t SLOT_CAPACITY_MIN = 8;
 
     HeapSlot *fixedSlots() const {
         return reinterpret_cast<HeapSlot *>(uintptr_t(this) + sizeof(ObjectImpl));
     }
 
+    friend class ElementsHeader;
+    friend class DenseElementsHeader;
+    friend class SparseElementsHeader;
+
+    enum DenseElementsResult {
+        Failure,
+        ConvertToSparse,
+        Succeeded
+    };
+
+    DenseElementsResult ensureDenseElementsInitialized(JSContext *cx, uint32_t index,
+                                                       uint32_t extra)
+    {
+        NEW_OBJECT_REPRESENTATION_ONLY();
+
+        MOZ_NOT_REACHED("NYI");
+        return Failure;
+    }
+
     /*
      * These functions are currently public for simplicity; in the long run
      * it may make sense to make at least some of them private.
      */
 
   public:
     Shape * lastProperty() const {
         MOZ_ASSERT(shape_);
@@ -712,11 +815,15 @@ 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);
+
 } /* namespace js */
 
 #endif /* ObjectImpl_h__ */