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