tor-browser

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

ArrayBufferObject.h (41132B)


      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_ArrayBufferObject_h
      8 #define vm_ArrayBufferObject_h
      9 
     10 #include "mozilla/Maybe.h"
     11 
     12 #include <tuple>  // std::tuple
     13 
     14 #include "builtin/TypedArrayConstants.h"
     15 #include "gc/Memory.h"
     16 #include "gc/ZoneAllocator.h"
     17 #include "js/ArrayBuffer.h"
     18 #include "js/GCHashTable.h"
     19 #include "vm/JSFunction.h"
     20 #include "vm/JSObject.h"
     21 #include "vm/SharedMem.h"
     22 #include "wasm/WasmMemory.h"
     23 
     24 namespace js {
     25 
     26 class JS_PUBLIC_API GenericPrinter;
     27 class JSONPrinter;
     28 
     29 class ArrayBufferViewObject;
     30 class AutoSetNewObjectMetadata;
     31 class FixedLengthTypedArrayObject;
     32 class WasmArrayRawBuffer;
     33 
     34 namespace wasm {
     35 struct MemoryDesc;
     36 }  // namespace wasm
     37 
     38 // Create a new mapping of size `mappedSize` with an initially committed prefix
     39 // of size `initialCommittedSize`.  Both arguments denote bytes and must be
     40 // multiples of the page size, with `initialCommittedSize` <= `mappedSize`.
     41 // Returns nullptr on failure.
     42 void* MapBufferMemory(wasm::AddressType, wasm::PageSize, size_t mappedSize,
     43                      size_t initialCommittedSize);
     44 
     45 // Commit additional memory in an existing mapping.  `dataEnd` must be the
     46 // correct value for the end of the existing committed area, and `delta` must be
     47 // a byte amount to grow the mapping by, and must be a multiple of the page
     48 // size.  Returns false on failure.
     49 bool CommitBufferMemory(void* dataEnd, size_t delta);
     50 
     51 // Remove an existing mapping.  `dataStart` must be the pointer to the start of
     52 // the mapping, and `mappedSize` the size of that mapping.
     53 void UnmapBufferMemory(wasm::AddressType t, void* dataStart, size_t mappedSize,
     54                       size_t committedSize);
     55 
     56 // Return the number of bytes currently reserved for WebAssembly memory
     57 uint64_t WasmReservedBytes();
     58 
     59 // The inheritance hierarchy for the various classes relating to typed arrays
     60 // is as follows.
     61 //
     62 //
     63 // - JSObject
     64 //   - NativeObject
     65 //     - ArrayBufferObjectMaybeShared
     66 //       - ArrayBufferObject
     67 //         - FixedLengthArrayBufferObject
     68 //         - ResizableArrayBufferObject
     69 //         - ImmutableArrayBufferObject
     70 //       - SharedArrayBufferObject
     71 //         - FixedLengthSharedArrayBufferObject
     72 //         - GrowableSharedArrayBufferObject
     73 //     - ArrayBufferViewObject
     74 //       - DataViewObject
     75 //         - FixedLengthDataViewObject
     76 //         - ResizableDataViewObject
     77 //         - ImmutableDataViewObject
     78 //       - TypedArrayObject (declared in vm/TypedArrayObject.h)
     79 //         - FixedLengthTypedArrayObject
     80 //           - FixedLengthTypedArrayObjectTemplate<NativeType>, also inheriting
     81 //             from TypedArrayObjectTemplate<NativeType>
     82 //             - FixedLengthTypedArrayObjectTemplate<int8_t>
     83 //             - FixedLengthTypedArrayObjectTemplate<uint8_t>
     84 //             - ...
     85 //         - ResizableTypedArrayObject
     86 //           - ResizableTypedArrayObjectTemplate<NativeType>, also inheriting
     87 //             from TypedArrayObjectTemplate<NativeType>
     88 //             - ResizableTypedArrayObjectTemplate<int8_t>
     89 //             - ResizableTypedArrayObjectTemplate<uint8_t>
     90 //             - ...
     91 //         - ImmutableTypedArrayObject
     92 //           - ImmutableTypedArrayObjectTemplate<NativeType>, also inheriting
     93 //             from TypedArrayObjectTemplate<NativeType>
     94 //             - ImmutableTypedArrayObjectTemplate<int8_t>
     95 //             - ImmutableTypedArrayObjectTemplate<uint8_t>
     96 //             - ...
     97 //
     98 // Note that |{FixedLength,Resizable,Immutable}TypedArrayObjectTemplate| is just
     99 // an implementation detail that makes implementing its various subclasses
    100 // easier.
    101 //
    102 // FixedLengthArrayBufferObject, ResizableArrayBufferObject, and
    103 // ImmutableArrayBufferObject are also implementation specific types to
    104 // differentiate between fixed-length, resizable, and immutable ArrayBuffers.
    105 //
    106 // ArrayBufferObject and SharedArrayBufferObject are unrelated data types:
    107 // the racy memory of the latter cannot substitute for the non-racy memory of
    108 // the former; the non-racy memory of the former cannot be used with the
    109 // atomics; the former can be detached and the latter not.  Hence they have been
    110 // separated completely.
    111 //
    112 // Most APIs will only accept ArrayBufferObject.  ArrayBufferObjectMaybeShared
    113 // exists as a join point to allow APIs that can take or use either, notably
    114 // AsmJS.
    115 //
    116 // In contrast with the separation of ArrayBufferObject and
    117 // SharedArrayBufferObject, the TypedArray types can map either.
    118 //
    119 // The possible data ownership and reference relationships with ArrayBuffers
    120 // and related classes are enumerated below. These are the possible locations
    121 // for typed data:
    122 //
    123 // (1) malloc'ed or mmap'ed data owned by an ArrayBufferObject.
    124 // (2) Data allocated inline with an ArrayBufferObject.
    125 // (3) Data allocated inline with a TypedArrayObject.
    126 //
    127 // An ArrayBufferObject may point to any of these sources of data, except (3).
    128 // All array buffer views may point to any of these sources of data, except
    129 // that (3) may only be pointed to by the typed array the data is inline with.
    130 //
    131 // During a minor GC, (3) may move. During a compacting GC, (2) and (3) may
    132 // move.
    133 
    134 class ArrayBufferObjectMaybeShared;
    135 
    136 wasm::AddressType WasmArrayBufferAddressType(
    137    const ArrayBufferObjectMaybeShared* buf);
    138 wasm::PageSize WasmArrayBufferPageSize(const ArrayBufferObjectMaybeShared* buf);
    139 wasm::Pages WasmArrayBufferPages(const ArrayBufferObjectMaybeShared* buf);
    140 wasm::Pages WasmArrayBufferClampedMaxPages(
    141    const ArrayBufferObjectMaybeShared* buf);
    142 mozilla::Maybe<wasm::Pages> WasmArrayBufferSourceMaxPages(
    143    const ArrayBufferObjectMaybeShared* buf);
    144 size_t WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf);
    145 
    146 class ArrayBufferObjectMaybeShared : public NativeObject {
    147 public:
    148  inline size_t byteLength() const;
    149  inline bool isDetached() const;
    150  inline bool isResizable() const;
    151  inline bool isImmutable() const;
    152  inline SharedMem<uint8_t*> dataPointerEither();
    153 
    154  inline bool pinLength(bool pin);
    155 
    156  // WebAssembly support:
    157  // Note: the eventual goal is to remove this from ArrayBuffer and have
    158  // (Shared)ArrayBuffers alias memory owned by some wasm::Memory object.
    159 
    160  wasm::AddressType wasmAddressType() const {
    161    return WasmArrayBufferAddressType(this);
    162  }
    163  wasm::PageSize wasmPageSize() const { return WasmArrayBufferPageSize(this); }
    164  wasm::Pages wasmPages() const { return WasmArrayBufferPages(this); }
    165  wasm::Pages wasmClampedMaxPages() const {
    166    return WasmArrayBufferClampedMaxPages(this);
    167  }
    168  mozilla::Maybe<wasm::Pages> wasmSourceMaxPages() const {
    169    return WasmArrayBufferSourceMaxPages(this);
    170  }
    171  size_t wasmMappedSize() const { return WasmArrayBufferMappedSize(this); }
    172 
    173  inline bool isPreparedForAsmJS() const;
    174  inline bool isWasm() const;
    175 };
    176 
    177 class FixedLengthArrayBufferObject;
    178 class ResizableArrayBufferObject;
    179 class ImmutableArrayBufferObject;
    180 
    181 /*
    182 * ArrayBufferObject
    183 *
    184 * This class holds the underlying raw buffer that the various ArrayBufferViews
    185 * (DataViewObject and the TypedArrays) access. It can be created explicitly and
    186 * used to construct an ArrayBufferView, or can be created lazily when it is
    187 * first accessed for a TypedArrayObject that doesn't have an explicit buffer.
    188 *
    189 * ArrayBufferObject is an abstract base class and has exactly three concrete
    190 * subclasses: FixedLengthArrayBufferObject, ResizableArrayBufferObject, and
    191 * ImmutableArrayBufferObject.
    192 *
    193 * ArrayBufferObject (or really the underlying memory) /is not racy/: the
    194 * memory is private to a single worker.
    195 */
    196 class ArrayBufferObject : public ArrayBufferObjectMaybeShared {
    197  static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
    198  static bool maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args);
    199  static bool resizableGetterImpl(JSContext* cx, const CallArgs& args);
    200  static bool detachedGetterImpl(JSContext* cx, const CallArgs& args);
    201 #ifdef NIGHTLY_BUILD
    202  static bool immutableGetterImpl(JSContext* cx, const CallArgs& args);
    203 #endif
    204  static bool sliceImpl(JSContext* cx, const CallArgs& args);
    205 #ifdef NIGHTLY_BUILD
    206  static bool sliceToImmutableImpl(JSContext* cx, const CallArgs& args);
    207 #endif
    208  static bool resizeImpl(JSContext* cx, const CallArgs& args);
    209  static bool transferImpl(JSContext* cx, const CallArgs& args);
    210  static bool transferToFixedLengthImpl(JSContext* cx, const CallArgs& args);
    211 #ifdef NIGHTLY_BUILD
    212  static bool transferToImmutableImpl(JSContext* cx, const CallArgs& args);
    213 #endif
    214 
    215 public:
    216  static const uint8_t DATA_SLOT = 0;
    217  static const uint8_t BYTE_LENGTH_SLOT = 1;
    218  static const uint8_t FIRST_VIEW_SLOT = 2;
    219  static const uint8_t FLAGS_SLOT = 3;
    220 
    221  static const uint8_t RESERVED_SLOTS = 4;
    222 
    223  // Alignment for ArrayBuffer objects. Must match the largest possible
    224  // TypedArray scalar to ensure TypedArray and Atomics accesses are always
    225  // aligned.
    226  static constexpr size_t ARRAY_BUFFER_ALIGNMENT = 8;
    227 
    228  static_assert(FLAGS_SLOT == JS_ARRAYBUFFER_FLAGS_SLOT,
    229                "self-hosted code with burned-in constants must get the "
    230                "right flags slot");
    231 
    232  // The length of an ArrayBuffer or SharedArrayBuffer can be at most INT32_MAX
    233  // on 32-bit platforms. Allow a larger limit on 64-bit platforms.
    234  static constexpr size_t ByteLengthLimitForSmallBuffer = INT32_MAX;
    235 #ifdef JS_64BIT
    236  static constexpr size_t ByteLengthLimit =
    237      size_t(16) * 1024 * 1024 * 1024;  // 16 GB.
    238 #else
    239  static constexpr size_t ByteLengthLimit = ByteLengthLimitForSmallBuffer;
    240 #endif
    241 
    242 public:
    243  enum BufferKind {
    244    /** Inline data kept in the repurposed slots of this ArrayBufferObject. */
    245    INLINE_DATA = 0b000,
    246 
    247    /*
    248     * Data allocated using the SpiderMonkey allocator, created within
    249     * js::ArrayBufferContentsArena.
    250     */
    251    MALLOCED_ARRAYBUFFER_CONTENTS_ARENA = 0b001,
    252 
    253    /**
    254     * No bytes are associated with this buffer.  (This could be because the
    255     * buffer is detached, because it's an internal, newborn buffer not yet
    256     * overwritten with user-exposable semantics, or some other reason.  The
    257     * point is, don't read precise language semantics into this kind.)
    258     */
    259    NO_DATA = 0b010,
    260 
    261    /**
    262     * User-owned memory.  The associated buffer must be manually detached
    263     * before the user invalidates (deallocates, reuses the storage of, &c.)
    264     * the user-owned memory.
    265     */
    266    USER_OWNED = 0b011,
    267 
    268    WASM = 0b100,
    269    MAPPED = 0b101,
    270    EXTERNAL = 0b110,
    271 
    272    /**
    273     * Data allocated using the SpiderMonkey allocator, created within an
    274     * unknown memory arena.
    275     */
    276    MALLOCED_UNKNOWN_ARENA = 0b111,
    277 
    278    KIND_MASK = 0b111
    279  };
    280 
    281 public:
    282  enum ArrayBufferFlags {
    283    // The flags also store the BufferKind
    284    BUFFER_KIND_MASK = BufferKind::KIND_MASK,
    285 
    286    DETACHED = 0b1000,
    287 
    288    // Resizable ArrayBuffer.
    289    RESIZABLE = 0b1'0000,
    290 
    291    // This MALLOCED, MAPPED, or EXTERNAL buffer has been prepared for asm.js
    292    // and cannot henceforth be transferred/detached.  (WASM, USER_OWNED, and
    293    // INLINE_DATA buffers can't be prepared for asm.js -- although if an
    294    // INLINE_DATA buffer is used with asm.js, it's silently rewritten into a
    295    // MALLOCED buffer which *can* be prepared.)
    296    FOR_ASMJS = 0b10'0000,
    297 
    298    // The length is temporarily pinned, so it should not be detached. In the
    299    // future, this will also prevent GrowableArrayBuffer/ResizeableArrayBuffer
    300    // from modifying the length while this is set.
    301    PINNED_LENGTH = 0b100'0000,
    302 
    303    // Immutable ArrayBuffer.
    304    IMMUTABLE = 0b1000'0000,
    305  };
    306 
    307  static_assert(JS_ARRAYBUFFER_DETACHED_FLAG == DETACHED,
    308                "self-hosted code with burned-in constants must use the "
    309                "correct DETACHED bit value");
    310 
    311  static_assert(JS_ARRAYBUFFER_IMMUTABLE_FLAG == IMMUTABLE,
    312                "self-hosted code with burned-in constants must use the "
    313                "correct IMMUTABLE bit value");
    314 
    315 protected:
    316  enum class FillContents { Zero, Uninitialized };
    317 
    318  template <class ArrayBufferType, FillContents FillType>
    319  static std::tuple<ArrayBufferType*, uint8_t*>
    320  createUninitializedBufferAndData(JSContext* cx, size_t nbytes,
    321                                   AutoSetNewObjectMetadata&,
    322                                   JS::Handle<JSObject*> proto);
    323 
    324  template <class ArrayBufferType, FillContents FillType>
    325  static std::tuple<ArrayBufferType*, uint8_t*> createBufferAndData(
    326      JSContext* cx, size_t nbytes, AutoSetNewObjectMetadata& metadata,
    327      JS::Handle<JSObject*> proto = nullptr);
    328 
    329 public:
    330  class BufferContents {
    331    uint8_t* data_;
    332    BufferKind kind_;
    333    JS::BufferContentsFreeFunc free_;
    334    void* freeUserData_;
    335 
    336    friend class ArrayBufferObject;
    337    friend class ResizableArrayBufferObject;
    338    friend class ImmutableArrayBufferObject;
    339 
    340    BufferContents(uint8_t* data, BufferKind kind,
    341                   JS::BufferContentsFreeFunc freeFunc = nullptr,
    342                   void* freeUserData = nullptr)
    343        : data_(data),
    344          kind_(kind),
    345          free_(freeFunc),
    346          freeUserData_(freeUserData) {
    347      MOZ_ASSERT((kind_ & ~KIND_MASK) == 0);
    348      MOZ_ASSERT_IF(free_ || freeUserData_, kind_ == EXTERNAL);
    349 
    350      // It is the caller's responsibility to ensure that the
    351      // BufferContents does not outlive the data.
    352    }
    353 
    354 #ifdef DEBUG
    355    // Checks if the buffer contents are properly aligned.
    356    //
    357    // `malloc(0)` is implementation defined and may return a pointer which
    358    // isn't aligned to `max_align_t`, so we only require proper alignment when
    359    // `byteLength` is non-zero.
    360    //
    361    // jemalloc doesn't implement restriction, but instead uses `sizeof(void*)`
    362    // for its smallest allocation class. Larger allocations are guaranteed to
    363    // be eight byte aligned.
    364    bool isAligned(size_t byteLength) const {
    365      // `malloc(0)` has implementation defined behavior.
    366      if (byteLength == 0) {
    367        return true;
    368      }
    369 
    370      // Allow jemalloc tiny allocations to have smaller alignment requirements
    371      // than `std::malloc`.
    372      if (sizeof(void*) < ArrayBufferObject::ARRAY_BUFFER_ALIGNMENT) {
    373        if (byteLength <= sizeof(void*)) {
    374          return true;
    375        }
    376      }
    377 
    378      // `std::malloc` returns memory at least as strictly aligned as for
    379      // max_align_t and the alignment of max_align_t is a multiple of the array
    380      // buffer alignment.
    381      static_assert(alignof(std::max_align_t) %
    382                        ArrayBufferObject::ARRAY_BUFFER_ALIGNMENT ==
    383                    0);
    384 
    385      // Otherwise the memory must be correctly alignment.
    386      auto ptr = reinterpret_cast<uintptr_t>(data());
    387      return ptr % ArrayBufferObject::ARRAY_BUFFER_ALIGNMENT == 0;
    388    }
    389 #endif
    390 
    391   public:
    392    static BufferContents createInlineData(void* data) {
    393      return BufferContents(static_cast<uint8_t*>(data), INLINE_DATA);
    394    }
    395 
    396    static BufferContents createMallocedArrayBufferContentsArena(void* data) {
    397      return BufferContents(static_cast<uint8_t*>(data),
    398                            MALLOCED_ARRAYBUFFER_CONTENTS_ARENA);
    399    }
    400 
    401    static BufferContents createMallocedUnknownArena(void* data) {
    402      return BufferContents(static_cast<uint8_t*>(data),
    403                            MALLOCED_UNKNOWN_ARENA);
    404    }
    405 
    406    static BufferContents createNoData() {
    407      return BufferContents(nullptr, NO_DATA);
    408    }
    409 
    410    static BufferContents createUserOwned(void* data) {
    411      return BufferContents(static_cast<uint8_t*>(data), USER_OWNED);
    412    }
    413 
    414    static BufferContents createWasm(void* data) {
    415      return BufferContents(static_cast<uint8_t*>(data), WASM);
    416    }
    417 
    418    static BufferContents createMapped(void* data) {
    419      return BufferContents(static_cast<uint8_t*>(data), MAPPED);
    420    }
    421 
    422    static BufferContents createExternal(void* data,
    423                                         JS::BufferContentsFreeFunc freeFunc,
    424                                         void* freeUserData = nullptr) {
    425      MOZ_ASSERT(freeFunc);
    426      return BufferContents(static_cast<uint8_t*>(data), EXTERNAL, freeFunc,
    427                            freeUserData);
    428    }
    429 
    430    static BufferContents createFailed() {
    431      // There's no harm in tagging this as MALLOCED_ARRAYBUFFER_CONTENTS_ARENA,
    432      // even tho obviously it isn't. And adding an extra tag purely for this
    433      // case is a complication that presently appears avoidable.
    434      return BufferContents(nullptr, MALLOCED_ARRAYBUFFER_CONTENTS_ARENA);
    435    }
    436 
    437    uint8_t* data() const { return data_; }
    438    BufferKind kind() const { return kind_; }
    439    JS::BufferContentsFreeFunc freeFunc() const { return free_; }
    440    void* freeUserData() const { return freeUserData_; }
    441 
    442    explicit operator bool() const { return data_ != nullptr; }
    443    WasmArrayRawBuffer* wasmBuffer() const;
    444  };
    445 
    446  static const JSClass protoClass_;
    447 
    448  static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
    449 
    450  static bool maxByteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
    451 
    452  static bool resizableGetter(JSContext* cx, unsigned argc, Value* vp);
    453 
    454  static bool detachedGetter(JSContext* cx, unsigned argc, Value* vp);
    455 
    456 #ifdef NIGHTLY_BUILD
    457  static bool immutableGetter(JSContext* cx, unsigned argc, Value* vp);
    458 #endif
    459 
    460  static bool fun_isView(JSContext* cx, unsigned argc, Value* vp);
    461 
    462  static bool slice(JSContext* cx, unsigned argc, Value* vp);
    463 
    464 #ifdef NIGHTLY_BUILD
    465  static bool sliceToImmutable(JSContext* cx, unsigned argc, Value* vp);
    466 #endif
    467 
    468  static bool resize(JSContext* cx, unsigned argc, Value* vp);
    469 
    470  static bool transfer(JSContext* cx, unsigned argc, Value* vp);
    471 
    472  static bool transferToFixedLength(JSContext* cx, unsigned argc, Value* vp);
    473 
    474 #ifdef NIGHTLY_BUILD
    475  static bool transferToImmutable(JSContext* cx, unsigned argc, Value* vp);
    476 #endif
    477 
    478  static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
    479 
    480  static ArrayBufferObject* createForContents(JSContext* cx, size_t nbytes,
    481                                              BufferContents contents);
    482 
    483  static ArrayBufferObject* createFromTypedArrayMallocedElements(
    484      JSContext* cx, Handle<FixedLengthTypedArrayObject*> tarray);
    485 
    486 protected:
    487  template <class ArrayBufferType>
    488  static ArrayBufferType* copy(JSContext* cx, size_t newByteLength,
    489                               JS::Handle<ArrayBufferObject*> source);
    490 
    491  template <class ArrayBufferType>
    492  static ArrayBufferType* copyAndDetach(JSContext* cx, size_t newByteLength,
    493                                        JS::Handle<ArrayBufferObject*> source);
    494 
    495 private:
    496  template <class ArrayBufferType>
    497  static ArrayBufferType* copyAndDetachSteal(
    498      JSContext* cx, JS::Handle<ArrayBufferObject*> source);
    499 
    500  template <class ArrayBufferType>
    501  static ArrayBufferType* copyAndDetachRealloc(
    502      JSContext* cx, size_t newByteLength,
    503      JS::Handle<ArrayBufferObject*> source);
    504 
    505 public:
    506  static FixedLengthArrayBufferObject* createZeroed(
    507      JSContext* cx, size_t nbytes, HandleObject proto = nullptr);
    508 
    509  // Create an ArrayBufferObject that is safely finalizable and can later be
    510  // initialize()d to become a real, content-visible ArrayBufferObject.
    511  static FixedLengthArrayBufferObject* createEmpty(JSContext* cx);
    512 
    513  // Create an ArrayBufferObject using the provided buffer and size.  Assumes
    514  // ownership of |buffer| even in case of failure, i.e. on failure |buffer|
    515  // is deallocated.
    516  static ArrayBufferObject* createFromNewRawBuffer(JSContext* cx,
    517                                                   WasmArrayRawBuffer* buffer,
    518                                                   size_t initialSize);
    519 
    520  // Create an ArrayBufferObject object (resizable or fixed-length) based
    521  // on the existing donor object. The |buffer| will be removed from the
    522  // latter, and its ownership will be assumed by the new object.
    523  template <typename ArrayBufferType>
    524  static ArrayBufferType* createFromWasmObject(
    525      JSContext* cx, Handle<ArrayBufferObject*> donor);
    526 
    527  static void copyData(ArrayBufferObject* toBuffer, size_t toIndex,
    528                       ArrayBufferObject* fromBuffer, size_t fromIndex,
    529                       size_t count);
    530 
    531  template <class ArrayBufferType>
    532  static size_t objectMoved(JSObject* obj, JSObject* old);
    533 
    534  static uint8_t* stealMallocedContents(JSContext* cx,
    535                                        Handle<ArrayBufferObject*> buffer);
    536 
    537  static BufferContents extractStructuredCloneContents(
    538      JSContext* cx, Handle<ArrayBufferObject*> buffer);
    539 
    540  static void addSizeOfExcludingThis(JSObject* obj,
    541                                     mozilla::MallocSizeOf mallocSizeOf,
    542                                     JS::ClassInfo* info,
    543                                     JS::RuntimeSizes* runtimeSizes);
    544 
    545  // ArrayBufferObjects (strongly) store the first view added to them, while
    546  // later views are (weakly) stored in the compartment's InnerViewTable
    547  // below. Buffers usually only have one view, so this slot optimizes for
    548  // the common case. Avoiding entries in the InnerViewTable saves memory and
    549  // non-incrementalized sweep time.
    550  JSObject* firstView();
    551 
    552  bool addView(JSContext* cx, ArrayBufferViewObject* view);
    553 
    554  // Pin or unpin the length. Returns whether pinned status was changed.
    555  bool pinLength(bool pin) {
    556    if (bool(flags() & PINNED_LENGTH) == pin) {
    557      return false;
    558    }
    559    setFlags(flags() ^ PINNED_LENGTH);
    560    return true;
    561  }
    562 
    563  static bool ensureNonInline(JSContext* cx, Handle<ArrayBufferObject*> buffer);
    564 
    565  // Detach this buffer from its original memory.  (This necessarily makes
    566  // views of this buffer unusable for modifying that original memory.)
    567  static void detach(JSContext* cx, Handle<ArrayBufferObject*> buffer);
    568 
    569  static constexpr size_t offsetOfByteLengthSlot() {
    570    return getFixedSlotOffset(BYTE_LENGTH_SLOT);
    571  }
    572  static constexpr size_t offsetOfFlagsSlot() {
    573    return getFixedSlotOffset(FLAGS_SLOT);
    574  }
    575 
    576 protected:
    577  void setFirstView(ArrayBufferViewObject* view);
    578 
    579 private:
    580  struct FreeInfo {
    581    JS::BufferContentsFreeFunc freeFunc;
    582    void* freeUserData;
    583  };
    584  FreeInfo* freeInfo() const;
    585 
    586 public:
    587  uint8_t* dataPointer() const;
    588  SharedMem<uint8_t*> dataPointerShared() const;
    589  size_t byteLength() const;
    590 
    591  BufferContents contents() const {
    592    if (isExternal()) {
    593      return BufferContents(dataPointer(), EXTERNAL, freeInfo()->freeFunc,
    594                            freeInfo()->freeUserData);
    595    }
    596    return BufferContents(dataPointer(), bufferKind());
    597  }
    598 
    599  void releaseData(JS::GCContext* gcx);
    600 
    601  BufferKind bufferKind() const {
    602    return BufferKind(flags() & BUFFER_KIND_MASK);
    603  }
    604 
    605  bool isInlineData() const { return bufferKind() == INLINE_DATA; }
    606  bool isMalloced() const {
    607    return bufferKind() == MALLOCED_ARRAYBUFFER_CONTENTS_ARENA ||
    608           bufferKind() == MALLOCED_UNKNOWN_ARENA;
    609  }
    610  bool isNoData() const { return bufferKind() == NO_DATA; }
    611  bool hasUserOwnedData() const { return bufferKind() == USER_OWNED; }
    612 
    613  bool isWasm() const { return bufferKind() == WASM; }
    614  bool isMapped() const { return bufferKind() == MAPPED; }
    615  bool isExternal() const { return bufferKind() == EXTERNAL; }
    616 
    617  bool isDetached() const { return flags() & DETACHED; }
    618  bool isResizable() const { return flags() & RESIZABLE; }
    619  bool isLengthPinned() const { return flags() & PINNED_LENGTH; }
    620  bool isPreparedForAsmJS() const { return flags() & FOR_ASMJS; }
    621  bool isImmutable() const { return flags() & IMMUTABLE; }
    622 
    623  // Only WASM and asm.js buffers have a non-undefined [[ArrayBufferDetachKey]].
    624  //
    625  // https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-instances
    626  bool hasDefinedDetachKey() const { return isWasm() || isPreparedForAsmJS(); }
    627 
    628  // WebAssembly support:
    629 
    630  /**
    631   * Prepare this ArrayBuffer for use with asm.js.  Returns true on success,
    632   * false on failure.  This function reports no errors.
    633   */
    634  [[nodiscard]] bool prepareForAsmJS();
    635 
    636  size_t wasmMappedSize() const;
    637 
    638  wasm::AddressType wasmAddressType() const;
    639  wasm::PageSize wasmPageSize() const;
    640  wasm::Pages wasmPages() const;
    641  wasm::Pages wasmClampedMaxPages() const;
    642  mozilla::Maybe<wasm::Pages> wasmSourceMaxPages() const;
    643 
    644  [[nodiscard]] static ArrayBufferObject* wasmGrowToPagesInPlace(
    645      wasm::AddressType t, wasm::Pages newPages,
    646      Handle<ArrayBufferObject*> oldBuf, JSContext* cx);
    647  [[nodiscard]] static ArrayBufferObject* wasmMovingGrowToPages(
    648      wasm::AddressType t, wasm::Pages newPages,
    649      Handle<ArrayBufferObject*> oldBuf, JSContext* cx);
    650  static void wasmDiscard(Handle<ArrayBufferObject*> buf, uint64_t byteOffset,
    651                          uint64_t byteLength);
    652 
    653  static void finalize(JS::GCContext* gcx, JSObject* obj);
    654 
    655  static BufferContents createMappedContents(int fd, size_t offset,
    656                                             size_t length);
    657 
    658 protected:
    659  void setDataPointer(BufferContents contents);
    660  void setByteLength(size_t length);
    661 
    662  /**
    663   * Return the byte length for fixed-length buffers or the maximum byte length
    664   * for resizable buffers.
    665   */
    666  inline size_t maxByteLength() const;
    667 
    668  size_t wasmClampedMaxByteLength() const {
    669    MOZ_ASSERT(isWasm());
    670    return wasmClampedMaxPages().byteLength();
    671  }
    672 
    673  size_t associatedBytes() const;
    674 
    675  uint32_t flags() const;
    676  void setFlags(uint32_t flags);
    677 
    678  void setIsDetached() {
    679    MOZ_ASSERT(!isLengthPinned());
    680    MOZ_ASSERT(!isImmutable());
    681    setFlags(flags() | DETACHED);
    682  }
    683  void setIsPreparedForAsmJS() {
    684    MOZ_ASSERT(!isWasm());
    685    MOZ_ASSERT(!hasUserOwnedData());
    686    MOZ_ASSERT(!isInlineData());
    687    MOZ_ASSERT(isMalloced() || isMapped() || isExternal());
    688    setFlags(flags() | FOR_ASMJS);
    689  }
    690 
    691  void initialize(size_t byteLength, BufferContents contents) {
    692    MOZ_ASSERT(contents.isAligned(byteLength));
    693    setByteLength(byteLength);
    694    setFlags(0);
    695    setFirstView(nullptr);
    696    setDataPointer(contents);
    697  }
    698 
    699 public:
    700 #if defined(DEBUG) || defined(JS_JITSPEW)
    701  void dumpOwnFields(js::JSONPrinter& json) const;
    702  void dumpOwnStringContent(js::GenericPrinter& out) const;
    703 #endif
    704 };
    705 
    706 /**
    707 * FixedLengthArrayBufferObject
    708 *
    709 * ArrayBuffer object with a fixed length. Its length is unmodifiable, except
    710 * when zeroing it for detached buffers. Supports all possible memory stores
    711 * for ArrayBuffer objects, including inline data, malloc'ed memory, mapped
    712 * memory, and user-owner memory.
    713 *
    714 * Fixed-length ArrayBuffers can be used for asm.js and WebAssembly.
    715 */
    716 class FixedLengthArrayBufferObject : public ArrayBufferObject {
    717  friend class ArrayBufferObject;
    718 
    719  uint8_t* inlineDataPointer() const;
    720 
    721  bool hasInlineData() const { return dataPointer() == inlineDataPointer(); }
    722 
    723 public:
    724  // Fixed-length ArrayBuffer objects don't have any additional reserved slots.
    725  static const uint8_t RESERVED_SLOTS = ArrayBufferObject::RESERVED_SLOTS;
    726 
    727  /** The largest number of bytes that can be stored inline. */
    728  static constexpr size_t MaxInlineBytes =
    729      (NativeObject::MAX_FIXED_SLOTS - RESERVED_SLOTS) * sizeof(JS::Value);
    730 
    731  static const JSClass class_;
    732 
    733  static FixedLengthArrayBufferObject* copy(
    734      JSContext* cx, size_t newByteLength,
    735      JS::Handle<ArrayBufferObject*> source) {
    736    return ArrayBufferObject::copy<FixedLengthArrayBufferObject>(
    737        cx, newByteLength, source);
    738  }
    739 
    740  static FixedLengthArrayBufferObject* copyAndDetach(
    741      JSContext* cx, size_t newByteLength,
    742      JS::Handle<ArrayBufferObject*> source) {
    743    return ArrayBufferObject::copyAndDetach<FixedLengthArrayBufferObject>(
    744        cx, newByteLength, source);
    745  }
    746 };
    747 
    748 /**
    749 * ResizableArrayBufferObject
    750 *
    751 * ArrayBuffer object which can both grow and shrink. The maximum byte length it
    752 * can grow to is set when creating the object. The data of resizable
    753 * ArrayBuffer object is either stored inline or malloc'ed memory.
    754 *
    755 * When a resizable ArrayBuffer object is detached, its maximum byte length
    756 * slot is set to zero in addition to the byte length slot.
    757 *
    758 * Resizable ArrayBuffers can neither be used for asm.js nor WebAssembly.
    759 */
    760 class ResizableArrayBufferObject : public ArrayBufferObject {
    761  friend class ArrayBufferObject;
    762 
    763  template <FillContents FillType>
    764  static std::tuple<ResizableArrayBufferObject*, uint8_t*> createBufferAndData(
    765      JSContext* cx, size_t byteLength, size_t maxByteLength,
    766      AutoSetNewObjectMetadata& metadata, Handle<JSObject*> proto);
    767 
    768  static ResizableArrayBufferObject* createEmpty(JSContext* cx);
    769 
    770 public:
    771  static ResizableArrayBufferObject* createZeroed(
    772      JSContext* cx, size_t byteLength, size_t maxByteLength,
    773      Handle<JSObject*> proto = nullptr);
    774 
    775 private:
    776  uint8_t* inlineDataPointer() const;
    777 
    778  bool hasInlineData() const { return dataPointer() == inlineDataPointer(); }
    779 
    780  void setMaxByteLength(size_t length) {
    781    MOZ_ASSERT(length <= ArrayBufferObject::ByteLengthLimit);
    782    setFixedSlot(MAX_BYTE_LENGTH_SLOT, PrivateValue(length));
    783  }
    784 
    785  void initialize(size_t byteLength, size_t maxByteLength,
    786                  BufferContents contents) {
    787    MOZ_ASSERT(contents.isAligned(byteLength));
    788    setByteLength(byteLength);
    789    setMaxByteLength(maxByteLength);
    790    setFlags(RESIZABLE);
    791    setFirstView(nullptr);
    792    setDataPointer(contents);
    793  }
    794 
    795  // Resize this buffer.
    796  void resize(size_t newByteLength);
    797 
    798  static ResizableArrayBufferObject* copy(
    799      JSContext* cx, size_t newByteLength,
    800      JS::Handle<ResizableArrayBufferObject*> source);
    801 
    802 public:
    803  static const uint8_t MAX_BYTE_LENGTH_SLOT = ArrayBufferObject::RESERVED_SLOTS;
    804 
    805  static const uint8_t RESERVED_SLOTS = ArrayBufferObject::RESERVED_SLOTS + 1;
    806 
    807  /** The largest number of bytes that can be stored inline. */
    808  static constexpr size_t MaxInlineBytes =
    809      (NativeObject::MAX_FIXED_SLOTS - RESERVED_SLOTS) * sizeof(JS::Value);
    810 
    811  static const JSClass class_;
    812 
    813  size_t maxByteLength() const {
    814    return size_t(getFixedSlot(MAX_BYTE_LENGTH_SLOT).toPrivate());
    815  }
    816 
    817  static ResizableArrayBufferObject* copyAndDetach(
    818      JSContext* cx, size_t newByteLength,
    819      JS::Handle<ResizableArrayBufferObject*> source);
    820 
    821 private:
    822  static ResizableArrayBufferObject* copyAndDetachSteal(
    823      JSContext* cx, size_t newByteLength,
    824      JS::Handle<ResizableArrayBufferObject*> source);
    825 };
    826 
    827 /**
    828 * ImmutableArrayBufferObject
    829 *
    830 * ArrayBuffer object with immutable length and contents. Supports all possible
    831 * memory stores for ArrayBuffer objects, including inline data, malloc'ed
    832 * memory, mapped memory, and user-owner memory.
    833 *
    834 * Immutable ArrayBuffers can neither be used for asm.js nor WebAssembly.
    835 */
    836 class ImmutableArrayBufferObject : public ArrayBufferObject {
    837  friend class ArrayBufferObject;
    838 
    839  static ImmutableArrayBufferObject* createEmpty(JSContext* cx);
    840 
    841 public:
    842  static ImmutableArrayBufferObject* createZeroed(
    843      JSContext* cx, size_t byteLength, Handle<JSObject*> proto = nullptr);
    844 
    845 private:
    846  uint8_t* inlineDataPointer() const;
    847 
    848  bool hasInlineData() const { return dataPointer() == inlineDataPointer(); }
    849 
    850  void initialize(size_t byteLength, BufferContents contents) {
    851    MOZ_ASSERT(contents.isAligned(byteLength));
    852    setByteLength(byteLength);
    853    setFlags(IMMUTABLE);
    854    setFirstView(nullptr);
    855    setDataPointer(contents);
    856  }
    857 
    858 public:
    859  // Immutable ArrayBuffer objects don't have any additional reserved slots.
    860  static const uint8_t RESERVED_SLOTS = ArrayBufferObject::RESERVED_SLOTS;
    861 
    862  /** The largest number of bytes that can be stored inline. */
    863  static constexpr size_t MaxInlineBytes =
    864      (NativeObject::MAX_FIXED_SLOTS - RESERVED_SLOTS) * sizeof(JS::Value);
    865 
    866  static const JSClass class_;
    867 
    868  static ImmutableArrayBufferObject* copy(
    869      JSContext* cx, size_t newByteLength,
    870      JS::Handle<ArrayBufferObject*> source) {
    871    return ArrayBufferObject::copy<ImmutableArrayBufferObject>(
    872        cx, newByteLength, source);
    873  }
    874 
    875  static ImmutableArrayBufferObject* copyAndDetach(
    876      JSContext* cx, size_t newByteLength,
    877      JS::Handle<ArrayBufferObject*> source) {
    878    return ArrayBufferObject::copyAndDetach<ImmutableArrayBufferObject>(
    879        cx, newByteLength, source);
    880  }
    881 
    882  static ImmutableArrayBufferObject* slice(
    883      JSContext* cx, size_t newByteLength,
    884      JS::Handle<ArrayBufferObject*> source, size_t sourceByteOffset);
    885 };
    886 
    887 size_t ArrayBufferObject::maxByteLength() const {
    888  if (isResizable()) {
    889    return as<ResizableArrayBufferObject>().maxByteLength();
    890  }
    891  return byteLength();
    892 }
    893 
    894 // Create a buffer for a wasm memory, whose type is determined by
    895 // memory.addressType().
    896 ArrayBufferObjectMaybeShared* CreateWasmBuffer(JSContext* cx,
    897                                               const wasm::MemoryDesc& memory);
    898 
    899 // Per-compartment table that manages the relationship between array buffers
    900 // and the views that use their storage.
    901 class InnerViewTable {
    902  // Store views in a vector such that all the tenured views come before any
    903  // nursery views. Maintain the index of the first nursery view so there is an
    904  // efficient way to access only the nursery views.
    905  using ViewVector =
    906      GCVector<UnsafeBarePtr<ArrayBufferViewObject*>, 1, ZoneAllocPolicy>;
    907  struct Views {
    908    ViewVector views;  // List of views with tenured views at the front.
    909    size_t firstNurseryView = 0;
    910 
    911    explicit Views(JS::Zone* zone) : views(zone) {}
    912    bool empty();
    913    bool hasNurseryViews();
    914    bool addView(ArrayBufferViewObject* view);
    915 
    916    bool traceWeak(JSTracer* trc, size_t startIndex = 0);
    917    bool sweepAfterMinorGC(JSTracer* trc);
    918 
    919    void check();
    920  };
    921 
    922  // For all objects sharing their storage with some other view, this maps
    923  // the object to the list of such views. All entries in this map are weak.
    924  //
    925  // This key is a raw pointer and not a WeakHeapPtr because the post-barrier
    926  // would hold nursery-allocated entries live unconditionally. It is a very
    927  // common pattern in low-level and performance-oriented JavaScript to create
    928  // hundreds or thousands of very short lived temporary views on a larger
    929  // buffer; having to tenure all of these would be a catastrophic performance
    930  // regression. Thus, it is vital that nursery pointers in this map not be held
    931  // live. Special support is required in the minor GC, implemented in
    932  // sweepAfterMinorGC.
    933  using ArrayBufferViewMap =
    934      GCHashMap<UnsafeBarePtr<ArrayBufferObject*>, Views,
    935                StableCellHasher<JSObject*>, ZoneAllocPolicy>;
    936  ArrayBufferViewMap map;
    937 
    938  // List of keys from map where either the source or at least one target is in
    939  // the nursery. The raw pointer to a JSObject is allowed here because this
    940  // vector is cleared after every minor collection. Users in sweepAfterMinorGC
    941  // must be careful to use MaybeForwarded before touching these pointers.
    942  using NurseryKeysVector =
    943      GCVector<UnsafeBarePtr<ArrayBufferObject*>, 0, SystemAllocPolicy>;
    944  NurseryKeysVector nurseryKeys;
    945 
    946  // Whether nurseryKeys is a complete list.
    947  bool nurseryKeysValid = true;
    948 
    949  bool sweepMapEntryAfterMinorGC(UnsafeBarePtr<JSObject*>& buffer,
    950                                 ViewVector& views);
    951 
    952 public:
    953  explicit InnerViewTable(Zone* zone) : map(zone) {}
    954 
    955  // Remove references to dead objects in the table and update table entries
    956  // to reflect moved objects.
    957  bool traceWeak(JSTracer* trc);
    958  void sweepAfterMinorGC(JSTracer* trc);
    959 
    960  bool empty() const { return map.empty(); }
    961 
    962  bool needsSweepAfterMinorGC() const {
    963    return !nurseryKeys.empty() || !nurseryKeysValid;
    964  }
    965 
    966  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
    967 
    968 private:
    969  friend class ArrayBufferObject;
    970  friend class ResizableArrayBufferObject;
    971  bool addView(JSContext* cx, ArrayBufferObject* buffer,
    972               ArrayBufferViewObject* view);
    973  ViewVector* maybeViewsUnbarriered(ArrayBufferObject* buffer);
    974  void removeViews(ArrayBufferObject* buffer);
    975 
    976  bool sweepViewsAfterMinorGC(JSTracer* trc, ArrayBufferObject* buffer,
    977                              Views& views);
    978 };
    979 
    980 template <typename Wrapper>
    981 class MutableWrappedPtrOperations<InnerViewTable, Wrapper>
    982    : public WrappedPtrOperations<InnerViewTable, Wrapper> {
    983  InnerViewTable& table() { return static_cast<Wrapper*>(this)->get(); }
    984 
    985 public:
    986  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
    987    return table().sizeOfExcludingThis(mallocSizeOf);
    988  }
    989 };
    990 
    991 class WasmArrayRawBuffer {
    992  wasm::AddressType addressType_;
    993  wasm::PageSize pageSize_;
    994  wasm::Pages clampedMaxPages_;
    995  mozilla::Maybe<wasm::Pages> sourceMaxPages_;
    996  size_t mappedSize_;  // See comment on mappedSize().
    997  size_t length_;
    998 
    999 protected:
   1000  WasmArrayRawBuffer(wasm::AddressType addressType, wasm::PageSize pageSize,
   1001                     uint8_t* buffer, wasm::Pages clampedMaxPages,
   1002                     const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
   1003                     size_t mappedSize, size_t length)
   1004      : addressType_(addressType),
   1005        pageSize_(pageSize),
   1006        clampedMaxPages_(clampedMaxPages),
   1007        sourceMaxPages_(sourceMaxPages),
   1008        mappedSize_(mappedSize),
   1009        length_(length) {
   1010    // Assert that this WasmArrayRawBuffer was allocated in the correct place
   1011    // relative to its data.
   1012    MOZ_ASSERT(buffer == dataPointer());
   1013    MOZ_ASSERT(pageSize == clampedMaxPages.pageSize());
   1014    MOZ_ASSERT_IF(sourceMaxPages.isSome(),
   1015                  (pageSize == sourceMaxPages->pageSize()));
   1016  }
   1017 
   1018 public:
   1019  static WasmArrayRawBuffer* AllocateWasm(
   1020      wasm::AddressType addressType, wasm::PageSize pageSize,
   1021      wasm::Pages initialPages, wasm::Pages clampedMaxPages,
   1022      const mozilla::Maybe<wasm::Pages>& sourceMaxPages,
   1023      const mozilla::Maybe<size_t>& mappedSize);
   1024  static void Release(void* mem);
   1025 
   1026  uint8_t* dataPointer() {
   1027    uint8_t* ptr = reinterpret_cast<uint8_t*>(this);
   1028    return ptr + sizeof(WasmArrayRawBuffer);
   1029  }
   1030 
   1031  static const WasmArrayRawBuffer* fromDataPtr(const uint8_t* dataPtr) {
   1032    return reinterpret_cast<const WasmArrayRawBuffer*>(
   1033        dataPtr - sizeof(WasmArrayRawBuffer));
   1034  }
   1035 
   1036  static WasmArrayRawBuffer* fromDataPtr(uint8_t* dataPtr) {
   1037    return reinterpret_cast<WasmArrayRawBuffer*>(dataPtr -
   1038                                                 sizeof(WasmArrayRawBuffer));
   1039  }
   1040 
   1041  wasm::AddressType addressType() const { return addressType_; }
   1042  wasm::PageSize pageSize() const { return pageSize_; }
   1043 
   1044  uint8_t* basePointer() { return dataPointer() - gc::SystemPageSize(); }
   1045 
   1046  /*
   1047   * The actual mmapped size. Access in the range [0, mappedSize) will either
   1048   * succeed, or be handled by the wasm signal handlers. The mapped size will be
   1049   * aligned to the system allocation granularity such that we can
   1050   * optimistically map other regions following it, in order to reduce copies
   1051   * when growing memory.
   1052   *
   1053   * Note that this does NOT include the header page in which this buffer itself
   1054   * is allocated.
   1055   */
   1056  size_t mappedSize() const { return mappedSize_; }
   1057 
   1058  /*
   1059   * The wasm-visible current length of the buffer in bytes. Accesses in the
   1060   * range [0, byteLength) will succeed. May only increase.
   1061   *
   1062   * For more info see "WASM Linear Memory structure" in ArrayBufferObject.cpp.
   1063   */
   1064  size_t byteLength() const { return length_; }
   1065 
   1066  wasm::Pages pages() const {
   1067    return wasm::Pages::fromByteLengthExact(length_, pageSize());
   1068  }
   1069 
   1070  /*
   1071   * The maximum size on how far the byteLength can grow in pages. This value
   1072   * respects implementation limits and is always representable as a byte
   1073   * length. Every memory has a clampedMaxSize, even if no maximum was specified
   1074   * in source. When a memory has no sourceMaxSize, the clampedMaxSize will be
   1075   * the maximum amount of memory that can be grown to while still respecting
   1076   * implementation limits.
   1077   *
   1078   * For more info see "WASM Linear Memory structure" in ArrayBufferObject.cpp.
   1079   */
   1080  wasm::Pages clampedMaxPages() const { return clampedMaxPages_; }
   1081 
   1082  /*
   1083   * The optional declared limit on how far byteLength can grow in pages. This
   1084   * is the unmodified maximum size from the source module or JS-API invocation.
   1085   * This may not be representable in byte lengths, nor feasible for a module to
   1086   * actually grow to due to implementation limits. It is used for correct
   1087   * linking checks and js-types reflection.
   1088   *
   1089   * For more info see "WASM Linear Memory structure" in ArrayBufferObject.cpp.
   1090   */
   1091  mozilla::Maybe<wasm::Pages> sourceMaxPages() const { return sourceMaxPages_; }
   1092 
   1093  [[nodiscard]] bool growToPagesInPlace(wasm::Pages newPages);
   1094 
   1095  // Discard a region of memory, zeroing the pages and releasing physical memory
   1096  // back to the operating system. byteOffset and byteLen must be wasm page
   1097  // aligned and in bounds. A discard of zero bytes will have no effect.
   1098  void discard(size_t byteOffset, size_t byteLen);
   1099 };
   1100 
   1101 }  // namespace js
   1102 
   1103 template <>
   1104 inline bool JSObject::is<js::ArrayBufferObject>() const {
   1105  return is<js::FixedLengthArrayBufferObject>() ||
   1106         is<js::ResizableArrayBufferObject>() ||
   1107         is<js::ImmutableArrayBufferObject>();
   1108 }
   1109 
   1110 template <>
   1111 bool JSObject::is<js::ArrayBufferObjectMaybeShared>() const;
   1112 
   1113 #endif  // vm_ArrayBufferObject_h