diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 0184bd47b42..cfdeddbb5ba 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -703,6 +703,14 @@ MagicValue(JSWhyMagic 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) { @@ -711,6 +719,55 @@ NumberValue(double dbl) return v; } +static JS_ALWAYS_INLINE Value +NumberValue(size_t s) +{ + Value v; + if (s > JSVAL_INT_MAX) + v.setDouble(s); + else + v.setInt32(int32_t(s)); + 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; +} + static JS_ALWAYS_INLINE Value ObjectOrNullValue(JSObject *obj) { diff --git a/js/src/vm/ObjectImpl.cpp b/js/src/vm/ObjectImpl.cpp index 64dd1952dc6..02d3c6e78e2 100644 --- a/js/src/vm/ObjectImpl.cpp +++ b/js/src/vm/ObjectImpl.cpp @@ -11,6 +11,8 @@ #include "jsscope.h" #include "jsobjinlines.h" +#include "js/TemplateLib.h" + #include "Debugger.h" #include "ObjectImpl.h" @@ -288,6 +290,63 @@ js::ObjectImpl::markChildren(JSTracer *trc) 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 +bool +TypedElementsHeader::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) @@ -345,7 +404,7 @@ DenseElementsHeader::defineElement(JSContext *cx, ObjectImpl *obj, uint32_t inde 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; } @@ -393,7 +452,7 @@ TypedElementsHeader::defineElement(JSContext *cx, ObjectImpl *obj, *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; } @@ -411,6 +470,108 @@ ArrayBufferElementsHeader::defineElement(JSContext *cx, ObjectImpl *obj, 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(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) diff --git a/js/src/vm/ObjectImpl.h b/js/src/vm/ObjectImpl.h index d29190b3897..40ec3297466 100644 --- a/js/src/vm/ObjectImpl.h +++ b/js/src/vm/ObjectImpl.h @@ -68,8 +68,27 @@ struct PropDesc { 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) * @@ -94,8 +113,6 @@ struct PropDesc { void initFromPropertyDescriptor(const PropertyDescriptor &desc); bool makeObject(JSContext *cx); - void setUndefined() { isUndefined_ = true; } - bool isUndefined() const { return isUndefined_; } bool hasGet() const { MOZ_ASSERT(!isUndefined()); return hasGet_; } @@ -304,6 +321,8 @@ class DenseElementsHeader : public ElementsHeader 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); @@ -328,6 +347,8 @@ class SparseElementsHeader : public ElementsHeader 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); @@ -431,11 +452,20 @@ template<> inline const bool TypeIsUint8Clamped() { return true; template class TypedElementsHeader : public ElementsHeader { + T getElement(uint32_t index) { + MOZ_ASSERT(index < length()); + return reinterpret_cast(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); @@ -521,6 +551,8 @@ class Uint8ClampedElementsHeader : public TypedElementsHeader 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); @@ -686,6 +718,9 @@ 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 @@ -772,6 +807,8 @@ class ObjectImpl : public gc::Cell JSObject * asObjectPtr() { return reinterpret_cast(this); } + friend inline Value ObjectValue(ObjectImpl &obj); + /* These functions are public, and they should remain public. */ public: @@ -1082,6 +1119,18 @@ class ObjectImpl : public gc::Cell 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);