blob: 8eba5da6a2a522190edc2124155ad813fd69205b [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_ObjectImpl_h
#define vm_ObjectImpl_h
#include "mozilla/Assertions.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/StandardInteger.h"
#include "jsfriendapi.h"
#include "jsinfer.h"
#include "gc/Barrier.h"
#include "gc/Heap.h"
#include "js/Value.h"
#include "vm/NumericConversions.h"
#include "vm/String.h"
namespace js {
class Debugger;
class ObjectImpl;
class Nursery;
class Shape;
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);
}
/*
* Properties are stored differently depending on the type of the key. If the
* key is an unsigned 32-bit integer (i.e. an index), we call such properties
* "elements" and store them in one of a number of forms (optimized for dense
* property storage, typed array data, and so on). All other properties are
* stored using shapes and shape trees. Keys for these properties are either
* PropertyNames (that is, atomized strings whose contents are not unsigned
* 32-bit integers) or SpecialIds (see jsid for details); the union of these
* types, used in individual shapes, is PropertyId.
*/
class PropertyId
{
jsid id;
public:
bool isName() const {
MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SPECIAL(id));
return JSID_IS_STRING(id);
}
bool isSpecial() const {
MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SPECIAL(id));
return !isName();
}
PropertyId() {
*this = PropertyId(SpecialId());
}
explicit PropertyId(PropertyName *name)
: id(NON_INTEGER_ATOM_TO_JSID(name))
{ }
explicit PropertyId(const SpecialId &sid)
: id(SPECIALID_TO_JSID(sid))
{ }
PropertyName * asName() const {
return JSID_TO_STRING(id)->asAtom().asPropertyName();
}
SpecialId asSpecial() const {
return JSID_TO_SPECIALID(id);
}
const jsid &asId() const {
return id;
}
jsid &asId() {
return id;
}
bool operator==(const PropertyId &rhs) const { return id == rhs.id; }
bool operator!=(const PropertyId &rhs) const { return id != rhs.id; }
};
/*
* A representation of ECMA-262 ed. 5's internal Property Descriptor data
* structure.
*/
struct PropDesc {
private:
/*
* Original object from which this descriptor derives, passed through for
* the benefit of proxies. FIXME: Remove this when direct proxies happen.
*/
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;
/* Or maybe this represents a property's absence, and it's undefined. */
bool isUndefined_ : 1;
PropDesc(const Value &v)
: pd_(UndefinedValue()),
value_(v),
get_(UndefinedValue()), set_(UndefinedValue()),
attrs(0),
hasGet_(false), hasSet_(false),
hasValue_(true), hasWritable_(false), hasEnumerable_(false), hasConfigurable_(false),
isUndefined_(false)
{
}
public:
friend class AutoPropDescRooter;
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(); }
static PropDesc valueOnly(const Value &v) { return PropDesc(v); }
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)
{}
inline PropDesc(const Value &getter, const Value &setter,
Enumerability enumerable, Configurability configurable);
/*
* 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);
/*
* If IsGenericDescriptor(desc) or IsDataDescriptor(desc) is true, then if
* the value of an attribute field of desc, considered as a data
* descriptor, is absent, set it to its default value. Else if the value of
* an attribute field of desc, considered as an attribute descriptor, is
* absent, set it to its default value.
*/
void complete();
/*
* 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);
void setUndefined() { isUndefined_ = true; }
bool isUndefined() const { return isUndefined_; }
bool hasGet() const { MOZ_ASSERT(!isUndefined()); return hasGet_; }
bool hasSet() const { MOZ_ASSERT(!isUndefined()); return hasSet_; }
bool hasValue() const { MOZ_ASSERT(!isUndefined()); return hasValue_; }
bool hasWritable() const { MOZ_ASSERT(!isUndefined()); return hasWritable_; }
bool hasEnumerable() const { MOZ_ASSERT(!isUndefined()); return hasEnumerable_; }
bool hasConfigurable() const { MOZ_ASSERT(!isUndefined()); return hasConfigurable_; }
Value pd() const { MOZ_ASSERT(!isUndefined()); return pd_; }
void clearPd() { pd_ = UndefinedValue(); }
uint8_t attributes() const { MOZ_ASSERT(!isUndefined()); return attrs; }
/* 8.10.1 IsAccessorDescriptor(desc) */
bool isAccessorDescriptor() const {
return !isUndefined() && (hasGet() || hasSet());
}
/* 8.10.2 IsDataDescriptor(desc) */
bool isDataDescriptor() const {
return !isUndefined() && (hasValue() || hasWritable());
}
/* 8.10.3 IsGenericDescriptor(desc) */
bool isGenericDescriptor() const {
return !isUndefined() && !isAccessorDescriptor() && !isDataDescriptor();
}
bool configurable() const {
MOZ_ASSERT(!isUndefined());
MOZ_ASSERT(hasConfigurable());
return (attrs & JSPROP_PERMANENT) == 0;
}
bool enumerable() const {
MOZ_ASSERT(!isUndefined());
MOZ_ASSERT(hasEnumerable());
return (attrs & JSPROP_ENUMERATE) != 0;
}
bool writable() const {
MOZ_ASSERT(!isUndefined());
MOZ_ASSERT(hasWritable());
return (attrs & JSPROP_READONLY) == 0;
}
HandleValue value() const {
MOZ_ASSERT(hasValue());
return HandleValue::fromMarkedLocation(&value_);
}
JSObject * getterObject() const {
MOZ_ASSERT(!isUndefined());
MOZ_ASSERT(hasGet());
return get_.isUndefined() ? NULL : &get_.toObject();
}
JSObject * setterObject() const {
MOZ_ASSERT(!isUndefined());
MOZ_ASSERT(hasSet());
return set_.isUndefined() ? NULL : &set_.toObject();
}
HandleValue getterValue() const {
MOZ_ASSERT(!isUndefined());
MOZ_ASSERT(hasGet());
return HandleValue::fromMarkedLocation(&get_);
}
HandleValue setterValue() const {
MOZ_ASSERT(!isUndefined());
MOZ_ASSERT(hasSet());
return HandleValue::fromMarkedLocation(&set_);
}
/*
* Unfortunately the values produced by these methods are used such that
* we can't assert anything here. :-(
*/
PropertyOp getter() const {
return CastAsPropertyOp(get_.isUndefined() ? NULL : &get_.toObject());
}
StrictPropertyOp setter() const {
return CastAsStrictPropertyOp(set_.isUndefined() ? NULL : &set_.toObject());
}
/*
* 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.
*/
bool checkGetter(JSContext *cx);
bool checkSetter(JSContext *cx);
bool unwrapDebuggerObjectsInto(JSContext *cx, Debugger *dbg, HandleObject obj,
PropDesc *unwrapped) const;
bool wrapInto(JSContext *cx, HandleObject obj, const jsid &id, jsid *wrappedId,
PropDesc *wrappedDesc) const;
};
class AutoPropDescRooter : private JS::CustomAutoRooter
{
public:
explicit AutoPropDescRooter(JSContext *cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: CustomAutoRooter(cx), skip(cx, &propDesc)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
PropDesc& getPropDesc() { return propDesc; }
void initFromPropertyDescriptor(const PropertyDescriptor &desc) {
propDesc.initFromPropertyDescriptor(desc);
}
bool makeObject(JSContext *cx) {
return propDesc.makeObject(cx);
}
void setUndefined() { propDesc.setUndefined(); }
bool isUndefined() const { return propDesc.isUndefined(); }
bool hasGet() const { return propDesc.hasGet(); }
bool hasSet() const { return propDesc.hasSet(); }
bool hasValue() const { return propDesc.hasValue(); }
bool hasWritable() const { return propDesc.hasWritable(); }
bool hasEnumerable() const { return propDesc.hasEnumerable(); }
bool hasConfigurable() const { return propDesc.hasConfigurable(); }
Value pd() const { return propDesc.pd(); }
void clearPd() { propDesc.clearPd(); }
uint8_t attributes() const { return propDesc.attributes(); }
bool isAccessorDescriptor() const { return propDesc.isAccessorDescriptor(); }
bool isDataDescriptor() const { return propDesc.isDataDescriptor(); }
bool isGenericDescriptor() const { return propDesc.isGenericDescriptor(); }
bool configurable() const { return propDesc.configurable(); }
bool enumerable() const { return propDesc.enumerable(); }
bool writable() const { return propDesc.writable(); }
HandleValue value() const { return propDesc.value(); }
JSObject *getterObject() const { return propDesc.getterObject(); }
JSObject *setterObject() const { return propDesc.setterObject(); }
HandleValue getterValue() const { return propDesc.getterValue(); }
HandleValue setterValue() const { return propDesc.setterValue(); }
PropertyOp getter() const { return propDesc.getter(); }
StrictPropertyOp setter() const { return propDesc.setter(); }
private:
virtual void trace(JSTracer *trc);
PropDesc propDesc;
SkipRoot skip;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
class DenseElementsHeader;
class SparseElementsHeader;
class Uint8ElementsHeader;
class Int8ElementsHeader;
class Uint16ElementsHeader;
class Int16ElementsHeader;
class Uint32ElementsHeader;
class Int32ElementsHeader;
class Uint8ClampedElementsHeader;
class Float32ElementsHeader;
class Float64ElementsHeader;
class Uint8ClampedElementsHeader;
class ArrayBufferElementsHeader;
enum ElementsKind {
DenseElements,
SparseElements,
ArrayBufferElements,
/* These typed element types must remain contiguous. */
Uint8Elements,
Int8Elements,
Uint16Elements,
Int16Elements,
Uint32Elements,
Int32Elements,
Uint8ClampedElements,
Float32Elements,
Float64Elements
};
class ElementsHeader
{
protected:
uint32_t type;
uint32_t length; /* Array length, ArrayBuffer length, typed array length */
union {
class {
friend class DenseElementsHeader;
uint32_t initializedLength;
uint32_t capacity;
} dense;
class {
friend class SparseElementsHeader;
Shape *shape;
} sparse;
class {
friend class ArrayBufferElementsHeader;
JSObject * views;
} buffer;
};
void staticAsserts() {
MOZ_STATIC_ASSERT(sizeof(ElementsHeader) == ValuesPerHeader * sizeof(Value),
"Elements size and values-per-Elements mismatch");
}
public:
ElementsKind kind() const {
MOZ_ASSERT(type <= ArrayBufferElements);
return ElementsKind(type);
}
inline bool isDenseElements() const { return kind() == DenseElements; }
inline bool isSparseElements() const { return kind() == SparseElements; }
inline bool isArrayBufferElements() const { return kind() == ArrayBufferElements; }
inline bool isUint8Elements() const { return kind() == Uint8Elements; }
inline bool isInt8Elements() const { return kind() == Int8Elements; }
inline bool isUint16Elements() const { return kind() == Uint16Elements; }
inline bool isInt16Elements() const { return kind() == Int16Elements; }
inline bool isUint32Elements() const { return kind() == Uint32Elements; }
inline bool isInt32Elements() const { return kind() == Int32Elements; }
inline bool isUint8ClampedElements() const { return kind() == Uint8ClampedElements; }
inline bool isFloat32Elements() const { return kind() == Float32Elements; }
inline bool isFloat64Elements() const { return kind() == Float64Elements; }
inline DenseElementsHeader & asDenseElements();
inline SparseElementsHeader & asSparseElements();
inline ArrayBufferElementsHeader & asArrayBufferElements();
inline Uint8ElementsHeader & asUint8Elements();
inline Int8ElementsHeader & asInt8Elements();
inline Uint16ElementsHeader & asUint16Elements();
inline Int16ElementsHeader & asInt16Elements();
inline Uint32ElementsHeader & asUint32Elements();
inline Int32ElementsHeader & asInt32Elements();
inline Uint8ClampedElementsHeader & asUint8ClampedElements();
inline Float32ElementsHeader & asFloat32Elements();
inline Float64ElementsHeader & asFloat64Elements();
static ElementsHeader * fromElements(HeapSlot *elems) {
return reinterpret_cast<ElementsHeader *>(uintptr_t(elems) - sizeof(ElementsHeader));
}
static const size_t ValuesPerHeader = 2;
};
class DenseElementsHeader : public ElementsHeader
{
public:
uint32_t capacity() const {
MOZ_ASSERT(ElementsHeader::isDenseElements());
return dense.capacity;
}
uint32_t initializedLength() const {
MOZ_ASSERT(ElementsHeader::isDenseElements());
return dense.initializedLength;
}
uint32_t length() const {
MOZ_ASSERT(ElementsHeader::isDenseElements());
return ElementsHeader::length;
}
bool getOwnElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index,
unsigned resolveFlags, PropDesc *desc);
bool defineElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index,
const PropDesc &desc, bool shouldThrow, unsigned resolveFlags,
bool *succeeded);
bool setElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver,
uint32_t index, const Value &v, unsigned resolveFlags, 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;
};
class SparseElementsHeader : public ElementsHeader
{
public:
Shape *shape() {
MOZ_ASSERT(ElementsHeader::isSparseElements());
return sparse.shape;
}
uint32_t length() const {
MOZ_ASSERT(ElementsHeader::isSparseElements());
return ElementsHeader::length;
}
bool getOwnElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index,
unsigned resolveFlags, PropDesc *desc);
bool defineElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index,
const PropDesc &desc, bool shouldThrow, unsigned resolveFlags,
bool *succeeded);
bool setElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver,
uint32_t index, const Value &v, unsigned resolveFlags, 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;
};
extern uint32_t JS_FASTCALL
ClampDoubleToUint8(const double x);
struct uint8_clamped {
uint8_t val;
uint8_clamped() { }
uint8_clamped(const uint8_clamped& other) : val(other.val) { }
// invoke our assignment helpers for constructor conversion
uint8_clamped(uint8_t x) { *this = x; }
uint8_clamped(uint16_t x) { *this = x; }
uint8_clamped(uint32_t x) { *this = x; }
uint8_clamped(int8_t x) { *this = x; }
uint8_clamped(int16_t x) { *this = x; }
uint8_clamped(int32_t x) { *this = x; }
uint8_clamped(double x) { *this = x; }
uint8_clamped& operator=(const uint8_clamped& x) {
val = x.val;
return *this;
}
uint8_clamped& operator=(uint8_t x) {
val = x;
return *this;
}
uint8_clamped& operator=(uint16_t x) {
val = (x > 255) ? 255 : uint8_t(x);
return *this;
}
uint8_clamped& operator=(uint32_t x) {
val = (x > 255) ? 255 : uint8_t(x);
return *this;
}
uint8_clamped& operator=(int8_t x) {
val = (x >= 0) ? uint8_t(x) : 0;
return *this;
}
uint8_clamped& operator=(int16_t x) {
val = (x >= 0)
? ((x < 255)
? uint8_t(x)
: 255)
: 0;
return *this;
}
uint8_clamped& operator=(int32_t x) {
val = (x >= 0)
? ((x < 255)
? uint8_t(x)
: 255)
: 0;
return *this;
}
uint8_clamped& operator=(const double x) {
val = uint8_t(ClampDoubleToUint8(x));
return *this;
}
operator uint8_t() const {
return val;
}
void staticAsserts() {
MOZ_STATIC_ASSERT(sizeof(uint8_clamped) == 1,
"uint8_clamped must be layout-compatible with uint8_t");
}
};
/* Note that we can't use std::numeric_limits here due to uint8_clamped. */
template<typename T> inline const bool TypeIsFloatingPoint() { return false; }
template<> inline const bool TypeIsFloatingPoint<float>() { return true; }
template<> inline const bool TypeIsFloatingPoint<double>() { return true; }
template<typename T> inline const bool TypeIsUnsigned() { return false; }
template<> inline const bool TypeIsUnsigned<uint8_t>() { return true; }
template<> inline const bool TypeIsUnsigned<uint16_t>() { return true; }
template<> inline const bool TypeIsUnsigned<uint32_t>() { 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];
}
inline void assign(uint32_t index, double d);
void setElement(uint32_t index, T value) {
MOZ_ASSERT(index < length());
reinterpret_cast<T *>(this + 1)[index] = value;
}
public:
uint32_t length() const {
MOZ_ASSERT(Uint8Elements <= kind());
MOZ_ASSERT(kind() <= Float64Elements);
return ElementsHeader::length;
}
bool getOwnElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index,
unsigned resolveFlags, PropDesc *desc);
bool defineElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index,
const PropDesc &desc, bool shouldThrow, unsigned resolveFlags,
bool *succeeded);
bool setElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver,
uint32_t index, const Value &v, unsigned resolveFlags, bool *succeeded);
private:
TypedElementsHeader(const TypedElementsHeader &other) MOZ_DELETE;
void operator=(const TypedElementsHeader &other) MOZ_DELETE;
};
template<typename T> inline void
TypedElementsHeader<T>::assign(uint32_t index, double d)
{
MOZ_NOT_REACHED("didn't specialize for this element type");
}
template<> inline void
TypedElementsHeader<uint8_clamped>::assign(uint32_t index, double d)
{
double i = ToInteger(d);
uint8_t u = (i <= 0)
? 0
: (i >= 255)
? 255
: uint8_t(i);
setElement(index, uint8_clamped(u));
}
template<> inline void
TypedElementsHeader<uint8_t>::assign(uint32_t index, double d)
{
setElement(index, uint8_t(ToUint32(d)));
}
template<> inline void
TypedElementsHeader<int8_t>::assign(uint32_t index, double d)
{
/* FIXME: Casting out-of-range signed integers has undefined behavior! */
setElement(index, int8_t(ToInt32(d)));
}
template<> inline void
TypedElementsHeader<uint16_t>::assign(uint32_t index, double d)
{
setElement(index, uint16_t(ToUint32(d)));
}
template<> inline void
TypedElementsHeader<int16_t>::assign(uint32_t index, double d)
{
/* FIXME: Casting out-of-range signed integers has undefined behavior! */
setElement(index, int16_t(ToInt32(d)));
}
template<> inline void
TypedElementsHeader<uint32_t>::assign(uint32_t index, double d)
{
setElement(index, ToUint32(d));
}
template<> inline void
TypedElementsHeader<int32_t>::assign(uint32_t index, double d)
{
/* FIXME: Casting out-of-range signed integers has undefined behavior! */
setElement(index, int32_t(ToInt32(d)));
}
template<> inline void
TypedElementsHeader<float>::assign(uint32_t index, double d)
{
setElement(index, float(d));
}
template<> inline void
TypedElementsHeader<double>::assign(uint32_t index, double d)
{
setElement(index, d);
}
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_clamped>
{
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 getOwnElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index,
unsigned resolveFlags, PropDesc *desc);
bool defineElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index,
const PropDesc &desc, bool shouldThrow, unsigned resolveFlags,
bool *succeeded);
bool setElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver,
uint32_t index, const Value &v, unsigned resolveFlags, bool *succeeded);
JSObject **viewList() { return &buffer.views; }
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;
};
inline DenseElementsHeader &
ElementsHeader::asDenseElements()
{
MOZ_ASSERT(isDenseElements());
return *static_cast<DenseElementsHeader *>(this);
}
inline SparseElementsHeader &
ElementsHeader::asSparseElements()
{
MOZ_ASSERT(isSparseElements());
return *static_cast<SparseElementsHeader *>(this);
}
inline Uint8ElementsHeader &
ElementsHeader::asUint8Elements()
{
MOZ_ASSERT(isUint8Elements());
return *static_cast<Uint8ElementsHeader *>(this);
}
inline Int8ElementsHeader &
ElementsHeader::asInt8Elements()
{
MOZ_ASSERT(isInt8Elements());
return *static_cast<Int8ElementsHeader *>(this);
}
inline Uint16ElementsHeader &
ElementsHeader::asUint16Elements()
{
MOZ_ASSERT(isUint16Elements());
return *static_cast<Uint16ElementsHeader *>(this);
}
inline Int16ElementsHeader &
ElementsHeader::asInt16Elements()
{
MOZ_ASSERT(isInt16Elements());
return *static_cast<Int16ElementsHeader *>(this);
}
inline Uint32ElementsHeader &
ElementsHeader::asUint32Elements()
{
MOZ_ASSERT(isUint32Elements());
return *static_cast<Uint32ElementsHeader *>(this);
}
inline Int32ElementsHeader &
ElementsHeader::asInt32Elements()
{
MOZ_ASSERT(isInt32Elements());
return *static_cast<Int32ElementsHeader *>(this);
}
inline Uint8ClampedElementsHeader &
ElementsHeader::asUint8ClampedElements()
{
MOZ_ASSERT(isUint8ClampedElements());
return *static_cast<Uint8ClampedElementsHeader *>(this);
}
inline Float32ElementsHeader &
ElementsHeader::asFloat32Elements()
{
MOZ_ASSERT(isFloat32Elements());
return *static_cast<Float32ElementsHeader *>(this);
}
inline Float64ElementsHeader &
ElementsHeader::asFloat64Elements()
{
MOZ_ASSERT(isFloat64Elements());
return *static_cast<Float64ElementsHeader *>(this);
}
inline ArrayBufferElementsHeader &
ElementsHeader::asArrayBufferElements()
{
MOZ_ASSERT(isArrayBufferElements());
return *static_cast<ArrayBufferElementsHeader *>(this);
}
class ArrayBufferObject;
/*
* ES6 20130308 draft 8.4.2.4 ArraySetLength.
*
* |id| must be "length", |attrs| are the attributes to be used for the newly-
* changed length property, |value| is the value for the new length, and
* |setterIsStrict| indicates whether invalid changes will cause a TypeError
* to be thrown.
*/
extern bool
ArraySetLength(JSContext *cx, HandleObject obj, HandleId id, unsigned attrs, HandleValue value,
bool setterIsStrict);
/*
* Elements header used for all native objects. The elements component of such
* objects offers an efficient representation for all or some of the indexed
* properties of the object, using a flat array of Values rather than a shape
* hierarchy stored in the object's slots. This structure is immediately
* followed by an array of elements, with the elements member in an object
* pointing to the beginning of that array (the end of this structure).
* See below for usage of this structure.
*
* The sets of properties represented by an object's elements and slots
* are disjoint. The elements contain only indexed properties, while the slots
* can contain both named and indexed properties; any indexes in the slots are
* distinct from those in the elements. If isIndexed() is false for an object,
* all indexed properties (if any) are stored in the dense elements.
*
* Indexes will be stored in the object's slots instead of its elements in
* the following case:
* - there are more than MIN_SPARSE_INDEX slots total and the load factor
* (COUNT / capacity) is less than 0.25
* - a property is defined that has non-default property attributes.
*
* We track these pieces of metadata for dense elements:
* - The length property as a uint32_t, accessible for array objects with
* getArrayLength(), setArrayLength(). This is unused for non-arrays.
* - The number of element slots (capacity), gettable with
* getDenseElementsCapacity().
* - The array's initialized length, accessible with
* getDenseElementsInitializedLength().
*
* Holes in the array are represented by MagicValue(JS_ELEMENTS_HOLE) values.
* These indicate indexes which are not dense properties of the array. The
* property may, however, be held by the object's properties.
*
* The capacity and length of an object's elements are almost entirely
* unrelated! In general the length may be greater than, less than, or equal
* to the capacity. The first case occurs with |new Array(100)|. The length
* is 100, but the capacity remains 0 (indices below length and above capacity
* must be treated as holes) until elements between capacity and length are
* set. The other two cases are common, depending upon the number of elements
* in an array and the underlying allocator used for element storage.
*
* The only case in which the capacity and length of an object's elements are
* related is when the object is an array with non-writable length. In this
* case the capacity is always less than or equal to the length. This permits
* JIT code to optimize away the check for non-writable length when assigning
* to possibly out-of-range elements: such code already has to check for
* |index < capacity|, and fallback code checks for non-writable length.
*
* The initialized length of an object specifies the number of elements that
* have been initialized. All elements above the initialized length are
* holes in the object, and the memory for all elements between the initialized
* length and capacity is left uninitialized. When type inference is disabled,
* the initialized length always equals the capacity. When inference is
* enabled, the initialized length is some value less than or equal to both the
* object's length and the object's capacity.
*
* With inference enabled, there is flexibility in exactly the value the
* initialized length must hold, e.g. if an array has length 5, capacity 10,
* completely empty, it is valid for the initialized length to be any value
* between zero and 5, as long as the in memory values below the initialized
* length have been initialized with a hole value. However, in such cases we
* want to keep the initialized length as small as possible: if the object is
* known to have no hole values below its initialized length, then it is
* "packed" and can be accessed much faster by JIT code.
*
* Elements do not track property creation order, so enumerating the elements
* of an object does not necessarily visit indexes in the order they were
* created.
*/
class ObjectElements
{
public:
enum Flags {
CONVERT_DOUBLE_ELEMENTS = 0x1,
ASMJS_ARRAY_BUFFER = 0x2,
NEUTERED_BUFFER = 0x4,
// Present only if these elements correspond to an array with
// non-writable length; never present for non-arrays.
NONWRITABLE_ARRAY_LENGTH = 0x8
};
private:
friend class ::JSObject;
friend class ObjectImpl;
friend class ArrayBufferObject;
friend class Nursery;
friend bool
ArraySetLength(JSContext *cx, HandleObject obj, HandleId id, unsigned attrs, HandleValue value,
bool setterIsStrict);
/* See Flags enum above. */
uint32_t flags;
/*
* Number of initialized elements. This is <= the capacity, and for arrays
* is <= the length. Memory for elements above the initialized length is
* uninitialized, but values between the initialized length and the proper
* length are conceptually holes.
*
* ArrayBufferObject uses this field to store byteLength.
*/
uint32_t initializedLength;
/*
* Beware, one or both of the following fields is clobbered by
* ArrayBufferObject. See GetViewList.
*/
/* Number of allocated slots. */
uint32_t capacity;
/* 'length' property of array objects, unused for other objects. */
uint32_t length;
void staticAsserts() {
MOZ_STATIC_ASSERT(sizeof(ObjectElements) == VALUES_PER_HEADER * sizeof(Value),
"Elements size and values-per-Elements mismatch");
}
bool shouldConvertDoubleElements() const {
return flags & CONVERT_DOUBLE_ELEMENTS;
}
void setShouldConvertDoubleElements() {
flags |= CONVERT_DOUBLE_ELEMENTS;
}
bool isAsmJSArrayBuffer() const {
return flags & ASMJS_ARRAY_BUFFER;
}
void setIsAsmJSArrayBuffer() {
flags |= ASMJS_ARRAY_BUFFER;
}
bool isNeuteredBuffer() const {
return flags & NEUTERED_BUFFER;
}
void setIsNeuteredBuffer() {
flags |= NEUTERED_BUFFER;
}
bool hasNonwritableArrayLength() const {
return flags & NONWRITABLE_ARRAY_LENGTH;
}
void setNonwritableArrayLength() {
flags |= NONWRITABLE_ARRAY_LENGTH;
}
public:
ObjectElements(uint32_t capacity, uint32_t length)
: flags(0), initializedLength(0), capacity(capacity), length(length)
{}
HeapSlot *elements() {
return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements));
}
static ObjectElements * fromElements(HeapSlot *elems) {
return reinterpret_cast<ObjectElements*>(uintptr_t(elems) - sizeof(ObjectElements));
}
static int offsetOfFlags() {
return int(offsetof(ObjectElements, flags)) - int(sizeof(ObjectElements));
}
static int offsetOfInitializedLength() {
return int(offsetof(ObjectElements, initializedLength)) - int(sizeof(ObjectElements));
}
static int offsetOfCapacity() {
return int(offsetof(ObjectElements, capacity)) - int(sizeof(ObjectElements));
}
static int offsetOfLength() {
return int(offsetof(ObjectElements, length)) - int(sizeof(ObjectElements));
}
static bool ConvertElementsToDoubles(JSContext *cx, uintptr_t elements);
static const size_t VALUES_PER_HEADER = 2;
};
/* Shared singleton for objects with no elements. */
extern HeapSlot *emptyObjectElements;
struct Class;
struct GCMarker;
struct ObjectOps;
class Shape;
class NewObjectCache;
class TaggedProto;
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.
*
* The type member stores the type of the object, which contains its prototype
* object and the possible types of its properties.
*
* The rest of the object stores its named properties and indexed elements.
* These are stored separately from one another. Objects are followed by an
* variable-sized array of values for inline storage, which may be used by
* either properties of native objects (fixed slots) or by elements.
*
* Two native objects with the same shape are guaranteed to have the same
* number of fixed slots.
*
* Named property storage can be split between fixed slots and a dynamically
* allocated array (the slots member). For an object with N fixed slots, shapes
* with slots [0..N-1] are stored in the fixed slots, and the remainder are
* stored in the dynamic array. If all properties fit in the fixed slots, the
* 'slots' member is NULL.
*
* Elements are indexed via the 'elements' member. This member can point to
* either the shared emptyObjectElements singleton, into the inline value array
* (the address of the third value, to leave room for a ObjectElements header;
* in this case numFixedSlots() is zero) or to a dynamically allocated array.
*
* Only certain combinations of slots and elements storage are possible.
*
* - For native objects, slots and elements may both be non-empty. The
* slots may be either names or indexes; no indexed property will be in both
* the slots and elements.
*
* - For non-native objects other than typed arrays, properties and elements
* are both empty.
*
* - For typed array buffers, elements are used and properties are not used.
* The data indexed by the elements do not represent Values, but primitive
* unboxed integers or floating point values.
*
* The members of this class are currently protected; in the long run this will
* will change so that some members are private, and only certain methods that
* act upon them will be protected.
*/
class ObjectImpl : public gc::Cell
{
protected:
/*
* Shape of the object, encodes the layout of the object's properties and
* all other information about its structure. See jsscope.h.
*/
HeapPtrShape shape_;
/*
* The object's type and prototype. For objects with the LAZY_TYPE flag
* set, this is the prototype's default 'new' type and can only be used
* to get that prototype.
*/
HeapPtrTypeObject type_;
HeapSlot *slots; /* Slots for object properties. */
HeapSlot *elements; /* Slots for object elements. */
friend bool
ArraySetLength(JSContext *cx, HandleObject obj, HandleId id, unsigned attrs, HandleValue value,
bool setterIsStrict);
private:
static void staticAsserts() {
MOZ_STATIC_ASSERT(sizeof(ObjectImpl) == sizeof(shadow::Object),
"shadow interface must match actual implementation");
MOZ_STATIC_ASSERT(sizeof(ObjectImpl) % sizeof(Value) == 0,
"fixed slots after an object must be aligned");
MOZ_STATIC_ASSERT(offsetof(ObjectImpl, shape_) == offsetof(shadow::Object, shape),
"shadow shape must match actual shape");
MOZ_STATIC_ASSERT(offsetof(ObjectImpl, type_) == offsetof(shadow::Object, type),
"shadow type must match actual type");
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); }
const JSObject * asObjectPtr() const { return reinterpret_cast<const JSObject *>(this); }
friend inline Value ObjectValue(ObjectImpl &obj);
/* These functions are public, and they should remain public. */
public:
JSObject * getProto() const {
return type_->proto;
}
Class *getClass() const {
return type_->clasp;
}
inline bool isExtensible() const;
// Attempt to change the [[Extensible]] bit on |obj| to false. Callers
// must ensure that |obj| is currently extensible before calling this!
static bool
preventExtensions(JSContext *cx, Handle<ObjectImpl*> obj);
inline HeapSlotArray getDenseElements();
inline const Value & getDenseElement(uint32_t idx);
inline bool containsDenseElement(uint32_t idx);
inline uint32_t getDenseInitializedLength();
inline uint32_t getDenseCapacity();
bool makeElementsSparse(JSContext *cx) {
NEW_OBJECT_REPRESENTATION_ONLY();
MOZ_NOT_REACHED("NYI");
return false;
}
inline bool isProxy() const;
protected:
#ifdef DEBUG
void checkShapeConsistency();
#else
void checkShapeConsistency() { }
#endif
Shape *
replaceWithNewEquivalentShape(JSContext *cx, Shape *existingShape, Shape *newShape = NULL);
enum GenerateShape {
GENERATE_NONE,
GENERATE_SHAPE
};
bool setFlag(JSContext *cx, /*BaseShape::Flag*/ uint32_t flag,
GenerateShape generateShape = GENERATE_NONE);
bool clearFlag(JSContext *cx, /*BaseShape::Flag*/ uint32_t flag);
bool toDictionaryMode(JSContext *cx);
private:
/*
* Get internal pointers to the range of values starting at start and
* running for length.
*/
inline void getSlotRangeUnchecked(uint32_t start, uint32_t length,
HeapSlot **fixedStart, HeapSlot **fixedEnd,
HeapSlot **slotsStart, HeapSlot **slotsEnd);
inline void getSlotRange(uint32_t start, uint32_t length,
HeapSlot **fixedStart, HeapSlot **fixedEnd,
HeapSlot **slotsStart, HeapSlot **slotsEnd);
protected:
friend struct GCMarker;
friend class Shape;
friend class NewObjectCache;
inline void invalidateSlotRange(uint32_t start, uint32_t count);
void initializeSlotRange(uint32_t start, uint32_t count);
/*
* Initialize a flat array of slots to this object at a start slot. The
* caller must ensure that are enough slots.
*/
void initSlotRange(uint32_t start, const Value *vector, uint32_t length);
/*
* Copy a flat array of slots to this object at a start slot. Caller must
* ensure there are enough slots in this object.
*/
void copySlotRange(uint32_t start, const Value *vector, uint32_t length);
#ifdef DEBUG
enum SentinelAllowed {
SENTINEL_NOT_ALLOWED,
SENTINEL_ALLOWED
};
/*
* Check that slot is in range for the object's allocated slots.
* If sentinelAllowed then slot may equal the slot capacity.
*/
bool slotInRange(uint32_t slot, SentinelAllowed sentinel = SENTINEL_NOT_ALLOWED) const;
#endif
/* 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:
inline js::TaggedProto getTaggedProto() const;
Shape * lastProperty() const {
MOZ_ASSERT(shape_);
return shape_;
}
bool generateOwnShape(JSContext *cx, js::Shape *newShape = NULL) {
return replaceWithNewEquivalentShape(cx, lastProperty(), newShape);
}
inline JSCompartment *compartment() const;
inline bool isNative() const;
types::TypeObject *type() const {
MOZ_ASSERT(!hasLazyType());
return type_;
}
uint32_t numFixedSlots() const {
return reinterpret_cast<const shadow::Object *>(this)->numFixedSlots();
}
/*
* Whether this is the only object which has its specified type. This
* object will have its type constructed lazily as needed by analysis.
*/
bool hasSingletonType() const { return !!type_->singleton; }
/*
* Whether the object's type has not been constructed yet. If an object
* might have a lazy type, use getType() below, otherwise type().
*/
bool hasLazyType() const { return type_->lazy(); }
inline uint32_t slotSpan() const;
/* Compute dynamicSlotsCount() for this object. */
inline uint32_t numDynamicSlots() const;
Shape *nativeLookup(JSContext *cx, jsid id);
inline Shape *nativeLookup(JSContext *cx, PropertyId pid);
inline Shape *nativeLookup(JSContext *cx, PropertyName *name);
inline bool nativeContains(JSContext *cx, jsid id);
inline bool nativeContains(JSContext *cx, PropertyName* name);
inline bool nativeContains(JSContext *cx, Shape* shape);
/*
* Contextless; can be called from parallel code. Returns false if the
* operation would have been effectful.
*/
Shape *nativeLookupPure(jsid id);
inline Shape *nativeLookupPure(PropertyId pid);
inline Shape *nativeLookupPure(PropertyName *name);
inline bool nativeContainsPure(jsid id);
inline bool nativeContainsPure(PropertyName* name);
inline bool nativeContainsPure(Shape* shape);
inline JSClass *getJSClass() const;
inline bool hasClass(const Class *c) const {
return getClass() == c;
}
inline const ObjectOps *getOps() const;
/*
* An object is a delegate if it is on another object's prototype or scope
* chain, and therefore the delegate might be asked implicitly to get or
* set a property on behalf of another object. Delegates may be accessed
* directly too, as may any object, but only those objects linked after the
* head of any prototype or scope chain are flagged as delegates. This
* definition helps to optimize shape-based property cache invalidation
* (see Purge{Scope,Proto}Chain in jsobj.cpp).
*/
inline bool isDelegate() const;
/*
* Return true if this object is a native one that has been converted from
* shared-immutable prototype-rooted shape storage to dictionary-shapes in
* a doubly-linked list.
*/
inline bool inDictionaryMode() const;
const Value &getSlot(uint32_t slot) const {
MOZ_ASSERT(slotInRange(slot));
uint32_t fixed = numFixedSlots();
if (slot < fixed)
return fixedSlots()[slot];
return slots[slot - fixed];
}
HeapSlot *getSlotAddressUnchecked(uint32_t slot) {
uint32_t fixed = numFixedSlots();
if (slot < fixed)
return fixedSlots() + slot;
return slots + (slot - fixed);
}
HeapSlot *getSlotAddress(uint32_t slot) {
/*
* This can be used to get the address of the end of the slots for the
* object, which may be necessary when fetching zero-length arrays of
* slots (e.g. for callObjVarArray).
*/
MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED));
return getSlotAddressUnchecked(slot);
}
HeapSlot &getSlotRef(uint32_t slot) {
MOZ_ASSERT(slotInRange(slot));
return *getSlotAddress(slot);
}
inline HeapSlot &nativeGetSlotRef(uint32_t slot);
inline const Value &nativeGetSlot(uint32_t slot) const;
inline void setSlot(uint32_t slot, const Value &value);
inline void setCrossCompartmentSlot(uint32_t slot, const Value &value);
inline void initSlot(uint32_t slot, const Value &value);
inline void initCrossCompartmentSlot(uint32_t slot, const Value &value);
inline void initSlotUnchecked(uint32_t slot, const Value &value);
/* For slots which are known to always be fixed, due to the way they are allocated. */
HeapSlot &getFixedSlotRef(uint32_t slot) {
MOZ_ASSERT(slot < numFixedSlots());
return fixedSlots()[slot];
}
const Value &getFixedSlot(uint32_t slot) const {
MOZ_ASSERT(slot < numFixedSlots());
return fixedSlots()[slot];
}
inline void setFixedSlot(uint32_t slot, const Value &value);
inline void initFixedSlot(uint32_t slot, const Value &value);
/*
* Get the number of dynamic slots to allocate to cover the properties in
* an object with the given number of fixed slots and slot span. The slot
* capacity is not stored explicitly, and the allocated size of the slot
* array is kept in sync with this count.
*/
static inline uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span);
/* Memory usage functions. */
inline size_t tenuredSizeOfThis() const;
/* Elements accessors. */
ObjectElements * getElementsHeader() const {
return ObjectElements::fromElements(elements);
}
ElementsHeader & elementsHeader() const {
NEW_OBJECT_REPRESENTATION_ONLY();
return *ElementsHeader::fromElements(elements);
}
inline HeapSlot *fixedElements() const {
MOZ_STATIC_ASSERT(2 * sizeof(Value) == sizeof(ObjectElements),
"when elements are stored inline, the first two "
"slots will hold the ObjectElements header");
return &fixedSlots()[2];
}
void setFixedElements() { this->elements = fixedElements(); }
inline bool hasDynamicElements() const {
/*
* Note: for objects with zero fixed slots this could potentially give
* a spurious 'true' result, if the end of this object is exactly
* aligned with the end of its arena and dynamic slots are allocated
* immediately afterwards. Such cases cannot occur for dense arrays
* (which have at least two fixed slots) and can only result in a leak.
*/
return !hasEmptyElements() && elements != fixedElements();
}
inline bool hasFixedElements() const {
return elements == fixedElements();
}
inline bool hasEmptyElements() const {
return elements == emptyObjectElements;
}
/* GC support. */
JS_ALWAYS_INLINE Zone *zone() const;
static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
static inline void readBarrier(ObjectImpl *obj);
static inline void writeBarrierPre(ObjectImpl *obj);
static inline void writeBarrierPost(ObjectImpl *obj, void *addr);
inline void privateWriteBarrierPre(void **oldval);
inline void privateWriteBarrierPost(void **pprivate);
void markChildren(JSTracer *trc);
/* Private data accessors. */
inline void *&privateRef(uint32_t nfixed) const { /* XXX should be private, not protected! */
/*
* The private pointer of an object can hold any word sized value.
* Private pointers are stored immediately after the last fixed slot of
* the object.
*/
MOZ_ASSERT(nfixed == numFixedSlots());
MOZ_ASSERT(hasPrivate());
HeapSlot *end = &fixedSlots()[nfixed];
return *reinterpret_cast<void**>(end);
}
inline bool hasPrivate() const {
return getClass()->hasPrivate();
}
inline void *getPrivate() const {
return privateRef(numFixedSlots());
}
inline void setPrivate(void *data);
inline void setPrivateGCThing(gc::Cell *cell);
inline void setPrivateUnbarriered(void *data);
inline void initPrivate(void *data);
/* Access private data for an object with a known number of fixed slots. */
inline void *getPrivate(uint32_t nfixed) const {
return privateRef(nfixed);
}
/* JIT Accessors */
static size_t offsetOfShape() { return offsetof(ObjectImpl, shape_); }
HeapPtrShape *addressOfShape() { return &shape_; }
static size_t offsetOfType() { return offsetof(ObjectImpl, type_); }
HeapPtrTypeObject *addressOfType() { return &type_; }
static size_t offsetOfElements() { return offsetof(ObjectImpl, elements); }
static size_t offsetOfFixedElements() {
return sizeof(ObjectImpl) + sizeof(ObjectElements);
}
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;
}
inline Handle<JSObject*>
Downcast(Handle<ObjectImpl*> obj)
{
return Handle<JSObject*>::fromMarkedLocation(reinterpret_cast<JSObject* const*>(obj.address()));
}
extern JSObject *
ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj);
/* Generic [[GetOwnProperty]] method. */
bool
GetOwnElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index, unsigned resolveFlags,
PropDesc *desc);
extern bool
GetOwnProperty(JSContext *cx, Handle<ObjectImpl*> obj, PropertyId pid, unsigned resolveFlags,
PropDesc *desc);
inline bool
GetOwnProperty(JSContext *cx, Handle<ObjectImpl*> obj, Handle<PropertyName*> name,
unsigned resolveFlags, PropDesc *desc)
{
return GetOwnProperty(cx, obj, PropertyId(name), resolveFlags, desc);
}
inline bool
GetOwnProperty(JSContext *cx, Handle<ObjectImpl*> obj, Handle<SpecialId> sid, unsigned resolveFlags,
PropDesc *desc)
{
return GetOwnProperty(cx, obj, PropertyId(sid), resolveFlags, desc);
}
/* Proposed default [[GetP]](Receiver, P) method. */
extern bool
GetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver, uint32_t index,
unsigned resolveFlags, Value *vp);
extern bool
GetProperty(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver,
Handle<PropertyId> pid, unsigned resolveFlags, MutableHandle<Value> vp);
inline bool
GetProperty(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver,
Handle<PropertyName*> name, unsigned resolveFlags, MutableHandle<Value> vp)
{
Rooted<PropertyId> pid(cx, PropertyId(name));
return GetProperty(cx, obj, receiver, pid, resolveFlags, vp);
}
inline bool
GetProperty(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver,
Handle<SpecialId> sid, unsigned resolveFlags, MutableHandle<Value> vp)
{
Rooted<PropertyId> pid(cx, PropertyId(sid));
return GetProperty(cx, obj, receiver, pid, resolveFlags, vp);
}
extern bool
DefineElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index, const PropDesc &desc,
bool shouldThrow, unsigned resolveFlags, bool *succeeded);
/* Proposed default [[SetP]](Receiver, P, V) method. */
extern bool
SetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver, uint32_t index,
const Value &v, unsigned resolveFlags, bool *succeeded);
extern bool
HasElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index, unsigned resolveFlags,
bool *found);
template <> struct GCMethods<PropertyId>
{
static PropertyId initial() { return PropertyId(); }
static ThingRootKind kind() { return THING_ROOT_PROPERTY_ID; }
static bool poisoned(PropertyId propid) { return IsPoisonedId(propid.asId()); }
};
} /* namespace js */
#endif /* vm_ObjectImpl_h */