Skip to main content

wasmtime_environ/
types.rs

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