Bug 739380 - Start to implement the default [[GetP]] hook from the proto-climbing refactoring for the various element types. r=bhackett
authorJeff Walden <jwalden@mit.edu>
Tue, 10 Apr 2012 16:33:44 -0700
changeset 92583 9bab3e0dd69d7c6628fb7f8983a5e4acb6566980
parent 92582 ef7803bad0b1f468850b50f3c4c009c848d3db6b
child 92584 e586bd795354e0c98a79499062eb083658eb8f69
push id22544
push useremorley@mozilla.com
push dateFri, 27 Apr 2012 11:53:27 +0000
treeherdermozilla-central@d871849ac3a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs739380
milestone15.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 - Start to implement the default [[GetP]] hook from the proto-climbing refactoring for the various element types. r=bhackett
js/src/jsapi.h
js/src/vm/ObjectImpl.cpp
js/src/vm/ObjectImpl.h
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -54,16 +54,18 @@
 #include "jspubtd.h"
 #include "jsutil.h"
 #include "jsval.h"
 
 #include "js/Utility.h"
 #include "gc/Root.h"
 
 #ifdef __cplusplus
+#include <limits> /* for std::numeric_limits */
+
 #include "jsalloc.h"
 #include "js/Vector.h"
 #endif
 
 /************************************************************************/
 
 /* JS::Value can store a full int32_t. */
 #define JSVAL_INT_BITS          32
@@ -699,24 +701,114 @@ static JS_ALWAYS_INLINE Value
 MagicValue(JSWhyMagic why)
 {
     Value v;
     v.setMagic(why);
     return v;
 }
 
 static JS_ALWAYS_INLINE Value
+NumberValue(float f)
+{
+    Value v;
+    v.setNumber(f);
+    return v;
+}
+
+static JS_ALWAYS_INLINE Value
 NumberValue(double dbl)
 {
     Value v;
     v.setNumber(dbl);
     return v;
 }
 
 static JS_ALWAYS_INLINE Value
+NumberValue(int8_t i)
+{
+    return Int32Value(i);
+}
+
+static JS_ALWAYS_INLINE Value
+NumberValue(uint8_t i)
+{
+    return Int32Value(i);
+}
+
+static JS_ALWAYS_INLINE Value
+NumberValue(int16_t i)
+{
+    return Int32Value(i);
+}
+
+static JS_ALWAYS_INLINE Value
+NumberValue(uint16_t i)
+{
+    return Int32Value(i);
+}
+
+static JS_ALWAYS_INLINE Value
+NumberValue(int32_t i)
+{
+    return Int32Value(i);
+}
+
+static JS_ALWAYS_INLINE Value
+NumberValue(uint32_t i)
+{
+    Value v;
+    v.setNumber(i);
+    return v;
+}
+
+namespace detail {
+
+template <bool Signed>
+class MakeNumberValue
+{
+  public:
+    template<typename T>
+    static inline Value create(const T t)
+    {
+        Value v;
+        if (JSVAL_INT_MIN <= t && t <= JSVAL_INT_MAX)
+            v.setInt32(int32_t(t));
+        else
+            v.setDouble(double(t));
+        return v;
+    }
+};
+
+template <>
+class MakeNumberValue<false>
+{
+  public:
+    template<typename T>
+    static inline Value create(const T t)
+    {
+        Value v;
+        if (t <= JSVAL_INT_MAX)
+            v.setInt32(int32_t(t));
+        else
+            v.setDouble(double(t));
+        return v;
+    }
+};
+
+} /* namespace detail */
+
+template <typename T>
+static JS_ALWAYS_INLINE Value
+NumberValue(const T t)
+{
+    MOZ_ASSERT(T(double(t)) == t, "value creation would be lossy");
+    return detail::MakeNumberValue<std::numeric_limits<T>::is_signed>::create(t);
+}
+
+static JS_ALWAYS_INLINE Value
 ObjectOrNullValue(JSObject *obj)
 {
     Value v;
     v.setObjectOrNull(obj);
     return v;
 }
 
 static JS_ALWAYS_INLINE Value
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -6,16 +6,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 
 #include "jsscope.h"
 #include "jsobjinlines.h"
 
