1 use crate::{Tunables, WasmResult, error::OutOfMemory, wasm_unsupported}; 2 use alloc::borrow::Cow; 3 use alloc::boxed::Box; 4 use core::{fmt, ops::Range}; 5 use cranelift_entity::entity_impl; 6 use serde_derive::{Deserialize, Serialize}; 7 use smallvec::SmallVec; 8 use wasmtime_core::alloc::{TryClone, TryCollect as _}; 9 10 /// A trait for things that can trace all type-to-type edges, aka all type 11 /// indices within this thing. 12 pub trait TypeTrace { 13 /// Visit each edge. 14 /// 15 /// The function can break out of tracing by returning `Err(E)`. 16 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 17 where 18 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>; 19 20 /// Visit each edge, mutably. 21 /// 22 /// Allows updating edges. 23 /// 24 /// The function can break out of tracing by returning `Err(E)`. 25 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 26 where 27 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>; 28 29 /// Trace all `VMSharedTypeIndex` edges, ignoring other edges. 30 fn trace_engine_indices<F, E>(&self, func: &mut F) -> Result<(), E> 31 where 32 F: FnMut(VMSharedTypeIndex) -> Result<(), E>, 33 { 34 self.trace(&mut |idx| match idx { 35 EngineOrModuleTypeIndex::Engine(idx) => func(idx), 36 EngineOrModuleTypeIndex::Module(_) | EngineOrModuleTypeIndex::RecGroup(_) => Ok(()), 37 }) 38 } 39 40 /// Canonicalize `self` by rewriting all type references inside `self` from 41 /// module-level interned type indices to engine-level interned type 42 /// indices. 43 /// 44 /// This produces types that are suitable for usage by the runtime (only 45 /// contains `VMSharedTypeIndex` type references). 46 /// 47 /// This does not produce types that are suitable for hash consing types 48 /// (must have recgroup-relative indices for type indices referencing other 49 /// types in the same recgroup). 50 fn canonicalize_for_runtime_usage<F>(&mut self, module_to_engine: &mut F) 51 where 52 F: FnMut(ModuleInternedTypeIndex) -> VMSharedTypeIndex, 53 { 54 self.trace_mut::<_, ()>(&mut |idx| match idx { 55 EngineOrModuleTypeIndex::Engine(_) => Ok(()), 56 EngineOrModuleTypeIndex::Module(module_index) => { 57 let engine_index = module_to_engine(*module_index); 58 *idx = EngineOrModuleTypeIndex::Engine(engine_index); 59 Ok(()) 60 } 61 EngineOrModuleTypeIndex::RecGroup(_) => { 62 panic!("should not already be canonicalized for hash consing") 63 } 64 }) 65 .unwrap() 66 } 67 68 /// Is this type canonicalized for runtime usage? 69 fn is_canonicalized_for_runtime_usage(&self) -> bool { 70 self.trace(&mut |idx| match idx { 71 EngineOrModuleTypeIndex::Engine(_) => Ok(()), 72 EngineOrModuleTypeIndex::Module(_) | EngineOrModuleTypeIndex::RecGroup(_) => Err(()), 73 }) 74 .is_ok() 75 } 76 77 /// Canonicalize `self` by rewriting all type references inside `self` from 78 /// module-level interned type indices to either engine-level interned type 79 /// indices or recgroup-relative indices. 80 /// 81 /// This produces types that are suitable for hash consing and deduplicating 82 /// recgroups (types may have recgroup-relative indices for references to 83 /// other types within the same recgroup). 84 /// 85 /// This does *not* produce types that are suitable for usage by the runtime 86 /// (only contain `VMSharedTypeIndex` type references). 87 fn canonicalize_for_hash_consing<F>( 88 &mut self, 89 rec_group_range: Range<ModuleInternedTypeIndex>, 90 module_to_engine: &mut F, 91 ) where 92 F: FnMut(ModuleInternedTypeIndex) -> VMSharedTypeIndex, 93 { 94 self.trace_mut::<_, ()>(&mut |idx| match *idx { 95 EngineOrModuleTypeIndex::Engine(_) => Ok(()), 96 EngineOrModuleTypeIndex::Module(module_index) => { 97 *idx = if rec_group_range.start <= module_index { 98 // Any module index within the recursion group gets 99 // translated into a recgroup-relative index. 100 debug_assert!(module_index < rec_group_range.end); 101 let relative = module_index.as_u32() - rec_group_range.start.as_u32(); 102 let relative = RecGroupRelativeTypeIndex::from_u32(relative); 103 EngineOrModuleTypeIndex::RecGroup(relative) 104 } else { 105 // Cross-group indices are translated directly into 106 // `VMSharedTypeIndex`es. 107 debug_assert!(module_index < rec_group_range.start); 108 EngineOrModuleTypeIndex::Engine(module_to_engine(module_index)) 109 }; 110 Ok(()) 111 } 112 EngineOrModuleTypeIndex::RecGroup(_) => { 113 panic!("should not already be canonicalized for hash consing") 114 } 115 }) 116 .unwrap() 117 } 118 119 /// Is this type canonicalized for hash consing? 120 fn is_canonicalized_for_hash_consing(&self) -> bool { 121 self.trace(&mut |idx| match idx { 122 EngineOrModuleTypeIndex::Engine(_) | EngineOrModuleTypeIndex::RecGroup(_) => Ok(()), 123 EngineOrModuleTypeIndex::Module(_) => Err(()), 124 }) 125 .is_ok() 126 } 127 } 128 129 /// WebAssembly value type -- equivalent of `wasmparser::ValType`. 130 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 131 pub enum WasmValType { 132 /// I32 type 133 I32, 134 /// I64 type 135 I64, 136 /// F32 type 137 F32, 138 /// F64 type 139 F64, 140 /// V128 type 141 V128, 142 /// Reference type 143 Ref(WasmRefType), 144 } 145 146 impl TryClone for WasmValType { 147 fn try_clone(&self) -> Result<Self, OutOfMemory> { 148 Ok(*self) 149 } 150 } 151 152 impl fmt::Display for WasmValType { 153 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 154 match self { 155 WasmValType::I32 => write!(f, "i32"), 156 WasmValType::I64 => write!(f, "i64"), 157 WasmValType::F32 => write!(f, "f32"), 158 WasmValType::F64 => write!(f, "f64"), 159 WasmValType::V128 => write!(f, "v128"), 160 WasmValType::Ref(rt) => write!(f, "{rt}"), 161 } 162 } 163 } 164 165 impl TypeTrace for WasmValType { 166 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 167 where 168 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 169 { 170 match self { 171 WasmValType::Ref(r) => r.trace(func), 172 WasmValType::I32 173 | WasmValType::I64 174 | WasmValType::F32 175 | WasmValType::F64 176 | WasmValType::V128 => Ok(()), 177 } 178 } 179 180 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 181 where 182 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 183 { 184 match self { 185 WasmValType::Ref(r) => r.trace_mut(func), 186 WasmValType::I32 187 | WasmValType::I64 188 | WasmValType::F32 189 | WasmValType::F64 190 | WasmValType::V128 => Ok(()), 191 } 192 } 193 } 194 195 impl WasmValType { 196 /// Is this a type that is represented as a `VMGcRef`? 197 #[inline] 198 pub fn is_vmgcref_type(&self) -> bool { 199 match self { 200 WasmValType::Ref(r) => r.is_vmgcref_type(), 201 _ => false, 202 } 203 } 204 205 /// Is this a type that is represented as a `VMGcRef` and is additionally 206 /// not an `i31`? 207 /// 208 /// That is, is this a type that actually refers to an object allocated in a 209 /// GC heap? 210 #[inline] 211 pub fn is_vmgcref_type_and_not_i31(&self) -> bool { 212 match self { 213 WasmValType::Ref(r) => r.is_vmgcref_type_and_not_i31(), 214 _ => false, 215 } 216 } 217 218 fn trampoline_type(&self) -> Self { 219 match self { 220 WasmValType::Ref(r) => WasmValType::Ref(WasmRefType { 221 nullable: true, 222 heap_type: r.heap_type.top().into(), 223 }), 224 WasmValType::I32 225 | WasmValType::I64 226 | WasmValType::F32 227 | WasmValType::F64 228 | WasmValType::V128 => *self, 229 } 230 } 231 232 /// Attempt to build a `WasmValType` with the passed number of bits. 233 /// 234 /// Panics if the number of bits doesn't map to a WASM int type. 235 pub fn int_from_bits(bits: u8) -> Self { 236 match bits { 237 32 => Self::I32, 238 64 => Self::I64, 239 size => panic!("invalid int bits for WasmValType: {size}"), 240 } 241 } 242 243 /// Returns the contained reference type. 244 /// 245 /// Panics if the value type is not a vmgcref 246 pub fn unwrap_ref_type(&self) -> WasmRefType { 247 match self { 248 WasmValType::Ref(ref_type) => *ref_type, 249 _ => panic!("Called WasmValType::unwrap_ref_type on non-reference type"), 250 } 251 } 252 } 253 254 /// WebAssembly reference type -- equivalent of `wasmparser`'s RefType 255 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 256 pub struct WasmRefType { 257 /// Whether or not this reference is nullable. 258 pub nullable: bool, 259 /// The heap type that this reference contains. 260 pub heap_type: WasmHeapType, 261 } 262 263 impl TypeTrace for WasmRefType { 264 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 265 where 266 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 267 { 268 self.heap_type.trace(func) 269 } 270 271 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 272 where 273 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 274 { 275 self.heap_type.trace_mut(func) 276 } 277 } 278 279 impl WasmRefType { 280 /// Shorthand for `externref` 281 pub const EXTERNREF: WasmRefType = WasmRefType { 282 nullable: true, 283 heap_type: WasmHeapType::Extern, 284 }; 285 /// Shorthand for `funcref` 286 pub const FUNCREF: WasmRefType = WasmRefType { 287 nullable: true, 288 heap_type: WasmHeapType::Func, 289 }; 290 291 /// Is this a type that is represented as a `VMGcRef`? 292 #[inline] 293 pub fn is_vmgcref_type(&self) -> bool { 294 self.heap_type.is_vmgcref_type() 295 } 296 297 /// Is this a type that is represented as a `VMGcRef` and is additionally 298 /// not an `i31`? 299 /// 300 /// That is, is this a type that actually refers to an object allocated in a 301 /// GC heap? 302 #[inline] 303 pub fn is_vmgcref_type_and_not_i31(&self) -> bool { 304 self.heap_type.is_vmgcref_type_and_not_i31() 305 } 306 } 307 308 impl fmt::Display for WasmRefType { 309 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 310 match *self { 311 Self::FUNCREF => write!(f, "funcref"), 312 Self::EXTERNREF => write!(f, "externref"), 313 _ => { 314 if self.nullable { 315 write!(f, "(ref null {})", self.heap_type) 316 } else { 317 write!(f, "(ref {})", self.heap_type) 318 } 319 } 320 } 321 } 322 } 323 324 /// An interned type index, either at the module or engine level. 325 /// 326 /// Roughly equivalent to `wasmparser::UnpackedIndex`, although doesn't have to 327 /// concern itself with recursion-group-local indices. 328 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 329 pub enum EngineOrModuleTypeIndex { 330 /// An index within an engine, canonicalized among all modules that can 331 /// interact with each other. 332 Engine(VMSharedTypeIndex), 333 334 /// An index within the current Wasm module, canonicalized within just this 335 /// current module. 336 Module(ModuleInternedTypeIndex), 337 338 /// An index within the containing type's rec group. This is only used when 339 /// hashing and canonicalizing rec groups, and should never appear outside 340 /// of the engine's type registry. 341 RecGroup(RecGroupRelativeTypeIndex), 342 } 343 344 impl From<ModuleInternedTypeIndex> for EngineOrModuleTypeIndex { 345 #[inline] 346 fn from(i: ModuleInternedTypeIndex) -> Self { 347 Self::Module(i) 348 } 349 } 350 351 impl From<VMSharedTypeIndex> for EngineOrModuleTypeIndex { 352 #[inline] 353 fn from(i: VMSharedTypeIndex) -> Self { 354 Self::Engine(i) 355 } 356 } 357 358 impl From<RecGroupRelativeTypeIndex> for EngineOrModuleTypeIndex { 359 #[inline] 360 fn from(i: RecGroupRelativeTypeIndex) -> Self { 361 Self::RecGroup(i) 362 } 363 } 364 365 impl fmt::Display for EngineOrModuleTypeIndex { 366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 367 match self { 368 Self::Engine(i) => write!(f, "(engine {})", i.bits()), 369 Self::Module(i) => write!(f, "(module {})", i.as_u32()), 370 Self::RecGroup(i) => write!(f, "(recgroup {})", i.as_u32()), 371 } 372 } 373 } 374 375 impl EngineOrModuleTypeIndex { 376 /// Is this an engine-level type index? 377 pub fn is_engine_type_index(self) -> bool { 378 matches!(self, Self::Engine(_)) 379 } 380 381 /// Get the underlying engine-level type index, if any. 382 #[inline] 383 pub fn as_engine_type_index(self) -> Option<VMSharedTypeIndex> { 384 match self { 385 Self::Engine(e) => Some(e), 386 Self::RecGroup(_) | Self::Module(_) => None, 387 } 388 } 389 390 /// Get the underlying engine-level type index, or panic. 391 #[track_caller] 392 #[inline] 393 pub fn unwrap_engine_type_index(self) -> VMSharedTypeIndex { 394 match self.as_engine_type_index() { 395 Some(x) => x, 396 None => panic!("`unwrap_engine_type_index` on {self:?}"), 397 } 398 } 399 400 /// Is this an module-level type index? 401 pub fn is_module_type_index(self) -> bool { 402 matches!(self, Self::Module(_)) 403 } 404 405 /// Get the underlying module-level type index, if any. 406 pub fn as_module_type_index(self) -> Option<ModuleInternedTypeIndex> { 407 match self { 408 Self::Module(e) => Some(e), 409 Self::RecGroup(_) | Self::Engine(_) => None, 410 } 411 } 412 413 /// Get the underlying module-level type index, or panic. 414 #[track_caller] 415 pub fn unwrap_module_type_index(self) -> ModuleInternedTypeIndex { 416 match self.as_module_type_index() { 417 Some(x) => x, 418 None => panic!("`unwrap_module_type_index` on {self:?}"), 419 } 420 } 421 422 /// Is this an recgroup-level type index? 423 pub fn is_rec_group_type_index(self) -> bool { 424 matches!(self, Self::RecGroup(_)) 425 } 426 427 /// Get the underlying recgroup-level type index, if any. 428 pub fn as_rec_group_type_index(self) -> Option<RecGroupRelativeTypeIndex> { 429 match self { 430 Self::RecGroup(r) => Some(r), 431 Self::Module(_) | Self::Engine(_) => None, 432 } 433 } 434 435 /// Get the underlying module-level type index, or panic. 436 #[track_caller] 437 pub fn unwrap_rec_group_type_index(self) -> RecGroupRelativeTypeIndex { 438 match self.as_rec_group_type_index() { 439 Some(x) => x, 440 None => panic!("`unwrap_rec_group_type_index` on {self:?}"), 441 } 442 } 443 } 444 445 /// WebAssembly heap type -- equivalent of `wasmparser`'s HeapType 446 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 447 #[expect(missing_docs, reason = "self-describing variants")] 448 pub enum WasmHeapType { 449 // External types. 450 Extern, 451 NoExtern, 452 453 // Function types. 454 Func, 455 ConcreteFunc(EngineOrModuleTypeIndex), 456 NoFunc, 457 458 // Exception types. 459 Exn, 460 ConcreteExn(EngineOrModuleTypeIndex), 461 NoExn, 462 463 // Continuation types. 464 Cont, 465 ConcreteCont(EngineOrModuleTypeIndex), 466 NoCont, 467 468 // Internal types. 469 Any, 470 Eq, 471 I31, 472 Array, 473 ConcreteArray(EngineOrModuleTypeIndex), 474 Struct, 475 ConcreteStruct(EngineOrModuleTypeIndex), 476 None, 477 } 478 479 impl From<WasmHeapTopType> for WasmHeapType { 480 #[inline] 481 fn from(value: WasmHeapTopType) -> Self { 482 match value { 483 WasmHeapTopType::Extern => Self::Extern, 484 WasmHeapTopType::Any => Self::Any, 485 WasmHeapTopType::Func => Self::Func, 486 WasmHeapTopType::Cont => Self::Cont, 487 WasmHeapTopType::Exn => Self::Exn, 488 } 489 } 490 } 491 492 impl From<WasmHeapBottomType> for WasmHeapType { 493 #[inline] 494 fn from(value: WasmHeapBottomType) -> Self { 495 match value { 496 WasmHeapBottomType::NoExtern => Self::NoExtern, 497 WasmHeapBottomType::None => Self::None, 498 WasmHeapBottomType::NoFunc => Self::NoFunc, 499 WasmHeapBottomType::NoCont => Self::NoCont, 500 WasmHeapBottomType::NoExn => Self::NoExn, 501 } 502 } 503 } 504 505 impl fmt::Display for WasmHeapType { 506 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 507 match self { 508 Self::Extern => write!(f, "extern"), 509 Self::NoExtern => write!(f, "noextern"), 510 Self::Func => write!(f, "func"), 511 Self::ConcreteFunc(i) => write!(f, "func {i}"), 512 Self::NoFunc => write!(f, "nofunc"), 513 Self::Cont => write!(f, "cont"), 514 Self::ConcreteCont(i) => write!(f, "cont {i}"), 515 Self::NoCont => write!(f, "nocont"), 516 Self::Any => write!(f, "any"), 517 Self::Eq => write!(f, "eq"), 518 Self::I31 => write!(f, "i31"), 519 Self::Array => write!(f, "array"), 520 Self::ConcreteArray(i) => write!(f, "array {i}"), 521 Self::Struct => write!(f, "struct"), 522 Self::ConcreteStruct(i) => write!(f, "struct {i}"), 523 Self::Exn => write!(f, "exn"), 524 Self::ConcreteExn(i) => write!(f, "exn {i}"), 525 Self::NoExn => write!(f, "noexn"), 526 Self::None => write!(f, "none"), 527 } 528 } 529 } 530 531 impl TypeTrace for WasmHeapType { 532 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 533 where 534 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 535 { 536 match *self { 537 Self::ConcreteArray(i) => func(i), 538 Self::ConcreteFunc(i) => func(i), 539 Self::ConcreteStruct(i) => func(i), 540 Self::ConcreteCont(i) => func(i), 541 _ => Ok(()), 542 } 543 } 544 545 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 546 where 547 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 548 { 549 match self { 550 Self::ConcreteArray(i) => func(i), 551 Self::ConcreteFunc(i) => func(i), 552 Self::ConcreteStruct(i) => func(i), 553 Self::ConcreteCont(i) => func(i), 554 _ => Ok(()), 555 } 556 } 557 } 558 559 impl WasmHeapType { 560 /// Is this a type that is represented as a `VMGcRef`? 561 #[inline] 562 pub fn is_vmgcref_type(&self) -> bool { 563 match self.top() { 564 // All `t <: (ref null any)`, `t <: (ref null extern)`, 565 // and `t <: (ref null exn)` are represented as 566 // `VMGcRef`s. 567 WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => true, 568 569 // All `t <: (ref null func)` are not. 570 WasmHeapTopType::Func => false, 571 WasmHeapTopType::Cont => false, 572 } 573 } 574 575 /// Is this a type that is represented as a `VMGcRef` and is additionally 576 /// not an `i31`? 577 /// 578 /// That is, is this a type that actually refers to an object allocated in a 579 /// GC heap? 580 #[inline] 581 pub fn is_vmgcref_type_and_not_i31(&self) -> bool { 582 self.is_vmgcref_type() && *self != Self::I31 583 } 584 585 /// Is this heap type the top of its type hierarchy? 586 #[inline] 587 pub fn is_top(&self) -> bool { 588 *self == Self::from(self.top()) 589 } 590 591 /// Get this type's top type. 592 #[inline] 593 pub fn top(&self) -> WasmHeapTopType { 594 match self { 595 WasmHeapType::Extern | WasmHeapType::NoExtern => WasmHeapTopType::Extern, 596 597 WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => { 598 WasmHeapTopType::Func 599 } 600 601 WasmHeapType::Cont | WasmHeapType::ConcreteCont(_) | WasmHeapType::NoCont => { 602 WasmHeapTopType::Cont 603 } 604 605 WasmHeapType::Exn | WasmHeapType::ConcreteExn(_) | WasmHeapType::NoExn => { 606 WasmHeapTopType::Exn 607 } 608 609 WasmHeapType::Any 610 | WasmHeapType::Eq 611 | WasmHeapType::I31 612 | WasmHeapType::Array 613 | WasmHeapType::ConcreteArray(_) 614 | WasmHeapType::Struct 615 | WasmHeapType::ConcreteStruct(_) 616 | WasmHeapType::None => WasmHeapTopType::Any, 617 } 618 } 619 620 /// Is this heap type the bottom of its type hierarchy? 621 #[inline] 622 pub fn is_bottom(&self) -> bool { 623 *self == Self::from(self.bottom()) 624 } 625 626 /// Get this type's bottom type. 627 #[inline] 628 pub fn bottom(&self) -> WasmHeapBottomType { 629 match self { 630 WasmHeapType::Extern | WasmHeapType::NoExtern => WasmHeapBottomType::NoExtern, 631 632 WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => { 633 WasmHeapBottomType::NoFunc 634 } 635 636 WasmHeapType::Cont | WasmHeapType::ConcreteCont(_) | WasmHeapType::NoCont => { 637 WasmHeapBottomType::NoCont 638 } 639 640 WasmHeapType::Exn | WasmHeapType::ConcreteExn(_) | WasmHeapType::NoExn => { 641 WasmHeapBottomType::NoExn 642 } 643 644 WasmHeapType::Any 645 | WasmHeapType::Eq 646 | WasmHeapType::I31 647 | WasmHeapType::Array 648 | WasmHeapType::ConcreteArray(_) 649 | WasmHeapType::Struct 650 | WasmHeapType::ConcreteStruct(_) 651 | WasmHeapType::None => WasmHeapBottomType::None, 652 } 653 } 654 } 655 656 /// A top heap type. 657 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] 658 pub enum WasmHeapTopType { 659 /// The common supertype of all external references. 660 Extern, 661 /// The common supertype of all internal references. 662 Any, 663 /// The common supertype of all function references. 664 Func, 665 /// The common supertype of all exception references. 666 Exn, 667 /// The common supertype of all continuation references. 668 Cont, 669 } 670 671 /// A bottom heap type. 672 #[derive(Debug, Clone, Copy, Eq, PartialEq)] 673 pub enum WasmHeapBottomType { 674 /// The common subtype of all external references. 675 NoExtern, 676 /// The common subtype of all internal references. 677 None, 678 /// The common subtype of all function references. 679 NoFunc, 680 /// The common subtype of all exception references. 681 NoExn, 682 /// The common subtype of all continuation references. 683 NoCont, 684 } 685 686 /// WebAssembly function type -- equivalent of `wasmparser`'s FuncType. 687 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 688 pub struct WasmFuncType { 689 params: Box<[WasmValType]>, 690 non_i31_gc_ref_params_count: usize, 691 returns: Box<[WasmValType]>, 692 non_i31_gc_ref_returns_count: usize, 693 } 694 695 impl TryClone for WasmFuncType { 696 fn try_clone(&self) -> Result<Self, OutOfMemory> { 697 Ok(WasmFuncType { 698 params: TryClone::try_clone(&self.params)?, 699 non_i31_gc_ref_params_count: self.non_i31_gc_ref_params_count, 700 returns: TryClone::try_clone(&self.returns)?, 701 non_i31_gc_ref_returns_count: self.non_i31_gc_ref_returns_count, 702 }) 703 } 704 } 705 706 impl fmt::Display for WasmFuncType { 707 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 708 write!(f, "(func")?; 709 if !self.params.is_empty() { 710 write!(f, " (param")?; 711 for p in self.params.iter() { 712 write!(f, " {p}")?; 713 } 714 write!(f, ")")?; 715 } 716 if !self.returns.is_empty() { 717 write!(f, " (result")?; 718 for r in self.returns.iter() { 719 write!(f, " {r}")?; 720 } 721 write!(f, ")")?; 722 } 723 write!(f, ")") 724 } 725 } 726 727 impl TypeTrace for WasmFuncType { 728 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 729 where 730 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 731 { 732 for p in self.params.iter() { 733 p.trace(func)?; 734 } 735 for r in self.returns.iter() { 736 r.trace(func)?; 737 } 738 Ok(()) 739 } 740 741 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 742 where 743 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 744 { 745 for p in self.params.iter_mut() { 746 p.trace_mut(func)?; 747 } 748 for r in self.returns.iter_mut() { 749 r.trace_mut(func)?; 750 } 751 Ok(()) 752 } 753 } 754 755 impl WasmFuncType { 756 /// Creates a new function type from the provided `params` and `returns`. 757 #[inline] 758 pub fn new(params: Box<[WasmValType]>, returns: Box<[WasmValType]>) -> Self { 759 let non_i31_gc_ref_params_count = params 760 .iter() 761 .filter(|p| p.is_vmgcref_type_and_not_i31()) 762 .count(); 763 let non_i31_gc_ref_returns_count = returns 764 .iter() 765 .filter(|r| r.is_vmgcref_type_and_not_i31()) 766 .count(); 767 WasmFuncType { 768 params, 769 non_i31_gc_ref_params_count, 770 returns, 771 non_i31_gc_ref_returns_count, 772 } 773 } 774 775 /// Function params types. 776 #[inline] 777 pub fn params(&self) -> &[WasmValType] { 778 &self.params 779 } 780 781 /// How many `externref`s are in this function's params? 782 #[inline] 783 pub fn non_i31_gc_ref_params_count(&self) -> usize { 784 self.non_i31_gc_ref_params_count 785 } 786 787 /// Returns params types. 788 #[inline] 789 pub fn returns(&self) -> &[WasmValType] { 790 &self.returns 791 } 792 793 /// How many `externref`s are in this function's returns? 794 #[inline] 795 pub fn non_i31_gc_ref_returns_count(&self) -> usize { 796 self.non_i31_gc_ref_returns_count 797 } 798 799 /// Is this function type compatible with trampoline usage in Wasmtime? 800 pub fn is_trampoline_type(&self) -> bool { 801 self.params().iter().all(|p| *p == p.trampoline_type()) 802 && self.returns().iter().all(|r| *r == r.trampoline_type()) 803 } 804 805 /// Get the version of this function type that is suitable for usage as a 806 /// trampoline in Wasmtime. 807 /// 808 /// If this function is suitable for trampoline usage as-is, then a borrowed 809 /// `Cow` is returned. If it must be tweaked for trampoline usage, then an 810 /// owned `Cow` is returned. 811 /// 812 /// ## What is a trampoline type? 813 /// 814 /// All reference types in parameters and results are mapped to their 815 /// nullable top type, e.g. `(ref $my_struct_type)` becomes `(ref null 816 /// any)`. 817 /// 818 /// This allows us to share trampolines between functions whose signatures 819 /// both map to the same trampoline type. It also allows the host to satisfy 820 /// a Wasm module's function import of type `S` with a function of type `T` 821 /// where `T <: S`, even when the Wasm module never defines the type `T` 822 /// (and might never even be able to!) 823 /// 824 /// The flip side is that this adds a constraint to our trampolines: they 825 /// can only pass references around (e.g. move a reference from one calling 826 /// convention's location to another's) and may not actually inspect the 827 /// references themselves (unless the trampolines start doing explicit, 828 /// fallible downcasts, but if we ever need that, then we might want to 829 /// redesign this stuff). 830 pub fn trampoline_type(&self) -> Result<Cow<'_, Self>, OutOfMemory> { 831 if self.is_trampoline_type() { 832 return Ok(Cow::Borrowed(self)); 833 } 834 835 Ok(Cow::Owned(Self::new( 836 self.params() 837 .iter() 838 .map(|p| p.trampoline_type()) 839 .try_collect()?, 840 self.returns() 841 .iter() 842 .map(|r| r.trampoline_type()) 843 .try_collect()?, 844 ))) 845 } 846 } 847 848 /// WebAssembly continuation type -- equivalent of `wasmparser`'s ContType. 849 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] 850 pub struct WasmContType(EngineOrModuleTypeIndex); 851 852 impl fmt::Display for WasmContType { 853 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 854 write!(f, "(cont {})", self.0) 855 } 856 } 857 858 impl WasmContType { 859 /// Constructs a new continuation type. 860 pub fn new(idx: EngineOrModuleTypeIndex) -> Self { 861 WasmContType(idx) 862 } 863 864 /// Returns the (module interned) index to the underlying function type. 865 pub fn unwrap_module_type_index(self) -> ModuleInternedTypeIndex { 866 match self.0 { 867 EngineOrModuleTypeIndex::Engine(_) => panic!("not module interned"), 868 EngineOrModuleTypeIndex::Module(idx) => idx, 869 EngineOrModuleTypeIndex::RecGroup(_) => todo!(), 870 } 871 } 872 } 873 874 impl TypeTrace for WasmContType { 875 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 876 where 877 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 878 { 879 func(self.0) 880 } 881 882 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 883 where 884 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 885 { 886 func(&mut self.0) 887 } 888 } 889 890 /// WebAssembly exception type. 891 /// 892 /// This "exception type" is not a Wasm language-level 893 /// concept. Instead, it denotes an *exception object signature* -- 894 /// the types of the payload values. 895 /// 896 /// In contrast, at the Wasm language level, exception objects are 897 /// associated with specific tags, and these tags refer to their 898 /// signatures (function types). However, tags are *nominal*: like 899 /// memories and tables, a separate instance of a tag exists for every 900 /// instance of the defining module, and these tag instances can be 901 /// imported and exported. At runtime we handle tags like we do 902 /// memories and tables, but these runtime instances do not exist in 903 /// the type system here. 904 /// 905 /// Because the Wasm type system does not have concrete `exn` types 906 /// (i.e., the heap-type lattice has only top `exn` and bottom 907 /// `noexn`), we are free to decide what we mean by "concrete type" 908 /// here. Thus, we define an "exception type" to refer to the 909 /// type-level *signature*. When a particular *exception object* is 910 /// created in a store, it can be associated with a particular *tag 911 /// instance* also in that store, and the compatibility is checked 912 /// (the tag's function type must match the function type in the 913 /// associated WasmExnType). 914 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 915 pub struct WasmExnType { 916 /// The function type from which we get our signature. We hold 917 /// this directly so that we can efficiently derive a FuncType 918 /// without re-interning the field types. 919 pub func_ty: EngineOrModuleTypeIndex, 920 /// The fields (payload values) that make up this exception type. 921 /// 922 /// While we could obtain these by looking up the `func_ty` above, 923 /// we also need to be able to derive a GC object layout from this 924 /// type descriptor without referencing other type descriptors; so 925 /// we directly inline the information here. 926 pub fields: Box<[WasmFieldType]>, 927 } 928 929 impl TryClone for WasmExnType { 930 fn try_clone(&self) -> Result<Self, OutOfMemory> { 931 Ok(Self { 932 func_ty: self.func_ty, 933 fields: self.fields.try_clone()?, 934 }) 935 } 936 } 937 938 impl fmt::Display for WasmExnType { 939 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 940 write!(f, "(exn ({})", self.func_ty)?; 941 for ty in self.fields.iter() { 942 write!(f, " {ty}")?; 943 } 944 write!(f, ")") 945 } 946 } 947 948 impl TypeTrace for WasmExnType { 949 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 950 where 951 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 952 { 953 func(self.func_ty)?; 954 for f in self.fields.iter() { 955 f.trace(func)?; 956 } 957 Ok(()) 958 } 959 960 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 961 where 962 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 963 { 964 func(&mut self.func_ty)?; 965 for f in self.fields.iter_mut() { 966 f.trace_mut(func)?; 967 } 968 Ok(()) 969 } 970 } 971 972 /// Represents storage types introduced in the GC spec for array and struct fields. 973 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 974 pub enum WasmStorageType { 975 /// The storage type is i8. 976 I8, 977 /// The storage type is i16. 978 I16, 979 /// The storage type is a value type. 980 Val(WasmValType), 981 } 982 983 impl fmt::Display for WasmStorageType { 984 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 985 match self { 986 WasmStorageType::I8 => write!(f, "i8"), 987 WasmStorageType::I16 => write!(f, "i16"), 988 WasmStorageType::Val(v) => fmt::Display::fmt(v, f), 989 } 990 } 991 } 992 993 impl TypeTrace for WasmStorageType { 994 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 995 where 996 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 997 { 998 match self { 999 WasmStorageType::I8 | WasmStorageType::I16 => Ok(()), 1000 WasmStorageType::Val(v) => v.trace(func), 1001 } 1002 } 1003 1004 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1005 where 1006 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1007 { 1008 match self { 1009 WasmStorageType::I8 | WasmStorageType::I16 => Ok(()), 1010 WasmStorageType::Val(v) => v.trace_mut(func), 1011 } 1012 } 1013 } 1014 1015 impl WasmStorageType { 1016 /// Is this a type that is represented as a `VMGcRef` and is additionally 1017 /// not an `i31`? 1018 /// 1019 /// That is, is this a type that actually refers to an object allocated in a 1020 /// GC heap? 1021 pub fn is_vmgcref_type_and_not_i31(&self) -> bool { 1022 match self { 1023 WasmStorageType::I8 | WasmStorageType::I16 => false, 1024 WasmStorageType::Val(v) => v.is_vmgcref_type_and_not_i31(), 1025 } 1026 } 1027 } 1028 1029 /// The type of a struct field or array element. 1030 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] 1031 pub struct WasmFieldType { 1032 /// The field's element type. 1033 pub element_type: WasmStorageType, 1034 1035 /// Whether this field can be mutated or not. 1036 pub mutable: bool, 1037 } 1038 1039 impl TryClone for WasmFieldType { 1040 fn try_clone(&self) -> Result<Self, OutOfMemory> { 1041 Ok(*self) 1042 } 1043 } 1044 1045 impl fmt::Display for WasmFieldType { 1046 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1047 if self.mutable { 1048 write!(f, "(mut {})", self.element_type) 1049 } else { 1050 fmt::Display::fmt(&self.element_type, f) 1051 } 1052 } 1053 } 1054 1055 impl TypeTrace for WasmFieldType { 1056 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1057 where 1058 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1059 { 1060 self.element_type.trace(func) 1061 } 1062 1063 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1064 where 1065 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1066 { 1067 self.element_type.trace_mut(func) 1068 } 1069 } 1070 1071 /// A concrete array type. 1072 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] 1073 pub struct WasmArrayType(pub WasmFieldType); 1074 1075 impl fmt::Display for WasmArrayType { 1076 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1077 write!(f, "(array {})", self.0) 1078 } 1079 } 1080 1081 impl TypeTrace for WasmArrayType { 1082 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1083 where 1084 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1085 { 1086 self.0.trace(func) 1087 } 1088 1089 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1090 where 1091 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1092 { 1093 self.0.trace_mut(func) 1094 } 1095 } 1096 1097 /// A concrete struct type. 1098 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 1099 pub struct WasmStructType { 1100 /// The fields that make up this struct type. 1101 pub fields: Box<[WasmFieldType]>, 1102 } 1103 1104 impl TryClone for WasmStructType { 1105 fn try_clone(&self) -> Result<Self, OutOfMemory> { 1106 Ok(Self { 1107 fields: self.fields.try_clone()?, 1108 }) 1109 } 1110 } 1111 1112 impl fmt::Display for WasmStructType { 1113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1114 write!(f, "(struct")?; 1115 for ty in self.fields.iter() { 1116 write!(f, " {ty}")?; 1117 } 1118 write!(f, ")") 1119 } 1120 } 1121 1122 impl TypeTrace for WasmStructType { 1123 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1124 where 1125 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1126 { 1127 for f in self.fields.iter() { 1128 f.trace(func)?; 1129 } 1130 Ok(()) 1131 } 1132 1133 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1134 where 1135 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1136 { 1137 for f in self.fields.iter_mut() { 1138 f.trace_mut(func)?; 1139 } 1140 Ok(()) 1141 } 1142 } 1143 1144 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 1145 #[expect(missing_docs, reason = "self-describing type")] 1146 pub struct WasmCompositeType { 1147 /// The type defined inside the composite type. 1148 pub inner: WasmCompositeInnerType, 1149 /// Is the composite type shared? This is part of the 1150 /// shared-everything-threads proposal. 1151 pub shared: bool, 1152 } 1153 1154 impl TryClone for WasmCompositeType { 1155 fn try_clone(&self) -> Result<Self, OutOfMemory> { 1156 Ok(Self { 1157 inner: self.inner.try_clone()?, 1158 shared: self.shared, 1159 }) 1160 } 1161 } 1162 1163 impl fmt::Display for WasmCompositeType { 1164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1165 if self.shared { 1166 write!(f, "(shared ")?; 1167 } 1168 fmt::Display::fmt(&self.inner, f)?; 1169 if self.shared { 1170 write!(f, ")")?; 1171 } 1172 Ok(()) 1173 } 1174 } 1175 1176 /// A function, array, or struct type. 1177 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 1178 #[expect(missing_docs, reason = "self-describing variants")] 1179 pub enum WasmCompositeInnerType { 1180 Array(WasmArrayType), 1181 Func(WasmFuncType), 1182 Struct(WasmStructType), 1183 Cont(WasmContType), 1184 Exn(WasmExnType), 1185 } 1186 1187 impl TryClone for WasmCompositeInnerType { 1188 fn try_clone(&self) -> Result<Self, OutOfMemory> { 1189 Ok(match self { 1190 Self::Array(ty) => Self::Array(*ty), 1191 Self::Func(ty) => Self::Func(ty.try_clone()?), 1192 Self::Struct(ty) => Self::Struct(ty.try_clone()?), 1193 Self::Cont(ty) => Self::Cont(*ty), 1194 Self::Exn(ty) => Self::Exn(ty.try_clone()?), 1195 }) 1196 } 1197 } 1198 1199 impl fmt::Display for WasmCompositeInnerType { 1200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1201 match self { 1202 Self::Array(ty) => fmt::Display::fmt(ty, f), 1203 Self::Func(ty) => fmt::Display::fmt(ty, f), 1204 Self::Struct(ty) => fmt::Display::fmt(ty, f), 1205 Self::Cont(ty) => fmt::Display::fmt(ty, f), 1206 Self::Exn(ty) => fmt::Display::fmt(ty, f), 1207 } 1208 } 1209 } 1210 1211 #[expect(missing_docs, reason = "self-describing functions")] 1212 impl WasmCompositeInnerType { 1213 #[inline] 1214 pub fn is_array(&self) -> bool { 1215 matches!(self, Self::Array(_)) 1216 } 1217 1218 #[inline] 1219 pub fn as_array(&self) -> Option<&WasmArrayType> { 1220 match self { 1221 Self::Array(f) => Some(f), 1222 _ => None, 1223 } 1224 } 1225 1226 #[inline] 1227 pub fn unwrap_array(&self) -> &WasmArrayType { 1228 self.as_array().unwrap() 1229 } 1230 1231 #[inline] 1232 pub fn is_func(&self) -> bool { 1233 matches!(self, Self::Func(_)) 1234 } 1235 1236 #[inline] 1237 pub fn as_func(&self) -> Option<&WasmFuncType> { 1238 match self { 1239 Self::Func(f) => Some(f), 1240 _ => None, 1241 } 1242 } 1243 1244 #[inline] 1245 pub fn unwrap_func(&self) -> &WasmFuncType { 1246 self.as_func().unwrap() 1247 } 1248 1249 #[inline] 1250 pub fn is_struct(&self) -> bool { 1251 matches!(self, Self::Struct(_)) 1252 } 1253 1254 #[inline] 1255 pub fn as_struct(&self) -> Option<&WasmStructType> { 1256 match self { 1257 Self::Struct(f) => Some(f), 1258 _ => None, 1259 } 1260 } 1261 1262 #[inline] 1263 pub fn unwrap_struct(&self) -> &WasmStructType { 1264 self.as_struct().unwrap() 1265 } 1266 1267 #[inline] 1268 pub fn is_cont(&self) -> bool { 1269 matches!(self, Self::Cont(_)) 1270 } 1271 1272 #[inline] 1273 pub fn as_cont(&self) -> Option<&WasmContType> { 1274 match self { 1275 Self::Cont(f) => Some(f), 1276 _ => None, 1277 } 1278 } 1279 1280 #[inline] 1281 pub fn unwrap_cont(&self) -> &WasmContType { 1282 self.as_cont().unwrap() 1283 } 1284 1285 #[inline] 1286 pub fn is_exn(&self) -> bool { 1287 matches!(self, Self::Exn(_)) 1288 } 1289 1290 #[inline] 1291 pub fn as_exn(&self) -> Option<&WasmExnType> { 1292 match self { 1293 Self::Exn(f) => Some(f), 1294 _ => None, 1295 } 1296 } 1297 1298 #[inline] 1299 pub fn unwrap_exn(&self) -> &WasmExnType { 1300 self.as_exn().unwrap() 1301 } 1302 } 1303 1304 impl TypeTrace for WasmCompositeType { 1305 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1306 where 1307 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1308 { 1309 match &self.inner { 1310 WasmCompositeInnerType::Array(a) => a.trace(func), 1311 WasmCompositeInnerType::Func(f) => f.trace(func), 1312 WasmCompositeInnerType::Struct(a) => a.trace(func), 1313 WasmCompositeInnerType::Cont(c) => c.trace(func), 1314 WasmCompositeInnerType::Exn(e) => e.trace(func), 1315 } 1316 } 1317 1318 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1319 where 1320 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1321 { 1322 match &mut self.inner { 1323 WasmCompositeInnerType::Array(a) => a.trace_mut(func), 1324 WasmCompositeInnerType::Func(f) => f.trace_mut(func), 1325 WasmCompositeInnerType::Struct(a) => a.trace_mut(func), 1326 WasmCompositeInnerType::Cont(c) => c.trace_mut(func), 1327 WasmCompositeInnerType::Exn(e) => e.trace_mut(func), 1328 } 1329 } 1330 } 1331 1332 /// A concrete, user-defined (or host-defined) Wasm type. 1333 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 1334 pub struct WasmSubType { 1335 /// Whether this type is forbidden from being the supertype of any other 1336 /// type. 1337 pub is_final: bool, 1338 1339 /// This type's supertype, if any. 1340 pub supertype: Option<EngineOrModuleTypeIndex>, 1341 1342 /// The array, function, or struct that is defined. 1343 pub composite_type: WasmCompositeType, 1344 } 1345 1346 impl TryClone for WasmSubType { 1347 fn try_clone(&self) -> Result<Self, OutOfMemory> { 1348 Ok(Self { 1349 is_final: self.is_final, 1350 supertype: self.supertype, 1351 composite_type: self.composite_type.try_clone()?, 1352 }) 1353 } 1354 } 1355 1356 impl fmt::Display for WasmSubType { 1357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1358 if self.is_final && self.supertype.is_none() { 1359 fmt::Display::fmt(&self.composite_type, f) 1360 } else { 1361 write!(f, "(sub")?; 1362 if self.is_final { 1363 write!(f, " final")?; 1364 } 1365 if let Some(sup) = self.supertype { 1366 write!(f, " {sup}")?; 1367 } 1368 write!(f, " {})", self.composite_type) 1369 } 1370 } 1371 } 1372 1373 /// Implicitly define all of these helper functions to handle only unshared 1374 /// types; essentially, these act like `is_unshared_*` functions until shared 1375 /// support is implemented. 1376 #[expect(missing_docs, reason = "self-describing functions")] 1377 impl WasmSubType { 1378 #[inline] 1379 pub fn is_func(&self) -> bool { 1380 self.composite_type.inner.is_func() && !self.composite_type.shared 1381 } 1382 1383 #[inline] 1384 pub fn as_func(&self) -> Option<&WasmFuncType> { 1385 if self.composite_type.shared { 1386 None 1387 } else { 1388 self.composite_type.inner.as_func() 1389 } 1390 } 1391 1392 #[inline] 1393 pub fn unwrap_func(&self) -> &WasmFuncType { 1394 assert!(!self.composite_type.shared); 1395 self.composite_type.inner.unwrap_func() 1396 } 1397 1398 #[inline] 1399 pub fn is_array(&self) -> bool { 1400 self.composite_type.inner.is_array() && !self.composite_type.shared 1401 } 1402 1403 #[inline] 1404 pub fn as_array(&self) -> Option<&WasmArrayType> { 1405 if self.composite_type.shared { 1406 None 1407 } else { 1408 self.composite_type.inner.as_array() 1409 } 1410 } 1411 1412 #[inline] 1413 pub fn unwrap_array(&self) -> &WasmArrayType { 1414 assert!(!self.composite_type.shared); 1415 self.composite_type.inner.unwrap_array() 1416 } 1417 1418 #[inline] 1419 pub fn is_struct(&self) -> bool { 1420 self.composite_type.inner.is_struct() && !self.composite_type.shared 1421 } 1422 1423 #[inline] 1424 pub fn as_struct(&self) -> Option<&WasmStructType> { 1425 if self.composite_type.shared { 1426 None 1427 } else { 1428 self.composite_type.inner.as_struct() 1429 } 1430 } 1431 1432 #[inline] 1433 pub fn unwrap_struct(&self) -> &WasmStructType { 1434 assert!(!self.composite_type.shared); 1435 self.composite_type.inner.unwrap_struct() 1436 } 1437 1438 #[inline] 1439 pub fn is_cont(&self) -> bool { 1440 self.composite_type.inner.is_cont() && !self.composite_type.shared 1441 } 1442 1443 #[inline] 1444 pub fn as_cont(&self) -> Option<&WasmContType> { 1445 if self.composite_type.shared { 1446 None 1447 } else { 1448 self.composite_type.inner.as_cont() 1449 } 1450 } 1451 1452 #[inline] 1453 pub fn unwrap_cont(&self) -> &WasmContType { 1454 assert!(!self.composite_type.shared); 1455 self.composite_type.inner.unwrap_cont() 1456 } 1457 1458 #[inline] 1459 pub fn is_exn(&self) -> bool { 1460 self.composite_type.inner.is_exn() && !self.composite_type.shared 1461 } 1462 1463 #[inline] 1464 pub fn as_exn(&self) -> Option<&WasmExnType> { 1465 if self.composite_type.shared { 1466 None 1467 } else { 1468 self.composite_type.inner.as_exn() 1469 } 1470 } 1471 1472 #[inline] 1473 pub fn unwrap_exn(&self) -> &WasmExnType { 1474 assert!(!self.composite_type.shared); 1475 self.composite_type.inner.unwrap_exn() 1476 } 1477 } 1478 1479 impl TypeTrace for WasmSubType { 1480 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1481 where 1482 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1483 { 1484 if let Some(sup) = self.supertype { 1485 func(sup)?; 1486 } 1487 self.composite_type.trace(func) 1488 } 1489 1490 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1491 where 1492 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1493 { 1494 if let Some(sup) = self.supertype.as_mut() { 1495 func(sup)?; 1496 } 1497 self.composite_type.trace_mut(func) 1498 } 1499 } 1500 1501 /// A recursive type group. 1502 /// 1503 /// Types within a recgroup can have forward references to each other, which 1504 /// allows for cyclic types, for example a function `$f` that returns a 1505 /// reference to a function `$g` which returns a reference to a function `$f`: 1506 /// 1507 /// ```ignore 1508 /// (rec (type (func $f (result (ref null $g)))) 1509 /// (type (func $g (result (ref null $f))))) 1510 /// ``` 1511 #[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 1512 pub struct WasmRecGroup { 1513 /// The types inside of this recgroup. 1514 pub types: Box<[WasmSubType]>, 1515 } 1516 1517 impl TypeTrace for WasmRecGroup { 1518 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1519 where 1520 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1521 { 1522 for ty in self.types.iter() { 1523 ty.trace(func)?; 1524 } 1525 Ok(()) 1526 } 1527 1528 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1529 where 1530 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1531 { 1532 for ty in self.types.iter_mut() { 1533 ty.trace_mut(func)?; 1534 } 1535 Ok(()) 1536 } 1537 } 1538 1539 /// Index type of a function (imported or defined) inside the WebAssembly module. 1540 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1541 pub struct FuncIndex(u32); 1542 entity_impl!(FuncIndex); 1543 1544 /// Index type of a defined function inside the WebAssembly module. 1545 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1546 pub struct DefinedFuncIndex(u32); 1547 entity_impl!(DefinedFuncIndex); 1548 1549 /// Index type of a defined table inside the WebAssembly module. 1550 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1551 pub struct DefinedTableIndex(u32); 1552 entity_impl!(DefinedTableIndex); 1553 1554 /// Index type of a defined memory inside the WebAssembly module. 1555 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1556 pub struct DefinedMemoryIndex(u32); 1557 entity_impl!(DefinedMemoryIndex); 1558 1559 /// Index type of a defined memory inside the WebAssembly module. 1560 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1561 pub struct OwnedMemoryIndex(u32); 1562 entity_impl!(OwnedMemoryIndex); 1563 1564 /// Index type of a defined global inside the WebAssembly module. 1565 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1566 pub struct DefinedGlobalIndex(u32); 1567 entity_impl!(DefinedGlobalIndex); 1568 1569 /// Index type of a table (imported or defined) inside the WebAssembly module. 1570 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1571 pub struct TableIndex(u32); 1572 entity_impl!(TableIndex); 1573 1574 /// Index type of a global variable (imported or defined) inside the WebAssembly module. 1575 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1576 pub struct GlobalIndex(u32); 1577 entity_impl!(GlobalIndex); 1578 1579 /// Index type of a linear memory (imported or defined) inside the WebAssembly module. 1580 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1581 pub struct MemoryIndex(u32); 1582 entity_impl!(MemoryIndex); 1583 1584 /// Index type of a canonicalized recursive type group inside a WebAssembly 1585 /// module (as opposed to canonicalized within the whole engine). 1586 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1587 pub struct ModuleInternedRecGroupIndex(u32); 1588 entity_impl!(ModuleInternedRecGroupIndex); 1589 1590 /// Index type of a canonicalized recursive type group inside the whole engine 1591 /// (as opposed to canonicalized within just a single Wasm module). 1592 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1593 pub struct EngineInternedRecGroupIndex(u32); 1594 entity_impl!(EngineInternedRecGroupIndex); 1595 1596 /// Index type of a type (imported or defined) inside the WebAssembly module. 1597 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1598 pub struct TypeIndex(u32); 1599 entity_impl!(TypeIndex); 1600 1601 /// A canonicalized type index referencing a type within a single recursion 1602 /// group from another type within that same recursion group. 1603 /// 1604 /// This is only suitable for use when hash consing and deduplicating rec 1605 /// groups. 1606 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1607 pub struct RecGroupRelativeTypeIndex(u32); 1608 entity_impl!(RecGroupRelativeTypeIndex); 1609 1610 /// A canonicalized type index for a type within a single WebAssembly module. 1611 /// 1612 /// Note that this is deduplicated only at the level of a single WebAssembly 1613 /// module, not at the level of a whole store or engine. This means that these 1614 /// indices are only unique within the context of a single Wasm module, and 1615 /// therefore are not suitable for runtime type checks (which, in general, may 1616 /// involve entities defined in different modules). 1617 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1618 pub struct ModuleInternedTypeIndex(u32); 1619 entity_impl!(ModuleInternedTypeIndex); 1620 1621 /// A canonicalized type index into an engine's shared type registry. 1622 /// 1623 /// This is canonicalized/deduped at the level of a whole engine, across all the 1624 /// modules loaded into that engine, not just at the level of a single 1625 /// particular module. This means that `VMSharedTypeIndex` is usable for 1626 /// e.g. checking that function signatures match during an indirect call 1627 /// (potentially to a function defined in a different module) at runtime. 1628 #[repr(transparent)] // Used directly by JIT code. 1629 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1630 pub struct VMSharedTypeIndex(u32); 1631 entity_impl!(VMSharedTypeIndex); 1632 1633 impl VMSharedTypeIndex { 1634 /// Create a new `VMSharedTypeIndex`. 1635 #[inline] 1636 pub fn new(value: u32) -> Self { 1637 assert_ne!( 1638 value, 1639 u32::MAX, 1640 "u32::MAX is reserved for the default value" 1641 ); 1642 Self(value) 1643 } 1644 1645 /// Returns the underlying bits of the index. 1646 #[inline] 1647 pub fn bits(&self) -> u32 { 1648 self.0 1649 } 1650 } 1651 1652 impl Default for VMSharedTypeIndex { 1653 #[inline] 1654 fn default() -> Self { 1655 Self(u32::MAX) 1656 } 1657 } 1658 1659 /// Index type of a passive data segment inside the WebAssembly module. 1660 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1661 pub struct DataIndex(u32); 1662 entity_impl!(DataIndex); 1663 1664 /// Index type of a passive element segment inside the WebAssembly module. 1665 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1666 pub struct ElemIndex(u32); 1667 entity_impl!(ElemIndex); 1668 1669 /// Index type of a defined tag inside the WebAssembly module. 1670 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1671 pub struct DefinedTagIndex(u32); 1672 entity_impl!(DefinedTagIndex); 1673 1674 /// Index type of an event inside the WebAssembly module. 1675 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1676 pub struct TagIndex(u32); 1677 entity_impl!(TagIndex); 1678 1679 /// Index into the global list of modules found within an entire component. 1680 /// 1681 /// Module translations are saved on the side to get fully compiled after 1682 /// the original component has finished being translated. 1683 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1684 pub struct StaticModuleIndex(u32); 1685 entity_impl!(StaticModuleIndex); 1686 1687 /// An index of an entity. 1688 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1689 pub enum EntityIndex { 1690 /// Function index. 1691 Function(FuncIndex), 1692 /// Table index. 1693 Table(TableIndex), 1694 /// Memory index. 1695 Memory(MemoryIndex), 1696 /// Global index. 1697 Global(GlobalIndex), 1698 /// Tag index. 1699 Tag(TagIndex), 1700 } 1701 1702 impl From<FuncIndex> for EntityIndex { 1703 fn from(idx: FuncIndex) -> EntityIndex { 1704 EntityIndex::Function(idx) 1705 } 1706 } 1707 1708 impl From<TableIndex> for EntityIndex { 1709 fn from(idx: TableIndex) -> EntityIndex { 1710 EntityIndex::Table(idx) 1711 } 1712 } 1713 1714 impl From<MemoryIndex> for EntityIndex { 1715 fn from(idx: MemoryIndex) -> EntityIndex { 1716 EntityIndex::Memory(idx) 1717 } 1718 } 1719 1720 impl From<GlobalIndex> for EntityIndex { 1721 fn from(idx: GlobalIndex) -> EntityIndex { 1722 EntityIndex::Global(idx) 1723 } 1724 } 1725 1726 impl From<TagIndex> for EntityIndex { 1727 fn from(idx: TagIndex) -> EntityIndex { 1728 EntityIndex::Tag(idx) 1729 } 1730 } 1731 1732 /// A type of an item in a wasm module where an item is typically something that 1733 /// can be exported. 1734 #[derive(Clone, Debug, Serialize, Deserialize)] 1735 pub enum EntityType { 1736 /// A global variable with the specified content type 1737 Global(Global), 1738 /// A linear memory with the specified limits 1739 Memory(Memory), 1740 /// An exception and control tag definition. 1741 Tag(Tag), 1742 /// A table with the specified element type and limits 1743 Table(Table), 1744 /// A function type where the index points to the type section and records a 1745 /// function signature. 1746 Function(EngineOrModuleTypeIndex), 1747 } 1748 1749 impl TypeTrace for EntityType { 1750 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1751 where 1752 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1753 { 1754 match self { 1755 Self::Global(g) => g.trace(func), 1756 Self::Table(t) => t.trace(func), 1757 Self::Function(idx) => func(*idx), 1758 Self::Memory(_) => Ok(()), 1759 Self::Tag(t) => t.trace(func), 1760 } 1761 } 1762 1763 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1764 where 1765 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1766 { 1767 match self { 1768 Self::Global(g) => g.trace_mut(func), 1769 Self::Table(t) => t.trace_mut(func), 1770 Self::Function(idx) => func(idx), 1771 Self::Memory(_) => Ok(()), 1772 Self::Tag(t) => t.trace_mut(func), 1773 } 1774 } 1775 } 1776 1777 impl EntityType { 1778 /// Assert that this entity is a global 1779 pub fn unwrap_global(&self) -> &Global { 1780 match self { 1781 EntityType::Global(g) => g, 1782 _ => panic!("not a global"), 1783 } 1784 } 1785 1786 /// Assert that this entity is a memory 1787 pub fn unwrap_memory(&self) -> &Memory { 1788 match self { 1789 EntityType::Memory(g) => g, 1790 _ => panic!("not a memory"), 1791 } 1792 } 1793 1794 /// Assert that this entity is a tag 1795 pub fn unwrap_tag(&self) -> &Tag { 1796 match self { 1797 EntityType::Tag(g) => g, 1798 _ => panic!("not a tag"), 1799 } 1800 } 1801 1802 /// Assert that this entity is a table 1803 pub fn unwrap_table(&self) -> &Table { 1804 match self { 1805 EntityType::Table(g) => g, 1806 _ => panic!("not a table"), 1807 } 1808 } 1809 1810 /// Assert that this entity is a function 1811 pub fn unwrap_func(&self) -> EngineOrModuleTypeIndex { 1812 match self { 1813 EntityType::Function(g) => *g, 1814 _ => panic!("not a func"), 1815 } 1816 } 1817 } 1818 1819 /// A WebAssembly global. 1820 /// 1821 /// Note that we record both the original Wasm type and the Cranelift IR type 1822 /// used to represent it. This is because multiple different kinds of Wasm types 1823 /// might be represented with the same Cranelift IR type. For example, both a 1824 /// Wasm `i64` and a `funcref` might be represented with a Cranelift `i64` on 1825 /// 64-bit architectures, and when GC is not required for func refs. 1826 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 1827 pub struct Global { 1828 /// The Wasm type of the value stored in the global. 1829 pub wasm_ty: crate::WasmValType, 1830 /// A flag indicating whether the value may change at runtime. 1831 pub mutability: bool, 1832 } 1833 1834 impl TypeTrace for Global { 1835 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1836 where 1837 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1838 { 1839 let Global { 1840 wasm_ty, 1841 mutability: _, 1842 } = self; 1843 wasm_ty.trace(func) 1844 } 1845 1846 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1847 where 1848 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1849 { 1850 let Global { 1851 wasm_ty, 1852 mutability: _, 1853 } = self; 1854 wasm_ty.trace_mut(func) 1855 } 1856 } 1857 1858 /// A constant expression. 1859 /// 1860 /// These are used to initialize globals, table elements, etc... 1861 #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] 1862 pub struct ConstExpr { 1863 ops: SmallVec<[ConstOp; 2]>, 1864 } 1865 1866 impl ConstExpr { 1867 /// Create a new const expression from the given opcodes. 1868 /// 1869 /// Does not do any validation that the const expression is well-typed. 1870 /// 1871 /// Panics if given zero opcodes. 1872 pub fn new(ops: impl IntoIterator<Item = ConstOp>) -> Self { 1873 let ops = ops.into_iter().collect::<SmallVec<[ConstOp; 2]>>(); 1874 assert!(!ops.is_empty()); 1875 ConstExpr { ops } 1876 } 1877 1878 /// Create a new const expression from a `wasmparser` const expression. 1879 /// 1880 /// Returns the new const expression as well as the escaping function 1881 /// indices that appeared in `ref.func` instructions, if any. 1882 pub fn from_wasmparser( 1883 env: &dyn TypeConvert, 1884 expr: wasmparser::ConstExpr<'_>, 1885 ) -> WasmResult<(Self, SmallVec<[FuncIndex; 1]>)> { 1886 let mut iter = expr 1887 .get_operators_reader() 1888 .into_iter_with_offsets() 1889 .peekable(); 1890 1891 let mut ops = SmallVec::<[ConstOp; 2]>::new(); 1892 let mut escaped = SmallVec::<[FuncIndex; 1]>::new(); 1893 while let Some(res) = iter.next() { 1894 let (op, offset) = res?; 1895 1896 // If we reach an `end` instruction, and there are no more 1897 // instructions after that, then we are done reading this const 1898 // expression. 1899 if matches!(op, wasmparser::Operator::End) && iter.peek().is_none() { 1900 break; 1901 } 1902 1903 // Track any functions that appear in `ref.func` so that callers can 1904 // make sure to flag them as escaping. 1905 if let wasmparser::Operator::RefFunc { function_index } = &op { 1906 escaped.push(FuncIndex::from_u32(*function_index)); 1907 } 1908 1909 ops.push(ConstOp::from_wasmparser(env, op, offset)?); 1910 } 1911 Ok((Self { ops }, escaped)) 1912 } 1913 1914 /// Get the opcodes that make up this const expression. 1915 #[inline] 1916 pub fn ops(&self) -> &[ConstOp] { 1917 &self.ops 1918 } 1919 1920 /// Is this ConstExpr a provably nonzero integer value? 1921 /// 1922 /// This must be conservative: if the expression *might* be zero, 1923 /// it must return `false`. It is always allowed to return `false` 1924 /// for some expression kind that we don't support. However, if it 1925 /// returns `true`, the expression must be actually nonzero. 1926 /// 1927 /// We use this for certain table optimizations that rely on 1928 /// knowing for sure that index 0 is not referenced. 1929 pub fn provably_nonzero_i32(&self) -> bool { 1930 match self.const_eval() { 1931 Some(GlobalConstValue::I32(x)) => x != 0, 1932 1933 // Conservatively return `false` for non-const-eval-able expressions 1934 // as well as everything else. 1935 _ => false, 1936 } 1937 } 1938 1939 /// Attempt to evaluate the given const-expr at compile time. 1940 pub fn const_eval(&self) -> Option<GlobalConstValue> { 1941 // TODO: Actually maintain an evaluation stack and handle `i32.add`, 1942 // `i32.sub`, etc... const ops. 1943 match self.ops() { 1944 [ConstOp::I32Const(x)] => Some(GlobalConstValue::I32(*x)), 1945 [ConstOp::I64Const(x)] => Some(GlobalConstValue::I64(*x)), 1946 [ConstOp::F32Const(x)] => Some(GlobalConstValue::F32(*x)), 1947 [ConstOp::F64Const(x)] => Some(GlobalConstValue::F64(*x)), 1948 [ConstOp::V128Const(x)] => Some(GlobalConstValue::V128(*x)), 1949 _ => None, 1950 } 1951 } 1952 } 1953 1954 /// A global's constant value, known at compile time. 1955 #[expect(missing_docs, reason = "self-describing variants")] 1956 #[derive(Clone, Copy)] 1957 pub enum GlobalConstValue { 1958 I32(i32), 1959 I64(i64), 1960 F32(u32), 1961 F64(u64), 1962 V128(u128), 1963 } 1964 1965 /// The subset of Wasm opcodes that are constant. 1966 #[expect(missing_docs, reason = "self-describing variants")] 1967 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] 1968 pub enum ConstOp { 1969 I32Const(i32), 1970 I64Const(i64), 1971 F32Const(u32), 1972 F64Const(u64), 1973 V128Const(u128), 1974 GlobalGet(GlobalIndex), 1975 RefI31, 1976 RefNull(WasmHeapTopType), 1977 RefFunc(FuncIndex), 1978 I32Add, 1979 I32Sub, 1980 I32Mul, 1981 I64Add, 1982 I64Sub, 1983 I64Mul, 1984 StructNew { 1985 struct_type_index: TypeIndex, 1986 }, 1987 StructNewDefault { 1988 struct_type_index: TypeIndex, 1989 }, 1990 ArrayNew { 1991 array_type_index: TypeIndex, 1992 }, 1993 ArrayNewDefault { 1994 array_type_index: TypeIndex, 1995 }, 1996 ArrayNewFixed { 1997 array_type_index: TypeIndex, 1998 array_size: u32, 1999 }, 2000 ExternConvertAny, 2001 AnyConvertExtern, 2002 } 2003 2004 impl ConstOp { 2005 /// Convert a `wasmparser::Operator` to a `ConstOp`. 2006 pub fn from_wasmparser( 2007 env: &dyn TypeConvert, 2008 op: wasmparser::Operator<'_>, 2009 offset: usize, 2010 ) -> WasmResult<Self> { 2011 use wasmparser::Operator as O; 2012 Ok(match op { 2013 O::I32Const { value } => Self::I32Const(value), 2014 O::I64Const { value } => Self::I64Const(value), 2015 O::F32Const { value } => Self::F32Const(value.bits()), 2016 O::F64Const { value } => Self::F64Const(value.bits()), 2017 O::V128Const { value } => Self::V128Const(u128::from_le_bytes(*value.bytes())), 2018 O::RefNull { hty } => Self::RefNull(env.convert_heap_type(hty)?.top()), 2019 O::RefFunc { function_index } => Self::RefFunc(FuncIndex::from_u32(function_index)), 2020 O::GlobalGet { global_index } => Self::GlobalGet(GlobalIndex::from_u32(global_index)), 2021 O::RefI31 => Self::RefI31, 2022 O::I32Add => Self::I32Add, 2023 O::I32Sub => Self::I32Sub, 2024 O::I32Mul => Self::I32Mul, 2025 O::I64Add => Self::I64Add, 2026 O::I64Sub => Self::I64Sub, 2027 O::I64Mul => Self::I64Mul, 2028 O::StructNew { struct_type_index } => Self::StructNew { 2029 struct_type_index: TypeIndex::from_u32(struct_type_index), 2030 }, 2031 O::StructNewDefault { struct_type_index } => Self::StructNewDefault { 2032 struct_type_index: TypeIndex::from_u32(struct_type_index), 2033 }, 2034 O::ArrayNew { array_type_index } => Self::ArrayNew { 2035 array_type_index: TypeIndex::from_u32(array_type_index), 2036 }, 2037 O::ArrayNewDefault { array_type_index } => Self::ArrayNewDefault { 2038 array_type_index: TypeIndex::from_u32(array_type_index), 2039 }, 2040 O::ArrayNewFixed { 2041 array_type_index, 2042 array_size, 2043 } => Self::ArrayNewFixed { 2044 array_type_index: TypeIndex::from_u32(array_type_index), 2045 array_size, 2046 }, 2047 O::ExternConvertAny => Self::ExternConvertAny, 2048 O::AnyConvertExtern => Self::AnyConvertExtern, 2049 op => { 2050 return Err(wasm_unsupported!( 2051 "unsupported opcode in const expression at offset {offset:#x}: {op:?}", 2052 )); 2053 } 2054 }) 2055 } 2056 } 2057 2058 /// The type that can be used to index into [Memory] and [Table]. 2059 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 2060 #[expect(missing_docs, reason = "self-describing variants")] 2061 pub enum IndexType { 2062 I32, 2063 I64, 2064 } 2065 2066 /// The size range of resizeable storage associated with [Memory] types and [Table] types. 2067 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 2068 #[expect(missing_docs, reason = "self-describing fields")] 2069 pub struct Limits { 2070 pub min: u64, 2071 pub max: Option<u64>, 2072 } 2073 2074 /// WebAssembly table. 2075 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 2076 pub struct Table { 2077 /// The type of the index used to access the table. 2078 pub idx_type: IndexType, 2079 /// Tables are constrained by limits for their minimum and optionally maximum size. 2080 /// The limits are given in numbers of entries. 2081 pub limits: Limits, 2082 /// The table elements' Wasm type. 2083 pub ref_type: WasmRefType, 2084 } 2085 2086 impl TypeTrace for Table { 2087 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 2088 where 2089 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 2090 { 2091 let Table { 2092 ref_type: wasm_ty, 2093 idx_type: _, 2094 limits: _, 2095 } = self; 2096 wasm_ty.trace(func) 2097 } 2098 2099 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 2100 where 2101 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 2102 { 2103 let Table { 2104 ref_type: wasm_ty, 2105 idx_type: _, 2106 limits: _, 2107 } = self; 2108 wasm_ty.trace_mut(func) 2109 } 2110 } 2111 2112 /// WebAssembly linear memory. 2113 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 2114 pub struct Memory { 2115 /// The type of the index used to access the memory. 2116 pub idx_type: IndexType, 2117 /// The limits constrain the minimum and optionally the maximum size of a memory. 2118 /// The limits are given in units of page size. 2119 pub limits: Limits, 2120 /// Whether the memory may be shared between multiple threads. 2121 pub shared: bool, 2122 /// The log2 of this memory's page size, in bytes. 2123 /// 2124 /// By default the page size is 64KiB (0x10000; 2**16; 1<<16; 65536) but the 2125 /// custom-page-sizes proposal allows opting into a page size of `1`. 2126 pub page_size_log2: u8, 2127 } 2128 2129 /// Maximum size, in bytes, of 32-bit memories (4G) 2130 pub const WASM32_MAX_SIZE: u64 = 1 << 32; 2131 2132 impl Memory { 2133 /// WebAssembly page sizes are 64KiB by default. 2134 pub const DEFAULT_PAGE_SIZE: u32 = 0x10000; 2135 2136 /// WebAssembly page sizes are 64KiB (or `2**16`) by default. 2137 pub const DEFAULT_PAGE_SIZE_LOG2: u8 = { 2138 let log2 = 16; 2139 assert!(1 << log2 == Memory::DEFAULT_PAGE_SIZE); 2140 log2 2141 }; 2142 2143 /// Returns the minimum size, in bytes, that this memory must be. 2144 /// 2145 /// # Errors 2146 /// 2147 /// Returns an error if the calculation of the minimum size overflows the 2148 /// `u64` return type. This means that the memory can't be allocated but 2149 /// it's deferred to the caller to how to deal with that. 2150 pub fn minimum_byte_size(&self) -> Result<u64, SizeOverflow> { 2151 self.limits 2152 .min 2153 .checked_mul(self.page_size()) 2154 .ok_or(SizeOverflow) 2155 } 2156 2157 /// Returns the maximum size, in bytes, that this memory is allowed to be. 2158 /// 2159 /// Note that the return value here is not an `Option` despite the maximum 2160 /// size of a linear memory being optional in wasm. If a maximum size 2161 /// is not present in the memory's type then a maximum size is selected for 2162 /// it. For example the maximum size of a 32-bit memory is `1<<32`. The 2163 /// maximum size of a 64-bit linear memory is chosen to be a value that 2164 /// won't ever be allowed at runtime. 2165 /// 2166 /// # Errors 2167 /// 2168 /// Returns an error if the calculation of the maximum size overflows the 2169 /// `u64` return type. This means that the memory can't be allocated but 2170 /// it's deferred to the caller to how to deal with that. 2171 pub fn maximum_byte_size(&self) -> Result<u64, SizeOverflow> { 2172 match self.limits.max { 2173 Some(max) => max.checked_mul(self.page_size()).ok_or(SizeOverflow), 2174 None => { 2175 let min = self.minimum_byte_size()?; 2176 Ok(min.max(self.max_size_based_on_index_type())) 2177 } 2178 } 2179 } 2180 2181 /// Get the size of this memory's pages, in bytes. 2182 pub fn page_size(&self) -> u64 { 2183 debug_assert!( 2184 self.page_size_log2 == 16 || self.page_size_log2 == 0, 2185 "invalid page_size_log2: {}; must be 16 or 0", 2186 self.page_size_log2 2187 ); 2188 1 << self.page_size_log2 2189 } 2190 2191 /// Returns the maximum size memory is allowed to be only based on the 2192 /// index type used by this memory. 2193 /// 2194 /// For example 32-bit linear memories return `1<<32` from this method. 2195 pub fn max_size_based_on_index_type(&self) -> u64 { 2196 match self.idx_type { 2197 IndexType::I64 => 2198 // Note that the true maximum size of a 64-bit linear memory, in 2199 // bytes, cannot be represented in a `u64`. That would require a u65 2200 // to store `1<<64`. Despite that no system can actually allocate a 2201 // full 64-bit linear memory so this is instead emulated as "what if 2202 // the kernel fit in a single Wasm page of linear memory". Shouldn't 2203 // ever actually be possible but it provides a number to serve as an 2204 // effective maximum. 2205 { 2206 0_u64.wrapping_sub(self.page_size()) 2207 } 2208 IndexType::I32 => WASM32_MAX_SIZE, 2209 } 2210 } 2211 2212 /// Returns whether this memory can be implemented with virtual memory on 2213 /// a host with `host_page_size_log2`. 2214 /// 2215 /// When this function returns `true` then it means that signals such as 2216 /// SIGSEGV on the host are compatible with wasm and can be used to 2217 /// represent out-of-bounds memory accesses. 2218 /// 2219 /// When this function returns `false` then it means that this memory must, 2220 /// for example, have explicit bounds checks. This additionally means that 2221 /// virtual memory traps (e.g. SIGSEGV) cannot be relied on to implement 2222 /// linear memory semantics. 2223 pub fn can_use_virtual_memory(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool { 2224 tunables.signals_based_traps && self.page_size_log2 >= host_page_size_log2 2225 } 2226 2227 /// Returns whether this memory is a candidate for bounds check elision 2228 /// given the configuration and host page size. 2229 /// 2230 /// This function determines whether the given compilation configuration and 2231 /// hos enables possible bounds check elision for this memory. Bounds checks 2232 /// can only be elided if [`Memory::can_use_virtual_memory`] returns `true` 2233 /// for example but there are additionally requirements on the index size of 2234 /// this memory and the memory reservation in `tunables`. 2235 /// 2236 /// Currently the only case that supports bounds check elision is when all 2237 /// of these apply: 2238 /// 2239 /// * When [`Memory::can_use_virtual_memory`] returns `true`. 2240 /// * This is a 32-bit linear memory (e.g. not 64-bit) 2241 /// * `tunables.memory_reservation` is in excess of 4GiB 2242 /// 2243 /// In this situation all computable addresses fall within the reserved 2244 /// space (modulo static offsets factoring in guard pages) so bounds checks 2245 /// may be elidable. 2246 pub fn can_elide_bounds_check(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool { 2247 self.can_use_virtual_memory(tunables, host_page_size_log2) 2248 && self.idx_type == IndexType::I32 2249 && tunables.memory_reservation + tunables.memory_guard_size >= (1 << 32) 2250 } 2251 2252 /// Returns the static size of this heap in bytes at runtime, if available. 2253 /// 2254 /// This is only computable when the minimum size equals the maximum size. 2255 pub fn static_heap_size(&self) -> Option<u64> { 2256 let min = self.minimum_byte_size().ok()?; 2257 let max = self.maximum_byte_size().ok()?; 2258 if min == max { Some(min) } else { None } 2259 } 2260 2261 /// Returns whether or not the base pointer of this memory is allowed to be 2262 /// relocated at runtime. 2263 /// 2264 /// When this function returns `false` then it means that after the initial 2265 /// allocation the base pointer is constant for the entire lifetime of a 2266 /// memory. This can enable compiler optimizations, for example. 2267 pub fn memory_may_move(&self, tunables: &Tunables) -> bool { 2268 // Shared memories cannot ever relocate their base pointer so the 2269 // settings configured in the engine must be appropriate for them ahead 2270 // of time. 2271 if self.shared { 2272 return false; 2273 } 2274 2275 // If movement is disallowed in engine configuration, then the answer is 2276 // "no". 2277 if !tunables.memory_may_move { 2278 return false; 2279 } 2280 2281 // If its minimum and maximum are the same, then the memory will never 2282 // be resized, and therefore will never move. 2283 if self.limits.max.is_some_and(|max| self.limits.min == max) { 2284 return false; 2285 } 2286 2287 // If the maximum size of this memory is above the threshold of the 2288 // initial memory reservation then the memory may move. 2289 let max = self.maximum_byte_size().unwrap_or(u64::MAX); 2290 max > tunables.memory_reservation 2291 } 2292 } 2293 2294 #[derive(Copy, Clone, Debug)] 2295 #[expect(missing_docs, reason = "self-describing error struct")] 2296 pub struct SizeOverflow; 2297 2298 impl fmt::Display for SizeOverflow { 2299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 2300 f.write_str("size overflow calculating memory size") 2301 } 2302 } 2303 2304 impl core::error::Error for SizeOverflow {} 2305 2306 impl From<wasmparser::MemoryType> for Memory { 2307 fn from(ty: wasmparser::MemoryType) -> Memory { 2308 let idx_type = match ty.memory64 { 2309 false => IndexType::I32, 2310 true => IndexType::I64, 2311 }; 2312 let limits = Limits { 2313 min: ty.initial, 2314 max: ty.maximum, 2315 }; 2316 let page_size_log2 = u8::try_from(ty.page_size_log2.unwrap_or(16)).unwrap(); 2317 debug_assert!( 2318 page_size_log2 == 16 || page_size_log2 == 0, 2319 "invalid page_size_log2: {page_size_log2}; must be 16 or 0" 2320 ); 2321 Memory { 2322 idx_type, 2323 limits, 2324 shared: ty.shared, 2325 page_size_log2, 2326 } 2327 } 2328 } 2329 2330 /// WebAssembly exception and control tag. 2331 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 2332 pub struct Tag { 2333 /// The tag signature type. 2334 pub signature: EngineOrModuleTypeIndex, 2335 /// The corresponding exception type. 2336 pub exception: EngineOrModuleTypeIndex, 2337 } 2338 2339 impl TypeTrace for Tag { 2340 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 2341 where 2342 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 2343 { 2344 func(self.signature)?; 2345 func(self.exception)?; 2346 Ok(()) 2347 } 2348 2349 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 2350 where 2351 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 2352 { 2353 func(&mut self.signature)?; 2354 func(&mut self.exception)?; 2355 Ok(()) 2356 } 2357 } 2358 2359 /// Helpers used to convert a `wasmparser` type to a type in this crate. 2360 #[expect(missing_docs, reason = "self-describing functions")] 2361 pub trait TypeConvert { 2362 /// Converts a wasmparser table type into a wasmtime type 2363 fn convert_global_type(&self, ty: &wasmparser::GlobalType) -> WasmResult<Global> { 2364 Ok(Global { 2365 wasm_ty: self.convert_valtype(ty.content_type)?, 2366 mutability: ty.mutable, 2367 }) 2368 } 2369 2370 /// Converts a wasmparser table type into a wasmtime type 2371 fn convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table> { 2372 let idx_type = match ty.table64 { 2373 false => IndexType::I32, 2374 true => IndexType::I64, 2375 }; 2376 let limits = Limits { 2377 min: ty.initial, 2378 max: ty.maximum, 2379 }; 2380 Ok(Table { 2381 idx_type, 2382 limits, 2383 ref_type: self.convert_ref_type(ty.element_type)?, 2384 }) 2385 } 2386 2387 fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> { 2388 Ok(WasmSubType { 2389 is_final: ty.is_final, 2390 supertype: ty.supertype_idx.map(|i| self.lookup_type_index(i.unpack())), 2391 composite_type: self.convert_composite_type(&ty.composite_type)?, 2392 }) 2393 } 2394 2395 fn convert_composite_type( 2396 &self, 2397 ty: &wasmparser::CompositeType, 2398 ) -> WasmResult<WasmCompositeType> { 2399 let inner = match &ty.inner { 2400 wasmparser::CompositeInnerType::Func(f) => { 2401 WasmCompositeInnerType::Func(self.convert_func_type(f)?) 2402 } 2403 wasmparser::CompositeInnerType::Array(a) => { 2404 WasmCompositeInnerType::Array(self.convert_array_type(a)?) 2405 } 2406 wasmparser::CompositeInnerType::Struct(s) => { 2407 WasmCompositeInnerType::Struct(self.convert_struct_type(s)?) 2408 } 2409 wasmparser::CompositeInnerType::Cont(c) => { 2410 WasmCompositeInnerType::Cont(self.convert_cont_type(c)) 2411 } 2412 }; 2413 Ok(WasmCompositeType { 2414 inner, 2415 shared: ty.shared, 2416 }) 2417 } 2418 2419 /// Converts a wasmparser continuation type to a wasmtime type 2420 fn convert_cont_type(&self, ty: &wasmparser::ContType) -> WasmContType { 2421 if let WasmHeapType::ConcreteFunc(sigidx) = self.lookup_heap_type(ty.0.unpack()) { 2422 WasmContType::new(sigidx) 2423 } else { 2424 panic!("Failed to extract signature index for continuation type.") 2425 } 2426 } 2427 2428 fn convert_struct_type(&self, ty: &wasmparser::StructType) -> WasmResult<WasmStructType> { 2429 Ok(WasmStructType { 2430 fields: ty 2431 .fields 2432 .iter() 2433 .map(|f| self.convert_field_type(f)) 2434 .collect::<WasmResult<_>>()?, 2435 }) 2436 } 2437 2438 fn convert_array_type(&self, ty: &wasmparser::ArrayType) -> WasmResult<WasmArrayType> { 2439 Ok(WasmArrayType(self.convert_field_type(&ty.0)?)) 2440 } 2441 2442 fn convert_field_type(&self, ty: &wasmparser::FieldType) -> WasmResult<WasmFieldType> { 2443 Ok(WasmFieldType { 2444 element_type: self.convert_storage_type(&ty.element_type)?, 2445 mutable: ty.mutable, 2446 }) 2447 } 2448 2449 fn convert_storage_type(&self, ty: &wasmparser::StorageType) -> WasmResult<WasmStorageType> { 2450 Ok(match ty { 2451 wasmparser::StorageType::I8 => WasmStorageType::I8, 2452 wasmparser::StorageType::I16 => WasmStorageType::I16, 2453 wasmparser::StorageType::Val(v) => WasmStorageType::Val(self.convert_valtype(*v)?), 2454 }) 2455 } 2456 2457 /// Converts a wasmparser function type to a wasmtime type 2458 fn convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmResult<WasmFuncType> { 2459 let params = ty 2460 .params() 2461 .iter() 2462 .map(|t| self.convert_valtype(*t)) 2463 .collect::<WasmResult<_>>()?; 2464 let results = ty 2465 .results() 2466 .iter() 2467 .map(|t| self.convert_valtype(*t)) 2468 .collect::<WasmResult<_>>()?; 2469 Ok(WasmFuncType::new(params, results)) 2470 } 2471 2472 /// Converts a wasmparser value type to a wasmtime type 2473 fn convert_valtype(&self, ty: wasmparser::ValType) -> WasmResult<WasmValType> { 2474 Ok(match ty { 2475 wasmparser::ValType::I32 => WasmValType::I32, 2476 wasmparser::ValType::I64 => WasmValType::I64, 2477 wasmparser::ValType::F32 => WasmValType::F32, 2478 wasmparser::ValType::F64 => WasmValType::F64, 2479 wasmparser::ValType::V128 => WasmValType::V128, 2480 wasmparser::ValType::Ref(t) => WasmValType::Ref(self.convert_ref_type(t)?), 2481 }) 2482 } 2483 2484 /// Converts a wasmparser reference type to a wasmtime type 2485 fn convert_ref_type(&self, ty: wasmparser::RefType) -> WasmResult<WasmRefType> { 2486 Ok(WasmRefType { 2487 nullable: ty.is_nullable(), 2488 heap_type: self.convert_heap_type(ty.heap_type())?, 2489 }) 2490 } 2491 2492 /// Converts a wasmparser heap type to a wasmtime type 2493 fn convert_heap_type(&self, ty: wasmparser::HeapType) -> WasmResult<WasmHeapType> { 2494 Ok(match ty { 2495 wasmparser::HeapType::Concrete(i) => self.lookup_heap_type(i), 2496 wasmparser::HeapType::Abstract { ty, shared: false } => match ty { 2497 wasmparser::AbstractHeapType::Extern => WasmHeapType::Extern, 2498 wasmparser::AbstractHeapType::NoExtern => WasmHeapType::NoExtern, 2499 wasmparser::AbstractHeapType::Func => WasmHeapType::Func, 2500 wasmparser::AbstractHeapType::NoFunc => WasmHeapType::NoFunc, 2501 wasmparser::AbstractHeapType::Any => WasmHeapType::Any, 2502 wasmparser::AbstractHeapType::Eq => WasmHeapType::Eq, 2503 wasmparser::AbstractHeapType::I31 => WasmHeapType::I31, 2504 wasmparser::AbstractHeapType::Array => WasmHeapType::Array, 2505 wasmparser::AbstractHeapType::Struct => WasmHeapType::Struct, 2506 wasmparser::AbstractHeapType::None => WasmHeapType::None, 2507 wasmparser::AbstractHeapType::Cont => WasmHeapType::Cont, 2508 wasmparser::AbstractHeapType::NoCont => WasmHeapType::NoCont, 2509 wasmparser::AbstractHeapType::Exn => WasmHeapType::Exn, 2510 wasmparser::AbstractHeapType::NoExn => WasmHeapType::NoExn, 2511 }, 2512 _ => return Err(wasm_unsupported!("unsupported heap type {ty:?}")), 2513 }) 2514 } 2515 2516 /// Converts the specified type index from a heap type into a canonicalized 2517 /// heap type. 2518 fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType; 2519 2520 /// Converts the specified type index from a heap type into a canonicalized 2521 /// heap type. 2522 fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex; 2523 } 2524