author | Jeff Walden <jwalden@mit.edu> |
Tue, 10 Apr 2012 16:33:44 -0700 | |
changeset 92583 | 9bab3e0dd69d7c6628fb7f8983a5e4acb6566980 |
parent 92582 | ef7803bad0b1f468850b50f3c4c009c848d3db6b |
child 92584 | e586bd795354e0c98a79499062eb083658eb8f69 |
push id | 22544 |
push user | emorley@mozilla.com |
push date | Fri, 27 Apr 2012 11:53:27 +0000 |
treeherder | mozilla-central@d871849ac3a3 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bhackett |
bugs | 739380 |
milestone | 15.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
|
--- 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__ */