tor-browser

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

WasmGcObject.cpp (22984B)


      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 #include "wasm/WasmGcObject-inl.h"
      8 
      9 #include "gc/Tracer.h"
     10 #include "js/CharacterEncoding.h"
     11 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     12 #include "js/PropertySpec.h"
     13 #include "js/ScalarType.h"  // js::Scalar::Type
     14 #include "js/Vector.h"
     15 #include "util/StringBuilder.h"
     16 #include "vm/GlobalObject.h"
     17 #include "vm/JSFunction.h"
     18 #include "vm/JSObject.h"
     19 #include "vm/PlainObject.h"  // js::PlainObject
     20 #include "vm/PropertyResult.h"
     21 #include "vm/Realm.h"
     22 #include "vm/SelfHosting.h"
     23 #include "vm/StringType.h"
     24 #include "vm/TypedArrayObject.h"
     25 #include "vm/Uint8Clamped.h"
     26 
     27 #include "gc/BufferAllocator-inl.h"
     28 #include "gc/GCContext-inl.h"  // GCContext::removeCellMemory
     29 #include "gc/ObjectKind-inl.h"
     30 #include "vm/JSContext-inl.h"
     31 
     32 using namespace js;
     33 using namespace wasm;
     34 
     35 // [SMDOC] Management of OOL storage areas for Wasm{Array,Struct}Object.
     36 //
     37 // WasmArrayObject always has its payload data stored in a block which is
     38 // pointed to from the WasmArrayObject. The same is true for WasmStructObject in
     39 // the case where the fields cannot fit in the object itself. These blocks are
     40 // in some places referred to as "trailer blocks".
     41 //
     42 // These blocks are allocated in the same way as JSObects slots and element
     43 // buffers, either using the GC's buffer allocator or directly in the nursery if
     44 // they are small enough.
     45 //
     46 // They require the use of WasmArrayObject/WasmStructObject::obj_moved hooks to
     47 // update the pointer and mark the allocation when the object gets tenured, and
     48 // also update the pointer in case it is an internal pointer when the object is
     49 // moved.
     50 //
     51 // The blocks are freed by the GC when no longer referenced.
     52 
     53 //=========================================================================
     54 // WasmGcObject
     55 
     56 const ObjectOps WasmGcObject::objectOps_ = {
     57    WasmGcObject::obj_lookupProperty,            // lookupProperty
     58    WasmGcObject::obj_defineProperty,            // defineProperty
     59    WasmGcObject::obj_hasProperty,               // hasProperty
     60    WasmGcObject::obj_getProperty,               // getProperty
     61    WasmGcObject::obj_setProperty,               // setProperty
     62    WasmGcObject::obj_getOwnPropertyDescriptor,  // getOwnPropertyDescriptor
     63    WasmGcObject::obj_deleteProperty,            // deleteProperty
     64    nullptr,                                     // getElements
     65    nullptr,                                     // funToString
     66 };
     67 
     68 /* static */
     69 bool WasmGcObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
     70                                      HandleId id, MutableHandleObject objp,
     71                                      PropertyResult* propp) {
     72  objp.set(nullptr);
     73  propp->setNotFound();
     74  return true;
     75 }
     76 
     77 bool WasmGcObject::obj_defineProperty(JSContext* cx, HandleObject obj,
     78                                      HandleId id,
     79                                      Handle<PropertyDescriptor> desc,
     80                                      ObjectOpResult& result) {
     81  result.failReadOnly();
     82  return true;
     83 }
     84 
     85 bool WasmGcObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id,
     86                                   bool* foundp) {
     87  *foundp = false;
     88  return true;
     89 }
     90 
     91 bool WasmGcObject::obj_getProperty(JSContext* cx, HandleObject obj,
     92                                   HandleValue receiver, HandleId id,
     93                                   MutableHandleValue vp) {
     94  vp.setUndefined();
     95  return true;
     96 }
     97 
     98 bool WasmGcObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id,
     99                                   HandleValue v, HandleValue receiver,
    100                                   ObjectOpResult& result) {
    101  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    102                            JSMSG_WASM_MODIFIED_GC_OBJECT);
    103  return false;
    104 }
    105 
    106 bool WasmGcObject::obj_getOwnPropertyDescriptor(
    107    JSContext* cx, HandleObject obj, HandleId id,
    108    MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
    109  desc.reset();
    110  return true;
    111 }
    112 
    113 bool WasmGcObject::obj_deleteProperty(JSContext* cx, HandleObject obj,
    114                                      HandleId id, ObjectOpResult& result) {
    115  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    116                            JSMSG_WASM_MODIFIED_GC_OBJECT);
    117  return false;
    118 }
    119 
    120 bool WasmGcObject::lookUpProperty(JSContext* cx, Handle<WasmGcObject*> obj,
    121                                  jsid id, WasmGcObject::PropOffset* offset,
    122                                  StorageType* type) {
    123  switch (obj->kind()) {
    124    case wasm::TypeDefKind::Struct: {
    125      const auto& structType = obj->typeDef().structType();
    126      uint32_t index;
    127      if (!IdIsIndex(id, &index)) {
    128        return false;
    129      }
    130      MOZ_ASSERT(structType.fields_.length() ==
    131                 structType.fieldAccessPaths_.length());
    132      if (index >= structType.fields_.length()) {
    133        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    134                                 JSMSG_WASM_OUT_OF_BOUNDS);
    135        return false;
    136      }
    137      offset->set(index);
    138      *type = structType.fields_[index].type;
    139      return true;
    140    }
    141    case wasm::TypeDefKind::Array: {
    142      const auto& arrayType = obj->typeDef().arrayType();
    143 
    144      uint32_t index;
    145      if (!IdIsIndex(id, &index)) {
    146        return false;
    147      }
    148      uint32_t numElements = obj->as<WasmArrayObject>().numElements_;
    149      if (index >= numElements) {
    150        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    151                                 JSMSG_WASM_OUT_OF_BOUNDS);
    152        return false;
    153      }
    154      uint64_t scaledIndex =
    155          uint64_t(index) * uint64_t(arrayType.elementType().size());
    156      if (scaledIndex >= uint64_t(UINT32_MAX)) {
    157        // It's unrepresentable as an WasmGcObject::PropOffset. Give up.
    158        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    159                                 JSMSG_WASM_OUT_OF_BOUNDS);
    160        return false;
    161      }
    162      offset->set(uint32_t(scaledIndex));
    163      *type = arrayType.elementType();
    164      return true;
    165    }
    166    default:
    167      MOZ_ASSERT_UNREACHABLE();
    168      return false;
    169  }
    170 }
    171 
    172 bool WasmGcObject::loadValue(JSContext* cx, Handle<WasmGcObject*> obj, jsid id,
    173                             MutableHandleValue vp) {
    174  WasmGcObject::PropOffset offset;
    175  StorageType type;
    176  if (!lookUpProperty(cx, obj, id, &offset, &type)) {
    177    return false;
    178  }
    179 
    180  // Temporary hack, (ref T) is not exposable to JS yet but some tests would
    181  // like to access it so we erase (ref T) with eqref when loading. This is
    182  // safe as (ref T) <: eqref and we're not in the writing case where we
    183  // would need to perform a type check.
    184  if (type.isTypeRef()) {
    185    type = RefType::fromTypeCode(TypeCode::EqRef, true);
    186  }
    187 
    188  if (!type.isExposable()) {
    189    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    190                             JSMSG_WASM_BAD_VAL_TYPE);
    191    return false;
    192  }
    193 
    194  if (obj->is<WasmStructObject>()) {
    195    // `offset` is the field index.
    196    WasmStructObject& structObj = obj->as<WasmStructObject>();
    197    MOZ_RELEASE_ASSERT(structObj.kind() == TypeDefKind::Struct);
    198    // The above call to `lookUpProperty` will reject a request for a struct
    199    // field whose index is out of range.  Hence the following will be safe
    200    // providing the FieldAccessPaths are correct.
    201    return ToJSValue(cx, structObj.fieldIndexToAddress(offset.get()), type, vp);
    202  }
    203 
    204  MOZ_ASSERT(obj->is<WasmArrayObject>());
    205  const WasmArrayObject& arrayObj = obj->as<WasmArrayObject>();
    206  return ToJSValue(cx, arrayObj.data_ + offset.get(), type, vp);
    207 }
    208 
    209 bool WasmGcObject::isRuntimeSubtypeOf(
    210    const wasm::TypeDef* parentTypeDef) const {
    211  return TypeDef::isSubTypeOf(&typeDef(), parentTypeDef);
    212 }
    213 
    214 bool WasmGcObject::obj_newEnumerate(JSContext* cx, HandleObject obj,
    215                                    MutableHandleIdVector properties,
    216                                    bool enumerableOnly) {
    217  return true;
    218 }
    219 
    220 static void WriteValTo(WasmGcObject* owner, const Val& val, StorageType ty,
    221                       void* dest) {
    222  switch (ty.kind()) {
    223    case StorageType::I8:
    224      *((uint8_t*)dest) = val.i32();
    225      break;
    226    case StorageType::I16:
    227      *((uint16_t*)dest) = val.i32();
    228      break;
    229    case StorageType::I32:
    230      *((uint32_t*)dest) = val.i32();
    231      break;
    232    case StorageType::I64:
    233      *((uint64_t*)dest) = val.i64();
    234      break;
    235    case StorageType::F32:
    236      *((float*)dest) = val.f32();
    237      break;
    238    case StorageType::F64:
    239      *((double*)dest) = val.f64();
    240      break;
    241    case StorageType::V128:
    242      *((V128*)dest) = val.v128();
    243      break;
    244    case StorageType::Ref:
    245      if (owner->isTenured()) {
    246        *((GCPtr<AnyRef>*)dest) = val.ref();
    247      } else {
    248        *((PreBarriered<AnyRef>*)dest) = val.ref();
    249      }
    250      break;
    251  }
    252 }
    253 
    254 //=========================================================================
    255 // WasmArrayObject
    256 
    257 /* static */
    258 size_t js::WasmArrayObject::sizeOfExcludingThis() const {
    259  if (!isDataInline() || !gc::IsBufferAlloc(dataHeader())) {
    260    return 0;
    261  }
    262 
    263  return gc::GetAllocSize(zone(), dataHeader());
    264 }
    265 
    266 /* static */
    267 void WasmArrayObject::obj_trace(JSTracer* trc, JSObject* object) {
    268  WasmArrayObject& arrayObj = object->as<WasmArrayObject>();
    269  uint8_t* data = arrayObj.data_;
    270 
    271  if (!arrayObj.isDataInline()) {
    272    uint8_t* outlineAlloc = (uint8_t*)dataHeaderFromDataPointer(arrayObj.data_);
    273    uint8_t* prior = outlineAlloc;
    274    TraceBufferEdge(trc, &arrayObj, &outlineAlloc, "WasmArrayObject storage");
    275    if (outlineAlloc != prior) {
    276      arrayObj.data_ = (uint8_t*)(((DataHeader*)outlineAlloc) + 1);
    277    }
    278  }
    279 
    280  const auto& typeDef = arrayObj.typeDef();
    281  const auto& arrayType = typeDef.arrayType();
    282  if (!arrayType.elementType().isRefRepr()) {
    283    return;
    284  }
    285 
    286  uint32_t numElements = arrayObj.numElements_;
    287  uint32_t elemSize = arrayType.elementType().size();
    288  for (uint32_t i = 0; i < numElements; i++) {
    289    AnyRef* elementPtr = reinterpret_cast<AnyRef*>(data + i * elemSize);
    290    TraceManuallyBarrieredEdge(trc, elementPtr, "wasm-array-element");
    291  }
    292 }
    293 
    294 /* static */
    295 size_t WasmArrayObject::obj_moved(JSObject* objNew, JSObject* objOld) {
    296  // This gets called for array objects, both with and without OOL areas.
    297  // Dealing with the no-OOL case is simple.  Thereafter, the logic for the OOL
    298  // case is essentially the same as for WasmStructObject::obj_moved, since
    299  // that routine is only used for WasmStructObjects that have OOL storage.
    300  MOZ_ASSERT(objNew != objOld);
    301 
    302  WasmArrayObject& arrayNew = objNew->as<WasmArrayObject>();
    303  WasmArrayObject& arrayOld = objOld->as<WasmArrayObject>();
    304 
    305  const TypeDef* typeDefNew = &arrayNew.typeDef();
    306  mozilla::DebugOnly<const TypeDef*> typeDefOld = &arrayOld.typeDef();
    307  MOZ_ASSERT(typeDefNew->isArrayType());
    308  MOZ_ASSERT(typeDefOld == typeDefNew);
    309 
    310  // At this point, the object has been copied, but the OOL storage area, if
    311  // any, has not been copied, nor has the data_ pointer been updated.  Hence:
    312  MOZ_ASSERT(arrayNew.data_ == arrayOld.data_);
    313 
    314  if (arrayOld.isDataInline()) {
    315    // The old array had inline storage, which has been copied.  Fix up the
    316    // data pointer in the new array to point to it, and we're done.
    317    arrayNew.data_ = WasmArrayObject::addressOfInlineData(&arrayNew);
    318    MOZ_ASSERT(arrayNew.isDataInline());
    319    return 0;
    320  }
    321 
    322  // The array has OOL storage.  This means the logic that follows is similar
    323  // to that for WasmStructObject::obj_moved, since that routine is only used
    324  // for WasmStructObjects that have OOL storage.
    325 
    326  bool newIsInNursery = IsInsideNursery(objNew);
    327  bool oldIsInNursery = IsInsideNursery(objOld);
    328 
    329  // Tenured -> Tenured
    330  if (!oldIsInNursery && !newIsInNursery) {
    331    // The object already was in the tenured heap and has merely been moved
    332    // somewhere else in the the tenured heap.  This isn't interesting to us.
    333    return 0;
    334  }
    335 
    336  // Tenured -> Nursery: this transition isn't possible.
    337  MOZ_RELEASE_ASSERT(oldIsInNursery);
    338 
    339  // Nursery -> Nursery and Nursery -> Tenured
    340  // The object is being moved, either within the nursery or from the nursery
    341  // to the tenured heap.  Either way, we have to ask the nursery if it wants
    342  // to move the OOL block too, and if so set up a forwarding record for it.
    343 
    344  // arrayNew.numElements_ was validated not to overflow when constructing
    345  // the array.
    346  size_t oolBlockSize = calcStorageBytesUnchecked(
    347      typeDefNew->arrayType().elementType().size(), arrayNew.numElements_);
    348  // Ensured by WasmArrayObject::createArrayOOL.
    349  MOZ_RELEASE_ASSERT(oolBlockSize <= size_t(MaxArrayPayloadBytes) +
    350                                         sizeof(WasmArrayObject::DataHeader));
    351 
    352  // Ask the nursery if it wants to relocate the OOL block, and if so capture
    353  // its new location in `oolHeaderNew`.  Note, at this point `arrayNew.data_`
    354  // has not been updated; hence the computation for `oolHeaderOld` is correct.
    355  DataHeader* oolHeaderOld = dataHeaderFromDataPointer(arrayNew.data_);
    356  DataHeader* oolHeaderNew = oolHeaderOld;
    357  Nursery& nursery = objNew->runtimeFromMainThread()->gc.nursery();
    358  nursery.maybeMoveBufferOnPromotion(&oolHeaderNew, objNew, oolBlockSize);
    359 
    360  if (oolHeaderNew != oolHeaderOld) {
    361    // The OOL block has been moved.  Fix up the data pointer in the new
    362    // object.
    363    arrayNew.data_ = dataHeaderToDataPointer(oolHeaderNew);
    364    // Set up forwarding for the OOL block.  Use indirect forwarding.
    365    // Unfortunately, if the call to `.setForwardingPointer..` OOMs, there's no
    366    // way to recover.
    367    nursery.setForwardingPointerWhileTenuring(oolHeaderOld, oolHeaderNew,
    368                                              /*direct=*/false);
    369  }
    370 
    371  return 0;
    372 }
    373 
    374 void WasmArrayObject::storeVal(const Val& val, uint32_t itemIndex) {
    375  const ArrayType& arrayType = typeDef().arrayType();
    376  size_t elementSize = arrayType.elementType().size();
    377  MOZ_ASSERT(itemIndex < numElements_);
    378  uint8_t* data = data_ + elementSize * itemIndex;
    379  WriteValTo(this, val, arrayType.elementType(), data);
    380 }
    381 
    382 void WasmArrayObject::fillVal(const Val& val, uint32_t itemIndex,
    383                              uint32_t len) {
    384  const ArrayType& arrayType = typeDef().arrayType();
    385  size_t elementSize = arrayType.elementType().size();
    386  uint8_t* data = data_ + elementSize * itemIndex;
    387  MOZ_ASSERT(itemIndex <= numElements_ && len <= numElements_ - itemIndex);
    388  for (uint32_t i = 0; i < len; i++) {
    389    WriteValTo(this, val, arrayType.elementType(), data);
    390    data += elementSize;
    391  }
    392 }
    393 
    394 static const JSClassOps WasmArrayObjectClassOps = {
    395    nullptr, /* addProperty */
    396    nullptr, /* delProperty */
    397    nullptr, /* enumerate   */
    398    WasmGcObject::obj_newEnumerate,
    399    nullptr, /* resolve     */
    400    nullptr, /* mayResolve  */
    401    nullptr, /* finalize    */
    402    nullptr, /* call        */
    403    nullptr, /* construct   */
    404    WasmArrayObject::obj_trace,
    405 };
    406 static const ClassExtension WasmArrayObjectClassExt = {
    407    WasmArrayObject::obj_moved, /* objectMovedOp */
    408 };
    409 const JSClass WasmArrayObject::class_ = {
    410    "WasmArrayObject",
    411    JSClass::NON_NATIVE | JSCLASS_DELAY_METADATA_BUILDER,
    412    &WasmArrayObjectClassOps,
    413    JS_NULL_CLASS_SPEC,
    414    &WasmArrayObjectClassExt,
    415    &WasmGcObject::objectOps_,
    416 };
    417 
    418 //=========================================================================
    419 // WasmStructObject
    420 
    421 /* static */
    422 size_t js::WasmStructObject::sizeOfExcludingThis() const {
    423  if (!hasOOLPointer()) {
    424    return 0;
    425  }
    426  const uint8_t* oolPointer = getOOLPointer();
    427  if (!gc::IsBufferAlloc((void*)oolPointer)) {
    428    return 0;
    429  }
    430 
    431  return gc::GetAllocSize(zone(), oolPointer);
    432 }
    433 
    434 /* static */
    435 bool WasmStructObject::getField(JSContext* cx, uint32_t index,
    436                                MutableHandle<Value> val) {
    437  const StructType& resultType = typeDef().structType();
    438  MOZ_ASSERT(index <= resultType.fields_.length());
    439  const FieldType& field = resultType.fields_[index];
    440  StorageType ty = field.type.storageType();
    441  return ToJSValue(cx, fieldIndexToAddress(index), ty, val);
    442 }
    443 
    444 /* static */
    445 uint8_t* WasmStructObject::fieldIndexToAddress(uint32_t fieldIndex) {
    446  const wasm::SuperTypeVector* stv = superTypeVector_;
    447  const wasm::TypeDef* typeDef = stv->typeDef();
    448  MOZ_ASSERT(typeDef->superTypeVector() == stv);
    449  const wasm::StructType& structType = typeDef->structType();
    450  const wasm::FieldAccessPathVector& fieldAccessPaths =
    451      structType.fieldAccessPaths_;
    452  MOZ_RELEASE_ASSERT(fieldIndex < fieldAccessPaths.length());
    453  wasm::FieldAccessPath path = fieldAccessPaths[fieldIndex];
    454  uint32_t ilOffset = path.ilOffset();
    455  MOZ_RELEASE_ASSERT(ilOffset != wasm::StructType::InvalidOffset);
    456  if (MOZ_LIKELY(!path.hasOOL())) {
    457    return (uint8_t*)this + ilOffset;
    458  }
    459  uint8_t* oolBlock = *(uint8_t**)((uint8_t*)this + ilOffset);
    460  uint32_t oolOffset = path.oolOffset();
    461  MOZ_RELEASE_ASSERT(oolOffset != wasm::StructType::InvalidOffset);
    462  return oolBlock + oolOffset;
    463 }
    464 
    465 /* static */
    466 void WasmStructObject::obj_trace(JSTracer* trc, JSObject* object) {
    467  WasmStructObject& structObj = object->as<WasmStructObject>();
    468 
    469  const auto& structType = structObj.typeDef().structType();
    470  for (uint32_t offset : structType.inlineTraceOffsets_) {
    471    AnyRef* fieldPtr = reinterpret_cast<AnyRef*>((uint8_t*)&structObj + offset);
    472    TraceManuallyBarrieredEdge(trc, fieldPtr, "wasm-struct-field");
    473  }
    474  if (MOZ_UNLIKELY(structType.totalSizeOOL_ > 0)) {
    475    uint8_t** addressOfOOLPtr = structObj.addressOfOOLPointer();
    476    TraceBufferEdge(trc, &structObj, addressOfOOLPtr,
    477                    "WasmStructObject outline data");
    478    uint8_t* oolBase = *addressOfOOLPtr;
    479    for (uint32_t offset : structType.outlineTraceOffsets_) {
    480      AnyRef* fieldPtr = reinterpret_cast<AnyRef*>(oolBase + offset);
    481      TraceManuallyBarrieredEdge(trc, fieldPtr, "wasm-struct-field");
    482    }
    483  }
    484 }
    485 
    486 /* static */
    487 size_t WasmStructObject::obj_moved(JSObject* objNew, JSObject* objOld) {
    488  // This gets called only for struct objects that have an OOL area.  Compare
    489  // WasmStructObjectInlineClassExt vs WasmStructObjectOutlineClassExt below.
    490  MOZ_ASSERT(objNew != objOld);
    491 
    492  WasmStructObject& structNew = objNew->as<WasmStructObject>();
    493  WasmStructObject& structOld = objOld->as<WasmStructObject>();
    494  MOZ_ASSERT(structNew.hasOOLPointer() && structOld.hasOOLPointer());
    495 
    496  const TypeDef* typeDefNew = &structNew.typeDef();
    497  mozilla::DebugOnly<const TypeDef*> typeDefOld = &structOld.typeDef();
    498  MOZ_ASSERT(typeDefNew == typeDefOld);
    499  MOZ_ASSERT(typeDefNew->isStructType());
    500  MOZ_ASSERT(typeDefOld == typeDefNew);
    501 
    502  // At this point, the object has been copied, but the OOL storage area has
    503  // not been copied, nor has the OOL pointer been updated.  Hence:
    504  MOZ_ASSERT(structNew.getOOLPointer() == structOld.getOOLPointer());
    505 
    506  bool newIsInNursery = IsInsideNursery(objNew);
    507  bool oldIsInNursery = IsInsideNursery(objOld);
    508 
    509  // Tenured -> Tenured
    510  if (!oldIsInNursery && !newIsInNursery) {
    511    // The object already was in the tenured heap and has merely been moved
    512    // somewhere else in the the tenured heap.  This isn't interesting to us.
    513    return 0;
    514  }
    515 
    516  // Tenured -> Nursery: this transition isn't possible.
    517  MOZ_RELEASE_ASSERT(oldIsInNursery);
    518 
    519  // Nursery -> Nursery and Nursery -> Tenured
    520  // The object is being moved, either within the nursery or from the nursery
    521  // to the tenured heap.  Either way, we have to ask the nursery if it wants
    522  // to move the OOL block too, and if so set up a forwarding record for it.
    523 
    524  const StructType& structType = typeDefNew->structType();
    525  uint32_t outlineBytes = structType.totalSizeOOL_;
    526  // These must always agree.
    527  MOZ_ASSERT((outlineBytes > 0) == structNew.hasOOLPointer());
    528  // Because the WasmStructObjectInlineClassExt doesn't reference this
    529  // method; only WasmStructObjectOutlineClassExt does.
    530  MOZ_ASSERT(outlineBytes > 0);
    531 
    532  // Ask the nursery if it wants to relocate the OOL area, and if so capture
    533  // its new location in `addressOfOOLPointerNew`.
    534  Nursery& nursery = structNew.runtimeFromMainThread()->gc.nursery();
    535  uint8_t** addressOfOOLPointerNew = structNew.addressOfOOLPointer();
    536  nursery.maybeMoveBufferOnPromotion(addressOfOOLPointerNew, objNew,
    537                                     outlineBytes);
    538 
    539  // Set up forwarding for the OOL area.  Use indirect forwarding.  As in
    540  // WasmArrayObject::obj_moved, if the call to `.setForwardingPointer..` OOMs,
    541  // there's no way to recover.
    542  uint8_t* oolPointerOld = structOld.getOOLPointer();
    543  uint8_t* oolPointerNew = structNew.getOOLPointer();
    544  if (oolPointerOld != oolPointerNew) {
    545    nursery.setForwardingPointerWhileTenuring(oolPointerOld, oolPointerNew,
    546                                              /*direct=*/false);
    547  }
    548 
    549  return 0;
    550 }
    551 
    552 void WasmStructObject::storeVal(const Val& val, uint32_t fieldIndex) {
    553  const StructType& structType = typeDef().structType();
    554  MOZ_ASSERT(fieldIndex < structType.fields_.length());
    555 
    556  StorageType fieldType = structType.fields_[fieldIndex].type;
    557  uint8_t* data = fieldIndexToAddress(fieldIndex);
    558 
    559  WriteValTo(this, val, fieldType, data);
    560 }
    561 
    562 static const JSClassOps WasmStructObjectOutlineClassOps = {
    563    nullptr, /* addProperty */
    564    nullptr, /* delProperty */
    565    nullptr, /* enumerate   */
    566    WasmGcObject::obj_newEnumerate,
    567    nullptr, /* resolve     */
    568    nullptr, /* mayResolve  */
    569    nullptr, /* finalize    */
    570    nullptr, /* call        */
    571    nullptr, /* construct   */
    572    WasmStructObject::obj_trace,
    573 };
    574 static const ClassExtension WasmStructObjectOutlineClassExt = {
    575    WasmStructObject::obj_moved, /* objectMovedOp */
    576 };
    577 const JSClass WasmStructObject::classOutline_ = {
    578    "WasmStructObject",
    579    JSClass::NON_NATIVE | JSCLASS_DELAY_METADATA_BUILDER,
    580    &WasmStructObjectOutlineClassOps,
    581    JS_NULL_CLASS_SPEC,
    582    &WasmStructObjectOutlineClassExt,
    583    &WasmGcObject::objectOps_,
    584 };
    585 
    586 // Structs that only have inline data get a different class without a
    587 // finalizer. This class should otherwise be identical to the class for
    588 // structs with outline data.
    589 static const JSClassOps WasmStructObjectInlineClassOps = {
    590    nullptr, /* addProperty */
    591    nullptr, /* delProperty */
    592    nullptr, /* enumerate   */
    593    WasmGcObject::obj_newEnumerate,
    594    nullptr, /* resolve     */
    595    nullptr, /* mayResolve  */
    596    nullptr, /* finalize    */
    597    nullptr, /* call        */
    598    nullptr, /* construct   */
    599    WasmStructObject::obj_trace,
    600 };
    601 static const ClassExtension WasmStructObjectInlineClassExt = {
    602    nullptr, /* objectMovedOp */
    603 };
    604 const JSClass WasmStructObject::classInline_ = {
    605    "WasmStructObject",
    606    JSClass::NON_NATIVE | JSCLASS_DELAY_METADATA_BUILDER,
    607    &WasmStructObjectInlineClassOps,
    608    JS_NULL_CLASS_SPEC,
    609    &WasmStructObjectInlineClassExt,
    610    &WasmGcObject::objectOps_,
    611 };