+#include "js/TemplateLib.h"
+
 #include "Debugger.h"
 #include "ObjectImpl.h"
 
 #include "gc/Barrier-inl.h"
 
 #include "ObjectImpl-inl.h"
 
 using namespace js;
@@ -284,16 +286,73 @@ js::ObjectImpl::markChildren(JSTracer *t
     if (clasp->trace)
         clasp->trace(trc, obj);
 
     if (shape_->isNative())
         MarkObjectSlots(trc, obj, 0, obj->slotSpan());
 }
 
 bool
+DenseElementsHeader::getOwnElement(JSContext *cx, ObjectImpl *obj, uint32_t index, PropDesc *desc)
+{
+    MOZ_ASSERT(this == &obj->elementsHeader());
+
+    uint32_t len = initializedLength();
+    if (index >= len) {
+        *desc = PropDesc::undefined();
+        return true;
+    }
+
+    HeapSlot &slot = obj->elements[index];
+    if (slot.isMagic(JS_ARRAY_HOLE)) {
+        *desc = PropDesc::undefined();
+        return true;
+    }
+
+    *desc = PropDesc(slot, PropDesc::Writable, PropDesc::Enumerable, PropDesc::Configurable);
+    return true;
+}
+
+bool
+SparseElementsHeader::getOwnElement(JSContext *cx, ObjectImpl *obj, uint32_t index, PropDesc *desc)
+{
+    MOZ_ASSERT(this == &obj->elementsHeader());
+
+    MOZ_NOT_REACHED("NYI");
+    return false;
+}
+
+template<typename T>
+bool
+TypedElementsHeader<T>::getOwnElement(JSContext *cx, ObjectImpl *obj, uint32_t index,
+                                      PropDesc *desc)
+{
+    MOZ_ASSERT(this == &obj->elementsHeader());
+
+    if (index >= length()) {
+        *desc = PropDesc::undefined();
+        return true;
+    }
+
+    *desc = PropDesc(NumberValue(getElement(index)), PropDesc::Writable,
+                     PropDesc::Enumerable, PropDesc::Configurable);
+    return false;
+}
+
+bool
+ArrayBufferElementsHeader::getOwnElement(JSContext *cx, ObjectImpl *obj, uint32_t index,
+                                         PropDesc *desc)
+{
+    MOZ_ASSERT(this == &obj->elementsHeader());
+
+    MOZ_NOT_REACHED("NYI");
+    return false;
+}
+
+bool
 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;
 }
@@ -340,17 +399,17 @@ DenseElementsHeader::defineElement(JSCon
      * 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()),
+                                                  ObjectValue(*obj),
                                                   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. */
