WasmTypeDef.h (52524B)
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 * 4 * Copyright 2021 Mozilla Foundation 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 #ifndef wasm_type_def_h 20 #define wasm_type_def_h 21 22 #include "mozilla/Assertions.h" 23 #include "mozilla/HashTable.h" 24 25 #include "js/RefCounted.h" 26 27 #include "wasm/WasmCodegenConstants.h" 28 #include "wasm/WasmCompileArgs.h" 29 #include "wasm/WasmConstants.h" 30 #include "wasm/WasmSerialize.h" 31 #include "wasm/WasmStructLayout.h" 32 #include "wasm/WasmUtility.h" 33 #include "wasm/WasmValType.h" 34 35 namespace js { 36 namespace wasm { 37 38 class RecGroup; 39 40 //========================================================================= 41 // Function types 42 43 // The FuncType class represents a WebAssembly function signature which takes a 44 // list of value types and returns an expression type. The engine uses two 45 // in-memory representations of the argument Vector's memory (when elements do 46 // not fit inline): normal malloc allocation (via SystemAllocPolicy) and 47 // allocation in a LifoAlloc (via LifoAllocPolicy). The former FuncType objects 48 // can have any lifetime since they own the memory. The latter FuncType objects 49 // must not outlive the associated LifoAlloc mark/release interval (which is 50 // currently the duration of module validation+compilation). Thus, long-lived 51 // objects like WasmModule must use malloced allocation. 52 53 class FuncType { 54 ValTypeVector args_; 55 ValTypeVector results_; 56 // A packed structural type identifier for use in the call_indirect type 57 // check in the prologue of functions. If this function type cannot fit in 58 // this immediate, it will be NO_IMMEDIATE_TYPE_ID. 59 // 60 // This is initialized in DecodeTypeSection once we have the full recursion 61 // group around. 62 uint32_t immediateTypeId_ = NO_IMMEDIATE_TYPE_ID; 63 64 // This function type cannot be packed into an immediate for call_indirect 65 // signature checks. 66 static const uint32_t NO_IMMEDIATE_TYPE_ID = UINT32_MAX; 67 68 // Entry from JS to wasm via the JIT is currently unimplemented for 69 // functions that return multiple values. 70 bool temporarilyUnsupportedResultCountForJitEntry() const { 71 return results().length() > MaxResultsForJitEntry; 72 } 73 // Calls out from wasm to JS that return multiple values is currently 74 // unsupported. 75 bool temporarilyUnsupportedResultCountForJitExit() const { 76 return results().length() > MaxResultsForJitExit; 77 } 78 // For JS->wasm jit entries, temporarily disallow certain types until the 79 // stubs generator is improved. 80 // * ref params may be nullable externrefs 81 // * ref results may not be type indices 82 // V128 types are excluded per spec but are guarded against separately. 83 bool temporarilyUnsupportedReftypeForEntry() const { 84 for (ValType arg : args()) { 85 if (arg.isRefType() && (!arg.isExternRef() || !arg.isNullable())) { 86 return true; 87 } 88 } 89 for (ValType result : results()) { 90 if (result.isTypeRef()) { 91 return true; 92 } 93 } 94 return false; 95 } 96 // For wasm->JS jit exits, temporarily disallow certain types until 97 // the stubs generator is improved. 98 // * ref results may be nullable externrefs 99 // Unexposable types must be guarded against separately. 100 bool temporarilyUnsupportedReftypeForExit() const { 101 for (ValType result : results()) { 102 if (result.isRefType() && 103 (!result.isExternRef() || !result.isNullable())) { 104 return true; 105 } 106 } 107 return false; 108 } 109 110 public: 111 FuncType() = default; 112 FuncType(ValTypeVector&& args, ValTypeVector&& results) 113 : args_(std::move(args)), results_(std::move(results)) { 114 MOZ_ASSERT(args_.length() <= MaxParams); 115 MOZ_ASSERT(results_.length() <= MaxResults); 116 } 117 118 FuncType(FuncType&&) = default; 119 FuncType& operator=(FuncType&&) = default; 120 121 [[nodiscard]] bool clone(const FuncType& src) { 122 MOZ_ASSERT(args_.empty()); 123 MOZ_ASSERT(results_.empty()); 124 immediateTypeId_ = src.immediateTypeId_; 125 return args_.appendAll(src.args_) && results_.appendAll(src.results_); 126 } 127 128 ValType arg(unsigned i) const { return args_[i]; } 129 const ValTypeVector& args() const { return args_; } 130 ValType result(unsigned i) const { return results_[i]; } 131 const ValTypeVector& results() const { return results_; } 132 133 void initImmediateTypeId(bool isFinal, const TypeDef* superTypeDef, 134 uint32_t recGroupLength); 135 bool hasImmediateTypeId() const { 136 return immediateTypeId_ != NO_IMMEDIATE_TYPE_ID; 137 } 138 uint32_t immediateTypeId() const { 139 MOZ_ASSERT(hasImmediateTypeId()); 140 return immediateTypeId_; 141 } 142 143 // The lsb for every immediate type id is set to distinguish an immediate type 144 // id from a type id represented by a pointer to the global hash type set. 145 static const uint32_t ImmediateBit = 0x1; 146 147 HashNumber hash(const RecGroup* recGroup) const { 148 HashNumber hn = 0; 149 for (const ValType& vt : args_) { 150 hn = mozilla::AddToHash(hn, vt.forIsoEquals(recGroup).hash()); 151 } 152 for (const ValType& vt : results_) { 153 hn = mozilla::AddToHash(hn, vt.forIsoEquals(recGroup).hash()); 154 } 155 return hn; 156 } 157 158 // Compares two function types for isorecursive equality. See 159 // "Comparing type definitions" in WasmValType.h for more background. 160 static bool isoEquals(const RecGroup* lhsRecGroup, const FuncType& lhs, 161 const RecGroup* rhsRecGroup, const FuncType& rhs) { 162 if (lhs.args_.length() != rhs.args_.length() || 163 lhs.results_.length() != rhs.results_.length()) { 164 return false; 165 } 166 for (uint32_t i = 0; i < lhs.args_.length(); i++) { 167 if (lhs.args_[i].forIsoEquals(lhsRecGroup) != 168 rhs.args_[i].forIsoEquals(rhsRecGroup)) { 169 return false; 170 } 171 } 172 for (uint32_t i = 0; i < lhs.results_.length(); i++) { 173 if (lhs.results_[i].forIsoEquals(lhsRecGroup) != 174 rhs.results_[i].forIsoEquals(rhsRecGroup)) { 175 return false; 176 } 177 } 178 return true; 179 } 180 181 // Checks if every arg and result of the specified function types are bitwise 182 // equal. Type references must therefore point to exactly the same type 183 // definition instance. 184 static bool strictlyEquals(const FuncType& lhs, const FuncType& rhs) { 185 return EqualContainers(lhs.args(), rhs.args()) && 186 EqualContainers(lhs.results(), rhs.results()); 187 } 188 189 // Checks if two function types are compatible in a given subtyping 190 // relationship. 191 static bool canBeSubTypeOf(const FuncType& subType, 192 const FuncType& superType) { 193 // A subtype must have exactly as many arguments as its supertype 194 if (subType.args().length() != superType.args().length()) { 195 return false; 196 } 197 198 // A subtype must have exactly as many returns as its supertype 199 if (subType.results().length() != superType.results().length()) { 200 return false; 201 } 202 203 // Function result types are covariant 204 for (uint32_t i = 0; i < superType.results().length(); i++) { 205 if (!ValType::isSubTypeOf(subType.results()[i], superType.results()[i])) { 206 return false; 207 } 208 } 209 210 // Function argument types are contravariant 211 for (uint32_t i = 0; i < superType.args().length(); i++) { 212 if (!ValType::isSubTypeOf(superType.args()[i], subType.args()[i])) { 213 return false; 214 } 215 } 216 217 return true; 218 } 219 220 bool canHaveJitEntry() const; 221 bool canHaveJitExit() const; 222 223 bool hasInt64Arg() const { 224 for (ValType arg : args()) { 225 if (arg.kind() == ValType::Kind::I64) { 226 return true; 227 } 228 } 229 return false; 230 } 231 232 bool hasUnexposableArgOrRet() const { 233 for (ValType arg : args()) { 234 if (!arg.isExposable()) { 235 return true; 236 } 237 } 238 for (ValType result : results()) { 239 if (!result.isExposable()) { 240 return true; 241 } 242 } 243 return false; 244 } 245 246 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 247 WASM_DECLARE_FRIEND_SERIALIZE(FuncType); 248 }; 249 250 //========================================================================= 251 // Structure types 252 253 // The Module owns a dense array of StructType values that represent the 254 // structure types that the module knows about. It is created from the sparse 255 // array of types in the ModuleMetadata when the Module is created. 256 257 struct FieldType { 258 StorageType type; 259 bool isMutable; 260 261 FieldType() : isMutable(false) {} 262 FieldType(StorageType type, bool isMutable) 263 : type(type), isMutable(isMutable) {} 264 265 HashNumber hash(const RecGroup* recGroup) const { 266 HashNumber hn = 0; 267 hn = mozilla::AddToHash(hn, type.forIsoEquals(recGroup).hash()); 268 hn = mozilla::AddToHash(hn, HashNumber(isMutable)); 269 return hn; 270 } 271 272 // Compares two field types for isorecursive equality. See 273 // "Comparing type definitions" in WasmValType.h for more background. 274 static bool isoEquals(const RecGroup* lhsRecGroup, const FieldType& lhs, 275 const RecGroup* rhsRecGroup, const FieldType& rhs) { 276 return lhs.isMutable == rhs.isMutable && 277 lhs.type.forIsoEquals(lhsRecGroup) == 278 rhs.type.forIsoEquals(rhsRecGroup); 279 } 280 281 // Checks if two struct fields are compatible in a given subtyping 282 // relationship. 283 static bool canBeSubTypeOf(const FieldType& subType, 284 const FieldType& superType) { 285 // Mutable fields are invariant w.r.t. field types 286 if (subType.isMutable && superType.isMutable) { 287 return subType.type == superType.type; 288 } 289 290 // Immutable fields are covariant w.r.t. field types 291 if (!subType.isMutable && !superType.isMutable) { 292 return StorageType::isSubTypeOf(subType.type, superType.type); 293 } 294 295 return false; 296 } 297 }; 298 299 using FieldTypeVector = Vector<FieldType, 0, SystemAllocPolicy>; 300 301 using FieldAccessPathVector = Vector<FieldAccessPath, 2, SystemAllocPolicy>; 302 using InlineTraceOffsetVector = Vector<uint32_t, 2, SystemAllocPolicy>; 303 using OutlineTraceOffsetVector = Vector<uint32_t, 0, SystemAllocPolicy>; 304 305 class StructType { 306 public: 307 // Vector of the fields in this struct 308 FieldTypeVector fields_; 309 // The access path for every struct field. Same length as `fields_`. 310 FieldAccessPathVector fieldAccessPaths_; 311 312 // The offsets of fields that must be traced in the inline portion of wasm 313 // struct object. Offsets are from the base of the WasmStructObject. 314 InlineTraceOffsetVector inlineTraceOffsets_; 315 // The offsets of fields that must be traced in the outline portion of wasm 316 // struct object. 317 OutlineTraceOffsetVector outlineTraceOffsets_; 318 319 // The total block size for the WasmStructObject's OOL data area, or zero if 320 // no OOL data is required. 321 uint32_t totalSizeOOL_; 322 // The offset from the start of the WasmStructObject to the OOL pointer 323 // field, or InvalidOffset if no OOL data is required. 324 uint32_t oolPointerOffset_; 325 326 // The total object size for the WasmStructObject, including fixed overheads 327 // (its header words). 328 uint32_t totalSizeIL_; 329 // The offset from the start of the WasmStructObject to the first payload 330 // byte. 331 uint8_t payloadOffsetIL_; 332 333 // The AllocKind for the object, computed directly from `totalSizeIL_`. Note 334 // this requires further processing with GetFinalizedAllocKindForClass before 335 // use. 336 gc::AllocKind allocKind_; 337 338 // Whether this struct only contains defaultable fields. 339 bool isDefaultable_; 340 341 static const uint32_t InvalidOffset = 0xFFFFFFFF; 342 343 StructType() 344 : totalSizeOOL_(0), 345 oolPointerOffset_(InvalidOffset), 346 totalSizeIL_(0), 347 payloadOffsetIL_(0), 348 allocKind_(gc::AllocKind::INVALID), 349 isDefaultable_(false) {} 350 351 explicit StructType(FieldTypeVector&& fields) 352 : fields_(std::move(fields)), 353 totalSizeOOL_(0), 354 oolPointerOffset_(InvalidOffset), 355 totalSizeIL_(0), 356 payloadOffsetIL_(0), 357 allocKind_(gc::AllocKind::INVALID), 358 isDefaultable_(false) { 359 MOZ_ASSERT(fields_.length() <= MaxStructFields); 360 } 361 362 StructType(StructType&&) = default; 363 StructType& operator=(StructType&&) = default; 364 365 [[nodiscard]] bool init(); 366 367 bool isDefaultable() const { return isDefaultable_; } 368 369 bool hasOOL() const { return totalSizeOOL_ > 0; } 370 371 HashNumber hash(const RecGroup* recGroup) const { 372 HashNumber hn = 0; 373 for (const FieldType& field : fields_) { 374 hn = mozilla::AddToHash(hn, field.hash(recGroup)); 375 } 376 return hn; 377 } 378 379 // Compares two struct types for isorecursive equality. See 380 // "Comparing type definitions" in WasmValType.h for more background. 381 static bool isoEquals(const RecGroup* lhsRecGroup, const StructType& lhs, 382 const RecGroup* rhsRecGroup, const StructType& rhs) { 383 if (lhs.fields_.length() != rhs.fields_.length()) { 384 return false; 385 } 386 for (uint32_t i = 0; i < lhs.fields_.length(); i++) { 387 const FieldType& lhsField = lhs.fields_[i]; 388 const FieldType& rhsField = rhs.fields_[i]; 389 if (!FieldType::isoEquals(lhsRecGroup, lhsField, rhsRecGroup, rhsField)) { 390 return false; 391 } 392 } 393 return true; 394 } 395 396 // Checks if two struct types are compatible in a given subtyping 397 // relationship. 398 static bool canBeSubTypeOf(const StructType& subType, 399 const StructType& superType) { 400 // A subtype must have at least as many fields as its supertype 401 if (subType.fields_.length() < superType.fields_.length()) { 402 return false; 403 } 404 405 // Every field that is in both superType and subType must be compatible 406 for (uint32_t i = 0; i < superType.fields_.length(); i++) { 407 if (!FieldType::canBeSubTypeOf(subType.fields_[i], 408 superType.fields_[i])) { 409 return false; 410 } 411 } 412 return true; 413 } 414 415 static bool createImmutable(const ValTypeVector& types, StructType* struct_); 416 417 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 418 WASM_DECLARE_FRIEND_SERIALIZE(StructType); 419 }; 420 421 using StructTypeVector = Vector<StructType, 0, SystemAllocPolicy>; 422 423 //========================================================================= 424 // Array types 425 426 class ArrayType { 427 public: 428 // The kind of value stored in this array 429 FieldType fieldType_; 430 431 public: 432 ArrayType() = default; 433 ArrayType(StorageType elementType, bool isMutable) 434 : fieldType_(FieldType(elementType, isMutable)) {} 435 436 ArrayType(const ArrayType&) = default; 437 ArrayType& operator=(const ArrayType&) = default; 438 439 ArrayType(ArrayType&&) = default; 440 ArrayType& operator=(ArrayType&&) = default; 441 442 StorageType elementType() const { return fieldType_.type; } 443 bool isMutable() const { return fieldType_.isMutable; } 444 bool isDefaultable() const { return elementType().isDefaultable(); } 445 446 HashNumber hash(const RecGroup* recGroup) const { 447 return fieldType_.hash(recGroup); 448 } 449 450 // Compares two array types for isorecursive equality. See 451 // "Comparing type definitions" in WasmValType.h for more background. 452 static bool isoEquals(const RecGroup* lhsRecGroup, const ArrayType& lhs, 453 const RecGroup* rhsRecGroup, const ArrayType& rhs) { 454 return FieldType::isoEquals(lhsRecGroup, lhs.fieldType_, rhsRecGroup, 455 rhs.fieldType_); 456 } 457 458 // Checks if two arrays are compatible in a given subtyping relationship. 459 static bool canBeSubTypeOf(const ArrayType& subType, 460 const ArrayType& superType) { 461 return FieldType::canBeSubTypeOf(subType.fieldType_, superType.fieldType_); 462 } 463 464 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 465 }; 466 467 WASM_DECLARE_CACHEABLE_POD(ArrayType); 468 469 using ArrayTypeVector = Vector<ArrayType, 0, SystemAllocPolicy>; 470 471 //========================================================================= 472 // SuperTypeVector 473 474 // [SMDOC] Super type vector 475 // 476 // A super type vector is a vector representation of the linked list of super 477 // types that a type definition has. Every element is a raw pointer to another 478 // super type vector - they are one-to-one with type definitions, so they are 479 // functionally equivalent. It is possible to form a vector here because 480 // subtypes in wasm form trees, not DAGs, with every type having at most one 481 // super type. 482 // 483 // The first element in the vector is the 'root' type definition without a 484 // super type. The last element is to the type definition itself. 485 // 486 // ## Subtype checking 487 // 488 // The only purpose of a super type vector is to support constant time 489 // subtyping checks. This is not free, it comes at the cost of worst case N^2 490 // metadata size growth. We limit the max subtyping depth to counter this. 491 // 492 // To perform a subtype check we rely on the following: 493 // (1) a type A is a subtype (<:) of type B iff: 494 // type A == type B OR 495 // type B is reachable by following declared super types of type A 496 // (2) we order super type vectors from least to most derived types 497 // (3) the 'subtyping depth' of all type definitions is statically known 498 // 499 // With the above, we know that if type B is a super type of type A, that it 500 // must be in A's super type vector at type B's subtyping depth. We can 501 // therefore just do an index and comparison to determine if that's the case. 502 // 503 // ## Example 504 // 505 // For the following type section: 506 // .. 507 // 12: (type (struct)) 508 // .. 509 // 34: (type (sub 12 (struct))) 510 // .. 511 // 56: (type (sub 34 (struct))) 512 // .. 513 // 78: (type (sub 56 (struct))) 514 // .. 515 // 516 // (type 12) would have the following super type vector: 517 // [(type 12)] 518 // 519 // (type 78) would have the following super type vector: 520 // [(type 12), (type 34), (type 56), (type 78)] 521 // 522 // Checking that (type 78) <: (type 12) can use the fact that (type 12) will 523 // always be present at depth 0 of any super type vector it is in, and 524 // therefore check the vector at that index. 525 // 526 // ## Minimum sizing 527 // 528 // As a further optimization to avoid bounds checking, we guarantee that all 529 // super type vectors are at least `MinSuperTypeVectorLength`. All checks 530 // against indices that we know statically are at/below that can skip bounds 531 // checking. Extra entries added to reach the minimum size are initialized to 532 // null. 533 class SuperTypeVector { 534 SuperTypeVector() : typeDef_(nullptr), length_(0) {} 535 536 // The TypeDef for which this is the supertype vector. That TypeDef should 537 // point back to this SuperTypeVector. 538 const TypeDef* typeDef_; 539 540 // A cached copy of subTypingDepth from TypeDef. 541 uint32_t subTypingDepth_; 542 543 // The length of types stored inline below. 544 uint32_t length_; 545 546 public: 547 // Raw pointers to the super types of this type definition. Ordered from 548 // least-derived to most-derived. Do not add any fields after this point. 549 const SuperTypeVector* types_[0]; 550 551 // Batch allocate super type vectors for all the types in a recursion group. 552 // Returns a pointer to the first super type vector, which can be used to 553 // free all vectors. 554 [[nodiscard]] static const SuperTypeVector* createMultipleForRecGroup( 555 RecGroup* recGroup); 556 557 const TypeDef* typeDef() const { return typeDef_; } 558 559 uint32_t length() const { return length_; } 560 561 const SuperTypeVector* type(size_t index) const { 562 MOZ_ASSERT(index < length_); 563 return types_[index]; 564 } 565 566 // The length of a super type vector for a specific type def. 567 static size_t lengthForTypeDef(const TypeDef& typeDef); 568 // The byte size of a super type vector for a specific type def. 569 static size_t byteSizeForTypeDef(const TypeDef& typeDef); 570 571 static size_t offsetOfSubTypingDepth() { 572 return offsetof(SuperTypeVector, subTypingDepth_); 573 } 574 static size_t offsetOfLength() { return offsetof(SuperTypeVector, length_); } 575 static size_t offsetOfSelfTypeDef() { 576 return offsetof(SuperTypeVector, typeDef_); 577 }; 578 static size_t offsetOfSTVInVector(uint32_t subTypingDepth); 579 }; 580 581 // Ensure it is safe to use `sizeof(SuperTypeVector)` to find the offset of 582 // `types_[0]`. 583 static_assert(offsetof(SuperTypeVector, types_) == sizeof(SuperTypeVector)); 584 585 //========================================================================= 586 // TypeDef and supporting types 587 588 // A tagged container for the various types that can be present in a wasm 589 // module's type section. 590 591 enum class TypeDefKind : uint8_t { 592 None = 0, 593 Func, 594 Struct, 595 Array, 596 }; 597 598 class TypeDef { 599 uint32_t offsetToRecGroup_; 600 601 // The supertype vector for this TypeDef. That SuperTypeVector should point 602 // back to this TypeDef. 603 const SuperTypeVector* superTypeVector_; 604 605 const TypeDef* superTypeDef_; 606 uint16_t subTypingDepth_; 607 bool isFinal_; 608 TypeDefKind kind_; 609 union { 610 FuncType funcType_; 611 StructType structType_; 612 ArrayType arrayType_; 613 }; 614 615 void setRecGroup(RecGroup* recGroup) { 616 uintptr_t recGroupAddr = (uintptr_t)recGroup; 617 uintptr_t typeDefAddr = (uintptr_t)this; 618 MOZ_ASSERT(typeDefAddr > recGroupAddr); 619 MOZ_ASSERT(typeDefAddr - recGroupAddr <= UINT32_MAX); 620 offsetToRecGroup_ = typeDefAddr - recGroupAddr; 621 } 622 623 public: 624 explicit TypeDef(RecGroup* recGroup) 625 : offsetToRecGroup_(0), 626 superTypeVector_(nullptr), 627 superTypeDef_(nullptr), 628 subTypingDepth_(0), 629 isFinal_(true), 630 kind_(TypeDefKind::None) { 631 setRecGroup(recGroup); 632 } 633 634 ~TypeDef() { 635 switch (kind_) { 636 case TypeDefKind::Func: 637 funcType_.~FuncType(); 638 break; 639 case TypeDefKind::Struct: 640 structType_.~StructType(); 641 break; 642 case TypeDefKind::Array: 643 arrayType_.~ArrayType(); 644 break; 645 case TypeDefKind::None: 646 break; 647 } 648 } 649 650 TypeDef& operator=(FuncType&& that) noexcept { 651 MOZ_ASSERT(isNone()); 652 kind_ = TypeDefKind::Func; 653 new (&funcType_) FuncType(std::move(that)); 654 return *this; 655 } 656 657 TypeDef& operator=(StructType&& that) noexcept { 658 MOZ_ASSERT(isNone()); 659 kind_ = TypeDefKind::Struct; 660 new (&structType_) StructType(std::move(that)); 661 return *this; 662 } 663 664 TypeDef& operator=(ArrayType&& that) noexcept { 665 MOZ_ASSERT(isNone()); 666 kind_ = TypeDefKind::Array; 667 new (&arrayType_) ArrayType(std::move(that)); 668 return *this; 669 } 670 671 const SuperTypeVector* superTypeVector() const { return superTypeVector_; } 672 673 void setSuperTypeVector(const SuperTypeVector* superTypeVector) { 674 superTypeVector_ = superTypeVector; 675 } 676 677 static size_t offsetOfKind() { return offsetof(TypeDef, kind_); } 678 679 static size_t offsetOfSuperTypeVector() { 680 return offsetof(TypeDef, superTypeVector_); 681 } 682 683 static size_t offsetOfSubTypingDepth() { 684 return offsetof(TypeDef, subTypingDepth_); 685 } 686 687 const TypeDef* superTypeDef() const { return superTypeDef_; } 688 689 bool isFinal() const { return isFinal_; } 690 691 uint16_t subTypingDepth() const { return subTypingDepth_; } 692 693 const RecGroup& recGroup() const { 694 uintptr_t typeDefAddr = (uintptr_t)this; 695 uintptr_t recGroupAddr = typeDefAddr - offsetToRecGroup_; 696 return *(const RecGroup*)recGroupAddr; 697 } 698 699 TypeDefKind kind() const { return kind_; } 700 701 bool isNone() const { return kind_ == TypeDefKind::None; } 702 703 bool isFuncType() const { return kind_ == TypeDefKind::Func; } 704 705 bool isStructType() const { return kind_ == TypeDefKind::Struct; } 706 707 bool isArrayType() const { return kind_ == TypeDefKind::Array; } 708 709 bool isGcType() const { return isStructType() || isArrayType(); } 710 711 const FuncType& funcType() const { 712 MOZ_ASSERT(isFuncType()); 713 return funcType_; 714 } 715 716 FuncType& funcType() { 717 MOZ_ASSERT(isFuncType()); 718 return funcType_; 719 } 720 721 const StructType& structType() const { 722 MOZ_ASSERT(isStructType()); 723 return structType_; 724 } 725 726 StructType& structType() { 727 MOZ_ASSERT(isStructType()); 728 return structType_; 729 } 730 731 const ArrayType& arrayType() const { 732 MOZ_ASSERT(isArrayType()); 733 return arrayType_; 734 } 735 736 ArrayType& arrayType() { 737 MOZ_ASSERT(isArrayType()); 738 return arrayType_; 739 } 740 741 // Get a value that can be used for comparing type definitions across 742 // different recursion groups. 743 static inline uintptr_t forIsoEquals(const TypeDef* typeDef, 744 const RecGroup* recGroup); 745 746 HashNumber hash() const { 747 HashNumber hn = HashNumber(kind_); 748 hn = mozilla::AddToHash(hn, 749 TypeDef::forIsoEquals(superTypeDef_, &recGroup())); 750 hn = mozilla::AddToHash(hn, isFinal_); 751 switch (kind_) { 752 case TypeDefKind::Func: 753 hn = mozilla::AddToHash(hn, funcType_.hash(&recGroup())); 754 break; 755 case TypeDefKind::Struct: 756 hn = mozilla::AddToHash(hn, structType_.hash(&recGroup())); 757 break; 758 case TypeDefKind::Array: 759 hn = mozilla::AddToHash(hn, arrayType_.hash(&recGroup())); 760 break; 761 case TypeDefKind::None: 762 break; 763 } 764 return hn; 765 } 766 767 // Compares two type definitions for isorecursive equality. See 768 // "Comparing type definitions" in WasmValType.h for more background. 769 static bool isoEquals(const TypeDef& lhs, const TypeDef& rhs) { 770 if (lhs.kind_ != rhs.kind_) { 771 return false; 772 } 773 if (lhs.isFinal_ != rhs.isFinal_) { 774 return false; 775 } 776 if (TypeDef::forIsoEquals(lhs.superTypeDef_, &lhs.recGroup()) != 777 TypeDef::forIsoEquals(rhs.superTypeDef_, &rhs.recGroup())) { 778 return false; 779 } 780 switch (lhs.kind_) { 781 case TypeDefKind::Func: 782 return FuncType::isoEquals(&lhs.recGroup(), lhs.funcType_, 783 &rhs.recGroup(), rhs.funcType_); 784 case TypeDefKind::Struct: 785 return StructType::isoEquals(&lhs.recGroup(), lhs.structType_, 786 &rhs.recGroup(), rhs.structType_); 787 case TypeDefKind::Array: 788 return ArrayType::isoEquals(&lhs.recGroup(), lhs.arrayType_, 789 &rhs.recGroup(), rhs.arrayType_); 790 case TypeDefKind::None: 791 MOZ_CRASH("can't match TypeDefKind::None"); 792 } 793 return false; 794 } 795 796 // Checks if two type definitions are compatible in a given subtyping 797 // relationship. 798 static bool canBeSubTypeOf(const TypeDef* subType, const TypeDef* superType) { 799 if (subType->kind() != superType->kind()) { 800 return false; 801 } 802 803 // A subtype can't declare a final super type. 804 if (superType->isFinal()) { 805 return false; 806 } 807 808 switch (subType->kind_) { 809 case TypeDefKind::Func: 810 return FuncType::canBeSubTypeOf(subType->funcType_, 811 superType->funcType_); 812 case TypeDefKind::Struct: 813 return StructType::canBeSubTypeOf(subType->structType_, 814 superType->structType_); 815 case TypeDefKind::Array: 816 return ArrayType::canBeSubTypeOf(subType->arrayType_, 817 superType->arrayType_); 818 case TypeDefKind::None: 819 MOZ_CRASH(); 820 } 821 return false; 822 } 823 824 void setSuperTypeDef(const TypeDef* superTypeDef) { 825 superTypeDef_ = superTypeDef; 826 subTypingDepth_ = superTypeDef_->subTypingDepth_ + 1; 827 } 828 829 void setFinal(const bool value) { isFinal_ = value; } 830 831 // Checks if `subTypeDef` is a declared sub type of `superTypeDef`. 832 static bool isSubTypeOf(const TypeDef* subTypeDef, 833 const TypeDef* superTypeDef) { 834 // Fast path for when the types are equal 835 if (MOZ_LIKELY(subTypeDef == superTypeDef)) { 836 return true; 837 } 838 const SuperTypeVector* subSTV = subTypeDef->superTypeVector(); 839 const SuperTypeVector* superSTV = superTypeDef->superTypeVector(); 840 841 // During construction of a recursion group, the super type vectors may not 842 // have been computed yet, in which case we need to fall back to a linear 843 // search. 844 if (!subSTV || !superSTV) { 845 while (subTypeDef) { 846 if (subTypeDef == superTypeDef) { 847 return true; 848 } 849 subTypeDef = subTypeDef->superTypeDef(); 850 } 851 return false; 852 } 853 854 // The supertype vectors do exist. Check that they point to the right 855 // places. 856 MOZ_ASSERT(subSTV); 857 MOZ_ASSERT(superSTV); 858 MOZ_ASSERT(subSTV->typeDef() == subTypeDef); 859 MOZ_ASSERT(superSTV->typeDef() == superTypeDef); 860 861 // We need to check if `superTypeDef` is one of `subTypeDef`s super types 862 // by checking in `subTypeDef`s super type vector. We can use the static 863 // information of the depth of `superTypeDef` to index directly into the 864 // vector. 865 uint32_t subTypingDepth = superTypeDef->subTypingDepth(); 866 if (subTypingDepth >= subSTV->length()) { 867 return false; 868 } 869 870 return subSTV->type(subTypingDepth) == superSTV; 871 } 872 873 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 874 WASM_DECLARE_FRIEND_SERIALIZE(TypeDef); 875 876 inline void AddRef() const; 877 inline void Release() const; 878 }; 879 880 using SharedTypeDef = RefPtr<const TypeDef>; 881 using MutableTypeDef = RefPtr<TypeDef>; 882 883 using TypeDefVector = Vector<TypeDef, 0, SystemAllocPolicy>; 884 using TypeDefPtrVector = Vector<const TypeDef*, 0, SystemAllocPolicy>; 885 886 using TypeDefPtrToIndexMap = 887 HashMap<const TypeDef*, uint32_t, PointerHasher<const TypeDef*>, 888 SystemAllocPolicy>; 889 890 //========================================================================= 891 // RecGroup 892 893 // A recursion group is a set of type definitions that may refer to each other 894 // or to type definitions in another recursion group. There is an ordering 895 // restriction on type references such that references across recursion groups 896 // must be acyclic. 897 // 898 // Type definitions are stored inline in their containing recursion group, and 899 // have an offset to their containing recursion group. Recursion groups are 900 // atomically refcounted and hold strong references to other recursion groups 901 // they depend on. 902 // 903 // Type equality is structural in WebAssembly, and we canonicalize recursion 904 // groups while building them so that pointer equality of types implies 905 // equality of types. There is a global hash set of weak pointers to recursion 906 // groups that holds the current canonical instance of a recursion group. 907 class RecGroup : public AtomicRefCounted<RecGroup> { 908 // Whether this recursion group has been finished and acquired strong 909 // references to external recursion groups. 910 bool finalizedTypes_; 911 // The number of types stored in this recursion group. 912 uint32_t numTypes_; 913 // The batch allocated super type vectors for all type definitions in this 914 // recursion group. 915 const SuperTypeVector* vectors_; 916 // The first type definition stored inline in this recursion group. 917 TypeDef types_[0]; 918 919 friend class TypeContext; 920 921 explicit RecGroup(uint32_t numTypes) 922 : finalizedTypes_(false), numTypes_(numTypes), vectors_(nullptr) {} 923 924 // Compute the size in bytes of a recursion group with the specified amount 925 // of types. 926 static constexpr size_t sizeOfRecGroup(uint32_t numTypes) { 927 // TypeDef can find containing RecGroup using only a 32-bit offset 928 static_assert(offsetof(RecGroup, types_) + sizeof(TypeDef) * MaxTypes <= 929 UINT32_MAX); 930 // We will not overflow in our calculation here 931 static_assert(MaxTypes <= SIZE_MAX / sizeof(TypeDef)); 932 return sizeof(RecGroup) + sizeof(TypeDef) * numTypes; 933 } 934 935 // Allocate a recursion group with the specified amount of types. The type 936 // definitions will be ready to be filled in. Users must call `finish` once 937 // type definitions are initialized so that strong references to external 938 // recursion groups are taken. 939 static RefPtr<RecGroup> allocate(uint32_t numTypes) { 940 // Validation must ensure this 941 MOZ_RELEASE_ASSERT(numTypes <= MaxTypes); 942 943 // Allocate the recursion group with the correct size 944 RecGroup* recGroup = (RecGroup*)js_malloc(sizeOfRecGroup(numTypes)); 945 if (!recGroup) { 946 return nullptr; 947 } 948 949 // Construct the recursion group and types that are stored inline 950 new (recGroup) RecGroup(numTypes); 951 for (uint32_t i = 0; i < numTypes; i++) { 952 new (recGroup->types_ + i) TypeDef(recGroup); 953 } 954 return recGroup; 955 } 956 957 // Finish initialization by acquiring strong references to groups referenced 958 // by type definitions. 959 [[nodiscard]] bool finalizeDefinitions() { 960 MOZ_ASSERT(!finalizedTypes_); 961 // Super type vectors are only needed for GC and have a size/time impact 962 // that we don't want to encur until we're ready for it. Only use them when 963 // GC is built into the binary. 964 vectors_ = SuperTypeVector::createMultipleForRecGroup(this); 965 if (!vectors_) { 966 return false; 967 } 968 visitReferencedGroups([](const RecGroup* recGroup) { recGroup->AddRef(); }); 969 finalizedTypes_ = true; 970 return true; 971 } 972 973 // Visit every external recursion group that is referenced by the types in 974 // this recursion group. 975 template <typename Visitor> 976 void visitReferencedGroups(Visitor visitor) const { 977 auto visitValType = [this, visitor](ValType type) { 978 if (type.isTypeRef() && &type.typeDef()->recGroup() != this) { 979 visitor(&type.typeDef()->recGroup()); 980 } 981 }; 982 auto visitStorageType = [this, visitor](StorageType type) { 983 if (type.isTypeRef() && &type.typeDef()->recGroup() != this) { 984 visitor(&type.typeDef()->recGroup()); 985 } 986 }; 987 988 for (uint32_t i = 0; i < numTypes_; i++) { 989 const TypeDef& typeDef = types_[i]; 990 991 if (typeDef.superTypeDef() && 992 &typeDef.superTypeDef()->recGroup() != this) { 993 visitor(&typeDef.superTypeDef()->recGroup()); 994 } 995 996 switch (typeDef.kind()) { 997 case TypeDefKind::Func: { 998 const FuncType& funcType = typeDef.funcType(); 999 MOZ_RELEASE_ASSERT(funcType.args().length() <= MaxParams); 1000 MOZ_RELEASE_ASSERT(funcType.results().length() <= MaxResults); 1001 for (auto type : funcType.args()) { 1002 visitValType(type); 1003 } 1004 for (auto type : funcType.results()) { 1005 visitValType(type); 1006 } 1007 break; 1008 } 1009 case TypeDefKind::Struct: { 1010 const StructType& structType = typeDef.structType(); 1011 MOZ_RELEASE_ASSERT(structType.fields_.length() <= MaxStructFields); 1012 for (const auto& field : structType.fields_) { 1013 visitStorageType(field.type); 1014 } 1015 break; 1016 } 1017 case TypeDefKind::Array: { 1018 const ArrayType& arrayType = typeDef.arrayType(); 1019 visitStorageType(arrayType.elementType()); 1020 break; 1021 } 1022 case TypeDefKind::None: { 1023 MOZ_CRASH(); 1024 } 1025 } 1026 } 1027 } 1028 1029 public: 1030 ~RecGroup() { 1031 // Release the referenced recursion groups if we acquired references to 1032 // them. Do this before the type definitions are destroyed below. 1033 if (finalizedTypes_) { 1034 finalizedTypes_ = false; 1035 visitReferencedGroups( 1036 [](const RecGroup* recGroup) { recGroup->Release(); }); 1037 } 1038 1039 if (vectors_) { 1040 js_free((void*)vectors_); 1041 vectors_ = nullptr; 1042 } 1043 1044 // Call destructors on all the type definitions. 1045 for (uint32_t i = 0; i < numTypes_; i++) { 1046 type(i).~TypeDef(); 1047 } 1048 } 1049 1050 // Recursion groups cannot be copied or moved 1051 RecGroup& operator=(const RecGroup&) = delete; 1052 RecGroup& operator=(RecGroup&&) = delete; 1053 1054 // Get the type definition at the group type index (not module type index). 1055 TypeDef& type(uint32_t groupTypeIndex) { 1056 // We cannot mutate type definitions after we've finalized them 1057 MOZ_ASSERT(!finalizedTypes_); 1058 return types_[groupTypeIndex]; 1059 } 1060 const TypeDef& type(uint32_t groupTypeIndex) const { 1061 return types_[groupTypeIndex]; 1062 } 1063 1064 // The number of types stored in this recursion group. 1065 uint32_t numTypes() const { return numTypes_; } 1066 1067 // Get the index of a type definition that's in this recursion group. 1068 uint32_t indexOf(const TypeDef* typeDef) const { 1069 MOZ_ASSERT(typeDef >= types_); 1070 size_t groupTypeIndex = (size_t)(typeDef - types_); 1071 MOZ_ASSERT(groupTypeIndex < numTypes()); 1072 return (uint32_t)groupTypeIndex; 1073 } 1074 1075 bool hasGcType() const { 1076 for (uint32_t groupTypeIndex = 0; groupTypeIndex < numTypes(); 1077 groupTypeIndex++) { 1078 if (type(groupTypeIndex).isGcType()) { 1079 return true; 1080 } 1081 } 1082 return false; 1083 } 1084 1085 HashNumber hash() const { 1086 HashNumber hn = 0; 1087 for (uint32_t i = 0; i < numTypes(); i++) { 1088 hn = mozilla::AddToHash(hn, types_[i].hash()); 1089 } 1090 return hn; 1091 } 1092 1093 // Compares two recursion groups for isorecursive equality. See 1094 // "Comparing type definitions" in WasmValType.h for more background. 1095 static bool isoEquals(const RecGroup& lhs, const RecGroup& rhs) { 1096 if (lhs.numTypes() != rhs.numTypes()) { 1097 return false; 1098 } 1099 for (uint32_t i = 0; i < lhs.numTypes(); i++) { 1100 if (!TypeDef::isoEquals(lhs.type(i), rhs.type(i))) { 1101 return false; 1102 } 1103 } 1104 return true; 1105 } 1106 }; 1107 1108 // Remove all types from the canonical type set that are not referenced from 1109 // outside the type set. 1110 extern void PurgeCanonicalTypes(); 1111 1112 using SharedRecGroup = RefPtr<const RecGroup>; 1113 using MutableRecGroup = RefPtr<RecGroup>; 1114 using SharedRecGroupVector = Vector<SharedRecGroup, 0, SystemAllocPolicy>; 1115 1116 //========================================================================= 1117 // TypeContext 1118 1119 // A type context holds the recursion groups and corresponding type definitions 1120 // defined in a module. 1121 class TypeContext : public AtomicRefCounted<TypeContext> { 1122 // The pending recursion group that is currently being constructed 1123 MutableRecGroup pendingRecGroup_; 1124 // An in-order list of all the recursion groups defined in this module 1125 SharedRecGroupVector recGroups_; 1126 // An in-order list of the type definitions in the module. Each type is 1127 // stored in a recursion group. 1128 TypeDefPtrVector types_; 1129 // A map from type definition to the original module index. 1130 TypeDefPtrToIndexMap moduleIndices_; 1131 1132 static SharedRecGroup canonicalizeGroup(SharedRecGroup recGroup); 1133 1134 public: 1135 TypeContext() = default; 1136 ~TypeContext(); 1137 1138 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 1139 return types_.sizeOfExcludingThis(mallocSizeOf) + 1140 moduleIndices_.shallowSizeOfExcludingThis(mallocSizeOf); 1141 } 1142 1143 // Disallow copy, allow move initialization 1144 TypeContext(const TypeContext&) = delete; 1145 TypeContext& operator=(const TypeContext&) = delete; 1146 TypeContext(TypeContext&&) = delete; 1147 TypeContext& operator=(TypeContext&&) = delete; 1148 1149 // Begin creating a recursion group with the specified number of types. 1150 // Returns a recursion group to be filled in with type definitions. This must 1151 // be paired with `endGroup`. 1152 [[nodiscard]] MutableRecGroup startRecGroup(uint32_t numTypes) { 1153 // We must not have a pending group 1154 MOZ_ASSERT(!pendingRecGroup_); 1155 1156 // Create the group and add it to the list of groups 1157 MutableRecGroup recGroup = RecGroup::allocate(numTypes); 1158 if (!recGroup || !addRecGroup(recGroup)) { 1159 return nullptr; 1160 } 1161 1162 // Store this group for later use in endRecGroup 1163 pendingRecGroup_ = recGroup; 1164 return recGroup; 1165 } 1166 1167 // Finish creation of a recursion group after type definitions have been 1168 // initialized. This must be paired with `startGroup`. 1169 [[nodiscard]] bool endRecGroup() { 1170 // We must have started a recursion group 1171 MOZ_ASSERT(pendingRecGroup_); 1172 MutableRecGroup recGroup = pendingRecGroup_; 1173 pendingRecGroup_ = nullptr; 1174 1175 // Finalize the type definitions in the recursion group 1176 if (!recGroup->finalizeDefinitions()) { 1177 return false; 1178 } 1179 1180 // Canonicalize the recursion group 1181 SharedRecGroup canonicalRecGroup = canonicalizeGroup(recGroup); 1182 if (!canonicalRecGroup) { 1183 return false; 1184 } 1185 1186 // Nothing left to do if this group became the canonical group 1187 if (canonicalRecGroup == recGroup) { 1188 return true; 1189 } 1190 1191 // Store the canonical group into the list 1192 recGroups_.back() = canonicalRecGroup; 1193 1194 // Overwrite all the entries we stored into the index space maps when we 1195 // started this group. 1196 MOZ_ASSERT(recGroup->numTypes() == canonicalRecGroup->numTypes()); 1197 for (uint32_t groupTypeIndex = 0; groupTypeIndex < recGroup->numTypes(); 1198 groupTypeIndex++) { 1199 uint32_t typeIndex = length() - recGroup->numTypes() + groupTypeIndex; 1200 const TypeDef* oldTypeDef = types_[typeIndex]; 1201 const TypeDef* canonTypeDef = &canonicalRecGroup->type(groupTypeIndex); 1202 1203 types_[typeIndex] = canonTypeDef; 1204 moduleIndices_.remove(oldTypeDef); 1205 1206 // Ensure there is an module index entry pointing to the canonical type 1207 // definition. Don't overwrite it if it already exists, serialization 1208 // relies on the module index map pointing to the first occurrence of a 1209 // type definition to avoid creating forward references that didn't exist 1210 // in the original module. 1211 auto canonTypeIndexEntry = moduleIndices_.lookupForAdd(canonTypeDef); 1212 if (!canonTypeIndexEntry && 1213 !moduleIndices_.add(canonTypeIndexEntry, canonTypeDef, typeIndex)) { 1214 return false; 1215 } 1216 } 1217 1218 return true; 1219 } 1220 1221 // Add a pre-existing recursion group to this type context. 1222 [[nodiscard]] bool addRecGroup(SharedRecGroup recGroup) { 1223 // We must not have a pending group 1224 MOZ_ASSERT(!pendingRecGroup_); 1225 1226 // Add it to the list of groups 1227 if (!recGroups_.append(recGroup)) { 1228 return false; 1229 } 1230 1231 // Store the types of the group into our index space maps. These may get 1232 // overwritten if this group is being added by `startRecGroup` and we 1233 // overwrite it with a canonical group in `endRecGroup`. We need to do 1234 // this before finishing though, because these entries will be used by 1235 // decoding and error printing. 1236 for (uint32_t groupTypeIndex = 0; groupTypeIndex < recGroup->numTypes(); 1237 groupTypeIndex++) { 1238 const TypeDef* typeDef = &recGroup->type(groupTypeIndex); 1239 uint32_t typeIndex = types_.length(); 1240 if (!types_.append(typeDef) || !moduleIndices_.put(typeDef, typeIndex)) { 1241 return false; 1242 } 1243 } 1244 return true; 1245 } 1246 1247 template <typename T> 1248 [[nodiscard]] const TypeDef* addType(T&& type) { 1249 MutableRecGroup recGroup = startRecGroup(1); 1250 if (!recGroup) { 1251 return nullptr; 1252 } 1253 recGroup->type(0) = std::move(type); 1254 if (!endRecGroup()) { 1255 return nullptr; 1256 } 1257 return &this->type(length() - 1); 1258 } 1259 1260 const TypeDef& type(uint32_t index) const { return *types_[index]; } 1261 const TypeDef& operator[](uint32_t index) const { return *types_[index]; } 1262 1263 bool empty() const { return types_.empty(); } 1264 uint32_t length() const { return types_.length(); } 1265 1266 const SharedRecGroupVector& groups() const { return recGroups_; } 1267 1268 bool hasGcType() const { 1269 for (const SharedRecGroup& recGroup : groups()) { 1270 if (recGroup->hasGcType()) { 1271 return true; 1272 } 1273 } 1274 return false; 1275 } 1276 1277 // Map from type definition to index 1278 1279 uint32_t indexOf(const TypeDef& typeDef) const { 1280 auto moduleIndex = moduleIndices_.readonlyThreadsafeLookup(&typeDef); 1281 MOZ_RELEASE_ASSERT(moduleIndex.found()); 1282 return moduleIndex->value(); 1283 } 1284 }; 1285 1286 using SharedTypeContext = RefPtr<const TypeContext>; 1287 using MutableTypeContext = RefPtr<TypeContext>; 1288 1289 //========================================================================= 1290 // misc 1291 1292 /* static */ 1293 inline uintptr_t TypeDef::forIsoEquals(const TypeDef* typeDef, 1294 const RecGroup* recGroup) { 1295 // TypeDef is aligned sufficiently to allow a tag to distinguish a local type 1296 // reference (index) from a non-local type reference (pointer). 1297 static_assert(alignof(TypeDef) > 1); 1298 MOZ_ASSERT((uintptr_t(typeDef) & 0x1) == 0); 1299 1300 // Return a tagged index for local type references 1301 if (typeDef && &typeDef->recGroup() == recGroup) { 1302 // recGroup->indexOf result is expected to be not greater than MaxTypes, 1303 // and shall fit in 32-bit pointer without loss. 1304 static_assert(MaxTypes <= 0x7FFFFFFF); 1305 return (uintptr_t(recGroup->indexOf(typeDef)) << 1) | 0x1; 1306 } 1307 1308 // Return an untagged pointer for non-local type references 1309 return uintptr_t(typeDef); 1310 } 1311 1312 /* static */ 1313 inline IsoEqualsTypeCode IsoEqualsTypeCode::forIsoEquals( 1314 PackedTypeCode ptc, const RecGroup* recGroup) { 1315 IsoEqualsTypeCode mtc = {}; 1316 mtc.typeCode = PackedRepr(ptc.typeCode()); 1317 mtc.typeRef = TypeDef::forIsoEquals(ptc.typeDef(), recGroup); 1318 mtc.nullable = ptc.isNullable(); 1319 return mtc; 1320 } 1321 1322 template <class T> 1323 void PackedType<T>::AddRef() const { 1324 if (!isRefType()) { 1325 return; 1326 } 1327 refType().AddRef(); 1328 } 1329 template <class T> 1330 void PackedType<T>::Release() const { 1331 if (!isRefType()) { 1332 return; 1333 } 1334 refType().Release(); 1335 } 1336 1337 void RefType::AddRef() const { 1338 if (!isTypeRef()) { 1339 return; 1340 } 1341 typeDef()->AddRef(); 1342 } 1343 void RefType::Release() const { 1344 if (!isTypeRef()) { 1345 return; 1346 } 1347 typeDef()->Release(); 1348 } 1349 1350 void TypeDef::AddRef() const { recGroup().AddRef(); } 1351 void TypeDef::Release() const { recGroup().Release(); } 1352 1353 inline RefTypeHierarchy RefType::hierarchy() const { 1354 switch (kind()) { 1355 case RefType::Func: 1356 case RefType::NoFunc: 1357 return RefTypeHierarchy::Func; 1358 case RefType::Extern: 1359 case RefType::NoExtern: 1360 return RefTypeHierarchy::Extern; 1361 case RefType::Exn: 1362 case RefType::NoExn: 1363 return RefTypeHierarchy::Exn; 1364 case RefType::Any: 1365 case RefType::None: 1366 case RefType::I31: 1367 case RefType::Eq: 1368 case RefType::Struct: 1369 case RefType::Array: 1370 return RefTypeHierarchy::Any; 1371 case RefType::TypeRef: 1372 switch (typeDef()->kind()) { 1373 case TypeDefKind::Struct: 1374 case TypeDefKind::Array: 1375 return RefTypeHierarchy::Any; 1376 case TypeDefKind::Func: 1377 return RefTypeHierarchy::Func; 1378 case TypeDefKind::None: 1379 MOZ_CRASH(); 1380 } 1381 } 1382 MOZ_CRASH("switch is exhaustive"); 1383 } 1384 1385 inline TableRepr RefType::tableRepr() const { 1386 switch (hierarchy()) { 1387 case RefTypeHierarchy::Any: 1388 case RefTypeHierarchy::Extern: 1389 case RefTypeHierarchy::Exn: 1390 return TableRepr::Ref; 1391 case RefTypeHierarchy::Func: 1392 return TableRepr::Func; 1393 } 1394 MOZ_CRASH("switch is exhaustive"); 1395 } 1396 1397 inline bool RefType::isFuncHierarchy() const { 1398 return hierarchy() == RefTypeHierarchy::Func; 1399 } 1400 inline bool RefType::isExternHierarchy() const { 1401 return hierarchy() == RefTypeHierarchy::Extern; 1402 } 1403 inline bool RefType::isAnyHierarchy() const { 1404 return hierarchy() == RefTypeHierarchy::Any; 1405 } 1406 inline bool RefType::isExnHierarchy() const { 1407 return hierarchy() == RefTypeHierarchy::Exn; 1408 } 1409 1410 /* static */ 1411 inline bool RefType::isSubTypeOf(RefType subType, RefType superType) { 1412 // Anything is a subtype of itself. 1413 if (subType == superType) { 1414 return true; 1415 } 1416 1417 // A subtype must have the same nullability as the supertype or the 1418 // supertype must be nullable. 1419 if (subType.isNullable() && !superType.isNullable()) { 1420 return false; 1421 } 1422 1423 // Non type-index references are subtypes if they have the same kind 1424 if (!subType.isTypeRef() && !superType.isTypeRef() && 1425 subType.kind() == superType.kind()) { 1426 return true; 1427 } 1428 1429 // eq is a subtype of any 1430 if (subType.isEq() && superType.isAny()) { 1431 return true; 1432 } 1433 1434 // i31 is a subtype of eq and any 1435 if (subType.isI31() && (superType.isAny() || superType.isEq())) { 1436 return true; 1437 } 1438 1439 // Abstract struct/array are subtypes of eq and any 1440 if ((subType.isStruct() || subType.isArray()) && 1441 (superType.isAny() || superType.isEq())) { 1442 return true; 1443 } 1444 1445 // Concrete struct types are subtypes of struct, eq, and any 1446 if (subType.isTypeRef() && subType.typeDef()->isStructType() && 1447 (superType.isAny() || superType.isEq() || superType.isStruct())) { 1448 return true; 1449 } 1450 1451 // Concrete array types are subtypes of array, eq, and any 1452 if (subType.isTypeRef() && subType.typeDef()->isArrayType() && 1453 (superType.isAny() || superType.isEq() || superType.isArray())) { 1454 return true; 1455 } 1456 1457 // Concrete func types are subtypes of func 1458 if (subType.isTypeRef() && subType.typeDef()->isFuncType() && 1459 superType.isFunc()) { 1460 return true; 1461 } 1462 1463 // Type references can be subtypes 1464 if (subType.isTypeRef() && superType.isTypeRef()) { 1465 return TypeDef::isSubTypeOf(subType.typeDef(), superType.typeDef()); 1466 } 1467 1468 // nofunc is the bottom type of the func hierarchy 1469 if (subType.isNoFunc() && superType.hierarchy() == RefTypeHierarchy::Func) { 1470 return true; 1471 } 1472 1473 // noextern is the bottom type of the extern hierarchy 1474 if (subType.isNoExtern() && 1475 superType.hierarchy() == RefTypeHierarchy::Extern) { 1476 return true; 1477 } 1478 1479 // none is the bottom type of the any hierarchy 1480 if (subType.isNone() && superType.hierarchy() == RefTypeHierarchy::Any) { 1481 return true; 1482 } 1483 1484 // noexn is the bottom type of the exn hierarchy 1485 if (subType.isNoExn() && superType.hierarchy() == RefTypeHierarchy::Exn) { 1486 return true; 1487 } 1488 1489 return false; 1490 } 1491 1492 /* static */ 1493 inline bool RefType::castPossible(RefType sourceType, RefType destType) { 1494 // Nullable types always have null in common. 1495 if (sourceType.isNullable() && destType.isNullable()) { 1496 return true; 1497 } 1498 1499 // At least one of the types is non-nullable, so the only common values can be 1500 // non-null. Therefore, if either type is a bottom type, common values are 1501 // impossible. 1502 if (sourceType.isRefBottom() || destType.isRefBottom()) { 1503 return false; 1504 } 1505 1506 // After excluding bottom types, our type hierarchy is a tree, and after 1507 // excluding nulls, subtype relationships are sufficient to tell if the types 1508 // share any values. If neither type is a subtype of the other, then they are 1509 // on different branches of the tree and completely disjoint. 1510 RefType sourceNonNull = sourceType.withIsNullable(false); 1511 RefType destNonNull = destType.withIsNullable(false); 1512 return RefType::isSubTypeOf(sourceNonNull, destNonNull) || 1513 RefType::isSubTypeOf(destNonNull, sourceNonNull); 1514 } 1515 1516 //========================================================================= 1517 // [SMDOC] Signatures and runtime types 1518 // 1519 // TypeIdDesc describes the runtime representation of a TypeDef suitable for 1520 // type equality checks. The kind of representation depends on whether the type 1521 // is a function or a GC type. This design is in flux and will evolve. 1522 // 1523 // # Function types 1524 // 1525 // For functions in the general case, a FuncType is allocated and stored in a 1526 // process-wide hash table, so that pointer equality implies structural 1527 // equality. This process does not correctly handle type references (which would 1528 // require hash-consing of infinite-trees), but that's okay while 1529 // function-references and gc-types are experimental. 1530 // 1531 // A pointer to the hash table entry is stored in the global data 1532 // area for each instance, and TypeIdDesc gives the offset to this entry. 1533 // 1534 // ## Immediate function types 1535 // 1536 // As an optimization for the 99% case where the FuncType has a small number of 1537 // parameters, the FuncType is bit-packed into a uint32 immediate value so that 1538 // integer equality implies structural equality. Both cases can be handled with 1539 // a single comparison by always setting the LSB for the immediates 1540 // (the LSB is necessarily 0 for allocated FuncType pointers due to alignment). 1541 // 1542 // # GC types 1543 // 1544 // For GC types, an entry is always created in the global data area and a 1545 // unique RttValue (see wasm/WasmGcObject.h) is stored there. This RttValue 1546 // is the value given by 'rtt.canon $t' for each type definition. As each entry 1547 // is given a unique value and no canonicalization is done (which would require 1548 // hash-consing of infinite-trees), this is not yet spec compliant. 1549 // 1550 // # wasm::Instance and the global type context 1551 // 1552 // As GC objects may outlive the module they are created in, types are 1553 // additionally transferred to a wasm::Context (which is part of JSContext) upon 1554 // instantiation. This wasm::Context contains the 'global type context' that 1555 // RTTValues refer to by type index. Types are never freed from the global type 1556 // context as that would shift the index space. In the future, this will be 1557 // fixed. 1558 1559 } // namespace wasm 1560 } // namespace js 1561 1562 #endif // wasm_type_def_h