Skip to main content

wasmtime_environ/
types.rs

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