@@ -388,17 +447,17 @@ bool
 TypedElementsHeader<T>::defineElement(JSContext *cx, ObjectImpl *obj,
                                       uint32_t index, const PropDesc &desc, bool shouldThrow,
                                       bool *succeeded)
 {
     /* 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 jwalden dodgy cast
+                             ObjectValue(*obj),
                              NULL, NULL, NULL);
     return false;
 }
 
 bool
 ArrayBufferElementsHeader::defineElement(JSContext *cx, ObjectImpl *obj,
                                          uint32_t index, const PropDesc &desc, bool shouldThrow,
                                          bool *succeeded)
@@ -407,16 +466,118 @@ ArrayBufferElementsHeader::defineElement
 
     JSObject *delegate = ArrayBufferDelegate(cx, obj);
     if (!delegate)
         return false;
     return DefineElement(cx, delegate, index, desc, shouldThrow, succeeded);
 }
 
 bool
+js::GetElement(JSContext *cx, ObjectImpl *obj, ObjectImpl *receiver, uint32_t index,
+               Value *vp)
+{
+    NEW_OBJECT_REPRESENTATION_ONLY();
+
+    do {
+        MOZ_ASSERT(obj);
+
+        if (static_cast<JSObject *>(obj)->isProxy()) { // XXX
+            MOZ_NOT_REACHED("NYI: proxy [[GetP]]");
+            return false;
+        }
+
+        ElementsHeader &header = obj->elementsHeader();
+
+        bool res;
+        PropDesc desc;
+        switch (header.kind()) {
+          case DenseElements:
+            res = header.asDenseElements().getOwnElement(cx, obj, index, &desc);
+            break;
+          case SparseElements:
+            res = header.asSparseElements().getOwnElement(cx, obj, index, &desc);
+            break;
+          case Uint8Elements:
+            res = header.asUint8Elements().getOwnElement(cx, obj, index, &desc);
+            break;
+          case Int8Elements:
+            res = header.asInt8Elements().getOwnElement(cx, obj, index, &desc);
+            break;
+          case Uint16Elements:
+            res = header.asUint16Elements().getOwnElement(cx, obj, index, &desc);
+            break;
+          case Int16Elements:
+            res = header.asInt16Elements().getOwnElement(cx, obj, index, &desc);
+            break;
+          case Uint32Elements:
+            res = header.asUint32Elements().getOwnElement(cx, obj, index, &desc);
+            break;
+          case Int32Elements:
+            res = header.asInt32Elements().getOwnElement(cx, obj, index, &desc);
+            break;
+          case Uint8ClampedElements:
+            res = header.asUint8ClampedElements().getOwnElement(cx, obj, index, &desc);
+            break;
+          case Float32Elements:
+            res = header.asFloat32Elements().getOwnElement(cx, obj, index, &desc);
+            break;
+          case Float64Elements:
+            res = header.asFloat64Elements().getOwnElement(cx, obj, index, &desc);
+            break;
+          case ArrayBufferElements:
+            res = header.asArrayBufferElements().getOwnElement(cx, obj, index, &desc);
+            break;
+        }
+        if (!res)
+            return false;
+
+        /* No property?  Recur or bottom out. */
+        if (desc.isUndefined()) {
+            obj = obj->getProto();
+            if (obj)
+                continue;
+
+            vp->setUndefined();
+            return true;
+        }
+
+        /* If it's a data property, return the value. */
+        if (desc.isDataDescriptor()) {
+            *vp = desc.value();
+            return true;
+        }
+
+        /* If it's an accessor property, call its [[Get]] with the receiver. */
+        if (desc.isAccessorDescriptor()) {
+            Value get = desc.getterValue();
+            if (get.isUndefined()) {
+                vp->setUndefined();
+                return true;
+            }
+
+            InvokeArgsGuard args;
+            if (!cx->stack.pushInvokeArgs(cx, 0, &args))
+                return false;
+
+            /* Push fval, thisv, and the args. */
+            args.calleev() = get;
+            args.thisv() = ObjectValue(*receiver);
+
+            bool ok = Invoke(cx, args);
+            *vp = args.rval();
+            return ok;
+        }
+
+        /* Otherwise it's a PropertyOp-based property.  XXX handle this! */
+        MOZ_NOT_REACHED("NYI: handle PropertyOp'd properties here");
+        return false;
+    } while (false);
+}
+
+bool
 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()) {
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -63,18 +63,37 @@ struct PropDesc {
 
     /* Or maybe this represents a property's absence, and it's undefined. */
     bool isUndefined_ : 1;
 
   public:
     friend class AutoPropDescArrayRooter;
     friend void JS::AutoGCRooter::trace(JSTracer *trc);
 
+    enum Enumerability { Enumerable = true, NonEnumerable = false };
+    enum Configurability { Configurable = true, NonConfigurable = false };
+    enum Writability { Writable = true, NonWritable = false };
+
     PropDesc();
 
+    static PropDesc undefined() { return PropDesc(); }
+
+    PropDesc(const Value &v, Writability writable,
+             Enumerability enumerable, Configurability configurable)
+      : pd_(UndefinedValue()),
+        value_(v),
+        get_(UndefinedValue()), set_(UndefinedValue()),
+        attrs((writable ? 0 : JSPROP_READONLY) |
+              (enumerable ? JSPROP_ENUMERATE : 0) |
+              (configurable ? 0 : JSPROP_PERMANENT)),
+        hasGet_(false), hasSet_(false),
+        hasValue_(true), hasWritable_(true), hasEnumerable_(true), hasConfigurable_(true),
+        isUndefined_(false)
+    {}
+
     /*
      * 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
@@ -299,16 +318,18 @@ class DenseElementsHeader : public Eleme
         return dense.initializedLength;
     }
 
     uint32_t length() const {
         MOZ_ASSERT(ElementsHeader::isDenseElements());
         return ElementsHeader::length;
     }
 
+    bool getOwnElement(JSContext *cx, ObjectImpl *obj, uint32_t index, PropDesc *desc);
+
     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;
@@ -323,16 +344,18 @@ class SparseElementsHeader : public Elem
         return sparse.shape;
     }
 
     uint32_t length() const {
         MOZ_ASSERT(ElementsHeader::isSparseElements());
         return ElementsHeader::length;
     }
 
+    bool getOwnElement(JSContext *cx, ObjectImpl *obj, uint32_t index, PropDesc *desc);
+
     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;
@@ -426,21 +449,30 @@ template<> inline const bool TypeIsUnsig
 template<> inline const bool TypeIsUnsigned<uint32_t>() { return true; }
 
 template<typename T> static inline const bool TypeIsUint8Clamped() { return false; }
 template<> inline const bool TypeIsUint8Clamped<uint8_clamped>() { return true; }
 
 template <typename T>
 class TypedElementsHeader : public ElementsHeader
 {
+    T getElement(uint32_t index) {
+        MOZ_ASSERT(index < length());
+        return reinterpret_cast<T *>(this + 1)[index];
+    }
+
   public:
-    uint32_t byteLength() const {
+    uint32_t length() const {
+        MOZ_ASSERT(Uint8Elements <= kind());
+        MOZ_ASSERT(kind() <= Float64Elements);
         return ElementsHeader::length;
     }
 
+    bool getOwnElement(JSContext *cx, ObjectImpl *obj, uint32_t index, PropDesc *desc);
+
     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;
 };
 
@@ -516,16 +548,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 getOwnElement(JSContext *cx, ObjectImpl *obj, uint32_t index, PropDesc *desc);
+
     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;
@@ -680,16 +714,19 @@ extern HeapSlot *emptyObjectElements;
 
 struct Class;
 struct GCMarker;
 struct ObjectOps;
 struct Shape;
 
 class NewObjectCache;
 
+inline Value
+ObjectValue(ObjectImpl &obj);
+
 /*
  * ObjectImpl specifies the internal implementation of an object.  (In contrast
  * JSObject specifies an "external" interface, at the conceptual level of that
  * exposed in ECMAScript.)
  *
  * The |shape_| member stores the shape of the object, which includes the
  * object's class and the layout of all its properties.
  *
@@ -766,16 +803,18 @@ class ObjectImpl : public gc::Cell
         MOZ_STATIC_ASSERT(offsetof(ObjectImpl, slots) == offsetof(shadow::Object, slots),
                           "shadow slots must match actual slots");
         MOZ_STATIC_ASSERT(offsetof(ObjectImpl, elements) == offsetof(shadow::Object, _1),
                           "shadow placeholder must match actual elements");
     }
 
     JSObject * asObjectPtr() { return reinterpret_cast<JSObject *>(this); }
 
+    friend inline Value ObjectValue(ObjectImpl &obj);
+
     /* These functions are public, and they should remain public. */
 
   public:
     JSObject * getProto() const {
         return type_->proto;
     }
 
     inline bool isExtensible() const;
@@ -1076,15 +1115,27 @@ 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); }
 };
 
+inline Value
+ObjectValue(ObjectImpl &obj)
+{
+    Value v;
+    v.setObject(*obj.asObjectPtr());
+    return v;
+}
+
+/* Proposed default [[GetP]](Receiver, P) method. */
+extern bool
+GetElement(JSContext *cx, ObjectImpl *obj, ObjectImpl *receiver, uint32_t index, Value *vp);
+
 extern bool
 DefineElement(JSContext *cx, ObjectImpl *obj, uint32_t index, const PropDesc &desc,
               bool shouldThrow, bool *succeeded);
 
 } /* namespace js */
 
 #endif /* ObjectImpl_h__ */