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 "wasm/WasmCompileArgs.h" 23 #include "wasm/WasmSerialize.h" 24 #include "wasm/WasmUtility.h" 25 #include "wasm/WasmValType.h" 26 27 namespace js { 28 namespace wasm { 29 30 using mozilla::MallocSizeOf; 31 32 // The FuncType class represents a WebAssembly function signature which takes a 33 // list of value types and returns an expression type. The engine uses two 34 // in-memory representations of the argument Vector's memory (when elements do 35 // not fit inline): normal malloc allocation (via SystemAllocPolicy) and 36 // allocation in a LifoAlloc (via LifoAllocPolicy). The former FuncType objects 37 // can have any lifetime since they own the memory. The latter FuncType objects 38 // must not outlive the associated LifoAlloc mark/release interval (which is 39 // currently the duration of module validation+compilation). Thus, long-lived 40 // objects like WasmModule must use malloced allocation. 41 42 class FuncType { 43 ValTypeVector args_; 44 ValTypeVector results_; 45 46 // Entry from JS to wasm via the JIT is currently unimplemented for 47 // functions that return multiple values. temporarilyUnsupportedResultCountForJitEntry()48 bool temporarilyUnsupportedResultCountForJitEntry() const { 49 return results().length() > MaxResultsForJitEntry; 50 } 51 // Calls out from wasm to JS that return multiple values is currently 52 // unsupported. temporarilyUnsupportedResultCountForJitExit()53 bool temporarilyUnsupportedResultCountForJitExit() const { 54 return results().length() > MaxResultsForJitExit; 55 } 56 // For JS->wasm jit entries, temporarily disallow certain types until the 57 // stubs generator is improved. 58 // * ref params may be nullable externrefs 59 // * ref results may not be type indices 60 // V128 types are excluded per spec but are guarded against separately. temporarilyUnsupportedReftypeForEntry()61 bool temporarilyUnsupportedReftypeForEntry() const { 62 for (ValType arg : args()) { 63 if (arg.isReference() && (!arg.isExternRef() || !arg.isNullable())) { 64 return true; 65 } 66 } 67 for (ValType result : results()) { 68 if (result.isTypeIndex()) { 69 return true; 70 } 71 } 72 return false; 73 } 74 // For wasm->JS jit exits, temporarily disallow certain types until 75 // the stubs generator is improved. 76 // * ref results may be nullable externrefs 77 // Unexposable types must be guarded against separately. temporarilyUnsupportedReftypeForExit()78 bool temporarilyUnsupportedReftypeForExit() const { 79 for (ValType result : results()) { 80 if (result.isReference() && 81 (!result.isExternRef() || !result.isNullable())) { 82 return true; 83 } 84 } 85 return false; 86 } 87 88 public: FuncType()89 FuncType() : args_(), results_() {} FuncType(ValTypeVector && args,ValTypeVector && results)90 FuncType(ValTypeVector&& args, ValTypeVector&& results) 91 : args_(std::move(args)), results_(std::move(results)) {} 92 clone(const FuncType & src)93 [[nodiscard]] bool clone(const FuncType& src) { 94 MOZ_ASSERT(args_.empty()); 95 MOZ_ASSERT(results_.empty()); 96 return args_.appendAll(src.args_) && results_.appendAll(src.results_); 97 } 98 renumber(const RenumberMap & map)99 void renumber(const RenumberMap& map) { 100 for (auto& arg : args_) { 101 arg.renumber(map); 102 } 103 for (auto& result : results_) { 104 result.renumber(map); 105 } 106 } offsetTypeIndex(uint32_t offsetBy)107 void offsetTypeIndex(uint32_t offsetBy) { 108 for (auto& arg : args_) { 109 arg.offsetTypeIndex(offsetBy); 110 } 111 for (auto& result : results_) { 112 result.offsetTypeIndex(offsetBy); 113 } 114 } 115 arg(unsigned i)116 ValType arg(unsigned i) const { return args_[i]; } args()117 const ValTypeVector& args() const { return args_; } result(unsigned i)118 ValType result(unsigned i) const { return results_[i]; } results()119 const ValTypeVector& results() const { return results_; } 120 hash()121 HashNumber hash() const { 122 HashNumber hn = 0; 123 for (const ValType& vt : args_) { 124 hn = mozilla::AddToHash(hn, HashNumber(vt.packed().bits())); 125 } 126 for (const ValType& vt : results_) { 127 hn = mozilla::AddToHash(hn, HashNumber(vt.packed().bits())); 128 } 129 return hn; 130 } 131 bool operator==(const FuncType& rhs) const { 132 return EqualContainers(args(), rhs.args()) && 133 EqualContainers(results(), rhs.results()); 134 } 135 bool operator!=(const FuncType& rhs) const { return !(*this == rhs); } 136 137 bool canHaveJitEntry() const; 138 bool canHaveJitExit() const; 139 hasUnexposableArgOrRet()140 bool hasUnexposableArgOrRet() const { 141 for (ValType arg : args()) { 142 if (!arg.isExposable()) { 143 return true; 144 } 145 } 146 for (ValType result : results()) { 147 if (!result.isExposable()) { 148 return true; 149 } 150 } 151 return false; 152 } 153 154 #ifdef WASM_PRIVATE_REFTYPES exposesTypeIndex()155 bool exposesTypeIndex() const { 156 for (const ValType& arg : args()) { 157 if (arg.isTypeIndex()) { 158 return true; 159 } 160 } 161 for (const ValType& result : results()) { 162 if (result.isTypeIndex()) { 163 return true; 164 } 165 } 166 return false; 167 } 168 #endif 169 170 WASM_DECLARE_SERIALIZABLE(FuncType) 171 }; 172 173 struct FuncTypeHashPolicy { 174 using Lookup = const FuncType&; hashFuncTypeHashPolicy175 static HashNumber hash(Lookup ft) { return ft.hash(); } matchFuncTypeHashPolicy176 static bool match(const FuncType* lhs, Lookup rhs) { return *lhs == rhs; } 177 }; 178 179 // Structure type. 180 // 181 // The Module owns a dense array of StructType values that represent the 182 // structure types that the module knows about. It is created from the sparse 183 // array of types in the ModuleEnvironment when the Module is created. 184 185 struct StructField { 186 FieldType type; 187 uint32_t offset; 188 bool isMutable; 189 }; 190 191 using StructFieldVector = Vector<StructField, 0, SystemAllocPolicy>; 192 193 class StructType { 194 public: 195 StructFieldVector fields_; // Field type, offset, and mutability 196 uint32_t size_; // The size of the type in bytes. 197 198 public: StructType()199 StructType() : fields_(), size_(0) {} 200 StructType(StructFieldVector && fields)201 explicit StructType(StructFieldVector&& fields) 202 : fields_(std::move(fields)), size_(0) {} 203 204 StructType(StructType&&) = default; 205 StructType& operator=(StructType&&) = default; 206 clone(const StructType & src)207 [[nodiscard]] bool clone(const StructType& src) { 208 if (!fields_.appendAll(src.fields_)) { 209 return false; 210 } 211 size_ = src.size_; 212 return true; 213 } 214 renumber(const RenumberMap & map)215 void renumber(const RenumberMap& map) { 216 for (auto& field : fields_) { 217 field.type.renumber(map); 218 } 219 } offsetTypeIndex(uint32_t offsetBy)220 void offsetTypeIndex(uint32_t offsetBy) { 221 for (auto& field : fields_) { 222 field.type.offsetTypeIndex(offsetBy); 223 } 224 } 225 isDefaultable()226 bool isDefaultable() const { 227 for (auto& field : fields_) { 228 if (!field.type.isDefaultable()) { 229 return false; 230 } 231 } 232 return true; 233 } 234 [[nodiscard]] bool computeLayout(); 235 236 WASM_DECLARE_SERIALIZABLE(StructType) 237 }; 238 239 using StructTypeVector = Vector<StructType, 0, SystemAllocPolicy>; 240 using StructTypePtrVector = Vector<const StructType*, 0, SystemAllocPolicy>; 241 242 // Array type 243 244 class ArrayType { 245 public: 246 FieldType elementType_; // field type 247 bool isMutable_; // mutability 248 249 public: ArrayType(FieldType elementType,bool isMutable)250 ArrayType(FieldType elementType, bool isMutable) 251 : elementType_(elementType), isMutable_(isMutable) {} 252 253 ArrayType(const ArrayType&) = default; 254 ArrayType& operator=(const ArrayType&) = default; 255 256 ArrayType(ArrayType&&) = default; 257 ArrayType& operator=(ArrayType&&) = default; 258 clone(const ArrayType & src)259 [[nodiscard]] bool clone(const ArrayType& src) { 260 elementType_ = src.elementType_; 261 isMutable_ = src.isMutable_; 262 return true; 263 } 264 renumber(const RenumberMap & map)265 void renumber(const RenumberMap& map) { elementType_.renumber(map); } offsetTypeIndex(uint32_t offsetBy)266 void offsetTypeIndex(uint32_t offsetBy) { 267 elementType_.offsetTypeIndex(offsetBy); 268 } 269 isDefaultable()270 bool isDefaultable() const { return elementType_.isDefaultable(); } 271 272 WASM_DECLARE_SERIALIZABLE(ArrayType) 273 }; 274 275 using ArrayTypeVector = Vector<ArrayType, 0, SystemAllocPolicy>; 276 using ArrayTypePtrVector = Vector<const ArrayType*, 0, SystemAllocPolicy>; 277 278 // A tagged container for the various types that can be present in a wasm 279 // module's type section. 280 281 enum class TypeDefKind : uint8_t { 282 None = 0, 283 Func, 284 Struct, 285 Array, 286 }; 287 288 class TypeDef { 289 TypeDefKind kind_; 290 union { 291 FuncType funcType_; 292 StructType structType_; 293 ArrayType arrayType_; 294 }; 295 296 public: TypeDef()297 TypeDef() : kind_(TypeDefKind::None) {} 298 TypeDef(FuncType && funcType)299 explicit TypeDef(FuncType&& funcType) 300 : kind_(TypeDefKind::Func), funcType_(std::move(funcType)) {} 301 TypeDef(StructType && structType)302 explicit TypeDef(StructType&& structType) 303 : kind_(TypeDefKind::Struct), structType_(std::move(structType)) {} 304 TypeDef(ArrayType && arrayType)305 explicit TypeDef(ArrayType&& arrayType) 306 : kind_(TypeDefKind::Array), arrayType_(std::move(arrayType)) {} 307 TypeDef(TypeDef && td)308 TypeDef(TypeDef&& td) noexcept : kind_(td.kind_) { 309 switch (kind_) { 310 case TypeDefKind::Func: 311 new (&funcType_) FuncType(std::move(td.funcType_)); 312 break; 313 case TypeDefKind::Struct: 314 new (&structType_) StructType(std::move(td.structType_)); 315 break; 316 case TypeDefKind::Array: 317 new (&arrayType_) ArrayType(std::move(td.arrayType_)); 318 break; 319 case TypeDefKind::None: 320 break; 321 } 322 } 323 ~TypeDef()324 ~TypeDef() { 325 switch (kind_) { 326 case TypeDefKind::Func: 327 funcType_.~FuncType(); 328 break; 329 case TypeDefKind::Struct: 330 structType_.~StructType(); 331 break; 332 case TypeDefKind::Array: 333 arrayType_.~ArrayType(); 334 break; 335 case TypeDefKind::None: 336 break; 337 } 338 } 339 340 TypeDef& operator=(TypeDef&& that) noexcept { 341 MOZ_ASSERT(isNone()); 342 switch (that.kind_) { 343 case TypeDefKind::Func: 344 new (&funcType_) FuncType(std::move(that.funcType_)); 345 break; 346 case TypeDefKind::Struct: 347 new (&structType_) StructType(std::move(that.structType_)); 348 break; 349 case TypeDefKind::Array: 350 new (&arrayType_) ArrayType(std::move(that.arrayType_)); 351 break; 352 case TypeDefKind::None: 353 break; 354 } 355 kind_ = that.kind_; 356 return *this; 357 } 358 clone(const TypeDef & src)359 [[nodiscard]] bool clone(const TypeDef& src) { 360 MOZ_ASSERT(isNone()); 361 kind_ = src.kind_; 362 switch (src.kind_) { 363 case TypeDefKind::Func: 364 new (&funcType_) FuncType(); 365 return funcType_.clone(src.funcType()); 366 case TypeDefKind::Struct: 367 new (&structType_) StructType(); 368 return structType_.clone(src.structType()); 369 case TypeDefKind::Array: 370 new (&arrayType_) ArrayType(src.arrayType()); 371 return true; 372 case TypeDefKind::None: 373 break; 374 } 375 MOZ_ASSERT_UNREACHABLE(); 376 return false; 377 } 378 kind()379 TypeDefKind kind() const { return kind_; } 380 isNone()381 bool isNone() const { return kind_ == TypeDefKind::None; } 382 isFuncType()383 bool isFuncType() const { return kind_ == TypeDefKind::Func; } 384 isStructType()385 bool isStructType() const { return kind_ == TypeDefKind::Struct; } 386 isArrayType()387 bool isArrayType() const { return kind_ == TypeDefKind::Array; } 388 funcType()389 const FuncType& funcType() const { 390 MOZ_ASSERT(isFuncType()); 391 return funcType_; 392 } 393 funcType()394 FuncType& funcType() { 395 MOZ_ASSERT(isFuncType()); 396 return funcType_; 397 } 398 structType()399 const StructType& structType() const { 400 MOZ_ASSERT(isStructType()); 401 return structType_; 402 } 403 structType()404 StructType& structType() { 405 MOZ_ASSERT(isStructType()); 406 return structType_; 407 } 408 arrayType()409 const ArrayType& arrayType() const { 410 MOZ_ASSERT(isArrayType()); 411 return arrayType_; 412 } 413 arrayType()414 ArrayType& arrayType() { 415 MOZ_ASSERT(isArrayType()); 416 return arrayType_; 417 } 418 renumber(const RenumberMap & map)419 void renumber(const RenumberMap& map) { 420 switch (kind_) { 421 case TypeDefKind::Func: 422 funcType_.renumber(map); 423 break; 424 case TypeDefKind::Struct: 425 structType_.renumber(map); 426 break; 427 case TypeDefKind::Array: 428 arrayType_.renumber(map); 429 break; 430 case TypeDefKind::None: 431 break; 432 } 433 } offsetTypeIndex(uint32_t offsetBy)434 void offsetTypeIndex(uint32_t offsetBy) { 435 switch (kind_) { 436 case TypeDefKind::Func: 437 funcType_.offsetTypeIndex(offsetBy); 438 break; 439 case TypeDefKind::Struct: 440 structType_.offsetTypeIndex(offsetBy); 441 break; 442 case TypeDefKind::Array: 443 arrayType_.offsetTypeIndex(offsetBy); 444 break; 445 case TypeDefKind::None: 446 break; 447 } 448 } 449 450 WASM_DECLARE_SERIALIZABLE(TypeDef) 451 }; 452 453 using TypeDefVector = Vector<TypeDef, 0, SystemAllocPolicy>; 454 455 template <typename T> 456 using DerivedTypeDefVector = Vector<T, 0, SystemAllocPolicy>; 457 458 // A type cache maintains a cache of equivalence and subtype relations between 459 // wasm types. This is required for the computation of equivalence and subtyping 460 // on recursive types. 461 // 462 // This class is not thread-safe and so must exist separately from TypeContext, 463 // which may be shared between multiple threads. 464 465 class TypeCache { 466 using TypeIndex = uint32_t; 467 using TypePair = uint64_t; 468 using TypeSet = HashSet<TypePair, DefaultHasher<TypePair>, SystemAllocPolicy>; 469 470 // Generates a hash key for the ordered pair (a, b). makeOrderedPair(TypeIndex a,TypeIndex b)471 static constexpr TypePair makeOrderedPair(TypeIndex a, TypeIndex b) { 472 return (TypePair(a) << 32) | TypePair(b); 473 } 474 475 // Generates a hash key for the unordered pair (a, b). makeUnorderedPair(TypeIndex a,TypeIndex b)476 static constexpr TypePair makeUnorderedPair(TypeIndex a, TypeIndex b) { 477 if (a < b) { 478 return (TypePair(a) << 32) | TypePair(b); 479 } 480 return (TypePair(b) << 32) | TypePair(a); 481 } 482 483 TypeSet equivalence_; 484 TypeSet subtype_; 485 486 public: 487 TypeCache() = default; 488 489 // Mark `a` as equivalent to `b` in the equivalence cache. markEquivalent(TypeIndex a,TypeIndex b)490 [[nodiscard]] bool markEquivalent(TypeIndex a, TypeIndex b) { 491 return equivalence_.put(makeUnorderedPair(a, b)); 492 } 493 // Unmark `a` as equivalent to `b` in the equivalence cache unmarkEquivalent(TypeIndex a,TypeIndex b)494 void unmarkEquivalent(TypeIndex a, TypeIndex b) { 495 equivalence_.remove(makeUnorderedPair(a, b)); 496 } 497 498 // Check if `a` is equivalent to `b` in the equivalence cache isEquivalent(TypeIndex a,TypeIndex b)499 bool isEquivalent(TypeIndex a, TypeIndex b) { 500 return equivalence_.has(makeUnorderedPair(a, b)); 501 } 502 503 // Mark `a` as a subtype of `b` in the subtype cache markSubtypeOf(TypeIndex a,TypeIndex b)504 [[nodiscard]] bool markSubtypeOf(TypeIndex a, TypeIndex b) { 505 return subtype_.put(makeOrderedPair(a, b)); 506 } 507 // Unmark `a` as a subtype of `b` in the subtype cache unmarkSubtypeOf(TypeIndex a,TypeIndex b)508 void unmarkSubtypeOf(TypeIndex a, TypeIndex b) { 509 subtype_.remove(makeOrderedPair(a, b)); 510 } 511 // Check if `a` is a subtype of `b` in the subtype cache isSubtypeOf(TypeIndex a,TypeIndex b)512 bool isSubtypeOf(TypeIndex a, TypeIndex b) { 513 return subtype_.has(makeOrderedPair(a, b)); 514 } 515 }; 516 517 // The result of an equivalence or subtyping check between types. 518 enum class TypeResult { 519 True, 520 False, 521 OOM, 522 }; 523 524 // A type context maintains an index space for TypeDef's that can be used to 525 // give ValType's meaning. It is used during compilation for modules, and 526 // during runtime for all instances. 527 528 class TypeContext { 529 FeatureArgs features_; 530 TypeDefVector types_; 531 532 public: TypeContext(const FeatureArgs & features,TypeDefVector && types)533 TypeContext(const FeatureArgs& features, TypeDefVector&& types) 534 : features_(features), types_(std::move(types)) {} 535 sizeOfExcludingThis(MallocSizeOf mallocSizeOf)536 size_t sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const { 537 return types_.sizeOfExcludingThis(mallocSizeOf); 538 } 539 540 // Disallow copy, allow move initialization 541 TypeContext(const TypeContext&) = delete; 542 TypeContext& operator=(const TypeContext&) = delete; 543 TypeContext(TypeContext&&) = default; 544 TypeContext& operator=(TypeContext&&) = default; 545 type(uint32_t index)546 TypeDef& type(uint32_t index) { return types_[index]; } type(uint32_t index)547 const TypeDef& type(uint32_t index) const { return types_[index]; } 548 549 TypeDef& operator[](uint32_t index) { return types_[index]; } 550 const TypeDef& operator[](uint32_t index) const { return types_[index]; } 551 length()552 uint32_t length() const { return types_.length(); } 553 554 template <typename U> append(U && typeDef)555 [[nodiscard]] bool append(U&& typeDef) { 556 return types_.append(std::forward<U>(typeDef)); 557 } resize(uint32_t length)558 [[nodiscard]] bool resize(uint32_t length) { return types_.resize(length); } 559 560 template <typename T> transferTypes(const DerivedTypeDefVector<T> & types,uint32_t * baseIndex)561 [[nodiscard]] bool transferTypes(const DerivedTypeDefVector<T>& types, 562 uint32_t* baseIndex) { 563 *baseIndex = length(); 564 if (!resize(*baseIndex + types.length())) { 565 return false; 566 } 567 for (uint32_t i = 0; i < types.length(); i++) { 568 if (!types_[*baseIndex + i].clone(types[i])) { 569 return false; 570 } 571 types_[*baseIndex + i].offsetTypeIndex(*baseIndex); 572 } 573 return true; 574 } 575 576 // FuncType accessors 577 isFuncType(uint32_t index)578 bool isFuncType(uint32_t index) const { return types_[index].isFuncType(); } isFuncType(RefType t)579 bool isFuncType(RefType t) const { 580 return t.isTypeIndex() && isFuncType(t.typeIndex()); 581 } 582 funcType(uint32_t index)583 FuncType& funcType(uint32_t index) { return types_[index].funcType(); } funcType(uint32_t index)584 const FuncType& funcType(uint32_t index) const { 585 return types_[index].funcType(); 586 } funcType(RefType t)587 FuncType& funcType(RefType t) { return funcType(t.typeIndex()); } funcType(RefType t)588 const FuncType& funcType(RefType t) const { return funcType(t.typeIndex()); } 589 590 // StructType accessors 591 isStructType(uint32_t index)592 bool isStructType(uint32_t index) const { 593 return types_[index].isStructType(); 594 } isStructType(RefType t)595 bool isStructType(RefType t) const { 596 return t.isTypeIndex() && isStructType(t.typeIndex()); 597 } 598 structType(uint32_t index)599 StructType& structType(uint32_t index) { return types_[index].structType(); } structType(uint32_t index)600 const StructType& structType(uint32_t index) const { 601 return types_[index].structType(); 602 } structType(RefType t)603 StructType& structType(RefType t) { return structType(t.typeIndex()); } structType(RefType t)604 const StructType& structType(RefType t) const { 605 return structType(t.typeIndex()); 606 } 607 608 // StructType accessors 609 isArrayType(uint32_t index)610 bool isArrayType(uint32_t index) const { return types_[index].isArrayType(); } isArrayType(RefType t)611 bool isArrayType(RefType t) const { 612 return t.isTypeIndex() && isArrayType(t.typeIndex()); 613 } 614 arrayType(uint32_t index)615 ArrayType& arrayType(uint32_t index) { return types_[index].arrayType(); } arrayType(uint32_t index)616 const ArrayType& arrayType(uint32_t index) const { 617 return types_[index].arrayType(); 618 } arrayType(RefType t)619 ArrayType& arrayType(RefType t) { return arrayType(t.typeIndex()); } arrayType(RefType t)620 const ArrayType& arrayType(RefType t) const { 621 return arrayType(t.typeIndex()); 622 } 623 624 // Type equivalence 625 626 template <class T> isEquivalent(T one,T two,TypeCache * cache)627 TypeResult isEquivalent(T one, T two, TypeCache* cache) const { 628 // Anything's equal to itself. 629 if (one == two) { 630 return TypeResult::True; 631 } 632 633 // A reference may be equal to another reference 634 if (one.isReference() && two.isReference()) { 635 return isRefEquivalent(one.refType(), two.refType(), cache); 636 } 637 638 #ifdef ENABLE_WASM_GC 639 // An rtt may be a equal to another rtt 640 if (one.isRtt() && two.isRtt()) { 641 return isTypeIndexEquivalent(one.typeIndex(), two.typeIndex(), cache); 642 } 643 #endif 644 645 return TypeResult::False; 646 } 647 648 TypeResult isRefEquivalent(RefType one, RefType two, TypeCache* cache) const; 649 #ifdef ENABLE_WASM_FUNCTION_REFERENCES 650 TypeResult isTypeIndexEquivalent(uint32_t one, uint32_t two, 651 TypeCache* cache) const; 652 #endif 653 #ifdef ENABLE_WASM_GC 654 TypeResult isStructEquivalent(uint32_t oneIndex, uint32_t twoIndex, 655 TypeCache* cache) const; 656 TypeResult isStructFieldEquivalent(const StructField one, 657 const StructField two, 658 TypeCache* cache) const; 659 TypeResult isArrayEquivalent(uint32_t oneIndex, uint32_t twoIndex, 660 TypeCache* cache) const; 661 TypeResult isArrayElementEquivalent(const ArrayType& one, 662 const ArrayType& two, 663 TypeCache* cache) const; 664 #endif 665 666 // Subtyping 667 668 template <class T> isSubtypeOf(T one,T two,TypeCache * cache)669 TypeResult isSubtypeOf(T one, T two, TypeCache* cache) const { 670 // Anything's a subtype of itself. 671 if (one == two) { 672 return TypeResult::True; 673 } 674 675 // A reference may be a subtype of another reference 676 if (one.isReference() && two.isReference()) { 677 return isRefSubtypeOf(one.refType(), two.refType(), cache); 678 } 679 680 // An rtt may be a subtype of another rtt 681 #ifdef ENABLE_WASM_GC 682 if (one.isRtt() && two.isRtt()) { 683 return isTypeIndexEquivalent(one.typeIndex(), two.typeIndex(), cache); 684 } 685 #endif 686 687 return TypeResult::False; 688 } 689 690 TypeResult isRefSubtypeOf(RefType one, RefType two, TypeCache* cache) const; 691 #ifdef ENABLE_WASM_FUNCTION_REFERENCES 692 TypeResult isTypeIndexSubtypeOf(uint32_t one, uint32_t two, 693 TypeCache* cache) const; 694 #endif 695 696 #ifdef ENABLE_WASM_GC 697 TypeResult isStructSubtypeOf(uint32_t oneIndex, uint32_t twoIndex, 698 TypeCache* cache) const; 699 TypeResult isStructFieldSubtypeOf(const StructField one, 700 const StructField two, 701 TypeCache* cache) const; 702 TypeResult isArraySubtypeOf(uint32_t oneIndex, uint32_t twoIndex, 703 TypeCache* cache) const; 704 TypeResult isArrayElementSubtypeOf(const ArrayType& one, const ArrayType& two, 705 TypeCache* cache) const; 706 #endif 707 }; 708 709 class TypeHandle { 710 private: 711 uint32_t index_; 712 713 public: TypeHandle(uint32_t index)714 explicit TypeHandle(uint32_t index) : index_(index) {} 715 716 TypeHandle(const TypeHandle&) = default; 717 TypeHandle& operator=(const TypeHandle&) = default; 718 get(TypeContext * tycx)719 TypeDef& get(TypeContext* tycx) const { return tycx->type(index_); } get(const TypeContext * tycx)720 const TypeDef& get(const TypeContext* tycx) const { 721 return tycx->type(index_); 722 } 723 index()724 uint32_t index() const { return index_; } 725 }; 726 727 } // namespace wasm 728 } // namespace js 729 730 #endif // wasm_type_def_h 731