tor-browser

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

WasmGcObject.h (23188B)


      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 wasm_WasmGcObject_h
      8 #define wasm_WasmGcObject_h
      9 
     10 #include "mozilla/Attributes.h"
     11 #include "mozilla/CheckedInt.h"
     12 #include "mozilla/Maybe.h"
     13 
     14 #include "gc/GCProbes.h"
     15 #include "gc/Pretenuring.h"
     16 #include "gc/ZoneAllocator.h"  // AddCellMemory
     17 #include "vm/JSContext.h"
     18 #include "vm/JSObject.h"
     19 #include "vm/Probes.h"
     20 #include "wasm/WasmInstanceData.h"
     21 #include "wasm/WasmMemory.h"
     22 #include "wasm/WasmTypeDef.h"
     23 #include "wasm/WasmValType.h"
     24 
     25 namespace js {
     26 
     27 //=========================================================================
     28 // WasmGcObject
     29 
     30 class WasmGcObject : public JSObject {
     31 protected:
     32  const wasm::SuperTypeVector* superTypeVector_;
     33 
     34  static const ObjectOps objectOps_;
     35 
     36  [[nodiscard]] static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
     37                                               HandleId id,
     38                                               MutableHandleObject objp,
     39                                               PropertyResult* propp);
     40 
     41  [[nodiscard]] static bool obj_defineProperty(JSContext* cx, HandleObject obj,
     42                                               HandleId id,
     43                                               Handle<PropertyDescriptor> desc,
     44                                               ObjectOpResult& result);
     45 
     46  [[nodiscard]] static bool obj_hasProperty(JSContext* cx, HandleObject obj,
     47                                            HandleId id, bool* foundp);
     48 
     49  [[nodiscard]] static bool obj_getProperty(JSContext* cx, HandleObject obj,
     50                                            HandleValue receiver, HandleId id,
     51                                            MutableHandleValue vp);
     52 
     53  [[nodiscard]] static bool obj_setProperty(JSContext* cx, HandleObject obj,
     54                                            HandleId id, HandleValue v,
     55                                            HandleValue receiver,
     56                                            ObjectOpResult& result);
     57 
     58  [[nodiscard]] static bool obj_getOwnPropertyDescriptor(
     59      JSContext* cx, HandleObject obj, HandleId id,
     60      MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc);
     61 
     62  [[nodiscard]] static bool obj_deleteProperty(JSContext* cx, HandleObject obj,
     63                                               HandleId id,
     64                                               ObjectOpResult& result);
     65 
     66  // PropOffset is a uint32_t that is used to carry information about the
     67  // location of an value from WasmGcObject::lookupProperty to
     68  // WasmGcObject::loadValue.  It is distinct from a normal uint32_t to
     69  // emphasise the fact that it cannot be interpreted as an offset in any
     70  // single contiguous area of memory:
     71  //
     72  // * If the object in question is a WasmStructObject, it is the index of
     73  //   the relevant field.
     74  //
     75  // * If the object in question is a WasmArrayObject, then
     76  //   - u32 == UINT32_MAX (0xFFFF'FFFF) means the "length" property
     77  //     is requested
     78  //   - u32 < UINT32_MAX means the array element starting at that byte
     79  //     offset in WasmArrayObject::data_.  It is not an array index value.
     80  //   See WasmGcObject::lookupProperty for details.
     81  class PropOffset {
     82    uint32_t u32_;
     83 
     84   public:
     85    PropOffset() : u32_(0) {}
     86    uint32_t get() const { return u32_; }
     87    void set(uint32_t u32) { u32_ = u32; }
     88  };
     89 
     90  [[nodiscard]] static bool lookUpProperty(JSContext* cx,
     91                                           Handle<WasmGcObject*> obj, jsid id,
     92                                           PropOffset* offset,
     93                                           wasm::StorageType* type);
     94 
     95 public:
     96  [[nodiscard]] static bool loadValue(JSContext* cx, Handle<WasmGcObject*> obj,
     97                                      jsid id, MutableHandleValue vp);
     98 
     99  const wasm::SuperTypeVector& superTypeVector() const {
    100    return *superTypeVector_;
    101  }
    102 
    103  static constexpr size_t offsetOfSuperTypeVector() {
    104    return offsetof(WasmGcObject, superTypeVector_);
    105  }
    106 
    107  // These are both expensive in that they involve a double indirection.
    108  // Avoid them if possible.
    109  const wasm::TypeDef& typeDef() const { return *superTypeVector().typeDef(); }
    110  wasm::TypeDefKind kind() const { return superTypeVector().typeDef()->kind(); }
    111 
    112  [[nodiscard]] bool isRuntimeSubtypeOf(
    113      const wasm::TypeDef* parentTypeDef) const;
    114 
    115  [[nodiscard]] static bool obj_newEnumerate(JSContext* cx, HandleObject obj,
    116                                             MutableHandleIdVector properties,
    117                                             bool enumerableOnly);
    118 };
    119 
    120 //=========================================================================
    121 // WasmArrayObject
    122 
    123 // Class for a wasm array. It contains a pointer to the array contents and
    124 // possibly inline data. Array data is allocated with a DataHeader that tracks
    125 // whether the array data is stored inline in a trailing array, or out of line
    126 // in heap memory. The array's data pointer will always point at the start of
    127 // the array data, and the data header can always be read by subtracting
    128 // sizeof(DataHeader).
    129 class WasmArrayObject : public WasmGcObject,
    130                        public TrailingArray<WasmArrayObject> {
    131 public:
    132  static const JSClass class_;
    133 
    134  // The number of elements in the array.
    135  uint32_t numElements_;
    136 
    137  // Owned data pointer, holding `numElements_` entries. This may point to
    138  // `inlineStorage` or to an externally-allocated block of memory. It points
    139  // to the start of the array data, after the data header.
    140  //
    141  // This pointer is never null. An empty array will be stored like any other
    142  // inline-storage array.
    143  uint8_t* data_;
    144 
    145  // The inline (wasm-array-level) data fields, stored as a trailing array. We
    146  // request this field to begin at an 8-aligned offset relative to the start of
    147  // the object, so as to guarantee that `double` typed fields are not subject
    148  // to misaligned-access penalties on any target, whilst wasting at maximum 4
    149  // bytes of space. (v128 fields are possible, but we have opted to favor
    150  // slightly smaller objects over requiring a 16-byte alignment.)
    151  //
    152  // If used, the inline storage area will begin with the data header, followed
    153  // by the actual array data. See the main comment on WasmArrayObject.
    154  //
    155  // Remember that `inlineStorage` is in reality a variable length block with
    156  // maximum size WasmArrayObject_MaxInlineBytes bytes.  Do not add any
    157  // (C++-level) fields after this point!
    158  uint8_t* inlineStorage() {
    159    return offsetToPointer<uint8_t>(offsetOfInlineStorage());
    160  }
    161 
    162  // Actual array data that follows DataHeader. The array data is a part of the
    163  // `inlineStorage`.
    164  template <typename T>
    165  T* inlineArrayElements() {
    166    return offsetToPointer<T>(offsetOfInlineArrayData());
    167  }
    168 
    169  // AllocKind for object creation
    170  static inline gc::AllocKind allocKindForOOL();
    171  static inline gc::AllocKind allocKindForIL(uint32_t storageBytes);
    172  inline gc::AllocKind allocKind() const;
    173 
    174  // Calculate the byte length of the array's data storage, being careful to
    175  // check for overflow. This includes the data header, data, and any extra
    176  // space for alignment with GC sizes. Note this logic assumes that
    177  // MaxArrayPayloadBytes is within uint32_t range.
    178  //
    179  // This logic is mirrored in WasmArrayObject::maxInlineElementsForElemSize and
    180  // MacroAssembler::wasmNewArrayObject.
    181  static constexpr mozilla::CheckedUint32 calcStorageBytesChecked(
    182      uint32_t elemSize, uint32_t numElements) {
    183    static_assert(sizeof(WasmArrayObject) % gc::CellAlignBytes == 0);
    184    mozilla::CheckedUint32 storageBytes = elemSize;
    185    storageBytes *= numElements;
    186    storageBytes += sizeof(WasmArrayObject::DataHeader);
    187    // Round total allocation up to gc::CellAlignBytes
    188    storageBytes -= 1;
    189    storageBytes += gc::CellAlignBytes - (storageBytes % gc::CellAlignBytes);
    190    return storageBytes;
    191  }
    192  // Calculate the byte length of the array's data storage, without checking for
    193  // overflow. This includes the data header, data, and any extra space for
    194  // alignment with GC sizes.
    195  static uint32_t calcStorageBytesUnchecked(uint32_t elemSize,
    196                                            uint32_t numElements) {
    197    mozilla::CheckedUint32 storageBytes =
    198        calcStorageBytesChecked(elemSize, numElements);
    199    MOZ_ASSERT(storageBytes.isValid());
    200    return storageBytes.value();
    201  }
    202  // Compute the maximum number of elements that can be stored inline for the
    203  // given element size.
    204  static inline constexpr uint32_t maxInlineElementsForElemSize(
    205      uint32_t elemSize);
    206 
    207  size_t sizeOfExcludingThis() const;
    208 
    209  // These constants can be anything, so long as they are not the same.  Use
    210  // small but unlikely values in the hope of getting more value from
    211  // assertions involving them.
    212  using DataHeader = uintptr_t;
    213  static const DataHeader DataIsIL = 0x37;
    214  static const DataHeader DataIsOOL = 0x71;
    215 
    216  // Creates a new array object with out-of-line storage. Reports an error on
    217  // OOM. The element type, shape, class pointer, alloc site and alloc kind are
    218  // taken from `typeDefData`; the initial heap must be specified separately.
    219  // The size of storage is debug-asserted to be larger than
    220  // WasmArrayObject_MaxInlineBytes - generally, C++ code should use
    221  // WasmArrayObject::createArray.
    222  template <bool ZeroFields>
    223  static MOZ_ALWAYS_INLINE WasmArrayObject* createArrayOOL(
    224      JSContext* cx, wasm::TypeDefInstanceData* typeDefData,
    225      js::gc::AllocSite* allocSite, js::gc::Heap initialHeap,
    226      uint32_t numElements, uint32_t storageBytes);
    227 
    228  // Creates a new array object with inline storage. Reports an error on OOM.
    229  // The element type, shape, class pointer, alloc site and alloc kind are taken
    230  // from `typeDefData`; the initial heap must be specified separately. The size
    231  // of storage is debug-asserted to be within WasmArrayObject_MaxInlineBytes -
    232  // generally, C++ code should use WasmArrayObject::createArray.
    233  template <bool ZeroFields>
    234  static MOZ_ALWAYS_INLINE WasmArrayObject* createArrayIL(
    235      JSContext* cx, wasm::TypeDefInstanceData* typeDefData,
    236      js::gc::AllocSite* allocSite, js::gc::Heap initialHeap,
    237      uint32_t numElements, uint32_t storageBytes);
    238 
    239  // This selects one of the above two routines, depending on how much storage
    240  // is required for the given type and number of elements.
    241  template <bool ZeroFields>
    242  static MOZ_ALWAYS_INLINE WasmArrayObject* createArray(
    243      JSContext* cx, wasm::TypeDefInstanceData* typeDefData,
    244      js::gc::AllocSite* allocSite, js::gc::Heap initialHeap,
    245      uint32_t numElements);
    246 
    247  // JIT accessors
    248  static constexpr size_t offsetOfNumElements() {
    249    return offsetof(WasmArrayObject, numElements_);
    250  }
    251  static constexpr size_t offsetOfData() {
    252    return offsetof(WasmArrayObject, data_);
    253  }
    254  static const uint32_t inlineStorageAlignment = 8;
    255  static constexpr size_t offsetOfInlineStorage() {
    256    return AlignBytes(sizeof(WasmArrayObject), inlineStorageAlignment);
    257  }
    258  static constexpr size_t offsetOfInlineArrayData() {
    259    return offsetOfInlineStorage() + sizeof(DataHeader);
    260  }
    261 
    262  // Tracing and finalization
    263  static void obj_trace(JSTracer* trc, JSObject* object);
    264  static void obj_finalize(JS::GCContext* gcx, JSObject* object);
    265  static size_t obj_moved(JSObject* objNew, JSObject* objOld);
    266 
    267  void storeVal(const wasm::Val& val, uint32_t itemIndex);
    268  void fillVal(const wasm::Val& val, uint32_t itemIndex, uint32_t len);
    269 
    270  static inline DataHeader* dataHeaderFromDataPointer(const uint8_t* data) {
    271    MOZ_ASSERT(data);
    272    DataHeader* header = (DataHeader*)data;
    273    header--;
    274    MOZ_ASSERT(*header == DataIsIL || *header == DataIsOOL);
    275    return header;
    276  }
    277  DataHeader* dataHeader() const {
    278    return WasmArrayObject::dataHeaderFromDataPointer(data_);
    279  }
    280 
    281  static inline uint8_t* dataHeaderToDataPointer(const DataHeader* header) {
    282    MOZ_ASSERT(header);
    283    MOZ_ASSERT(*header == DataIsIL || *header == DataIsOOL);
    284    header++;
    285    return (uint8_t*)header;
    286  }
    287 
    288  static bool isDataInline(uint8_t* data) {
    289    const DataHeader* header = dataHeaderFromDataPointer(data);
    290    MOZ_ASSERT(*header == DataIsIL || *header == DataIsOOL);
    291    return *header == DataIsIL;
    292  }
    293  bool isDataInline() const { return WasmArrayObject::isDataInline(data_); }
    294 
    295  static WasmArrayObject* fromInlineDataPointer(uint8_t* data) {
    296    MOZ_ASSERT(isDataInline(data));
    297    return (WasmArrayObject*)(data -
    298                              WasmArrayObject::offsetOfInlineArrayData());
    299  }
    300 
    301  static DataHeader* addressOfInlineDataHeader(WasmArrayObject* base) {
    302    return base->offsetToPointer<DataHeader>(offsetOfInlineStorage());
    303  }
    304  static uint8_t* addressOfInlineData(WasmArrayObject* base) {
    305    return base->offsetToPointer<uint8_t>(offsetOfInlineArrayData());
    306  }
    307 };
    308 
    309 static_assert((WasmArrayObject::offsetOfInlineStorage() % 8) == 0);
    310 
    311 // Helper to mark all locations that assume that the type of
    312 // WasmArrayObject::numElements is uint32_t.
    313 #define STATIC_ASSERT_WASMARRAYELEMENTS_NUMELEMENTS_IS_U32 \
    314  static_assert(sizeof(js::WasmArrayObject::numElements_) == sizeof(uint32_t))
    315 
    316 //=========================================================================
    317 // WasmStructObject
    318 
    319 // Class for a wasm struct.  It has inline data and, if the inline area is
    320 // insufficient, a pointer to outline data that lives in the C++ heap.
    321 // Computing the field offsets is somewhat tricky; see SMDOC in
    322 // WasmStructLayout.h.
    323 //
    324 // From a C++ viewpoint, WasmStructObject just holds two pointers, a shape
    325 // pointer and the supertype vector pointer.  Because of class-total-size
    326 // roundup effects, it is 16 bytes on both 64- and 32-bit targets.
    327 //
    328 // For our purposes a WasmStructObject is always followed immediately by an
    329 // in-line data area, with maximum size WasmStructObject_MaxInlineBytes.  Both
    330 // the two-word header and the inline data area have 8-aligned sizes.  The GC's
    331 // allocation routines only guarantee 8-byte alignment.  This means a
    332 // WasmStructObject can offer naturally aligned storage for fields of size 8,
    333 // 4, 2 and 1, but not for fields of size 16, even though the header size is 16
    334 // bytes.
    335 //
    336 // If the available inline storage is insufficient, some part of the inline
    337 // data are will be used as a pointer to the out of line area.  This however is
    338 // not WasmStructObject's concern: it is unaware of the in-line area layout,
    339 // all details of which are stored in the associated StructType, and partially
    340 // cached in TypeDefInstanceData.cached.strukt.
    341 //
    342 // Note that MIR alias analysis assumes the OOL-pointer field, if any, is
    343 // readonly for the life of the object; do not change it once the object is
    344 // created.  See MWasmLoadField::congruentTo.
    345 
    346 class WasmStructObject : public WasmGcObject,
    347                         public TrailingArray<WasmStructObject> {
    348 public:
    349  static const JSClass classInline_;
    350  static const JSClass classOutline_;
    351 
    352  static const JSClass* classFromOOLness(bool needsOOLstorage) {
    353    return needsOOLstorage ? &classOutline_ : &classInline_;
    354  }
    355 
    356  size_t sizeOfExcludingThis() const;
    357 
    358  // Creates a new struct typed object, optionally initialized to zero.
    359  // Reports if there is an out of memory error.  The structure's type, shape,
    360  // class pointer, alloc site and alloc kind are taken from `typeDefData`;
    361  // the initial heap must be specified separately.  It is assumed and debug-
    362  // asserted that `typeDefData` refers to a type that does not need OOL
    363  // storage.
    364  template <bool ZeroFields>
    365  static MOZ_ALWAYS_INLINE WasmStructObject* createStructIL(
    366      JSContext* cx, wasm::TypeDefInstanceData* typeDefData,
    367      gc::AllocSite* allocSite, js::gc::Heap initialHeap);
    368 
    369  // Same as ::createStructIL, except it is assumed and debug-asserted that
    370  // `typeDefData` refers to a type that does need OOL storage.
    371  template <bool ZeroFields>
    372  static MOZ_ALWAYS_INLINE WasmStructObject* createStructOOL(
    373      JSContext* cx, wasm::TypeDefInstanceData* typeDefData,
    374      gc::AllocSite* allocSite, js::gc::Heap initialHeap);
    375 
    376  // Given the index of a field, return its actual address.
    377  uint8_t* fieldIndexToAddress(uint32_t fieldIndex);
    378 
    379  // Operations relating to the OOL block pointer.  These involve chain-chasing
    380  // starting from `superTypeVector_` and shouldn't be used in very hot paths.
    381  bool hasOOLPointer() const;
    382  // These will release-assert if called when `!hasOOLPointer()`.
    383  uint8_t** addressOfOOLPointer() const;
    384  uint8_t* getOOLPointer() const;
    385  void setOOLPointer(uint8_t* newOOLpointer);
    386 
    387  // Similar to the above, but find the OOL pointer by looking in the supplied
    388  // TypeDefInstanceData.  This requires less chain-chasing.
    389  uint8_t** addressOfOOLPointer(
    390      const wasm::TypeDefInstanceData* typeDefData) const;
    391  void setOOLPointer(const wasm::TypeDefInstanceData* typeDefData,
    392                     uint8_t* newOOLpointer);
    393 
    394  // Gets JS Value of the structure field.
    395  bool getField(JSContext* cx, uint32_t index, MutableHandle<Value> val);
    396 
    397  // Tracing and finalization
    398  static void obj_trace(JSTracer* trc, JSObject* object);
    399  static size_t obj_moved(JSObject* objNew, JSObject* objOld);
    400 
    401  void storeVal(const wasm::Val& val, uint32_t fieldIndex);
    402 };
    403 
    404 // This isn't specifically required.  Is merely here to make it obvious when
    405 // the size does change.
    406 static_assert(sizeof(WasmStructObject) == 16);
    407 
    408 // Both `sizeof(WasmStructObject)` and WasmStructObject_MaxInlineBytes
    409 // must be multiples of 8 for reasons described in the comment on
    410 // `class WasmStructObject` above.
    411 static_assert((sizeof(WasmStructObject) % 8) == 0);
    412 
    413 const size_t WasmStructObject_MaxInlineBytes =
    414    ((JSObject::MAX_BYTE_SIZE - sizeof(WasmStructObject)) / 8) * 8;
    415 
    416 static_assert((WasmStructObject_MaxInlineBytes % 8) == 0);
    417 
    418 // These are EXTREMELY IMPORTANT.  Do not remove them.  Without them, there is
    419 // nothing that ensures that the object layouts created by StructType::init()
    420 // will actually be in accordance with the WasmStructObject layout constraints
    421 // described above.  If either fails, the _ASSUMED values are wrong and will
    422 // need to be updated.
    423 static_assert(wasm::WasmStructObject_Size_ASSUMED == sizeof(WasmStructObject));
    424 static_assert(wasm::WasmStructObject_MaxInlineBytes_ASSUMED ==
    425              WasmStructObject_MaxInlineBytes);
    426 
    427 const size_t WasmArrayObject_MaxInlineBytes =
    428    ((JSObject::MAX_BYTE_SIZE - sizeof(WasmArrayObject)) / 16) * 16;
    429 
    430 static_assert((WasmArrayObject_MaxInlineBytes % 16) == 0);
    431 
    432 /* static */
    433 inline constexpr uint32_t WasmArrayObject::maxInlineElementsForElemSize(
    434    uint32_t elemSize) {
    435  // This implementation inverts the logic of WasmArrayObject::calcStorageBytes
    436  // to compute numElements.
    437  MOZ_RELEASE_ASSERT(elemSize > 0);
    438  uint32_t result = WasmArrayObject_MaxInlineBytes;
    439  static_assert(WasmArrayObject_MaxInlineBytes % gc::CellAlignBytes == 0);
    440  result -= sizeof(WasmArrayObject::DataHeader);
    441  result /= elemSize;
    442 
    443  MOZ_RELEASE_ASSERT(calcStorageBytesChecked(elemSize, result).isValid());
    444  return result;
    445 }
    446 
    447 inline bool WasmStructObject::hasOOLPointer() const {
    448  const wasm::SuperTypeVector* stv = superTypeVector_;
    449  const wasm::TypeDef* typeDef = stv->typeDef();
    450  MOZ_ASSERT(typeDef->superTypeVector() == stv);
    451  const wasm::StructType& structType = typeDef->structType();
    452  uint32_t offset = structType.oolPointerOffset_;
    453  return offset != wasm::StructType::InvalidOffset;
    454 }
    455 
    456 inline uint8_t** WasmStructObject::addressOfOOLPointer() const {
    457  const wasm::SuperTypeVector* stv = superTypeVector_;
    458  const wasm::TypeDef* typeDef = stv->typeDef();
    459  MOZ_ASSERT(typeDef->superTypeVector() == stv);
    460  const wasm::StructType& structType = typeDef->structType();
    461  uint32_t offset = structType.oolPointerOffset_;
    462  MOZ_RELEASE_ASSERT(offset != wasm::StructType::InvalidOffset);
    463  return (uint8_t**)((uint8_t*)this + offset);
    464 }
    465 
    466 inline uint8_t* WasmStructObject::getOOLPointer() const {
    467  return *addressOfOOLPointer();
    468 }
    469 
    470 inline void WasmStructObject::setOOLPointer(uint8_t* newOOLpointer) {
    471  *addressOfOOLPointer() = newOOLpointer;
    472 }
    473 
    474 inline uint8_t** WasmStructObject::addressOfOOLPointer(
    475    const wasm::TypeDefInstanceData* typeDefData) const {
    476  uint32_t offset = typeDefData->cached.strukt.oolPointerOffset;
    477  MOZ_RELEASE_ASSERT(offset != wasm::StructType::InvalidOffset);
    478  uint8_t** addr = (uint8_t**)((uint8_t*)this + offset);
    479  // Don't turn this into a release-assert; that would defeat the purpose of
    480  // having this method.
    481  MOZ_ASSERT(addr == addressOfOOLPointer());
    482  return addr;
    483 }
    484 
    485 inline void WasmStructObject::setOOLPointer(
    486    const wasm::TypeDefInstanceData* typeDefData, uint8_t* newOOLpointer) {
    487  *addressOfOOLPointer(typeDefData) = newOOLpointer;
    488 }
    489 
    490 // Ensure that faulting loads/stores for WasmStructObject and WasmArrayObject
    491 // are in the NULL pointer guard page.
    492 static_assert(WasmStructObject_MaxInlineBytes <= wasm::NullPtrGuardSize);
    493 static_assert(sizeof(WasmArrayObject) <= wasm::NullPtrGuardSize);
    494 
    495 // Template to acquire a stable pointer to the elements of a WasmArrayObject
    496 // that will not move even if there is a GC. This will create a copy of the
    497 // array onto the stack when the array has inline data, and can be expensive.
    498 template <typename T>
    499 class MOZ_RAII StableWasmArrayObjectElements {
    500  static constexpr size_t MaxInlineElements =
    501      WasmArrayObject::maxInlineElementsForElemSize(sizeof(T));
    502  Rooted<WasmArrayObject*> array_;
    503  T* elements_;
    504  mozilla::Maybe<mozilla::Vector<T, MaxInlineElements, SystemAllocPolicy>>
    505      ownElements_;
    506 
    507 public:
    508  StableWasmArrayObjectElements(JSContext* cx, Handle<WasmArrayObject*> array)
    509      : array_(cx, array), elements_(nullptr) {
    510    if (array->isDataInline()) {
    511      ownElements_.emplace();
    512      if (!ownElements_->resize(array->numElements_)) {
    513        // Should not happen as we have inline storage for the maximum needed
    514        // elements.
    515        MOZ_CRASH();
    516      }
    517      const T* src = array->inlineArrayElements<T>();
    518      std::copy(src, src + array->numElements_, ownElements_->begin());
    519      elements_ = ownElements_->begin();
    520    } else {
    521      elements_ = reinterpret_cast<T*>(array->data_);
    522    }
    523  }
    524 
    525  T* elements() { return elements_; }
    526  size_t length() const { return array_->numElements_; }
    527 };
    528 
    529 }  // namespace js
    530 
    531 //=========================================================================
    532 // misc
    533 
    534 namespace js {
    535 
    536 inline bool IsWasmGcObjectClass(const JSClass* class_) {
    537  return class_ == &WasmArrayObject::class_ ||
    538         class_ == &WasmStructObject::classInline_ ||
    539         class_ == &WasmStructObject::classOutline_;
    540 }
    541 
    542 }  // namespace js
    543 
    544 template <>
    545 inline bool JSObject::is<js::WasmGcObject>() const {
    546  return js::IsWasmGcObjectClass(getClass());
    547 }
    548 
    549 template <>
    550 inline bool JSObject::is<js::WasmStructObject>() const {
    551  const JSClass* class_ = getClass();
    552  return class_ == &js::WasmStructObject::classInline_ ||
    553         class_ == &js::WasmStructObject::classOutline_;
    554 }
    555 
    556 #endif /* wasm_WasmGcObject_h */