tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

NativeObject.h (72526B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      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 #include "mozilla/Maybe.h"
     13 
     14 #include <algorithm>
     15 #include <stdint.h>
     16 
     17 #include "NamespaceImports.h"
     18 
     19 #include "gc/Barrier.h"
     20 #include "gc/BufferAllocator.h"
     21 #include "gc/MaybeRooted.h"
     22 #include "gc/ZoneAllocator.h"
     23 #include "js/shadow/Object.h"  // JS::shadow::Object
     24 #include "js/shadow/Zone.h"    // JS::shadow::Zone
     25 #include "js/Value.h"
     26 #include "vm/GetterSetter.h"
     27 #include "vm/JSAtomUtils.h"  // AtomIsMarked
     28 #include "vm/JSObject.h"
     29 #include "vm/Shape.h"
     30 #include "vm/StringType.h"
     31 
     32 namespace js {
     33 
     34 class JS_PUBLIC_API GenericPrinter;
     35 class IteratorProperty;
     36 class PropertyResult;
     37 
     38 namespace gc {
     39 class TenuringTracer;
     40 }  // namespace gc
     41 
     42 /*
     43 * To really poison a set of values, using 'magic' or 'undefined' isn't good
     44 * enough since often these will just be ignored by buggy code (see bug 629974)
     45 * in debug builds and crash in release builds. Instead, we use a safe-for-crash
     46 * pointer.
     47 */
     48 static MOZ_ALWAYS_INLINE void Debug_SetValueRangeToCrashOnTouch(Value* beg,
     49                                                                Value* end) {
     50 #ifdef DEBUG
     51  for (Value* v = beg; v != end; ++v) {
     52    *v = js::PoisonedObjectValue(0x48);
     53  }
     54 #endif
     55 }
     56 
     57 static MOZ_ALWAYS_INLINE void Debug_SetValueRangeToCrashOnTouch(Value* vec,
     58                                                                size_t len) {
     59 #ifdef DEBUG
     60  Debug_SetValueRangeToCrashOnTouch(vec, vec + len);
     61 #endif
     62 }
     63 
     64 static MOZ_ALWAYS_INLINE void Debug_SetValueRangeToCrashOnTouch(
     65    GCPtr<Value>* vec, size_t len) {
     66 #ifdef DEBUG
     67  Debug_SetValueRangeToCrashOnTouch((Value*)vec, len);
     68 #endif
     69 }
     70 
     71 static MOZ_ALWAYS_INLINE void Debug_SetSlotRangeToCrashOnTouch(HeapSlot* vec,
     72                                                               uint32_t len) {
     73 #ifdef DEBUG
     74  Debug_SetValueRangeToCrashOnTouch((Value*)vec, len);
     75 #endif
     76 }
     77 
     78 static MOZ_ALWAYS_INLINE void Debug_SetSlotRangeToCrashOnTouch(HeapSlot* begin,
     79                                                               HeapSlot* end) {
     80 #ifdef DEBUG
     81  Debug_SetValueRangeToCrashOnTouch((Value*)begin, end - begin);
     82 #endif
     83 }
     84 
     85 class ArrayObject;
     86 
     87 /**
     88 * 10.4.2.4 ArraySetLength ( A, Desc )
     89 *
     90 * ES2025 draft rev ac21460fedf4b926520b06c9820bdbebad596a8b
     91 *
     92 * |id| must be "length", |desc| is the new non-accessor descriptor, and
     93 * |result| receives an error code if the change is invalid.
     94 */
     95 extern bool ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj, HandleId id,
     96                           Handle<PropertyDescriptor> desc,
     97                           ObjectOpResult& result);
     98 
     99 /*
    100 * [SMDOC] NativeObject Elements layout
    101 *
    102 * Elements header used for native objects. The elements component of such
    103 * objects offers an efficient representation for all or some of the indexed
    104 * properties of the object, using a flat array of Values rather than a shape
    105 * hierarchy stored in the object's slots. This structure is immediately
    106 * followed by an array of elements, with the elements member in an object
    107 * pointing to the beginning of that array (the end of this structure). See
    108 * below for usage of this structure.
    109 *
    110 * The sets of properties represented by an object's elements and slots
    111 * are disjoint. The elements contain only indexed properties, while the slots
    112 * can contain both named and indexed properties; any indexes in the slots are
    113 * distinct from those in the elements. If isIndexed() is false for an object,
    114 * all indexed properties (if any) are stored in the dense elements.
    115 *
    116 * Indexes will be stored in the object's slots instead of its elements in
    117 * the following case:
    118 *  - there are more than MIN_SPARSE_INDEX slots total and the load factor
    119 *    (COUNT / capacity) is less than 0.25
    120 *  - a property is defined that has non-default property attributes.
    121 *
    122 * We track these pieces of metadata for dense elements:
    123 *  - The length property as a uint32_t, accessible for array objects with
    124 *    ArrayObject::{length,setLength}().  This is unused for non-arrays.
    125 *  - The number of element slots (capacity), gettable with
    126 *    getDenseCapacity().
    127 *  - The array's initialized length, accessible with
    128 *    getDenseInitializedLength().
    129 *
    130 * Holes in the array are represented by MagicValue(JS_ELEMENTS_HOLE) values.
    131 * These indicate indexes which are not dense properties of the array. The
    132 * property may, however, be held by the object's properties.
    133 *
    134 * The capacity and length of an object's elements are almost entirely
    135 * unrelated!  In general the length may be greater than, less than, or equal
    136 * to the capacity.  The first case occurs with |new Array(100)|.  The length
    137 * is 100, but the capacity remains 0 (indices below length and above capacity
    138 * must be treated as holes) until elements between capacity and length are
    139 * set.  The other two cases are common, depending upon the number of elements
    140 * in an array and the underlying allocator used for element storage.
    141 *
    142 * The only case in which the capacity and length of an object's elements are
    143 * related is when the object is an array with non-writable length.  In this
    144 * case the capacity is always less than or equal to the length.  This permits
    145 * JIT code to optimize away the check for non-writable length when assigning
    146 * to possibly out-of-range elements: such code already has to check for
    147 * |index < capacity|, and fallback code checks for non-writable length.
    148 *
    149 * The initialized length of an object specifies the number of elements that
    150 * have been initialized. All elements above the initialized length are
    151 * holes in the object, and the memory for all elements between the initialized
    152 * length and capacity is left uninitialized. The initialized length is some
    153 * value less than or equal to both the object's length and the object's
    154 * capacity.
    155 *
    156 * There is flexibility in exactly the value the initialized length must hold,
    157 * e.g. if an array has length 5, capacity 10, completely empty, it is valid
    158 * for the initialized length to be any value between zero and 5, as long as
    159 * the in memory values below the initialized length have been initialized with
    160 * a hole value. However, in such cases we want to keep the initialized length
    161 * as small as possible: if the object is known to have no hole values below
    162 * its initialized length, then it is "packed" and can be accessed much faster
    163 * by JIT code.
    164 *
    165 * Elements do not track property creation order, so enumerating the elements
    166 * of an object does not necessarily visit indexes in the order they were
    167 * created.
    168 *
    169 *
    170 * [SMDOC] NativeObject shifted elements optimization
    171 *
    172 * Shifted elements
    173 * ----------------
    174 * It's pretty common to use an array as a queue, like this:
    175 *
    176 *    while (arr.length > 0)
    177 *        foo(arr.shift());
    178 *
    179 * To ensure we don't get quadratic behavior on this, elements can be 'shifted'
    180 * in memory. tryShiftDenseElements does this by incrementing elements_ to point
    181 * to the next element and moving the ObjectElements header in memory (so it's
    182 * stored where the shifted Value used to be).
    183 *
    184 * Shifted elements can be moved when we grow the array, when the array is
    185 * made non-extensible (for simplicity, shifted elements are not supported on
    186 * objects that are non-extensible, have copy-on-write elements, or on arrays
    187 * with non-writable length).
    188 */
    189 class ObjectElements {
    190 public:
    191  enum Flags : uint16_t {
    192    // Elements are stored inline in the object allocation.
    193    // An object allocated with the FIXED flag set can have the flag unset later
    194    // if `growElements()` is called to increase the capacity beyond what was
    195    // initially allocated. Once the flag is unset, it will remain so for the
    196    // rest of the lifetime of the object.
    197    FIXED = 0x1,
    198 
    199    // Present only if these elements correspond to an array with
    200    // non-writable length; never present for non-arrays.
    201    NONWRITABLE_ARRAY_LENGTH = 0x2,
    202 
    203    // For TypedArrays only: this TypedArray's storage is mapping shared
    204    // memory.  This is a static property of the TypedArray, set when it
    205    // is created and never changed.
    206    SHARED_MEMORY = 0x8,
    207 
    208    // These elements are not extensible. If this flag is set, the object's
    209    // Shape must also have the NotExtensible flag. This exists on
    210    // ObjectElements in addition to Shape to simplify JIT code.
    211    NOT_EXTENSIBLE = 0x10,
    212 
    213    // These elements are set to integrity level "sealed". If this flag is
    214    // set, the NOT_EXTENSIBLE flag must be set as well.
    215    SEALED = 0x20,
    216 
    217    // These elements are set to integrity level "frozen". If this flag is
    218    // set, the SEALED flag must be set as well.
    219    //
    220    // This flag must only be set if the Shape has the FrozenElements flag.
    221    // The Shape flag ensures a shape guard can be used to guard against frozen
    222    // elements. The ObjectElements flag is convenient for JIT code and
    223    // ObjectElements assertions.
    224    FROZEN = 0x40,
    225 
    226    // If this flag is not set, the elements are guaranteed to contain no hole
    227    // values (the JS_ELEMENTS_HOLE MagicValue) in [0, initializedLength).
    228    NON_PACKED = 0x80,
    229 
    230    // If this flag is not set, there's definitely no for-in iterator that
    231    // covers these dense elements so elements can be deleted without calling
    232    // SuppressDeletedProperty. This is used by fast paths for various Array
    233    // builtins. See also NativeObject::denseElementsMaybeInIteration.
    234    MAYBE_IN_ITERATION = 0x100,
    235  };
    236 
    237  // The flags word stores both the flags and the number of shifted elements.
    238  // Allow shifting 2047 elements before actually moving the elements.
    239  static const size_t NumShiftedElementsBits = 11;
    240  static const size_t MaxShiftedElements = (1 << NumShiftedElementsBits) - 1;
    241  static const size_t NumShiftedElementsShift = 32 - NumShiftedElementsBits;
    242  static const size_t FlagsMask = (1 << NumShiftedElementsShift) - 1;
    243  static_assert(MaxShiftedElements == 2047,
    244                "MaxShiftedElements should match the comment");
    245 
    246 private:
    247  friend class ::JSObject;
    248  friend class ArrayObject;
    249  friend class NativeObject;
    250  friend class gc::TenuringTracer;
    251 
    252  friend bool js::SetIntegrityLevel(JSContext* cx, HandleObject obj,
    253                                    IntegrityLevel level);
    254 
    255  friend bool ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj,
    256                             HandleId id, Handle<PropertyDescriptor> desc,
    257                             ObjectOpResult& result);
    258 
    259  // The NumShiftedElementsBits high bits of this are used to store the
    260  // number of shifted elements, the other bits are available for the flags.
    261  // See Flags enum above.
    262  uint32_t flags;
    263 
    264  /*
    265   * Number of initialized elements. This is <= the capacity, and for arrays
    266   * is <= the length. Memory for elements above the initialized length is
    267   * uninitialized, but values between the initialized length and the proper
    268   * length are conceptually holes.
    269   */
    270  uint32_t initializedLength;
    271 
    272  /* Number of allocated slots. */
    273  uint32_t capacity;
    274 
    275  /* 'length' property of array objects, unused for other objects. */
    276  uint32_t length;
    277 
    278  void setNonwritableArrayLength() {
    279    // See ArrayObject::setNonWritableLength.
    280    MOZ_ASSERT(capacity == initializedLength);
    281    MOZ_ASSERT(numShiftedElements() == 0);
    282    flags |= NONWRITABLE_ARRAY_LENGTH;
    283  }
    284 
    285  void addShiftedElements(uint32_t count) {
    286    MOZ_ASSERT(count < capacity);
    287    MOZ_ASSERT(count < initializedLength);
    288    MOZ_ASSERT(!(
    289        flags & (NONWRITABLE_ARRAY_LENGTH | NOT_EXTENSIBLE | SEALED | FROZEN)));
    290    uint32_t numShifted = numShiftedElements() + count;
    291    MOZ_ASSERT(numShifted <= MaxShiftedElements);
    292    flags = (numShifted << NumShiftedElementsShift) | (flags & FlagsMask);
    293    capacity -= count;
    294    initializedLength -= count;
    295  }
    296  void unshiftShiftedElements(uint32_t count) {
    297    MOZ_ASSERT(count > 0);
    298    MOZ_ASSERT(!(
    299        flags & (NONWRITABLE_ARRAY_LENGTH | NOT_EXTENSIBLE | SEALED | FROZEN)));
    300    uint32_t numShifted = numShiftedElements();
    301    MOZ_ASSERT(count <= numShifted);
    302    numShifted -= count;
    303    flags = (numShifted << NumShiftedElementsShift) | (flags & FlagsMask);
    304    capacity += count;
    305    initializedLength += count;
    306  }
    307  void clearShiftedElements() {
    308    flags &= FlagsMask;
    309    MOZ_ASSERT(numShiftedElements() == 0);
    310  }
    311 
    312  void markNonPacked() { flags |= NON_PACKED; }
    313 
    314  void markMaybeInIteration() { flags |= MAYBE_IN_ITERATION; }
    315 
    316  void setNotExtensible() {
    317    MOZ_ASSERT(!isNotExtensible());
    318    flags |= NOT_EXTENSIBLE;
    319  }
    320 
    321  void seal() {
    322    MOZ_ASSERT(isNotExtensible());
    323    MOZ_ASSERT(!isSealed());
    324    MOZ_ASSERT(!isFrozen());
    325    flags |= SEALED;
    326  }
    327  void freeze() {
    328    MOZ_ASSERT(isNotExtensible());
    329    MOZ_ASSERT(isSealed());
    330    MOZ_ASSERT(!isFrozen());
    331    flags |= FROZEN;
    332  }
    333 
    334  bool isFrozen() const { return flags & FROZEN; }
    335 
    336 public:
    337  constexpr ObjectElements(uint32_t capacity, uint32_t length)
    338      : flags(0), initializedLength(0), capacity(capacity), length(length) {}
    339 
    340  enum class SharedMemory { IsShared };
    341 
    342  constexpr ObjectElements(uint32_t capacity, uint32_t length,
    343                           SharedMemory shmem)
    344      : flags(SHARED_MEMORY),
    345        initializedLength(0),
    346        capacity(capacity),
    347        length(length) {}
    348 
    349  HeapSlot* elements() {
    350    return reinterpret_cast<HeapSlot*>(uintptr_t(this) +
    351                                       sizeof(ObjectElements));
    352  }
    353  const HeapSlot* elements() const {
    354    return reinterpret_cast<const HeapSlot*>(uintptr_t(this) +
    355                                             sizeof(ObjectElements));
    356  }
    357  static ObjectElements* fromElements(HeapSlot* elems) {
    358    return reinterpret_cast<ObjectElements*>(uintptr_t(elems) -
    359                                             sizeof(ObjectElements));
    360  }
    361 
    362  bool isSharedMemory() const { return flags & SHARED_MEMORY; }
    363 
    364  static int offsetOfFlags() {
    365    return int(offsetof(ObjectElements, flags)) - int(sizeof(ObjectElements));
    366  }
    367  static int offsetOfInitializedLength() {
    368    return int(offsetof(ObjectElements, initializedLength)) -
    369           int(sizeof(ObjectElements));
    370  }
    371  static int offsetOfCapacity() {
    372    return int(offsetof(ObjectElements, capacity)) -
    373           int(sizeof(ObjectElements));
    374  }
    375  static int offsetOfLength() {
    376    return int(offsetof(ObjectElements, length)) - int(sizeof(ObjectElements));
    377  }
    378 
    379  static void PrepareForPreventExtensions(JSContext* cx, NativeObject* obj);
    380  static void PreventExtensions(NativeObject* obj);
    381  [[nodiscard]] static bool FreezeOrSeal(JSContext* cx,
    382                                         Handle<NativeObject*> obj,
    383                                         IntegrityLevel level);
    384 
    385  bool isSealed() const { return flags & SEALED; }
    386 
    387  bool isPacked() const { return !(flags & NON_PACKED); }
    388 
    389  JS::PropertyAttributes elementAttributes() const {
    390    if (isFrozen()) {
    391      return {JS::PropertyAttribute::Enumerable};
    392    }
    393    if (isSealed()) {
    394      return {JS::PropertyAttribute::Enumerable,
    395              JS::PropertyAttribute::Writable};
    396    }
    397    return {JS::PropertyAttribute::Configurable,
    398            JS::PropertyAttribute::Enumerable, JS::PropertyAttribute::Writable};
    399  }
    400 
    401  uint32_t numShiftedElements() const {
    402    uint32_t numShifted = flags >> NumShiftedElementsShift;
    403    MOZ_ASSERT_IF(numShifted > 0,
    404                  !(flags & (NONWRITABLE_ARRAY_LENGTH | NOT_EXTENSIBLE |
    405                             SEALED | FROZEN)));
    406    return numShifted;
    407  }
    408 
    409  uint32_t numAllocatedElements() const {
    410    return VALUES_PER_HEADER + capacity + numShiftedElements();
    411  }
    412 
    413  bool hasNonwritableArrayLength() const {
    414    return flags & NONWRITABLE_ARRAY_LENGTH;
    415  }
    416 
    417  bool maybeInIteration() { return flags & MAYBE_IN_ITERATION; }
    418 
    419  bool isNotExtensible() { return flags & NOT_EXTENSIBLE; }
    420 
    421  // This is enough slots to store an object of this class. See the static
    422  // assertion below.
    423  static const size_t VALUES_PER_HEADER = 2;
    424 
    425 #if defined(DEBUG) || defined(JS_JITSPEW)
    426  void dumpStringContent(js::GenericPrinter& out) const;
    427 #endif
    428 };
    429 
    430 static_assert(ObjectElements::VALUES_PER_HEADER * sizeof(HeapSlot) ==
    431                  sizeof(ObjectElements),
    432              "ObjectElements doesn't fit in the given number of slots");
    433 
    434 /*
    435 * Slots header used for native objects. The header stores the capacity and the
    436 * slot data follows in memory.
    437 */
    438 class alignas(HeapSlot) ObjectSlots {
    439  uint32_t capacity_;
    440  uint32_t dictionarySlotSpan_;
    441  uint64_t maybeUniqueId_;
    442 
    443 public:
    444  // Special values for maybeUniqueId_ to indicate no unique ID is present.
    445  static constexpr uint64_t NoUniqueIdInDynamicSlots = 0;
    446  static constexpr uint64_t NoUniqueIdInSharedEmptySlots = 1;
    447  static constexpr uint64_t LastNoUniqueIdValue = NoUniqueIdInSharedEmptySlots;
    448 
    449  static constexpr size_t VALUES_PER_HEADER = 2;
    450 
    451  static inline size_t allocCount(size_t slotCount) {
    452    static_assert(sizeof(ObjectSlots) ==
    453                  ObjectSlots::VALUES_PER_HEADER * sizeof(HeapSlot));
    454 #ifdef MOZ_VALGRIND
    455    if (slotCount == 0) {
    456      // Add an extra unused slot so that NativeObject::slots_ always points
    457      // into the allocation otherwise valgrind thinks this is a leak.
    458      slotCount = 1;
    459    }
    460 #endif
    461    return slotCount + VALUES_PER_HEADER;
    462  }
    463 
    464  static inline size_t allocSize(size_t slotCount) {
    465    return allocCount(slotCount) * sizeof(HeapSlot);
    466  }
    467 
    468  static ObjectSlots* fromSlots(HeapSlot* slots) {
    469    MOZ_ASSERT(slots);
    470    return reinterpret_cast<ObjectSlots*>(uintptr_t(slots) -
    471                                          sizeof(ObjectSlots));
    472  }
    473 
    474  static constexpr size_t offsetOfCapacity() {
    475    return offsetof(ObjectSlots, capacity_);
    476  }
    477  static constexpr size_t offsetOfDictionarySlotSpan() {
    478    return offsetof(ObjectSlots, dictionarySlotSpan_);
    479  }
    480  static constexpr size_t offsetOfMaybeUniqueId() {
    481    return offsetof(ObjectSlots, maybeUniqueId_);
    482  }
    483  static constexpr size_t offsetOfSlots() { return sizeof(ObjectSlots); }
    484 
    485  constexpr ObjectSlots(uint32_t capacity, uint32_t dictionarySlotSpan,
    486                        uint64_t maybeUniqueId);
    487 
    488  constexpr uint32_t capacity() const { return capacity_; }
    489 
    490  constexpr uint32_t dictionarySlotSpan() const { return dictionarySlotSpan_; }
    491 
    492  bool isSharedEmptySlots() const {
    493    return maybeUniqueId_ == NoUniqueIdInSharedEmptySlots;
    494  }
    495 
    496  constexpr bool hasUniqueId() const {
    497    return maybeUniqueId_ > LastNoUniqueIdValue;
    498  }
    499  uint64_t uniqueId() const {
    500    MOZ_ASSERT(hasUniqueId());
    501    return maybeUniqueId_;
    502  }
    503  uintptr_t maybeUniqueId() const { return hasUniqueId() ? maybeUniqueId_ : 0; }
    504  void setUniqueId(uint64_t uid) {
    505    MOZ_ASSERT(uid > LastNoUniqueIdValue);
    506    MOZ_ASSERT(!isSharedEmptySlots());
    507    maybeUniqueId_ = uid;
    508  }
    509 
    510  void setDictionarySlotSpan(uint32_t span) { dictionarySlotSpan_ = span; }
    511 
    512  HeapSlot* slots() const {
    513    return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(ObjectSlots));
    514  }
    515 };
    516 
    517 /*
    518 * Shared singletons for objects with no elements.
    519 * emptyObjectElementsShared is used only for TypedArrays, when the TA
    520 * maps shared memory.
    521 */
    522 extern HeapSlot* const emptyObjectElements;
    523 extern HeapSlot* const emptyObjectElementsShared;
    524 
    525 /*
    526 * Shared singletons for objects with no dynamic slots.
    527 */
    528 extern HeapSlot* const emptyObjectSlots;
    529 extern HeapSlot* const emptyObjectSlotsForDictionaryObject[];
    530 
    531 class AutoCheckShapeConsistency;
    532 class GCMarker;
    533 
    534 // Operations which change an object's dense elements can either succeed, fail,
    535 // or be unable to complete. The latter is used when the object's elements must
    536 // become sparse instead. The enum below is used for such operations.
    537 enum class DenseElementResult { Failure, Success, Incomplete };
    538 
    539 // Stores a slot offset in bytes relative to either the NativeObject* address
    540 // (if isFixedSlot) or to NativeObject::slots_ (if !isFixedSlot).
    541 class TaggedSlotOffset {
    542  uint32_t bits_ = 0;
    543 
    544 public:
    545  static constexpr size_t OffsetShift = 1;
    546  static constexpr size_t IsFixedSlotFlag = 0b1;
    547 
    548  static constexpr size_t MaxOffset = SHAPE_MAXIMUM_SLOT * sizeof(Value);
    549  static_assert((uint64_t(MaxOffset) << OffsetShift) <= UINT32_MAX,
    550                "maximum slot offset must fit in TaggedSlotOffset");
    551 
    552  constexpr TaggedSlotOffset() = default;
    553 
    554  TaggedSlotOffset(uint32_t offset, bool isFixedSlot)
    555      : bits_((offset << OffsetShift) | isFixedSlot) {
    556    MOZ_ASSERT(offset <= MaxOffset);
    557  }
    558 
    559  uint32_t offset() const { return bits_ >> OffsetShift; }
    560  bool isFixedSlot() const { return bits_ & IsFixedSlotFlag; }
    561 
    562  bool operator==(const TaggedSlotOffset& other) const {
    563    return bits_ == other.bits_;
    564  }
    565  bool operator!=(const TaggedSlotOffset& other) const {
    566    return !(*this == other);
    567  }
    568 };
    569 
    570 enum class CanReuseShape {
    571  // The Shape can be reused. This implies CanReusePropMap.
    572  CanReuseShape,
    573 
    574  // Only the PropMap can be reused.
    575  CanReusePropMap,
    576 
    577  // Neither the PropMap nor Shape can be reused.
    578  NoReuse,
    579 };
    580 
    581 /*
    582 * [SMDOC] NativeObject layout
    583 *
    584 * NativeObject specifies the internal implementation of a native object.
    585 *
    586 * Native objects use ShapedObject::shape to record property information. Two
    587 * native objects with the same shape are guaranteed to have the same number of
    588 * fixed slots.
    589 *
    590 * Native objects extend the base implementation of an object with storage for
    591 * the object's named properties and indexed elements.
    592 *
    593 * These are stored separately from one another. Objects are followed by a
    594 * variable-sized array of values for inline storage, which may be used by
    595 * either properties of native objects (fixed slots), by elements (fixed
    596 * elements), or by other data for certain kinds of objects, such as
    597 * ArrayBufferObjects and TypedArrayObjects.
    598 *
    599 * Named property storage can be split between fixed slots and a dynamically
    600 * allocated array (the slots member). For an object with N fixed slots, shapes
    601 * with slots [0..N-1] are stored in the fixed slots, and the remainder are
    602 * stored in the dynamic array. If all properties fit in the fixed slots, the
    603 * 'slots_' member is nullptr.
    604 *
    605 * Elements are indexed via the 'elements_' member. This member can point to
    606 * either the shared emptyObjectElements and emptyObjectElementsShared
    607 * singletons, into the inline value array (the address of the third value, to
    608 * leave room for a ObjectElements header;in this case numFixedSlots() is zero)
    609 * or to a dynamically allocated array.
    610 *
    611 * Slots and elements may both be non-empty. The slots may be either names or
    612 * indexes; no indexed property will be in both the slots and elements.
    613 */
    614 class NativeObject : public JSObject {
    615 protected:
    616  /* Slots for object properties. */
    617  js::HeapSlot* slots_;
    618 
    619  /* Slots for object dense elements. */
    620  js::HeapSlot* elements_;
    621 
    622  friend class ::JSObject;
    623 
    624 private:
    625  static void staticAsserts() {
    626    static_assert(sizeof(NativeObject) == sizeof(JSObject_Slots0),
    627                  "native object size must match GC thing size");
    628    static_assert(sizeof(NativeObject) == sizeof(JS::shadow::Object),
    629                  "shadow interface must match actual implementation");
    630    static_assert(sizeof(NativeObject) % sizeof(Value) == 0,
    631                  "fixed slots after an object must be aligned");
    632 
    633    static_assert(offsetOfShape() == offsetof(JS::shadow::Object, shape),
    634                  "shadow type must match actual type");
    635    static_assert(
    636        offsetof(NativeObject, slots_) == offsetof(JS::shadow::Object, slots),
    637        "shadow slots must match actual slots");
    638    static_assert(
    639        offsetof(NativeObject, elements_) == offsetof(JS::shadow::Object, _1),
    640        "shadow placeholder must match actual elements");
    641 
    642    static_assert(MAX_FIXED_SLOTS <= Shape::FIXED_SLOTS_MAX,
    643                  "verify numFixedSlots() bitfield is big enough");
    644    static_assert(sizeof(NativeObject) + MAX_FIXED_SLOTS * sizeof(Value) ==
    645                      JSObject::MAX_BYTE_SIZE,
    646                  "inconsistent maximum object size");
    647 
    648    // Sanity check NativeObject size is what we expect.
    649 #ifdef JS_64BIT
    650    static_assert(sizeof(NativeObject) == 3 * sizeof(void*));
    651 #else
    652    static_assert(sizeof(NativeObject) == 4 * sizeof(void*));
    653 #endif
    654  }
    655 
    656 public:
    657  NativeShape* shape() const { return &JSObject::shape()->asNative(); }
    658  SharedShape* sharedShape() const { return &shape()->asShared(); }
    659  DictionaryShape* dictionaryShape() const { return &shape()->asDictionary(); }
    660 
    661  PropertyInfoWithKey getLastProperty() const {
    662    return shape()->lastProperty();
    663  }
    664 
    665  HeapSlotArray getDenseElements() const { return HeapSlotArray(elements_); }
    666 
    667  const Value& getDenseElement(uint32_t idx) const {
    668    MOZ_ASSERT(idx < getDenseInitializedLength());
    669    return elements_[idx];
    670  }
    671  bool containsDenseElement(uint32_t idx) const {
    672    return idx < getDenseInitializedLength() &&
    673           !elements_[idx].isMagic(JS_ELEMENTS_HOLE);
    674  }
    675  uint32_t getDenseInitializedLength() const {
    676    return getElementsHeader()->initializedLength;
    677  }
    678  uint32_t getDenseCapacity() const { return getElementsHeader()->capacity; }
    679 
    680  bool isSharedMemory() const { return getElementsHeader()->isSharedMemory(); }
    681 
    682  // Update the object's shape and allocate slots if needed to match the shape's
    683  // slot span.
    684  MOZ_ALWAYS_INLINE bool setShapeAndAddNewSlots(JSContext* cx,
    685                                                SharedShape* newShape,
    686                                                uint32_t oldSpan,
    687                                                uint32_t newSpan);
    688 
    689  // Methods optimized for adding/removing a single slot. Must only be used for
    690  // non-dictionary objects.
    691  MOZ_ALWAYS_INLINE bool setShapeAndAddNewSlot(JSContext* cx,
    692                                               SharedShape* newShape,
    693                                               uint32_t slot);
    694  void setShapeAndRemoveLastSlot(JSContext* cx, SharedShape* newShape,
    695                                 uint32_t slot);
    696 
    697  MOZ_ALWAYS_INLINE CanReuseShape
    698  canReuseShapeForNewProperties(NativeShape* newShape) const {
    699    NativeShape* oldShape = shape();
    700    MOZ_ASSERT(oldShape->propMapLength() == 0,
    701               "object must have no properties");
    702    MOZ_ASSERT(newShape->propMapLength() > 0,
    703               "new shape must have at least one property");
    704    if (oldShape->isDictionary() || newShape->isDictionary()) {
    705      return CanReuseShape::NoReuse;
    706    }
    707    // We only handle the common case where the old shape has no object flags
    708    // (expected because it's an empty object) and the new shape has just the
    709    // HasEnumerable flag that we can copy safely.
    710    if (!oldShape->objectFlags().isEmpty()) {
    711      return CanReuseShape::NoReuse;
    712    }
    713    MOZ_ASSERT(newShape->hasObjectFlag(ObjectFlag::HasEnumerable));
    714    if (newShape->objectFlags() != ObjectFlags({ObjectFlag::HasEnumerable})) {
    715      return CanReuseShape::NoReuse;
    716    }
    717    // If the number of fixed slots or the BaseShape is different, we can't
    718    // reuse the Shape but we can still reuse the PropMap.
    719    if (oldShape->numFixedSlots() != newShape->numFixedSlots() ||
    720        oldShape->base() != newShape->base()) {
    721      return CanReuseShape::CanReusePropMap;
    722    }
    723    MOZ_ASSERT(oldShape->getObjectClass() == newShape->getObjectClass());
    724    MOZ_ASSERT(oldShape->proto() == newShape->proto());
    725    MOZ_ASSERT(oldShape->realm() == newShape->realm());
    726    return CanReuseShape::CanReuseShape;
    727  }
    728 
    729  // Newly-created TypedArrays that map a SharedArrayBuffer are
    730  // marked as shared by giving them an ObjectElements that has the
    731  // ObjectElements::SHARED_MEMORY flag set.
    732  void setIsSharedMemory() {
    733    MOZ_ASSERT(elements_ == emptyObjectElements);
    734    elements_ = emptyObjectElementsShared;
    735  }
    736 
    737  static inline NativeObject* create(JSContext* cx, gc::AllocKind kind,
    738                                     gc::Heap heap, Handle<SharedShape*> shape,
    739                                     gc::AllocSite* site = nullptr);
    740 
    741  template <typename T>
    742  static inline T* create(JSContext* cx, gc::AllocKind kind, gc::Heap heap,
    743                          Handle<SharedShape*> shape,
    744                          gc::AllocSite* site = nullptr) {
    745    NativeObject* nobj = create(cx, kind, heap, shape, site);
    746    return nobj ? &nobj->as<T>() : nullptr;
    747  }
    748 
    749 #ifdef DEBUG
    750  static void enableShapeConsistencyChecks();
    751 #endif
    752 
    753 protected:
    754 #ifdef DEBUG
    755  friend class js::AutoCheckShapeConsistency;
    756  void checkShapeConsistency();
    757 #else
    758  void checkShapeConsistency() {}
    759 #endif
    760 
    761  void maybeFreeDictionaryPropSlots(JSContext* cx, DictionaryPropMap* map,
    762                                    uint32_t mapLength);
    763 
    764  [[nodiscard]] static bool toDictionaryMode(JSContext* cx,
    765                                             Handle<NativeObject*> obj);
    766 
    767 private:
    768  inline void setEmptyDynamicSlots(uint32_t dictonarySlotSpan);
    769 
    770  inline void setDictionaryModeSlotSpan(uint32_t span);
    771 
    772  friend class gc::TenuringTracer;
    773 
    774  // Given a slot range from |start| to |end| exclusive, call |fun| with
    775  // pointers to the corresponding fixed slot and/or dynamic slot ranges.
    776  template <typename Fun>
    777  void forEachSlotRangeUnchecked(uint32_t start, uint32_t end, const Fun& fun) {
    778    MOZ_ASSERT(end >= start);
    779    uint32_t nfixed = numFixedSlots();
    780    if (start < nfixed) {
    781      HeapSlot* fixedStart = &fixedSlots()[start];
    782      HeapSlot* fixedEnd = &fixedSlots()[std::min(nfixed, end)];
    783      fun(fixedStart, fixedEnd);
    784      start = nfixed;
    785    }
    786    if (end > nfixed) {
    787      HeapSlot* dynStart = &slots_[start - nfixed];
    788      HeapSlot* dynEnd = &slots_[end - nfixed];
    789      fun(dynStart, dynEnd);
    790    }
    791  }
    792 
    793  template <typename Fun>
    794  void forEachSlotRange(uint32_t start, uint32_t end, const Fun& fun) {
    795    MOZ_ASSERT(slotInRange(end, SENTINEL_ALLOWED));
    796    forEachSlotRangeUnchecked(start, end, fun);
    797  }
    798 
    799 protected:
    800  friend class DictionaryPropMap;
    801  friend class GCMarker;
    802  friend class Shape;
    803 
    804  void invalidateSlotRange(uint32_t start, uint32_t end) {
    805 #ifdef DEBUG
    806    forEachSlotRange(start, end, [](HeapSlot* slotsStart, HeapSlot* slotsEnd) {
    807      Debug_SetSlotRangeToCrashOnTouch(slotsStart, slotsEnd);
    808    });
    809 #endif /* DEBUG */
    810  }
    811 
    812  void initFixedSlots(uint32_t numSlots) {
    813    MOZ_ASSERT(numSlots == numUsedFixedSlots());
    814    HeapSlot* slots = fixedSlots();
    815    for (uint32_t i = 0; i < numSlots; i++) {
    816      slots[i].initAsUndefined();
    817    }
    818  }
    819  void initDynamicSlots(uint32_t numSlots) {
    820    MOZ_ASSERT(numSlots == sharedShape()->slotSpan() - numFixedSlots());
    821    HeapSlot* slots = slots_;
    822    for (uint32_t i = 0; i < numSlots; i++) {
    823      slots[i].initAsUndefined();
    824    }
    825  }
    826  void initSlots(uint32_t nfixed, uint32_t slotSpan) {
    827    initFixedSlots(std::min(nfixed, slotSpan));
    828    if (slotSpan > nfixed) {
    829      initDynamicSlots(slotSpan - nfixed);
    830    }
    831  }
    832 
    833 #ifdef DEBUG
    834  enum SentinelAllowed { SENTINEL_NOT_ALLOWED, SENTINEL_ALLOWED };
    835 
    836  /*
    837   * Check that slot is in range for the object's allocated slots.
    838   * If sentinelAllowed then slot may equal the slot capacity.
    839   */
    840  bool slotInRange(uint32_t slot,
    841                   SentinelAllowed sentinel = SENTINEL_NOT_ALLOWED) const;
    842 
    843  /*
    844   * Check whether a slot is a fixed slot.
    845   */
    846  bool slotIsFixed(uint32_t slot) const;
    847 
    848  /*
    849   * Check whether the supplied number of fixed slots is correct.
    850   */
    851  bool isNumFixedSlots(uint32_t nfixed) const;
    852 #endif
    853 
    854  /*
    855   * Minimum size for dynamically allocated slots in normal Objects.
    856   * ArrayObjects don't use this limit and can have a lower slot capacity,
    857   * since they normally don't have a lot of slots.
    858   */
    859  static const uint32_t SLOT_CAPACITY_MIN = 6;
    860 
    861  /*
    862   * Minimum size for dynamically allocated elements in normal Objects.
    863   */
    864  static const uint32_t ELEMENT_CAPACITY_MIN = 6;
    865 
    866  HeapSlot* fixedSlots() const {
    867    return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(NativeObject));
    868  }
    869 
    870 public:
    871  inline void initEmptyDynamicSlots();
    872 
    873  [[nodiscard]] static bool generateNewDictionaryShape(
    874      JSContext* cx, Handle<NativeObject*> obj);
    875 
    876  // The maximum number of slots in an object.
    877  // |MAX_SLOTS_COUNT * sizeof(JS::Value)| shouldn't overflow
    878  // int32_t (see slotsSizeMustNotOverflow).
    879  static const uint32_t MAX_SLOTS_COUNT = (1 << 28) - 1;
    880 
    881  static void slotsSizeMustNotOverflow() {
    882    static_assert(
    883        NativeObject::MAX_SLOTS_COUNT <= INT32_MAX / sizeof(JS::Value),
    884        "every caller of this method requires that a slot "
    885        "number (or slot count) count multiplied by "
    886        "sizeof(Value) can't overflow uint32_t (and sometimes "
    887        "int32_t, too)");
    888  }
    889 
    890  uint32_t numFixedSlots() const {
    891    return reinterpret_cast<const JS::shadow::Object*>(this)->numFixedSlots();
    892  }
    893 
    894  // Get the number of fixed slots when the shape pointer may have been
    895  // forwarded by a moving GC. You need to use this rather that
    896  // numFixedSlots() in a trace hook if you access an object that is not the
    897  // object being traced, since it may have a stale shape pointer.
    898  inline uint32_t numFixedSlotsMaybeForwarded() const;
    899 
    900  uint32_t numUsedFixedSlots() const {
    901    uint32_t nslots = sharedShape()->slotSpan();
    902    return std::min(nslots, numFixedSlots());
    903  }
    904 
    905  uint32_t slotSpan() const {
    906    if (inDictionaryMode()) {
    907      return dictionaryModeSlotSpan();
    908    }
    909    MOZ_ASSERT(getSlotsHeader()->dictionarySlotSpan() == 0);
    910    return sharedShape()->slotSpan();
    911  }
    912 
    913  uint32_t dictionaryModeSlotSpan() const {
    914    MOZ_ASSERT(inDictionaryMode());
    915    return getSlotsHeader()->dictionarySlotSpan();
    916  }
    917 
    918  /* Whether a slot is at a fixed offset from this object. */
    919  bool isFixedSlot(size_t slot) { return slot < numFixedSlots(); }
    920 
    921  /* Index into the dynamic slots array to use for a dynamic slot. */
    922  size_t dynamicSlotIndex(size_t slot) {
    923    MOZ_ASSERT(slot >= numFixedSlots());
    924    return slot - numFixedSlots();
    925  }
    926 
    927  // Native objects are never proxies. Call isExtensible instead.
    928  bool nonProxyIsExtensible() const = delete;
    929 
    930  bool isExtensible() const { return !hasFlag(ObjectFlag::NotExtensible); }
    931 
    932  /*
    933   * Whether there may be indexed properties on this object, excluding any in
    934   * the object's elements.
    935   */
    936  bool isIndexed() const { return hasFlag(ObjectFlag::Indexed); }
    937 
    938  bool hasInterestingSymbol() const {
    939    return hasFlag(ObjectFlag::HasInterestingSymbol);
    940  }
    941 
    942  bool hasEnumerableProperty() const {
    943    return hasFlag(ObjectFlag::HasEnumerable);
    944  }
    945 
    946  static bool setHadGetterSetterChange(JSContext* cx,
    947                                       Handle<NativeObject*> obj) {
    948    return setFlag(cx, obj, ObjectFlag::HadGetterSetterChange);
    949  }
    950  bool hadGetterSetterChange() const {
    951    return hasFlag(ObjectFlag::HadGetterSetterChange);
    952  }
    953 
    954  static bool setHasObjectFuse(JSContext* cx, Handle<NativeObject*> obj) {
    955    return setFlag(cx, obj, js::ObjectFlag::HasObjectFuse);
    956  }
    957 
    958  bool allocateInitialSlots(JSContext* cx, uint32_t capacity);
    959 
    960  /*
    961   * Grow or shrink slots immediately before changing the slot span.
    962   * The number of allocated slots is not stored explicitly, and changes to
    963   * the slots must track changes in the slot span.
    964   */
    965  bool growSlots(JSContext* cx, uint32_t oldCapacity, uint32_t newCapacity);
    966  bool growSlotsForNewSlot(JSContext* cx, uint32_t numFixed, uint32_t slot);
    967  void shrinkSlots(JSContext* cx, uint32_t oldCapacity, uint32_t newCapacity);
    968 
    969  bool allocateSlots(Nursery& nursery, uint32_t newCapacity);
    970 
    971  /*
    972   * This method is static because it's called from JIT code. On OOM, returns
    973   * false without leaving a pending exception on the context.
    974   */
    975  static bool growSlotsPure(JSContext* cx, NativeObject* obj,
    976                            uint32_t newCapacity);
    977 
    978  /*
    979   * Like growSlotsPure but for dense elements. This will return
    980   * false if we failed to allocate a dense element for some reason (OOM, too
    981   * many dense elements, non-writable array length, etc).
    982   */
    983  static bool addDenseElementPure(JSContext* cx, NativeObject* obj);
    984 
    985  /*
    986   * Indicates whether this object has an ObjectSlots allocation attached. The
    987   * capacity of this can be zero if it is only used to hold a unique ID.
    988   */
    989  bool hasDynamicSlots() const {
    990    return !getSlotsHeader()->isSharedEmptySlots();
    991  }
    992 
    993  /* Compute the number of dynamic slots required for this object. */
    994  MOZ_ALWAYS_INLINE uint32_t calculateDynamicSlots() const;
    995 
    996  MOZ_ALWAYS_INLINE uint32_t numDynamicSlots() const;
    997 
    998 #ifdef DEBUG
    999  uint32_t outOfLineNumDynamicSlots() const;
   1000 #endif
   1001 
   1002  bool empty() const { return shape()->propMapLength() == 0; }
   1003 
   1004  mozilla::Maybe<PropertyInfo> lookup(JSContext* cx, jsid id);
   1005  mozilla::Maybe<PropertyInfo> lookup(JSContext* cx, PropertyName* name) {
   1006    return lookup(cx, NameToId(name));
   1007  }
   1008 
   1009  bool contains(JSContext* cx, jsid id) { return lookup(cx, id).isSome(); }
   1010  bool contains(JSContext* cx, PropertyName* name) {
   1011    return lookup(cx, name).isSome();
   1012  }
   1013  bool contains(JSContext* cx, jsid id, PropertyInfo prop) {
   1014    mozilla::Maybe<PropertyInfo> found = lookup(cx, id);
   1015    return found.isSome() && *found == prop;
   1016  }
   1017 
   1018  /* Contextless; can be called from other pure code. */
   1019  mozilla::Maybe<PropertyInfo> lookupPure(jsid id);
   1020  mozilla::Maybe<PropertyInfo> lookupPure(PropertyName* name) {
   1021    return lookupPure(NameToId(name));
   1022  }
   1023 
   1024  bool containsPure(jsid id) { return lookupPure(id).isSome(); }
   1025  bool containsPure(PropertyName* name) { return containsPure(NameToId(name)); }
   1026  bool containsPure(jsid id, PropertyInfo prop) {
   1027    mozilla::Maybe<PropertyInfo> found = lookupPure(id);
   1028    return found.isSome() && *found == prop;
   1029  }
   1030 
   1031 private:
   1032  /*
   1033   * Allocate and free an object slot.
   1034   *
   1035   * FIXME: bug 593129 -- slot allocation should be done by object methods
   1036   * after calling object-parameter-free shape methods, avoiding coupling
   1037   * logic across the object vs. shape module wall.
   1038   */
   1039  static bool allocDictionarySlot(JSContext* cx, Handle<NativeObject*> obj,
   1040                                  uint32_t* slotp);
   1041 
   1042  void freeDictionarySlot(uint32_t slot);
   1043 
   1044  static MOZ_ALWAYS_INLINE bool maybeConvertToDictionaryForAdd(
   1045      JSContext* cx, Handle<NativeObject*> obj);
   1046 
   1047 public:
   1048  // Add a new property. Must only be used when the |id| is not already present
   1049  // in the object's shape. Checks for non-extensibility must be done by the
   1050  // callers.
   1051  static bool addProperty(JSContext* cx, Handle<NativeObject*> obj, HandleId id,
   1052                          PropertyFlags flags, uint32_t* slotOut);
   1053 
   1054  static bool addProperty(JSContext* cx, Handle<NativeObject*> obj,
   1055                          Handle<PropertyName*> name, PropertyFlags flags,
   1056                          uint32_t* slotOut) {
   1057    RootedId id(cx, NameToId(name));
   1058    return addProperty(cx, obj, id, flags, slotOut);
   1059  }
   1060 
   1061  static bool addPropertyInReservedSlot(JSContext* cx,
   1062                                        Handle<NativeObject*> obj, HandleId id,
   1063                                        uint32_t slot, PropertyFlags flags);
   1064  static bool addPropertyInReservedSlot(JSContext* cx,
   1065                                        Handle<NativeObject*> obj,
   1066                                        Handle<PropertyName*> name,
   1067                                        uint32_t slot, PropertyFlags flags) {
   1068    RootedId id(cx, NameToId(name));
   1069    return addPropertyInReservedSlot(cx, obj, id, slot, flags);
   1070  }
   1071 
   1072  static bool addCustomDataProperty(JSContext* cx, Handle<NativeObject*> obj,
   1073                                    HandleId id, PropertyFlags flags);
   1074 
   1075  // Change a property with key |id| in this object. The object must already
   1076  // have a property (stored in the shape tree) with this |id|.
   1077  static bool changeProperty(JSContext* cx, Handle<NativeObject*> obj,
   1078                             HandleId id, PropertyFlags flags,
   1079                             uint32_t* slotOut);
   1080 
   1081  static bool changeCustomDataPropAttributes(JSContext* cx,
   1082                                             Handle<NativeObject*> obj,
   1083                                             HandleId id, PropertyFlags flags);
   1084 
   1085  // Remove the property named by id from this object.
   1086  static bool removeProperty(JSContext* cx, Handle<NativeObject*> obj,
   1087                             HandleId id);
   1088 
   1089  static bool freezeOrSealProperties(JSContext* cx, Handle<NativeObject*> obj,
   1090                                     IntegrityLevel level);
   1091 
   1092 protected:
   1093  static bool changeNumFixedSlotsAfterSwap(JSContext* cx,
   1094                                           Handle<NativeObject*> obj,
   1095                                           uint32_t nfixed);
   1096 
   1097  // For use from JSObject::swap.
   1098  [[nodiscard]] bool prepareForSwap(JSContext* cx, JSObject* other,
   1099                                    MutableHandleValueVector slotValuesOut);
   1100  [[nodiscard]] static bool fixupAfterSwap(JSContext* cx,
   1101                                           Handle<NativeObject*> obj,
   1102                                           gc::AllocKind kind,
   1103                                           HandleValueVector slotValues);
   1104 
   1105 public:
   1106  // Return true if this object has been converted from shared-immutable
   1107  // shapes to object-owned dictionary shapes.
   1108  bool inDictionaryMode() const { return shape()->isDictionary(); }
   1109 
   1110  const Value& getSlot(uint32_t slot) const {
   1111    MOZ_ASSERT(slotInRange(slot));
   1112    uint32_t fixed = numFixedSlots();
   1113    if (slot < fixed) {
   1114      return fixedSlots()[slot];
   1115    }
   1116    return slots_[slot - fixed];
   1117  }
   1118 
   1119  const HeapSlot* getSlotAddressUnchecked(uint32_t slot) const {
   1120    uint32_t fixed = numFixedSlots();
   1121    if (slot < fixed) {
   1122      return fixedSlots() + slot;
   1123    }
   1124    return slots_ + (slot - fixed);
   1125  }
   1126 
   1127  HeapSlot* getSlotAddressUnchecked(uint32_t slot) {
   1128    uint32_t fixed = numFixedSlots();
   1129    if (slot < fixed) {
   1130      return fixedSlots() + slot;
   1131    }
   1132    return slots_ + (slot - fixed);
   1133  }
   1134 
   1135  HeapSlot* getSlotsUnchecked() { return slots_; }
   1136 
   1137  HeapSlot* getSlotAddress(uint32_t slot) {
   1138    /*
   1139     * This can be used to get the address of the end of the slots for the
   1140     * object, which may be necessary when fetching zero-length arrays of
   1141     * slots (e.g. for callObjVarArray).
   1142     */
   1143    MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED));
   1144    return getSlotAddressUnchecked(slot);
   1145  }
   1146 
   1147  const HeapSlot* getSlotAddress(uint32_t slot) const {
   1148    /*
   1149     * This can be used to get the address of the end of the slots for the
   1150     * object, which may be necessary when fetching zero-length arrays of
   1151     * slots (e.g. for callObjVarArray).
   1152     */
   1153    MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED));
   1154    return getSlotAddressUnchecked(slot);
   1155  }
   1156 
   1157  MOZ_ALWAYS_INLINE HeapSlot& getSlotRef(uint32_t slot) {
   1158    MOZ_ASSERT(slotInRange(slot));
   1159    return *getSlotAddress(slot);
   1160  }
   1161 
   1162  MOZ_ALWAYS_INLINE const HeapSlot& getSlotRef(uint32_t slot) const {
   1163    MOZ_ASSERT(slotInRange(slot));
   1164    return *getSlotAddress(slot);
   1165  }
   1166 
   1167  // Check requirements on values stored to this object.
   1168  MOZ_ALWAYS_INLINE void checkStoredValue(const Value& v) {
   1169    MOZ_ASSERT(IsObjectValueInCompartment(v, compartment()));
   1170    MOZ_ASSERT(AtomIsMarked(zoneFromAnyThread(), v));
   1171    MOZ_ASSERT_IF(v.isMagic() && v.whyMagic() == JS_ELEMENTS_HOLE,
   1172                  !denseElementsArePacked());
   1173  }
   1174 
   1175  MOZ_ALWAYS_INLINE void setSlot(uint32_t slot, const Value& value) {
   1176    MOZ_ASSERT(slotInRange(slot));
   1177    checkStoredValue(value);
   1178    getSlotRef(slot).set(this, HeapSlot::Slot, slot, value);
   1179  }
   1180 
   1181  MOZ_ALWAYS_INLINE void initSlot(uint32_t slot, const Value& value) {
   1182    MOZ_ASSERT(getSlot(slot).isUndefined());
   1183    MOZ_ASSERT(slotInRange(slot));
   1184    checkStoredValue(value);
   1185    initSlotUnchecked(slot, value);
   1186  }
   1187 
   1188  MOZ_ALWAYS_INLINE void initSlotUnchecked(uint32_t slot, const Value& value) {
   1189    getSlotAddressUnchecked(slot)->init(this, HeapSlot::Slot, slot, value);
   1190  }
   1191 
   1192  // Returns the GetterSetter for an accessor property.
   1193  GetterSetter* getGetterSetter(uint32_t slot) const {
   1194    return getSlot(slot).toGCThing()->as<GetterSetter>();
   1195  }
   1196  GetterSetter* getGetterSetter(PropertyInfo prop) const {
   1197    MOZ_ASSERT(prop.isAccessorProperty());
   1198    return getGetterSetter(prop.slot());
   1199  }
   1200 
   1201  // Returns the (possibly nullptr) getter or setter object. |prop| and |slot|
   1202  // must be (for) an accessor property.
   1203  JSObject* getGetter(uint32_t slot) const {
   1204    return getGetterSetter(slot)->getter();
   1205  }
   1206  JSObject* getGetter(PropertyInfo prop) const {
   1207    return getGetterSetter(prop)->getter();
   1208  }
   1209  JSObject* getSetter(PropertyInfo prop) const {
   1210    return getGetterSetter(prop)->setter();
   1211  }
   1212 
   1213  // Returns true if the property has a non-nullptr getter or setter object.
   1214  // |prop| can be any property.
   1215  bool hasGetter(PropertyInfo prop) const {
   1216    return prop.isAccessorProperty() && getGetter(prop);
   1217  }
   1218  bool hasSetter(PropertyInfo prop) const {
   1219    return prop.isAccessorProperty() && getSetter(prop);
   1220  }
   1221 
   1222  // If the property has a non-nullptr getter/setter, return it as ObjectValue.
   1223  // Else return |undefined|. |prop| must be an accessor property.
   1224  Value getGetterValue(PropertyInfo prop) const {
   1225    MOZ_ASSERT(prop.isAccessorProperty());
   1226    if (JSObject* getterObj = getGetter(prop)) {
   1227      return ObjectValue(*getterObj);
   1228    }
   1229    return UndefinedValue();
   1230  }
   1231  Value getSetterValue(PropertyInfo prop) const {
   1232    MOZ_ASSERT(prop.isAccessorProperty());
   1233    if (JSObject* setterObj = getSetter(prop)) {
   1234      return ObjectValue(*setterObj);
   1235    }
   1236    return UndefinedValue();
   1237  }
   1238 
   1239  [[nodiscard]] bool setUniqueId(JSRuntime* runtime, uint64_t uid);
   1240  inline bool hasUniqueId() const { return getSlotsHeader()->hasUniqueId(); }
   1241  inline uint64_t uniqueId() const { return getSlotsHeader()->uniqueId(); }
   1242  inline uint64_t maybeUniqueId() const {
   1243    return getSlotsHeader()->maybeUniqueId();
   1244  }
   1245  bool setOrUpdateUniqueId(JSContext* cx, uint64_t uid);
   1246 
   1247  // MAX_FIXED_SLOTS is the biggest number of fixed slots our GC
   1248  // size classes will give an object.
   1249  static constexpr uint32_t MAX_FIXED_SLOTS =
   1250      JS::shadow::Object::MAX_FIXED_SLOTS;
   1251 
   1252 private:
   1253  void prepareElementRangeForOverwrite(size_t start, size_t end) {
   1254    MOZ_ASSERT(end <= getDenseInitializedLength());
   1255    for (size_t i = start; i < end; i++) {
   1256      elements_[i].destroy();
   1257    }
   1258  }
   1259 
   1260  /*
   1261   * Trigger the write barrier on a range of slots that will no longer be
   1262   * reachable.
   1263   */
   1264  void prepareSlotRangeForOverwrite(size_t start, size_t end) {
   1265    for (size_t i = start; i < end; i++) {
   1266      getSlotAddressUnchecked(i)->destroy();
   1267    }
   1268  }
   1269 
   1270  inline void shiftDenseElementsUnchecked(uint32_t count);
   1271 
   1272  // Like getSlotRef, but optimized for reserved slots. This relies on the fact
   1273  // that the first reserved slots (up to MAX_FIXED_SLOTS) are always stored in
   1274  // fixed slots. This lets the compiler optimize away the branch below when
   1275  // |index| is a constant (after inlining).
   1276  //
   1277  // Note: objects that may be swapped have less predictable slot layouts
   1278  // because they could have been swapped with an object with fewer fixed slots.
   1279  // Fortunately, the only native objects that can be swapped are DOM objects
   1280  // and these shouldn't end up here (asserted below).
   1281  MOZ_ALWAYS_INLINE HeapSlot& getReservedSlotRef(uint32_t index) {
   1282    MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
   1283    MOZ_ASSERT(slotIsFixed(index) == (index < MAX_FIXED_SLOTS));
   1284    MOZ_ASSERT(!ObjectMayBeSwapped(this));
   1285    return index < MAX_FIXED_SLOTS ? fixedSlots()[index]
   1286                                   : slots_[index - MAX_FIXED_SLOTS];
   1287  }
   1288  MOZ_ALWAYS_INLINE const HeapSlot& getReservedSlotRef(uint32_t index) const {
   1289    MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
   1290    MOZ_ASSERT(slotIsFixed(index) == (index < MAX_FIXED_SLOTS));
   1291    MOZ_ASSERT(!ObjectMayBeSwapped(this));
   1292    return index < MAX_FIXED_SLOTS ? fixedSlots()[index]
   1293                                   : slots_[index - MAX_FIXED_SLOTS];
   1294  }
   1295 
   1296 public:
   1297  MOZ_ALWAYS_INLINE const Value& getReservedSlot(uint32_t index) const {
   1298    return getReservedSlotRef(index);
   1299  }
   1300  MOZ_ALWAYS_INLINE void initReservedSlot(uint32_t index, const Value& v) {
   1301    MOZ_ASSERT(getReservedSlot(index).isUndefined());
   1302    checkStoredValue(v);
   1303    getReservedSlotRef(index).init(this, HeapSlot::Slot, index, v);
   1304  }
   1305  MOZ_ALWAYS_INLINE void setReservedSlot(uint32_t index, const Value& v) {
   1306    checkStoredValue(v);
   1307    getReservedSlotRef(index).set(this, HeapSlot::Slot, index, v);
   1308  }
   1309 
   1310  // For slots which are known to always be fixed, due to the way they are
   1311  // allocated.
   1312 
   1313  HeapSlot& getFixedSlotRef(uint32_t slot) {
   1314    MOZ_ASSERT(slotIsFixed(slot));
   1315    return fixedSlots()[slot];
   1316  }
   1317 
   1318  const Value& getFixedSlot(uint32_t slot) const {
   1319    MOZ_ASSERT(slotIsFixed(slot));
   1320    return fixedSlots()[slot];
   1321  }
   1322 
   1323  const Value& getDynamicSlot(uint32_t dynamicSlotIndex) const {
   1324    MOZ_ASSERT(dynamicSlotIndex < outOfLineNumDynamicSlots());
   1325    return slots_[dynamicSlotIndex];
   1326  }
   1327 
   1328  void setFixedSlot(uint32_t slot, const Value& value) {
   1329    MOZ_ASSERT(slotIsFixed(slot));
   1330    checkStoredValue(value);
   1331    fixedSlots()[slot].set(this, HeapSlot::Slot, slot, value);
   1332  }
   1333 
   1334  void setDynamicSlot(uint32_t numFixed, uint32_t slot, const Value& value) {
   1335    MOZ_ASSERT(numFixedSlots() == numFixed);
   1336    MOZ_ASSERT(slot >= numFixed);
   1337    MOZ_ASSERT(slot - numFixed < getSlotsHeader()->capacity());
   1338    checkStoredValue(value);
   1339    slots_[slot - numFixed].set(this, HeapSlot::Slot, slot, value);
   1340  }
   1341 
   1342  void initFixedSlot(uint32_t slot, const Value& value) {
   1343    MOZ_ASSERT(slotIsFixed(slot));
   1344    checkStoredValue(value);
   1345    fixedSlots()[slot].init(this, HeapSlot::Slot, slot, value);
   1346  }
   1347 
   1348  void initDynamicSlot(uint32_t numFixed, uint32_t slot, const Value& value) {
   1349    MOZ_ASSERT(numFixedSlots() == numFixed);
   1350    MOZ_ASSERT(slot >= numFixed);
   1351    MOZ_ASSERT(slot - numFixed < getSlotsHeader()->capacity());
   1352    checkStoredValue(value);
   1353    slots_[slot - numFixed].init(this, HeapSlot::Slot, slot, value);
   1354  }
   1355 
   1356  template <typename T>
   1357  T* maybePtrFromReservedSlot(uint32_t slot) const {
   1358    Value v = getReservedSlot(slot);
   1359    return v.isUndefined() ? nullptr : static_cast<T*>(v.toPrivate());
   1360  }
   1361 
   1362  // Returns the address of a reserved fixed slot that stores a T* as
   1363  // PrivateValue. Be very careful when using this because the object might be
   1364  // moved in memory!
   1365  template <typename T>
   1366  T** addressOfFixedSlotPrivatePtr(size_t slot) {
   1367    MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(getClass()));
   1368    MOZ_ASSERT(slotIsFixed(slot));
   1369    MOZ_ASSERT(getReservedSlot(slot).isDouble());
   1370    void* addr = &getFixedSlotRef(slot);
   1371    return reinterpret_cast<T**>(addr);
   1372  }
   1373 
   1374  /*
   1375   * Calculate the number of dynamic slots to allocate to cover the properties
   1376   * in an object with the given number of fixed slots and slot span.
   1377   */
   1378  static MOZ_ALWAYS_INLINE uint32_t calculateDynamicSlots(uint32_t nfixed,
   1379                                                          uint32_t span,
   1380                                                          const JSClass* clasp);
   1381  static MOZ_ALWAYS_INLINE uint32_t calculateDynamicSlots(SharedShape* shape);
   1382 
   1383  ObjectSlots* getSlotsHeader() const { return ObjectSlots::fromSlots(slots_); }
   1384 
   1385  /* Elements accessors. */
   1386 
   1387  // The maximum size, in sizeof(Value), of the allocation used for an
   1388  // object's dense elements.  (This includes space used to store an
   1389  // ObjectElements instance.)
   1390  // |MAX_DENSE_ELEMENTS_ALLOCATION * sizeof(JS::Value)| shouldn't overflow
   1391  // int32_t (see elementsSizeMustNotOverflow).
   1392  static const uint32_t MAX_DENSE_ELEMENTS_ALLOCATION = (1 << 28) - 1;
   1393 
   1394  // The maximum number of usable dense elements in an object.
   1395  static const uint32_t MAX_DENSE_ELEMENTS_COUNT =
   1396      MAX_DENSE_ELEMENTS_ALLOCATION - ObjectElements::VALUES_PER_HEADER;
   1397 
   1398  static void elementsSizeMustNotOverflow() {
   1399    static_assert(
   1400        NativeObject::MAX_DENSE_ELEMENTS_COUNT <= INT32_MAX / sizeof(JS::Value),
   1401        "every caller of this method require that an element "
   1402        "count multiplied by sizeof(Value) can't overflow "
   1403        "uint32_t (and sometimes int32_t ,too)");
   1404  }
   1405 
   1406  ObjectElements* getElementsHeader() const {
   1407    return ObjectElements::fromElements(elements_);
   1408  }
   1409 
   1410  // Returns a pointer to the first element, including shifted elements.
   1411  inline HeapSlot* unshiftedElements() const {
   1412    return elements_ - getElementsHeader()->numShiftedElements();
   1413  }
   1414 
   1415  // Like getElementsHeader, but returns a pointer to the unshifted header.
   1416  // This is mainly useful for free()ing dynamic elements: the pointer
   1417  // returned here is the one we got from malloc.
   1418  void* getUnshiftedElementsHeader() const {
   1419    return ObjectElements::fromElements(unshiftedElements());
   1420  }
   1421 
   1422  uint32_t unshiftedIndex(uint32_t index) const {
   1423    return index + getElementsHeader()->numShiftedElements();
   1424  }
   1425 
   1426  /* Accessors for elements. */
   1427  bool ensureElements(JSContext* cx, uint32_t capacity) {
   1428    MOZ_ASSERT(isExtensible());
   1429    if (capacity > getDenseCapacity()) {
   1430      return growElements(cx, capacity);
   1431    }
   1432    return true;
   1433  }
   1434 
   1435  // Try to shift |count| dense elements, see the "Shifted elements" comment.
   1436  inline bool tryShiftDenseElements(uint32_t count);
   1437 
   1438  // Try to make space for |count| dense elements at the start of the array.
   1439  bool tryUnshiftDenseElements(uint32_t count);
   1440 
   1441  // Move the elements header and all shifted elements to the start of the
   1442  // allocated elements space, so that numShiftedElements is 0 afterwards.
   1443  void moveShiftedElements();
   1444 
   1445  // If this object has many shifted elements call moveShiftedElements.
   1446  void maybeMoveShiftedElements();
   1447 
   1448  static bool goodElementsAllocationAmount(JSContext* cx, uint32_t reqAllocated,
   1449                                           uint32_t length,
   1450                                           uint32_t* goodAmount);
   1451  bool growElements(JSContext* cx, uint32_t newcap);
   1452  void shrinkElements(JSContext* cx, uint32_t cap);
   1453 
   1454 private:
   1455  // Run a post write barrier that encompasses multiple contiguous elements in a
   1456  // single step.
   1457  inline void elementsRangePostWriteBarrier(uint32_t start, uint32_t count);
   1458 
   1459 public:
   1460  void shrinkCapacityToInitializedLength(JSContext* cx);
   1461 
   1462 private:
   1463  void setDenseInitializedLengthInternal(uint32_t length) {
   1464    MOZ_ASSERT(length <= getDenseCapacity());
   1465    MOZ_ASSERT(!denseElementsAreFrozen());
   1466    prepareElementRangeForOverwrite(length,
   1467                                    getElementsHeader()->initializedLength);
   1468    getElementsHeader()->initializedLength = length;
   1469  }
   1470 
   1471 public:
   1472  void setDenseInitializedLength(uint32_t length) {
   1473    MOZ_ASSERT(isExtensible());
   1474    setDenseInitializedLengthInternal(length);
   1475  }
   1476 
   1477  void setDenseInitializedLengthMaybeNonExtensible(JSContext* cx,
   1478                                                   uint32_t length) {
   1479    setDenseInitializedLengthInternal(length);
   1480    if (!isExtensible()) {
   1481      shrinkCapacityToInitializedLength(cx);
   1482    }
   1483  }
   1484 
   1485  inline void ensureDenseInitializedLength(uint32_t index, uint32_t extra);
   1486 
   1487  void setDenseElement(uint32_t index, const Value& val) {
   1488    MOZ_ASSERT_IF(val.isMagic(), val.whyMagic() != JS_ELEMENTS_HOLE);
   1489    setDenseElementUnchecked(index, val);
   1490  }
   1491 
   1492  void initDenseElement(uint32_t index, const Value& val) {
   1493    MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
   1494    initDenseElementUnchecked(index, val);
   1495  }
   1496 
   1497 private:
   1498  // Note: 'Unchecked' here means we don't assert |val| isn't the hole
   1499  // MagicValue.
   1500  void initDenseElementUnchecked(uint32_t index, const Value& val) {
   1501    MOZ_ASSERT(index < getDenseInitializedLength());
   1502    MOZ_ASSERT(isExtensible());
   1503    checkStoredValue(val);
   1504    elements_[index].init(this, HeapSlot::Element, unshiftedIndex(index), val);
   1505  }
   1506  void setDenseElementUnchecked(uint32_t index, const Value& val) {
   1507    MOZ_ASSERT(index < getDenseInitializedLength());
   1508    MOZ_ASSERT(!denseElementsAreFrozen());
   1509    checkStoredValue(val);
   1510    elements_[index].set(this, HeapSlot::Element, unshiftedIndex(index), val);
   1511  }
   1512 
   1513  // Mark the dense elements as possibly containing holes.
   1514  inline void markDenseElementsNotPacked();
   1515 
   1516 public:
   1517  inline void initDenseElementHole(uint32_t index);
   1518  inline void setDenseElementHole(uint32_t index);
   1519  inline void removeDenseElementForSparseIndex(uint32_t index);
   1520 
   1521  inline void copyDenseElements(uint32_t dstStart, const Value* src,
   1522                                uint32_t count);
   1523 
   1524  inline void initDenseElements(const Value* src, uint32_t count);
   1525  inline void initDenseElements(IteratorProperty* src, uint32_t count);
   1526  inline void initDenseElements(NativeObject* src, uint32_t srcStart,
   1527                                uint32_t count);
   1528 
   1529  // Copy the first `count` dense elements from `src` to `this`, starting at
   1530  // `destStart`. The initialized length must already include the new elements.
   1531  inline void initDenseElementRange(uint32_t destStart, NativeObject* src,
   1532                                    uint32_t count);
   1533 
   1534  // Store the Values in the range [begin, end) as elements of this array.
   1535  //
   1536  // Preconditions: This must be a boring ArrayObject with dense initialized
   1537  // length 0: no shifted elements, no frozen elements, no fixed "length", not
   1538  // indexed, not inextensible, not copy-on-write. Existing capacity is
   1539  // optional.
   1540  //
   1541  // This runs write barriers but does not update types. `end - begin` must
   1542  // return the size of the range, which must be >= 0 and fit in an int32_t.
   1543  template <typename Iter>
   1544  [[nodiscard]] inline bool initDenseElementsFromRange(JSContext* cx,
   1545                                                       Iter begin, Iter end);
   1546 
   1547  inline void moveDenseElements(uint32_t dstStart, uint32_t srcStart,
   1548                                uint32_t count);
   1549  inline void reverseDenseElementsNoPreBarrier(uint32_t length);
   1550 
   1551  inline DenseElementResult setOrExtendDenseElements(JSContext* cx,
   1552                                                     uint32_t start,
   1553                                                     const Value* vp,
   1554                                                     uint32_t count);
   1555 
   1556  bool denseElementsAreSealed() const {
   1557    return getElementsHeader()->isSealed();
   1558  }
   1559  bool denseElementsAreFrozen() const {
   1560    return hasFlag(ObjectFlag::FrozenElements);
   1561  }
   1562 
   1563  bool denseElementsArePacked() const {
   1564    return getElementsHeader()->isPacked();
   1565  }
   1566 
   1567  void markDenseElementsMaybeInIteration() {
   1568    getElementsHeader()->markMaybeInIteration();
   1569  }
   1570 
   1571  // Return whether the object's dense elements might be in the midst of for-in
   1572  // iteration. We rely on this to be able to safely delete or move dense array
   1573  // elements without worrying about updating in-progress iterators.
   1574  // See bug 690622.
   1575  //
   1576  // Note that it's fine to return false if this object is on the prototype of
   1577  // another object: SuppressDeletedProperty only suppresses properties deleted
   1578  // from the iterated object itself.
   1579  inline bool denseElementsHaveMaybeInIterationFlag();
   1580  inline bool denseElementsMaybeInIteration();
   1581 
   1582  // Ensures that the object can hold at least index + extra elements. This
   1583  // returns DenseElement_Success on success, DenseElement_Failed on failure
   1584  // to grow the array, or DenseElement_Incomplete when the object is too
   1585  // sparse to grow (this includes the case of index + extra overflow). In
   1586  // the last two cases the object is kept intact.
   1587  inline DenseElementResult ensureDenseElements(JSContext* cx, uint32_t index,
   1588                                                uint32_t extra);
   1589 
   1590  inline DenseElementResult extendDenseElements(JSContext* cx,
   1591                                                uint32_t requiredCapacity,
   1592                                                uint32_t extra);
   1593 
   1594  /* Small objects are dense, no matter what. */
   1595  static const uint32_t MIN_SPARSE_INDEX = 1000;
   1596 
   1597  /*
   1598   * Element storage for an object will be sparse if fewer than 1/8 indexes
   1599   * are filled in.
   1600   */
   1601  static const unsigned SPARSE_DENSITY_RATIO = 8;
   1602 
   1603  /*
   1604   * Check if after growing the object's elements will be too sparse.
   1605   * newElementsHint is an estimated number of elements to be added.
   1606   */
   1607  bool willBeSparseElements(uint32_t requiredCapacity,
   1608                            uint32_t newElementsHint);
   1609 
   1610  /*
   1611   * After adding a sparse index to obj, see if it should be converted to use
   1612   * dense elements.
   1613   */
   1614  static DenseElementResult maybeDensifySparseElements(
   1615      JSContext* cx, Handle<NativeObject*> obj);
   1616  static bool densifySparseElements(JSContext* cx, Handle<NativeObject*> obj);
   1617 
   1618  inline HeapSlot* fixedElements() const {
   1619    static_assert(2 * sizeof(Value) == sizeof(ObjectElements),
   1620                  "when elements are stored inline, the first two "
   1621                  "slots will hold the ObjectElements header");
   1622    return &fixedSlots()[2];
   1623  }
   1624 
   1625 #ifdef DEBUG
   1626  bool canHaveNonEmptyElements();
   1627 #endif
   1628 
   1629  void setEmptyElements() { elements_ = emptyObjectElements; }
   1630 
   1631  void initFixedElements(gc::AllocKind kind, uint32_t length);
   1632 
   1633  // Update the elements pointer to use the fixed elements storage. The caller
   1634  // is responsible for initializing the elements themselves and setting the
   1635  // FIXED flag.
   1636  void setFixedElements(uint32_t numShifted = 0) {
   1637    MOZ_ASSERT(canHaveNonEmptyElements());
   1638    elements_ = fixedElements() + numShifted;
   1639  }
   1640 
   1641  inline bool hasDynamicElements() const {
   1642    return !hasEmptyElements() && !hasFixedElements();
   1643  }
   1644 
   1645  inline bool hasFixedElements() const {
   1646    bool fixed = getElementsHeader()->flags & ObjectElements::FIXED;
   1647    MOZ_ASSERT_IF(fixed, unshiftedElements() == fixedElements());
   1648    return fixed;
   1649  }
   1650 
   1651  inline bool hasEmptyElements() const {
   1652    return elements_ == emptyObjectElements ||
   1653           elements_ == emptyObjectElementsShared;
   1654  }
   1655 
   1656  /*
   1657   * Get a pointer to the unused data in the object's allocation immediately
   1658   * following this object, for use with objects which allocate a larger size
   1659   * class than they need and store non-elements data inline.
   1660   */
   1661  inline uint8_t* fixedData(size_t nslots) const;
   1662 
   1663  inline void privatePreWriteBarrier(HeapSlot* pprivate);
   1664 
   1665  // The methods below are used to store GC things in a reserved slot as
   1666  // PrivateValues. This is done to bypass the normal tracing code (debugger
   1667  // objects use this to store cross-compartment pointers).
   1668  //
   1669  // WARNING: make sure you REALLY need this and you know what you're doing
   1670  // before using these methods!
   1671  void setReservedSlotGCThingAsPrivate(uint32_t slot, gc::Cell* cell) {
   1672 #ifdef DEBUG
   1673    if (IsMarkedBlack(this)) {
   1674      JS::AssertCellIsNotGray(cell);
   1675    }
   1676 #endif
   1677    HeapSlot* pslot = getSlotAddress(slot);
   1678    Cell* prev = nullptr;
   1679    if (!pslot->isUndefined()) {
   1680      prev = static_cast<gc::Cell*>(pslot->toPrivate());
   1681      privatePreWriteBarrier(pslot);
   1682    }
   1683    setReservedSlotGCThingAsPrivateUnbarriered(slot, cell);
   1684    gc::PostWriteBarrierCell(this, prev, cell);
   1685  }
   1686  void setReservedSlotGCThingAsPrivateUnbarriered(uint32_t slot,
   1687                                                  gc::Cell* cell) {
   1688    MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(getClass()));
   1689    MOZ_ASSERT(cell);
   1690    getReservedSlotRef(slot).unbarrieredSet(PrivateValue(cell));
   1691  }
   1692  void clearReservedSlotGCThingAsPrivate(uint32_t slot) {
   1693    MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(getClass()));
   1694    HeapSlot* pslot = &getReservedSlotRef(slot);
   1695    if (!pslot->isUndefined()) {
   1696      privatePreWriteBarrier(pslot);
   1697      pslot->unbarrieredSet(UndefinedValue());
   1698    }
   1699  }
   1700 
   1701  // This is equivalent to |setReservedSlot(slot, PrivateValue(v))| but it
   1702  // avoids GC barriers. Use this only when storing a private value in a
   1703  // reserved slot that never holds a GC thing.
   1704  void setReservedSlotPrivateUnbarriered(uint32_t slot, void* v) {
   1705    MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(getClass()));
   1706    MOZ_ASSERT(getReservedSlot(slot).isUndefined() ||
   1707               getReservedSlot(slot).isDouble());
   1708    getReservedSlotRef(slot).unbarrieredSet(PrivateValue(v));
   1709  }
   1710 
   1711  // Like setReservedSlotPrivateUnbarriered but for PrivateUint32Value.
   1712  void setReservedSlotPrivateUint32Unbarriered(uint32_t slot, uint32_t u) {
   1713    MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(getClass()));
   1714    MOZ_ASSERT(getReservedSlot(slot).isUndefined() ||
   1715               getReservedSlot(slot).isInt32());
   1716    getReservedSlotRef(slot).unbarrieredSet(PrivateUint32Value(u));
   1717  }
   1718 
   1719  /* Return the allocKind we would use if we were to tenure this object. */
   1720  inline js::gc::AllocKind allocKindForTenure() const;
   1721 
   1722  // Native objects are never wrappers, so a native object always has a realm
   1723  // and global.
   1724  JS::Realm* realm() const { return nonCCWRealm(); }
   1725  inline js::GlobalObject& global() const;
   1726 
   1727  TaggedSlotOffset getTaggedSlotOffset(size_t slot) const {
   1728    MOZ_ASSERT(slot < slotSpan());
   1729    uint32_t nfixed = numFixedSlots();
   1730    if (slot < nfixed) {
   1731      return TaggedSlotOffset(getFixedSlotOffset(slot),
   1732                              /* isFixedSlot = */ true);
   1733    }
   1734    return TaggedSlotOffset((slot - nfixed) * sizeof(Value),
   1735                            /* isFixedSlot = */ false);
   1736  }
   1737 
   1738  bool hasUnpreservedWrapper() const {
   1739    return getClass()->preservesWrapper() &&
   1740           !shape()->hasObjectFlag(ObjectFlag::HasPreservedWrapper);
   1741  }
   1742 
   1743  /* JIT Accessors */
   1744  static size_t offsetOfElements() { return offsetof(NativeObject, elements_); }
   1745  static size_t offsetOfFixedElements() {
   1746    return sizeof(NativeObject) + sizeof(ObjectElements);
   1747  }
   1748 
   1749  static constexpr size_t getFixedSlotOffset(size_t slot) {
   1750    MOZ_ASSERT(slot < MAX_FIXED_SLOTS);
   1751    return sizeof(NativeObject) + slot * sizeof(Value);
   1752  }
   1753  static constexpr size_t getFixedSlotIndexFromOffset(size_t offset) {
   1754    MOZ_ASSERT(offset >= sizeof(NativeObject));
   1755    offset -= sizeof(NativeObject);
   1756    MOZ_ASSERT(offset % sizeof(Value) == 0);
   1757    MOZ_ASSERT(offset / sizeof(Value) < MAX_FIXED_SLOTS);
   1758    return offset / sizeof(Value);
   1759  }
   1760  static constexpr size_t getDynamicSlotIndexFromOffset(size_t offset) {
   1761    MOZ_ASSERT(offset % sizeof(Value) == 0);
   1762    return offset / sizeof(Value);
   1763  }
   1764  static size_t offsetOfSlots() { return offsetof(NativeObject, slots_); }
   1765 };
   1766 
   1767 inline void NativeObject::privatePreWriteBarrier(HeapSlot* pprivate) {
   1768  JS::shadow::Zone* shadowZone = this->shadowZoneFromAnyThread();
   1769  if (shadowZone->needsIncrementalBarrier() && pprivate->get().toPrivate() &&
   1770      getClass()->hasTrace()) {
   1771    getClass()->doTrace(shadowZone->barrierTracer(), this);
   1772  }
   1773 }
   1774 
   1775 /*** Standard internal methods **********************************************/
   1776 
   1777 /*
   1778 * These functions should follow the algorithms in ES2025 section 10.1
   1779 * ("Ordinary Object Internal Methods").
   1780 *
   1781 * Many native objects are not "ordinary" in ES2025, so these functions also
   1782 * have to serve some of the special needs of Arrays (10.4.2), Strings (10.4.3),
   1783 * and so on.
   1784 */
   1785 
   1786 extern bool NativeDefineProperty(JSContext* cx, Handle<NativeObject*> obj,
   1787                                 HandleId id,
   1788                                 Handle<JS::PropertyDescriptor> desc,
   1789                                 ObjectOpResult& result);
   1790 
   1791 extern bool NativeDefineDataProperty(JSContext* cx, Handle<NativeObject*> obj,
   1792                                     HandleId id, HandleValue value,
   1793                                     unsigned attrs, ObjectOpResult& result);
   1794 
   1795 /* If the result out-param is omitted, throw on failure. */
   1796 
   1797 extern bool NativeDefineAccessorProperty(JSContext* cx,
   1798                                         Handle<NativeObject*> obj, HandleId id,
   1799                                         HandleObject getter,
   1800                                         HandleObject setter, unsigned attrs);
   1801 
   1802 extern bool NativeDefineDataProperty(JSContext* cx, Handle<NativeObject*> obj,
   1803                                     HandleId id, HandleValue value,
   1804                                     unsigned attrs);
   1805 
   1806 extern bool NativeDefineDataProperty(JSContext* cx, Handle<NativeObject*> obj,
   1807                                     PropertyName* name, HandleValue value,
   1808                                     unsigned attrs);
   1809 
   1810 extern bool NativeHasProperty(JSContext* cx, Handle<NativeObject*> obj,
   1811                              HandleId id, bool* foundp);
   1812 
   1813 extern bool NativeGetOwnPropertyDescriptor(
   1814    JSContext* cx, Handle<NativeObject*> obj, HandleId id,
   1815    MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
   1816 
   1817 extern bool NativeGetProperty(JSContext* cx, Handle<NativeObject*> obj,
   1818                              HandleValue receiver, HandleId id,
   1819                              MutableHandleValue vp);
   1820 
   1821 extern bool NativeGetPropertyNoGC(JSContext* cx, NativeObject* obj,
   1822                                  const Value& receiver, jsid id, Value* vp);
   1823 
   1824 inline bool NativeGetProperty(JSContext* cx, Handle<NativeObject*> obj,
   1825                              HandleId id, MutableHandleValue vp) {
   1826  RootedValue receiver(cx, ObjectValue(*obj));
   1827  return NativeGetProperty(cx, obj, receiver, id, vp);
   1828 }
   1829 
   1830 extern bool NativeGetElement(JSContext* cx, Handle<NativeObject*> obj,
   1831                             HandleValue receiver, int32_t index,
   1832                             MutableHandleValue vp);
   1833 
   1834 bool GetSparseElementHelper(JSContext* cx, Handle<NativeObject*> obj,
   1835                            int32_t int_id, MutableHandleValue result);
   1836 
   1837 bool AddOrUpdateSparseElementHelper(JSContext* cx, Handle<NativeObject*> obj,
   1838                                    int32_t int_id, HandleValue v, bool strict);
   1839 
   1840 /*
   1841 * Indicates whether an assignment operation is qualified (`x.y = 0`) or
   1842 * unqualified (`y = 0`). In strict mode, the latter is an error if no such
   1843 * variable already exists.
   1844 *
   1845 * Used as an argument to NativeSetProperty.
   1846 */
   1847 enum QualifiedBool { Unqualified = 0, Qualified = 1 };
   1848 
   1849 template <QualifiedBool Qualified>
   1850 extern bool NativeSetProperty(JSContext* cx, Handle<NativeObject*> obj,
   1851                              HandleId id, HandleValue v, HandleValue receiver,
   1852                              ObjectOpResult& result);
   1853 
   1854 extern bool NativeSetElement(JSContext* cx, Handle<NativeObject*> obj,
   1855                             uint32_t index, HandleValue v,
   1856                             HandleValue receiver, ObjectOpResult& result);
   1857 
   1858 extern bool NativeDeleteProperty(JSContext* cx, Handle<NativeObject*> obj,
   1859                                 HandleId id, ObjectOpResult& result);
   1860 
   1861 /*** SpiderMonkey nonstandard internal methods ******************************/
   1862 
   1863 template <AllowGC allowGC>
   1864 extern bool NativeLookupOwnProperty(
   1865    JSContext* cx, typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
   1866    typename MaybeRooted<jsid, allowGC>::HandleType id, PropertyResult* propp);
   1867 
   1868 /*
   1869 * Get a property from `receiver`, after having already done a lookup and found
   1870 * the property on a native object `obj`.
   1871 *
   1872 * `prop` must be present in obj's shape.
   1873 */
   1874 extern bool NativeGetExistingProperty(JSContext* cx, HandleObject receiver,
   1875                                      Handle<NativeObject*> obj, HandleId id,
   1876                                      PropertyInfo prop, MutableHandleValue vp);
   1877 
   1878 /* * */
   1879 
   1880 extern bool GetNameBoundInEnvironment(JSContext* cx, HandleObject env,
   1881                                      HandleId id, MutableHandleValue vp);
   1882 
   1883 } /* namespace js */
   1884 
   1885 template <>
   1886 inline bool JSObject::is<js::NativeObject>() const {
   1887  return shape()->isNative();
   1888 }
   1889 
   1890 namespace js {
   1891 
   1892 // Alternate to JSObject::as<NativeObject>() that tolerates null pointers.
   1893 inline NativeObject* MaybeNativeObject(JSObject* obj) {
   1894  return obj ? &obj->as<NativeObject>() : nullptr;
   1895 }
   1896 
   1897 // Defined in NativeObject-inl.h.
   1898 bool IsPackedArray(JSObject* obj);
   1899 
   1900 // Initialize an object's reserved slot with a private value pointing to
   1901 // malloc-allocated memory and associate the memory with the object.
   1902 //
   1903 // This call should be matched with a call to JS::GCContext::free_/delete_ in
   1904 // the object's finalizer to free the memory and update the memory accounting.
   1905 
   1906 inline void InitReservedSlot(NativeObject* obj, uint32_t slot, void* ptr,
   1907                             size_t nbytes, MemoryUse use) {
   1908  AddCellMemory(obj, nbytes, use);
   1909  obj->initReservedSlot(slot, PrivateValue(ptr));
   1910 }
   1911 template <typename T>
   1912 inline void InitReservedSlot(NativeObject* obj, uint32_t slot, T* ptr,
   1913                             MemoryUse use) {
   1914  InitReservedSlot(obj, slot, ptr, sizeof(T), use);
   1915 }
   1916 
   1917 bool AddSlotAndCallAddPropHook(JSContext* cx, Handle<NativeObject*> obj,
   1918                               HandleValue v, Handle<Shape*> newShape);
   1919 
   1920 }  // namespace js
   1921 
   1922 #endif /* vm_NativeObject_h */