tor-browser

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

NativeObject-inl.h (28132B)


      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_inl_h
      8 #define vm_NativeObject_inl_h
      9 
     10 #include "vm/NativeObject.h"
     11 
     12 #include "mozilla/Maybe.h"
     13 
     14 #include <type_traits>
     15 
     16 #include "gc/GCEnum.h"
     17 #include "gc/GCProbes.h"
     18 #include "gc/MaybeRooted.h"
     19 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     20 #include "vm/Compartment.h"
     21 #include "vm/Iteration.h"
     22 #include "vm/PlainObject.h"
     23 #include "vm/PropertyResult.h"
     24 #include "vm/StringType.h"
     25 #include "vm/TypedArrayObject.h"
     26 
     27 #include "gc/Marking-inl.h"
     28 #include "gc/ObjectKind-inl.h"
     29 #include "vm/Compartment-inl.h"
     30 #include "vm/JSContext-inl.h"
     31 #include "vm/JSObject-inl.h"
     32 #include "vm/Realm-inl.h"
     33 #include "vm/Shape-inl.h"
     34 
     35 namespace js {
     36 
     37 constexpr ObjectSlots::ObjectSlots(uint32_t capacity,
     38                                   uint32_t dictionarySlotSpan,
     39                                   uint64_t maybeUniqueId)
     40    : capacity_(capacity),
     41      dictionarySlotSpan_(dictionarySlotSpan),
     42      maybeUniqueId_(maybeUniqueId) {
     43  MOZ_ASSERT(this->capacity() == capacity);
     44  MOZ_ASSERT(this->dictionarySlotSpan() == dictionarySlotSpan);
     45 }
     46 
     47 inline uint32_t NativeObject::numFixedSlotsMaybeForwarded() const {
     48  return gc::MaybeForwarded(JSObject::shape())->asNative().numFixedSlots();
     49 }
     50 
     51 inline uint8_t* NativeObject::fixedData(size_t nslots) const {
     52  MOZ_ASSERT(ClassCanHaveFixedData(gc::MaybeForwardedObjectClass(this)));
     53  MOZ_ASSERT(nslots == numFixedSlotsMaybeForwarded());
     54  return reinterpret_cast<uint8_t*>(&fixedSlots()[nslots]);
     55 }
     56 
     57 inline void NativeObject::initDenseElementHole(uint32_t index) {
     58  markDenseElementsNotPacked();
     59  initDenseElementUnchecked(index, MagicValue(JS_ELEMENTS_HOLE));
     60 }
     61 
     62 inline void NativeObject::setDenseElementHole(uint32_t index) {
     63  markDenseElementsNotPacked();
     64  setDenseElementUnchecked(index, MagicValue(JS_ELEMENTS_HOLE));
     65 }
     66 
     67 inline void NativeObject::removeDenseElementForSparseIndex(uint32_t index) {
     68  MOZ_ASSERT(containsPure(PropertyKey::Int(index)));
     69  if (containsDenseElement(index)) {
     70    setDenseElementHole(index);
     71  }
     72 }
     73 
     74 inline void NativeObject::markDenseElementsNotPacked() {
     75  MOZ_ASSERT(is<NativeObject>());
     76  getElementsHeader()->markNonPacked();
     77 }
     78 
     79 inline void NativeObject::elementsRangePostWriteBarrier(uint32_t start,
     80                                                        uint32_t count) {
     81  if (!isTenured()) {
     82    return;
     83  }
     84  for (size_t i = 0; i < count; i++) {
     85    const Value& v = elements_[start + i];
     86    if (v.isGCThing()) {
     87      if (gc::StoreBuffer* sb = v.toGCThing()->storeBuffer()) {
     88        sb->putSlot(this, HeapSlot::Element, unshiftedIndex(start + i),
     89                    count - i);
     90        return;
     91      }
     92    }
     93  }
     94 }
     95 
     96 inline void NativeObject::copyDenseElements(uint32_t dstStart, const Value* src,
     97                                            uint32_t count) {
     98  MOZ_ASSERT(dstStart + count <= getDenseCapacity());
     99  MOZ_ASSERT(isExtensible());
    100  MOZ_ASSERT_IF(count > 0, src != nullptr);
    101 #ifdef DEBUG
    102  for (uint32_t i = 0; i < count; ++i) {
    103    checkStoredValue(src[i]);
    104  }
    105 #endif
    106  if (count == 0) {
    107    return;
    108  }
    109  if (zone()->needsIncrementalBarrier()) {
    110    uint32_t numShifted = getElementsHeader()->numShiftedElements();
    111    for (uint32_t i = 0; i < count; ++i) {
    112      elements_[dstStart + i].set(this, HeapSlot::Element,
    113                                  dstStart + i + numShifted, src[i]);
    114    }
    115  } else {
    116    memcpy(reinterpret_cast<Value*>(&elements_[dstStart]), src,
    117           count * sizeof(Value));
    118    elementsRangePostWriteBarrier(dstStart, count);
    119  }
    120 }
    121 
    122 inline void NativeObject::initDenseElements(NativeObject* src,
    123                                            uint32_t srcStart, uint32_t count) {
    124  MOZ_ASSERT(src->getDenseInitializedLength() >= srcStart + count);
    125 
    126  const Value* vp = src->getDenseElements() + srcStart;
    127 
    128  if (!src->denseElementsArePacked()) {
    129    // Mark non-packed if we're copying holes or if there are too many elements
    130    // to check this efficiently.
    131    static constexpr uint32_t MaxCountForPackedCheck = 30;
    132    if (count > MaxCountForPackedCheck) {
    133      markDenseElementsNotPacked();
    134    } else {
    135      for (uint32_t i = 0; i < count; i++) {
    136        if (vp[i].isMagic(JS_ELEMENTS_HOLE)) {
    137          markDenseElementsNotPacked();
    138          break;
    139        }
    140      }
    141    }
    142  }
    143 
    144  initDenseElements(vp, count);
    145 }
    146 
    147 inline void NativeObject::initDenseElements(const Value* src, uint32_t count) {
    148  MOZ_ASSERT(getDenseInitializedLength() == 0);
    149  MOZ_ASSERT(count <= getDenseCapacity());
    150  MOZ_ASSERT(src);
    151  MOZ_ASSERT(isExtensible());
    152 
    153  setDenseInitializedLength(count);
    154 
    155 #ifdef DEBUG
    156  for (uint32_t i = 0; i < count; ++i) {
    157    checkStoredValue(src[i]);
    158  }
    159 #endif
    160 
    161  memcpy(reinterpret_cast<Value*>(elements_), src, count * sizeof(Value));
    162  elementsRangePostWriteBarrier(0, count);
    163 }
    164 
    165 inline void NativeObject::initDenseElements(IteratorProperty* src,
    166                                            uint32_t count) {
    167  MOZ_ASSERT(getDenseInitializedLength() == 0);
    168  MOZ_ASSERT(count <= getDenseCapacity());
    169  MOZ_ASSERT(src);
    170  MOZ_ASSERT(isExtensible());
    171 
    172  setDenseInitializedLength(count);
    173  Value* elementsBase = reinterpret_cast<Value*>(elements_);
    174  for (size_t i = 0; i < count; i++) {
    175    elementsBase[i].setString(src[i].asString());
    176  }
    177 
    178  elementsRangePostWriteBarrier(0, count);
    179 }
    180 
    181 inline void NativeObject::initDenseElementRange(uint32_t destStart,
    182                                                NativeObject* src,
    183                                                uint32_t count) {
    184  MOZ_ASSERT(count <= src->getDenseInitializedLength());
    185 
    186  // The initialized length must already be set to the correct value.
    187  MOZ_ASSERT(destStart + count == getDenseInitializedLength());
    188 
    189  if (!src->denseElementsArePacked()) {
    190    markDenseElementsNotPacked();
    191  }
    192 
    193  const Value* vp = src->getDenseElements();
    194 #ifdef DEBUG
    195  for (uint32_t i = 0; i < count; ++i) {
    196    checkStoredValue(vp[i]);
    197  }
    198 #endif
    199  memcpy(reinterpret_cast<Value*>(elements_) + destStart, vp,
    200         count * sizeof(Value));
    201  elementsRangePostWriteBarrier(destStart, count);
    202 }
    203 
    204 template <typename Iter>
    205 inline bool NativeObject::initDenseElementsFromRange(JSContext* cx, Iter begin,
    206                                                     Iter end) {
    207  // This method populates the elements of a particular Array that's an
    208  // internal implementation detail of GeneratorObject. Failing any of the
    209  // following means the Array has escaped and/or been mistreated.
    210  MOZ_ASSERT(isExtensible());
    211  MOZ_ASSERT(!isIndexed());
    212  MOZ_ASSERT(is<ArrayObject>());
    213  MOZ_ASSERT(as<ArrayObject>().lengthIsWritable());
    214  MOZ_ASSERT(!denseElementsAreFrozen());
    215  MOZ_ASSERT(getElementsHeader()->numShiftedElements() == 0);
    216 
    217  MOZ_ASSERT(getDenseInitializedLength() == 0);
    218 
    219  auto size = end - begin;
    220  uint32_t count = uint32_t(size);
    221  MOZ_ASSERT(count <= uint32_t(INT32_MAX));
    222  if (count > getDenseCapacity()) {
    223    if (!growElements(cx, count)) {
    224      return false;
    225    }
    226  }
    227 
    228  HeapSlot* sp = elements_;
    229  size_t slot = 0;
    230  for (; begin != end; sp++, begin++) {
    231    Value v = *begin;
    232 #ifdef DEBUG
    233    checkStoredValue(v);
    234 #endif
    235    sp->init(this, HeapSlot::Element, slot++, v);
    236  }
    237  MOZ_ASSERT(slot == count);
    238 
    239  getElementsHeader()->initializedLength = count;
    240  as<ArrayObject>().setLengthToInitializedLength();
    241  return true;
    242 }
    243 
    244 inline bool NativeObject::tryShiftDenseElements(uint32_t count) {
    245  MOZ_ASSERT(isExtensible());
    246 
    247  ObjectElements* header = getElementsHeader();
    248  if (header->initializedLength == count ||
    249      count > ObjectElements::MaxShiftedElements ||
    250      header->hasNonwritableArrayLength()) {
    251    return false;
    252  }
    253 
    254  shiftDenseElementsUnchecked(count);
    255  return true;
    256 }
    257 
    258 inline void NativeObject::shiftDenseElementsUnchecked(uint32_t count) {
    259  MOZ_ASSERT(isExtensible());
    260 
    261  ObjectElements* header = getElementsHeader();
    262  MOZ_ASSERT(count > 0);
    263  MOZ_ASSERT(count < header->initializedLength);
    264 
    265  if (MOZ_UNLIKELY(header->numShiftedElements() + count >
    266                   ObjectElements::MaxShiftedElements)) {
    267    moveShiftedElements();
    268    header = getElementsHeader();
    269  }
    270 
    271  prepareElementRangeForOverwrite(0, count);
    272  header->addShiftedElements(count);
    273 
    274  elements_ += count;
    275  ObjectElements* newHeader = getElementsHeader();
    276  memmove(newHeader, header, sizeof(ObjectElements));
    277 }
    278 
    279 inline void NativeObject::moveDenseElements(uint32_t dstStart,
    280                                            uint32_t srcStart, uint32_t count) {
    281  MOZ_ASSERT(dstStart + count <= getDenseCapacity());
    282  MOZ_ASSERT(srcStart + count <= getDenseInitializedLength());
    283  MOZ_ASSERT(isExtensible());
    284 
    285  /*
    286   * Using memmove here would skip write barriers. Also, we need to consider
    287   * an array containing [A, B, C], in the following situation:
    288   *
    289   * 1. Incremental GC marks slot 0 of array (i.e., A), then returns to JS code.
    290   * 2. JS code moves slots 1..2 into slots 0..1, so it contains [B, C, C].
    291   * 3. Incremental GC finishes by marking slots 1 and 2 (i.e., C).
    292   *
    293   * Since normal marking never happens on B, it is very important that the
    294   * write barrier is invoked here on B, despite the fact that it exists in
    295   * the array before and after the move.
    296   */
    297  if (zone()->needsIncrementalBarrier()) {
    298    uint32_t numShifted = getElementsHeader()->numShiftedElements();
    299    if (dstStart < srcStart) {
    300      HeapSlot* dst = elements_ + dstStart;
    301      HeapSlot* src = elements_ + srcStart;
    302      for (uint32_t i = 0; i < count; i++, dst++, src++) {
    303        dst->set(this, HeapSlot::Element, dst - elements_ + numShifted, *src);
    304      }
    305    } else {
    306      HeapSlot* dst = elements_ + dstStart + count - 1;
    307      HeapSlot* src = elements_ + srcStart + count - 1;
    308      for (uint32_t i = 0; i < count; i++, dst--, src--) {
    309        dst->set(this, HeapSlot::Element, dst - elements_ + numShifted, *src);
    310      }
    311    }
    312  } else {
    313    memmove(elements_ + dstStart, elements_ + srcStart,
    314            count * sizeof(HeapSlot));
    315    elementsRangePostWriteBarrier(dstStart, count);
    316  }
    317 }
    318 
    319 inline void NativeObject::reverseDenseElementsNoPreBarrier(uint32_t length) {
    320  MOZ_ASSERT(!zone()->needsIncrementalBarrier());
    321 
    322  MOZ_ASSERT(isExtensible());
    323 
    324  MOZ_ASSERT(length > 1);
    325  MOZ_ASSERT(length <= getDenseInitializedLength());
    326 
    327  Value* valLo = reinterpret_cast<Value*>(elements_);
    328  Value* valHi = valLo + (length - 1);
    329  MOZ_ASSERT(valLo < valHi);
    330 
    331  do {
    332    Value origLo = *valLo;
    333    *valLo = *valHi;
    334    *valHi = origLo;
    335    ++valLo;
    336    --valHi;
    337  } while (valLo < valHi);
    338 
    339  elementsRangePostWriteBarrier(0, length);
    340 }
    341 
    342 inline void NativeObject::ensureDenseInitializedLength(uint32_t index,
    343                                                       uint32_t extra) {
    344  // Ensure that the array's contents have been initialized up to index, and
    345  // mark the elements through 'index + extra' as initialized in preparation
    346  // for a write.
    347 
    348  MOZ_ASSERT(!denseElementsAreFrozen());
    349  MOZ_ASSERT(isExtensible() || (containsDenseElement(index) && extra == 1));
    350  MOZ_ASSERT(index + extra <= getDenseCapacity());
    351 
    352  uint32_t initlen = getDenseInitializedLength();
    353  if (index + extra <= initlen) {
    354    return;
    355  }
    356 
    357  MOZ_ASSERT(isExtensible());
    358 
    359  if (index > initlen) {
    360    markDenseElementsNotPacked();
    361  }
    362 
    363  uint32_t numShifted = getElementsHeader()->numShiftedElements();
    364  size_t offset = initlen;
    365  for (HeapSlot* sp = elements_ + initlen; sp != elements_ + (index + extra);
    366       sp++, offset++) {
    367    sp->init(this, HeapSlot::Element, offset + numShifted,
    368             MagicValue(JS_ELEMENTS_HOLE));
    369  }
    370 
    371  getElementsHeader()->initializedLength = index + extra;
    372 }
    373 
    374 DenseElementResult NativeObject::extendDenseElements(JSContext* cx,
    375                                                     uint32_t requiredCapacity,
    376                                                     uint32_t extra) {
    377  MOZ_ASSERT(isExtensible());
    378 
    379  /*
    380   * Don't grow elements for objects which already have sparse indexes.
    381   * This avoids needing to count non-hole elements in willBeSparseElements
    382   * every time a new index is added.
    383   */
    384  if (isIndexed()) {
    385    return DenseElementResult::Incomplete;
    386  }
    387 
    388  /*
    389   * We use the extra argument also as a hint about number of non-hole
    390   * elements to be inserted.
    391   */
    392  if (requiredCapacity > MIN_SPARSE_INDEX &&
    393      willBeSparseElements(requiredCapacity, extra)) {
    394    return DenseElementResult::Incomplete;
    395  }
    396 
    397  if (!growElements(cx, requiredCapacity)) {
    398    return DenseElementResult::Failure;
    399  }
    400 
    401  return DenseElementResult::Success;
    402 }
    403 
    404 inline DenseElementResult NativeObject::ensureDenseElements(JSContext* cx,
    405                                                            uint32_t index,
    406                                                            uint32_t extra) {
    407  MOZ_ASSERT(is<NativeObject>());
    408  MOZ_ASSERT(isExtensible() || (containsDenseElement(index) && extra == 1));
    409 
    410  uint32_t requiredCapacity;
    411  if (extra == 1) {
    412    /* Optimize for the common case. */
    413    if (index < getDenseCapacity()) {
    414      ensureDenseInitializedLength(index, 1);
    415      return DenseElementResult::Success;
    416    }
    417    requiredCapacity = index + 1;
    418    if (requiredCapacity == 0) {
    419      /* Overflow. */
    420      return DenseElementResult::Incomplete;
    421    }
    422  } else {
    423    requiredCapacity = index + extra;
    424    if (requiredCapacity < index) {
    425      /* Overflow. */
    426      return DenseElementResult::Incomplete;
    427    }
    428    if (requiredCapacity <= getDenseCapacity()) {
    429      ensureDenseInitializedLength(index, extra);
    430      return DenseElementResult::Success;
    431    }
    432  }
    433 
    434  DenseElementResult result = extendDenseElements(cx, requiredCapacity, extra);
    435  if (result != DenseElementResult::Success) {
    436    return result;
    437  }
    438 
    439  ensureDenseInitializedLength(index, extra);
    440  return DenseElementResult::Success;
    441 }
    442 
    443 inline DenseElementResult NativeObject::setOrExtendDenseElements(
    444    JSContext* cx, uint32_t start, const Value* vp, uint32_t count) {
    445  if (!isExtensible()) {
    446    return DenseElementResult::Incomplete;
    447  }
    448 
    449  if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable() &&
    450      start + count >= as<ArrayObject>().length()) {
    451    return DenseElementResult::Incomplete;
    452  }
    453 
    454  DenseElementResult result = ensureDenseElements(cx, start, count);
    455  if (result != DenseElementResult::Success) {
    456    return result;
    457  }
    458 
    459  if (is<ArrayObject>() && start + count >= as<ArrayObject>().length()) {
    460    as<ArrayObject>().setLengthToInitializedLength();
    461  }
    462 
    463  copyDenseElements(start, vp, count);
    464  return DenseElementResult::Success;
    465 }
    466 
    467 /* static */
    468 inline NativeObject* NativeObject::create(
    469    JSContext* cx, js::gc::AllocKind kind, js::gc::Heap heap,
    470    js::Handle<SharedShape*> shape, js::gc::AllocSite* site /* = nullptr */) {
    471  debugCheckNewObject(shape, kind, heap);
    472 
    473  const JSClass* clasp = shape->getObjectClass();
    474  MOZ_ASSERT(clasp->isNativeObject());
    475  MOZ_ASSERT(!clasp->isJSFunction(), "should use JSFunction::create");
    476  MOZ_ASSERT(clasp != &ArrayObject::class_, "should use ArrayObject::create");
    477 
    478  const uint32_t nfixed = shape->numFixedSlots();
    479  const uint32_t slotSpan = shape->slotSpan();
    480  const size_t nDynamicSlots = calculateDynamicSlots(nfixed, slotSpan, clasp);
    481 
    482  NativeObject* nobj = cx->newCell<NativeObject>(kind, heap, clasp, site);
    483  if (!nobj) {
    484    return nullptr;
    485  }
    486 
    487  nobj->initShape(shape);
    488  nobj->setEmptyElements();
    489 
    490  if (!nDynamicSlots) {
    491    nobj->initEmptyDynamicSlots();
    492  } else if (!nobj->allocateInitialSlots(cx, nDynamicSlots)) {
    493    return nullptr;
    494  }
    495 
    496  if (slotSpan > 0) {
    497    nobj->initSlots(nfixed, slotSpan);
    498  }
    499 
    500  if (MOZ_UNLIKELY(cx->realm()->hasAllocationMetadataBuilder())) {
    501    if (clasp->shouldDelayMetadataBuilder()) {
    502      cx->realm()->setObjectPendingMetadata(nobj);
    503    } else {
    504      nobj = SetNewObjectMetadata(cx, nobj);
    505    }
    506  }
    507 
    508  js::gc::gcprobes::CreateObject(nobj);
    509 
    510  return nobj;
    511 }
    512 
    513 MOZ_ALWAYS_INLINE void NativeObject::initEmptyDynamicSlots() {
    514  setEmptyDynamicSlots(0);
    515 }
    516 
    517 MOZ_ALWAYS_INLINE void NativeObject::setDictionaryModeSlotSpan(uint32_t span) {
    518  MOZ_ASSERT(inDictionaryMode());
    519 
    520  if (!hasDynamicSlots()) {
    521    setEmptyDynamicSlots(span);
    522    return;
    523  }
    524 
    525  getSlotsHeader()->setDictionarySlotSpan(span);
    526 }
    527 
    528 MOZ_ALWAYS_INLINE void NativeObject::setEmptyDynamicSlots(
    529    uint32_t dictionarySlotSpan) {
    530  MOZ_ASSERT_IF(!inDictionaryMode(), dictionarySlotSpan == 0);
    531  MOZ_ASSERT(dictionarySlotSpan <= MAX_FIXED_SLOTS);
    532 
    533  slots_ = emptyObjectSlotsForDictionaryObject[dictionarySlotSpan];
    534 
    535  MOZ_ASSERT(getSlotsHeader()->capacity() == 0);
    536  MOZ_ASSERT(getSlotsHeader()->dictionarySlotSpan() == dictionarySlotSpan);
    537  MOZ_ASSERT(!hasDynamicSlots());
    538  MOZ_ASSERT(!hasUniqueId());
    539 }
    540 
    541 MOZ_ALWAYS_INLINE bool NativeObject::setShapeAndAddNewSlots(
    542    JSContext* cx, SharedShape* newShape, uint32_t oldSpan, uint32_t newSpan) {
    543  MOZ_ASSERT(!inDictionaryMode());
    544  MOZ_ASSERT(newShape->isShared());
    545  MOZ_ASSERT(newShape->zone() == zone());
    546  MOZ_ASSERT(newShape->numFixedSlots() == numFixedSlots());
    547  MOZ_ASSERT(newShape->getObjectClass() == getClass());
    548 
    549  MOZ_ASSERT(oldSpan < newSpan);
    550  MOZ_ASSERT(sharedShape()->slotSpan() == oldSpan);
    551  MOZ_ASSERT(newShape->slotSpan() == newSpan);
    552 
    553  uint32_t numFixed = newShape->numFixedSlots();
    554  if (newSpan > numFixed) {
    555    uint32_t oldCapacity = numDynamicSlots();
    556    uint32_t newCapacity =
    557        calculateDynamicSlots(numFixed, newSpan, newShape->getObjectClass());
    558    MOZ_ASSERT(oldCapacity <= newCapacity);
    559 
    560    if (oldCapacity < newCapacity) {
    561      if (MOZ_UNLIKELY(!growSlots(cx, oldCapacity, newCapacity))) {
    562        return false;
    563      }
    564    }
    565  }
    566 
    567  // Initialize slots [oldSpan, newSpan). Use the *Unchecked version because
    568  // the shape's slot span does not reflect the allocated slots at this
    569  // point.
    570  auto initRange = [](HeapSlot* start, HeapSlot* end) {
    571    for (HeapSlot* slot = start; slot < end; slot++) {
    572      slot->initAsUndefined();
    573    }
    574  };
    575  forEachSlotRangeUnchecked(oldSpan, newSpan, initRange);
    576 
    577  setShape(newShape);
    578  return true;
    579 }
    580 
    581 MOZ_ALWAYS_INLINE bool NativeObject::setShapeAndAddNewSlot(
    582    JSContext* cx, SharedShape* newShape, uint32_t slot) {
    583  MOZ_ASSERT(!inDictionaryMode());
    584  MOZ_ASSERT(newShape->isShared());
    585  MOZ_ASSERT(newShape->zone() == zone());
    586  MOZ_ASSERT(newShape->numFixedSlots() == numFixedSlots());
    587 
    588  MOZ_ASSERT(newShape->base() == shape()->base());
    589  MOZ_ASSERT(newShape->slotSpan() == sharedShape()->slotSpan() + 1);
    590  MOZ_ASSERT(newShape->slotSpan() == slot + 1);
    591 
    592  uint32_t numFixed = newShape->numFixedSlots();
    593  if (slot < numFixed) {
    594    initFixedSlot(slot, UndefinedValue());
    595  } else {
    596    uint32_t dynamicSlotIndex = slot - numFixed;
    597    if (dynamicSlotIndex >= numDynamicSlots()) {
    598      if (MOZ_UNLIKELY(!growSlotsForNewSlot(cx, numFixed, slot))) {
    599        return false;
    600      }
    601    }
    602    initDynamicSlot(numFixed, slot, UndefinedValue());
    603  }
    604 
    605  setShape(newShape);
    606  return true;
    607 }
    608 
    609 inline js::gc::AllocKind NativeObject::allocKindForTenure() const {
    610  using namespace js::gc;
    611  AllocKind kind = GetGCObjectFixedSlotsKind(numFixedSlots());
    612  MOZ_ASSERT(!IsFinalizedKind(kind));
    613  return GetFinalizedAllocKindForClass(kind, getClass());
    614 }
    615 
    616 inline js::GlobalObject& NativeObject::global() const { return nonCCWGlobal(); }
    617 
    618 inline bool NativeObject::denseElementsHaveMaybeInIterationFlag() {
    619  if (!getElementsHeader()->maybeInIteration()) {
    620    AssertDenseElementsNotIterated(this);
    621    return false;
    622  }
    623  return true;
    624 }
    625 
    626 inline bool NativeObject::denseElementsMaybeInIteration() {
    627  if (!denseElementsHaveMaybeInIterationFlag()) {
    628    return false;
    629  }
    630  return compartment()->objectMaybeInIteration(this);
    631 }
    632 
    633 /*
    634 * Call obj's resolve hook.
    635 *
    636 * cx and id are the parameters initially passed to the ongoing lookup;
    637 * propp and recursedp are its out parameters.
    638 *
    639 * There are four possible outcomes:
    640 *
    641 *  - On failure, report an error or exception and return false.
    642 *
    643 *  - If we are already resolving a property of obj, call setRecursiveResolve on
    644 *    propp and return true.
    645 *
    646 *  - If the resolve hook finds or defines the sought property, set propp
    647 *    appropriately, and return true.
    648 *
    649 *  - Otherwise no property was resolved. Set propp to NotFound and return true.
    650 */
    651 static MOZ_ALWAYS_INLINE bool CallResolveOp(JSContext* cx,
    652                                            Handle<NativeObject*> obj,
    653                                            HandleId id,
    654                                            PropertyResult* propp) {
    655  // Avoid recursion on (obj, id) already being resolved on cx.
    656  AutoResolving resolving(cx, obj, id);
    657  if (resolving.alreadyStarted()) {
    658    // Already resolving id in obj, suppress recursion.
    659    propp->setRecursiveResolve();
    660    return true;
    661  }
    662 
    663  bool resolved = false;
    664  AutoRealm ar(cx, obj);
    665  if (!obj->getClass()->getResolve()(cx, obj, id, &resolved)) {
    666    return false;
    667  }
    668 
    669  if (!resolved) {
    670    propp->setNotFound();
    671    return true;
    672  }
    673 
    674  // Assert the mayResolve hook, if there is one, returns true for this
    675  // property.
    676  MOZ_ASSERT_IF(obj->getClass()->getMayResolve(),
    677                obj->getClass()->getMayResolve()(cx->names(), id, obj));
    678 
    679  if (id.isInt()) {
    680    uint32_t index = id.toInt();
    681    if (obj->containsDenseElement(index)) {
    682      propp->setDenseElement(index);
    683      return true;
    684    }
    685  }
    686 
    687  MOZ_ASSERT(!obj->is<TypedArrayObject>());
    688 
    689  mozilla::Maybe<PropertyInfo> prop = obj->lookup(cx, id);
    690  if (prop.isSome()) {
    691    propp->setNativeProperty(*prop);
    692  } else {
    693    propp->setNotFound();
    694  }
    695 
    696  return true;
    697 }
    698 
    699 enum class LookupResolveMode {
    700  IgnoreResolve,
    701  CheckResolve,
    702  CheckMayResolve,
    703 };
    704 
    705 template <AllowGC allowGC,
    706          LookupResolveMode resolveMode = LookupResolveMode::CheckResolve>
    707 static MOZ_ALWAYS_INLINE bool NativeLookupOwnPropertyInline(
    708    JSContext* cx, typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
    709    typename MaybeRooted<jsid, allowGC>::HandleType id, PropertyResult* propp) {
    710  // Native objects should should avoid `lookupProperty` hooks, and those that
    711  // use them should avoid recursively triggering lookup, and those that still
    712  // violate this guidance are the ModuleEnvironmentObject.
    713  MOZ_ASSERT_IF(obj->getOpsLookupProperty(),
    714                obj->template is<ModuleEnvironmentObject>());
    715 
    716  // Check for a native dense element.
    717  if (id.isInt()) {
    718    uint32_t index = id.toInt();
    719    if (obj->containsDenseElement(index)) {
    720      propp->setDenseElement(index);
    721      return true;
    722    }
    723  }
    724 
    725  // Check for a typed array element. Integer lookups always finish here
    726  // so that integer properties on the prototype are ignored even for out
    727  // of bounds accesses.
    728  if (obj->template is<TypedArrayObject>()) {
    729    if (mozilla::Maybe<uint64_t> index = ToTypedArrayIndex(id)) {
    730      uint64_t idx = index.value();
    731      if (idx < obj->template as<TypedArrayObject>().length().valueOr(0)) {
    732        propp->setTypedArrayElement(idx);
    733      } else {
    734        propp->setTypedArrayOutOfRange();
    735      }
    736      return true;
    737    }
    738  }
    739 
    740  MOZ_ASSERT(cx->compartment() == obj->compartment());
    741 
    742  // Check for a native property. Call Shape::lookup directly (instead of
    743  // NativeObject::lookup) because it's inlined.
    744  uint32_t index;
    745  if (PropMap* map = obj->shape()->lookup(cx, id, &index)) {
    746    propp->setNativeProperty(map->getPropertyInfo(index));
    747    return true;
    748  }
    749 
    750  // Some callers explicitily want us to ignore the resolve hook entirely. In
    751  // that case, we report the property as NotFound.
    752  if constexpr (resolveMode == LookupResolveMode::IgnoreResolve) {
    753    propp->setNotFound();
    754    return true;
    755  }
    756 
    757  // JITs in particular use the `mayResolve` hook to determine a JSClass can
    758  // never resolve this property name (for all instances of the class).
    759  if constexpr (resolveMode == LookupResolveMode::CheckMayResolve) {
    760    static_assert(allowGC == false,
    761                  "CheckMayResolve can only be used with NoGC");
    762 
    763    MOZ_ASSERT(propp->isNotFound());
    764    return !ClassMayResolveId(cx->names(), obj->getClass(), id, obj);
    765  }
    766 
    767  MOZ_ASSERT(resolveMode == LookupResolveMode::CheckResolve);
    768 
    769  // If there is no resolve hook, the property definitely does not exist.
    770  if (obj->getClass()->getResolve()) {
    771    if constexpr (!allowGC) {
    772      return false;
    773    } else {
    774      return CallResolveOp(cx, obj, id, propp);
    775    }
    776  }
    777 
    778  propp->setNotFound();
    779  return true;
    780 }
    781 
    782 /*
    783 * Simplified version of NativeLookupOwnPropertyInline that doesn't call
    784 * resolve hooks.
    785 */
    786 [[nodiscard]] static inline bool NativeLookupOwnPropertyNoResolve(
    787    JSContext* cx, NativeObject* obj, jsid id, PropertyResult* result) {
    788  return NativeLookupOwnPropertyInline<NoGC, LookupResolveMode::IgnoreResolve>(
    789      cx, obj, id, result);
    790 }
    791 
    792 template <AllowGC allowGC,
    793          LookupResolveMode resolveMode = LookupResolveMode::CheckResolve>
    794 static MOZ_ALWAYS_INLINE bool NativeLookupPropertyInline(
    795    JSContext* cx, typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
    796    typename MaybeRooted<jsid, allowGC>::HandleType id,
    797    typename MaybeRooted<
    798        std::conditional_t<allowGC == AllowGC::CanGC, JSObject*, NativeObject*>,
    799        allowGC>::MutableHandleType objp,
    800    PropertyResult* propp) {
    801  /* Search scopes starting with obj and following the prototype link. */
    802  typename MaybeRooted<NativeObject*, allowGC>::RootType current(cx, obj);
    803 
    804  while (true) {
    805    if (!NativeLookupOwnPropertyInline<allowGC, resolveMode>(cx, current, id,
    806                                                             propp)) {
    807      return false;
    808    }
    809 
    810    if (propp->isFound()) {
    811      objp.set(current);
    812      return true;
    813    }
    814 
    815    if (propp->shouldIgnoreProtoChain()) {
    816      break;
    817    }
    818 
    819    JSObject* proto = current->staticPrototype();
    820    if (!proto) {
    821      break;
    822    }
    823 
    824    // If a `lookupProperty` hook exists, recurse into LookupProperty, otherwise
    825    // we can simply loop within this call frame.
    826    if (proto->getOpsLookupProperty()) {
    827      if constexpr (allowGC) {
    828        RootedObject protoRoot(cx, proto);
    829        return LookupProperty(cx, protoRoot, id, objp, propp);
    830      } else {
    831        return false;
    832      }
    833    }
    834 
    835    current = &proto->as<NativeObject>();
    836  }
    837 
    838  MOZ_ASSERT(propp->isNotFound());
    839  objp.set(nullptr);
    840  return true;
    841 }
    842 
    843 inline bool ThrowIfNotConstructing(JSContext* cx, const CallArgs& args,
    844                                   const char* builtinName) {
    845  if (args.isConstructing()) {
    846    return true;
    847  }
    848  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    849                            JSMSG_BUILTIN_CTOR_NO_NEW, builtinName);
    850  return false;
    851 }
    852 
    853 inline bool IsPackedArray(JSObject* obj) {
    854  if (!obj->is<ArrayObject>()) {
    855    return false;
    856  }
    857 
    858  ArrayObject* arr = &obj->as<ArrayObject>();
    859  if (arr->getDenseInitializedLength() != arr->length()) {
    860    return false;
    861  }
    862 
    863  if (!arr->denseElementsArePacked()) {
    864    return false;
    865  }
    866 
    867 #ifdef DEBUG
    868  // Assert correctness of the NON_PACKED flag by checking the first few
    869  // elements don't contain holes.
    870  uint32_t numToCheck = std::min<uint32_t>(5, arr->getDenseInitializedLength());
    871  for (uint32_t i = 0; i < numToCheck; i++) {
    872    MOZ_ASSERT(!arr->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE));
    873  }
    874 #endif
    875 
    876  return true;
    877 }
    878 
    879 // Like AddDataProperty but optimized for plain objects. Plain objects don't
    880 // have an addProperty hook.
    881 MOZ_ALWAYS_INLINE bool AddDataPropertyToPlainObject(
    882    JSContext* cx, Handle<PlainObject*> obj, HandleId id, HandleValue v,
    883    uint32_t* resultSlot = nullptr) {
    884  MOZ_ASSERT(!id.isInt());
    885 
    886  uint32_t slot;
    887  if (!resultSlot) {
    888    resultSlot = &slot;
    889  }
    890  if (!NativeObject::addProperty(
    891          cx, obj, id, PropertyFlags::defaultDataPropFlags, resultSlot)) {
    892    return false;
    893  }
    894 
    895  obj->initSlot(*resultSlot, v);
    896 
    897  MOZ_ASSERT(!obj->getClass()->getAddProperty());
    898  MOZ_ASSERT(!obj->getClass()->preservesWrapper());
    899  return true;
    900 }
    901 
    902 }  // namespace js
    903 
    904 #endif /* vm_NativeObject_inl_h */