js/src/vm/ObjectImpl.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #ifndef vm_ObjectImpl_h
     8 #define vm_ObjectImpl_h
    10 #include "mozilla/Assertions.h"
    11 #include "mozilla/Attributes.h"
    13 #include <stdint.h>
    15 #include "jsfriendapi.h"
    16 #include "jsinfer.h"
    17 #include "NamespaceImports.h"
    19 #include "gc/Barrier.h"
    20 #include "gc/Heap.h"
    21 #include "gc/Marking.h"
    22 #include "js/Value.h"
    23 #include "vm/NumericConversions.h"
    24 #include "vm/Shape.h"
    25 #include "vm/String.h"
    27 namespace js {
    29 class ObjectImpl;
    30 class Nursery;
    31 class Shape;
    33 /*
    34  * To really poison a set of values, using 'magic' or 'undefined' isn't good
    35  * enough since often these will just be ignored by buggy code (see bug 629974)
    36  * in debug builds and crash in release builds. Instead, we use a safe-for-crash
    37  * pointer.
    38  */
    39 static MOZ_ALWAYS_INLINE void
    40 Debug_SetValueRangeToCrashOnTouch(Value *beg, Value *end)
    41 {
    42 #ifdef DEBUG
    43     for (Value *v = beg; v != end; ++v)
    44         v->setObject(*reinterpret_cast<JSObject *>(0x42));
    45 #endif
    46 }
    48 static MOZ_ALWAYS_INLINE void
    49 Debug_SetValueRangeToCrashOnTouch(Value *vec, size_t len)
    50 {
    51 #ifdef DEBUG
    52     Debug_SetValueRangeToCrashOnTouch(vec, vec + len);
    53 #endif
    54 }
    56 static MOZ_ALWAYS_INLINE void
    57 Debug_SetValueRangeToCrashOnTouch(HeapValue *vec, size_t len)
    58 {
    59 #ifdef DEBUG
    60     Debug_SetValueRangeToCrashOnTouch((Value *) vec, len);
    61 #endif
    62 }
    64 static MOZ_ALWAYS_INLINE void
    65 Debug_SetSlotRangeToCrashOnTouch(HeapSlot *vec, uint32_t len)
    66 {
    67 #ifdef DEBUG
    68     Debug_SetValueRangeToCrashOnTouch((Value *) vec, len);
    69 #endif
    70 }
    72 static MOZ_ALWAYS_INLINE void
    73 Debug_SetSlotRangeToCrashOnTouch(HeapSlot *begin, HeapSlot *end)
    74 {
    75 #ifdef DEBUG
    76     Debug_SetValueRangeToCrashOnTouch((Value *) begin, end - begin);
    77 #endif
    78 }
    80 class ArrayObject;
    82 /*
    83  * ES6 20130308 draft 8.4.2.4 ArraySetLength.
    84  *
    85  * |id| must be "length", |attrs| are the attributes to be used for the newly-
    86  * changed length property, |value| is the value for the new length, and
    87  * |setterIsStrict| indicates whether invalid changes will cause a TypeError
    88  * to be thrown.
    89  */
    90 template <ExecutionMode mode>
    91 extern bool
    92 ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx,
    93                Handle<ArrayObject*> obj, HandleId id,
    94                unsigned attrs, HandleValue value, bool setterIsStrict);
    96 /*
    97  * Elements header used for all objects. The elements component of such objects
    98  * offers an efficient representation for all or some of the indexed properties
    99  * of the object, using a flat array of Values rather than a shape hierarchy
   100  * stored in the object's slots. This structure is immediately followed by an
   101  * array of elements, with the elements member in an object pointing to the
   102  * beginning of that array (the end of this structure).
   103  * See below for usage of this structure.
   104  *
   105  * The sets of properties represented by an object's elements and slots
   106  * are disjoint. The elements contain only indexed properties, while the slots
   107  * can contain both named and indexed properties; any indexes in the slots are
   108  * distinct from those in the elements. If isIndexed() is false for an object,
   109  * all indexed properties (if any) are stored in the dense elements.
   110  *
   111  * Indexes will be stored in the object's slots instead of its elements in
   112  * the following case:
   113  *  - there are more than MIN_SPARSE_INDEX slots total and the load factor
   114  *    (COUNT / capacity) is less than 0.25
   115  *  - a property is defined that has non-default property attributes.
   116  *
   117  * We track these pieces of metadata for dense elements:
   118  *  - The length property as a uint32_t, accessible for array objects with
   119  *    ArrayObject::{length,setLength}().  This is unused for non-arrays.
   120  *  - The number of element slots (capacity), gettable with
   121  *    getDenseElementsCapacity().
   122  *  - The array's initialized length, accessible with
   123  *    getDenseElementsInitializedLength().
   124  *
   125  * Holes in the array are represented by MagicValue(JS_ELEMENTS_HOLE) values.
   126  * These indicate indexes which are not dense properties of the array. The
   127  * property may, however, be held by the object's properties.
   128  *
   129  * The capacity and length of an object's elements are almost entirely
   130  * unrelated!  In general the length may be greater than, less than, or equal
   131  * to the capacity.  The first case occurs with |new Array(100)|.  The length
   132  * is 100, but the capacity remains 0 (indices below length and above capacity
   133  * must be treated as holes) until elements between capacity and length are
   134  * set.  The other two cases are common, depending upon the number of elements
   135  * in an array and the underlying allocator used for element storage.
   136  *
   137  * The only case in which the capacity and length of an object's elements are
   138  * related is when the object is an array with non-writable length.  In this
   139  * case the capacity is always less than or equal to the length.  This permits
   140  * JIT code to optimize away the check for non-writable length when assigning
   141  * to possibly out-of-range elements: such code already has to check for
   142  * |index < capacity|, and fallback code checks for non-writable length.
   143  *
   144  * The initialized length of an object specifies the number of elements that
   145  * have been initialized. All elements above the initialized length are
   146  * holes in the object, and the memory for all elements between the initialized
   147  * length and capacity is left uninitialized. The initialized length is some
   148  * value less than or equal to both the object's length and the object's
   149  * capacity.
   150  *
   151  * There is flexibility in exactly the value the initialized length must hold,
   152  * e.g. if an array has length 5, capacity 10, completely empty, it is valid
   153  * for the initialized length to be any value between zero and 5, as long as
   154  * the in memory values below the initialized length have been initialized with
   155  * a hole value. However, in such cases we want to keep the initialized length
   156  * as small as possible: if the object is known to have no hole values below
   157  * its initialized length, then it is "packed" and can be accessed much faster
   158  * by JIT code.
   159  *
   160  * Elements do not track property creation order, so enumerating the elements
   161  * of an object does not necessarily visit indexes in the order they were
   162  * created.
   163  */
   164 class ObjectElements
   165 {
   166   public:
   167     enum Flags {
   168         CONVERT_DOUBLE_ELEMENTS     = 0x1,
   170         // Present only if these elements correspond to an array with
   171         // non-writable length; never present for non-arrays.
   172         NONWRITABLE_ARRAY_LENGTH    = 0x2
   173     };
   175   private:
   176     friend class ::JSObject;
   177     friend class ObjectImpl;
   178     friend class ArrayObject;
   179     friend class Nursery;
   181     template <ExecutionMode mode>
   182     friend bool
   183     ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx,
   184                    Handle<ArrayObject*> obj, HandleId id,
   185                    unsigned attrs, HandleValue value, bool setterIsStrict);
   187     /* See Flags enum above. */
   188     uint32_t flags;
   190     /*
   191      * Number of initialized elements. This is <= the capacity, and for arrays
   192      * is <= the length. Memory for elements above the initialized length is
   193      * uninitialized, but values between the initialized length and the proper
   194      * length are conceptually holes.
   195      */
   196     uint32_t initializedLength;
   198     /* Number of allocated slots. */
   199     uint32_t capacity;
   201     /* 'length' property of array objects, unused for other objects. */
   202     uint32_t length;
   204     void staticAsserts() {
   205         static_assert(sizeof(ObjectElements) == VALUES_PER_HEADER * sizeof(Value),
   206                       "Elements size and values-per-Elements mismatch");
   207     }
   209     bool shouldConvertDoubleElements() const {
   210         return flags & CONVERT_DOUBLE_ELEMENTS;
   211     }
   212     void setShouldConvertDoubleElements() {
   213         flags |= CONVERT_DOUBLE_ELEMENTS;
   214     }
   215     void clearShouldConvertDoubleElements() {
   216         flags &= ~CONVERT_DOUBLE_ELEMENTS;
   217     }
   218     bool hasNonwritableArrayLength() const {
   219         return flags & NONWRITABLE_ARRAY_LENGTH;
   220     }
   221     void setNonwritableArrayLength() {
   222         flags |= NONWRITABLE_ARRAY_LENGTH;
   223     }
   225   public:
   226     MOZ_CONSTEXPR ObjectElements(uint32_t capacity, uint32_t length)
   227       : flags(0), initializedLength(0), capacity(capacity), length(length)
   228     {}
   230     HeapSlot *elements() {
   231         return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements));
   232     }
   233     static ObjectElements * fromElements(HeapSlot *elems) {
   234         return reinterpret_cast<ObjectElements*>(uintptr_t(elems) - sizeof(ObjectElements));
   235     }
   237     static int offsetOfFlags() {
   238         return int(offsetof(ObjectElements, flags)) - int(sizeof(ObjectElements));
   239     }
   240     static int offsetOfInitializedLength() {
   241         return int(offsetof(ObjectElements, initializedLength)) - int(sizeof(ObjectElements));
   242     }
   243     static int offsetOfCapacity() {
   244         return int(offsetof(ObjectElements, capacity)) - int(sizeof(ObjectElements));
   245     }
   246     static int offsetOfLength() {
   247         return int(offsetof(ObjectElements, length)) - int(sizeof(ObjectElements));
   248     }
   250     static bool ConvertElementsToDoubles(JSContext *cx, uintptr_t elements);
   252     static const size_t VALUES_PER_HEADER = 2;
   253 };
   255 /* Shared singleton for objects with no elements. */
   256 extern HeapSlot *const emptyObjectElements;
   258 struct Class;
   259 struct GCMarker;
   260 struct ObjectOps;
   261 class Shape;
   263 class NewObjectCache;
   264 class TaggedProto;
   266 inline Value
   267 ObjectValue(ObjectImpl &obj);
   269 #ifdef DEBUG
   270 static inline bool
   271 IsObjectValueInCompartment(js::Value v, JSCompartment *comp);
   272 #endif
   274 /*
   275  * ObjectImpl specifies the internal implementation of an object.  (In contrast
   276  * JSObject specifies an "external" interface, at the conceptual level of that
   277  * exposed in ECMAScript.)
   278  *
   279  * The |shape_| member stores the shape of the object, which includes the
   280  * object's class and the layout of all its properties.
   281  *
   282  * The |type_| member stores the type of the object, which contains its
   283  * prototype object and the possible types of its properties.
   284  *
   285  * The rest of the object stores its named properties and indexed elements.
   286  * These are stored separately from one another. Objects are followed by a
   287  * variable-sized array of values for inline storage, which may be used by
   288  * either properties of native objects (fixed slots), by elements (fixed
   289  * elements), or by other data for certain kinds of objects, such as
   290  * ArrayBufferObjects and TypedArrayObjects.
   291  *
   292  * Two native objects with the same shape are guaranteed to have the same
   293  * number of fixed slots.
   294  *
   295  * Named property storage can be split between fixed slots and a dynamically
   296  * allocated array (the slots member). For an object with N fixed slots, shapes
   297  * with slots [0..N-1] are stored in the fixed slots, and the remainder are
   298  * stored in the dynamic array. If all properties fit in the fixed slots, the
   299  * 'slots' member is nullptr.
   300  *
   301  * Elements are indexed via the 'elements' member. This member can point to
   302  * either the shared emptyObjectElements singleton, into the inline value array
   303  * (the address of the third value, to leave room for a ObjectElements header;
   304  * in this case numFixedSlots() is zero) or to a dynamically allocated array.
   305  *
   306  * Only certain combinations of slots and elements storage are possible.
   307  *
   308  * - For native objects, slots and elements may both be non-empty. The
   309  *   slots may be either names or indexes; no indexed property will be in both
   310  *   the slots and elements.
   311  *
   312  * - For non-native objects, slots and elements are both empty.
   313  *
   314  * The members of this class are currently protected; in the long run this will
   315  * will change so that some members are private, and only certain methods that
   316  * act upon them will be protected.
   317  */
   318 class ObjectImpl : public gc::BarrieredCell<ObjectImpl>
   319 {
   320     friend Zone *js::gc::BarrieredCell<ObjectImpl>::zone() const;
   321     friend Zone *js::gc::BarrieredCell<ObjectImpl>::zoneFromAnyThread() const;
   323   protected:
   324     /*
   325      * Shape of the object, encodes the layout of the object's properties and
   326      * all other information about its structure. See vm/Shape.h.
   327      */
   328     HeapPtrShape shape_;
   330     /*
   331      * The object's type and prototype. For objects with the LAZY_TYPE flag
   332      * set, this is the prototype's default 'new' type and can only be used
   333      * to get that prototype.
   334      */
   335     HeapPtrTypeObject type_;
   337     HeapSlot *slots;     /* Slots for object properties. */
   338     HeapSlot *elements;  /* Slots for object elements. */
   340     friend bool
   341     ArraySetLength(JSContext *cx, Handle<ArrayObject*> obj, HandleId id, unsigned attrs,
   342                    HandleValue value, bool setterIsStrict);
   344   private:
   345     static void staticAsserts() {
   346         static_assert(sizeof(ObjectImpl) == sizeof(shadow::Object),
   347                       "shadow interface must match actual implementation");
   348         static_assert(sizeof(ObjectImpl) % sizeof(Value) == 0,
   349                       "fixed slots after an object must be aligned");
   351         static_assert(offsetof(ObjectImpl, shape_) == offsetof(shadow::Object, shape),
   352                       "shadow shape must match actual shape");
   353         static_assert(offsetof(ObjectImpl, type_) == offsetof(shadow::Object, type),
   354                       "shadow type must match actual type");
   355         static_assert(offsetof(ObjectImpl, slots) == offsetof(shadow::Object, slots),
   356                       "shadow slots must match actual slots");
   357         static_assert(offsetof(ObjectImpl, elements) == offsetof(shadow::Object, _1),
   358                       "shadow placeholder must match actual elements");
   359     }
   361     JSObject * asObjectPtr() { return reinterpret_cast<JSObject *>(this); }
   362     const JSObject * asObjectPtr() const { return reinterpret_cast<const JSObject *>(this); }
   364     friend inline Value ObjectValue(ObjectImpl &obj);
   366     /* These functions are public, and they should remain public. */
   368   public:
   369     TaggedProto getTaggedProto() const {
   370         return type_->proto();
   371     }
   373     bool hasTenuredProto() const;
   375     const Class *getClass() const {
   376         return type_->clasp();
   377     }
   379     static inline bool
   380     isExtensible(ExclusiveContext *cx, Handle<ObjectImpl*> obj, bool *extensible);
   382     // Indicates whether a non-proxy is extensible.  Don't call on proxies!
   383     // This method really shouldn't exist -- but there are a few internal
   384     // places that want it (JITs and the like), and it'd be a pain to mark them
   385     // all as friends.
   386     bool nonProxyIsExtensible() const {
   387         MOZ_ASSERT(!isProxy());
   389         // [[Extensible]] for ordinary non-proxy objects is an object flag.
   390         return !lastProperty()->hasObjectFlag(BaseShape::NOT_EXTENSIBLE);
   391     }
   393 #ifdef DEBUG
   394     bool isProxy() const;
   395 #endif
   397     // Attempt to change the [[Extensible]] bit on |obj| to false.  Callers
   398     // must ensure that |obj| is currently extensible before calling this!
   399     static bool
   400     preventExtensions(JSContext *cx, Handle<ObjectImpl*> obj);
   402     HeapSlotArray getDenseElements() {
   403         JS_ASSERT(isNative());
   404         return HeapSlotArray(elements);
   405     }
   406     const Value &getDenseElement(uint32_t idx) {
   407         JS_ASSERT(isNative());
   408         MOZ_ASSERT(idx < getDenseInitializedLength());
   409         return elements[idx];
   410     }
   411     bool containsDenseElement(uint32_t idx) {
   412         JS_ASSERT(isNative());
   413         return idx < getDenseInitializedLength() && !elements[idx].isMagic(JS_ELEMENTS_HOLE);
   414     }
   415     uint32_t getDenseInitializedLength() {
   416         JS_ASSERT(getClass()->isNative());
   417         return getElementsHeader()->initializedLength;
   418     }
   419     uint32_t getDenseCapacity() {
   420         JS_ASSERT(getClass()->isNative());
   421         return getElementsHeader()->capacity;
   422     }
   424   protected:
   425 #ifdef DEBUG
   426     void checkShapeConsistency();
   427 #else
   428     void checkShapeConsistency() { }
   429 #endif
   431     Shape *
   432     replaceWithNewEquivalentShape(ThreadSafeContext *cx,
   433                                   Shape *existingShape, Shape *newShape = nullptr);
   435     enum GenerateShape {
   436         GENERATE_NONE,
   437         GENERATE_SHAPE
   438     };
   440     bool setFlag(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag,
   441                  GenerateShape generateShape = GENERATE_NONE);
   442     bool clearFlag(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag);
   444     bool toDictionaryMode(ThreadSafeContext *cx);
   446   private:
   447     friend class Nursery;
   449     /*
   450      * Get internal pointers to the range of values starting at start and
   451      * running for length.
   452      */
   453     void getSlotRangeUnchecked(uint32_t start, uint32_t length,
   454                                HeapSlot **fixedStart, HeapSlot **fixedEnd,
   455                                HeapSlot **slotsStart, HeapSlot **slotsEnd)
   456     {
   457         MOZ_ASSERT(start + length >= start);
   459         uint32_t fixed = numFixedSlots();
   460         if (start < fixed) {
   461             if (start + length < fixed) {
   462                 *fixedStart = &fixedSlots()[start];
   463                 *fixedEnd = &fixedSlots()[start + length];
   464                 *slotsStart = *slotsEnd = nullptr;
   465             } else {
   466                 uint32_t localCopy = fixed - start;
   467                 *fixedStart = &fixedSlots()[start];
   468                 *fixedEnd = &fixedSlots()[start + localCopy];
   469                 *slotsStart = &slots[0];
   470                 *slotsEnd = &slots[length - localCopy];
   471             }
   472         } else {
   473             *fixedStart = *fixedEnd = nullptr;
   474             *slotsStart = &slots[start - fixed];
   475             *slotsEnd = &slots[start - fixed + length];
   476         }
   477     }
   479     void getSlotRange(uint32_t start, uint32_t length,
   480                       HeapSlot **fixedStart, HeapSlot **fixedEnd,
   481                       HeapSlot **slotsStart, HeapSlot **slotsEnd)
   482     {
   483         MOZ_ASSERT(slotInRange(start + length, SENTINEL_ALLOWED));
   484         getSlotRangeUnchecked(start, length, fixedStart, fixedEnd, slotsStart, slotsEnd);
   485     }
   487   protected:
   488     friend struct GCMarker;
   489     friend class Shape;
   490     friend class NewObjectCache;
   492     void invalidateSlotRange(uint32_t start, uint32_t length) {
   493 #ifdef DEBUG
   494         HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
   495         getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
   496         Debug_SetSlotRangeToCrashOnTouch(fixedStart, fixedEnd);
   497         Debug_SetSlotRangeToCrashOnTouch(slotsStart, slotsEnd);
   498 #endif /* DEBUG */
   499     }
   501     void initializeSlotRange(uint32_t start, uint32_t count);
   503     /*
   504      * Initialize a flat array of slots to this object at a start slot.  The
   505      * caller must ensure that are enough slots.
   506      */
   507     void initSlotRange(uint32_t start, const Value *vector, uint32_t length);
   509     /*
   510      * Copy a flat array of slots to this object at a start slot. Caller must
   511      * ensure there are enough slots in this object.
   512      */
   513     void copySlotRange(uint32_t start, const Value *vector, uint32_t length);
   515 #ifdef DEBUG
   516     enum SentinelAllowed {
   517         SENTINEL_NOT_ALLOWED,
   518         SENTINEL_ALLOWED
   519     };
   521     /*
   522      * Check that slot is in range for the object's allocated slots.
   523      * If sentinelAllowed then slot may equal the slot capacity.
   524      */
   525     bool slotInRange(uint32_t slot, SentinelAllowed sentinel = SENTINEL_NOT_ALLOWED) const;
   526 #endif
   528     /*
   529      * Minimum size for dynamically allocated slots in normal Objects.
   530      * ArrayObjects don't use this limit and can have a lower slot capacity,
   531      * since they normally don't have a lot of slots.
   532      */
   533     static const uint32_t SLOT_CAPACITY_MIN = 8;
   535     HeapSlot *fixedSlots() const {
   536         return reinterpret_cast<HeapSlot *>(uintptr_t(this) + sizeof(ObjectImpl));
   537     }
   539     /*
   540      * These functions are currently public for simplicity; in the long run
   541      * it may make sense to make at least some of them private.
   542      */
   544   public:
   545     Shape * lastProperty() const {
   546         MOZ_ASSERT(shape_);
   547         return shape_;
   548     }
   550     bool generateOwnShape(ThreadSafeContext *cx, js::Shape *newShape = nullptr) {
   551         return replaceWithNewEquivalentShape(cx, lastProperty(), newShape);
   552     }
   554     JSCompartment *compartment() const {
   555         return lastProperty()->base()->compartment();
   556     }
   558     bool isNative() const {
   559         return lastProperty()->isNative();
   560     }
   562     types::TypeObject *type() const {
   563         MOZ_ASSERT(!hasLazyType());
   564         return typeRaw();
   565     }
   567     types::TypeObject *typeRaw() const {
   568         return type_;
   569     }
   571     uint32_t numFixedSlots() const {
   572         return reinterpret_cast<const shadow::Object *>(this)->numFixedSlots();
   573     }
   575     /*
   576      * Whether this is the only object which has its specified type. This
   577      * object will have its type constructed lazily as needed by analysis.
   578      */
   579     bool hasSingletonType() const {
   580         return !!type_->singleton();
   581     }
   583     /*
   584      * Whether the object's type has not been constructed yet. If an object
   585      * might have a lazy type, use getType() below, otherwise type().
   586      */
   587     bool hasLazyType() const {
   588         return type_->lazy();
   589     }
   591     uint32_t slotSpan() const {
   592         if (inDictionaryMode())
   593             return lastProperty()->base()->slotSpan();
   594         return lastProperty()->slotSpan();
   595     }
   597     /* Compute dynamicSlotsCount() for this object. */
   598     uint32_t numDynamicSlots() const {
   599         return dynamicSlotsCount(numFixedSlots(), slotSpan(), getClass());
   600     }
   603     Shape *nativeLookup(ExclusiveContext *cx, jsid id);
   604     Shape *nativeLookup(ExclusiveContext *cx, PropertyName *name) {
   605         return nativeLookup(cx, NameToId(name));
   606     }
   608     bool nativeContains(ExclusiveContext *cx, jsid id) {
   609         return nativeLookup(cx, id) != nullptr;
   610     }
   611     bool nativeContains(ExclusiveContext *cx, PropertyName* name) {
   612         return nativeLookup(cx, name) != nullptr;
   613     }
   614     bool nativeContains(ExclusiveContext *cx, Shape* shape) {
   615         return nativeLookup(cx, shape->propid()) == shape;
   616     }
   618     /* Contextless; can be called from parallel code. */
   619     Shape *nativeLookupPure(jsid id);
   620     Shape *nativeLookupPure(PropertyName *name) {
   621         return nativeLookupPure(NameToId(name));
   622     }
   624     bool nativeContainsPure(jsid id) {
   625         return nativeLookupPure(id) != nullptr;
   626     }
   627     bool nativeContainsPure(PropertyName* name) {
   628         return nativeContainsPure(NameToId(name));
   629     }
   630     bool nativeContainsPure(Shape* shape) {
   631         return nativeLookupPure(shape->propid()) == shape;
   632     }
   634     const JSClass *getJSClass() const {
   635         return Jsvalify(getClass());
   636     }
   637     bool hasClass(const Class *c) const {
   638         return getClass() == c;
   639     }
   640     const ObjectOps *getOps() const {
   641         return &getClass()->ops;
   642     }
   644     /*
   645      * An object is a delegate if it is on another object's prototype or scope
   646      * chain, and therefore the delegate might be asked implicitly to get or
   647      * set a property on behalf of another object. Delegates may be accessed
   648      * directly too, as may any object, but only those objects linked after the
   649      * head of any prototype or scope chain are flagged as delegates. This
   650      * definition helps to optimize shape-based property cache invalidation
   651      * (see Purge{Scope,Proto}Chain in jsobj.cpp).
   652      */
   653     bool isDelegate() const {
   654         return lastProperty()->hasObjectFlag(BaseShape::DELEGATE);
   655     }
   657     /*
   658      * Return true if this object is a native one that has been converted from
   659      * shared-immutable prototype-rooted shape storage to dictionary-shapes in
   660      * a doubly-linked list.
   661      */
   662     bool inDictionaryMode() const {
   663         return lastProperty()->inDictionary();
   664     }
   666     const Value &getSlot(uint32_t slot) const {
   667         MOZ_ASSERT(slotInRange(slot));
   668         uint32_t fixed = numFixedSlots();
   669         if (slot < fixed)
   670             return fixedSlots()[slot];
   671         return slots[slot - fixed];
   672     }
   674     const HeapSlot *getSlotAddressUnchecked(uint32_t slot) const {
   675         uint32_t fixed = numFixedSlots();
   676         if (slot < fixed)
   677             return fixedSlots() + slot;
   678         return slots + (slot - fixed);
   679     }
   681     HeapSlot *getSlotAddressUnchecked(uint32_t slot) {
   682         const ObjectImpl *obj = static_cast<const ObjectImpl*>(this);
   683         return const_cast<HeapSlot*>(obj->getSlotAddressUnchecked(slot));
   684     }
   686     HeapSlot *getSlotAddress(uint32_t slot) {
   687         /*
   688          * This can be used to get the address of the end of the slots for the
   689          * object, which may be necessary when fetching zero-length arrays of
   690          * slots (e.g. for callObjVarArray).
   691          */
   692         MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED));
   693         return getSlotAddressUnchecked(slot);
   694     }
   696     const HeapSlot *getSlotAddress(uint32_t slot) const {
   697         /*
   698          * This can be used to get the address of the end of the slots for the
   699          * object, which may be necessary when fetching zero-length arrays of
   700          * slots (e.g. for callObjVarArray).
   701          */
   702         MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED));
   703         return getSlotAddressUnchecked(slot);
   704     }
   706     HeapSlot &getSlotRef(uint32_t slot) {
   707         MOZ_ASSERT(slotInRange(slot));
   708         return *getSlotAddress(slot);
   709     }
   711     const HeapSlot &getSlotRef(uint32_t slot) const {
   712         MOZ_ASSERT(slotInRange(slot));
   713         return *getSlotAddress(slot);
   714     }
   716     HeapSlot &nativeGetSlotRef(uint32_t slot) {
   717         JS_ASSERT(isNative() && slot < slotSpan());
   718         return getSlotRef(slot);
   719     }
   720     const Value &nativeGetSlot(uint32_t slot) const {
   721         JS_ASSERT(isNative() && slot < slotSpan());
   722         return getSlot(slot);
   723     }
   725     void setSlot(uint32_t slot, const Value &value) {
   726         MOZ_ASSERT(slotInRange(slot));
   727         MOZ_ASSERT(IsObjectValueInCompartment(value, compartment()));
   728         getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
   729     }
   731     inline void setCrossCompartmentSlot(uint32_t slot, const Value &value) {
   732         MOZ_ASSERT(slotInRange(slot));
   733         getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
   734     }
   736     void initSlot(uint32_t slot, const Value &value) {
   737         MOZ_ASSERT(getSlot(slot).isUndefined());
   738         MOZ_ASSERT(slotInRange(slot));
   739         MOZ_ASSERT(IsObjectValueInCompartment(value, compartment()));
   740         initSlotUnchecked(slot, value);
   741     }
   743     void initCrossCompartmentSlot(uint32_t slot, const Value &value) {
   744         MOZ_ASSERT(getSlot(slot).isUndefined());
   745         MOZ_ASSERT(slotInRange(slot));
   746         initSlotUnchecked(slot, value);
   747     }
   749     void initSlotUnchecked(uint32_t slot, const Value &value) {
   750         getSlotAddressUnchecked(slot)->init(this->asObjectPtr(), HeapSlot::Slot, slot, value);
   751     }
   753     /* For slots which are known to always be fixed, due to the way they are allocated. */
   755     HeapSlot &getFixedSlotRef(uint32_t slot) {
   756         MOZ_ASSERT(slot < numFixedSlots());
   757         return fixedSlots()[slot];
   758     }
   760     const Value &getFixedSlot(uint32_t slot) const {
   761         MOZ_ASSERT(slot < numFixedSlots());
   762         return fixedSlots()[slot];
   763     }
   765     void setFixedSlot(uint32_t slot, const Value &value) {
   766         MOZ_ASSERT(slot < numFixedSlots());
   767         fixedSlots()[slot].set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
   768     }
   770     void initFixedSlot(uint32_t slot, const Value &value) {
   771         MOZ_ASSERT(slot < numFixedSlots());
   772         fixedSlots()[slot].init(this->asObjectPtr(), HeapSlot::Slot, slot, value);
   773     }
   775     /*
   776      * Get the number of dynamic slots to allocate to cover the properties in
   777      * an object with the given number of fixed slots and slot span. The slot
   778      * capacity is not stored explicitly, and the allocated size of the slot
   779      * array is kept in sync with this count.
   780      */
   781     static uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class *clasp);
   783     /* Memory usage functions. */
   784     size_t tenuredSizeOfThis() const {
   785         return js::gc::Arena::thingSize(tenuredGetAllocKind());
   786     }
   788     /* Elements accessors. */
   790     ObjectElements * getElementsHeader() const {
   791         return ObjectElements::fromElements(elements);
   792     }
   794     inline HeapSlot *fixedElements() const {
   795         static_assert(2 * sizeof(Value) == sizeof(ObjectElements),
   796                       "when elements are stored inline, the first two "
   797                       "slots will hold the ObjectElements header");
   798         return &fixedSlots()[2];
   799     }
   801 #ifdef DEBUG
   802     bool canHaveNonEmptyElements();
   803 #endif
   805     void setFixedElements() {
   806         JS_ASSERT(canHaveNonEmptyElements());
   807         this->elements = fixedElements();
   808     }
   810     inline bool hasDynamicElements() const {
   811         /*
   812          * Note: for objects with zero fixed slots this could potentially give
   813          * a spurious 'true' result, if the end of this object is exactly
   814          * aligned with the end of its arena and dynamic slots are allocated
   815          * immediately afterwards. Such cases cannot occur for dense arrays
   816          * (which have at least two fixed slots) and can only result in a leak.
   817          */
   818         return !hasEmptyElements() && elements != fixedElements();
   819     }
   821     inline bool hasFixedElements() const {
   822         return elements == fixedElements();
   823     }
   825     inline bool hasEmptyElements() const {
   826         return elements == emptyObjectElements;
   827     }
   829     /*
   830      * Get a pointer to the unused data in the object's allocation immediately
   831      * following this object, for use with objects which allocate a larger size
   832      * class than they need and store non-elements data inline.
   833      */
   834     inline void *fixedData(size_t nslots) const;
   836     /* GC support. */
   837     static ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
   839     inline void privateWriteBarrierPre(void **oldval);
   841     void privateWriteBarrierPost(void **pprivate) {
   842 #ifdef JSGC_GENERATIONAL
   843         shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell(reinterpret_cast<js::gc::Cell **>(pprivate));
   844 #endif
   845     }
   847     void markChildren(JSTracer *trc);
   849     /* Private data accessors. */
   851     inline void *&privateRef(uint32_t nfixed) const { /* XXX should be private, not protected! */
   852         /*
   853          * The private pointer of an object can hold any word sized value.
   854          * Private pointers are stored immediately after the last fixed slot of
   855          * the object.
   856          */
   857         MOZ_ASSERT(nfixed == numFixedSlots());
   858         MOZ_ASSERT(hasPrivate());
   859         HeapSlot *end = &fixedSlots()[nfixed];
   860         return *reinterpret_cast<void**>(end);
   861     }
   863     bool hasPrivate() const {
   864         return getClass()->hasPrivate();
   865     }
   866     void *getPrivate() const {
   867         return privateRef(numFixedSlots());
   868     }
   869     void setPrivate(void *data) {
   870         void **pprivate = &privateRef(numFixedSlots());
   871         privateWriteBarrierPre(pprivate);
   872         *pprivate = data;
   873     }
   875     void setPrivateGCThing(gc::Cell *cell) {
   876         void **pprivate = &privateRef(numFixedSlots());
   877         privateWriteBarrierPre(pprivate);
   878         *pprivate = reinterpret_cast<void *>(cell);
   879         privateWriteBarrierPost(pprivate);
   880     }
   882     void setPrivateUnbarriered(void *data) {
   883         void **pprivate = &privateRef(numFixedSlots());
   884         *pprivate = data;
   885     }
   886     void initPrivate(void *data) {
   887         privateRef(numFixedSlots()) = data;
   888     }
   890     /* Access private data for an object with a known number of fixed slots. */
   891     inline void *getPrivate(uint32_t nfixed) const {
   892         return privateRef(nfixed);
   893     }
   895     /* GC Accessors */
   896     void setInitialSlots(HeapSlot *newSlots) { slots = newSlots; }
   898     /* JIT Accessors */
   899     static size_t offsetOfShape() { return offsetof(ObjectImpl, shape_); }
   900     HeapPtrShape *addressOfShape() { return &shape_; }
   902     static size_t offsetOfType() { return offsetof(ObjectImpl, type_); }
   903     HeapPtrTypeObject *addressOfType() { return &type_; }
   905     static size_t offsetOfElements() { return offsetof(ObjectImpl, elements); }
   906     static size_t offsetOfFixedElements() {
   907         return sizeof(ObjectImpl) + sizeof(ObjectElements);
   908     }
   910     static size_t getFixedSlotOffset(size_t slot) {
   911         return sizeof(ObjectImpl) + slot * sizeof(Value);
   912     }
   913     static size_t getPrivateDataOffset(size_t nfixed) { return getFixedSlotOffset(nfixed); }
   914     static size_t offsetOfSlots() { return offsetof(ObjectImpl, slots); }
   915 };
   917 namespace gc {
   919 template <>
   920 MOZ_ALWAYS_INLINE Zone *
   921 BarrieredCell<ObjectImpl>::zone() const
   922 {
   923     const ObjectImpl* obj = static_cast<const ObjectImpl*>(this);
   924     JS::Zone *zone = obj->shape_->zone();
   925     JS_ASSERT(CurrentThreadCanAccessZone(zone));
   926     return zone;
   927 }
   929 template <>
   930 MOZ_ALWAYS_INLINE Zone *
   931 BarrieredCell<ObjectImpl>::zoneFromAnyThread() const
   932 {
   933     const ObjectImpl* obj = static_cast<const ObjectImpl*>(this);
   934     return obj->shape_->zoneFromAnyThread();
   935 }
   937 // TypeScript::global uses 0x1 as a special value.
   938 template<>
   939 /* static */ inline bool
   940 BarrieredCell<ObjectImpl>::isNullLike(ObjectImpl *obj)
   941 {
   942     return IsNullTaggedPointer(obj);
   943 }
   945 template<>
   946 /* static */ inline void
   947 BarrieredCell<ObjectImpl>::writeBarrierPost(ObjectImpl *obj, void *addr)
   948 {
   949 #ifdef JSGC_GENERATIONAL
   950     if (IsNullTaggedPointer(obj))
   951         return;
   952     obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell((Cell **)addr);
   953 #endif
   954 }
   956 template<>
   957 /* static */ inline void
   958 BarrieredCell<ObjectImpl>::writeBarrierPostRelocate(ObjectImpl *obj, void *addr)
   959 {
   960 #ifdef JSGC_GENERATIONAL
   961     obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putRelocatableCell((Cell **)addr);
   962 #endif
   963 }
   965 template<>
   966 /* static */ inline void
   967 BarrieredCell<ObjectImpl>::writeBarrierPostRemove(ObjectImpl *obj, void *addr)
   968 {
   969 #ifdef JSGC_GENERATIONAL
   970     obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->removeRelocatableCell((Cell **)addr);
   971 #endif
   972 }
   974 } // namespace gc
   976 inline void
   977 ObjectImpl::privateWriteBarrierPre(void **oldval)
   978 {
   979 #ifdef JSGC_INCREMENTAL
   980     JS::shadow::Zone *shadowZone = this->shadowZoneFromAnyThread();
   981     if (shadowZone->needsBarrier()) {
   982         if (*oldval && getClass()->trace)
   983             getClass()->trace(shadowZone->barrierTracer(), this->asObjectPtr());
   984     }
   985 #endif
   986 }
   988 inline Value
   989 ObjectValue(ObjectImpl &obj)
   990 {
   991     Value v;
   992     v.setObject(*obj.asObjectPtr());
   993     return v;
   994 }
   996 inline Handle<JSObject*>
   997 Downcast(Handle<ObjectImpl*> obj)
   998 {
   999     return Handle<JSObject*>::fromMarkedLocation(reinterpret_cast<JSObject* const*>(obj.address()));
  1002 #ifdef DEBUG
  1003 static inline bool
  1004 IsObjectValueInCompartment(js::Value v, JSCompartment *comp)
  1006     if (!v.isObject())
  1007         return true;
  1008     return reinterpret_cast<ObjectImpl*>(&v.toObject())->compartment() == comp;
  1010 #endif
  1012 } /* namespace js */
  1014 #endif /* vm_ObjectImpl_h */

mercurial