/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * 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 ObjectImpl_h___ #define ObjectImpl_h___ #include "mozilla/Assertions.h" #include "mozilla/StandardInteger.h" #include "jsfriendapi.h" #include "jsinfer.h" #include "jsval.h" #include "gc/Barrier.h" namespace js { /* * Header structure for object element arrays. 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. */ class ObjectElements { friend struct ::JSObject; /* Number of allocated slots. */ uint32_t capacity; /* * 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. */ uint32_t initializedLength; /* 'length' property of array objects, unused for other objects. */ uint32_t length; /* :XXX: bug 586842 store state about sparse slots. */ uint32_t unused; void staticAsserts() { MOZ_STATIC_ASSERT(sizeof(ObjectElements) == VALUES_PER_HEADER * sizeof(Value), "Elements size and values-per-Elements mismatch"); } public: ObjectElements(uint32_t capacity, uint32_t length) : capacity(capacity), initializedLength(0), length(length) {} HeapSlot *elements() { return (HeapSlot *)(uintptr_t(this) + sizeof(ObjectElements)); } static ObjectElements * fromElements(HeapSlot *elems) { return (ObjectElements *)(uintptr_t(elems) - sizeof(ObjectElements)); } static int offsetOfCapacity() { return (int)offsetof(ObjectElements, capacity) - (int)sizeof(ObjectElements); } static int offsetOfInitializedLength() { return (int)offsetof(ObjectElements, initializedLength) - (int)sizeof(ObjectElements); } static int offsetOfLength() { return (int)offsetof(ObjectElements, length) - (int)sizeof(ObjectElements); } static const size_t VALUES_PER_HEADER = 2; }; /* Shared singleton for objects with no elements. */ extern HeapSlot *emptyObjectElements; struct Class; struct GCMarker; struct ObjectOps; struct Shape; class NewObjectCache; /* * 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 properties and elements storage are currently * possible. This will be changing soon :XXX: bug 586842. * * - For objects other than arrays and typed arrays, the elements are empty. * * - For 'slow' arrays, both elements and properties are used, but the * elements have zero capacity --- only the length member is used. * * - For dense arrays, elements are used and properties are not used. * * - 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. */ 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(this); } protected: friend struct GCMarker; friend struct Shape; friend class NewObjectCache; /* Minimum size for dynamically allocated slots. */ static const uint32_t SLOT_CAPACITY_MIN = 8; HeapSlot *fixedSlots() const { return reinterpret_cast(uintptr_t(this) + sizeof(ObjectImpl)); } /* * These functions are currently public for simplicity; in the long run * it may make sense to make at least some of them private. */ public: Shape * lastProperty() const { MOZ_ASSERT(shape_); return shape_; } types::TypeObject *type() const { MOZ_ASSERT(!hasLazyType()); return type_; } size_t numFixedSlots() const { return reinterpret_cast(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 bool isNative() const; const Shape * nativeLookup(JSContext *cx, jsid id); inline Class *getClass() const; inline JSClass *getJSClass() const; inline bool hasClass(const Class *c) const; 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; /* * 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 size_t dynamicSlotsCount(size_t nfixed, size_t span); /* Memory usage functions. */ inline size_t sizeOfThis() const; /* Elements accessors. */ ObjectElements * getElementsHeader() const { return ObjectElements::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 elements != emptyObjectElements && elements != fixedElements(); } /* Write barrier support. */ 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 **oldval); /* 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); } /* These functions are public, and they should remain public. */ public: JSObject * getProto() const { return type_->proto; } inline bool isExtensible() const; }; } /* namespace js */ #endif /* ObjectImpl_h__ */