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