LCOV - code coverage report
Current view: top level - js/src/vm - NativeObject.h (source / functions) Hit Total Coverage
Test: output.info Lines: 347 423 82.0 %
Date: 2017-07-14 16:53:18 Functions: 109 126 86.5 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #ifndef vm_NativeObject_h
       8             : #define vm_NativeObject_h
       9             : 
      10             : #include "mozilla/Assertions.h"
      11             : #include "mozilla/Attributes.h"
      12             : 
      13             : #include <stdint.h>
      14             : 
      15             : #include "jsfriendapi.h"
      16             : #include "jsobj.h"
      17             : #include "NamespaceImports.h"
      18             : 
      19             : #include "gc/Barrier.h"
      20             : #include "gc/Heap.h"
      21             : #include "gc/Marking.h"
      22             : #include "js/Value.h"
      23             : #include "vm/Shape.h"
      24             : #include "vm/ShapedObject.h"
      25             : #include "vm/String.h"
      26             : #include "vm/TypeInference.h"
      27             : 
      28             : namespace js {
      29             : 
      30             : class Shape;
      31             : class TenuringTracer;
      32             : 
      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       17455 : Debug_SetValueRangeToCrashOnTouch(Value* beg, Value* end)
      41             : {
      42             : #ifdef DEBUG
      43      280449 :     for (Value* v = beg; v != end; ++v)
      44      262994 :         v->setObject(*reinterpret_cast<JSObject*>(0x48));
      45             : #endif
      46       17455 : }
      47             : 
      48             : static MOZ_ALWAYS_INLINE void
      49       17455 : Debug_SetValueRangeToCrashOnTouch(Value* vec, size_t len)
      50             : {
      51             : #ifdef DEBUG
      52       17455 :     Debug_SetValueRangeToCrashOnTouch(vec, vec + len);
      53             : #endif
      54       17455 : }
      55             : 
      56             : static MOZ_ALWAYS_INLINE void
      57             : Debug_SetValueRangeToCrashOnTouch(GCPtrValue* vec, size_t len)
      58             : {
      59             : #ifdef DEBUG
      60             :     Debug_SetValueRangeToCrashOnTouch((Value*) vec, len);
      61             : #endif
      62             : }
      63             : 
      64             : static MOZ_ALWAYS_INLINE void
      65       16926 : Debug_SetSlotRangeToCrashOnTouch(HeapSlot* vec, uint32_t len)
      66             : {
      67             : #ifdef DEBUG
      68       16926 :     Debug_SetValueRangeToCrashOnTouch((Value*) vec, len);
      69             : #endif
      70       16926 : }
      71             : 
      72             : static MOZ_ALWAYS_INLINE void
      73          32 : Debug_SetSlotRangeToCrashOnTouch(HeapSlot* begin, HeapSlot* end)
      74             : {
      75             : #ifdef DEBUG
      76          32 :     Debug_SetValueRangeToCrashOnTouch((Value*) begin, end - begin);
      77             : #endif
      78          32 : }
      79             : 
      80             : class ArrayObject;
      81             : 
      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             :  * |result| receives an error code if the change is invalid.
      88             :  */
      89             : extern bool
      90             : ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj, HandleId id,
      91             :                unsigned attrs, HandleValue value, ObjectOpResult& result);
      92             : 
      93             : /*
      94             :  * Elements header used for native objects. The elements component of such objects
      95             :  * offers an efficient representation for all or some of the indexed properties
      96             :  * of the object, using a flat array of Values rather than a shape hierarchy
      97             :  * stored in the object's slots. This structure is immediately followed by an
      98             :  * array of elements, with the elements member in an object pointing to the
      99             :  * beginning of that array (the end of this structure).
     100             :  * See below for usage of this structure.
     101             :  *
     102             :  * The sets of properties represented by an object's elements and slots
     103             :  * are disjoint. The elements contain only indexed properties, while the slots
     104             :  * can contain both named and indexed properties; any indexes in the slots are
     105             :  * distinct from those in the elements. If isIndexed() is false for an object,
     106             :  * all indexed properties (if any) are stored in the dense elements.
     107             :  *
     108             :  * Indexes will be stored in the object's slots instead of its elements in
     109             :  * the following case:
     110             :  *  - there are more than MIN_SPARSE_INDEX slots total and the load factor
     111             :  *    (COUNT / capacity) is less than 0.25
     112             :  *  - a property is defined that has non-default property attributes.
     113             :  *
     114             :  * We track these pieces of metadata for dense elements:
     115             :  *  - The length property as a uint32_t, accessible for array objects with
     116             :  *    ArrayObject::{length,setLength}().  This is unused for non-arrays.
     117             :  *  - The number of element slots (capacity), gettable with
     118             :  *    getDenseCapacity().
     119             :  *  - The array's initialized length, accessible with
     120             :  *    getDenseInitializedLength().
     121             :  *
     122             :  * Holes in the array are represented by MagicValue(JS_ELEMENTS_HOLE) values.
     123             :  * These indicate indexes which are not dense properties of the array. The
     124             :  * property may, however, be held by the object's properties.
     125             :  *
     126             :  * The capacity and length of an object's elements are almost entirely
     127             :  * unrelated!  In general the length may be greater than, less than, or equal
     128             :  * to the capacity.  The first case occurs with |new Array(100)|.  The length
     129             :  * is 100, but the capacity remains 0 (indices below length and above capacity
     130             :  * must be treated as holes) until elements between capacity and length are
     131             :  * set.  The other two cases are common, depending upon the number of elements
     132             :  * in an array and the underlying allocator used for element storage.
     133             :  *
     134             :  * The only case in which the capacity and length of an object's elements are
     135             :  * related is when the object is an array with non-writable length.  In this
     136             :  * case the capacity is always less than or equal to the length.  This permits
     137             :  * JIT code to optimize away the check for non-writable length when assigning
     138             :  * to possibly out-of-range elements: such code already has to check for
     139             :  * |index < capacity|, and fallback code checks for non-writable length.
     140             :  *
     141             :  * The initialized length of an object specifies the number of elements that
     142             :  * have been initialized. All elements above the initialized length are
     143             :  * holes in the object, and the memory for all elements between the initialized
     144             :  * length and capacity is left uninitialized. The initialized length is some
     145             :  * value less than or equal to both the object's length and the object's
     146             :  * capacity.
     147             :  *
     148             :  * There is flexibility in exactly the value the initialized length must hold,
     149             :  * e.g. if an array has length 5, capacity 10, completely empty, it is valid
     150             :  * for the initialized length to be any value between zero and 5, as long as
     151             :  * the in memory values below the initialized length have been initialized with
     152             :  * a hole value. However, in such cases we want to keep the initialized length
     153             :  * as small as possible: if the object is known to have no hole values below
     154             :  * its initialized length, then it is "packed" and can be accessed much faster
     155             :  * by JIT code.
     156             :  *
     157             :  * Elements do not track property creation order, so enumerating the elements
     158             :  * of an object does not necessarily visit indexes in the order they were
     159             :  * created.
     160             :  *
     161             :  * Shifted elements
     162             :  * ----------------
     163             :  * It's pretty common to use an array as a queue, like this:
     164             :  *
     165             :  *    while (arr.length > 0)
     166             :  *        foo(arr.shift());
     167             :  *
     168             :  * To ensure we don't get quadratic behavior on this, elements can be 'shifted'
     169             :  * in memory. tryShiftDenseElements does this by incrementing elements_ to point
     170             :  * to the next element and moving the ObjectElements header in memory (so it's
     171             :  * stored where the shifted Value used to be).
     172             :  *
     173             :  * Shifted elements can be moved when we grow the array, when the array is
     174             :  * frozen (for simplicity, shifted elements are not supported on objects that
     175             :  * are frozen, have copy-on-write elements, or on arrays with non-writable
     176             :  * length).
     177             :  */
     178             : class ObjectElements
     179             : {
     180             :   public:
     181             :     enum Flags: uint16_t {
     182             :         // Integers written to these elements must be converted to doubles.
     183             :         CONVERT_DOUBLE_ELEMENTS     = 0x1,
     184             : 
     185             :         // Present only if these elements correspond to an array with
     186             :         // non-writable length; never present for non-arrays.
     187             :         NONWRITABLE_ARRAY_LENGTH    = 0x2,
     188             : 
     189             :         // These elements are shared with another object and must be copied
     190             :         // before they can be changed. A pointer to the original owner of the
     191             :         // elements, which is immutable, is stored immediately after the
     192             :         // elements data. There is one case where elements can be written to
     193             :         // before being copied: when setting the CONVERT_DOUBLE_ELEMENTS flag
     194             :         // the shared elements may change (from ints to doubles) without
     195             :         // making a copy first.
     196             :         COPY_ON_WRITE               = 0x4,
     197             : 
     198             :         // For TypedArrays only: this TypedArray's storage is mapping shared
     199             :         // memory.  This is a static property of the TypedArray, set when it
     200             :         // is created and never changed.
     201             :         SHARED_MEMORY               = 0x8,
     202             : 
     203             :         // These elements are set to integrity level "frozen".
     204             :         FROZEN                      = 0x10,
     205             :     };
     206             : 
     207             :     // The flags word stores both the flags and the number of shifted elements.
     208             :     // Allow shifting 2047 elements before actually moving the elements.
     209             :     static const size_t NumShiftedElementsBits = 11;
     210             :     static const size_t MaxShiftedElements = (1 << NumShiftedElementsBits) - 1;
     211             :     static const size_t NumShiftedElementsShift = 32 - NumShiftedElementsBits;
     212             :     static const size_t FlagsMask = (1 << NumShiftedElementsShift) - 1;
     213             :     static_assert(MaxShiftedElements == 2047,
     214             :                   "MaxShiftedElements should match the comment");
     215             : 
     216             :   private:
     217             :     friend class ::JSObject;
     218             :     friend class ArrayObject;
     219             :     friend class NativeObject;
     220             :     friend class TenuringTracer;
     221             : 
     222             :     friend bool js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level);
     223             : 
     224             :     friend bool
     225             :     ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj, HandleId id,
     226             :                    unsigned attrs, HandleValue value, ObjectOpResult& result);
     227             : 
     228             :     // The NumShiftedElementsBits high bits of this are used to store the
     229             :     // number of shifted elements, the other bits are available for the flags.
     230             :     // See Flags enum above.
     231             :     uint32_t flags;
     232             : 
     233             :     /*
     234             :      * Number of initialized elements. This is <= the capacity, and for arrays
     235             :      * is <= the length. Memory for elements above the initialized length is
     236             :      * uninitialized, but values between the initialized length and the proper
     237             :      * length are conceptually holes.
     238             :      */
     239             :     uint32_t initializedLength;
     240             : 
     241             :     /* Number of allocated slots. */
     242             :     uint32_t capacity;
     243             : 
     244             :     /* 'length' property of array objects, unused for other objects. */
     245             :     uint32_t length;
     246             : 
     247        3734 :     bool shouldConvertDoubleElements() const {
     248        3734 :         return flags & CONVERT_DOUBLE_ELEMENTS;
     249             :     }
     250           0 :     void setShouldConvertDoubleElements() {
     251             :         // Note: allow isCopyOnWrite() here, see comment above.
     252           0 :         flags |= CONVERT_DOUBLE_ELEMENTS;
     253           0 :     }
     254             :     void clearShouldConvertDoubleElements() {
     255             :         MOZ_ASSERT(!isCopyOnWrite());
     256             :         flags &= ~CONVERT_DOUBLE_ELEMENTS;
     257             :     }
     258       15708 :     bool hasNonwritableArrayLength() const {
     259       15708 :         return flags & NONWRITABLE_ARRAY_LENGTH;
     260             :     }
     261           8 :     void setNonwritableArrayLength() {
     262             :         // See ArrayObject::setNonWritableLength.
     263           8 :         MOZ_ASSERT(capacity == initializedLength);
     264           8 :         MOZ_ASSERT(numShiftedElements() == 0);
     265           8 :         MOZ_ASSERT(!isCopyOnWrite());
     266           8 :         flags |= NONWRITABLE_ARRAY_LENGTH;
     267           8 :     }
     268       76466 :     bool isCopyOnWrite() const {
     269       76466 :         return flags & COPY_ON_WRITE;
     270             :     }
     271          12 :     void clearCopyOnWrite() {
     272          12 :         MOZ_ASSERT(isCopyOnWrite());
     273          12 :         flags &= ~COPY_ON_WRITE;
     274          12 :     }
     275             : 
     276           7 :     void addShiftedElements(uint32_t count) {
     277           7 :         MOZ_ASSERT(count < capacity);
     278           7 :         MOZ_ASSERT(count < initializedLength);
     279           7 :         MOZ_ASSERT(!(flags & (NONWRITABLE_ARRAY_LENGTH | FROZEN | COPY_ON_WRITE)));
     280           7 :         uint32_t numShifted = numShiftedElements() + count;
     281           7 :         MOZ_ASSERT(numShifted <= MaxShiftedElements);
     282           7 :         flags = (numShifted << NumShiftedElementsShift) | (flags & FlagsMask);
     283           7 :         capacity -= count;
     284           7 :         initializedLength -= count;
     285           7 :     }
     286           0 :     void unshiftShiftedElements(uint32_t count) {
     287           0 :         MOZ_ASSERT(count > 0);
     288           0 :         MOZ_ASSERT(!(flags & (NONWRITABLE_ARRAY_LENGTH | FROZEN | COPY_ON_WRITE)));
     289           0 :         uint32_t numShifted = numShiftedElements();
     290           0 :         MOZ_ASSERT(count <= numShifted);
     291           0 :         numShifted -= count;
     292           0 :         flags = (numShifted << NumShiftedElementsShift) | (flags & FlagsMask);
     293           0 :         capacity += count;
     294           0 :         initializedLength += count;
     295           0 :     }
     296           0 :     void clearShiftedElements() {
     297           0 :         flags &= FlagsMask;
     298           0 :         MOZ_ASSERT(numShiftedElements() == 0);
     299           0 :     }
     300             : 
     301             :   public:
     302        2829 :     constexpr ObjectElements(uint32_t capacity, uint32_t length)
     303        2829 :       : flags(0), initializedLength(0), capacity(capacity), length(length)
     304        2829 :     {}
     305             : 
     306             :     enum class SharedMemory {
     307             :         IsShared
     308             :     };
     309             : 
     310             :     constexpr ObjectElements(uint32_t capacity, uint32_t length, SharedMemory shmem)
     311             :       : flags(SHARED_MEMORY), initializedLength(0), capacity(capacity), length(length)
     312             :     {}
     313             : 
     314        2870 :     HeapSlot* elements() {
     315        2870 :         return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements));
     316             :     }
     317        2322 :     const HeapSlot* elements() const {
     318        2322 :         return reinterpret_cast<const HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements));
     319             :     }
     320      354583 :     static ObjectElements* fromElements(HeapSlot* elems) {
     321      354583 :         return reinterpret_cast<ObjectElements*>(uintptr_t(elems) - sizeof(ObjectElements));
     322             :     }
     323             : 
     324          71 :     bool isSharedMemory() const {
     325          71 :         return flags & SHARED_MEMORY;
     326             :     }
     327             : 
     328        2322 :     GCPtrNativeObject& ownerObject() const {
     329        2322 :         MOZ_ASSERT(isCopyOnWrite());
     330        2322 :         return *(GCPtrNativeObject*)(&elements()[initializedLength]);
     331             :     }
     332             : 
     333          11 :     static int offsetOfFlags() {
     334          11 :         return int(offsetof(ObjectElements, flags)) - int(sizeof(ObjectElements));
     335             :     }
     336          44 :     static int offsetOfInitializedLength() {
     337          44 :         return int(offsetof(ObjectElements, initializedLength)) - int(sizeof(ObjectElements));
     338             :     }
     339          10 :     static int offsetOfCapacity() {
     340          10 :         return int(offsetof(ObjectElements, capacity)) - int(sizeof(ObjectElements));
     341             :     }
     342          25 :     static int offsetOfLength() {
     343          25 :         return int(offsetof(ObjectElements, length)) - int(sizeof(ObjectElements));
     344             :     }
     345             : 
     346             :     static bool ConvertElementsToDoubles(JSContext* cx, uintptr_t elements);
     347             :     static bool MakeElementsCopyOnWrite(JSContext* cx, NativeObject* obj);
     348             :     static bool FreezeElements(JSContext* cx, HandleNativeObject obj);
     349             : 
     350       37292 :     bool isFrozen() const {
     351       37292 :         return flags & FROZEN;
     352             :     }
     353           8 :     void freeze() {
     354           8 :         MOZ_ASSERT(!isFrozen());
     355           8 :         MOZ_ASSERT(!isCopyOnWrite());
     356           8 :         flags |= FROZEN;
     357           8 :     }
     358             : 
     359           0 :     uint8_t elementAttributes() const {
     360           0 :         if (isFrozen())
     361           0 :             return JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
     362           0 :         return JSPROP_ENUMERATE;
     363             :     }
     364             : 
     365       58680 :     uint32_t numShiftedElements() const {
     366       58680 :         uint32_t numShifted = flags >> NumShiftedElementsShift;
     367       58680 :         MOZ_ASSERT_IF(numShifted > 0,
     368             :                       !(flags & (NONWRITABLE_ARRAY_LENGTH | FROZEN | COPY_ON_WRITE)));
     369       58680 :         return numShifted;
     370             :     }
     371             : 
     372        1105 :     uint32_t numAllocatedElements() const {
     373        1105 :         return VALUES_PER_HEADER + capacity + numShiftedElements();
     374             :     }
     375             : 
     376             :     // This is enough slots to store an object of this class. See the static
     377             :     // assertion below.
     378             :     static const size_t VALUES_PER_HEADER = 2;
     379             : };
     380             : 
     381             : static_assert(ObjectElements::VALUES_PER_HEADER * sizeof(HeapSlot) == sizeof(ObjectElements),
     382             :               "ObjectElements doesn't fit in the given number of slots");
     383             : 
     384             : /*
     385             :  * Shared singletons for objects with no elements.
     386             :  * emptyObjectElementsShared is used only for TypedArrays, when the TA
     387             :  * maps shared memory.
     388             :  */
     389             : extern HeapSlot* const emptyObjectElements;
     390             : extern HeapSlot* const emptyObjectElementsShared;
     391             : 
     392             : struct Class;
     393             : class GCMarker;
     394             : class Shape;
     395             : 
     396             : class NewObjectCache;
     397             : 
     398             : // Operations which change an object's dense elements can either succeed, fail,
     399             : // or be unable to complete. For native objects, the latter is used when the
     400             : // object's elements must become sparse instead. The enum below is used for
     401             : // such operations, and for similar operations on unboxed arrays and methods
     402             : // that work on both kinds of objects.
     403             : enum class DenseElementResult {
     404             :     Failure,
     405             :     Success,
     406             :     Incomplete
     407             : };
     408             : 
     409             : /*
     410             :  * NativeObject specifies the internal implementation of a native object.
     411             :  *
     412             :  * Native objects use ShapedObject::shape_ to record property information.  Two
     413             :  * native objects with the same shape are guaranteed to have the same number of
     414             :  * fixed slots.
     415             :  *
     416             :  * Native objects extend the base implementation of an object with storage for
     417             :  * the object's named properties and indexed elements.
     418             :  *
     419             :  * These are stored separately from one another. Objects are followed by a
     420             :  * variable-sized array of values for inline storage, which may be used by
     421             :  * either properties of native objects (fixed slots), by elements (fixed
     422             :  * elements), or by other data for certain kinds of objects, such as
     423             :  * ArrayBufferObjects and TypedArrayObjects.
     424             :  *
     425             :  * Named property storage can be split between fixed slots and a dynamically
     426             :  * allocated array (the slots member). For an object with N fixed slots, shapes
     427             :  * with slots [0..N-1] are stored in the fixed slots, and the remainder are
     428             :  * stored in the dynamic array. If all properties fit in the fixed slots, the
     429             :  * 'slots_' member is nullptr.
     430             :  *
     431             :  * Elements are indexed via the 'elements_' member. This member can point to
     432             :  * either the shared emptyObjectElements and emptyObjectElementsShared singletons,
     433             :  * into the inline value array (the address of the third value, to leave room
     434             :  * for a ObjectElements header;in this case numFixedSlots() is zero) or to
     435             :  * a dynamically allocated array.
     436             :  *
     437             :  * Slots and elements may both be non-empty. The slots may be either names or
     438             :  * indexes; no indexed property will be in both the slots and elements.
     439             :  */
     440             : class NativeObject : public ShapedObject
     441             : {
     442             :   protected:
     443             :     /* Slots for object properties. */
     444             :     js::HeapSlot* slots_;
     445             : 
     446             :     /* Slots for object dense elements. */
     447             :     js::HeapSlot* elements_;
     448             : 
     449             :     friend class ::JSObject;
     450             : 
     451             :   private:
     452             :     static void staticAsserts() {
     453             :         static_assert(sizeof(NativeObject) == sizeof(JSObject_Slots0),
     454             :                       "native object size must match GC thing size");
     455             :         static_assert(sizeof(NativeObject) == sizeof(shadow::Object),
     456             :                       "shadow interface must match actual implementation");
     457             :         static_assert(sizeof(NativeObject) % sizeof(Value) == 0,
     458             :                       "fixed slots after an object must be aligned");
     459             : 
     460             :         static_assert(offsetof(NativeObject, group_) == offsetof(shadow::Object, group),
     461             :                       "shadow type must match actual type");
     462             :         static_assert(offsetof(NativeObject, slots_) == offsetof(shadow::Object, slots),
     463             :                       "shadow slots must match actual slots");
     464             :         static_assert(offsetof(NativeObject, elements_) == offsetof(shadow::Object, _1),
     465             :                       "shadow placeholder must match actual elements");
     466             : 
     467             :         static_assert(MAX_FIXED_SLOTS <= Shape::FIXED_SLOTS_MAX,
     468             :                       "verify numFixedSlots() bitfield is big enough");
     469             :         static_assert(sizeof(NativeObject) + MAX_FIXED_SLOTS * sizeof(Value) == JSObject::MAX_BYTE_SIZE,
     470             :                       "inconsistent maximum object size");
     471             :     }
     472             : 
     473             :   public:
     474     6962473 :     Shape* lastProperty() const {
     475     6962473 :         MOZ_ASSERT(shape_);
     476     6962485 :         return shape_;
     477             :     }
     478             : 
     479             :     uint32_t propertyCount() const {
     480             :         return lastProperty()->entryCount();
     481             :     }
     482             : 
     483           0 :     bool hasShapeTable() const {
     484           0 :         return lastProperty()->hasTable();
     485             :     }
     486             : 
     487       21082 :     HeapSlotArray getDenseElements() {
     488       21082 :         return HeapSlotArray(elements_, !getElementsHeader()->isCopyOnWrite());
     489             :     }
     490         909 :     HeapSlotArray getDenseElementsAllowCopyOnWrite() {
     491             :         // Backdoor allowing direct access to copy on write elements.
     492         909 :         return HeapSlotArray(elements_, true);
     493             :     }
     494       20632 :     const Value& getDenseElement(uint32_t idx) const {
     495       20632 :         MOZ_ASSERT(idx < getDenseInitializedLength());
     496       20632 :         return elements_[idx];
     497             :     }
     498       10430 :     bool containsDenseElement(uint32_t idx) {
     499       10430 :         return idx < getDenseInitializedLength() && !elements_[idx].isMagic(JS_ELEMENTS_HOLE);
     500             :     }
     501       66520 :     uint32_t getDenseInitializedLength() const {
     502       66520 :         return getElementsHeader()->initializedLength;
     503             :     }
     504       41411 :     uint32_t getDenseCapacity() const {
     505       41411 :         return getElementsHeader()->capacity;
     506             :     }
     507             : 
     508          71 :     bool isSharedMemory() const {
     509          71 :         return getElementsHeader()->isSharedMemory();
     510             :     }
     511             : 
     512             :     // Update the last property, keeping the number of allocated slots in sync
     513             :     // with the object's new slot span.
     514             :     MOZ_ALWAYS_INLINE bool setLastProperty(JSContext* cx, Shape* shape);
     515             : 
     516             :     // As for setLastProperty(), but allows the number of fixed slots to
     517             :     // change. This can only be used when fixed slots are being erased from the
     518             :     // object, and only when the object will not require dynamic slots to cover
     519             :     // the new properties.
     520             :     void setLastPropertyShrinkFixedSlots(Shape* shape);
     521             : 
     522             :     // As for setLastProperty(), but changes the class associated with the
     523             :     // object to a non-native one. This leaves the object with a type and shape
     524             :     // that are (temporarily) inconsistent.
     525             :     void setLastPropertyMakeNonNative(Shape* shape);
     526             : 
     527             :     // As for setLastProperty(), but changes the class associated with the
     528             :     // object to a native one. The object's type has already been changed, and
     529             :     // this brings the shape into sync with it.
     530             :     void setLastPropertyMakeNative(JSContext* cx, Shape* shape);
     531             : 
     532             :     // Newly-created TypedArrays that map a SharedArrayBuffer are
     533             :     // marked as shared by giving them an ObjectElements that has the
     534             :     // ObjectElements::SHARED_MEMORY flag set.
     535           0 :     void setIsSharedMemory() {
     536           0 :         MOZ_ASSERT(elements_ == emptyObjectElements);
     537           0 :         elements_ = emptyObjectElementsShared;
     538           0 :     }
     539             : 
     540             :     inline bool isInWholeCellBuffer() const;
     541             : 
     542             :     static inline JS::Result<NativeObject*, JS::OOM&>
     543             :     create(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
     544             :            js::HandleShape shape, js::HandleObjectGroup group);
     545             : 
     546             :   protected:
     547             : #ifdef DEBUG
     548             :     void checkShapeConsistency();
     549             : #else
     550             :     void checkShapeConsistency() { }
     551             : #endif
     552             : 
     553             :     static Shape*
     554             :     replaceWithNewEquivalentShape(JSContext* cx, HandleNativeObject obj,
     555             :                                   Shape* existingShape, Shape* newShape = nullptr,
     556             :                                   bool accessorShape = false);
     557             : 
     558             :     /*
     559             :      * Remove the last property of an object, provided that it is safe to do so
     560             :      * (the shape and previous shape do not carry conflicting information about
     561             :      * the object itself).
     562             :      */
     563             :     inline void removeLastProperty(JSContext* cx);
     564             :     inline bool canRemoveLastProperty();
     565             : 
     566             :     /*
     567             :      * Update the slot span directly for a dictionary object, and allocate
     568             :      * slots to cover the new span if necessary.
     569             :      */
     570             :     bool setSlotSpan(JSContext* cx, uint32_t span);
     571             : 
     572             :     static MOZ_MUST_USE bool toDictionaryMode(JSContext* cx, HandleNativeObject obj);
     573             : 
     574             :   private:
     575             :     friend class TenuringTracer;
     576             : 
     577             :     /*
     578             :      * Get internal pointers to the range of values starting at start and
     579             :      * running for length.
     580             :      */
     581       44376 :     void getSlotRangeUnchecked(uint32_t start, uint32_t length,
     582             :                                HeapSlot** fixedStart, HeapSlot** fixedEnd,
     583             :                                HeapSlot** slotsStart, HeapSlot** slotsEnd)
     584             :     {
     585       44376 :         MOZ_ASSERT(start + length >= start);
     586             : 
     587       44376 :         uint32_t fixed = numFixedSlots();
     588       44376 :         if (start < fixed) {
     589       27074 :             if (start + length < fixed) {
     590       12979 :                 *fixedStart = &fixedSlots()[start];
     591       12979 :                 *fixedEnd = &fixedSlots()[start + length];
     592       12979 :                 *slotsStart = *slotsEnd = nullptr;
     593             :             } else {
     594       14095 :                 uint32_t localCopy = fixed - start;
     595       14095 :                 *fixedStart = &fixedSlots()[start];
     596       14095 :                 *fixedEnd = &fixedSlots()[start + localCopy];
     597       14095 :                 *slotsStart = &slots_[0];
     598       14095 :                 *slotsEnd = &slots_[length - localCopy];
     599             :             }
     600             :         } else {
     601       17302 :             *fixedStart = *fixedEnd = nullptr;
     602       17302 :             *slotsStart = &slots_[start - fixed];
     603       17302 :             *slotsEnd = &slots_[start - fixed + length];
     604             :         }
     605       44376 :     }
     606             : 
     607       24619 :     void getSlotRange(uint32_t start, uint32_t length,
     608             :                       HeapSlot** fixedStart, HeapSlot** fixedEnd,
     609             :                       HeapSlot** slotsStart, HeapSlot** slotsEnd)
     610             :     {
     611       24619 :         MOZ_ASSERT(slotInRange(start + length, SENTINEL_ALLOWED));
     612       24619 :         getSlotRangeUnchecked(start, length, fixedStart, fixedEnd, slotsStart, slotsEnd);
     613       24619 :     }
     614             : 
     615             :   protected:
     616             :     friend class GCMarker;
     617             :     friend class Shape;
     618             :     friend class NewObjectCache;
     619             : 
     620          16 :     void invalidateSlotRange(uint32_t start, uint32_t length) {
     621             : #ifdef DEBUG
     622             :         HeapSlot* fixedStart;
     623             :         HeapSlot* fixedEnd;
     624             :         HeapSlot* slotsStart;
     625             :         HeapSlot* slotsEnd;
     626          16 :         getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
     627          16 :         Debug_SetSlotRangeToCrashOnTouch(fixedStart, fixedEnd);
     628          16 :         Debug_SetSlotRangeToCrashOnTouch(slotsStart, slotsEnd);
     629             : #endif /* DEBUG */
     630          16 :     }
     631             : 
     632             :     void initializeSlotRange(uint32_t start, uint32_t count);
     633             : 
     634             :     /*
     635             :      * Initialize a flat array of slots to this object at a start slot.  The
     636             :      * caller must ensure that are enough slots.
     637             :      */
     638             :     void initSlotRange(uint32_t start, const Value* vector, uint32_t length);
     639             : 
     640             :     /*
     641             :      * Copy a flat array of slots to this object at a start slot. Caller must
     642             :      * ensure there are enough slots in this object.
     643             :      */
     644             :     void copySlotRange(uint32_t start, const Value* vector, uint32_t length);
     645             : 
     646             : #ifdef DEBUG
     647             :     enum SentinelAllowed {
     648             :         SENTINEL_NOT_ALLOWED,
     649             :         SENTINEL_ALLOWED
     650             :     };
     651             : 
     652             :     /*
     653             :      * Check that slot is in range for the object's allocated slots.
     654             :      * If sentinelAllowed then slot may equal the slot capacity.
     655             :      */
     656             :     bool slotInRange(uint32_t slot, SentinelAllowed sentinel = SENTINEL_NOT_ALLOWED) const;
     657             : #endif
     658             : 
     659             :     /*
     660             :      * Minimum size for dynamically allocated slots in normal Objects.
     661             :      * ArrayObjects don't use this limit and can have a lower slot capacity,
     662             :      * since they normally don't have a lot of slots.
     663             :      */
     664             :     static const uint32_t SLOT_CAPACITY_MIN = 8;
     665             : 
     666     1534009 :     HeapSlot* fixedSlots() const {
     667     1534009 :         return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(NativeObject));
     668             :     }
     669             : 
     670             :   public:
     671        4006 :     static MOZ_MUST_USE bool generateOwnShape(JSContext* cx, HandleNativeObject obj,
     672             :                                               Shape* newShape = nullptr)
     673             :     {
     674        4006 :         return replaceWithNewEquivalentShape(cx, obj, obj->lastProperty(), newShape);
     675             :     }
     676             : 
     677             :     static MOZ_MUST_USE bool shadowingShapeChange(JSContext* cx, HandleNativeObject obj,
     678             :                                                   const Shape& shape);
     679             :     static bool clearFlag(JSContext* cx, HandleNativeObject obj, BaseShape::Flag flag);
     680             : 
     681             :     // The maximum number of slots in an object.
     682             :     // |MAX_SLOTS_COUNT * sizeof(JS::Value)| shouldn't overflow
     683             :     // int32_t (see slotsSizeMustNotOverflow).
     684             :     static const uint32_t MAX_SLOTS_COUNT = (1 << 28) - 1;
     685             : 
     686       15965 :     static void slotsSizeMustNotOverflow() {
     687             :         static_assert(NativeObject::MAX_SLOTS_COUNT <= INT32_MAX / sizeof(JS::Value),
     688             :                       "every caller of this method requires that a slot "
     689             :                       "number (or slot count) count multiplied by "
     690             :                       "sizeof(Value) can't overflow uint32_t (and sometimes "
     691             :                       "int32_t, too)");
     692       15965 :     }
     693             : 
     694     7720381 :     uint32_t numFixedSlots() const {
     695     7720381 :         return reinterpret_cast<const shadow::Object*>(this)->numFixedSlots();
     696             :     }
     697          35 :     uint32_t numUsedFixedSlots() const {
     698          35 :         uint32_t nslots = lastProperty()->slotSpan(getClass());
     699          35 :         return Min(nslots, numFixedSlots());
     700             :     }
     701             :     uint32_t numFixedSlotsForCompilation() const;
     702             : 
     703     2539816 :     uint32_t slotSpan() const {
     704     2539816 :         if (inDictionaryMode())
     705      189581 :             return lastProperty()->base()->slotSpan();
     706     2350188 :         return lastProperty()->slotSpan();
     707             :     }
     708             : 
     709             :     /* Whether a slot is at a fixed offset from this object. */
     710        4548 :     bool isFixedSlot(size_t slot) {
     711        4548 :         return slot < numFixedSlots();
     712             :     }
     713             : 
     714             :     /* Index into the dynamic slots array to use for a dynamic slot. */
     715        3142 :     size_t dynamicSlotIndex(size_t slot) {
     716        3142 :         MOZ_ASSERT(slot >= numFixedSlots());
     717        3142 :         return slot - numFixedSlots();
     718             :     }
     719             : 
     720             :     /*
     721             :      * Grow or shrink slots immediately before changing the slot span.
     722             :      * The number of allocated slots is not stored explicitly, and changes to
     723             :      * the slots must track changes in the slot span.
     724             :      */
     725             :     bool growSlots(JSContext* cx, uint32_t oldCount, uint32_t newCount);
     726             :     void shrinkSlots(JSContext* cx, uint32_t oldCount, uint32_t newCount);
     727             : 
     728             :     /*
     729             :      * This method is static because it's called from JIT code. On OOM, returns
     730             :      * false without leaving a pending exception on the context.
     731             :      */
     732             :     static bool growSlotsDontReportOOM(JSContext* cx, NativeObject* obj, uint32_t newCount);
     733             : 
     734             :     /*
     735             :      * Like growSlotsDontReportOOM but for dense elements. This will return
     736             :      * false if we failed to allocate a dense element for some reason (OOM, too
     737             :      * many dense elements, non-writable array length, etc).
     738             :      */
     739             :     static bool addDenseElementDontReportOOM(JSContext* cx, NativeObject* obj);
     740             : 
     741       27276 :     bool hasDynamicSlots() const { return !!slots_; }
     742             : 
     743             :     /* Compute dynamicSlotsCount() for this object. */
     744             :     MOZ_ALWAYS_INLINE uint32_t numDynamicSlots() const;
     745             : 
     746        1287 :     bool empty() const {
     747        1287 :         return lastProperty()->isEmptyShape();
     748             :     }
     749             : 
     750             :     Shape* lookup(JSContext* cx, jsid id);
     751       14885 :     Shape* lookup(JSContext* cx, PropertyName* name) {
     752       14885 :         return lookup(cx, NameToId(name));
     753             :     }
     754             : 
     755         602 :     bool contains(JSContext* cx, jsid id) {
     756         602 :         return lookup(cx, id) != nullptr;
     757             :     }
     758             :     bool contains(JSContext* cx, PropertyName* name) {
     759             :         return lookup(cx, name) != nullptr;
     760             :     }
     761       10261 :     bool contains(JSContext* cx, Shape* shape) {
     762       10261 :         return lookup(cx, shape->propid()) == shape;
     763             :     }
     764             : 
     765           0 :     bool containsShapeOrElement(JSContext* cx, jsid id) {
     766           0 :         if (JSID_IS_INT(id) && containsDenseElement(JSID_TO_INT(id)))
     767           0 :             return true;
     768           0 :         return contains(cx, id);
     769             :     }
     770             : 
     771             :     /* Contextless; can be called from other pure code. */
     772             :     Shape* lookupPure(jsid id);
     773       41838 :     Shape* lookupPure(PropertyName* name) {
     774       41838 :         return lookupPure(NameToId(name));
     775             :     }
     776             : 
     777      117704 :     bool containsPure(jsid id) {
     778      117704 :         return lookupPure(id) != nullptr;
     779             :     }
     780         251 :     bool containsPure(PropertyName* name) {
     781         251 :         return containsPure(NameToId(name));
     782             :     }
     783          44 :     bool containsPure(Shape* shape) {
     784          44 :         return lookupPure(shape->propid()) == shape;
     785             :     }
     786             : 
     787             :     /*
     788             :      * Allocate and free an object slot.
     789             :      *
     790             :      * FIXME: bug 593129 -- slot allocation should be done by object methods
     791             :      * after calling object-parameter-free shape methods, avoiding coupling
     792             :      * logic across the object vs. shape module wall.
     793             :      */
     794             :     static bool allocDictionarySlot(JSContext* cx, HandleNativeObject obj, uint32_t* slotp);
     795             :     void freeSlot(JSContext* cx, uint32_t slot);
     796             : 
     797             :   private:
     798             :     static MOZ_ALWAYS_INLINE Shape* getChildProperty(JSContext* cx, HandleNativeObject obj,
     799             :                                                      HandleShape parent,
     800             :                                                      MutableHandle<StackShape> child);
     801             : 
     802             :   public:
     803             :     /* Add a property whose id is not yet in this scope. */
     804             :     static MOZ_ALWAYS_INLINE Shape* addProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
     805             :                                                 JSGetterOp getter, JSSetterOp setter,
     806             :                                                 uint32_t slot, unsigned attrs, unsigned flags,
     807             :                                                 bool allowDictionary = true);
     808             : 
     809             :     /* Add a data property whose id is not yet in this scope. */
     810             :     static Shape* addDataProperty(JSContext* cx, HandleNativeObject obj,
     811             :                                   jsid id_, uint32_t slot, unsigned attrs);
     812             :     static Shape* addDataProperty(JSContext* cx, HandleNativeObject obj,
     813             :                                   HandlePropertyName name, uint32_t slot, unsigned attrs);
     814             : 
     815             :     /* Add or overwrite a property for id in this scope. */
     816             :     static Shape*
     817             :     putProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
     818             :                 JSGetterOp getter, JSSetterOp setter,
     819             :                 uint32_t slot, unsigned attrs,
     820             :                 unsigned flags);
     821             :     static inline Shape*
     822             :     putProperty(JSContext* cx, HandleObject obj, PropertyName* name,
     823             :                 JSGetterOp getter, JSSetterOp setter,
     824             :                 uint32_t slot, unsigned attrs,
     825             :                 unsigned flags);
     826             : 
     827             :     /* Change the given property into a sibling with the same id in this scope. */
     828             :     static Shape*
     829             :     changeProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape,
     830             :                    unsigned attrs, JSGetterOp getter, JSSetterOp setter);
     831             : 
     832             :     /* Remove the property named by id from this object. */
     833             :     static bool removeProperty(JSContext* cx, HandleNativeObject obj, jsid id);
     834             : 
     835             :     /* Clear the scope, making it empty. */
     836             :     static void clear(JSContext* cx, HandleNativeObject obj);
     837             : 
     838             :   protected:
     839             :     /*
     840             :      * Internal helper that adds a shape not yet mapped by this object.
     841             :      *
     842             :      * Notes:
     843             :      * 1. getter and setter must be normalized based on flags (see jsscope.cpp).
     844             :      * 2. Checks for non-extensibility must be done by callers.
     845             :      */
     846             :     static Shape*
     847             :     addPropertyInternal(JSContext* cx, HandleNativeObject obj, HandleId id,
     848             :                         JSGetterOp getter, JSSetterOp setter, uint32_t slot, unsigned attrs,
     849             :                         unsigned flags, ShapeTable::Entry* entry, bool allowDictionary,
     850             :                         const AutoKeepShapeTables& keep);
     851             : 
     852             :     static MOZ_MUST_USE bool fillInAfterSwap(JSContext* cx, HandleNativeObject obj,
     853             :                                              const Vector<Value>& values, void* priv);
     854             : 
     855             :   public:
     856             :     // Return true if this object has been converted from shared-immutable
     857             :     // prototype-rooted shape storage to dictionary-shapes in a doubly-linked
     858             :     // list.
     859     3190663 :     bool inDictionaryMode() const {
     860     3190663 :         return lastProperty()->inDictionary();
     861             :     }
     862             : 
     863     1347809 :     const Value& getSlot(uint32_t slot) const {
     864     1347809 :         MOZ_ASSERT(slotInRange(slot));
     865     1347761 :         uint32_t fixed = numFixedSlots();
     866     1347866 :         if (slot < fixed)
     867      870524 :             return fixedSlots()[slot];
     868      477342 :         return slots_[slot - fixed];
     869             :     }
     870             : 
     871           0 :     const HeapSlot* getSlotAddressUnchecked(uint32_t slot) const {
     872           0 :         uint32_t fixed = numFixedSlots();
     873           0 :         if (slot < fixed)
     874           0 :             return fixedSlots() + slot;
     875           0 :         return slots_ + (slot - fixed);
     876             :     }
     877             : 
     878     1049559 :     HeapSlot* getSlotAddressUnchecked(uint32_t slot) {
     879     1049559 :         uint32_t fixed = numFixedSlots();
     880     1049600 :         if (slot < fixed)
     881      437585 :             return fixedSlots() + slot;
     882      612015 :         return slots_ + (slot - fixed);
     883             :     }
     884             : 
     885      387473 :     HeapSlot* getSlotAddress(uint32_t slot) {
     886             :         /*
     887             :          * This can be used to get the address of the end of the slots for the
     888             :          * object, which may be necessary when fetching zero-length arrays of
     889             :          * slots (e.g. for callObjVarArray).
     890             :          */
     891      387473 :         MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED));
     892      387475 :         return getSlotAddressUnchecked(slot);
     893             :     }
     894             : 
     895           0 :     const HeapSlot* getSlotAddress(uint32_t slot) const {
     896             :         /*
     897             :          * This can be used to get the address of the end of the slots for the
     898             :          * object, which may be necessary when fetching zero-length arrays of
     899             :          * slots (e.g. for callObjVarArray).
     900             :          */
     901           0 :         MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED));
     902           0 :         return getSlotAddressUnchecked(slot);
     903             :     }
     904             : 
     905      387475 :     MOZ_ALWAYS_INLINE HeapSlot& getSlotRef(uint32_t slot) {
     906      387475 :         MOZ_ASSERT(slotInRange(slot));
     907      387472 :         return *getSlotAddress(slot);
     908             :     }
     909             : 
     910           0 :     MOZ_ALWAYS_INLINE const HeapSlot& getSlotRef(uint32_t slot) const {
     911           0 :         MOZ_ASSERT(slotInRange(slot));
     912           0 :         return *getSlotAddress(slot);
     913             :     }
     914             : 
     915             :     // Check requirements on values stored to this object.
     916      261312 :     MOZ_ALWAYS_INLINE void checkStoredValue(const Value& v) {
     917      261312 :         MOZ_ASSERT(IsObjectValueInCompartment(v, compartment()));
     918      261313 :         MOZ_ASSERT(AtomIsMarked(zoneFromAnyThread(), v));
     919      261313 :     }
     920             : 
     921      184374 :     MOZ_ALWAYS_INLINE void setSlot(uint32_t slot, const Value& value) {
     922      184374 :         MOZ_ASSERT(slotInRange(slot));
     923      184374 :         checkStoredValue(value);
     924      184374 :         getSlotRef(slot).set(this, HeapSlot::Slot, slot, value);
     925      184373 :     }
     926             : 
     927       41844 :     MOZ_ALWAYS_INLINE void initSlot(uint32_t slot, const Value& value) {
     928       41844 :         MOZ_ASSERT(getSlot(slot).isUndefined());
     929       41844 :         MOZ_ASSERT(slotInRange(slot));
     930       41844 :         checkStoredValue(value);
     931       41844 :         initSlotUnchecked(slot, value);
     932       41844 :     }
     933             : 
     934      167154 :     MOZ_ALWAYS_INLINE void initSlotUnchecked(uint32_t slot, const Value& value) {
     935      167154 :         getSlotAddressUnchecked(slot)->init(this, HeapSlot::Slot, slot, value);
     936      167154 :     }
     937             : 
     938             :     // MAX_FIXED_SLOTS is the biggest number of fixed slots our GC
     939             :     // size classes will give an object.
     940             :     static const uint32_t MAX_FIXED_SLOTS = shadow::Object::MAX_FIXED_SLOTS;
     941             : 
     942             :   protected:
     943             :     MOZ_ALWAYS_INLINE bool updateSlotsForSpan(JSContext* cx, size_t oldSpan, size_t newSpan);
     944             : 
     945             :   private:
     946        2464 :     void prepareElementRangeForOverwrite(size_t start, size_t end) {
     947        2464 :         MOZ_ASSERT(end <= getDenseInitializedLength());
     948        2464 :         MOZ_ASSERT(!denseElementsAreCopyOnWrite());
     949        2474 :         for (size_t i = start; i < end; i++)
     950          10 :             elements_[i].HeapSlot::~HeapSlot();
     951        2464 :     }
     952             : 
     953             :     /*
     954             :      * Trigger the write barrier on a range of slots that will no longer be
     955             :      * reachable.
     956             :      */
     957          16 :     void prepareSlotRangeForOverwrite(size_t start, size_t end) {
     958          32 :         for (size_t i = start; i < end; i++)
     959          16 :             getSlotAddressUnchecked(i)->HeapSlot::~HeapSlot();
     960          16 :     }
     961             : 
     962             :     inline void shiftDenseElementsUnchecked(uint32_t count);
     963             : 
     964             :   public:
     965             :     static bool rollbackProperties(JSContext* cx, HandleNativeObject obj,
     966             :                                    uint32_t slotSpan);
     967             : 
     968             :     MOZ_ALWAYS_INLINE void setSlotWithType(JSContext* cx, Shape* shape,
     969             :                                            const Value& value, bool overwriting = true);
     970             : 
     971      509536 :     MOZ_ALWAYS_INLINE const Value& getReservedSlot(uint32_t index) const {
     972      509536 :         MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
     973      509536 :         return getSlot(index);
     974             :     }
     975             : 
     976           0 :     MOZ_ALWAYS_INLINE const HeapSlot& getReservedSlotRef(uint32_t index) const {
     977           0 :         MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
     978           0 :         return getSlotRef(index);
     979             :     }
     980             : 
     981           0 :     MOZ_ALWAYS_INLINE HeapSlot& getReservedSlotRef(uint32_t index) {
     982           0 :         MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
     983           0 :         return getSlotRef(index);
     984             :     }
     985             : 
     986       38852 :     MOZ_ALWAYS_INLINE void initReservedSlot(uint32_t index, const Value& v) {
     987       38852 :         MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
     988       38852 :         initSlot(index, v);
     989       38852 :     }
     990             : 
     991       20823 :     MOZ_ALWAYS_INLINE void setReservedSlot(uint32_t index, const Value& v) {
     992       20823 :         MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
     993       20823 :         setSlot(index, v);
     994       20823 :     }
     995             : 
     996             :     /* For slots which are known to always be fixed, due to the way they are allocated. */
     997             : 
     998           0 :     HeapSlot& getFixedSlotRef(uint32_t slot) {
     999           0 :         MOZ_ASSERT(slot < numFixedSlots());
    1000           0 :         return fixedSlots()[slot];
    1001             :     }
    1002             : 
    1003       17761 :     const Value& getFixedSlot(uint32_t slot) const {
    1004       17761 :         MOZ_ASSERT(slot < numFixedSlots());
    1005       17761 :         return fixedSlots()[slot];
    1006             :     }
    1007             : 
    1008       10340 :     void setFixedSlot(uint32_t slot, const Value& value) {
    1009       10340 :         MOZ_ASSERT(slot < numFixedSlots());
    1010       10340 :         checkStoredValue(value);
    1011       10340 :         fixedSlots()[slot].set(this, HeapSlot::Slot, slot, value);
    1012       10340 :     }
    1013             : 
    1014        4657 :     void initFixedSlot(uint32_t slot, const Value& value) {
    1015        4657 :         MOZ_ASSERT(slot < numFixedSlots());
    1016        4657 :         checkStoredValue(value);
    1017        4657 :         fixedSlots()[slot].init(this, HeapSlot::Slot, slot, value);
    1018        4657 :     }
    1019             : 
    1020             :     /*
    1021             :      * Get the number of dynamic slots to allocate to cover the properties in
    1022             :      * an object with the given number of fixed slots and slot span. The slot
    1023             :      * capacity is not stored explicitly, and the allocated size of the slot
    1024             :      * array is kept in sync with this count.
    1025             :      */
    1026             :     static MOZ_ALWAYS_INLINE uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span,
    1027             :                                                         const Class* clasp);
    1028             :     static MOZ_ALWAYS_INLINE uint32_t dynamicSlotsCount(Shape* shape);
    1029             : 
    1030             :     /* Elements accessors. */
    1031             : 
    1032             :     // The maximum size, in sizeof(Value), of the allocation used for an
    1033             :     // object's dense elements.  (This includes space used to store an
    1034             :     // ObjectElements instance.)
    1035             :     // |MAX_DENSE_ELEMENTS_ALLOCATION * sizeof(JS::Value)| shouldn't overflow
    1036             :     // int32_t (see elementsSizeMustNotOverflow).
    1037             :     static const uint32_t MAX_DENSE_ELEMENTS_ALLOCATION = (1 << 28) - 1;
    1038             : 
    1039             :     // The maximum number of usable dense elements in an object.
    1040             :     static const uint32_t MAX_DENSE_ELEMENTS_COUNT =
    1041             :         MAX_DENSE_ELEMENTS_ALLOCATION - ObjectElements::VALUES_PER_HEADER;
    1042             : 
    1043          25 :     static void elementsSizeMustNotOverflow() {
    1044             :         static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= INT32_MAX / sizeof(JS::Value),
    1045             :                       "every caller of this method require that an element "
    1046             :                       "count multiplied by sizeof(Value) can't overflow "
    1047             :                       "uint32_t (and sometimes int32_t ,too)");
    1048          25 :     }
    1049             : 
    1050      352841 :     ObjectElements* getElementsHeader() const {
    1051      352841 :         return ObjectElements::fromElements(elements_);
    1052             :     }
    1053             : 
    1054             :     // Returns a pointer to the first element, including shifted elements.
    1055        9442 :     inline HeapSlot* unshiftedElements() const {
    1056        9442 :         return elements_ - getElementsHeader()->numShiftedElements();
    1057             :     }
    1058             : 
    1059             :     // Like getElementsHeader, but returns a pointer to the unshifted header.
    1060             :     // This is mainly useful for free()ing dynamic elements: the pointer
    1061             :     // returned here is the one we got from malloc.
    1062        1742 :     void* getUnshiftedElementsHeader() const {
    1063        1742 :         return ObjectElements::fromElements(unshiftedElements());
    1064             :     }
    1065             : 
    1066        9645 :     uint32_t unshiftedIndex(uint32_t index) const {
    1067        9645 :         return index + getElementsHeader()->numShiftedElements();
    1068             :     }
    1069             : 
    1070             :     /* Accessors for elements. */
    1071        7350 :     bool ensureElements(JSContext* cx, uint32_t capacity) {
    1072        7350 :         MOZ_ASSERT(!denseElementsAreCopyOnWrite());
    1073        7350 :         MOZ_ASSERT(!denseElementsAreFrozen());
    1074        7350 :         if (capacity > getDenseCapacity())
    1075         514 :             return growElements(cx, capacity);
    1076        6836 :         return true;
    1077             :     }
    1078             : 
    1079             :     // Try to shift |count| dense elements, see the "Shifted elements" comment.
    1080             :     inline bool tryShiftDenseElements(uint32_t count);
    1081             : 
    1082             :     // Try to make space for |count| dense elements at the start of the array.
    1083             :     bool tryUnshiftDenseElements(uint32_t count);
    1084             : 
    1085             :     // Move the elements header and all shifted elements to the start of the
    1086             :     // allocated elements space, so that numShiftedElements is 0 afterwards.
    1087             :     void moveShiftedElements();
    1088             : 
    1089             :     // If this object has many shifted elements call moveShiftedElements.
    1090             :     void maybeMoveShiftedElements();
    1091             : 
    1092             :     static bool goodElementsAllocationAmount(JSContext* cx, uint32_t reqAllocated,
    1093             :                                              uint32_t length, uint32_t* goodAmount);
    1094             :     bool growElements(JSContext* cx, uint32_t newcap);
    1095             :     void shrinkElements(JSContext* cx, uint32_t cap);
    1096             :     void setDynamicElements(ObjectElements* header) {
    1097             :         MOZ_ASSERT(!hasDynamicElements());
    1098             :         elements_ = header->elements();
    1099             :         MOZ_ASSERT(hasDynamicElements());
    1100             :     }
    1101             : 
    1102             :     static bool CopyElementsForWrite(JSContext* cx, NativeObject* obj);
    1103             : 
    1104       11424 :     bool maybeCopyElementsForWrite(JSContext* cx) {
    1105       11424 :         if (denseElementsAreCopyOnWrite())
    1106          12 :             return CopyElementsForWrite(cx, this);
    1107       11412 :         return true;
    1108             :     }
    1109             : 
    1110             :   private:
    1111             :     inline void ensureDenseInitializedLengthNoPackedCheck(JSContext* cx,
    1112             :                                                           uint32_t index, uint32_t extra);
    1113             : 
    1114             :     // Run a post write barrier that encompasses multiple contiguous elements in a
    1115             :     // single step.
    1116             :     inline void elementsRangeWriteBarrierPost(uint32_t start, uint32_t count);
    1117             : 
    1118             :     // See the comment over setDenseElementUnchecked, this applies in the same way.
    1119        2457 :     void setDenseInitializedLengthUnchecked(uint32_t length) {
    1120        2457 :         MOZ_ASSERT(length <= getDenseCapacity());
    1121        2457 :         MOZ_ASSERT(!denseElementsAreCopyOnWrite());
    1122        2457 :         prepareElementRangeForOverwrite(length, getElementsHeader()->initializedLength);
    1123        2457 :         getElementsHeader()->initializedLength = length;
    1124        2457 :     }
    1125             : 
    1126             :     // Use this function with care.  This is done to allow sparsifying frozen
    1127             :     // objects, but should only be called in a few places, and should be
    1128             :     // audited carefully!
    1129        9253 :     void setDenseElementUnchecked(uint32_t index, const Value& val) {
    1130        9253 :         MOZ_ASSERT(index < getDenseInitializedLength());
    1131        9253 :         MOZ_ASSERT(!denseElementsAreCopyOnWrite());
    1132        9253 :         checkStoredValue(val);
    1133        9253 :         elements_[index].set(this, HeapSlot::Element, unshiftedIndex(index), val);
    1134        9253 :     }
    1135             : 
    1136             :   public:
    1137        2457 :     void setDenseInitializedLength(uint32_t length) {
    1138        2457 :         MOZ_ASSERT(!denseElementsAreFrozen());
    1139        2457 :         setDenseInitializedLengthUnchecked(length);
    1140        2457 :     }
    1141             : 
    1142             :     inline void ensureDenseInitializedLength(JSContext* cx,
    1143             :                                              uint32_t index, uint32_t extra);
    1144             : 
    1145        9253 :     void setDenseElement(uint32_t index, const Value& val) {
    1146        9253 :         MOZ_ASSERT(!denseElementsAreFrozen());
    1147        9253 :         setDenseElementUnchecked(index, val);
    1148        9253 :     }
    1149             : 
    1150         171 :     void initDenseElement(uint32_t index, const Value& val) {
    1151         171 :         MOZ_ASSERT(index < getDenseInitializedLength());
    1152         171 :         MOZ_ASSERT(!denseElementsAreCopyOnWrite());
    1153         171 :         MOZ_ASSERT(!denseElementsAreFrozen());
    1154         171 :         checkStoredValue(val);
    1155         171 :         elements_[index].init(this, HeapSlot::Element, unshiftedIndex(index), val);
    1156         171 :     }
    1157             : 
    1158        9006 :     void setDenseElementMaybeConvertDouble(uint32_t index, const Value& val) {
    1159        9006 :         if (val.isInt32() && shouldConvertDoubleElements())
    1160           0 :             setDenseElement(index, DoubleValue(val.toInt32()));
    1161             :         else
    1162        9006 :             setDenseElement(index, val);
    1163        9006 :     }
    1164             : 
    1165             :     inline void setDenseElementWithType(JSContext* cx, uint32_t index,
    1166             :                                         const Value& val);
    1167             :     inline void initDenseElementWithType(JSContext* cx, uint32_t index,
    1168             :                                          const Value& val);
    1169             :     inline void setDenseElementHole(JSContext* cx, uint32_t index);
    1170             :     static inline void removeDenseElementForSparseIndex(JSContext* cx,
    1171             :                                                         HandleNativeObject obj, uint32_t index);
    1172             : 
    1173             :     inline Value getDenseOrTypedArrayElement(uint32_t idx);
    1174             : 
    1175             :     inline void copyDenseElements(uint32_t dstStart, const Value* src, uint32_t count);
    1176             :     inline void initDenseElements(uint32_t dstStart, const Value* src, uint32_t count);
    1177             :     inline void moveDenseElements(uint32_t dstStart, uint32_t srcStart, uint32_t count);
    1178             :     inline void moveDenseElementsNoPreBarrier(uint32_t dstStart, uint32_t srcStart, uint32_t count);
    1179             : 
    1180        3734 :     bool shouldConvertDoubleElements() {
    1181        3734 :         return getElementsHeader()->shouldConvertDoubleElements();
    1182             :     }
    1183             : 
    1184             :     inline void setShouldConvertDoubleElements();
    1185             :     inline void clearShouldConvertDoubleElements();
    1186             : 
    1187       50748 :     bool denseElementsAreCopyOnWrite() {
    1188       50748 :         return getElementsHeader()->isCopyOnWrite();
    1189             :     }
    1190             : 
    1191       35452 :     bool denseElementsAreFrozen() {
    1192       35452 :         return getElementsHeader()->isFrozen();
    1193             :     }
    1194             : 
    1195             :     /* Packed information for this object's elements. */
    1196             :     inline bool writeToIndexWouldMarkNotPacked(uint32_t index);
    1197             :     inline void markDenseElementsNotPacked(JSContext* cx);
    1198             : 
    1199             :     // Ensures that the object can hold at least index + extra elements. This
    1200             :     // returns DenseElement_Success on success, DenseElement_Failed on failure
    1201             :     // to grow the array, or DenseElement_Incomplete when the object is too
    1202             :     // sparse to grow (this includes the case of index + extra overflow). In
    1203             :     // the last two cases the object is kept intact.
    1204             :     inline DenseElementResult ensureDenseElements(JSContext* cx,
    1205             :                                                   uint32_t index, uint32_t extra);
    1206             : 
    1207             :     inline DenseElementResult extendDenseElements(JSContext* cx,
    1208             :                                                   uint32_t requiredCapacity, uint32_t extra);
    1209             : 
    1210             :     /* Convert a single dense element to a sparse property. */
    1211             :     static bool sparsifyDenseElement(JSContext* cx,
    1212             :                                      HandleNativeObject obj, uint32_t index);
    1213             : 
    1214             :     /* Convert all dense elements to sparse properties. */
    1215             :     static bool sparsifyDenseElements(JSContext* cx, HandleNativeObject obj);
    1216             : 
    1217             :     /* Small objects are dense, no matter what. */
    1218             :     static const uint32_t MIN_SPARSE_INDEX = 1000;
    1219             : 
    1220             :     /*
    1221             :      * Element storage for an object will be sparse if fewer than 1/8 indexes
    1222             :      * are filled in.
    1223             :      */
    1224             :     static const unsigned SPARSE_DENSITY_RATIO = 8;
    1225             : 
    1226             :     /*
    1227             :      * Check if after growing the object's elements will be too sparse.
    1228             :      * newElementsHint is an estimated number of elements to be added.
    1229             :      */
    1230             :     bool willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint);
    1231             : 
    1232             :     /*
    1233             :      * After adding a sparse index to obj, see if it should be converted to use
    1234             :      * dense elements.
    1235             :      */
    1236             :     static DenseElementResult maybeDensifySparseElements(JSContext* cx,
    1237             :                                                          HandleNativeObject obj);
    1238             : 
    1239       14890 :     inline HeapSlot* fixedElements() const {
    1240             :         static_assert(2 * sizeof(Value) == sizeof(ObjectElements),
    1241             :                       "when elements are stored inline, the first two "
    1242             :                       "slots will hold the ObjectElements header");
    1243       14890 :         return &fixedSlots()[2];
    1244             :     }
    1245             : 
    1246             : #ifdef DEBUG
    1247             :     bool canHaveNonEmptyElements();
    1248             : #endif
    1249             : 
    1250        7190 :     void setFixedElements(uint32_t numShifted = 0) {
    1251        7190 :         MOZ_ASSERT(canHaveNonEmptyElements());
    1252        7190 :         elements_ = fixedElements() + numShifted;
    1253        7190 :     }
    1254             : 
    1255        7597 :     inline bool hasDynamicElements() const {
    1256             :         /*
    1257             :          * Note: for objects with zero fixed slots this could potentially give
    1258             :          * a spurious 'true' result, if the end of this object is exactly
    1259             :          * aligned with the end of its arena and dynamic slots are allocated
    1260             :          * immediately afterwards. Such cases cannot occur for dense arrays
    1261             :          * (which have at least two fixed slots) and can only result in a leak.
    1262             :          */
    1263        7597 :         return !hasEmptyElements() && !hasFixedElements();
    1264             :     }
    1265             : 
    1266        7700 :     inline bool hasFixedElements() const {
    1267        7700 :         return unshiftedElements() == fixedElements();
    1268             :     }
    1269             : 
    1270       53207 :     inline bool hasEmptyElements() const {
    1271       53207 :         return elements_ == emptyObjectElements || elements_ == emptyObjectElementsShared;
    1272             :     }
    1273             : 
    1274             :     /*
    1275             :      * Get a pointer to the unused data in the object's allocation immediately
    1276             :      * following this object, for use with objects which allocate a larger size
    1277             :      * class than they need and store non-elements data inline.
    1278             :      */
    1279             :     inline uint8_t* fixedData(size_t nslots) const;
    1280             : 
    1281             :     inline void privateWriteBarrierPre(void** oldval);
    1282             : 
    1283           0 :     void privateWriteBarrierPost(void** pprivate) {
    1284           0 :         gc::Cell** cellp = reinterpret_cast<gc::Cell**>(pprivate);
    1285           0 :         MOZ_ASSERT(cellp);
    1286           0 :         MOZ_ASSERT(*cellp);
    1287           0 :         gc::StoreBuffer* storeBuffer = (*cellp)->storeBuffer();
    1288           0 :         if (storeBuffer)
    1289           0 :             storeBuffer->putCell(cellp);
    1290           0 :     }
    1291             : 
    1292             :     /* Private data accessors. */
    1293             : 
    1294       27297 :     inline void*& privateRef(uint32_t nfixed) const { /* XXX should be private, not protected! */
    1295             :         /*
    1296             :          * The private pointer of an object can hold any word sized value.
    1297             :          * Private pointers are stored immediately after the last fixed slot of
    1298             :          * the object.
    1299             :          */
    1300       27297 :         MOZ_ASSERT(nfixed == numFixedSlots());
    1301       27297 :         MOZ_ASSERT(hasPrivate());
    1302       27297 :         HeapSlot* end = &fixedSlots()[nfixed];
    1303       27297 :         return *reinterpret_cast<void**>(end);
    1304             :     }
    1305             : 
    1306       27439 :     bool hasPrivate() const {
    1307       27439 :         return getClass()->hasPrivate();
    1308             :     }
    1309        7171 :     void* getPrivate() const {
    1310        7171 :         return privateRef(numFixedSlots());
    1311             :     }
    1312        8390 :     void setPrivate(void* data) {
    1313        8390 :         void** pprivate = &privateRef(numFixedSlots());
    1314        8390 :         privateWriteBarrierPre(pprivate);
    1315        8390 :         *pprivate = data;
    1316        8390 :     }
    1317             : 
    1318           0 :     void setPrivateGCThing(gc::Cell* cell) {
    1319           0 :         MOZ_ASSERT_IF(IsMarkedBlack(this),
    1320             :                       !JS::GCThingIsMarkedGray(JS::GCCellPtr(cell, cell->getTraceKind())));
    1321           0 :         void** pprivate = &privateRef(numFixedSlots());
    1322           0 :         privateWriteBarrierPre(pprivate);
    1323           0 :         *pprivate = reinterpret_cast<void*>(cell);
    1324           0 :         privateWriteBarrierPost(pprivate);
    1325           0 :     }
    1326             : 
    1327           0 :     void setPrivateUnbarriered(void* data) {
    1328           0 :         void** pprivate = &privateRef(numFixedSlots());
    1329           0 :         *pprivate = data;
    1330           0 :     }
    1331         909 :     void initPrivate(void* data) {
    1332         909 :         privateRef(numFixedSlots()) = data;
    1333         909 :     }
    1334             : 
    1335             :     /* Access private data for an object with a known number of fixed slots. */
    1336         264 :     inline void* getPrivate(uint32_t nfixed) const {
    1337         264 :         return privateRef(nfixed);
    1338             :     }
    1339             : 
    1340             :     static inline NativeObject*
    1341             :     copy(JSContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
    1342             :          HandleNativeObject templateObject);
    1343             : 
    1344             :     void updateShapeAfterMovingGC();
    1345             :     void sweepDictionaryListPointer();
    1346             : 
    1347             :     /* JIT Accessors */
    1348         138 :     static size_t offsetOfElements() { return offsetof(NativeObject, elements_); }
    1349           4 :     static size_t offsetOfFixedElements() {
    1350           4 :         return sizeof(NativeObject) + sizeof(ObjectElements);
    1351             :     }
    1352             : 
    1353        2945 :     static size_t getFixedSlotOffset(size_t slot) {
    1354        2945 :         return sizeof(NativeObject) + slot * sizeof(Value);
    1355             :     }
    1356          12 :     static size_t getPrivateDataOffset(size_t nfixed) { return getFixedSlotOffset(nfixed); }
    1357         184 :     static size_t offsetOfSlots() { return offsetof(NativeObject, slots_); }
    1358             : };
    1359             : 
    1360             : // Object class for plain native objects created using '{}' object literals,
    1361             : // 'new Object()', 'Object.create', etc.
    1362             : class PlainObject : public NativeObject
    1363             : {
    1364             :   public:
    1365             :     static const js::Class class_;
    1366             : };
    1367             : 
    1368             : inline void
    1369        8390 : NativeObject::privateWriteBarrierPre(void** oldval)
    1370             : {
    1371        8390 :     JS::shadow::Zone* shadowZone = this->shadowZoneFromAnyThread();
    1372        8390 :     if (shadowZone->needsIncrementalBarrier() && *oldval && getClass()->hasTrace())
    1373           0 :         getClass()->doTrace(shadowZone->barrierTracer(), this);
    1374        8390 : }
    1375             : 
    1376             : 
    1377             : /*** Standard internal methods *******************************************************************/
    1378             : 
    1379             : /*
    1380             :  * These functions should follow the algorithms in ES6 draft rev 29 section 9.1
    1381             :  * ("Ordinary Object Internal Methods"). It's an ongoing project.
    1382             :  *
    1383             :  * Many native objects are not "ordinary" in ES6, so these functions also have
    1384             :  * to serve some of the special needs of Functions (9.2, 9.3, 9.4.1), Arrays
    1385             :  * (9.4.2), Strings (9.4.3), and so on.
    1386             :  */
    1387             : 
    1388             : extern bool
    1389             : NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
    1390             :                      Handle<JS::PropertyDescriptor> desc,
    1391             :                      ObjectOpResult& result);
    1392             : 
    1393             : extern bool
    1394             : NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
    1395             :                      JSGetterOp getter, JSSetterOp setter, unsigned attrs,
    1396             :                      ObjectOpResult& result);
    1397             : 
    1398             : extern bool
    1399             : NativeDefineProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
    1400             :                      HandleValue value, GetterOp getter, SetterOp setter,
    1401             :                      unsigned attrs, ObjectOpResult& result);
    1402             : 
    1403             : extern bool
    1404             : NativeDefineElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue value,
    1405             :                     JSGetterOp getter, JSSetterOp setter, unsigned attrs,
    1406             :                     ObjectOpResult& result);
    1407             : 
    1408             : /* If the result out-param is omitted, throw on failure. */
    1409             : extern bool
    1410             : NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
    1411             :                      JSGetterOp getter, JSSetterOp setter, unsigned attrs);
    1412             : 
    1413             : extern bool
    1414             : NativeDefineProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
    1415             :                      HandleValue value, JSGetterOp getter, JSSetterOp setter,
    1416             :                      unsigned attrs);
    1417             : 
    1418             : extern bool
    1419             : NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* foundp);
    1420             : 
    1421             : extern bool
    1422             : NativeGetOwnPropertyDescriptor(JSContext* cx, HandleNativeObject obj, HandleId id,
    1423             :                                MutableHandle<JS::PropertyDescriptor> desc);
    1424             : 
    1425             : extern bool
    1426             : NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleValue receiver, HandleId id,
    1427             :                   MutableHandleValue vp);
    1428             : 
    1429             : extern bool
    1430             : NativeGetPropertyNoGC(JSContext* cx, NativeObject* obj, const Value& receiver, jsid id, Value* vp);
    1431             : 
    1432             : inline bool
    1433         141 : NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, MutableHandleValue vp)
    1434             : {
    1435         282 :     RootedValue receiver(cx, ObjectValue(*obj));
    1436         282 :     return NativeGetProperty(cx, obj, receiver, id, vp);
    1437             : }
    1438             : 
    1439             : bool
    1440             : SetPropertyByDefining(JSContext* cx, HandleId id, HandleValue v, HandleValue receiver,
    1441             :                       ObjectOpResult& result);
    1442             : 
    1443             : bool
    1444             : SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
    1445             :                    HandleValue receiver, ObjectOpResult& result);
    1446             : 
    1447             : /*
    1448             :  * Indicates whether an assignment operation is qualified (`x.y = 0`) or
    1449             :  * unqualified (`y = 0`). In strict mode, the latter is an error if no such
    1450             :  * variable already exists.
    1451             :  *
    1452             :  * Used as an argument to NativeSetProperty.
    1453             :  */
    1454             : enum QualifiedBool {
    1455             :     Unqualified = 0,
    1456             :     Qualified = 1
    1457             : };
    1458             : 
    1459             : extern bool
    1460             : NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v,
    1461             :                   HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result);
    1462             : 
    1463             : extern bool
    1464             : NativeSetElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v,
    1465             :                  HandleValue receiver, ObjectOpResult& result);
    1466             : 
    1467             : extern bool
    1468             : NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id, ObjectOpResult& result);
    1469             : 
    1470             : 
    1471             : /*** SpiderMonkey nonstandard internal methods ***************************************************/
    1472             : 
    1473             : template <AllowGC allowGC>
    1474             : extern bool
    1475             : NativeLookupOwnProperty(JSContext* cx,
    1476             :                         typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
    1477             :                         typename MaybeRooted<jsid, allowGC>::HandleType id,
    1478             :                         typename MaybeRooted<PropertyResult, allowGC>::MutableHandleType propp);
    1479             : 
    1480             : /*
    1481             :  * Get a property from `receiver`, after having already done a lookup and found
    1482             :  * the property on a native object `obj`.
    1483             :  *
    1484             :  * `shape` must not be null and must not be an implicit dense property. It must
    1485             :  * be present in obj's shape chain.
    1486             :  */
    1487             : extern bool
    1488             : NativeGetExistingProperty(JSContext* cx, HandleObject receiver, HandleNativeObject obj,
    1489             :                           HandleShape shape, MutableHandleValue vp);
    1490             : 
    1491             : /* * */
    1492             : 
    1493             : extern bool
    1494             : GetNameBoundInEnvironment(JSContext* cx, HandleObject env, HandleId id, MutableHandleValue vp);
    1495             : 
    1496             : } /* namespace js */
    1497             : 
    1498             : template <>
    1499             : inline bool
    1500      687798 : JSObject::is<js::NativeObject>() const { return isNative(); }
    1501             : 
    1502             : namespace js {
    1503             : 
    1504             : // Alternate to JSObject::as<NativeObject>() that tolerates null pointers.
    1505             : inline NativeObject*
    1506        4738 : MaybeNativeObject(JSObject* obj)
    1507             : {
    1508        4738 :     return obj ? &obj->as<NativeObject>() : nullptr;
    1509             : }
    1510             : 
    1511             : // Defined in NativeObject-inl.h.
    1512             : bool IsPackedArray(JSObject* obj);
    1513             : 
    1514             : extern void
    1515             : AddPropertyTypesAfterProtoChange(JSContext* cx, NativeObject* obj, ObjectGroup* oldGroup);
    1516             : 
    1517             : } // namespace js
    1518             : 
    1519             : 
    1520             : /*** Inline functions declared in jsobj.h that use the native declarations above *****************/
    1521             : 
    1522             : inline bool
    1523       20665 : js::HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
    1524             : {
    1525       20665 :     if (HasPropertyOp op = obj->getOpsHasProperty())
    1526        8023 :         return op(cx, obj, id, foundp);
    1527       12642 :     return NativeHasProperty(cx, obj.as<NativeObject>(), id, foundp);
    1528             : }
    1529             : 
    1530             : inline bool
    1531       68183 : js::GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
    1532             :                 MutableHandleValue vp)
    1533             : {
    1534       68183 :     if (GetPropertyOp op = obj->getOpsGetProperty())
    1535       12194 :         return op(cx, obj, receiver, id, vp);
    1536       55989 :     return NativeGetProperty(cx, obj.as<NativeObject>(), receiver, id, vp);
    1537             : }
    1538             : 
    1539             : inline bool
    1540        3075 : js::GetPropertyNoGC(JSContext* cx, JSObject* obj, const Value& receiver, jsid id, Value* vp)
    1541             : {
    1542        3075 :     if (obj->getOpsGetProperty())
    1543         153 :         return false;
    1544        2922 :     return NativeGetPropertyNoGC(cx, &obj->as<NativeObject>(), receiver, id, vp);
    1545             : }
    1546             : 
    1547             : inline bool
    1548        9301 : js::SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
    1549             :                 HandleValue receiver, ObjectOpResult& result)
    1550             : {
    1551        9301 :     if (obj->getOpsSetProperty())
    1552         386 :         return JSObject::nonNativeSetProperty(cx, obj, id, v, receiver, result);
    1553        8915 :     return NativeSetProperty(cx, obj.as<NativeObject>(), id, v, receiver, Qualified, result);
    1554             : }
    1555             : 
    1556             : inline bool
    1557           0 : js::SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v,
    1558             :                HandleValue receiver, ObjectOpResult& result)
    1559             : {
    1560           0 :     if (obj->getOpsSetProperty())
    1561           0 :         return JSObject::nonNativeSetElement(cx, obj, index, v, receiver, result);
    1562           0 :     return NativeSetElement(cx, obj.as<NativeObject>(), index, v, receiver, result);
    1563             : }
    1564             : 
    1565             : #endif /* vm_NativeObject_h */

Generated by: LCOV version 1.13