WasmSerialize.cpp (53714B)
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/WasmSerialize.h" 8 9 #include "mozilla/EnumeratedRange.h" 10 #include "mozilla/Maybe.h" 11 #include "mozilla/RefPtr.h" 12 #include "mozilla/Try.h" 13 #include "mozilla/Vector.h" 14 15 #include <cstdint> 16 #include <cstring> 17 #include <type_traits> 18 #include <utility> 19 20 #include "jit/ProcessExecutableMemory.h" 21 #include "js/StreamConsumer.h" 22 #include "wasm/WasmCode.h" 23 #include "wasm/WasmCodegenTypes.h" 24 #include "wasm/WasmGC.h" 25 #include "wasm/WasmInitExpr.h" 26 #include "wasm/WasmModule.h" 27 #include "wasm/WasmModuleTypes.h" 28 #include "wasm/WasmTypeDef.h" 29 #include "wasm/WasmValType.h" 30 #include "wasm/WasmValue.h" 31 32 using namespace js; 33 using namespace js::wasm; 34 35 using mozilla::Err; 36 using mozilla::Maybe; 37 using mozilla::Ok; 38 39 namespace js { 40 namespace wasm { 41 42 // The following assert is used as a tripwire for for new fields being added to 43 // types. If this assertion is broken by your code, verify the serialization 44 // code is correct, and then update the assertion. 45 // 46 // We only check serialized type sizes on one 'golden' platform. The platform 47 // is arbitrary, but must remain consistent. The platform should be as specific 48 // as possible so these assertions don't erroneously fire. Checkout the 49 // definition of ENABLE_WASM_VERIFY_SERIALIZATION_FOR_SIZE in js/moz.configure 50 // for the current platform. 51 // 52 // If this mechanism becomes a hassle, we can investigate other methods of 53 // achieving the same goal. 54 #if defined(ENABLE_WASM_VERIFY_SERIALIZATION_FOR_SIZE) 55 56 template <typename T, size_t Size> 57 struct Tripwire { 58 // The following will print a compile error that contains the size of type, 59 // and can be used for updating the assertion to the correct size. 60 static char (*_Error)[sizeof(T)] = 1; 61 62 // We must reference the bad declaration to work around SFINAE. 63 static const bool Value = !_Error; 64 }; 65 66 template <typename T> 67 struct Tripwire<T, sizeof(T)> { 68 static const bool Value = true; 69 }; 70 71 # define WASM_VERIFY_SERIALIZATION_FOR_SIZE(Type, Size) \ 72 static_assert(Tripwire<Type, Size>::Value); 73 74 #else 75 # define WASM_VERIFY_SERIALIZATION_FOR_SIZE(Type, Size) static_assert(true); 76 #endif 77 78 // A pointer is not cacheable POD 79 static_assert(!is_cacheable_pod<const uint8_t*>); 80 81 // A non-fixed sized array is not cacheable POD 82 static_assert(!is_cacheable_pod<uint8_t[]>); 83 84 // Cacheable POD is not inherited 85 struct TestPodBase {}; 86 WASM_DECLARE_CACHEABLE_POD(TestPodBase); 87 struct InheritTestPodBase : public TestPodBase {}; 88 static_assert(is_cacheable_pod<TestPodBase> && 89 !is_cacheable_pod<InheritTestPodBase>); 90 91 // Coding functions for containers and smart pointers need to know which code 92 // function to apply to the inner value. 'CodeFunc' is the common signature to 93 // be used for this. 94 template <CoderMode mode, typename T> 95 using CodeFunc = CoderResult (*)(Coder<mode>&, CoderArg<mode, T>); 96 97 // Some functions are generic for MODE_SIZE and MODE_ENCODE, but not 98 // MODE_DECODE. This assert is used to ensure that the right function overload 99 // is chosen in these cases. 100 #define STATIC_ASSERT_ENCODING_OR_SIZING \ 101 static_assert(mode == MODE_ENCODE || mode == MODE_SIZE, "wrong overload"); 102 103 CoderResult Coder<MODE_SIZE>::writeBytes(const void* unusedSrc, size_t length) { 104 size_ += length; 105 if (!size_.isValid()) { 106 return Err(OutOfMemory()); 107 } 108 return Ok(); 109 } 110 111 CoderResult Coder<MODE_ENCODE>::writeBytes(const void* src, size_t length) { 112 MOZ_RELEASE_ASSERT(buffer_ + length <= end_); 113 memcpy(buffer_, src, length); 114 buffer_ += length; 115 return Ok(); 116 } 117 118 CoderResult Coder<MODE_DECODE>::readBytes(void* dest, size_t length) { 119 MOZ_RELEASE_ASSERT(buffer_ + length <= end_); 120 memcpy(dest, buffer_, length); 121 buffer_ += length; 122 return Ok(); 123 } 124 125 CoderResult Coder<MODE_DECODE>::readBytesRef(size_t length, 126 const uint8_t** bytesBegin) { 127 MOZ_RELEASE_ASSERT(buffer_ + length <= end_); 128 *bytesBegin = buffer_; 129 buffer_ += length; 130 return Ok(); 131 } 132 133 // Cacheable POD coding functions 134 135 template <CoderMode mode, typename T, 136 typename = std::enable_if_t<is_cacheable_pod<T>>, 137 typename = std::enable_if_t<mode == MODE_DECODE>> 138 CoderResult CodePod(Coder<mode>& coder, T* item) { 139 return coder.readBytes((void*)item, sizeof(T)); 140 } 141 142 template <CoderMode mode, typename T, 143 typename = std::enable_if_t<is_cacheable_pod<T>>, 144 typename = std::enable_if_t<mode != MODE_DECODE>> 145 CoderResult CodePod(Coder<mode>& coder, const T* item) { 146 STATIC_ASSERT_ENCODING_OR_SIZING; 147 return coder.writeBytes((const void*)item, sizeof(T)); 148 } 149 150 // "Magic Marker". Use to sanity check the serialization process. 151 152 enum class Marker : uint32_t { 153 LinkData = 0x49102278, 154 Imports, 155 Exports, 156 DataSegments, 157 ElemSegments, 158 CustomSections, 159 Code, 160 Metadata, 161 ModuleMetadata, 162 CodeMetadata, 163 CodeBlock 164 }; 165 166 template <CoderMode mode> 167 CoderResult Magic(Coder<mode>& coder, Marker item) { 168 if constexpr (mode == MODE_DECODE) { 169 // Assert the specified marker is in the binary 170 Marker decoded; 171 MOZ_TRY(CodePod(coder, &decoded)); 172 MOZ_RELEASE_ASSERT(decoded == item); 173 return Ok(); 174 } else { 175 // Encode the specified marker in the binary 176 return CodePod(coder, &item); 177 } 178 } 179 180 // Coding function for a nullable pointer 181 // 182 // These functions will only code the inner value is not null. The 183 // coding function to use for the inner value is specified by a template 184 // parameter. 185 186 template <CoderMode _, typename T, CodeFunc<MODE_DECODE, T> CodeT> 187 CoderResult CodeNullablePtr(Coder<MODE_DECODE>& coder, T* item) { 188 // Decode 'isNonNull' 189 uint8_t isNonNull; 190 MOZ_TRY(CodePod(coder, &isNonNull)); 191 192 if (isNonNull == 1) { 193 // Code the inner type 194 MOZ_TRY(CodeT(coder, item)); 195 } else { 196 // Initialize to nullptr 197 *item = nullptr; 198 } 199 return Ok(); 200 } 201 202 template <CoderMode mode, typename T, CodeFunc<mode, T> CodeT> 203 CoderResult CodeNullablePtr(Coder<mode>& coder, const T* item) { 204 STATIC_ASSERT_ENCODING_OR_SIZING; 205 206 // Encode or size 'isNonNull' 207 const uint8_t isNonNull = *item != nullptr; 208 MOZ_TRY(CodePod(coder, &isNonNull)); 209 210 if (isNonNull) { 211 // Encode or size the inner value 212 MOZ_TRY(CodeT(coder, item)); 213 } 214 return Ok(); 215 } 216 217 // mozilla::Maybe coding functions 218 // 219 // These functions will only code the inner value if Maybe.isSome(). The 220 // coding function to use for the inner value is specified by a template 221 // parameter. 222 223 template <CoderMode _, typename T, CodeFunc<MODE_DECODE, T> CodeT> 224 CoderResult CodeMaybe(Coder<MODE_DECODE>& coder, Maybe<T>* item) { 225 // Decode 'isSome()' 226 uint8_t isSome; 227 MOZ_TRY(CodePod(coder, &isSome)); 228 229 if (isSome == 1) { 230 // Initialize to Some with default constructor 231 item->emplace(); 232 // Code the inner type 233 MOZ_TRY(CodeT(coder, item->ptr())); 234 } else { 235 // Initialize to nothing 236 *item = mozilla::Nothing(); 237 } 238 return Ok(); 239 } 240 241 template <CoderMode mode, typename T, CodeFunc<mode, T> CodeT> 242 CoderResult CodeMaybe(Coder<mode>& coder, const Maybe<T>* item) { 243 STATIC_ASSERT_ENCODING_OR_SIZING; 244 245 // Encode or size 'isSome()' 246 const uint8_t isSome = item->isSome() ? 1 : 0; 247 MOZ_TRY(CodePod(coder, &isSome)); 248 249 if (item->isSome()) { 250 // Encode or size the inner value 251 MOZ_TRY(CodeT(coder, item->ptr())); 252 } 253 return Ok(); 254 } 255 256 // Cacheable POD mozilla::Vector coding functions 257 // 258 // These functions are only available if the element type is cacheable POD. In 259 // this case, the whole contents of the vector are copied directly to/from the 260 // buffer. 261 262 template <CoderMode mode, typename T, size_t N, 263 typename = std::enable_if_t<is_cacheable_pod<T>>, 264 typename = std::enable_if_t<mode == MODE_DECODE>> 265 CoderResult CodePodVector(Coder<mode>& coder, 266 Vector<T, N, SystemAllocPolicy>* item) { 267 // Decode the length 268 size_t length; 269 MOZ_TRY(CodePod(coder, &length)); 270 271 // Prepare to copy into the vector 272 if (!item->initLengthUninitialized(length)) { 273 return Err(OutOfMemory()); 274 } 275 276 // Copy directly from the buffer to the vector 277 const size_t byteLength = length * sizeof(T); 278 return coder.readBytes((void*)item->begin(), byteLength); 279 } 280 281 template <CoderMode mode, typename T, size_t N, 282 typename = std::enable_if_t<is_cacheable_pod<T>>, 283 typename = std::enable_if_t<mode != MODE_DECODE>> 284 CoderResult CodePodVector(Coder<mode>& coder, 285 const Vector<T, N, SystemAllocPolicy>* item) { 286 STATIC_ASSERT_ENCODING_OR_SIZING; 287 288 // Encode the length 289 const size_t length = item->length(); 290 MOZ_TRY(CodePod(coder, &length)); 291 292 // Copy directly from the vector to the buffer 293 const size_t byteLength = length * sizeof(T); 294 return coder.writeBytes((const void*)item->begin(), byteLength); 295 } 296 297 // Non-cacheable-POD mozilla::Vector coding functions 298 // 299 // These functions implement the general case of coding a vector of some type. 300 // The coding function to use on the vector elements is provided through a 301 // template parameter. 302 303 template <CoderMode _, typename T, CodeFunc<MODE_DECODE, T> CodeT, size_t N, 304 typename std::enable_if_t<!is_cacheable_pod<T>, bool> = true> 305 CoderResult CodeVector(Coder<MODE_DECODE>& coder, 306 Vector<T, N, SystemAllocPolicy>* item) { 307 // Decode the length 308 size_t length; 309 MOZ_TRY(CodePod(coder, &length)); 310 311 // Attempt to grow the buffer to length, this will default initialize each 312 // element 313 if (!item->resize(length)) { 314 return Err(OutOfMemory()); 315 } 316 317 // Decode each child element from the buffer 318 for (auto iter = item->begin(); iter != item->end(); iter++) { 319 MOZ_TRY(CodeT(coder, iter)); 320 } 321 return Ok(); 322 } 323 324 template <CoderMode mode, typename T, CodeFunc<mode, T> CodeT, size_t N, 325 typename std::enable_if_t<!is_cacheable_pod<T>, bool> = true> 326 CoderResult CodeVector(Coder<mode>& coder, 327 const Vector<T, N, SystemAllocPolicy>* item) { 328 STATIC_ASSERT_ENCODING_OR_SIZING; 329 330 // Encode the length 331 const size_t length = item->length(); 332 MOZ_TRY(CodePod(coder, &length)); 333 334 // Encode each child element 335 for (auto iter = item->begin(); iter != item->end(); iter++) { 336 MOZ_TRY(CodeT(coder, iter)); 337 } 338 return Ok(); 339 } 340 341 // This function implements encoding and decoding of RefPtr<T>. A coding 342 // function is provided for the inner value through a template parameter. 343 // 344 // The special handling of const qualification allows a RefPtr<const T> to be 345 // decoded correctly. 346 template <CoderMode mode, typename T, 347 CodeFunc<mode, std::remove_const_t<T>> CodeT> 348 CoderResult CodeRefPtr(Coder<mode>& coder, CoderArg<mode, RefPtr<T>> item) { 349 if constexpr (mode == MODE_DECODE) { 350 // The RefPtr should not be initialized yet 351 MOZ_ASSERT(!item->get()); 352 353 // Allocate and default construct the inner type 354 auto* allocated = js_new<std::remove_const_t<T>>(); 355 if (!allocated) { 356 return Err(OutOfMemory()); 357 } 358 359 // Initialize the RefPtr 360 *item = allocated; 361 362 // Decode the inner type 363 MOZ_TRY(CodeT(coder, allocated)); 364 return Ok(); 365 } else { 366 // Encode the inner type 367 return CodeT(coder, item->get()); 368 } 369 } 370 371 // The same as CodeRefPtr, but allowing for nullable pointers. 372 template <CoderMode mode, typename T, 373 CodeFunc<mode, std::remove_const_t<T>> CodeT> 374 CoderResult CodeNullableRefPtr(Coder<mode>& coder, 375 CoderArg<mode, RefPtr<T>> item) { 376 if constexpr (mode == MODE_DECODE) { 377 uint32_t isNull; 378 MOZ_TRY(CodePod(coder, &isNull)); 379 if (isNull == 0) { 380 MOZ_ASSERT(item->get() == nullptr); 381 return Ok(); 382 } 383 return CodeRefPtr<mode, T, CodeT>(coder, item); 384 } else { 385 uint32_t isNull = !!item->get() ? 1 : 0; 386 MOZ_TRY(CodePod(coder, &isNull)); 387 if (isNull == 0) { 388 return Ok(); 389 } 390 return CodeRefPtr<mode, T, CodeT>(coder, item); 391 } 392 } 393 394 // This function implements encoding and decoding of UniquePtr<T>. 395 // A coding function is provided for the inner value as a function parameter. 396 template <CoderMode mode, typename T, 397 CodeFunc<mode, std::remove_const_t<T>> CodeT> 398 CoderResult CodeUniquePtr(Coder<mode>& coder, 399 CoderArg<mode, UniquePtr<T>> item) { 400 if constexpr (mode == MODE_DECODE) { 401 // The UniquePtr should not be initialized yet 402 MOZ_ASSERT(!item->get()); 403 404 // Allocate and default construct the inner type 405 auto allocated = js::MakeUnique<std::remove_const_t<T>>(); 406 if (!allocated.get()) { 407 return Err(OutOfMemory()); 408 } 409 410 // Decode the inner type 411 MOZ_TRY(CodeT(coder, allocated.get())); 412 413 // Initialize the UniquePtr 414 *item = std::move(allocated); 415 return Ok(); 416 } else { 417 // Encode the inner type 418 return CodeT(coder, item->get()); 419 } 420 } 421 422 // UniqueChars coding functions 423 424 static size_t StringLengthWithNullChar(const char* chars) { 425 return chars ? strlen(chars) + 1 : 0; 426 } 427 428 CoderResult CodeUniqueChars(Coder<MODE_DECODE>& coder, UniqueChars* item) { 429 uint32_t lengthWithNullChar; 430 MOZ_TRY(CodePod(coder, &lengthWithNullChar)); 431 432 // Decode the bytes, if any 433 if (lengthWithNullChar) { 434 item->reset(js_pod_malloc<char>(lengthWithNullChar)); 435 if (!item->get()) { 436 return Err(OutOfMemory()); 437 } 438 return coder.readBytes((char*)item->get(), lengthWithNullChar); 439 } 440 441 // If there were no bytes to write, the string should be null 442 MOZ_ASSERT(!item->get()); 443 return Ok(); 444 } 445 446 template <CoderMode mode> 447 CoderResult CodeUniqueChars(Coder<mode>& coder, const UniqueChars* item) { 448 WASM_VERIFY_SERIALIZATION_FOR_SIZE(UniqueChars, 8); 449 STATIC_ASSERT_ENCODING_OR_SIZING; 450 451 // Encode the length 452 const uint32_t lengthWithNullChar = StringLengthWithNullChar(item->get()); 453 MOZ_TRY(CodePod(coder, &lengthWithNullChar)); 454 455 // Write the bytes, if any 456 if (lengthWithNullChar) { 457 return coder.writeBytes((const void*)item->get(), lengthWithNullChar); 458 } 459 460 // If there were no bytes to write, the string should be null 461 MOZ_ASSERT(!item->get()); 462 return Ok(); 463 } 464 465 // Code a CacheableChars. This just forwards to UniqueChars, as that's the 466 // only data in the class, via inheritance. 467 template <CoderMode mode> 468 CoderResult CodeCacheableChars(Coder<mode>& coder, 469 CoderArg<mode, CacheableChars> item) { 470 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::CacheableChars, 8); 471 return CodeUniqueChars(coder, (UniqueChars*)item); 472 } 473 474 // Code a ShareableChars. This functions only needs to forward to the inner 475 // unique chars. 476 template <CoderMode mode> 477 CoderResult CodeShareableChars(Coder<mode>& coder, 478 CoderArg<mode, ShareableChars> item) { 479 return CodeUniqueChars(coder, &item->chars); 480 } 481 482 // Code a CacheableName 483 template <CoderMode mode> 484 CoderResult CodeCacheableName(Coder<mode>& coder, 485 CoderArg<mode, CacheableName> item) { 486 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::CacheableName, 40); 487 MOZ_TRY(CodePodVector(coder, &item->bytes_)); 488 return Ok(); 489 } 490 491 // Code a ShareableBytes. 492 template <CoderMode mode> 493 CoderResult CodeShareableBytes(Coder<mode>& coder, 494 CoderArg<mode, ShareableBytes> item) { 495 return CodePodVector<mode>(coder, &item->vector); 496 } 497 498 // WasmValType.h 499 500 /* static */ 501 SerializableTypeCode SerializableTypeCode::serialize(PackedTypeCode ptc, 502 const TypeContext& types) { 503 SerializableTypeCode stc = {}; 504 stc.typeCode = PackedRepr(ptc.typeCode()); 505 stc.typeIndex = ptc.typeDef() ? types.indexOf(*ptc.typeDef()) 506 : SerializableTypeCode::NoTypeIndex; 507 stc.nullable = ptc.isNullable(); 508 return stc; 509 } 510 511 PackedTypeCode SerializableTypeCode::deserialize(const TypeContext& types) { 512 if (typeIndex == SerializableTypeCode::NoTypeIndex) { 513 return PackedTypeCode::pack(TypeCode(typeCode), nullable); 514 } 515 const TypeDef* typeDef = &types.type(typeIndex); 516 return PackedTypeCode::pack(TypeCode(typeCode), typeDef, nullable); 517 } 518 519 template <CoderMode mode> 520 CoderResult CodePackedTypeCode(Coder<mode>& coder, 521 CoderArg<mode, PackedTypeCode> item) { 522 if constexpr (mode == MODE_DECODE) { 523 SerializableTypeCode stc; 524 MOZ_TRY(CodePod(coder, &stc)); 525 *item = stc.deserialize(*coder.types_); 526 return Ok(); 527 } else if constexpr (mode == MODE_SIZE) { 528 return coder.writeBytes(nullptr, sizeof(SerializableTypeCode)); 529 } else { 530 SerializableTypeCode stc = 531 SerializableTypeCode::serialize(*item, *coder.types_); 532 return CodePod(coder, &stc); 533 } 534 } 535 536 template <CoderMode mode, typename T> 537 CoderResult CodeTypeDefRef_Impl(Coder<mode>& coder, CoderArg<mode, T> item) { 538 static constexpr uint32_t NullTypeIndex = UINT32_MAX; 539 static_assert(NullTypeIndex > MaxTypes, "invariant"); 540 541 if constexpr (mode == MODE_DECODE) { 542 uint32_t typeIndex; 543 MOZ_TRY(CodePod(coder, &typeIndex)); 544 if (typeIndex != NullTypeIndex) { 545 *item = &coder.types_->type(typeIndex); 546 } 547 return Ok(); 548 } else if constexpr (mode == MODE_SIZE) { 549 return coder.writeBytes(nullptr, sizeof(uint32_t)); 550 } else { 551 uint32_t typeIndex = !*item ? NullTypeIndex : coder.types_->indexOf(**item); 552 return CodePod(coder, &typeIndex); 553 } 554 } 555 556 template <CoderMode mode> 557 CoderResult CodeTypeDefRef(Coder<mode>& coder, 558 CoderArg<mode, const TypeDef*> item) { 559 return CodeTypeDefRef_Impl<mode, const TypeDef*>(coder, item); 560 } 561 562 template <CoderMode mode> 563 CoderResult CodeTypeDefRef(Coder<mode>& coder, 564 CoderArg<mode, SharedTypeDef> item) { 565 return CodeTypeDefRef_Impl<mode, SharedTypeDef>(coder, item); 566 } 567 568 template <CoderMode mode> 569 CoderResult CodeValType(Coder<mode>& coder, CoderArg<mode, ValType> item) { 570 return CodePackedTypeCode(coder, item->addressOfPacked()); 571 } 572 573 template <CoderMode mode> 574 CoderResult CodeStorageType(Coder<mode>& coder, 575 CoderArg<mode, StorageType> item) { 576 return CodePackedTypeCode(coder, item->addressOfPacked()); 577 } 578 579 template <CoderMode mode> 580 CoderResult CodeRefType(Coder<mode>& coder, CoderArg<mode, RefType> item) { 581 return CodePackedTypeCode(coder, item->addressOfPacked()); 582 } 583 584 // WasmValue.h 585 586 template <CoderMode mode> 587 CoderResult CodeLitVal(Coder<mode>& coder, CoderArg<mode, LitVal> item) { 588 MOZ_TRY(CodeValType(coder, &item->type_)); 589 MOZ_TRY(CodePod(coder, &item->cell_)); 590 return Ok(); 591 } 592 593 // WasmInitExpr.h 594 595 template <CoderMode mode> 596 CoderResult CodeInitExpr(Coder<mode>& coder, CoderArg<mode, InitExpr> item) { 597 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::InitExpr, 80); 598 MOZ_TRY(CodePod(coder, &item->kind_)); 599 MOZ_TRY(CodeValType(coder, &item->type_)); 600 switch (item->kind_) { 601 case InitExprKind::Literal: 602 MOZ_TRY(CodeLitVal(coder, &item->literal_)); 603 break; 604 case InitExprKind::Variable: 605 MOZ_TRY(CodePodVector(coder, &item->bytecode_)); 606 break; 607 default: 608 MOZ_CRASH(); 609 } 610 return Ok(); 611 } 612 613 // WasmTypeDef.h 614 615 template <CoderMode mode> 616 CoderResult CodeFuncType(Coder<mode>& coder, CoderArg<mode, FuncType> item) { 617 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::FuncType, 344); 618 MOZ_TRY((CodeVector<mode, ValType, &CodeValType<mode>>(coder, &item->args_))); 619 MOZ_TRY( 620 (CodeVector<mode, ValType, &CodeValType<mode>>(coder, &item->results_))); 621 MOZ_TRY(CodePod(coder, &item->immediateTypeId_)); 622 return Ok(); 623 } 624 625 template <CoderMode mode> 626 CoderResult CodeFieldType(Coder<mode>& coder, CoderArg<mode, FieldType> item) { 627 MOZ_TRY(CodeStorageType(coder, &item->type)); 628 MOZ_TRY(CodePod(coder, &item->isMutable)); 629 return Ok(); 630 } 631 632 template <CoderMode mode> 633 CoderResult CodeStructType(Coder<mode>& coder, 634 CoderArg<mode, StructType> item) { 635 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::StructType, 192); 636 MOZ_TRY((CodeVector<mode, FieldType, &CodeFieldType<mode>>(coder, 637 &item->fields_))); 638 if constexpr (mode == MODE_DECODE) { 639 if (!item->init()) { 640 return Err(OutOfMemory()); 641 } 642 } 643 return Ok(); 644 } 645 646 template <CoderMode mode> 647 CoderResult CodeArrayType(Coder<mode>& coder, CoderArg<mode, ArrayType> item) { 648 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::ArrayType, 16); 649 MOZ_TRY(CodeFieldType(coder, &item->fieldType_)); 650 return Ok(); 651 } 652 653 template <CoderMode mode> 654 CoderResult CodeTypeDef(Coder<mode>& coder, CoderArg<mode, TypeDef> item) { 655 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TypeDef, 376); 656 MOZ_TRY(CodeTypeDefRef(coder, &item->superTypeDef_)); 657 MOZ_TRY(CodePod(coder, &item->subTypingDepth_)); 658 MOZ_TRY(CodePod(coder, &item->isFinal_)); 659 // TypeDef is a tagged union containing kind = None. This implies that 660 // we must manually initialize the variant that we decode. 661 if constexpr (mode == MODE_DECODE) { 662 MOZ_RELEASE_ASSERT(item->kind_ == TypeDefKind::None); 663 } 664 MOZ_TRY(CodePod(coder, &item->kind_)); 665 switch (item->kind_) { 666 case TypeDefKind::Struct: { 667 if constexpr (mode == MODE_DECODE) { 668 new (&item->structType_) StructType(); 669 } 670 MOZ_TRY(CodeStructType(coder, &item->structType_)); 671 break; 672 } 673 case TypeDefKind::Func: { 674 if constexpr (mode == MODE_DECODE) { 675 new (&item->funcType_) FuncType(); 676 } 677 MOZ_TRY(CodeFuncType(coder, &item->funcType_)); 678 break; 679 } 680 case TypeDefKind::Array: { 681 if constexpr (mode == MODE_DECODE) { 682 new (&item->arrayType_) ArrayType(); 683 } 684 MOZ_TRY(CodeArrayType(coder, &item->arrayType_)); 685 break; 686 } 687 case TypeDefKind::None: { 688 break; 689 } 690 default: 691 MOZ_ASSERT_UNREACHABLE(); 692 } 693 return Ok(); 694 } 695 696 using RecGroupIndexMap = 697 HashMap<const RecGroup*, uint32_t, PointerHasher<const RecGroup*>, 698 SystemAllocPolicy>; 699 700 template <CoderMode mode> 701 CoderResult CodeTypeContext(Coder<mode>& coder, 702 CoderArg<mode, TypeContext> item) { 703 if constexpr (mode == MODE_DECODE) { 704 // Decoding type definitions needs to reference the type context of the 705 // module 706 MOZ_ASSERT(!coder.types_); 707 coder.types_ = item; 708 709 // Decode the number of recursion groups in the module 710 uint32_t numRecGroups; 711 MOZ_TRY(CodePod(coder, &numRecGroups)); 712 713 // Decode each recursion group 714 for (uint32_t recGroupIndex = 0; recGroupIndex < numRecGroups; 715 recGroupIndex++) { 716 // Decode if this recursion group is equivalent to a previous recursion 717 // group 718 uint32_t canonRecGroupIndex; 719 MOZ_TRY(CodePod(coder, &canonRecGroupIndex)); 720 MOZ_RELEASE_ASSERT(canonRecGroupIndex <= recGroupIndex); 721 722 // If the decoded index is not ours, we must re-use the previous decoded 723 // recursion group. 724 if (canonRecGroupIndex != recGroupIndex) { 725 SharedRecGroup recGroup = item->groups()[canonRecGroupIndex]; 726 if (!item->addRecGroup(recGroup)) { 727 return Err(OutOfMemory()); 728 } 729 continue; 730 } 731 732 // Decode the number of types in the recursion group 733 uint32_t numTypes; 734 MOZ_TRY(CodePod(coder, &numTypes)); 735 736 MutableRecGroup recGroup = item->startRecGroup(numTypes); 737 if (!recGroup) { 738 return Err(OutOfMemory()); 739 } 740 741 // Decode the type definitions 742 for (uint32_t groupTypeIndex = 0; groupTypeIndex < numTypes; 743 groupTypeIndex++) { 744 MOZ_TRY(CodeTypeDef(coder, &recGroup->type(groupTypeIndex))); 745 } 746 747 // Finish the recursion group 748 if (!item->endRecGroup()) { 749 return Err(OutOfMemory()); 750 } 751 } 752 } else { 753 // Encode the number of recursion groups in the module 754 uint32_t numRecGroups = item->groups().length(); 755 MOZ_TRY(CodePod(coder, &numRecGroups)); 756 757 // We must be careful to only encode every unique recursion group only once 758 // and in module order. The reason for this is that encoding type def 759 // references uses the module type index map, which only stores the first 760 // type index a type was canonicalized to. 761 // 762 // Using this map to encode both recursion groups would turn the following 763 // type section from: 764 // 765 // 0: (type (struct (field 0))) 766 // 1: (type (struct (field 1))) ;; identical to 0 767 // 768 // into: 769 // 770 // 0: (type (struct (field 0))) 771 // 1: (type (struct (field 0))) ;; not identical to 0! 772 RecGroupIndexMap canonRecGroups; 773 774 // Encode each recursion group 775 for (uint32_t groupIndex = 0; groupIndex < numRecGroups; groupIndex++) { 776 SharedRecGroup group = item->groups()[groupIndex]; 777 778 // Find the index of the first time this recursion group was encoded, or 779 // set it to this index if it hasn't been encoded. 780 RecGroupIndexMap::AddPtr canonRecGroupIndex = 781 canonRecGroups.lookupForAdd(group.get()); 782 if (!canonRecGroupIndex) { 783 if (!canonRecGroups.add(canonRecGroupIndex, group.get(), groupIndex)) { 784 return Err(OutOfMemory()); 785 } 786 } 787 788 // Encode the canon index for this recursion group 789 MOZ_TRY(CodePod(coder, &canonRecGroupIndex->value())); 790 791 // Don't encode this recursion group if we've already encoded it 792 if (canonRecGroupIndex->value() != groupIndex) { 793 continue; 794 } 795 796 // Encode the number of types in the recursion group 797 uint32_t numTypes = group->numTypes(); 798 MOZ_TRY(CodePod(coder, &numTypes)); 799 800 // Encode the type definitions 801 for (uint32_t i = 0; i < numTypes; i++) { 802 MOZ_TRY(CodeTypeDef(coder, &group->type(i))); 803 } 804 } 805 } 806 return Ok(); 807 } 808 809 // WasmModuleTypes.h 810 811 template <CoderMode mode> 812 CoderResult CodeImport(Coder<mode>& coder, CoderArg<mode, Import> item) { 813 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::Import, 88); 814 MOZ_TRY(CodeCacheableName(coder, &item->module)); 815 MOZ_TRY(CodeCacheableName(coder, &item->field)); 816 MOZ_TRY(CodePod(coder, &item->kind)); 817 return Ok(); 818 } 819 820 template <CoderMode mode> 821 CoderResult CodeExport(Coder<mode>& coder, CoderArg<mode, Export> item) { 822 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::Export, 48); 823 MOZ_TRY(CodeCacheableName(coder, &item->fieldName_)); 824 MOZ_TRY(CodePod(coder, &item->pod)); 825 return Ok(); 826 } 827 828 template <CoderMode mode> 829 CoderResult CodeGlobalDesc(Coder<mode>& coder, 830 CoderArg<mode, GlobalDesc> item) { 831 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::GlobalDesc, 104); 832 MOZ_TRY(CodePod(coder, &item->kind_)); 833 MOZ_TRY(CodeInitExpr(coder, &item->initial_)); 834 MOZ_TRY(CodePod(coder, &item->offset_)); 835 MOZ_TRY(CodePod(coder, &item->isMutable_)); 836 MOZ_TRY(CodePod(coder, &item->isWasm_)); 837 MOZ_TRY(CodePod(coder, &item->isExport_)); 838 MOZ_TRY(CodePod(coder, &item->importIndex_)); 839 return Ok(); 840 } 841 842 template <CoderMode mode> 843 CoderResult CodeTagType(Coder<mode>& coder, CoderArg<mode, TagType> item) { 844 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TagType, 72); 845 // We skip serializing/deserializing the size and argOffsets fields because 846 // those are computed from the argTypes field when we deserialize. 847 MOZ_TRY(CodeTypeDefRef(coder, &item->type_)); 848 if constexpr (mode == MODE_DECODE) { 849 if (!item->initialize(item->type_)) { 850 return Err(OutOfMemory()); 851 } 852 } 853 854 return Ok(); 855 } 856 857 template <CoderMode mode> 858 CoderResult CodeTagDesc(Coder<mode>& coder, CoderArg<mode, TagDesc> item) { 859 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TagDesc, 24); 860 MOZ_TRY(CodePod(coder, &item->kind)); 861 MOZ_TRY(( 862 CodeRefPtr<mode, const TagType, &CodeTagType<mode>>(coder, &item->type))); 863 MOZ_TRY(CodePod(coder, &item->isExport)); 864 return Ok(); 865 } 866 867 template <CoderMode mode> 868 CoderResult CodeModuleElemSegment(Coder<mode>& coder, 869 CoderArg<mode, ModuleElemSegment> item) { 870 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::ModuleElemSegment, 232); 871 MOZ_TRY(CodePod(coder, &item->kind)); 872 MOZ_TRY(CodePod(coder, &item->tableIndex)); 873 MOZ_TRY(CodeRefType(coder, &item->elemType)); 874 MOZ_TRY((CodeMaybe<mode, InitExpr, &CodeInitExpr<mode>>( 875 coder, &item->offsetIfActive))); 876 MOZ_TRY(CodePod(coder, &item->encoding)); 877 MOZ_TRY(CodePodVector(coder, &item->elemIndices)); 878 MOZ_TRY(CodePod(coder, &item->elemExpressions.count)); 879 MOZ_TRY(CodePodVector(coder, &item->elemExpressions.exprBytes)); 880 return Ok(); 881 } 882 883 template <CoderMode mode> 884 CoderResult CodeDataSegment(Coder<mode>& coder, 885 CoderArg<mode, DataSegment> item) { 886 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::DataSegment, 144); 887 MOZ_TRY(CodePod(coder, &item->memoryIndex)); 888 MOZ_TRY((CodeMaybe<mode, InitExpr, &CodeInitExpr<mode>>( 889 coder, &item->offsetIfActive))); 890 MOZ_TRY(CodePodVector(coder, &item->bytes)); 891 return Ok(); 892 } 893 894 template <CoderMode mode> 895 CoderResult CodeCustomSection(Coder<mode>& coder, 896 CoderArg<mode, CustomSection> item) { 897 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::CustomSection, 48); 898 MOZ_TRY(CodePodVector(coder, &item->name)); 899 MOZ_TRY((CodeRefPtr<mode, const ShareableBytes, &CodeShareableBytes<mode>>( 900 coder, &item->payload))); 901 return Ok(); 902 } 903 904 template <CoderMode mode> 905 CoderResult CodeNameSection(Coder<mode>& coder, 906 CoderArg<mode, NameSection> item) { 907 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::NameSection, 56); 908 MOZ_TRY(CodePod(coder, &item->customSectionIndex)); 909 MOZ_TRY(CodePod(coder, &item->moduleName)); 910 MOZ_TRY(CodePodVector(coder, &item->funcNames)); 911 // We do not serialize `payload` because the ModuleMetadata will do that for 912 // us. 913 return Ok(); 914 } 915 916 template <CoderMode mode> 917 CoderResult CodeTableDesc(Coder<mode>& coder, CoderArg<mode, TableDesc> item) { 918 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TableDesc, 144); 919 MOZ_TRY(CodeRefType(coder, &item->elemType)); 920 MOZ_TRY(CodePod(coder, &item->isImported)); 921 MOZ_TRY(CodePod(coder, &item->isExported)); 922 MOZ_TRY(CodePod(coder, &item->isAsmJS)); 923 MOZ_TRY(CodePod(coder, &item->limits)); 924 MOZ_TRY( 925 (CodeMaybe<mode, InitExpr, &CodeInitExpr<mode>>(coder, &item->initExpr))); 926 return Ok(); 927 } 928 929 // WasmCodegenTypes.h 930 931 template <CoderMode mode> 932 CoderResult CodeTrapSitesForKind(Coder<mode>& coder, 933 CoderArg<mode, TrapSitesForKind> item) { 934 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TrapSitesForKind, 160); 935 #ifdef DEBUG 936 MOZ_TRY(CodePodVector(coder, &item->machineInsns_)); 937 #endif 938 MOZ_TRY(CodePodVector(coder, &item->pcOffsets_)); 939 MOZ_TRY(CodePodVector(coder, &item->bytecodeOffsets_)); 940 // Inlining requires lazy tiering, which does not support serialization yet. 941 MOZ_RELEASE_ASSERT(item->inlinedCallerOffsetsMap_.empty()); 942 return Ok(); 943 } 944 945 template <CoderMode mode> 946 CoderResult CodeTrapSites(Coder<mode>& coder, CoderArg<mode, TrapSites> item) { 947 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TrapSites, 2080); 948 for (Trap trap : mozilla::MakeEnumeratedRange(Trap::Limit)) { 949 MOZ_TRY(CodeTrapSitesForKind(coder, &item->array_[trap])); 950 } 951 return Ok(); 952 } 953 954 template <CoderMode mode> 955 CoderResult CodeCallSites(Coder<mode>& coder, CoderArg<mode, CallSites> item) { 956 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::CallSites, 160); 957 MOZ_TRY(CodePodVector(coder, &item->kinds_)); 958 MOZ_TRY(CodePodVector(coder, &item->lineOrBytecodes_)); 959 MOZ_TRY(CodePodVector(coder, &item->returnAddressOffsets_)); 960 // Inlining requires lazy tiering, which does not support serialization yet. 961 MOZ_RELEASE_ASSERT(item->inlinedCallerOffsetsMap_.empty()); 962 return Ok(); 963 } 964 965 // WasmCompileArgs.h 966 967 template <CoderMode mode> 968 CoderResult CodeScriptedCaller(Coder<mode>& coder, 969 CoderArg<mode, ScriptedCaller> item) { 970 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::ScriptedCaller, 16); 971 MOZ_TRY((CodeUniqueChars(coder, &item->filename))); 972 MOZ_TRY((CodePod(coder, &item->filenameIsURL))); 973 MOZ_TRY((CodePod(coder, &item->line))); 974 return Ok(); 975 } 976 977 template <CoderMode mode> 978 CoderResult CodeBuiltinModuleIds(Coder<mode>& coder, 979 CoderArg<mode, BuiltinModuleIds> item) { 980 WASM_VERIFY_SERIALIZATION_FOR_SIZE(BuiltinModuleIds, 16); 981 MOZ_TRY(CodePod(coder, &item->selfTest)); 982 MOZ_TRY(CodePod(coder, &item->intGemm)); 983 MOZ_TRY(CodePod(coder, &item->jsString)); 984 MOZ_TRY(CodePod(coder, &item->jsStringConstants)); 985 MOZ_TRY((CodeNullableRefPtr<mode, const ShareableChars, &CodeShareableChars>( 986 coder, &item->jsStringConstantsNamespace))); 987 return Ok(); 988 } 989 990 template <CoderMode mode> 991 CoderResult CodeFeatureArgs(Coder<mode>& coder, 992 CoderArg<mode, FeatureArgs> item) { 993 WASM_VERIFY_SERIALIZATION_FOR_SIZE(FeatureArgs, 32); 994 #define WASM_FEATURE(NAME, LOWER_NAME, ...) \ 995 MOZ_TRY(CodePod(coder, &item->LOWER_NAME)); 996 JS_FOR_WASM_FEATURES(WASM_FEATURE) 997 #undef WASM_FEATURE 998 MOZ_TRY(CodePod(coder, &item->sharedMemory)); 999 MOZ_TRY(CodePod(coder, &item->simd)); 1000 MOZ_TRY(CodePod(coder, &item->isBuiltinModule)); 1001 MOZ_TRY(CodeBuiltinModuleIds(coder, &item->builtinModules)); 1002 return Ok(); 1003 } 1004 1005 template <CoderMode mode> 1006 CoderResult CodeCompileArgs(Coder<mode>& coder, 1007 CoderArg<mode, CompileArgs> item) { 1008 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::CompileArgs, 72); 1009 MOZ_TRY((CodeScriptedCaller(coder, &item->scriptedCaller))); 1010 MOZ_TRY((CodeUniqueChars(coder, &item->sourceMapURL))); 1011 MOZ_TRY((CodePod(coder, &item->baselineEnabled))); 1012 MOZ_TRY((CodePod(coder, &item->ionEnabled))); 1013 MOZ_TRY((CodePod(coder, &item->debugEnabled))); 1014 MOZ_TRY((CodePod(coder, &item->forceTiering))); 1015 MOZ_TRY((CodeFeatureArgs(coder, &item->features))); 1016 return Ok(); 1017 } 1018 1019 // WasmGC.h 1020 1021 CoderResult CodeStackMap(Coder<MODE_DECODE>& coder, 1022 CoderArg<MODE_DECODE, wasm::StackMap*> item, 1023 wasm::StackMaps* stackMaps) { 1024 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::StackMap, 12); 1025 // Decode the stack map header 1026 StackMapHeader header; 1027 MOZ_TRY(CodePod(coder, &header)); 1028 1029 // Allocate a stack map for the header 1030 StackMap* map = stackMaps->create(header); 1031 if (!map) { 1032 return Err(OutOfMemory()); 1033 } 1034 1035 // Decode the bitmap into the stackmap 1036 MOZ_TRY(coder.readBytes(map->rawBitmap(), map->rawBitmapLengthInBytes())); 1037 1038 *item = map; 1039 return Ok(); 1040 } 1041 1042 template <CoderMode mode> 1043 CoderResult CodeStackMap(Coder<mode>& coder, 1044 CoderArg<mode, wasm::StackMap> item) { 1045 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::StackMap, 12); 1046 STATIC_ASSERT_ENCODING_OR_SIZING; 1047 1048 // Encode the stackmap header 1049 MOZ_TRY(CodePod(coder, &item->header)); 1050 1051 // Encode the stackmap bitmap 1052 MOZ_TRY(coder.writeBytes(item->rawBitmap(), item->rawBitmapLengthInBytes())); 1053 1054 return Ok(); 1055 } 1056 1057 CoderResult CodeStackMaps(Coder<MODE_DECODE>& coder, 1058 CoderArg<MODE_DECODE, wasm::StackMaps> item) { 1059 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::StackMaps, 200); 1060 // Decode the amount of stack maps 1061 size_t length; 1062 MOZ_TRY(CodePod(coder, &length)); 1063 1064 for (size_t i = 0; i < length; i++) { 1065 // Decode the offset 1066 uint32_t codeOffset; 1067 MOZ_TRY(CodePod(coder, &codeOffset)); 1068 1069 // Decode the stack map 1070 StackMap* map; 1071 MOZ_TRY(CodeStackMap(coder, &map, item)); 1072 1073 // Add it to the map 1074 if (!item->finalize(codeOffset, map)) { 1075 return Err(OutOfMemory()); 1076 } 1077 } 1078 1079 return Ok(); 1080 } 1081 1082 template <CoderMode mode> 1083 CoderResult CodeStackMaps(Coder<mode>& coder, 1084 CoderArg<mode, wasm::StackMaps> item) { 1085 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::StackMaps, 200); 1086 STATIC_ASSERT_ENCODING_OR_SIZING; 1087 1088 // Encode the amount of stack maps 1089 size_t length = item->length(); 1090 MOZ_TRY(CodePod(coder, &length)); 1091 1092 for (auto iter = item->codeOffsetToStackMap_.iter(); !iter.done(); 1093 iter.next()) { 1094 uint32_t codeOffset = iter.get().key(); 1095 1096 // Encode the offset 1097 MOZ_TRY(CodePod(coder, &codeOffset)); 1098 1099 // Encode the stack map 1100 MOZ_TRY(CodeStackMap(coder, iter.get().value())); 1101 } 1102 return Ok(); 1103 } 1104 1105 // WasmCode.h 1106 1107 template <CoderMode mode> 1108 CoderResult CodeSymbolicLinkArray( 1109 Coder<mode>& coder, 1110 CoderArg<mode, wasm::LinkData::SymbolicLinkArray> item) { 1111 for (SymbolicAddress address : 1112 mozilla::MakeEnumeratedRange(SymbolicAddress::Limit)) { 1113 MOZ_TRY(CodePodVector(coder, &(*item)[address])); 1114 } 1115 return Ok(); 1116 } 1117 1118 template <CoderMode mode> 1119 CoderResult CodeLinkData(Coder<mode>& coder, 1120 CoderArg<mode, wasm::LinkData> item) { 1121 // SymbolicLinkArray depends on SymbolicAddress::Limit, which is changed 1122 // often. Exclude symbolicLinks field from trip wire value calculation. 1123 WASM_VERIFY_SERIALIZATION_FOR_SIZE( 1124 wasm::LinkData, 88 + sizeof(wasm::LinkData::SymbolicLinkArray)); 1125 MOZ_TRY(CodePod(coder, &item->pod())); 1126 MOZ_TRY(CodePodVector(coder, &item->internalLinks)); 1127 MOZ_TRY(CodePodVector(coder, &item->callFarJumps)); 1128 MOZ_TRY(CodeSymbolicLinkArray(coder, &item->symbolicLinks)); 1129 return Ok(); 1130 } 1131 1132 // WasmMetadata.h 1133 1134 template <CoderMode mode> 1135 CoderResult CodeCodeMetadata(Coder<mode>& coder, 1136 CoderArg<mode, wasm::CodeMetadata> item) { 1137 // NOTE: keep the field sequence here in sync with the sequence in the 1138 // declaration of CodeMetadata. 1139 1140 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::CodeMetadata, 736); 1141 // Serialization doesn't handle asm.js or debug enabled modules 1142 MOZ_RELEASE_ASSERT(mode == MODE_SIZE || !item->isAsmJS()); 1143 1144 MOZ_TRY(Magic(coder, Marker::CodeMetadata)); 1145 1146 MOZ_TRY(CodePod(coder, &item->kind)); 1147 MOZ_TRY((CodeRefPtr<mode, const CompileArgs, &CodeCompileArgs>( 1148 coder, &item->compileArgs))); 1149 1150 MOZ_TRY(CodePod(coder, &item->numFuncImports)); 1151 MOZ_TRY(CodePod(coder, &item->funcImportsAreJS)); 1152 MOZ_TRY(CodePod(coder, &item->numGlobalImports)); 1153 1154 // We must deserialize types first so that they're available for 1155 // deserializing values that need types. 1156 MOZ_TRY( 1157 (CodeRefPtr<mode, TypeContext, &CodeTypeContext>(coder, &item->types))); 1158 MOZ_TRY(CodePodVector(coder, &item->funcs)); 1159 MOZ_TRY(( 1160 CodeVector<mode, TableDesc, &CodeTableDesc<mode>>(coder, &item->tables))); 1161 MOZ_TRY(CodePodVector(coder, &item->memories)); 1162 MOZ_TRY((CodeVector<mode, TagDesc, &CodeTagDesc<mode>>(coder, &item->tags))); 1163 MOZ_TRY((CodeVector<mode, GlobalDesc, &CodeGlobalDesc<mode>>( 1164 coder, &item->globals))); 1165 1166 MOZ_TRY((CodeMaybe<mode, uint32_t, &CodePod>(coder, &item->startFuncIndex))); 1167 1168 MOZ_TRY(( 1169 CodeVector<mode, RefType, &CodeRefType>(coder, &item->elemSegmentTypes))); 1170 1171 MOZ_TRY((CodeMaybe<mode, uint32_t, &CodePod>(coder, &item->dataCount))); 1172 MOZ_TRY((CodePodVector(coder, &item->exportedFuncIndices))); 1173 1174 // We do not serialize `asmJSSigToTableIndex` because we don't serialize 1175 // asm.js. 1176 1177 MOZ_TRY(CodePodVector(coder, &item->customSectionRanges)); 1178 1179 MOZ_TRY((CodeMaybe<mode, BytecodeRange, &CodePod>(coder, 1180 &item->codeSectionRange))); 1181 1182 MOZ_TRY((CodeMaybe<mode, NameSection, &CodeNameSection>(coder, 1183 &item->nameSection))); 1184 1185 // TODO (bug 1907645): We do not serialize branch hints yet. 1186 1187 MOZ_TRY(CodePod(coder, &item->funcDefsOffsetStart)); 1188 MOZ_TRY(CodePod(coder, &item->funcImportsOffsetStart)); 1189 MOZ_TRY(CodePod(coder, &item->funcExportsOffsetStart)); 1190 MOZ_TRY(CodePod(coder, &item->typeDefsOffsetStart)); 1191 MOZ_TRY(CodePod(coder, &item->memoriesOffsetStart)); 1192 MOZ_TRY(CodePod(coder, &item->tablesOffsetStart)); 1193 MOZ_TRY(CodePod(coder, &item->tagsOffsetStart)); 1194 MOZ_TRY(CodePod(coder, &item->instanceDataLength)); 1195 1196 if constexpr (mode == MODE_DECODE) { 1197 MOZ_ASSERT(!item->isAsmJS()); 1198 } 1199 1200 return Ok(); 1201 } 1202 1203 template <CoderMode mode> 1204 CoderResult CodeCodeTailMetadata(Coder<mode>& coder, 1205 CoderArg<mode, wasm::CodeTailMetadata> item) { 1206 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::CodeTailMetadata, 384); 1207 1208 if constexpr (mode == MODE_ENCODE) { 1209 MOZ_ASSERT(!item->debugEnabled); 1210 } 1211 1212 MOZ_TRY((CodeNullablePtr< 1213 mode, SharedBytes, 1214 &CodeRefPtr<mode, const ShareableBytes, CodeShareableBytes>>( 1215 coder, &item->codeSectionBytecode))); 1216 1217 if constexpr (mode == MODE_DECODE) { 1218 int64_t inliningBudget; 1219 MOZ_TRY(CodePod(coder, &inliningBudget)); 1220 item->inliningBudget.lock().get() = inliningBudget; 1221 } else { 1222 int64_t inliningBudget = item->inliningBudget.lock().get(); 1223 MOZ_TRY(CodePod(coder, &inliningBudget)); 1224 } 1225 1226 MOZ_TRY(CodePodVector(coder, &item->funcDefRanges)); 1227 MOZ_TRY(CodePodVector(coder, &item->funcDefFeatureUsages)); 1228 MOZ_TRY(CodePodVector(coder, &item->funcDefCallRefs)); 1229 MOZ_TRY(CodePodVector(coder, &item->funcDefAllocSites)); 1230 MOZ_TRY(CodePod(coder, &item->numCallRefMetrics)); 1231 MOZ_TRY(CodePod(coder, &item->numAllocSites)); 1232 1233 // Name section payload is handled by ModuleMetadata. 1234 1235 if constexpr (mode == MODE_DECODE) { 1236 // Initialize debugging state to disabled 1237 item->debugEnabled = false; 1238 } 1239 1240 return Ok(); 1241 } 1242 1243 template <CoderMode mode> 1244 CoderResult CodeModuleMetadata(Coder<mode>& coder, 1245 CoderArg<mode, wasm::ModuleMetadata> item) { 1246 // NOTE: keep the field sequence here in sync with the sequence in the 1247 // declaration of ModuleMetadata. 1248 1249 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::ModuleMetadata, 272); 1250 MOZ_TRY(Magic(coder, Marker::ModuleMetadata)); 1251 1252 MOZ_TRY((CodeRefPtr<mode, CodeMetadata, &CodeCodeMetadata>(coder, 1253 &item->codeMeta))); 1254 MOZ_TRY((CodeRefPtr<mode, CodeTailMetadata, CodeCodeTailMetadata>( 1255 coder, &item->codeTailMeta))); 1256 if constexpr (mode == MODE_DECODE) { 1257 item->codeTailMeta->codeMeta = item->codeMeta; 1258 } 1259 MOZ_TRY(Magic(coder, Marker::Imports)); 1260 MOZ_TRY((CodeVector<mode, Import, &CodeImport<mode>>(coder, &item->imports))); 1261 MOZ_TRY(Magic(coder, Marker::Exports)); 1262 MOZ_TRY((CodeVector<mode, Export, &CodeExport<mode>>(coder, &item->exports))); 1263 MOZ_TRY(Magic(coder, Marker::ElemSegments)); 1264 MOZ_TRY((CodeVector<mode, ModuleElemSegment, CodeModuleElemSegment<mode>>( 1265 coder, &item->elemSegments))); 1266 // not serialized: dataSegmentRanges 1267 MOZ_TRY(Magic(coder, Marker::DataSegments)); 1268 MOZ_TRY( 1269 (CodeVector<mode, SharedDataSegment, 1270 &CodeRefPtr<mode, const DataSegment, CodeDataSegment<mode>>>( 1271 coder, &item->dataSegments))); 1272 MOZ_TRY(Magic(coder, Marker::CustomSections)); 1273 MOZ_TRY((CodeVector<mode, CustomSection, &CodeCustomSection<mode>>( 1274 coder, &item->customSections))); 1275 MOZ_TRY(CodePod(coder, &item->featureUsage)); 1276 1277 // Give CodeTailMetadata a pointer to our name payload now that we've 1278 // deserialized it. 1279 if constexpr (mode == MODE_DECODE) { 1280 if (item->codeMeta->nameSection) { 1281 item->codeTailMeta->nameSectionPayload = 1282 item->customSections[item->codeMeta->nameSection->customSectionIndex] 1283 .payload; 1284 } 1285 } 1286 1287 return Ok(); 1288 } 1289 1290 // WasmCode.h 1291 1292 template <CoderMode mode> 1293 CoderResult CodeFuncToCodeRangeMap( 1294 Coder<mode>& coder, CoderArg<mode, wasm::FuncToCodeRangeMap> item) { 1295 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::FuncToCodeRangeMap, 80); 1296 MOZ_TRY(CodePod(coder, &item->startFuncIndex_)); 1297 MOZ_TRY(CodePodVector(coder, &item->funcToCodeRange_)); 1298 return Ok(); 1299 } 1300 1301 CoderResult CodeCodeBlock(Coder<MODE_DECODE>& coder, 1302 wasm::UniqueCodeBlock* item, 1303 const wasm::LinkData& linkData) { 1304 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::CodeBlock, 2784); 1305 *item = js::MakeUnique<CodeBlock>(CodeBlock::kindFromTier(Tier::Serialized)); 1306 if (!*item) { 1307 return Err(OutOfMemory()); 1308 } 1309 MOZ_TRY(Magic(coder, Marker::CodeBlock)); 1310 1311 // Decode the code byte range 1312 size_t codeBytesLength; 1313 const uint8_t* codeBytes; 1314 MOZ_TRY(CodePod(coder, &codeBytesLength)); 1315 MOZ_TRY(coder.readBytesRef(codeBytesLength, &codeBytes)); 1316 1317 // Allocate a code segment using the code bytes 1318 uint8_t* codeStart; 1319 uint32_t allocationLength; 1320 CodeSource codeSource(codeBytes, codeBytesLength, linkData, nullptr); 1321 (*item)->segment = 1322 CodeSegment::allocate(codeSource, nullptr, /* allowLastDitchGC */ true, 1323 &codeStart, &allocationLength); 1324 if (!(*item)->segment) { 1325 return Err(OutOfMemory()); 1326 } 1327 (*item)->codeBase = codeStart; 1328 (*item)->codeLength = codeSource.lengthBytes(); 1329 1330 MOZ_TRY(CodeFuncToCodeRangeMap(coder, &(*item)->funcToCodeRange)); 1331 MOZ_TRY(CodePodVector(coder, &(*item)->codeRanges)); 1332 MOZ_TRY(CodeCallSites(coder, &(*item)->callSites)); 1333 MOZ_TRY(CodeTrapSites(coder, &(*item)->trapSites)); 1334 MOZ_TRY(CodePodVector(coder, &(*item)->funcExports)); 1335 MOZ_TRY(CodeStackMaps(coder, &(*item)->stackMaps)); 1336 MOZ_TRY(CodePodVector(coder, &(*item)->tryNotes)); 1337 MOZ_TRY(CodePodVector(coder, &(*item)->codeRangeUnwindInfos)); 1338 return Ok(); 1339 } 1340 1341 template <CoderMode mode> 1342 CoderResult CodeCodeBlock(Coder<mode>& coder, 1343 CoderArg<mode, wasm::CodeBlock> item, 1344 const wasm::LinkData& linkData) { 1345 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::CodeBlock, 2784); 1346 STATIC_ASSERT_ENCODING_OR_SIZING; 1347 MOZ_TRY(Magic(coder, Marker::CodeBlock)); 1348 1349 // Encode the code bytes 1350 MOZ_TRY(CodePod(coder, &item->codeLength)); 1351 if constexpr (mode == MODE_SIZE) { 1352 // Just calculate the length of bytes written 1353 MOZ_TRY(coder.writeBytes(item->codeBase, item->codeLength)); 1354 } else { 1355 // Get the start of where the code bytes will be written 1356 uint8_t* serializedBase = coder.buffer_; 1357 // Write the code bytes 1358 MOZ_TRY(coder.writeBytes(item->codeBase, item->codeLength)); 1359 // Unlink the code bytes written to the buffer 1360 StaticallyUnlink(serializedBase, linkData); 1361 } 1362 1363 MOZ_TRY(CodeFuncToCodeRangeMap(coder, &item->funcToCodeRange)); 1364 MOZ_TRY(CodePodVector(coder, &item->codeRanges)); 1365 MOZ_TRY(CodeCallSites(coder, &item->callSites)); 1366 MOZ_TRY(CodeTrapSites(coder, &item->trapSites)); 1367 MOZ_TRY(CodePodVector(coder, &item->funcExports)); 1368 MOZ_TRY(CodeStackMaps(coder, &item->stackMaps)); 1369 MOZ_TRY(CodePodVector(coder, &item->tryNotes)); 1370 MOZ_TRY(CodePodVector(coder, &item->codeRangeUnwindInfos)); 1371 return Ok(); 1372 } 1373 1374 CoderResult CodeSharedCode(Coder<MODE_DECODE>& coder, wasm::SharedCode* item, 1375 const wasm::ModuleMetadata& moduleMeta) { 1376 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::Code, 976); 1377 1378 FuncImportVector funcImports; 1379 MOZ_TRY(CodePodVector(coder, &funcImports)); 1380 1381 UniqueCodeBlock sharedStubs; 1382 UniqueLinkData sharedStubsLinkData; 1383 MOZ_TRY((CodeUniquePtr<MODE_DECODE, LinkData, CodeLinkData>( 1384 coder, &sharedStubsLinkData))); 1385 MOZ_TRY(CodeCodeBlock(coder, &sharedStubs, *sharedStubsLinkData)); 1386 sharedStubs->sendToProfiler(*moduleMeta.codeMeta, *moduleMeta.codeTailMeta, 1387 nullptr, FuncIonPerfSpewerSpan(), 1388 FuncBaselinePerfSpewerSpan()); 1389 1390 UniqueLinkData optimizedCodeLinkData; 1391 UniqueCodeBlock optimizedCode; 1392 MOZ_TRY((CodeUniquePtr<MODE_DECODE, LinkData, CodeLinkData>( 1393 coder, &optimizedCodeLinkData))); 1394 MOZ_TRY(CodeCodeBlock(coder, &optimizedCode, *optimizedCodeLinkData)); 1395 optimizedCode->sendToProfiler(*moduleMeta.codeMeta, *moduleMeta.codeTailMeta, 1396 nullptr, FuncIonPerfSpewerSpan(), 1397 FuncBaselinePerfSpewerSpan()); 1398 1399 // Create and initialize the code 1400 MutableCode code = js_new<Code>(CompileMode::Once, *moduleMeta.codeMeta, 1401 *moduleMeta.codeTailMeta, 1402 /*codeMetaForAsmJS=*/nullptr); 1403 if (!code || !code->initialize( 1404 std::move(funcImports), std::move(sharedStubs), 1405 std::move(sharedStubsLinkData), std::move(optimizedCode), 1406 std::move(optimizedCodeLinkData), CompileAndLinkStats())) { 1407 return Err(OutOfMemory()); 1408 } 1409 1410 // not serialized: debugStubOffset_ 1411 1412 uint32_t offsetOfRequestTierUpStub = 0; 1413 MOZ_TRY(CodePod(coder, &offsetOfRequestTierUpStub)); 1414 code->setRequestTierUpStubOffset(offsetOfRequestTierUpStub); 1415 1416 uint32_t offsetOfCallRefMetricsStub = 0; 1417 MOZ_TRY(CodePod(coder, &offsetOfCallRefMetricsStub)); 1418 code->setUpdateCallRefMetricsStubOffset(offsetOfCallRefMetricsStub); 1419 1420 *item = code; 1421 return Ok(); 1422 } 1423 1424 template <CoderMode mode> 1425 CoderResult CodeSharedCode(Coder<mode>& coder, 1426 CoderArg<mode, wasm::SharedCode> item) { 1427 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::Code, 976); 1428 STATIC_ASSERT_ENCODING_OR_SIZING; 1429 // Don't encode the CodeMetadata or CodeTailMetadata, that is handled by 1430 // wasm::ModuleMetadata. 1431 MOZ_TRY(CodePodVector(coder, &(*item)->funcImports())); 1432 const CodeBlock& sharedStubsCodeBlock = (*item)->sharedStubs(); 1433 const LinkData& sharedStubsLinkData = 1434 *(*item)->codeBlockLinkData(sharedStubsCodeBlock); 1435 MOZ_TRY(CodeLinkData(coder, &sharedStubsLinkData)); 1436 MOZ_TRY(CodeCodeBlock(coder, &sharedStubsCodeBlock, sharedStubsLinkData)); 1437 const CodeBlock& optimizedCodeBlock = 1438 (*item)->completeTierCodeBlock(Tier::Serialized); 1439 const LinkData& optimizedLinkData = 1440 *(*item)->codeBlockLinkData(optimizedCodeBlock); 1441 MOZ_TRY(CodeLinkData(coder, &optimizedLinkData)); 1442 MOZ_TRY(CodeCodeBlock(coder, &optimizedCodeBlock, optimizedLinkData)); 1443 1444 // not serialized: debugStubOffset_ 1445 1446 uint32_t offsetOfRequestTierUpStub = (*item)->requestTierUpStubOffset(); 1447 MOZ_TRY(CodePod(coder, &offsetOfRequestTierUpStub)); 1448 1449 uint32_t offsetOfCallRefMetricsStub = 1450 (*item)->updateCallRefMetricsStubOffset(); 1451 MOZ_TRY(CodePod(coder, &offsetOfCallRefMetricsStub)); 1452 1453 return Ok(); 1454 } 1455 1456 // WasmModule.h 1457 1458 CoderResult CodeModule(Coder<MODE_DECODE>& coder, MutableModule* item) { 1459 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::Module, 56); 1460 JS::BuildIdCharVector currentBuildId; 1461 if (!GetOptimizedEncodingBuildId(¤tBuildId)) { 1462 return Err(OutOfMemory()); 1463 } 1464 JS::BuildIdCharVector deserializedBuildId; 1465 MOZ_TRY(CodePodVector(coder, &deserializedBuildId)); 1466 1467 MOZ_RELEASE_ASSERT(EqualContainers(currentBuildId, deserializedBuildId)); 1468 1469 MutableModuleMetadata moduleMeta; 1470 MOZ_TRY((CodeRefPtr<MODE_DECODE, ModuleMetadata, &CodeModuleMetadata>( 1471 coder, &moduleMeta))); 1472 1473 SharedCode code; 1474 MOZ_TRY(Magic(coder, Marker::Code)); 1475 MOZ_TRY(CodeSharedCode(coder, &code, *moduleMeta)); 1476 1477 *item = js_new<Module>(*moduleMeta, *code, 1478 /* loggingDeserialized = */ true); 1479 return Ok(); 1480 } 1481 1482 template <CoderMode mode> 1483 CoderResult CodeModule(Coder<mode>& coder, CoderArg<mode, Module> item) { 1484 WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::Module, 56); 1485 STATIC_ASSERT_ENCODING_OR_SIZING; 1486 MOZ_RELEASE_ASSERT(!item->code().debugEnabled()); 1487 MOZ_RELEASE_ASSERT(item->code_->hasCompleteTier(Tier::Serialized)); 1488 1489 JS::BuildIdCharVector currentBuildId; 1490 if (!GetOptimizedEncodingBuildId(¤tBuildId)) { 1491 return Err(OutOfMemory()); 1492 } 1493 MOZ_TRY(CodePodVector(coder, ¤tBuildId)); 1494 MOZ_TRY((CodeRefPtr<mode, const ModuleMetadata, &CodeModuleMetadata>( 1495 coder, &item->moduleMeta_))); 1496 MOZ_TRY(Magic(coder, Marker::Code)); 1497 MOZ_TRY(CodeSharedCode(coder, &item->code_)); 1498 return Ok(); 1499 } 1500 1501 } // namespace wasm 1502 } // namespace js 1503 1504 bool Module::canSerialize() const { 1505 // TODO(bug 1903131): JS string builtins don't support serialization 1506 // TODO(bug 1913109): lazy tiering doesn't support serialization 1507 return code_->mode() != CompileMode::LazyTiering && 1508 !codeMeta().isBuiltinModule() && 1509 codeMeta().features().builtinModules.hasNone() && 1510 !code_->debugEnabled(); 1511 } 1512 1513 static bool GetSerializedSize(const Module& module, size_t* size) { 1514 Coder<MODE_SIZE> coder(module.codeMeta().types.get()); 1515 auto result = CodeModule(coder, &module); 1516 if (result.isErr()) { 1517 return false; 1518 } 1519 *size = coder.size_.value(); 1520 return true; 1521 } 1522 1523 bool Module::serialize(Bytes* bytes) const { 1524 MOZ_RELEASE_ASSERT(canSerialize()); 1525 MOZ_RELEASE_ASSERT(code_->hasCompleteTier(Tier::Serialized)); 1526 1527 size_t serializedSize; 1528 if (!GetSerializedSize(*this, &serializedSize)) { 1529 // An error is an overflow, return false 1530 return false; 1531 } 1532 1533 // Try to allocate the destination buffer 1534 if (!bytes->resizeUninitialized(serializedSize)) { 1535 return false; 1536 } 1537 1538 Coder<MODE_ENCODE> coder(codeMeta().types.get(), bytes->begin(), 1539 serializedSize); 1540 CoderResult result = CodeModule(coder, this); 1541 if (result.isErr()) { 1542 // An error is an OOM, return false 1543 return false; 1544 } 1545 // Every byte is accounted for 1546 MOZ_RELEASE_ASSERT(coder.buffer_ == coder.end_); 1547 1548 // Clear out link data now, it's no longer needed. 1549 code().clearLinkData(); 1550 1551 return true; 1552 } 1553 1554 /* static */ 1555 MutableModule Module::deserialize(const uint8_t* begin, size_t size) { 1556 Coder<MODE_DECODE> coder(begin, size); 1557 MutableModule module; 1558 CoderResult result = CodeModule(coder, &module); 1559 if (result.isErr()) { 1560 // An error is an OOM, return nullptr 1561 return nullptr; 1562 } 1563 // Every byte is accounted for 1564 MOZ_RELEASE_ASSERT(coder.buffer_ == coder.end_); 1565 return module; 1566 } 1567 1568 void Module::initGCMallocBytesExcludingCode() { 1569 // The size doesn't have to be exact so use the serialization framework to 1570 // calculate a value. We consume all errors, as they can only be overflow and 1571 // can be ignored until the end. 1572 constexpr CoderMode MODE = MODE_SIZE; 1573 Coder<MODE> coder(codeMeta().types.get()); 1574 1575 // Add the size of the ModuleMetadata 1576 (void)CodeModuleMetadata<MODE>(coder, moduleMeta_); 1577 1578 // Overflow really shouldn't be possible here, but handle it anyways. 1579 size_t serializedSize = coder.size_.isValid() ? coder.size_.value() : 0; 1580 gcMallocBytesExcludingCode_ = sizeof(*this) + serializedSize; 1581 }