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 };