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 From<FuncIndex> for EntityIndex {
1795    fn from(idx: FuncIndex) -> EntityIndex {
1796        EntityIndex::Function(idx)
1797    }
1798}
1799
1800impl From<TableIndex> for EntityIndex {
1801    fn from(idx: TableIndex) -> EntityIndex {
1802        EntityIndex::Table(idx)
1803    }
1804}
1805
1806impl From<MemoryIndex> for EntityIndex {
1807    fn from(idx: MemoryIndex) -> EntityIndex {
1808        EntityIndex::Memory(idx)
1809    }
1810}
1811
1812impl From<GlobalIndex> for EntityIndex {
1813    fn from(idx: GlobalIndex) -> EntityIndex {
1814        EntityIndex::Global(idx)
1815    }
1816}
1817
1818impl From<TagIndex> for EntityIndex {
1819    fn from(idx: TagIndex) -> EntityIndex {
1820        EntityIndex::Tag(idx)
1821    }
1822}
1823
1824/// A type of an item in a wasm module where an item is typically something that
1825/// can be exported.
1826#[derive(Clone, Debug, Serialize, Deserialize)]
1827pub enum EntityType {
1828    /// A global variable with the specified content type
1829    Global(Global),
1830    /// A linear memory with the specified limits
1831    Memory(Memory),
1832    /// An exception and control tag definition.
1833    Tag(Tag),
1834    /// A table with the specified element type and limits
1835    Table(Table),
1836    /// A function type where the index points to the type section and records a
1837    /// function signature.
1838    Function(EngineOrModuleTypeIndex),
1839}
1840
1841impl TypeTrace for EntityType {
1842    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1843    where
1844        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1845    {
1846        match self {
1847            Self::Global(g) => g.trace(func),
1848            Self::Table(t) => t.trace(func),
1849            Self::Function(idx) => func(*idx),
1850            Self::Memory(_) => Ok(()),
1851            Self::Tag(t) => t.trace(func),
1852        }
1853    }
1854
1855    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1856    where
1857        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1858    {
1859        match self {
1860            Self::Global(g) => g.trace_mut(func),
1861            Self::Table(t) => t.trace_mut(func),
1862            Self::Function(idx) => func(idx),
1863            Self::Memory(_) => Ok(()),
1864            Self::Tag(t) => t.trace_mut(func),
1865        }
1866    }
1867}
1868
1869impl EntityType {
1870    /// Assert that this entity is a global
1871    pub fn unwrap_global(&self) -> &Global {
1872        match self {
1873            EntityType::Global(g) => g,
1874            _ => panic!("not a global"),
1875        }
1876    }
1877
1878    /// Assert that this entity is a memory
1879    pub fn unwrap_memory(&self) -> &Memory {
1880        match self {
1881            EntityType::Memory(g) => g,
1882            _ => panic!("not a memory"),
1883        }
1884    }
1885
1886    /// Assert that this entity is a tag
1887    pub fn unwrap_tag(&self) -> &Tag {
1888        match self {
1889            EntityType::Tag(g) => g,
1890            _ => panic!("not a tag"),
1891        }
1892    }
1893
1894    /// Assert that this entity is a table
1895    pub fn unwrap_table(&self) -> &Table {
1896        match self {
1897            EntityType::Table(g) => g,
1898            _ => panic!("not a table"),
1899        }
1900    }
1901
1902    /// Assert that this entity is a function
1903    pub fn unwrap_func(&self) -> EngineOrModuleTypeIndex {
1904        match self {
1905            EntityType::Function(g) => *g,
1906            _ => panic!("not a func"),
1907        }
1908    }
1909}
1910
1911/// A WebAssembly global.
1912///
1913/// Note that we record both the original Wasm type and the Cranelift IR type
1914/// used to represent it. This is because multiple different kinds of Wasm types
1915/// might be represented with the same Cranelift IR type. For example, both a
1916/// Wasm `i64` and a `funcref` might be represented with a Cranelift `i64` on
1917/// 64-bit architectures, and when GC is not required for func refs.
1918#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1919pub struct Global {
1920    /// The Wasm type of the value stored in the global.
1921    pub wasm_ty: crate::WasmValType,
1922    /// A flag indicating whether the value may change at runtime.
1923    pub mutability: bool,
1924}
1925
1926impl TypeTrace for Global {
1927    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1928    where
1929        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1930    {
1931        let Global {
1932            wasm_ty,
1933            mutability: _,
1934        } = self;
1935        wasm_ty.trace(func)
1936    }
1937
1938    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1939    where
1940        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1941    {
1942        let Global {
1943            wasm_ty,
1944            mutability: _,
1945        } = self;
1946        wasm_ty.trace_mut(func)
1947    }
1948}
1949
1950/// A constant expression.
1951///
1952/// These are used to initialize globals, table elements, etc...
1953#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1954pub struct ConstExpr {
1955    ops: SmallVec<[ConstOp; 2]>,
1956}
1957
1958impl ConstExpr {
1959    /// Create a new const expression from the given opcodes.
1960    ///
1961    /// Does not do any validation that the const expression is well-typed.
1962    ///
1963    /// Panics if given zero opcodes.
1964    pub fn new(ops: impl IntoIterator<Item = ConstOp>) -> Self {
1965        let ops = ops.into_iter().collect::<SmallVec<[ConstOp; 2]>>();
1966        assert!(!ops.is_empty());
1967        ConstExpr { ops }
1968    }
1969
1970    /// Create a new const expression from a `wasmparser` const expression.
1971    ///
1972    /// Returns the new const expression as well as the escaping function
1973    /// indices that appeared in `ref.func` instructions, if any.
1974    pub fn from_wasmparser(
1975        env: &dyn TypeConvert,
1976        expr: wasmparser::ConstExpr<'_>,
1977    ) -> WasmResult<(Self, SmallVec<[FuncIndex; 1]>)> {
1978        let mut iter = expr
1979            .get_operators_reader()
1980            .into_iter_with_offsets()
1981            .peekable();
1982
1983        let mut ops = SmallVec::<[ConstOp; 2]>::new();
1984        let mut escaped = SmallVec::<[FuncIndex; 1]>::new();
1985        while let Some(res) = iter.next() {
1986            let (op, offset) = res?;
1987
1988            // If we reach an `end` instruction, and there are no more
1989            // instructions after that, then we are done reading this const
1990            // expression.
1991            if matches!(op, wasmparser::Operator::End) && iter.peek().is_none() {
1992                break;
1993            }
1994
1995            // Track any functions that appear in `ref.func` so that callers can
1996            // make sure to flag them as escaping.
1997            if let wasmparser::Operator::RefFunc { function_index } = &op {
1998                escaped.push(FuncIndex::from_u32(*function_index));
1999            }
2000
2001            ops.push(ConstOp::from_wasmparser(env, op, offset)?);
2002        }
2003        Ok((Self { ops }, escaped))
2004    }
2005
2006    /// Get the opcodes that make up this const expression.
2007    #[inline]
2008    pub fn ops(&self) -> &[ConstOp] {
2009        &self.ops
2010    }
2011
2012    /// Is this ConstExpr a provably nonzero integer value?
2013    ///
2014    /// This must be conservative: if the expression *might* be zero,
2015    /// it must return `false`. It is always allowed to return `false`
2016    /// for some expression kind that we don't support. However, if it
2017    /// returns `true`, the expression must be actually nonzero.
2018    ///
2019    /// We use this for certain table optimizations that rely on
2020    /// knowing for sure that index 0 is not referenced.
2021    pub fn provably_nonzero_i32(&self) -> bool {
2022        match self.const_eval() {
2023            Some(GlobalConstValue::I32(x)) => x != 0,
2024
2025            // Conservatively return `false` for non-const-eval-able expressions
2026            // as well as everything else.
2027            _ => false,
2028        }
2029    }
2030
2031    /// Attempt to evaluate the given const-expr at compile time.
2032    pub fn const_eval(&self) -> Option<GlobalConstValue> {
2033        // TODO: Actually maintain an evaluation stack and handle `i32.add`,
2034        // `i32.sub`, etc... const ops.
2035        match self.ops() {
2036            [ConstOp::I32Const(x)] => Some(GlobalConstValue::I32(*x)),
2037            [ConstOp::I64Const(x)] => Some(GlobalConstValue::I64(*x)),
2038            [ConstOp::F32Const(x)] => Some(GlobalConstValue::F32(*x)),
2039            [ConstOp::F64Const(x)] => Some(GlobalConstValue::F64(*x)),
2040            [ConstOp::V128Const(x)] => Some(GlobalConstValue::V128(*x)),
2041            _ => None,
2042        }
2043    }
2044}
2045
2046/// A global's constant value, known at compile time.
2047#[expect(missing_docs, reason = "self-describing variants")]
2048#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
2049pub enum GlobalConstValue {
2050    I32(i32),
2051    I64(i64),
2052    F32(u32),
2053    F64(u64),
2054    V128(u128),
2055}
2056
2057/// The subset of Wasm opcodes that are constant.
2058#[expect(missing_docs, reason = "self-describing variants")]
2059#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
2060pub enum ConstOp {
2061    I32Const(i32),
2062    I64Const(i64),
2063    F32Const(u32),
2064    F64Const(u64),
2065    V128Const(u128),
2066    GlobalGet(GlobalIndex),
2067    RefI31,
2068    RefNull(WasmHeapType),
2069    RefFunc(FuncIndex),
2070    I32Add,
2071    I32Sub,
2072    I32Mul,
2073    I64Add,
2074    I64Sub,
2075    I64Mul,
2076    StructNew {
2077        struct_type_index: TypeIndex,
2078    },
2079    StructNewDefault {
2080        struct_type_index: TypeIndex,
2081    },
2082    ArrayNew {
2083        array_type_index: TypeIndex,
2084    },
2085    ArrayNewDefault {
2086        array_type_index: TypeIndex,
2087    },
2088    ArrayNewFixed {
2089        array_type_index: TypeIndex,
2090        array_size: u32,
2091    },
2092    ExternConvertAny,
2093    AnyConvertExtern,
2094}
2095
2096impl ConstOp {
2097    /// Convert a `wasmparser::Operator` to a `ConstOp`.
2098    pub fn from_wasmparser(
2099        env: &dyn TypeConvert,
2100        op: wasmparser::Operator<'_>,
2101        offset: usize,
2102    ) -> WasmResult<Self> {
2103        use wasmparser::Operator as O;
2104        Ok(match op {
2105            O::I32Const { value } => Self::I32Const(value),
2106            O::I64Const { value } => Self::I64Const(value),
2107            O::F32Const { value } => Self::F32Const(value.bits()),
2108            O::F64Const { value } => Self::F64Const(value.bits()),
2109            O::V128Const { value } => Self::V128Const(u128::from_le_bytes(*value.bytes())),
2110            O::RefNull { hty } => Self::RefNull(env.convert_heap_type(hty)?),
2111            O::RefFunc { function_index } => Self::RefFunc(FuncIndex::from_u32(function_index)),
2112            O::GlobalGet { global_index } => Self::GlobalGet(GlobalIndex::from_u32(global_index)),
2113            O::RefI31 => Self::RefI31,
2114            O::I32Add => Self::I32Add,
2115            O::I32Sub => Self::I32Sub,
2116            O::I32Mul => Self::I32Mul,
2117            O::I64Add => Self::I64Add,
2118            O::I64Sub => Self::I64Sub,
2119            O::I64Mul => Self::I64Mul,
2120            O::StructNew { struct_type_index } => Self::StructNew {
2121                struct_type_index: TypeIndex::from_u32(struct_type_index),
2122            },
2123            O::StructNewDefault { struct_type_index } => Self::StructNewDefault {
2124                struct_type_index: TypeIndex::from_u32(struct_type_index),
2125            },
2126            O::ArrayNew { array_type_index } => Self::ArrayNew {
2127                array_type_index: TypeIndex::from_u32(array_type_index),
2128            },
2129            O::ArrayNewDefault { array_type_index } => Self::ArrayNewDefault {
2130                array_type_index: TypeIndex::from_u32(array_type_index),
2131            },
2132            O::ArrayNewFixed {
2133                array_type_index,
2134                array_size,
2135            } => Self::ArrayNewFixed {
2136                array_type_index: TypeIndex::from_u32(array_type_index),
2137                array_size,
2138            },
2139            O::ExternConvertAny => Self::ExternConvertAny,
2140            O::AnyConvertExtern => Self::AnyConvertExtern,
2141            op => {
2142                return Err(wasm_unsupported!(
2143                    "unsupported opcode in const expression at offset {offset:#x}: {op:?}",
2144                ));
2145            }
2146        })
2147    }
2148}
2149
2150/// The type that can be used to index into [Memory] and [Table].
2151#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2152#[expect(missing_docs, reason = "self-describing variants")]
2153pub enum IndexType {
2154    I32,
2155    I64,
2156}
2157
2158/// The size range of resizeable storage associated with [Memory] types and [Table] types.
2159#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2160#[expect(missing_docs, reason = "self-describing fields")]
2161pub struct Limits {
2162    pub min: u64,
2163    pub max: Option<u64>,
2164}
2165
2166/// WebAssembly table.
2167#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2168pub struct Table {
2169    /// The type of the index used to access the table.
2170    pub idx_type: IndexType,
2171    /// Tables are constrained by limits for their minimum and optionally maximum size.
2172    /// The limits are given in numbers of entries.
2173    pub limits: Limits,
2174    /// The table elements' Wasm type.
2175    pub ref_type: WasmRefType,
2176}
2177
2178impl TypeTrace for Table {
2179    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
2180    where
2181        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
2182    {
2183        let Table {
2184            ref_type: wasm_ty,
2185            idx_type: _,
2186            limits: _,
2187        } = self;
2188        wasm_ty.trace(func)
2189    }
2190
2191    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
2192    where
2193        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
2194    {
2195        let Table {
2196            ref_type: wasm_ty,
2197            idx_type: _,
2198            limits: _,
2199        } = self;
2200        wasm_ty.trace_mut(func)
2201    }
2202}
2203
2204/// WebAssembly linear memory.
2205#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2206pub struct Memory {
2207    /// The type of the index used to access the memory.
2208    pub idx_type: IndexType,
2209    /// The limits constrain the minimum and optionally the maximum size of a memory.
2210    /// The limits are given in units of page size.
2211    pub limits: Limits,
2212    /// Whether the memory may be shared between multiple threads.
2213    pub shared: bool,
2214    /// The log2 of this memory's page size, in bytes.
2215    ///
2216    /// By default the page size is 64KiB (0x10000; 2**16; 1<<16; 65536) but the
2217    /// custom-page-sizes proposal allows opting into a page size of `1`.
2218    pub page_size_log2: u8,
2219}
2220
2221/// Maximum size, in bytes, of 32-bit memories (4G)
2222pub const WASM32_MAX_SIZE: u64 = 1 << 32;
2223
2224impl Memory {
2225    /// WebAssembly page sizes are 64KiB by default.
2226    pub const DEFAULT_PAGE_SIZE: u32 = 0x10000;
2227
2228    /// WebAssembly page sizes are 64KiB (or `2**16`) by default.
2229    pub const DEFAULT_PAGE_SIZE_LOG2: u8 = {
2230        let log2 = 16;
2231        assert!(1 << log2 == Memory::DEFAULT_PAGE_SIZE);
2232        log2
2233    };
2234
2235    /// Returns the minimum size, in bytes, that this memory must be.
2236    ///
2237    /// # Errors
2238    ///
2239    /// Returns an error if the calculation of the minimum size overflows the
2240    /// `u64` return type. This means that the memory can't be allocated but
2241    /// it's deferred to the caller to how to deal with that.
2242    pub fn minimum_byte_size(&self) -> Result<u64, SizeOverflow> {
2243        self.limits
2244            .min
2245            .checked_mul(self.page_size())
2246            .ok_or(SizeOverflow)
2247    }
2248
2249    /// Returns the maximum size, in bytes, that this memory is allowed to be.
2250    ///
2251    /// Note that the return value here is not an `Option` despite the maximum
2252    /// size of a linear memory being optional in wasm. If a maximum size
2253    /// is not present in the memory's type then a maximum size is selected for
2254    /// it. For example the maximum size of a 32-bit memory is `1<<32`. The
2255    /// maximum size of a 64-bit linear memory is chosen to be a value that
2256    /// won't ever be allowed at runtime.
2257    ///
2258    /// # Errors
2259    ///
2260    /// Returns an error if the calculation of the maximum size overflows the
2261    /// `u64` return type. This means that the memory can't be allocated but
2262    /// it's deferred to the caller to how to deal with that.
2263    pub fn maximum_byte_size(&self) -> Result<u64, SizeOverflow> {
2264        match self.limits.max {
2265            Some(max) => max.checked_mul(self.page_size()).ok_or(SizeOverflow),
2266            None => {
2267                let min = self.minimum_byte_size()?;
2268                Ok(min.max(self.max_size_based_on_index_type()))
2269            }
2270        }
2271    }
2272
2273    /// Get the size of this memory's pages, in bytes.
2274    pub fn page_size(&self) -> u64 {
2275        debug_assert!(
2276            self.page_size_log2 == 16 || self.page_size_log2 == 0,
2277            "invalid page_size_log2: {}; must be 16 or 0",
2278            self.page_size_log2
2279        );
2280        1 << self.page_size_log2
2281    }
2282
2283    /// Returns the maximum size memory is allowed to be only based on the
2284    /// index type used by this memory.
2285    ///
2286    /// For example 32-bit linear memories return `1<<32` from this method.
2287    pub fn max_size_based_on_index_type(&self) -> u64 {
2288        match self.idx_type {
2289            IndexType::I64 =>
2290            // Note that the true maximum size of a 64-bit linear memory, in
2291            // bytes, cannot be represented in a `u64`. That would require a u65
2292            // to store `1<<64`. Despite that no system can actually allocate a
2293            // full 64-bit linear memory so this is instead emulated as "what if
2294            // the kernel fit in a single Wasm page of linear memory". Shouldn't
2295            // ever actually be possible but it provides a number to serve as an
2296            // effective maximum.
2297            {
2298                0_u64.wrapping_sub(self.page_size())
2299            }
2300            IndexType::I32 => WASM32_MAX_SIZE,
2301        }
2302    }
2303
2304    /// Returns whether this memory can be implemented with virtual memory on
2305    /// a host with `host_page_size_log2`.
2306    ///
2307    /// When this function returns `true` then it means that signals such as
2308    /// SIGSEGV on the host are compatible with wasm and can be used to
2309    /// represent out-of-bounds memory accesses.
2310    ///
2311    /// When this function returns `false` then it means that this memory must,
2312    /// for example, have explicit bounds checks. This additionally means that
2313    /// virtual memory traps (e.g. SIGSEGV) cannot be relied on to implement
2314    /// linear memory semantics.
2315    pub fn can_use_virtual_memory(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool {
2316        tunables.signals_based_traps && self.page_size_log2 >= host_page_size_log2
2317    }
2318
2319    /// Returns whether this memory is a candidate for bounds check elision
2320    /// given the configuration and host page size.
2321    ///
2322    /// This function determines whether the given compilation configuration
2323    /// enables possible bounds check elision for this memory. Bounds checks
2324    /// can only be elided if [`Memory::can_use_virtual_memory`] returns `true`
2325    /// for example but there are additionally requirements on the index size of
2326    /// this memory and the memory reservation in the tunables.
2327    ///
2328    /// Currently the only case that supports bounds check elision is when all
2329    /// of these apply:
2330    ///
2331    /// * When [`Memory::can_use_virtual_memory`] returns `true`.
2332    /// * This is a 32-bit linear memory (e.g. not 64-bit)
2333    /// * The reservation + guard size is in excess of 4GiB
2334    ///
2335    /// In this situation all computable addresses fall within the reserved
2336    /// space (modulo static offsets factoring in guard pages) so bounds checks
2337    /// may be elidable.
2338    pub fn can_elide_bounds_check(
2339        &self,
2340        memory_tunables: &MemoryTunables<'_>,
2341        host_page_size_log2: u8,
2342    ) -> bool {
2343        self.can_use_virtual_memory(memory_tunables.tunables(), host_page_size_log2)
2344            && self.idx_type == IndexType::I32
2345            && memory_tunables.reservation() + memory_tunables.guard_size() >= (1 << 32)
2346    }
2347
2348    /// Returns the static size of this heap in bytes at runtime, if available.
2349    ///
2350    /// This is only computable when the minimum size equals the maximum size.
2351    pub fn static_heap_size(&self) -> Option<u64> {
2352        let min = self.minimum_byte_size().ok()?;
2353        let max = self.maximum_byte_size().ok()?;
2354        if min == max { Some(min) } else { None }
2355    }
2356
2357    /// Returns whether or not the base pointer of this memory is allowed to be
2358    /// relocated at runtime.
2359    ///
2360    /// When this function returns `false` then it means that after the initial
2361    /// allocation the base pointer is constant for the entire lifetime of a
2362    /// memory. This can enable compiler optimizations, for example.
2363    pub fn memory_may_move(&self, memory_tunables: &MemoryTunables<'_>) -> bool {
2364        // Shared memories cannot ever relocate their base pointer so the
2365        // settings configured in the engine must be appropriate for them ahead
2366        // of time.
2367        if self.shared {
2368            return false;
2369        }
2370
2371        // If movement is disallowed in engine configuration, then the answer is
2372        // "no".
2373        if !memory_tunables.may_move() {
2374            return false;
2375        }
2376
2377        // If its minimum and maximum are the same, then the memory will never
2378        // be resized, and therefore will never move.
2379        if self.limits.max.is_some_and(|max| self.limits.min == max) {
2380            return false;
2381        }
2382
2383        // If the maximum size of this memory is above the threshold of the
2384        // initial memory reservation then the memory may move.
2385        let max = self.maximum_byte_size().unwrap_or(u64::MAX);
2386        max > memory_tunables.reservation()
2387    }
2388
2389    /// Tests whether this memory type is allowed to grow up to `size` bytes.
2390    ///
2391    /// This is only applicable to custom-page-size memories which have a page
2392    /// size of a single byte. In that situation growth beyond `-1i32 as u32`
2393    /// bytes is not allowed because at that point memory growth succeeding and
2394    /// failing would be indistinguishable in the return value of `memory.grow`,
2395    /// for example. To handle this 32-bit memories are only allowed to grow to
2396    /// `-2i32 as u32`, for example, and 64-bit memories with a page size of 1
2397    /// are allowed to grow up to the maximum size.
2398    pub fn allow_growth_to(&self, size: usize) -> bool {
2399        if self.page_size_log2 != 0 {
2400            return true;
2401        }
2402        match self.idx_type {
2403            // For a 32-bit memory using 1-byte pages the last 2 bytes of the
2404            // 32-bit address space are addressable but disallowed for now.  A
2405            // memory that is 4GiB in size cannot report its size via
2406            // `memory.size`, and a memory that is 4GiB-1 bytes in size cannot
2407            // be distinguished when 1 byte is added from an allocation
2408            // failure.  To handle this the memory is capped at 4GiB-2 which
2409            // means that all memory-related instructions and such will have
2410            // unambiguous return codes.
2411            IndexType::I32 => size < 0xffff_ffff,
2412
2413            // Assume that for a 64-bit memory using 1-byte pages it's going to
2414            // exhaust system resources before a limit is actually reached.
2415            IndexType::I64 => true,
2416        }
2417    }
2418}
2419
2420#[derive(Copy, Clone, Debug)]
2421#[expect(missing_docs, reason = "self-describing error struct")]
2422pub struct SizeOverflow;
2423
2424impl fmt::Display for SizeOverflow {
2425    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2426        f.write_str("size overflow calculating memory size")
2427    }
2428}
2429
2430impl core::error::Error for SizeOverflow {}
2431
2432impl From<wasmparser::MemoryType> for Memory {
2433    fn from(ty: wasmparser::MemoryType) -> Memory {
2434        let idx_type = match ty.memory64 {
2435            false => IndexType::I32,
2436            true => IndexType::I64,
2437        };
2438        let limits = Limits {
2439            min: ty.initial,
2440            max: ty.maximum,
2441        };
2442        let page_size_log2 = u8::try_from(ty.page_size_log2.unwrap_or(16)).unwrap();
2443        debug_assert!(
2444            page_size_log2 == 16 || page_size_log2 == 0,
2445            "invalid page_size_log2: {page_size_log2}; must be 16 or 0"
2446        );
2447        Memory {
2448            idx_type,
2449            limits,
2450            shared: ty.shared,
2451            page_size_log2,
2452        }
2453    }
2454}
2455
2456/// WebAssembly exception and control tag.
2457#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2458pub struct Tag {
2459    /// The tag signature type.
2460    pub signature: EngineOrModuleTypeIndex,
2461    /// The corresponding exception type.
2462    pub exception: EngineOrModuleTypeIndex,
2463}
2464
2465impl TypeTrace for Tag {
2466    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
2467    where
2468        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
2469    {
2470        func(self.signature)?;
2471        func(self.exception)?;
2472        Ok(())
2473    }
2474
2475    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
2476    where
2477        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
2478    {
2479        func(&mut self.signature)?;
2480        func(&mut self.exception)?;
2481        Ok(())
2482    }
2483}
2484
2485/// Helpers used to convert a `wasmparser` type to a type in this crate.
2486#[expect(missing_docs, reason = "self-describing functions")]
2487pub trait TypeConvert {
2488    /// Converts a wasmparser table type into a wasmtime type
2489    fn convert_global_type(&self, ty: &wasmparser::GlobalType) -> WasmResult<Global> {
2490        Ok(Global {
2491            wasm_ty: self.convert_valtype(ty.content_type)?,
2492            mutability: ty.mutable,
2493        })
2494    }
2495
2496    /// Converts a wasmparser table type into a wasmtime type
2497    fn convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table> {
2498        let idx_type = match ty.table64 {
2499            false => IndexType::I32,
2500            true => IndexType::I64,
2501        };
2502        let limits = Limits {
2503            min: ty.initial,
2504            max: ty.maximum,
2505        };
2506        Ok(Table {
2507            idx_type,
2508            limits,
2509            ref_type: self.convert_ref_type(ty.element_type)?,
2510        })
2511    }
2512
2513    fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> {
2514        Ok(WasmSubType {
2515            is_final: ty.is_final,
2516            supertype: ty.supertype_idx.map(|i| self.lookup_type_index(i.unpack())),
2517            composite_type: self.convert_composite_type(&ty.composite_type)?,
2518        })
2519    }
2520
2521    fn convert_composite_type(
2522        &self,
2523        ty: &wasmparser::CompositeType,
2524    ) -> WasmResult<WasmCompositeType> {
2525        let inner = match &ty.inner {
2526            wasmparser::CompositeInnerType::Func(f) => {
2527                WasmCompositeInnerType::Func(self.convert_func_type(f)?)
2528            }
2529            wasmparser::CompositeInnerType::Array(a) => {
2530                WasmCompositeInnerType::Array(self.convert_array_type(a)?)
2531            }
2532            wasmparser::CompositeInnerType::Struct(s) => {
2533                WasmCompositeInnerType::Struct(self.convert_struct_type(s)?)
2534            }
2535            wasmparser::CompositeInnerType::Cont(c) => {
2536                WasmCompositeInnerType::Cont(self.convert_cont_type(c))
2537            }
2538        };
2539        Ok(WasmCompositeType {
2540            inner,
2541            shared: ty.shared,
2542        })
2543    }
2544
2545    /// Converts a wasmparser continuation type to a wasmtime type
2546    fn convert_cont_type(&self, ty: &wasmparser::ContType) -> WasmContType {
2547        if let WasmHeapType::ConcreteFunc(sigidx) = self.lookup_heap_type(ty.0.unpack()) {
2548            WasmContType::new(sigidx)
2549        } else {
2550            panic!("Failed to extract signature index for continuation type.")
2551        }
2552    }
2553
2554    fn convert_struct_type(&self, ty: &wasmparser::StructType) -> WasmResult<WasmStructType> {
2555        Ok(WasmStructType {
2556            fields: ty
2557                .fields
2558                .iter()
2559                .map(|f| self.convert_field_type(f))
2560                .collect::<WasmResult<_>>()?,
2561        })
2562    }
2563
2564    fn convert_array_type(&self, ty: &wasmparser::ArrayType) -> WasmResult<WasmArrayType> {
2565        Ok(WasmArrayType(self.convert_field_type(&ty.0)?))
2566    }
2567
2568    fn convert_field_type(&self, ty: &wasmparser::FieldType) -> WasmResult<WasmFieldType> {
2569        Ok(WasmFieldType {
2570            element_type: self.convert_storage_type(&ty.element_type)?,
2571            mutable: ty.mutable,
2572        })
2573    }
2574
2575    fn convert_storage_type(&self, ty: &wasmparser::StorageType) -> WasmResult<WasmStorageType> {
2576        Ok(match ty {
2577            wasmparser::StorageType::I8 => WasmStorageType::I8,
2578            wasmparser::StorageType::I16 => WasmStorageType::I16,
2579            wasmparser::StorageType::Val(v) => WasmStorageType::Val(self.convert_valtype(*v)?),
2580        })
2581    }
2582
2583    /// Converts a wasmparser function type to a wasmtime type
2584    fn convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmResult<WasmFuncType> {
2585        let params = ty
2586            .params()
2587            .iter()
2588            .map(|t| self.convert_valtype(*t))
2589            .collect::<WasmResult<Vec<_>>>()?;
2590        let results = ty
2591            .results()
2592            .iter()
2593            .map(|t| self.convert_valtype(*t))
2594            .collect::<WasmResult<Vec<_>>>()?;
2595        Ok(WasmFuncType::new(params, results).panic_on_oom())
2596    }
2597
2598    /// Converts a wasmparser value type to a wasmtime type
2599    fn convert_valtype(&self, ty: wasmparser::ValType) -> WasmResult<WasmValType> {
2600        Ok(match ty {
2601            wasmparser::ValType::I32 => WasmValType::I32,
2602            wasmparser::ValType::I64 => WasmValType::I64,
2603            wasmparser::ValType::F32 => WasmValType::F32,
2604            wasmparser::ValType::F64 => WasmValType::F64,
2605            wasmparser::ValType::V128 => WasmValType::V128,
2606            wasmparser::ValType::Ref(t) => WasmValType::Ref(self.convert_ref_type(t)?),
2607        })
2608    }
2609
2610    /// Converts a wasmparser reference type to a wasmtime type
2611    fn convert_ref_type(&self, ty: wasmparser::RefType) -> WasmResult<WasmRefType> {
2612        Ok(WasmRefType {
2613            nullable: ty.is_nullable(),
2614            heap_type: self.convert_heap_type(ty.heap_type())?,
2615        })
2616    }
2617
2618    /// Converts a wasmparser heap type to a wasmtime type
2619    fn convert_heap_type(&self, ty: wasmparser::HeapType) -> WasmResult<WasmHeapType> {
2620        Ok(match ty {
2621            wasmparser::HeapType::Concrete(i) => self.lookup_heap_type(i),
2622            wasmparser::HeapType::Abstract { ty, shared: false } => match ty {
2623                wasmparser::AbstractHeapType::Extern => WasmHeapType::Extern,
2624                wasmparser::AbstractHeapType::NoExtern => WasmHeapType::NoExtern,
2625                wasmparser::AbstractHeapType::Func => WasmHeapType::Func,
2626                wasmparser::AbstractHeapType::NoFunc => WasmHeapType::NoFunc,
2627                wasmparser::AbstractHeapType::Any => WasmHeapType::Any,
2628                wasmparser::AbstractHeapType::Eq => WasmHeapType::Eq,
2629                wasmparser::AbstractHeapType::I31 => WasmHeapType::I31,
2630                wasmparser::AbstractHeapType::Array => WasmHeapType::Array,
2631                wasmparser::AbstractHeapType::Struct => WasmHeapType::Struct,
2632                wasmparser::AbstractHeapType::None => WasmHeapType::None,
2633                wasmparser::AbstractHeapType::Cont => WasmHeapType::Cont,
2634                wasmparser::AbstractHeapType::NoCont => WasmHeapType::NoCont,
2635                wasmparser::AbstractHeapType::Exn => WasmHeapType::Exn,
2636                wasmparser::AbstractHeapType::NoExn => WasmHeapType::NoExn,
2637            },
2638            _ => return Err(wasm_unsupported!("unsupported heap type {ty:?}")),
2639        })
2640    }
2641
2642    /// Converts the specified type index from a heap type into a canonicalized
2643    /// heap type.
2644    fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType;
2645
2646    /// Converts the specified type index from a heap type into a canonicalized
2647    /// heap type.
2648    fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex;
2649}
2650
2651#[cfg(test)]
2652mod tests {
2653    use super::*;
2654
2655    #[test]
2656    fn wasm_func_type_new() -> Result<()> {
2657        let i32 = WasmValType::I32;
2658        let anyref = WasmValType::Ref(WasmRefType {
2659            nullable: true,
2660            heap_type: WasmHeapType::Any,
2661        });
2662        let ty = WasmFuncType::new([i32, i32, anyref, anyref], [i32, anyref])?;
2663        assert_eq!(ty.params(), &[i32, i32, anyref, anyref]);
2664        assert_eq!(ty.non_i31_gc_ref_params_count(), 2);
2665        assert_eq!(ty.results(), &[i32, anyref]);
2666        assert_eq!(ty.non_i31_gc_ref_results_count(), 1);
2667        Ok(())
2668    }
2669}