1 use crate::{ 2 PanicOnOom as _, Tunables, WasmResult, collections::TryCow, error::OutOfMemory, prelude::*, 3 wasm_unsupported, 4 }; 5 use alloc::boxed::Box; 6 use core::{fmt, ops::Range}; 7 use serde_derive::{Deserialize, Serialize}; 8 use smallvec::SmallVec; 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)`. trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), 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)`. trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), 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. trace_engine_indices<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(VMSharedTypeIndex) -> Result<(), E>,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). canonicalize_for_runtime_usage<F>(&mut self, module_to_engine: &mut F) where F: FnMut(ModuleInternedTypeIndex) -> VMSharedTypeIndex,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? is_canonicalized_for_runtime_usage(&self) -> bool69 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). canonicalize_for_hash_consing<F>( &mut self, rec_group_range: Range<ModuleInternedTypeIndex>, module_to_engine: &mut F, ) where F: FnMut(ModuleInternedTypeIndex) -> VMSharedTypeIndex,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? is_canonicalized_for_hash_consing(&self) -> bool120 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 { try_clone(&self) -> Result<Self, OutOfMemory>147 fn try_clone(&self) -> Result<Self, OutOfMemory> { 148 Ok(*self) 149 } 150 } 151 152 impl fmt::Display for WasmValType { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result153 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 { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,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 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,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] is_vmgcref_type(&self) -> bool198 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] is_vmgcref_type_and_not_i31(&self) -> bool211 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 trampoline_type(&self) -> Self218 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. int_from_bits(bits: u8) -> Self235 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 unwrap_ref_type(&self) -> WasmRefType246 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 { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,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 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,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] is_vmgcref_type(&self) -> bool293 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] is_vmgcref_type_and_not_i31(&self) -> bool303 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 { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result309 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] from(i: ModuleInternedTypeIndex) -> Self346 fn from(i: ModuleInternedTypeIndex) -> Self { 347 Self::Module(i) 348 } 349 } 350 351 impl From<VMSharedTypeIndex> for EngineOrModuleTypeIndex { 352 #[inline] from(i: VMSharedTypeIndex) -> Self353 fn from(i: VMSharedTypeIndex) -> Self { 354 Self::Engine(i) 355 } 356 } 357 358 impl From<RecGroupRelativeTypeIndex> for EngineOrModuleTypeIndex { 359 #[inline] from(i: RecGroupRelativeTypeIndex) -> Self360 fn from(i: RecGroupRelativeTypeIndex) -> Self { 361 Self::RecGroup(i) 362 } 363 } 364 365 impl fmt::Display for EngineOrModuleTypeIndex { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result366 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? is_engine_type_index(self) -> bool377 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] as_engine_type_index(self) -> Option<VMSharedTypeIndex>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] unwrap_engine_type_index(self) -> VMSharedTypeIndex393 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? is_module_type_index(self) -> bool401 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. as_module_type_index(self) -> Option<ModuleInternedTypeIndex>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] unwrap_module_type_index(self) -> ModuleInternedTypeIndex415 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? is_rec_group_type_index(self) -> bool423 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. as_rec_group_type_index(self) -> Option<RecGroupRelativeTypeIndex>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] unwrap_rec_group_type_index(self) -> RecGroupRelativeTypeIndex437 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] from(value: WasmHeapTopType) -> Self481 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] from(value: WasmHeapBottomType) -> Self494 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 { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result506 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 { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,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 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,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] is_vmgcref_type(&self) -> bool562 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] is_vmgcref_type_and_not_i31(&self) -> bool581 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] is_top(&self) -> bool587 pub fn is_top(&self) -> bool { 588 *self == Self::from(self.top()) 589 } 590 591 /// Get this type's top type. 592 #[inline] top(&self) -> WasmHeapTopType593 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] is_bottom(&self) -> bool622 pub fn is_bottom(&self) -> bool { 623 *self == Self::from(self.bottom()) 624 } 625 626 /// Get this type's bottom type. 627 #[inline] bottom(&self) -> WasmHeapBottomType628 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, Eq, PartialEq, Hash, Serialize, Deserialize)] 688 pub struct WasmFuncType { 689 #[serde(deserialize_with = "WasmFuncType::deserialize_params_results")] 690 params_results: Box<[WasmValType]>, 691 params_len: u32, 692 non_i31_gc_ref_params_count: u32, 693 non_i31_gc_ref_results_count: u32, 694 } 695 696 impl TryClone for WasmFuncType { try_clone(&self) -> Result<Self, OutOfMemory>697 fn try_clone(&self) -> Result<Self, OutOfMemory> { 698 Ok(Self { 699 params_results: TryClone::try_clone(&self.params_results)?, 700 params_len: self.params_len, 701 non_i31_gc_ref_params_count: self.non_i31_gc_ref_params_count, 702 non_i31_gc_ref_results_count: self.non_i31_gc_ref_results_count, 703 }) 704 } 705 } 706 707 impl fmt::Display for WasmFuncType { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result708 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 709 write!(f, "(func")?; 710 if !self.params().is_empty() { 711 write!(f, " (param")?; 712 for p in self.params() { 713 write!(f, " {p}")?; 714 } 715 write!(f, ")")?; 716 } 717 if !self.results().is_empty() { 718 write!(f, " (result")?; 719 for r in self.results() { 720 write!(f, " {r}")?; 721 } 722 write!(f, ")")?; 723 } 724 write!(f, ")") 725 } 726 } 727 728 impl TypeTrace for WasmFuncType { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,729 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 730 where 731 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 732 { 733 for ty in self.params_results.iter() { 734 ty.trace(func)?; 735 } 736 Ok(()) 737 } 738 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,739 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 740 where 741 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 742 { 743 for ty in self.params_results.iter_mut() { 744 ty.trace_mut(func)?; 745 } 746 Ok(()) 747 } 748 } 749 750 impl WasmFuncType { deserialize_params_results<'de, D>(deserializer: D) -> Result<Box<[WasmValType]>, D::Error> where D: serde::de::Deserializer<'de>,751 fn deserialize_params_results<'de, D>(deserializer: D) -> Result<Box<[WasmValType]>, D::Error> 752 where 753 D: serde::de::Deserializer<'de>, 754 { 755 let tys: crate::collections::TryVec<WasmValType> = 756 serde::Deserialize::deserialize(deserializer)?; 757 let tys = tys 758 .into_boxed_slice() 759 .map_err(|oom| serde::de::Error::custom(oom))?; 760 Ok(tys) 761 } 762 763 /// Creates a new function type from the provided `params` and `returns`. 764 #[inline] new( params: impl IntoIterator<Item = WasmValType>, results: impl IntoIterator<Item = WasmValType>, ) -> Result<Self, OutOfMemory>765 pub fn new( 766 params: impl IntoIterator<Item = WasmValType>, 767 results: impl IntoIterator<Item = WasmValType>, 768 ) -> Result<Self, OutOfMemory> { 769 let mut params_results: crate::collections::TryVec<_> = params.into_iter().try_collect()?; 770 let non_i31_gc_ref_params_count = params_results 771 .iter() 772 .filter(|p| p.is_vmgcref_type_and_not_i31()) 773 .count(); 774 775 let params_len = params_results.len(); 776 params_results.try_extend(results)?; 777 let non_i31_gc_ref_results_count = params_results[params_len..] 778 .iter() 779 .filter(|r| r.is_vmgcref_type_and_not_i31()) 780 .count(); 781 782 let params_results = params_results.into_boxed_slice()?; 783 let params_len = u32::try_from(params_len).unwrap(); 784 let non_i31_gc_ref_params_count = u32::try_from(non_i31_gc_ref_params_count).unwrap(); 785 let non_i31_gc_ref_results_count = u32::try_from(non_i31_gc_ref_results_count).unwrap(); 786 787 Ok(Self { 788 params_results, 789 params_len, 790 non_i31_gc_ref_params_count, 791 non_i31_gc_ref_results_count, 792 }) 793 } 794 results_start(&self) -> usize795 fn results_start(&self) -> usize { 796 usize::try_from(self.params_len).unwrap() 797 } 798 799 /// Function params types. 800 #[inline] params(&self) -> &[WasmValType]801 pub fn params(&self) -> &[WasmValType] { 802 &self.params_results[..self.results_start()] 803 } 804 805 /// How many `externref`s are in this function's params? 806 #[inline] non_i31_gc_ref_params_count(&self) -> usize807 pub fn non_i31_gc_ref_params_count(&self) -> usize { 808 usize::try_from(self.non_i31_gc_ref_params_count).unwrap() 809 } 810 811 /// Returns params types. 812 #[inline] results(&self) -> &[WasmValType]813 pub fn results(&self) -> &[WasmValType] { 814 &self.params_results[self.results_start()..] 815 } 816 817 /// How many `externref`s are in this function's returns? 818 #[inline] non_i31_gc_ref_results_count(&self) -> usize819 pub fn non_i31_gc_ref_results_count(&self) -> usize { 820 usize::try_from(self.non_i31_gc_ref_results_count).unwrap() 821 } 822 823 /// Is this function type compatible with trampoline usage in Wasmtime? is_trampoline_type(&self) -> bool824 pub fn is_trampoline_type(&self) -> bool { 825 self.params().iter().all(|p| *p == p.trampoline_type()) 826 && self.results().iter().all(|r| *r == r.trampoline_type()) 827 } 828 829 /// Get the version of this function type that is suitable for usage as a 830 /// trampoline in Wasmtime. 831 /// 832 /// If this function is suitable for trampoline usage as-is, then a borrowed 833 /// `Cow` is returned. If it must be tweaked for trampoline usage, then an 834 /// owned `Cow` is returned. 835 /// 836 /// ## What is a trampoline type? 837 /// 838 /// All reference types in parameters and results are mapped to their 839 /// nullable top type, e.g. `(ref $my_struct_type)` becomes `(ref null 840 /// any)`. 841 /// 842 /// This allows us to share trampolines between functions whose signatures 843 /// both map to the same trampoline type. It also allows the host to satisfy 844 /// a Wasm module's function import of type `S` with a function of type `T` 845 /// where `T <: S`, even when the Wasm module never defines the type `T` 846 /// (and might never even be able to!) 847 /// 848 /// The flip side is that this adds a constraint to our trampolines: they 849 /// can only pass references around (e.g. move a reference from one calling 850 /// convention's location to another's) and may not actually inspect the 851 /// references themselves (unless the trampolines start doing explicit, 852 /// fallible downcasts, but if we ever need that, then we might want to 853 /// redesign this stuff). trampoline_type(&self) -> Result<TryCow<'_, Self>, OutOfMemory>854 pub fn trampoline_type(&self) -> Result<TryCow<'_, Self>, OutOfMemory> { 855 if self.is_trampoline_type() { 856 return Ok(TryCow::Borrowed(self)); 857 } 858 859 Ok(TryCow::Owned(Self::new( 860 self.params().iter().map(|p| p.trampoline_type()), 861 self.results().iter().map(|r| r.trampoline_type()), 862 )?)) 863 } 864 } 865 866 /// WebAssembly continuation type -- equivalent of `wasmparser`'s ContType. 867 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] 868 pub struct WasmContType(EngineOrModuleTypeIndex); 869 870 impl fmt::Display for WasmContType { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result871 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 872 write!(f, "(cont {})", self.0) 873 } 874 } 875 876 impl WasmContType { 877 /// Constructs a new continuation type. new(idx: EngineOrModuleTypeIndex) -> Self878 pub fn new(idx: EngineOrModuleTypeIndex) -> Self { 879 WasmContType(idx) 880 } 881 882 /// Returns the (module interned) index to the underlying function type. unwrap_module_type_index(self) -> ModuleInternedTypeIndex883 pub fn unwrap_module_type_index(self) -> ModuleInternedTypeIndex { 884 match self.0 { 885 EngineOrModuleTypeIndex::Engine(_) => panic!("not module interned"), 886 EngineOrModuleTypeIndex::Module(idx) => idx, 887 EngineOrModuleTypeIndex::RecGroup(_) => todo!(), 888 } 889 } 890 } 891 892 impl TypeTrace for WasmContType { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,893 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 894 where 895 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 896 { 897 func(self.0) 898 } 899 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,900 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 901 where 902 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 903 { 904 func(&mut self.0) 905 } 906 } 907 908 /// WebAssembly exception type. 909 /// 910 /// This "exception type" is not a Wasm language-level 911 /// concept. Instead, it denotes an *exception object signature* -- 912 /// the types of the payload values. 913 /// 914 /// In contrast, at the Wasm language level, exception objects are 915 /// associated with specific tags, and these tags refer to their 916 /// signatures (function types). However, tags are *nominal*: like 917 /// memories and tables, a separate instance of a tag exists for every 918 /// instance of the defining module, and these tag instances can be 919 /// imported and exported. At runtime we handle tags like we do 920 /// memories and tables, but these runtime instances do not exist in 921 /// the type system here. 922 /// 923 /// Because the Wasm type system does not have concrete `exn` types 924 /// (i.e., the heap-type lattice has only top `exn` and bottom 925 /// `noexn`), we are free to decide what we mean by "concrete type" 926 /// here. Thus, we define an "exception type" to refer to the 927 /// type-level *signature*. When a particular *exception object* is 928 /// created in a store, it can be associated with a particular *tag 929 /// instance* also in that store, and the compatibility is checked 930 /// (the tag's function type must match the function type in the 931 /// associated WasmExnType). 932 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 933 pub struct WasmExnType { 934 /// The function type from which we get our signature. We hold 935 /// this directly so that we can efficiently derive a FuncType 936 /// without re-interning the field types. 937 pub func_ty: EngineOrModuleTypeIndex, 938 /// The fields (payload values) that make up this exception type. 939 /// 940 /// While we could obtain these by looking up the `func_ty` above, 941 /// we also need to be able to derive a GC object layout from this 942 /// type descriptor without referencing other type descriptors; so 943 /// we directly inline the information here. 944 pub fields: Box<[WasmFieldType]>, 945 } 946 947 impl TryClone for WasmExnType { try_clone(&self) -> Result<Self, OutOfMemory>948 fn try_clone(&self) -> Result<Self, OutOfMemory> { 949 Ok(Self { 950 func_ty: self.func_ty, 951 fields: self.fields.try_clone()?, 952 }) 953 } 954 } 955 956 impl fmt::Display for WasmExnType { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result957 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 958 write!(f, "(exn ({})", self.func_ty)?; 959 for ty in self.fields.iter() { 960 write!(f, " {ty}")?; 961 } 962 write!(f, ")") 963 } 964 } 965 966 impl TypeTrace for WasmExnType { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,967 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 968 where 969 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 970 { 971 func(self.func_ty)?; 972 for f in self.fields.iter() { 973 f.trace(func)?; 974 } 975 Ok(()) 976 } 977 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,978 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 979 where 980 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 981 { 982 func(&mut self.func_ty)?; 983 for f in self.fields.iter_mut() { 984 f.trace_mut(func)?; 985 } 986 Ok(()) 987 } 988 } 989 990 /// Represents storage types introduced in the GC spec for array and struct fields. 991 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 992 pub enum WasmStorageType { 993 /// The storage type is i8. 994 I8, 995 /// The storage type is i16. 996 I16, 997 /// The storage type is a value type. 998 Val(WasmValType), 999 } 1000 1001 impl fmt::Display for WasmStorageType { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1002 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1003 match self { 1004 WasmStorageType::I8 => write!(f, "i8"), 1005 WasmStorageType::I16 => write!(f, "i16"), 1006 WasmStorageType::Val(v) => fmt::Display::fmt(v, f), 1007 } 1008 } 1009 } 1010 1011 impl TypeTrace for WasmStorageType { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1012 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1013 where 1014 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1015 { 1016 match self { 1017 WasmStorageType::I8 | WasmStorageType::I16 => Ok(()), 1018 WasmStorageType::Val(v) => v.trace(func), 1019 } 1020 } 1021 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1022 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1023 where 1024 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1025 { 1026 match self { 1027 WasmStorageType::I8 | WasmStorageType::I16 => Ok(()), 1028 WasmStorageType::Val(v) => v.trace_mut(func), 1029 } 1030 } 1031 } 1032 1033 impl WasmStorageType { 1034 /// Is this a type that is represented as a `VMGcRef` and is additionally 1035 /// not an `i31`? 1036 /// 1037 /// That is, is this a type that actually refers to an object allocated in a 1038 /// GC heap? is_vmgcref_type_and_not_i31(&self) -> bool1039 pub fn is_vmgcref_type_and_not_i31(&self) -> bool { 1040 match self { 1041 WasmStorageType::I8 | WasmStorageType::I16 => false, 1042 WasmStorageType::Val(v) => v.is_vmgcref_type_and_not_i31(), 1043 } 1044 } 1045 } 1046 1047 /// The type of a struct field or array element. 1048 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] 1049 pub struct WasmFieldType { 1050 /// The field's element type. 1051 pub element_type: WasmStorageType, 1052 1053 /// Whether this field can be mutated or not. 1054 pub mutable: bool, 1055 } 1056 1057 impl TryClone for WasmFieldType { try_clone(&self) -> Result<Self, OutOfMemory>1058 fn try_clone(&self) -> Result<Self, OutOfMemory> { 1059 Ok(*self) 1060 } 1061 } 1062 1063 impl fmt::Display for WasmFieldType { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1064 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1065 if self.mutable { 1066 write!(f, "(mut {})", self.element_type) 1067 } else { 1068 fmt::Display::fmt(&self.element_type, f) 1069 } 1070 } 1071 } 1072 1073 impl TypeTrace for WasmFieldType { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1074 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1075 where 1076 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1077 { 1078 self.element_type.trace(func) 1079 } 1080 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1081 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1082 where 1083 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1084 { 1085 self.element_type.trace_mut(func) 1086 } 1087 } 1088 1089 /// A concrete array type. 1090 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] 1091 pub struct WasmArrayType(pub WasmFieldType); 1092 1093 impl fmt::Display for WasmArrayType { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1094 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1095 write!(f, "(array {})", self.0) 1096 } 1097 } 1098 1099 impl TypeTrace for WasmArrayType { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1100 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1101 where 1102 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1103 { 1104 self.0.trace(func) 1105 } 1106 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1107 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1108 where 1109 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1110 { 1111 self.0.trace_mut(func) 1112 } 1113 } 1114 1115 /// A concrete struct type. 1116 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 1117 pub struct WasmStructType { 1118 /// The fields that make up this struct type. 1119 pub fields: Box<[WasmFieldType]>, 1120 } 1121 1122 impl TryClone for WasmStructType { try_clone(&self) -> Result<Self, OutOfMemory>1123 fn try_clone(&self) -> Result<Self, OutOfMemory> { 1124 Ok(Self { 1125 fields: self.fields.try_clone()?, 1126 }) 1127 } 1128 } 1129 1130 impl fmt::Display for WasmStructType { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1132 write!(f, "(struct")?; 1133 for ty in self.fields.iter() { 1134 write!(f, " {ty}")?; 1135 } 1136 write!(f, ")") 1137 } 1138 } 1139 1140 impl TypeTrace for WasmStructType { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1141 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1142 where 1143 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1144 { 1145 for f in self.fields.iter() { 1146 f.trace(func)?; 1147 } 1148 Ok(()) 1149 } 1150 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1151 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1152 where 1153 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1154 { 1155 for f in self.fields.iter_mut() { 1156 f.trace_mut(func)?; 1157 } 1158 Ok(()) 1159 } 1160 } 1161 1162 #[derive(Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] 1163 #[expect(missing_docs, reason = "self-describing type")] 1164 pub struct WasmCompositeType { 1165 /// The type defined inside the composite type. 1166 pub inner: WasmCompositeInnerType, 1167 /// Is the composite type shared? This is part of the 1168 /// shared-everything-threads proposal. 1169 pub shared: bool, 1170 } 1171 1172 impl TryClone for WasmCompositeType { try_clone(&self) -> Result<Self, OutOfMemory>1173 fn try_clone(&self) -> Result<Self, OutOfMemory> { 1174 Ok(Self { 1175 inner: self.inner.try_clone()?, 1176 shared: self.shared, 1177 }) 1178 } 1179 } 1180 1181 impl fmt::Display for WasmCompositeType { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1183 if self.shared { 1184 write!(f, "(shared ")?; 1185 } 1186 fmt::Display::fmt(&self.inner, f)?; 1187 if self.shared { 1188 write!(f, ")")?; 1189 } 1190 Ok(()) 1191 } 1192 } 1193 1194 /// A function, array, or struct type. 1195 #[derive(Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] 1196 #[expect(missing_docs, reason = "self-describing variants")] 1197 pub enum WasmCompositeInnerType { 1198 Array(WasmArrayType), 1199 Func(WasmFuncType), 1200 Struct(WasmStructType), 1201 Cont(WasmContType), 1202 Exn(WasmExnType), 1203 } 1204 1205 impl TryClone for WasmCompositeInnerType { try_clone(&self) -> Result<Self, OutOfMemory>1206 fn try_clone(&self) -> Result<Self, OutOfMemory> { 1207 Ok(match self { 1208 Self::Array(ty) => Self::Array(*ty), 1209 Self::Func(ty) => Self::Func(ty.try_clone()?), 1210 Self::Struct(ty) => Self::Struct(ty.try_clone()?), 1211 Self::Cont(ty) => Self::Cont(*ty), 1212 Self::Exn(ty) => Self::Exn(ty.try_clone()?), 1213 }) 1214 } 1215 } 1216 1217 impl fmt::Display for WasmCompositeInnerType { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1219 match self { 1220 Self::Array(ty) => fmt::Display::fmt(ty, f), 1221 Self::Func(ty) => fmt::Display::fmt(ty, f), 1222 Self::Struct(ty) => fmt::Display::fmt(ty, f), 1223 Self::Cont(ty) => fmt::Display::fmt(ty, f), 1224 Self::Exn(ty) => fmt::Display::fmt(ty, f), 1225 } 1226 } 1227 } 1228 1229 #[expect(missing_docs, reason = "self-describing functions")] 1230 impl WasmCompositeInnerType { 1231 #[inline] is_array(&self) -> bool1232 pub fn is_array(&self) -> bool { 1233 matches!(self, Self::Array(_)) 1234 } 1235 1236 #[inline] as_array(&self) -> Option<&WasmArrayType>1237 pub fn as_array(&self) -> Option<&WasmArrayType> { 1238 match self { 1239 Self::Array(f) => Some(f), 1240 _ => None, 1241 } 1242 } 1243 1244 #[inline] unwrap_array(&self) -> &WasmArrayType1245 pub fn unwrap_array(&self) -> &WasmArrayType { 1246 self.as_array().unwrap() 1247 } 1248 1249 #[inline] is_func(&self) -> bool1250 pub fn is_func(&self) -> bool { 1251 matches!(self, Self::Func(_)) 1252 } 1253 1254 #[inline] as_func(&self) -> Option<&WasmFuncType>1255 pub fn as_func(&self) -> Option<&WasmFuncType> { 1256 match self { 1257 Self::Func(f) => Some(f), 1258 _ => None, 1259 } 1260 } 1261 1262 #[inline] unwrap_func(&self) -> &WasmFuncType1263 pub fn unwrap_func(&self) -> &WasmFuncType { 1264 self.as_func().unwrap() 1265 } 1266 1267 #[inline] is_struct(&self) -> bool1268 pub fn is_struct(&self) -> bool { 1269 matches!(self, Self::Struct(_)) 1270 } 1271 1272 #[inline] as_struct(&self) -> Option<&WasmStructType>1273 pub fn as_struct(&self) -> Option<&WasmStructType> { 1274 match self { 1275 Self::Struct(f) => Some(f), 1276 _ => None, 1277 } 1278 } 1279 1280 #[inline] unwrap_struct(&self) -> &WasmStructType1281 pub fn unwrap_struct(&self) -> &WasmStructType { 1282 self.as_struct().unwrap() 1283 } 1284 1285 #[inline] is_cont(&self) -> bool1286 pub fn is_cont(&self) -> bool { 1287 matches!(self, Self::Cont(_)) 1288 } 1289 1290 #[inline] as_cont(&self) -> Option<&WasmContType>1291 pub fn as_cont(&self) -> Option<&WasmContType> { 1292 match self { 1293 Self::Cont(f) => Some(f), 1294 _ => None, 1295 } 1296 } 1297 1298 #[inline] unwrap_cont(&self) -> &WasmContType1299 pub fn unwrap_cont(&self) -> &WasmContType { 1300 self.as_cont().unwrap() 1301 } 1302 1303 #[inline] is_exn(&self) -> bool1304 pub fn is_exn(&self) -> bool { 1305 matches!(self, Self::Exn(_)) 1306 } 1307 1308 #[inline] as_exn(&self) -> Option<&WasmExnType>1309 pub fn as_exn(&self) -> Option<&WasmExnType> { 1310 match self { 1311 Self::Exn(f) => Some(f), 1312 _ => None, 1313 } 1314 } 1315 1316 #[inline] unwrap_exn(&self) -> &WasmExnType1317 pub fn unwrap_exn(&self) -> &WasmExnType { 1318 self.as_exn().unwrap() 1319 } 1320 } 1321 1322 impl TypeTrace for WasmCompositeType { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1323 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1324 where 1325 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1326 { 1327 match &self.inner { 1328 WasmCompositeInnerType::Array(a) => a.trace(func), 1329 WasmCompositeInnerType::Func(f) => f.trace(func), 1330 WasmCompositeInnerType::Struct(a) => a.trace(func), 1331 WasmCompositeInnerType::Cont(c) => c.trace(func), 1332 WasmCompositeInnerType::Exn(e) => e.trace(func), 1333 } 1334 } 1335 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1336 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1337 where 1338 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1339 { 1340 match &mut self.inner { 1341 WasmCompositeInnerType::Array(a) => a.trace_mut(func), 1342 WasmCompositeInnerType::Func(f) => f.trace_mut(func), 1343 WasmCompositeInnerType::Struct(a) => a.trace_mut(func), 1344 WasmCompositeInnerType::Cont(c) => c.trace_mut(func), 1345 WasmCompositeInnerType::Exn(e) => e.trace_mut(func), 1346 } 1347 } 1348 } 1349 1350 /// A concrete, user-defined (or host-defined) Wasm type. 1351 #[derive(Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] 1352 pub struct WasmSubType { 1353 /// Whether this type is forbidden from being the supertype of any other 1354 /// type. 1355 pub is_final: bool, 1356 1357 /// This type's supertype, if any. 1358 pub supertype: Option<EngineOrModuleTypeIndex>, 1359 1360 /// The array, function, or struct that is defined. 1361 pub composite_type: WasmCompositeType, 1362 } 1363 1364 impl TryClone for WasmSubType { try_clone(&self) -> Result<Self, OutOfMemory>1365 fn try_clone(&self) -> Result<Self, OutOfMemory> { 1366 Ok(Self { 1367 is_final: self.is_final, 1368 supertype: self.supertype, 1369 composite_type: self.composite_type.try_clone()?, 1370 }) 1371 } 1372 } 1373 1374 impl fmt::Display for WasmSubType { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1376 if self.is_final && self.supertype.is_none() { 1377 fmt::Display::fmt(&self.composite_type, f) 1378 } else { 1379 write!(f, "(sub")?; 1380 if self.is_final { 1381 write!(f, " final")?; 1382 } 1383 if let Some(sup) = self.supertype { 1384 write!(f, " {sup}")?; 1385 } 1386 write!(f, " {})", self.composite_type) 1387 } 1388 } 1389 } 1390 1391 /// Implicitly define all of these helper functions to handle only unshared 1392 /// types; essentially, these act like `is_unshared_*` functions until shared 1393 /// support is implemented. 1394 #[expect(missing_docs, reason = "self-describing functions")] 1395 impl WasmSubType { 1396 #[inline] is_func(&self) -> bool1397 pub fn is_func(&self) -> bool { 1398 self.composite_type.inner.is_func() && !self.composite_type.shared 1399 } 1400 1401 #[inline] as_func(&self) -> Option<&WasmFuncType>1402 pub fn as_func(&self) -> Option<&WasmFuncType> { 1403 if self.composite_type.shared { 1404 None 1405 } else { 1406 self.composite_type.inner.as_func() 1407 } 1408 } 1409 1410 #[inline] unwrap_func(&self) -> &WasmFuncType1411 pub fn unwrap_func(&self) -> &WasmFuncType { 1412 assert!(!self.composite_type.shared); 1413 self.composite_type.inner.unwrap_func() 1414 } 1415 1416 #[inline] is_array(&self) -> bool1417 pub fn is_array(&self) -> bool { 1418 self.composite_type.inner.is_array() && !self.composite_type.shared 1419 } 1420 1421 #[inline] as_array(&self) -> Option<&WasmArrayType>1422 pub fn as_array(&self) -> Option<&WasmArrayType> { 1423 if self.composite_type.shared { 1424 None 1425 } else { 1426 self.composite_type.inner.as_array() 1427 } 1428 } 1429 1430 #[inline] unwrap_array(&self) -> &WasmArrayType1431 pub fn unwrap_array(&self) -> &WasmArrayType { 1432 assert!(!self.composite_type.shared); 1433 self.composite_type.inner.unwrap_array() 1434 } 1435 1436 #[inline] is_struct(&self) -> bool1437 pub fn is_struct(&self) -> bool { 1438 self.composite_type.inner.is_struct() && !self.composite_type.shared 1439 } 1440 1441 #[inline] as_struct(&self) -> Option<&WasmStructType>1442 pub fn as_struct(&self) -> Option<&WasmStructType> { 1443 if self.composite_type.shared { 1444 None 1445 } else { 1446 self.composite_type.inner.as_struct() 1447 } 1448 } 1449 1450 #[inline] unwrap_struct(&self) -> &WasmStructType1451 pub fn unwrap_struct(&self) -> &WasmStructType { 1452 assert!(!self.composite_type.shared); 1453 self.composite_type.inner.unwrap_struct() 1454 } 1455 1456 #[inline] is_cont(&self) -> bool1457 pub fn is_cont(&self) -> bool { 1458 self.composite_type.inner.is_cont() && !self.composite_type.shared 1459 } 1460 1461 #[inline] as_cont(&self) -> Option<&WasmContType>1462 pub fn as_cont(&self) -> Option<&WasmContType> { 1463 if self.composite_type.shared { 1464 None 1465 } else { 1466 self.composite_type.inner.as_cont() 1467 } 1468 } 1469 1470 #[inline] unwrap_cont(&self) -> &WasmContType1471 pub fn unwrap_cont(&self) -> &WasmContType { 1472 assert!(!self.composite_type.shared); 1473 self.composite_type.inner.unwrap_cont() 1474 } 1475 1476 #[inline] is_exn(&self) -> bool1477 pub fn is_exn(&self) -> bool { 1478 self.composite_type.inner.is_exn() && !self.composite_type.shared 1479 } 1480 1481 #[inline] as_exn(&self) -> Option<&WasmExnType>1482 pub fn as_exn(&self) -> Option<&WasmExnType> { 1483 if self.composite_type.shared { 1484 None 1485 } else { 1486 self.composite_type.inner.as_exn() 1487 } 1488 } 1489 1490 #[inline] unwrap_exn(&self) -> &WasmExnType1491 pub fn unwrap_exn(&self) -> &WasmExnType { 1492 assert!(!self.composite_type.shared); 1493 self.composite_type.inner.unwrap_exn() 1494 } 1495 } 1496 1497 impl TypeTrace for WasmSubType { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1498 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1499 where 1500 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1501 { 1502 if let Some(sup) = self.supertype { 1503 func(sup)?; 1504 } 1505 self.composite_type.trace(func) 1506 } 1507 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1508 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1509 where 1510 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1511 { 1512 if let Some(sup) = self.supertype.as_mut() { 1513 func(sup)?; 1514 } 1515 self.composite_type.trace_mut(func) 1516 } 1517 } 1518 1519 /// A recursive type group. 1520 /// 1521 /// Types within a recgroup can have forward references to each other, which 1522 /// allows for cyclic types, for example a function `$f` that returns a 1523 /// reference to a function `$g` which returns a reference to a function `$f`: 1524 /// 1525 /// ```ignore 1526 /// (rec (type (func $f (result (ref null $g)))) 1527 /// (type (func $g (result (ref null $f))))) 1528 /// ``` 1529 #[derive(Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] 1530 pub struct WasmRecGroup { 1531 /// The types inside of this recgroup. 1532 pub types: Box<[WasmSubType]>, 1533 } 1534 1535 impl TypeTrace for WasmRecGroup { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1536 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1537 where 1538 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1539 { 1540 for ty in self.types.iter() { 1541 ty.trace(func)?; 1542 } 1543 Ok(()) 1544 } 1545 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1546 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1547 where 1548 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1549 { 1550 for ty in self.types.iter_mut() { 1551 ty.trace_mut(func)?; 1552 } 1553 Ok(()) 1554 } 1555 } 1556 1557 macro_rules! entity_impl_with_try_clone { 1558 ( $ty:ident ) => { 1559 cranelift_entity::entity_impl!($ty); 1560 1561 impl TryClone for $ty { 1562 #[inline] 1563 fn try_clone(&self) -> Result<Self, $crate::error::OutOfMemory> { 1564 Ok(*self) 1565 } 1566 } 1567 }; 1568 } 1569 1570 /// Index type of a function (imported or defined) inside the WebAssembly module. 1571 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1572 pub struct FuncIndex(u32); 1573 entity_impl_with_try_clone!(FuncIndex); 1574 1575 /// Index type of a defined function inside the WebAssembly module. 1576 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1577 pub struct DefinedFuncIndex(u32); 1578 entity_impl_with_try_clone!(DefinedFuncIndex); 1579 1580 /// Index type of a defined table inside the WebAssembly module. 1581 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1582 pub struct DefinedTableIndex(u32); 1583 entity_impl_with_try_clone!(DefinedTableIndex); 1584 1585 /// Index type of a defined memory inside the WebAssembly module. 1586 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1587 pub struct DefinedMemoryIndex(u32); 1588 entity_impl_with_try_clone!(DefinedMemoryIndex); 1589 1590 /// Index type of a defined memory inside the WebAssembly module. 1591 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1592 pub struct OwnedMemoryIndex(u32); 1593 entity_impl_with_try_clone!(OwnedMemoryIndex); 1594 1595 /// Index type of a defined global inside the WebAssembly module. 1596 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1597 pub struct DefinedGlobalIndex(u32); 1598 entity_impl_with_try_clone!(DefinedGlobalIndex); 1599 1600 /// Index type of a table (imported or defined) inside the WebAssembly module. 1601 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1602 pub struct TableIndex(u32); 1603 entity_impl_with_try_clone!(TableIndex); 1604 1605 /// Index type of a global variable (imported or defined) inside the WebAssembly module. 1606 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1607 pub struct GlobalIndex(u32); 1608 entity_impl_with_try_clone!(GlobalIndex); 1609 1610 /// Index type of a linear memory (imported or defined) inside the WebAssembly module. 1611 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1612 pub struct MemoryIndex(u32); 1613 entity_impl_with_try_clone!(MemoryIndex); 1614 1615 /// Index type of a canonicalized recursive type group inside a WebAssembly 1616 /// module (as opposed to canonicalized within the whole engine). 1617 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1618 pub struct ModuleInternedRecGroupIndex(u32); 1619 entity_impl_with_try_clone!(ModuleInternedRecGroupIndex); 1620 1621 /// Index type of a canonicalized recursive type group inside the whole engine 1622 /// (as opposed to canonicalized within just a single Wasm module). 1623 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1624 pub struct EngineInternedRecGroupIndex(u32); 1625 entity_impl_with_try_clone!(EngineInternedRecGroupIndex); 1626 1627 /// Index type of a type (imported or defined) inside the WebAssembly module. 1628 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1629 pub struct TypeIndex(u32); 1630 entity_impl_with_try_clone!(TypeIndex); 1631 1632 /// A canonicalized type index referencing a type within a single recursion 1633 /// group from another type within that same recursion group. 1634 /// 1635 /// This is only suitable for use when hash consing and deduplicating rec 1636 /// groups. 1637 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1638 pub struct RecGroupRelativeTypeIndex(u32); 1639 entity_impl_with_try_clone!(RecGroupRelativeTypeIndex); 1640 1641 /// A canonicalized type index for a type within a single WebAssembly module. 1642 /// 1643 /// Note that this is deduplicated only at the level of a single WebAssembly 1644 /// module, not at the level of a whole store or engine. This means that these 1645 /// indices are only unique within the context of a single Wasm module, and 1646 /// therefore are not suitable for runtime type checks (which, in general, may 1647 /// involve entities defined in different modules). 1648 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1649 pub struct ModuleInternedTypeIndex(u32); 1650 entity_impl_with_try_clone!(ModuleInternedTypeIndex); 1651 1652 /// A canonicalized type index into an engine's shared type registry. 1653 /// 1654 /// This is canonicalized/deduped at the level of a whole engine, across all the 1655 /// modules loaded into that engine, not just at the level of a single 1656 /// particular module. This means that `VMSharedTypeIndex` is usable for 1657 /// e.g. checking that function signatures match during an indirect call 1658 /// (potentially to a function defined in a different module) at runtime. 1659 #[repr(transparent)] // Used directly by JIT code. 1660 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1661 pub struct VMSharedTypeIndex(u32); 1662 entity_impl_with_try_clone!(VMSharedTypeIndex); 1663 1664 impl VMSharedTypeIndex { 1665 /// Create a new `VMSharedTypeIndex`. 1666 #[inline] new(value: u32) -> Self1667 pub fn new(value: u32) -> Self { 1668 assert_ne!( 1669 value, 1670 u32::MAX, 1671 "u32::MAX is reserved for the default value" 1672 ); 1673 Self(value) 1674 } 1675 1676 /// Returns the underlying bits of the index. 1677 #[inline] bits(&self) -> u321678 pub fn bits(&self) -> u32 { 1679 self.0 1680 } 1681 } 1682 1683 impl Default for VMSharedTypeIndex { 1684 #[inline] default() -> Self1685 fn default() -> Self { 1686 Self(u32::MAX) 1687 } 1688 } 1689 1690 /// Index type of a passive data segment inside the WebAssembly module. 1691 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1692 pub struct DataIndex(u32); 1693 entity_impl_with_try_clone!(DataIndex); 1694 1695 /// Index type of a passive element segment inside the WebAssembly module. 1696 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1697 pub struct ElemIndex(u32); 1698 entity_impl_with_try_clone!(ElemIndex); 1699 1700 /// Index type of a defined tag inside the WebAssembly module. 1701 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1702 pub struct DefinedTagIndex(u32); 1703 entity_impl_with_try_clone!(DefinedTagIndex); 1704 1705 /// Index type of an event inside the WebAssembly module. 1706 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1707 pub struct TagIndex(u32); 1708 entity_impl_with_try_clone!(TagIndex); 1709 1710 /// Index into the global list of modules found within an entire component. 1711 /// 1712 /// Module translations are saved on the side to get fully compiled after 1713 /// the original component has finished being translated. 1714 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1715 pub struct StaticModuleIndex(u32); 1716 entity_impl_with_try_clone!(StaticModuleIndex); 1717 1718 /// An index of an entity. 1719 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)] 1720 pub enum EntityIndex { 1721 /// Function index. 1722 Function(FuncIndex), 1723 /// Table index. 1724 Table(TableIndex), 1725 /// Memory index. 1726 Memory(MemoryIndex), 1727 /// Global index. 1728 Global(GlobalIndex), 1729 /// Tag index. 1730 Tag(TagIndex), 1731 } 1732 1733 impl From<FuncIndex> for EntityIndex { from(idx: FuncIndex) -> EntityIndex1734 fn from(idx: FuncIndex) -> EntityIndex { 1735 EntityIndex::Function(idx) 1736 } 1737 } 1738 1739 impl From<TableIndex> for EntityIndex { from(idx: TableIndex) -> EntityIndex1740 fn from(idx: TableIndex) -> EntityIndex { 1741 EntityIndex::Table(idx) 1742 } 1743 } 1744 1745 impl From<MemoryIndex> for EntityIndex { from(idx: MemoryIndex) -> EntityIndex1746 fn from(idx: MemoryIndex) -> EntityIndex { 1747 EntityIndex::Memory(idx) 1748 } 1749 } 1750 1751 impl From<GlobalIndex> for EntityIndex { from(idx: GlobalIndex) -> EntityIndex1752 fn from(idx: GlobalIndex) -> EntityIndex { 1753 EntityIndex::Global(idx) 1754 } 1755 } 1756 1757 impl From<TagIndex> for EntityIndex { from(idx: TagIndex) -> EntityIndex1758 fn from(idx: TagIndex) -> EntityIndex { 1759 EntityIndex::Tag(idx) 1760 } 1761 } 1762 1763 /// A type of an item in a wasm module where an item is typically something that 1764 /// can be exported. 1765 #[derive(Clone, Debug, Serialize, Deserialize)] 1766 pub enum EntityType { 1767 /// A global variable with the specified content type 1768 Global(Global), 1769 /// A linear memory with the specified limits 1770 Memory(Memory), 1771 /// An exception and control tag definition. 1772 Tag(Tag), 1773 /// A table with the specified element type and limits 1774 Table(Table), 1775 /// A function type where the index points to the type section and records a 1776 /// function signature. 1777 Function(EngineOrModuleTypeIndex), 1778 } 1779 1780 impl TypeTrace for EntityType { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1781 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1782 where 1783 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1784 { 1785 match self { 1786 Self::Global(g) => g.trace(func), 1787 Self::Table(t) => t.trace(func), 1788 Self::Function(idx) => func(*idx), 1789 Self::Memory(_) => Ok(()), 1790 Self::Tag(t) => t.trace(func), 1791 } 1792 } 1793 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1794 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1795 where 1796 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1797 { 1798 match self { 1799 Self::Global(g) => g.trace_mut(func), 1800 Self::Table(t) => t.trace_mut(func), 1801 Self::Function(idx) => func(idx), 1802 Self::Memory(_) => Ok(()), 1803 Self::Tag(t) => t.trace_mut(func), 1804 } 1805 } 1806 } 1807 1808 impl EntityType { 1809 /// Assert that this entity is a global unwrap_global(&self) -> &Global1810 pub fn unwrap_global(&self) -> &Global { 1811 match self { 1812 EntityType::Global(g) => g, 1813 _ => panic!("not a global"), 1814 } 1815 } 1816 1817 /// Assert that this entity is a memory unwrap_memory(&self) -> &Memory1818 pub fn unwrap_memory(&self) -> &Memory { 1819 match self { 1820 EntityType::Memory(g) => g, 1821 _ => panic!("not a memory"), 1822 } 1823 } 1824 1825 /// Assert that this entity is a tag unwrap_tag(&self) -> &Tag1826 pub fn unwrap_tag(&self) -> &Tag { 1827 match self { 1828 EntityType::Tag(g) => g, 1829 _ => panic!("not a tag"), 1830 } 1831 } 1832 1833 /// Assert that this entity is a table unwrap_table(&self) -> &Table1834 pub fn unwrap_table(&self) -> &Table { 1835 match self { 1836 EntityType::Table(g) => g, 1837 _ => panic!("not a table"), 1838 } 1839 } 1840 1841 /// Assert that this entity is a function unwrap_func(&self) -> EngineOrModuleTypeIndex1842 pub fn unwrap_func(&self) -> EngineOrModuleTypeIndex { 1843 match self { 1844 EntityType::Function(g) => *g, 1845 _ => panic!("not a func"), 1846 } 1847 } 1848 } 1849 1850 /// A WebAssembly global. 1851 /// 1852 /// Note that we record both the original Wasm type and the Cranelift IR type 1853 /// used to represent it. This is because multiple different kinds of Wasm types 1854 /// might be represented with the same Cranelift IR type. For example, both a 1855 /// Wasm `i64` and a `funcref` might be represented with a Cranelift `i64` on 1856 /// 64-bit architectures, and when GC is not required for func refs. 1857 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 1858 pub struct Global { 1859 /// The Wasm type of the value stored in the global. 1860 pub wasm_ty: crate::WasmValType, 1861 /// A flag indicating whether the value may change at runtime. 1862 pub mutability: bool, 1863 } 1864 1865 impl TypeTrace for Global { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1866 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 1867 where 1868 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 1869 { 1870 let Global { 1871 wasm_ty, 1872 mutability: _, 1873 } = self; 1874 wasm_ty.trace(func) 1875 } 1876 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1877 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 1878 where 1879 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 1880 { 1881 let Global { 1882 wasm_ty, 1883 mutability: _, 1884 } = self; 1885 wasm_ty.trace_mut(func) 1886 } 1887 } 1888 1889 /// A constant expression. 1890 /// 1891 /// These are used to initialize globals, table elements, etc... 1892 #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] 1893 pub struct ConstExpr { 1894 ops: SmallVec<[ConstOp; 2]>, 1895 } 1896 1897 impl ConstExpr { 1898 /// Create a new const expression from the given opcodes. 1899 /// 1900 /// Does not do any validation that the const expression is well-typed. 1901 /// 1902 /// Panics if given zero opcodes. new(ops: impl IntoIterator<Item = ConstOp>) -> Self1903 pub fn new(ops: impl IntoIterator<Item = ConstOp>) -> Self { 1904 let ops = ops.into_iter().collect::<SmallVec<[ConstOp; 2]>>(); 1905 assert!(!ops.is_empty()); 1906 ConstExpr { ops } 1907 } 1908 1909 /// Create a new const expression from a `wasmparser` const expression. 1910 /// 1911 /// Returns the new const expression as well as the escaping function 1912 /// indices that appeared in `ref.func` instructions, if any. from_wasmparser( env: &dyn TypeConvert, expr: wasmparser::ConstExpr<'_>, ) -> WasmResult<(Self, SmallVec<[FuncIndex; 1]>)>1913 pub fn from_wasmparser( 1914 env: &dyn TypeConvert, 1915 expr: wasmparser::ConstExpr<'_>, 1916 ) -> WasmResult<(Self, SmallVec<[FuncIndex; 1]>)> { 1917 let mut iter = expr 1918 .get_operators_reader() 1919 .into_iter_with_offsets() 1920 .peekable(); 1921 1922 let mut ops = SmallVec::<[ConstOp; 2]>::new(); 1923 let mut escaped = SmallVec::<[FuncIndex; 1]>::new(); 1924 while let Some(res) = iter.next() { 1925 let (op, offset) = res?; 1926 1927 // If we reach an `end` instruction, and there are no more 1928 // instructions after that, then we are done reading this const 1929 // expression. 1930 if matches!(op, wasmparser::Operator::End) && iter.peek().is_none() { 1931 break; 1932 } 1933 1934 // Track any functions that appear in `ref.func` so that callers can 1935 // make sure to flag them as escaping. 1936 if let wasmparser::Operator::RefFunc { function_index } = &op { 1937 escaped.push(FuncIndex::from_u32(*function_index)); 1938 } 1939 1940 ops.push(ConstOp::from_wasmparser(env, op, offset)?); 1941 } 1942 Ok((Self { ops }, escaped)) 1943 } 1944 1945 /// Get the opcodes that make up this const expression. 1946 #[inline] ops(&self) -> &[ConstOp]1947 pub fn ops(&self) -> &[ConstOp] { 1948 &self.ops 1949 } 1950 1951 /// Is this ConstExpr a provably nonzero integer value? 1952 /// 1953 /// This must be conservative: if the expression *might* be zero, 1954 /// it must return `false`. It is always allowed to return `false` 1955 /// for some expression kind that we don't support. However, if it 1956 /// returns `true`, the expression must be actually nonzero. 1957 /// 1958 /// We use this for certain table optimizations that rely on 1959 /// knowing for sure that index 0 is not referenced. provably_nonzero_i32(&self) -> bool1960 pub fn provably_nonzero_i32(&self) -> bool { 1961 match self.const_eval() { 1962 Some(GlobalConstValue::I32(x)) => x != 0, 1963 1964 // Conservatively return `false` for non-const-eval-able expressions 1965 // as well as everything else. 1966 _ => false, 1967 } 1968 } 1969 1970 /// Attempt to evaluate the given const-expr at compile time. const_eval(&self) -> Option<GlobalConstValue>1971 pub fn const_eval(&self) -> Option<GlobalConstValue> { 1972 // TODO: Actually maintain an evaluation stack and handle `i32.add`, 1973 // `i32.sub`, etc... const ops. 1974 match self.ops() { 1975 [ConstOp::I32Const(x)] => Some(GlobalConstValue::I32(*x)), 1976 [ConstOp::I64Const(x)] => Some(GlobalConstValue::I64(*x)), 1977 [ConstOp::F32Const(x)] => Some(GlobalConstValue::F32(*x)), 1978 [ConstOp::F64Const(x)] => Some(GlobalConstValue::F64(*x)), 1979 [ConstOp::V128Const(x)] => Some(GlobalConstValue::V128(*x)), 1980 _ => None, 1981 } 1982 } 1983 } 1984 1985 /// A global's constant value, known at compile time. 1986 #[expect(missing_docs, reason = "self-describing variants")] 1987 #[derive(Clone, Copy)] 1988 pub enum GlobalConstValue { 1989 I32(i32), 1990 I64(i64), 1991 F32(u32), 1992 F64(u64), 1993 V128(u128), 1994 } 1995 1996 /// The subset of Wasm opcodes that are constant. 1997 #[expect(missing_docs, reason = "self-describing variants")] 1998 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] 1999 pub enum ConstOp { 2000 I32Const(i32), 2001 I64Const(i64), 2002 F32Const(u32), 2003 F64Const(u64), 2004 V128Const(u128), 2005 GlobalGet(GlobalIndex), 2006 RefI31, 2007 RefNull(WasmHeapTopType), 2008 RefFunc(FuncIndex), 2009 I32Add, 2010 I32Sub, 2011 I32Mul, 2012 I64Add, 2013 I64Sub, 2014 I64Mul, 2015 StructNew { 2016 struct_type_index: TypeIndex, 2017 }, 2018 StructNewDefault { 2019 struct_type_index: TypeIndex, 2020 }, 2021 ArrayNew { 2022 array_type_index: TypeIndex, 2023 }, 2024 ArrayNewDefault { 2025 array_type_index: TypeIndex, 2026 }, 2027 ArrayNewFixed { 2028 array_type_index: TypeIndex, 2029 array_size: u32, 2030 }, 2031 ExternConvertAny, 2032 AnyConvertExtern, 2033 } 2034 2035 impl ConstOp { 2036 /// Convert a `wasmparser::Operator` to a `ConstOp`. from_wasmparser( env: &dyn TypeConvert, op: wasmparser::Operator<'_>, offset: usize, ) -> WasmResult<Self>2037 pub fn from_wasmparser( 2038 env: &dyn TypeConvert, 2039 op: wasmparser::Operator<'_>, 2040 offset: usize, 2041 ) -> WasmResult<Self> { 2042 use wasmparser::Operator as O; 2043 Ok(match op { 2044 O::I32Const { value } => Self::I32Const(value), 2045 O::I64Const { value } => Self::I64Const(value), 2046 O::F32Const { value } => Self::F32Const(value.bits()), 2047 O::F64Const { value } => Self::F64Const(value.bits()), 2048 O::V128Const { value } => Self::V128Const(u128::from_le_bytes(*value.bytes())), 2049 O::RefNull { hty } => Self::RefNull(env.convert_heap_type(hty)?.top()), 2050 O::RefFunc { function_index } => Self::RefFunc(FuncIndex::from_u32(function_index)), 2051 O::GlobalGet { global_index } => Self::GlobalGet(GlobalIndex::from_u32(global_index)), 2052 O::RefI31 => Self::RefI31, 2053 O::I32Add => Self::I32Add, 2054 O::I32Sub => Self::I32Sub, 2055 O::I32Mul => Self::I32Mul, 2056 O::I64Add => Self::I64Add, 2057 O::I64Sub => Self::I64Sub, 2058 O::I64Mul => Self::I64Mul, 2059 O::StructNew { struct_type_index } => Self::StructNew { 2060 struct_type_index: TypeIndex::from_u32(struct_type_index), 2061 }, 2062 O::StructNewDefault { struct_type_index } => Self::StructNewDefault { 2063 struct_type_index: TypeIndex::from_u32(struct_type_index), 2064 }, 2065 O::ArrayNew { array_type_index } => Self::ArrayNew { 2066 array_type_index: TypeIndex::from_u32(array_type_index), 2067 }, 2068 O::ArrayNewDefault { array_type_index } => Self::ArrayNewDefault { 2069 array_type_index: TypeIndex::from_u32(array_type_index), 2070 }, 2071 O::ArrayNewFixed { 2072 array_type_index, 2073 array_size, 2074 } => Self::ArrayNewFixed { 2075 array_type_index: TypeIndex::from_u32(array_type_index), 2076 array_size, 2077 }, 2078 O::ExternConvertAny => Self::ExternConvertAny, 2079 O::AnyConvertExtern => Self::AnyConvertExtern, 2080 op => { 2081 return Err(wasm_unsupported!( 2082 "unsupported opcode in const expression at offset {offset:#x}: {op:?}", 2083 )); 2084 } 2085 }) 2086 } 2087 } 2088 2089 /// The type that can be used to index into [Memory] and [Table]. 2090 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 2091 #[expect(missing_docs, reason = "self-describing variants")] 2092 pub enum IndexType { 2093 I32, 2094 I64, 2095 } 2096 2097 /// The size range of resizeable storage associated with [Memory] types and [Table] types. 2098 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 2099 #[expect(missing_docs, reason = "self-describing fields")] 2100 pub struct Limits { 2101 pub min: u64, 2102 pub max: Option<u64>, 2103 } 2104 2105 /// WebAssembly table. 2106 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 2107 pub struct Table { 2108 /// The type of the index used to access the table. 2109 pub idx_type: IndexType, 2110 /// Tables are constrained by limits for their minimum and optionally maximum size. 2111 /// The limits are given in numbers of entries. 2112 pub limits: Limits, 2113 /// The table elements' Wasm type. 2114 pub ref_type: WasmRefType, 2115 } 2116 2117 impl TypeTrace for Table { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,2118 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 2119 where 2120 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 2121 { 2122 let Table { 2123 ref_type: wasm_ty, 2124 idx_type: _, 2125 limits: _, 2126 } = self; 2127 wasm_ty.trace(func) 2128 } 2129 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,2130 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 2131 where 2132 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 2133 { 2134 let Table { 2135 ref_type: wasm_ty, 2136 idx_type: _, 2137 limits: _, 2138 } = self; 2139 wasm_ty.trace_mut(func) 2140 } 2141 } 2142 2143 /// WebAssembly linear memory. 2144 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 2145 pub struct Memory { 2146 /// The type of the index used to access the memory. 2147 pub idx_type: IndexType, 2148 /// The limits constrain the minimum and optionally the maximum size of a memory. 2149 /// The limits are given in units of page size. 2150 pub limits: Limits, 2151 /// Whether the memory may be shared between multiple threads. 2152 pub shared: bool, 2153 /// The log2 of this memory's page size, in bytes. 2154 /// 2155 /// By default the page size is 64KiB (0x10000; 2**16; 1<<16; 65536) but the 2156 /// custom-page-sizes proposal allows opting into a page size of `1`. 2157 pub page_size_log2: u8, 2158 } 2159 2160 /// Maximum size, in bytes, of 32-bit memories (4G) 2161 pub const WASM32_MAX_SIZE: u64 = 1 << 32; 2162 2163 impl Memory { 2164 /// WebAssembly page sizes are 64KiB by default. 2165 pub const DEFAULT_PAGE_SIZE: u32 = 0x10000; 2166 2167 /// WebAssembly page sizes are 64KiB (or `2**16`) by default. 2168 pub const DEFAULT_PAGE_SIZE_LOG2: u8 = { 2169 let log2 = 16; 2170 assert!(1 << log2 == Memory::DEFAULT_PAGE_SIZE); 2171 log2 2172 }; 2173 2174 /// Returns the minimum size, in bytes, that this memory must be. 2175 /// 2176 /// # Errors 2177 /// 2178 /// Returns an error if the calculation of the minimum size overflows the 2179 /// `u64` return type. This means that the memory can't be allocated but 2180 /// it's deferred to the caller to how to deal with that. minimum_byte_size(&self) -> Result<u64, SizeOverflow>2181 pub fn minimum_byte_size(&self) -> Result<u64, SizeOverflow> { 2182 self.limits 2183 .min 2184 .checked_mul(self.page_size()) 2185 .ok_or(SizeOverflow) 2186 } 2187 2188 /// Returns the maximum size, in bytes, that this memory is allowed to be. 2189 /// 2190 /// Note that the return value here is not an `Option` despite the maximum 2191 /// size of a linear memory being optional in wasm. If a maximum size 2192 /// is not present in the memory's type then a maximum size is selected for 2193 /// it. For example the maximum size of a 32-bit memory is `1<<32`. The 2194 /// maximum size of a 64-bit linear memory is chosen to be a value that 2195 /// won't ever be allowed at runtime. 2196 /// 2197 /// # Errors 2198 /// 2199 /// Returns an error if the calculation of the maximum size overflows the 2200 /// `u64` return type. This means that the memory can't be allocated but 2201 /// it's deferred to the caller to how to deal with that. maximum_byte_size(&self) -> Result<u64, SizeOverflow>2202 pub fn maximum_byte_size(&self) -> Result<u64, SizeOverflow> { 2203 match self.limits.max { 2204 Some(max) => max.checked_mul(self.page_size()).ok_or(SizeOverflow), 2205 None => { 2206 let min = self.minimum_byte_size()?; 2207 Ok(min.max(self.max_size_based_on_index_type())) 2208 } 2209 } 2210 } 2211 2212 /// Get the size of this memory's pages, in bytes. page_size(&self) -> u642213 pub fn page_size(&self) -> u64 { 2214 debug_assert!( 2215 self.page_size_log2 == 16 || self.page_size_log2 == 0, 2216 "invalid page_size_log2: {}; must be 16 or 0", 2217 self.page_size_log2 2218 ); 2219 1 << self.page_size_log2 2220 } 2221 2222 /// Returns the maximum size memory is allowed to be only based on the 2223 /// index type used by this memory. 2224 /// 2225 /// For example 32-bit linear memories return `1<<32` from this method. max_size_based_on_index_type(&self) -> u642226 pub fn max_size_based_on_index_type(&self) -> u64 { 2227 match self.idx_type { 2228 IndexType::I64 => 2229 // Note that the true maximum size of a 64-bit linear memory, in 2230 // bytes, cannot be represented in a `u64`. That would require a u65 2231 // to store `1<<64`. Despite that no system can actually allocate a 2232 // full 64-bit linear memory so this is instead emulated as "what if 2233 // the kernel fit in a single Wasm page of linear memory". Shouldn't 2234 // ever actually be possible but it provides a number to serve as an 2235 // effective maximum. 2236 { 2237 0_u64.wrapping_sub(self.page_size()) 2238 } 2239 IndexType::I32 => WASM32_MAX_SIZE, 2240 } 2241 } 2242 2243 /// Returns whether this memory can be implemented with virtual memory on 2244 /// a host with `host_page_size_log2`. 2245 /// 2246 /// When this function returns `true` then it means that signals such as 2247 /// SIGSEGV on the host are compatible with wasm and can be used to 2248 /// represent out-of-bounds memory accesses. 2249 /// 2250 /// When this function returns `false` then it means that this memory must, 2251 /// for example, have explicit bounds checks. This additionally means that 2252 /// virtual memory traps (e.g. SIGSEGV) cannot be relied on to implement 2253 /// linear memory semantics. can_use_virtual_memory(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool2254 pub fn can_use_virtual_memory(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool { 2255 tunables.signals_based_traps && self.page_size_log2 >= host_page_size_log2 2256 } 2257 2258 /// Returns whether this memory is a candidate for bounds check elision 2259 /// given the configuration and host page size. 2260 /// 2261 /// This function determines whether the given compilation configuration and 2262 /// hos enables possible bounds check elision for this memory. Bounds checks 2263 /// can only be elided if [`Memory::can_use_virtual_memory`] returns `true` 2264 /// for example but there are additionally requirements on the index size of 2265 /// this memory and the memory reservation in `tunables`. 2266 /// 2267 /// Currently the only case that supports bounds check elision is when all 2268 /// of these apply: 2269 /// 2270 /// * When [`Memory::can_use_virtual_memory`] returns `true`. 2271 /// * This is a 32-bit linear memory (e.g. not 64-bit) 2272 /// * `tunables.memory_reservation` is in excess of 4GiB 2273 /// 2274 /// In this situation all computable addresses fall within the reserved 2275 /// space (modulo static offsets factoring in guard pages) so bounds checks 2276 /// may be elidable. can_elide_bounds_check(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool2277 pub fn can_elide_bounds_check(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool { 2278 self.can_use_virtual_memory(tunables, host_page_size_log2) 2279 && self.idx_type == IndexType::I32 2280 && tunables.memory_reservation + tunables.memory_guard_size >= (1 << 32) 2281 } 2282 2283 /// Returns the static size of this heap in bytes at runtime, if available. 2284 /// 2285 /// This is only computable when the minimum size equals the maximum size. static_heap_size(&self) -> Option<u64>2286 pub fn static_heap_size(&self) -> Option<u64> { 2287 let min = self.minimum_byte_size().ok()?; 2288 let max = self.maximum_byte_size().ok()?; 2289 if min == max { Some(min) } else { None } 2290 } 2291 2292 /// Returns whether or not the base pointer of this memory is allowed to be 2293 /// relocated at runtime. 2294 /// 2295 /// When this function returns `false` then it means that after the initial 2296 /// allocation the base pointer is constant for the entire lifetime of a 2297 /// memory. This can enable compiler optimizations, for example. memory_may_move(&self, tunables: &Tunables) -> bool2298 pub fn memory_may_move(&self, tunables: &Tunables) -> bool { 2299 // Shared memories cannot ever relocate their base pointer so the 2300 // settings configured in the engine must be appropriate for them ahead 2301 // of time. 2302 if self.shared { 2303 return false; 2304 } 2305 2306 // If movement is disallowed in engine configuration, then the answer is 2307 // "no". 2308 if !tunables.memory_may_move { 2309 return false; 2310 } 2311 2312 // If its minimum and maximum are the same, then the memory will never 2313 // be resized, and therefore will never move. 2314 if self.limits.max.is_some_and(|max| self.limits.min == max) { 2315 return false; 2316 } 2317 2318 // If the maximum size of this memory is above the threshold of the 2319 // initial memory reservation then the memory may move. 2320 let max = self.maximum_byte_size().unwrap_or(u64::MAX); 2321 max > tunables.memory_reservation 2322 } 2323 2324 /// Tests whether this memory type is allowed to grow up to `size` bytes. 2325 /// 2326 /// This is only applicable to custom-page-size memories which have a page 2327 /// size of a single byte. In that situation growth beyond `-1i32 as u32` 2328 /// bytes is not allowed because at that point memory growth succeeding and 2329 /// failing would be indistinguishable in the return value of `memory.grow`, 2330 /// for example. To handle this 32-bit memories are only allowed to grow to 2331 /// `-2i32 as u32`, for example, and 64-bit memories with a page size of 1 2332 /// are allowed to grow up to the maximum size. allow_growth_to(&self, size: usize) -> bool2333 pub fn allow_growth_to(&self, size: usize) -> bool { 2334 if self.page_size_log2 != 0 { 2335 return true; 2336 } 2337 match self.idx_type { 2338 // For a 32-bit memory using 1-byte pages the last 2 bytes of the 2339 // 32-bit address space are addressable but disallowed for now. A 2340 // memory that is 4GiB in size cannot report its size via 2341 // `memory.size`, and a memory that is 4GiB-1 bytes in size cannot 2342 // be distinguished when 1 byte is added from an allocation 2343 // failure. To handle this the memory is capped at 4GiB-2 which 2344 // means that all memory-related instructions and such will have 2345 // unambiguous return codes. 2346 IndexType::I32 => size < 0xffff_ffff, 2347 2348 // Assume that for a 64-bit memory using 1-byte pages it's going to 2349 // exhaust system resources before a limit is actually reached. 2350 IndexType::I64 => true, 2351 } 2352 } 2353 } 2354 2355 #[derive(Copy, Clone, Debug)] 2356 #[expect(missing_docs, reason = "self-describing error struct")] 2357 pub struct SizeOverflow; 2358 2359 impl fmt::Display for SizeOverflow { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result2360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 2361 f.write_str("size overflow calculating memory size") 2362 } 2363 } 2364 2365 impl core::error::Error for SizeOverflow {} 2366 2367 impl From<wasmparser::MemoryType> for Memory { from(ty: wasmparser::MemoryType) -> Memory2368 fn from(ty: wasmparser::MemoryType) -> Memory { 2369 let idx_type = match ty.memory64 { 2370 false => IndexType::I32, 2371 true => IndexType::I64, 2372 }; 2373 let limits = Limits { 2374 min: ty.initial, 2375 max: ty.maximum, 2376 }; 2377 let page_size_log2 = u8::try_from(ty.page_size_log2.unwrap_or(16)).unwrap(); 2378 debug_assert!( 2379 page_size_log2 == 16 || page_size_log2 == 0, 2380 "invalid page_size_log2: {page_size_log2}; must be 16 or 0" 2381 ); 2382 Memory { 2383 idx_type, 2384 limits, 2385 shared: ty.shared, 2386 page_size_log2, 2387 } 2388 } 2389 } 2390 2391 /// WebAssembly exception and control tag. 2392 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)] 2393 pub struct Tag { 2394 /// The tag signature type. 2395 pub signature: EngineOrModuleTypeIndex, 2396 /// The corresponding exception type. 2397 pub exception: EngineOrModuleTypeIndex, 2398 } 2399 2400 impl TypeTrace for Tag { trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,2401 fn trace<F, E>(&self, func: &mut F) -> Result<(), E> 2402 where 2403 F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>, 2404 { 2405 func(self.signature)?; 2406 func(self.exception)?; 2407 Ok(()) 2408 } 2409 trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,2410 fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> 2411 where 2412 F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>, 2413 { 2414 func(&mut self.signature)?; 2415 func(&mut self.exception)?; 2416 Ok(()) 2417 } 2418 } 2419 2420 /// Helpers used to convert a `wasmparser` type to a type in this crate. 2421 #[expect(missing_docs, reason = "self-describing functions")] 2422 pub trait TypeConvert { 2423 /// Converts a wasmparser table type into a wasmtime type convert_global_type(&self, ty: &wasmparser::GlobalType) -> WasmResult<Global>2424 fn convert_global_type(&self, ty: &wasmparser::GlobalType) -> WasmResult<Global> { 2425 Ok(Global { 2426 wasm_ty: self.convert_valtype(ty.content_type)?, 2427 mutability: ty.mutable, 2428 }) 2429 } 2430 2431 /// Converts a wasmparser table type into a wasmtime type convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table>2432 fn convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table> { 2433 let idx_type = match ty.table64 { 2434 false => IndexType::I32, 2435 true => IndexType::I64, 2436 }; 2437 let limits = Limits { 2438 min: ty.initial, 2439 max: ty.maximum, 2440 }; 2441 Ok(Table { 2442 idx_type, 2443 limits, 2444 ref_type: self.convert_ref_type(ty.element_type)?, 2445 }) 2446 } 2447 convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType>2448 fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> { 2449 Ok(WasmSubType { 2450 is_final: ty.is_final, 2451 supertype: ty.supertype_idx.map(|i| self.lookup_type_index(i.unpack())), 2452 composite_type: self.convert_composite_type(&ty.composite_type)?, 2453 }) 2454 } 2455 convert_composite_type( &self, ty: &wasmparser::CompositeType, ) -> WasmResult<WasmCompositeType>2456 fn convert_composite_type( 2457 &self, 2458 ty: &wasmparser::CompositeType, 2459 ) -> WasmResult<WasmCompositeType> { 2460 let inner = match &ty.inner { 2461 wasmparser::CompositeInnerType::Func(f) => { 2462 WasmCompositeInnerType::Func(self.convert_func_type(f)?) 2463 } 2464 wasmparser::CompositeInnerType::Array(a) => { 2465 WasmCompositeInnerType::Array(self.convert_array_type(a)?) 2466 } 2467 wasmparser::CompositeInnerType::Struct(s) => { 2468 WasmCompositeInnerType::Struct(self.convert_struct_type(s)?) 2469 } 2470 wasmparser::CompositeInnerType::Cont(c) => { 2471 WasmCompositeInnerType::Cont(self.convert_cont_type(c)) 2472 } 2473 }; 2474 Ok(WasmCompositeType { 2475 inner, 2476 shared: ty.shared, 2477 }) 2478 } 2479 2480 /// Converts a wasmparser continuation type to a wasmtime type convert_cont_type(&self, ty: &wasmparser::ContType) -> WasmContType2481 fn convert_cont_type(&self, ty: &wasmparser::ContType) -> WasmContType { 2482 if let WasmHeapType::ConcreteFunc(sigidx) = self.lookup_heap_type(ty.0.unpack()) { 2483 WasmContType::new(sigidx) 2484 } else { 2485 panic!("Failed to extract signature index for continuation type.") 2486 } 2487 } 2488 convert_struct_type(&self, ty: &wasmparser::StructType) -> WasmResult<WasmStructType>2489 fn convert_struct_type(&self, ty: &wasmparser::StructType) -> WasmResult<WasmStructType> { 2490 Ok(WasmStructType { 2491 fields: ty 2492 .fields 2493 .iter() 2494 .map(|f| self.convert_field_type(f)) 2495 .collect::<WasmResult<_>>()?, 2496 }) 2497 } 2498 convert_array_type(&self, ty: &wasmparser::ArrayType) -> WasmResult<WasmArrayType>2499 fn convert_array_type(&self, ty: &wasmparser::ArrayType) -> WasmResult<WasmArrayType> { 2500 Ok(WasmArrayType(self.convert_field_type(&ty.0)?)) 2501 } 2502 convert_field_type(&self, ty: &wasmparser::FieldType) -> WasmResult<WasmFieldType>2503 fn convert_field_type(&self, ty: &wasmparser::FieldType) -> WasmResult<WasmFieldType> { 2504 Ok(WasmFieldType { 2505 element_type: self.convert_storage_type(&ty.element_type)?, 2506 mutable: ty.mutable, 2507 }) 2508 } 2509 convert_storage_type(&self, ty: &wasmparser::StorageType) -> WasmResult<WasmStorageType>2510 fn convert_storage_type(&self, ty: &wasmparser::StorageType) -> WasmResult<WasmStorageType> { 2511 Ok(match ty { 2512 wasmparser::StorageType::I8 => WasmStorageType::I8, 2513 wasmparser::StorageType::I16 => WasmStorageType::I16, 2514 wasmparser::StorageType::Val(v) => WasmStorageType::Val(self.convert_valtype(*v)?), 2515 }) 2516 } 2517 2518 /// Converts a wasmparser function type to a wasmtime type convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmResult<WasmFuncType>2519 fn convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmResult<WasmFuncType> { 2520 let params = ty 2521 .params() 2522 .iter() 2523 .map(|t| self.convert_valtype(*t)) 2524 .collect::<WasmResult<Vec<_>>>()?; 2525 let results = ty 2526 .results() 2527 .iter() 2528 .map(|t| self.convert_valtype(*t)) 2529 .collect::<WasmResult<Vec<_>>>()?; 2530 Ok(WasmFuncType::new(params, results).panic_on_oom()) 2531 } 2532 2533 /// Converts a wasmparser value type to a wasmtime type convert_valtype(&self, ty: wasmparser::ValType) -> WasmResult<WasmValType>2534 fn convert_valtype(&self, ty: wasmparser::ValType) -> WasmResult<WasmValType> { 2535 Ok(match ty { 2536 wasmparser::ValType::I32 => WasmValType::I32, 2537 wasmparser::ValType::I64 => WasmValType::I64, 2538 wasmparser::ValType::F32 => WasmValType::F32, 2539 wasmparser::ValType::F64 => WasmValType::F64, 2540 wasmparser::ValType::V128 => WasmValType::V128, 2541 wasmparser::ValType::Ref(t) => WasmValType::Ref(self.convert_ref_type(t)?), 2542 }) 2543 } 2544 2545 /// Converts a wasmparser reference type to a wasmtime type convert_ref_type(&self, ty: wasmparser::RefType) -> WasmResult<WasmRefType>2546 fn convert_ref_type(&self, ty: wasmparser::RefType) -> WasmResult<WasmRefType> { 2547 Ok(WasmRefType { 2548 nullable: ty.is_nullable(), 2549 heap_type: self.convert_heap_type(ty.heap_type())?, 2550 }) 2551 } 2552 2553 /// Converts a wasmparser heap type to a wasmtime type convert_heap_type(&self, ty: wasmparser::HeapType) -> WasmResult<WasmHeapType>2554 fn convert_heap_type(&self, ty: wasmparser::HeapType) -> WasmResult<WasmHeapType> { 2555 Ok(match ty { 2556 wasmparser::HeapType::Concrete(i) => self.lookup_heap_type(i), 2557 wasmparser::HeapType::Abstract { ty, shared: false } => match ty { 2558 wasmparser::AbstractHeapType::Extern => WasmHeapType::Extern, 2559 wasmparser::AbstractHeapType::NoExtern => WasmHeapType::NoExtern, 2560 wasmparser::AbstractHeapType::Func => WasmHeapType::Func, 2561 wasmparser::AbstractHeapType::NoFunc => WasmHeapType::NoFunc, 2562 wasmparser::AbstractHeapType::Any => WasmHeapType::Any, 2563 wasmparser::AbstractHeapType::Eq => WasmHeapType::Eq, 2564 wasmparser::AbstractHeapType::I31 => WasmHeapType::I31, 2565 wasmparser::AbstractHeapType::Array => WasmHeapType::Array, 2566 wasmparser::AbstractHeapType::Struct => WasmHeapType::Struct, 2567 wasmparser::AbstractHeapType::None => WasmHeapType::None, 2568 wasmparser::AbstractHeapType::Cont => WasmHeapType::Cont, 2569 wasmparser::AbstractHeapType::NoCont => WasmHeapType::NoCont, 2570 wasmparser::AbstractHeapType::Exn => WasmHeapType::Exn, 2571 wasmparser::AbstractHeapType::NoExn => WasmHeapType::NoExn, 2572 }, 2573 _ => return Err(wasm_unsupported!("unsupported heap type {ty:?}")), 2574 }) 2575 } 2576 2577 /// Converts the specified type index from a heap type into a canonicalized 2578 /// heap type. lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType2579 fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType; 2580 2581 /// Converts the specified type index from a heap type into a canonicalized 2582 /// heap type. lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex2583 fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex; 2584 } 2585 2586 #[cfg(test)] 2587 mod tests { 2588 use super::*; 2589 2590 #[test] wasm_func_type_new() -> Result<()>2591 fn wasm_func_type_new() -> Result<()> { 2592 let i32 = WasmValType::I32; 2593 let anyref = WasmValType::Ref(WasmRefType { 2594 nullable: true, 2595 heap_type: WasmHeapType::Any, 2596 }); 2597 let ty = WasmFuncType::new([i32, i32, anyref, anyref], [i32, anyref])?; 2598 assert_eq!(ty.params(), &[i32, i32, anyref, anyref]); 2599 assert_eq!(ty.non_i31_gc_ref_params_count(), 2); 2600 assert_eq!(ty.results(), &[i32, anyref]); 2601 assert_eq!(ty.non_i31_gc_ref_results_count(), 1); 2602 Ok(()) 2603 } 2604 } 2605