wasmtime_environ/
types.rs

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