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: u64,
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    /// Convert a `ConstOp` back to a `wasmparser::Operator`.
2150    ///
2151    /// `RefNull`'s heap type does not round-trip, so only use this where the
2152    /// immediates do not matter, such as looking up an operator's fuel cost.
2153    pub fn to_operator(&self) -> wasmparser::Operator<'static> {
2154        use wasmparser::{AbstractHeapType, HeapType, Ieee32, Ieee64, Operator, V128};
2155        match self {
2156            ConstOp::I32Const(value) => Operator::I32Const { value: *value },
2157            ConstOp::I64Const(value) => Operator::I64Const { value: *value },
2158            ConstOp::F32Const(bits) => Operator::F32Const {
2159                value: Ieee32::from(f32::from_bits(*bits)),
2160            },
2161            ConstOp::F64Const(bits) => Operator::F64Const {
2162                value: Ieee64::from(f64::from_bits(*bits)),
2163            },
2164            ConstOp::V128Const(value) => Operator::V128Const {
2165                value: V128::from(*value as i128),
2166            },
2167            ConstOp::GlobalGet(index) => Operator::GlobalGet {
2168                global_index: index.as_u32(),
2169            },
2170            ConstOp::RefI31 => Operator::RefI31,
2171            ConstOp::RefNull(_) => Operator::RefNull {
2172                hty: HeapType::Abstract {
2173                    shared: false,
2174                    ty: AbstractHeapType::Any,
2175                },
2176            },
2177            ConstOp::RefFunc(index) => Operator::RefFunc {
2178                function_index: index.as_u32(),
2179            },
2180            ConstOp::I32Add => Operator::I32Add,
2181            ConstOp::I32Sub => Operator::I32Sub,
2182            ConstOp::I32Mul => Operator::I32Mul,
2183            ConstOp::I64Add => Operator::I64Add,
2184            ConstOp::I64Sub => Operator::I64Sub,
2185            ConstOp::I64Mul => Operator::I64Mul,
2186            ConstOp::StructNew { struct_type_index } => Operator::StructNew {
2187                struct_type_index: struct_type_index.as_u32(),
2188            },
2189            ConstOp::StructNewDefault { struct_type_index } => Operator::StructNewDefault {
2190                struct_type_index: struct_type_index.as_u32(),
2191            },
2192            ConstOp::ArrayNew { array_type_index } => Operator::ArrayNew {
2193                array_type_index: array_type_index.as_u32(),
2194            },
2195            ConstOp::ArrayNewDefault { array_type_index } => Operator::ArrayNewDefault {
2196                array_type_index: array_type_index.as_u32(),
2197            },
2198            ConstOp::ArrayNewFixed {
2199                array_type_index,
2200                array_size,
2201            } => Operator::ArrayNewFixed {
2202                array_type_index: array_type_index.as_u32(),
2203                array_size: *array_size,
2204            },
2205            ConstOp::ExternConvertAny => Operator::ExternConvertAny,
2206            ConstOp::AnyConvertExtern => Operator::AnyConvertExtern,
2207        }
2208    }
2209}
2210
2211/// The type that can be used to index into [Memory] and [Table].
2212#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2213#[expect(missing_docs, reason = "self-describing variants")]
2214pub enum IndexType {
2215    I32,
2216    I64,
2217}
2218
2219/// The size range of resizeable storage associated with [Memory] types and [Table] types.
2220#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2221#[expect(missing_docs, reason = "self-describing fields")]
2222pub struct Limits {
2223    pub min: u64,
2224    pub max: Option<u64>,
2225}
2226
2227/// WebAssembly table.
2228#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2229pub struct Table {
2230    /// The type of the index used to access the table.
2231    pub idx_type: IndexType,
2232    /// Tables are constrained by limits for their minimum and optionally maximum size.
2233    /// The limits are given in numbers of entries.
2234    pub limits: Limits,
2235    /// The table elements' Wasm type.
2236    pub ref_type: WasmRefType,
2237}
2238
2239impl TypeTrace for Table {
2240    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
2241    where
2242        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
2243    {
2244        let Table {
2245            ref_type: wasm_ty,
2246            idx_type: _,
2247            limits: _,
2248        } = self;
2249        wasm_ty.trace(func)
2250    }
2251
2252    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
2253    where
2254        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
2255    {
2256        let Table {
2257            ref_type: wasm_ty,
2258            idx_type: _,
2259            limits: _,
2260        } = self;
2261        wasm_ty.trace_mut(func)
2262    }
2263}
2264
2265/// WebAssembly linear memory.
2266#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2267pub struct Memory {
2268    /// The type of the index used to access the memory.
2269    pub idx_type: IndexType,
2270    /// The limits constrain the minimum and optionally the maximum size of a memory.
2271    /// The limits are given in units of page size.
2272    pub limits: Limits,
2273    /// Whether the memory may be shared between multiple threads.
2274    pub shared: bool,
2275    /// The log2 of this memory's page size, in bytes.
2276    ///
2277    /// By default the page size is 64KiB (0x10000; 2**16; 1<<16; 65536) but the
2278    /// custom-page-sizes proposal allows opting into a page size of `1`.
2279    pub page_size_log2: u8,
2280}
2281
2282/// Maximum size, in bytes, of 32-bit memories (4G)
2283pub const WASM32_MAX_SIZE: u64 = 1 << 32;
2284
2285impl Memory {
2286    /// WebAssembly page sizes are 64KiB by default.
2287    pub const DEFAULT_PAGE_SIZE: u32 = 0x10000;
2288
2289    /// WebAssembly page sizes are 64KiB (or `2**16`) by default.
2290    pub const DEFAULT_PAGE_SIZE_LOG2: u8 = {
2291        let log2 = 16;
2292        assert!(1 << log2 == Memory::DEFAULT_PAGE_SIZE);
2293        log2
2294    };
2295
2296    /// Returns the minimum size, in bytes, that this memory must be.
2297    ///
2298    /// # Errors
2299    ///
2300    /// Returns an error if the calculation of the minimum size overflows the
2301    /// `u64` return type. This means that the memory can't be allocated but
2302    /// it's deferred to the caller to how to deal with that.
2303    pub fn minimum_byte_size(&self) -> Result<u64, SizeOverflow> {
2304        self.limits
2305            .min
2306            .checked_mul(self.page_size())
2307            .ok_or(SizeOverflow)
2308    }
2309
2310    /// Returns the maximum size, in bytes, that this memory is allowed to be.
2311    ///
2312    /// Note that the return value here is not an `Option` despite the maximum
2313    /// size of a linear memory being optional in wasm. If a maximum size
2314    /// is not present in the memory's type then a maximum size is selected for
2315    /// it. For example the maximum size of a 32-bit memory is `1<<32`. The
2316    /// maximum size of a 64-bit linear memory is chosen to be a value that
2317    /// won't ever be allowed at runtime.
2318    ///
2319    /// # Errors
2320    ///
2321    /// Returns an error if the calculation of the maximum size overflows the
2322    /// `u64` return type. This means that the memory can't be allocated but
2323    /// it's deferred to the caller to how to deal with that.
2324    pub fn maximum_byte_size(&self) -> Result<u64, SizeOverflow> {
2325        match self.limits.max {
2326            Some(max) => max.checked_mul(self.page_size()).ok_or(SizeOverflow),
2327            None => {
2328                let min = self.minimum_byte_size()?;
2329                Ok(min.max(self.max_size_based_on_index_type()))
2330            }
2331        }
2332    }
2333
2334    /// Get the size of this memory's pages, in bytes.
2335    pub fn page_size(&self) -> u64 {
2336        debug_assert!(
2337            self.page_size_log2 == 16 || self.page_size_log2 == 0,
2338            "invalid page_size_log2: {}; must be 16 or 0",
2339            self.page_size_log2
2340        );
2341        1 << self.page_size_log2
2342    }
2343
2344    /// Returns the maximum size memory is allowed to be only based on the
2345    /// index type used by this memory.
2346    ///
2347    /// For example 32-bit linear memories return `1<<32` from this method.
2348    pub fn max_size_based_on_index_type(&self) -> u64 {
2349        match self.idx_type {
2350            IndexType::I64 =>
2351            // Note that the true maximum size of a 64-bit linear memory, in
2352            // bytes, cannot be represented in a `u64`. That would require a u65
2353            // to store `1<<64`. Despite that no system can actually allocate a
2354            // full 64-bit linear memory so this is instead emulated as "what if
2355            // the kernel fit in a single Wasm page of linear memory". Shouldn't
2356            // ever actually be possible but it provides a number to serve as an
2357            // effective maximum.
2358            {
2359                0_u64.wrapping_sub(self.page_size())
2360            }
2361            IndexType::I32 => WASM32_MAX_SIZE,
2362        }
2363    }
2364
2365    /// Returns whether this memory can be implemented with virtual memory on
2366    /// a host with `host_page_size_log2`.
2367    ///
2368    /// When this function returns `true` then it means that signals such as
2369    /// SIGSEGV on the host are compatible with wasm and can be used to
2370    /// represent out-of-bounds memory accesses.
2371    ///
2372    /// When this function returns `false` then it means that this memory must,
2373    /// for example, have explicit bounds checks. This additionally means that
2374    /// virtual memory traps (e.g. SIGSEGV) cannot be relied on to implement
2375    /// linear memory semantics.
2376    pub fn can_use_virtual_memory(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool {
2377        tunables.signals_based_traps && self.page_size_log2 >= host_page_size_log2
2378    }
2379
2380    /// Returns whether this memory is a candidate for bounds check elision
2381    /// given the configuration and host page size.
2382    ///
2383    /// This function determines whether the given compilation configuration
2384    /// enables possible bounds check elision for this memory. Bounds checks
2385    /// can only be elided if [`Memory::can_use_virtual_memory`] returns `true`
2386    /// for example but there are additionally requirements on the index size of
2387    /// this memory and the memory reservation in the tunables.
2388    ///
2389    /// Currently the only case that supports bounds check elision is when all
2390    /// of these apply:
2391    ///
2392    /// * When [`Memory::can_use_virtual_memory`] returns `true`.
2393    /// * This is a 32-bit linear memory (e.g. not 64-bit)
2394    /// * The reservation + guard size is in excess of 4GiB
2395    ///
2396    /// In this situation all computable addresses fall within the reserved
2397    /// space (modulo static offsets factoring in guard pages) so bounds checks
2398    /// may be elidable.
2399    pub fn can_elide_bounds_check(
2400        &self,
2401        memory_tunables: &MemoryTunables<'_>,
2402        host_page_size_log2: u8,
2403    ) -> bool {
2404        self.can_use_virtual_memory(memory_tunables.tunables(), host_page_size_log2)
2405            && self.idx_type == IndexType::I32
2406            && memory_tunables.reservation() + memory_tunables.guard_size() >= (1 << 32)
2407    }
2408
2409    /// Returns the static size of this heap in bytes at runtime, if available.
2410    ///
2411    /// This is only computable when the minimum size equals the maximum size.
2412    pub fn static_heap_size(&self) -> Option<u64> {
2413        let min = self.minimum_byte_size().ok()?;
2414        let max = self.maximum_byte_size().ok()?;
2415        if min == max { Some(min) } else { None }
2416    }
2417
2418    /// Returns whether or not the base pointer of this memory is allowed to be
2419    /// relocated at runtime.
2420    ///
2421    /// When this function returns `false` then it means that after the initial
2422    /// allocation the base pointer is constant for the entire lifetime of a
2423    /// memory. This can enable compiler optimizations, for example.
2424    pub fn memory_may_move(&self, memory_tunables: &MemoryTunables<'_>) -> bool {
2425        // Shared memories cannot ever relocate their base pointer so the
2426        // settings configured in the engine must be appropriate for them ahead
2427        // of time.
2428        if self.shared {
2429            return false;
2430        }
2431
2432        // If movement is disallowed in engine configuration, then the answer is
2433        // "no".
2434        if !memory_tunables.may_move() {
2435            return false;
2436        }
2437
2438        // If its minimum and maximum are the same, then the memory will never
2439        // be resized, and therefore will never move.
2440        if self.limits.max.is_some_and(|max| self.limits.min == max) {
2441            return false;
2442        }
2443
2444        // If the maximum size of this memory is above the threshold of the
2445        // initial memory reservation then the memory may move.
2446        let max = self.maximum_byte_size().unwrap_or(u64::MAX);
2447        max > memory_tunables.reservation()
2448    }
2449
2450    /// Tests whether this memory type is allowed to grow up to `size` bytes.
2451    ///
2452    /// This is only applicable to custom-page-size memories which have a page
2453    /// size of a single byte. In that situation growth beyond `-1i32 as u32`
2454    /// bytes is not allowed because at that point memory growth succeeding and
2455    /// failing would be indistinguishable in the return value of `memory.grow`,
2456    /// for example. To handle this 32-bit memories are only allowed to grow to
2457    /// `-2i32 as u32`, for example, and 64-bit memories with a page size of 1
2458    /// are allowed to grow up to the maximum size.
2459    pub fn allow_growth_to(&self, size: usize) -> bool {
2460        if self.page_size_log2 != 0 {
2461            return true;
2462        }
2463        match self.idx_type {
2464            // For a 32-bit memory using 1-byte pages the last 2 bytes of the
2465            // 32-bit address space are addressable but disallowed for now.  A
2466            // memory that is 4GiB in size cannot report its size via
2467            // `memory.size`, and a memory that is 4GiB-1 bytes in size cannot
2468            // be distinguished when 1 byte is added from an allocation
2469            // failure.  To handle this the memory is capped at 4GiB-2 which
2470            // means that all memory-related instructions and such will have
2471            // unambiguous return codes.
2472            IndexType::I32 => size < 0xffff_ffff,
2473
2474            // Assume that for a 64-bit memory using 1-byte pages it's going to
2475            // exhaust system resources before a limit is actually reached.
2476            IndexType::I64 => true,
2477        }
2478    }
2479}
2480
2481#[derive(Copy, Clone, Debug)]
2482#[expect(missing_docs, reason = "self-describing error struct")]
2483pub struct SizeOverflow;
2484
2485impl fmt::Display for SizeOverflow {
2486    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2487        f.write_str("size overflow calculating memory size")
2488    }
2489}
2490
2491impl core::error::Error for SizeOverflow {}
2492
2493impl From<wasmparser::MemoryType> for Memory {
2494    fn from(ty: wasmparser::MemoryType) -> Memory {
2495        let idx_type = match ty.memory64 {
2496            false => IndexType::I32,
2497            true => IndexType::I64,
2498        };
2499        let limits = Limits {
2500            min: ty.initial,
2501            max: ty.maximum,
2502        };
2503        let page_size_log2 = u8::try_from(ty.page_size_log2.unwrap_or(16)).unwrap();
2504        debug_assert!(
2505            page_size_log2 == 16 || page_size_log2 == 0,
2506            "invalid page_size_log2: {page_size_log2}; must be 16 or 0"
2507        );
2508        Memory {
2509            idx_type,
2510            limits,
2511            shared: ty.shared,
2512            page_size_log2,
2513        }
2514    }
2515}
2516
2517/// WebAssembly exception and control tag.
2518#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2519pub struct Tag {
2520    /// The tag signature type.
2521    pub signature: EngineOrModuleTypeIndex,
2522    /// The corresponding exception type.
2523    pub exception: EngineOrModuleTypeIndex,
2524}
2525
2526impl TypeTrace for Tag {
2527    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
2528    where
2529        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
2530    {
2531        func(self.signature)?;
2532        func(self.exception)?;
2533        Ok(())
2534    }
2535
2536    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
2537    where
2538        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
2539    {
2540        func(&mut self.signature)?;
2541        func(&mut self.exception)?;
2542        Ok(())
2543    }
2544}
2545
2546/// Helpers used to convert a `wasmparser` type to a type in this crate.
2547#[expect(missing_docs, reason = "self-describing functions")]
2548pub trait TypeConvert {
2549    /// Converts a wasmparser table type into a wasmtime type
2550    fn convert_global_type(&self, ty: &wasmparser::GlobalType) -> WasmResult<Global> {
2551        Ok(Global {
2552            wasm_ty: self.convert_valtype(ty.content_type)?,
2553            mutability: ty.mutable,
2554        })
2555    }
2556
2557    /// Converts a wasmparser table type into a wasmtime type
2558    fn convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table> {
2559        let idx_type = match ty.table64 {
2560            false => IndexType::I32,
2561            true => IndexType::I64,
2562        };
2563        let limits = Limits {
2564            min: ty.initial,
2565            max: ty.maximum,
2566        };
2567        Ok(Table {
2568            idx_type,
2569            limits,
2570            ref_type: self.convert_ref_type(ty.element_type)?,
2571        })
2572    }
2573
2574    fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> {
2575        Ok(WasmSubType {
2576            is_final: ty.is_final,
2577            supertype: ty.supertype_idx.map(|i| self.lookup_type_index(i.unpack())),
2578            composite_type: self.convert_composite_type(&ty.composite_type)?,
2579        })
2580    }
2581
2582    fn convert_composite_type(
2583        &self,
2584        ty: &wasmparser::CompositeType,
2585    ) -> WasmResult<WasmCompositeType> {
2586        let inner = match &ty.inner {
2587            wasmparser::CompositeInnerType::Func(f) => {
2588                WasmCompositeInnerType::Func(self.convert_func_type(f)?)
2589            }
2590            wasmparser::CompositeInnerType::Array(a) => {
2591                WasmCompositeInnerType::Array(self.convert_array_type(a)?)
2592            }
2593            wasmparser::CompositeInnerType::Struct(s) => {
2594                WasmCompositeInnerType::Struct(self.convert_struct_type(s)?)
2595            }
2596            wasmparser::CompositeInnerType::Cont(c) => {
2597                WasmCompositeInnerType::Cont(self.convert_cont_type(c))
2598            }
2599        };
2600        Ok(WasmCompositeType {
2601            inner,
2602            shared: ty.shared,
2603        })
2604    }
2605
2606    /// Converts a wasmparser continuation type to a wasmtime type
2607    fn convert_cont_type(&self, ty: &wasmparser::ContType) -> WasmContType {
2608        if let WasmHeapType::ConcreteFunc(sigidx) = self.lookup_heap_type(ty.0.unpack()) {
2609            WasmContType::new(sigidx)
2610        } else {
2611            panic!("Failed to extract signature index for continuation type.")
2612        }
2613    }
2614
2615    fn convert_struct_type(&self, ty: &wasmparser::StructType) -> WasmResult<WasmStructType> {
2616        Ok(WasmStructType {
2617            fields: ty
2618                .fields
2619                .iter()
2620                .map(|f| self.convert_field_type(f))
2621                .collect::<WasmResult<_>>()?,
2622        })
2623    }
2624
2625    fn convert_array_type(&self, ty: &wasmparser::ArrayType) -> WasmResult<WasmArrayType> {
2626        Ok(WasmArrayType(self.convert_field_type(&ty.0)?))
2627    }
2628
2629    fn convert_field_type(&self, ty: &wasmparser::FieldType) -> WasmResult<WasmFieldType> {
2630        Ok(WasmFieldType {
2631            element_type: self.convert_storage_type(&ty.element_type)?,
2632            mutable: ty.mutable,
2633        })
2634    }
2635
2636    fn convert_storage_type(&self, ty: &wasmparser::StorageType) -> WasmResult<WasmStorageType> {
2637        Ok(match ty {
2638            wasmparser::StorageType::I8 => WasmStorageType::I8,
2639            wasmparser::StorageType::I16 => WasmStorageType::I16,
2640            wasmparser::StorageType::Val(v) => WasmStorageType::Val(self.convert_valtype(*v)?),
2641        })
2642    }
2643
2644    /// Converts a wasmparser function type to a wasmtime type
2645    fn convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmResult<WasmFuncType> {
2646        let params = ty
2647            .params()
2648            .iter()
2649            .map(|t| self.convert_valtype(*t))
2650            .collect::<WasmResult<Vec<_>>>()?;
2651        let results = ty
2652            .results()
2653            .iter()
2654            .map(|t| self.convert_valtype(*t))
2655            .collect::<WasmResult<Vec<_>>>()?;
2656        Ok(WasmFuncType::new(params, results).panic_on_oom())
2657    }
2658
2659    /// Converts a wasmparser value type to a wasmtime type
2660    fn convert_valtype(&self, ty: wasmparser::ValType) -> WasmResult<WasmValType> {
2661        Ok(match ty {
2662            wasmparser::ValType::I32 => WasmValType::I32,
2663            wasmparser::ValType::I64 => WasmValType::I64,
2664            wasmparser::ValType::F32 => WasmValType::F32,
2665            wasmparser::ValType::F64 => WasmValType::F64,
2666            wasmparser::ValType::V128 => WasmValType::V128,
2667            wasmparser::ValType::Ref(t) => WasmValType::Ref(self.convert_ref_type(t)?),
2668        })
2669    }
2670
2671    /// Converts a wasmparser reference type to a wasmtime type
2672    fn convert_ref_type(&self, ty: wasmparser::RefType) -> WasmResult<WasmRefType> {
2673        Ok(WasmRefType {
2674            nullable: ty.is_nullable(),
2675            heap_type: self.convert_heap_type(ty.heap_type())?,
2676        })
2677    }
2678
2679    /// Converts a wasmparser heap type to a wasmtime type
2680    fn convert_heap_type(&self, ty: wasmparser::HeapType) -> WasmResult<WasmHeapType> {
2681        Ok(match ty {
2682            wasmparser::HeapType::Concrete(i) => self.lookup_heap_type(i),
2683            wasmparser::HeapType::Abstract { ty, shared: false } => match ty {
2684                wasmparser::AbstractHeapType::Extern => WasmHeapType::Extern,
2685                wasmparser::AbstractHeapType::NoExtern => WasmHeapType::NoExtern,
2686                wasmparser::AbstractHeapType::Func => WasmHeapType::Func,
2687                wasmparser::AbstractHeapType::NoFunc => WasmHeapType::NoFunc,
2688                wasmparser::AbstractHeapType::Any => WasmHeapType::Any,
2689                wasmparser::AbstractHeapType::Eq => WasmHeapType::Eq,
2690                wasmparser::AbstractHeapType::I31 => WasmHeapType::I31,
2691                wasmparser::AbstractHeapType::Array => WasmHeapType::Array,
2692                wasmparser::AbstractHeapType::Struct => WasmHeapType::Struct,
2693                wasmparser::AbstractHeapType::None => WasmHeapType::None,
2694                wasmparser::AbstractHeapType::Cont => WasmHeapType::Cont,
2695                wasmparser::AbstractHeapType::NoCont => WasmHeapType::NoCont,
2696                wasmparser::AbstractHeapType::Exn => WasmHeapType::Exn,
2697                wasmparser::AbstractHeapType::NoExn => WasmHeapType::NoExn,
2698            },
2699            _ => return Err(wasm_unsupported!("unsupported heap type {ty:?}")),
2700        })
2701    }
2702
2703    /// Converts the specified type index from a heap type into a canonicalized
2704    /// heap type.
2705    fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType;
2706
2707    /// Converts the specified type index from a heap type into a canonicalized
2708    /// heap type.
2709    fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex;
2710}
2711
2712#[cfg(test)]
2713mod tests {
2714    use super::*;
2715
2716    #[test]
2717    fn wasm_func_type_new() -> Result<()> {
2718        let i32 = WasmValType::I32;
2719        let anyref = WasmValType::Ref(WasmRefType {
2720            nullable: true,
2721            heap_type: WasmHeapType::Any,
2722        });
2723        let ty = WasmFuncType::new([i32, i32, anyref, anyref], [i32, anyref])?;
2724        assert_eq!(ty.params(), &[i32, i32, anyref, anyref]);
2725        assert_eq!(ty.non_i31_gc_ref_params_count(), 2);
2726        assert_eq!(ty.results(), &[i32, anyref]);
2727        assert_eq!(ty.non_i31_gc_ref_results_count(), 1);
2728        Ok(())
2729    }
2730}