Skip to main content

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        match self.const_eval() {
1853            Some(GlobalConstValue::I32(x)) => x != 0,
1854
1855            // Conservatively return `false` for non-const-eval-able expressions
1856            // as well as everything else.
1857            _ => false,
1858        }
1859    }
1860
1861    /// Attempt to evaluate the given const-expr at compile time.
1862    pub fn const_eval(&self) -> Option<GlobalConstValue> {
1863        // TODO: Actually maintain an evaluation stack and handle `i32.add`,
1864        // `i32.sub`, etc... const ops.
1865        match self.ops() {
1866            [ConstOp::I32Const(x)] => Some(GlobalConstValue::I32(*x)),
1867            [ConstOp::I64Const(x)] => Some(GlobalConstValue::I64(*x)),
1868            [ConstOp::F32Const(x)] => Some(GlobalConstValue::F32(*x)),
1869            [ConstOp::F64Const(x)] => Some(GlobalConstValue::F64(*x)),
1870            [ConstOp::V128Const(x)] => Some(GlobalConstValue::V128(*x)),
1871            _ => None,
1872        }
1873    }
1874}
1875
1876/// A global's constant value, known at compile time.
1877#[expect(missing_docs, reason = "self-describing variants")]
1878#[derive(Clone, Copy)]
1879pub enum GlobalConstValue {
1880    I32(i32),
1881    I64(i64),
1882    F32(u32),
1883    F64(u64),
1884    V128(u128),
1885}
1886
1887/// The subset of Wasm opcodes that are constant.
1888#[expect(missing_docs, reason = "self-describing variants")]
1889#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1890pub enum ConstOp {
1891    I32Const(i32),
1892    I64Const(i64),
1893    F32Const(u32),
1894    F64Const(u64),
1895    V128Const(u128),
1896    GlobalGet(GlobalIndex),
1897    RefI31,
1898    RefNull(WasmHeapTopType),
1899    RefFunc(FuncIndex),
1900    I32Add,
1901    I32Sub,
1902    I32Mul,
1903    I64Add,
1904    I64Sub,
1905    I64Mul,
1906    StructNew {
1907        struct_type_index: TypeIndex,
1908    },
1909    StructNewDefault {
1910        struct_type_index: TypeIndex,
1911    },
1912    ArrayNew {
1913        array_type_index: TypeIndex,
1914    },
1915    ArrayNewDefault {
1916        array_type_index: TypeIndex,
1917    },
1918    ArrayNewFixed {
1919        array_type_index: TypeIndex,
1920        array_size: u32,
1921    },
1922    ExternConvertAny,
1923    AnyConvertExtern,
1924}
1925
1926impl ConstOp {
1927    /// Convert a `wasmparser::Operator` to a `ConstOp`.
1928    pub fn from_wasmparser(
1929        env: &dyn TypeConvert,
1930        op: wasmparser::Operator<'_>,
1931        offset: usize,
1932    ) -> WasmResult<Self> {
1933        use wasmparser::Operator as O;
1934        Ok(match op {
1935            O::I32Const { value } => Self::I32Const(value),
1936            O::I64Const { value } => Self::I64Const(value),
1937            O::F32Const { value } => Self::F32Const(value.bits()),
1938            O::F64Const { value } => Self::F64Const(value.bits()),
1939            O::V128Const { value } => Self::V128Const(u128::from_le_bytes(*value.bytes())),
1940            O::RefNull { hty } => Self::RefNull(env.convert_heap_type(hty)?.top()),
1941            O::RefFunc { function_index } => Self::RefFunc(FuncIndex::from_u32(function_index)),
1942            O::GlobalGet { global_index } => Self::GlobalGet(GlobalIndex::from_u32(global_index)),
1943            O::RefI31 => Self::RefI31,
1944            O::I32Add => Self::I32Add,
1945            O::I32Sub => Self::I32Sub,
1946            O::I32Mul => Self::I32Mul,
1947            O::I64Add => Self::I64Add,
1948            O::I64Sub => Self::I64Sub,
1949            O::I64Mul => Self::I64Mul,
1950            O::StructNew { struct_type_index } => Self::StructNew {
1951                struct_type_index: TypeIndex::from_u32(struct_type_index),
1952            },
1953            O::StructNewDefault { struct_type_index } => Self::StructNewDefault {
1954                struct_type_index: TypeIndex::from_u32(struct_type_index),
1955            },
1956            O::ArrayNew { array_type_index } => Self::ArrayNew {
1957                array_type_index: TypeIndex::from_u32(array_type_index),
1958            },
1959            O::ArrayNewDefault { array_type_index } => Self::ArrayNewDefault {
1960                array_type_index: TypeIndex::from_u32(array_type_index),
1961            },
1962            O::ArrayNewFixed {
1963                array_type_index,
1964                array_size,
1965            } => Self::ArrayNewFixed {
1966                array_type_index: TypeIndex::from_u32(array_type_index),
1967                array_size,
1968            },
1969            O::ExternConvertAny => Self::ExternConvertAny,
1970            O::AnyConvertExtern => Self::AnyConvertExtern,
1971            op => {
1972                return Err(wasm_unsupported!(
1973                    "unsupported opcode in const expression at offset {offset:#x}: {op:?}",
1974                ));
1975            }
1976        })
1977    }
1978}
1979
1980/// The type that can be used to index into [Memory] and [Table].
1981#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1982#[expect(missing_docs, reason = "self-describing variants")]
1983pub enum IndexType {
1984    I32,
1985    I64,
1986}
1987
1988/// The size range of resizeable storage associated with [Memory] types and [Table] types.
1989#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1990#[expect(missing_docs, reason = "self-describing fields")]
1991pub struct Limits {
1992    pub min: u64,
1993    pub max: Option<u64>,
1994}
1995
1996/// WebAssembly table.
1997#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1998pub struct Table {
1999    /// The type of the index used to access the table.
2000    pub idx_type: IndexType,
2001    /// Tables are constrained by limits for their minimum and optionally maximum size.
2002    /// The limits are given in numbers of entries.
2003    pub limits: Limits,
2004    /// The table elements' Wasm type.
2005    pub ref_type: WasmRefType,
2006}
2007
2008impl TypeTrace for Table {
2009    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
2010    where
2011        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
2012    {
2013        let Table {
2014            ref_type: wasm_ty,
2015            idx_type: _,
2016            limits: _,
2017        } = self;
2018        wasm_ty.trace(func)
2019    }
2020
2021    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
2022    where
2023        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
2024    {
2025        let Table {
2026            ref_type: wasm_ty,
2027            idx_type: _,
2028            limits: _,
2029        } = self;
2030        wasm_ty.trace_mut(func)
2031    }
2032}
2033
2034/// WebAssembly linear memory.
2035#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2036pub struct Memory {
2037    /// The type of the index used to access the memory.
2038    pub idx_type: IndexType,
2039    /// The limits constrain the minimum and optionally the maximum size of a memory.
2040    /// The limits are given in units of page size.
2041    pub limits: Limits,
2042    /// Whether the memory may be shared between multiple threads.
2043    pub shared: bool,
2044    /// The log2 of this memory's page size, in bytes.
2045    ///
2046    /// By default the page size is 64KiB (0x10000; 2**16; 1<<16; 65536) but the
2047    /// custom-page-sizes proposal allows opting into a page size of `1`.
2048    pub page_size_log2: u8,
2049}
2050
2051/// Maximum size, in bytes, of 32-bit memories (4G)
2052pub const WASM32_MAX_SIZE: u64 = 1 << 32;
2053
2054impl Memory {
2055    /// WebAssembly page sizes are 64KiB by default.
2056    pub const DEFAULT_PAGE_SIZE: u32 = 0x10000;
2057
2058    /// WebAssembly page sizes are 64KiB (or `2**16`) by default.
2059    pub const DEFAULT_PAGE_SIZE_LOG2: u8 = {
2060        let log2 = 16;
2061        assert!(1 << log2 == Memory::DEFAULT_PAGE_SIZE);
2062        log2
2063    };
2064
2065    /// Returns the minimum size, in bytes, that this memory must be.
2066    ///
2067    /// # Errors
2068    ///
2069    /// Returns an error if the calculation of the minimum size overflows the
2070    /// `u64` return type. This means that the memory can't be allocated but
2071    /// it's deferred to the caller to how to deal with that.
2072    pub fn minimum_byte_size(&self) -> Result<u64, SizeOverflow> {
2073        self.limits
2074            .min
2075            .checked_mul(self.page_size())
2076            .ok_or(SizeOverflow)
2077    }
2078
2079    /// Returns the maximum size, in bytes, that this memory is allowed to be.
2080    ///
2081    /// Note that the return value here is not an `Option` despite the maximum
2082    /// size of a linear memory being optional in wasm. If a maximum size
2083    /// is not present in the memory's type then a maximum size is selected for
2084    /// it. For example the maximum size of a 32-bit memory is `1<<32`. The
2085    /// maximum size of a 64-bit linear memory is chosen to be a value that
2086    /// won't ever be allowed at runtime.
2087    ///
2088    /// # Errors
2089    ///
2090    /// Returns an error if the calculation of the maximum size overflows the
2091    /// `u64` return type. This means that the memory can't be allocated but
2092    /// it's deferred to the caller to how to deal with that.
2093    pub fn maximum_byte_size(&self) -> Result<u64, SizeOverflow> {
2094        match self.limits.max {
2095            Some(max) => max.checked_mul(self.page_size()).ok_or(SizeOverflow),
2096            None => {
2097                let min = self.minimum_byte_size()?;
2098                Ok(min.max(self.max_size_based_on_index_type()))
2099            }
2100        }
2101    }
2102
2103    /// Get the size of this memory's pages, in bytes.
2104    pub fn page_size(&self) -> u64 {
2105        debug_assert!(
2106            self.page_size_log2 == 16 || self.page_size_log2 == 0,
2107            "invalid page_size_log2: {}; must be 16 or 0",
2108            self.page_size_log2
2109        );
2110        1 << self.page_size_log2
2111    }
2112
2113    /// Returns the maximum size memory is allowed to be only based on the
2114    /// index type used by this memory.
2115    ///
2116    /// For example 32-bit linear memories return `1<<32` from this method.
2117    pub fn max_size_based_on_index_type(&self) -> u64 {
2118        match self.idx_type {
2119            IndexType::I64 =>
2120            // Note that the true maximum size of a 64-bit linear memory, in
2121            // bytes, cannot be represented in a `u64`. That would require a u65
2122            // to store `1<<64`. Despite that no system can actually allocate a
2123            // full 64-bit linear memory so this is instead emulated as "what if
2124            // the kernel fit in a single Wasm page of linear memory". Shouldn't
2125            // ever actually be possible but it provides a number to serve as an
2126            // effective maximum.
2127            {
2128                0_u64.wrapping_sub(self.page_size())
2129            }
2130            IndexType::I32 => WASM32_MAX_SIZE,
2131        }
2132    }
2133
2134    /// Returns whether this memory can be implemented with virtual memory on
2135    /// a host with `host_page_size_log2`.
2136    ///
2137    /// When this function returns `true` then it means that signals such as
2138    /// SIGSEGV on the host are compatible with wasm and can be used to
2139    /// represent out-of-bounds memory accesses.
2140    ///
2141    /// When this function returns `false` then it means that this memory must,
2142    /// for example, have explicit bounds checks. This additionally means that
2143    /// virtual memory traps (e.g. SIGSEGV) cannot be relied on to implement
2144    /// linear memory semantics.
2145    pub fn can_use_virtual_memory(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool {
2146        tunables.signals_based_traps && self.page_size_log2 >= host_page_size_log2
2147    }
2148
2149    /// Returns whether this memory is a candidate for bounds check elision
2150    /// given the configuration and host page size.
2151    ///
2152    /// This function determines whether the given compilation configuration and
2153    /// hos enables possible bounds check elision for this memory. Bounds checks
2154    /// can only be elided if [`Memory::can_use_virtual_memory`] returns `true`
2155    /// for example but there are additionally requirements on the index size of
2156    /// this memory and the memory reservation in `tunables`.
2157    ///
2158    /// Currently the only case that supports bounds check elision is when all
2159    /// of these apply:
2160    ///
2161    /// * When [`Memory::can_use_virtual_memory`] returns `true`.
2162    /// * This is a 32-bit linear memory (e.g. not 64-bit)
2163    /// * `tunables.memory_reservation` is in excess of 4GiB
2164    ///
2165    /// In this situation all computable addresses fall within the reserved
2166    /// space (modulo static offsets factoring in guard pages) so bounds checks
2167    /// may be elidable.
2168    pub fn can_elide_bounds_check(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool {
2169        self.can_use_virtual_memory(tunables, host_page_size_log2)
2170            && self.idx_type == IndexType::I32
2171            && tunables.memory_reservation + tunables.memory_guard_size >= (1 << 32)
2172    }
2173
2174    /// Returns the static size of this heap in bytes at runtime, if available.
2175    ///
2176    /// This is only computable when the minimum size equals the maximum size.
2177    pub fn static_heap_size(&self) -> Option<u64> {
2178        let min = self.minimum_byte_size().ok()?;
2179        let max = self.maximum_byte_size().ok()?;
2180        if min == max { Some(min) } else { None }
2181    }
2182
2183    /// Returns whether or not the base pointer of this memory is allowed to be
2184    /// relocated at runtime.
2185    ///
2186    /// When this function returns `false` then it means that after the initial
2187    /// allocation the base pointer is constant for the entire lifetime of a
2188    /// memory. This can enable compiler optimizations, for example.
2189    pub fn memory_may_move(&self, tunables: &Tunables) -> bool {
2190        // Shared memories cannot ever relocate their base pointer so the
2191        // settings configured in the engine must be appropriate for them ahead
2192        // of time.
2193        if self.shared {
2194            return false;
2195        }
2196
2197        // If movement is disallowed in engine configuration, then the answer is
2198        // "no".
2199        if !tunables.memory_may_move {
2200            return false;
2201        }
2202
2203        // If its minimum and maximum are the same, then the memory will never
2204        // be resized, and therefore will never move.
2205        if self.limits.max.is_some_and(|max| self.limits.min == max) {
2206            return false;
2207        }
2208
2209        // If the maximum size of this memory is above the threshold of the
2210        // initial memory reservation then the memory may move.
2211        let max = self.maximum_byte_size().unwrap_or(u64::MAX);
2212        max > tunables.memory_reservation
2213    }
2214}
2215
2216#[derive(Copy, Clone, Debug)]
2217#[expect(missing_docs, reason = "self-describing error struct")]
2218pub struct SizeOverflow;
2219
2220impl fmt::Display for SizeOverflow {
2221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2222        f.write_str("size overflow calculating memory size")
2223    }
2224}
2225
2226impl core::error::Error for SizeOverflow {}
2227
2228impl From<wasmparser::MemoryType> for Memory {
2229    fn from(ty: wasmparser::MemoryType) -> Memory {
2230        let idx_type = match ty.memory64 {
2231            false => IndexType::I32,
2232            true => IndexType::I64,
2233        };
2234        let limits = Limits {
2235            min: ty.initial,
2236            max: ty.maximum,
2237        };
2238        let page_size_log2 = u8::try_from(ty.page_size_log2.unwrap_or(16)).unwrap();
2239        debug_assert!(
2240            page_size_log2 == 16 || page_size_log2 == 0,
2241            "invalid page_size_log2: {page_size_log2}; must be 16 or 0"
2242        );
2243        Memory {
2244            idx_type,
2245            limits,
2246            shared: ty.shared,
2247            page_size_log2,
2248        }
2249    }
2250}
2251
2252/// WebAssembly exception and control tag.
2253#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2254pub struct Tag {
2255    /// The tag signature type.
2256    pub signature: EngineOrModuleTypeIndex,
2257    /// The corresponding exception type.
2258    pub exception: EngineOrModuleTypeIndex,
2259}
2260
2261impl TypeTrace for Tag {
2262    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
2263    where
2264        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
2265    {
2266        func(self.signature)?;
2267        func(self.exception)?;
2268        Ok(())
2269    }
2270
2271    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
2272    where
2273        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
2274    {
2275        func(&mut self.signature)?;
2276        func(&mut self.exception)?;
2277        Ok(())
2278    }
2279}
2280
2281/// Helpers used to convert a `wasmparser` type to a type in this crate.
2282#[expect(missing_docs, reason = "self-describing functions")]
2283pub trait TypeConvert {
2284    /// Converts a wasmparser table type into a wasmtime type
2285    fn convert_global_type(&self, ty: &wasmparser::GlobalType) -> WasmResult<Global> {
2286        Ok(Global {
2287            wasm_ty: self.convert_valtype(ty.content_type)?,
2288            mutability: ty.mutable,
2289        })
2290    }
2291
2292    /// Converts a wasmparser table type into a wasmtime type
2293    fn convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table> {
2294        let idx_type = match ty.table64 {
2295            false => IndexType::I32,
2296            true => IndexType::I64,
2297        };
2298        let limits = Limits {
2299            min: ty.initial,
2300            max: ty.maximum,
2301        };
2302        Ok(Table {
2303            idx_type,
2304            limits,
2305            ref_type: self.convert_ref_type(ty.element_type)?,
2306        })
2307    }
2308
2309    fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> {
2310        Ok(WasmSubType {
2311            is_final: ty.is_final,
2312            supertype: ty.supertype_idx.map(|i| self.lookup_type_index(i.unpack())),
2313            composite_type: self.convert_composite_type(&ty.composite_type)?,
2314        })
2315    }
2316
2317    fn convert_composite_type(
2318        &self,
2319        ty: &wasmparser::CompositeType,
2320    ) -> WasmResult<WasmCompositeType> {
2321        let inner = match &ty.inner {
2322            wasmparser::CompositeInnerType::Func(f) => {
2323                WasmCompositeInnerType::Func(self.convert_func_type(f)?)
2324            }
2325            wasmparser::CompositeInnerType::Array(a) => {
2326                WasmCompositeInnerType::Array(self.convert_array_type(a)?)
2327            }
2328            wasmparser::CompositeInnerType::Struct(s) => {
2329                WasmCompositeInnerType::Struct(self.convert_struct_type(s)?)
2330            }
2331            wasmparser::CompositeInnerType::Cont(c) => {
2332                WasmCompositeInnerType::Cont(self.convert_cont_type(c))
2333            }
2334        };
2335        Ok(WasmCompositeType {
2336            inner,
2337            shared: ty.shared,
2338        })
2339    }
2340
2341    /// Converts a wasmparser continuation type to a wasmtime type
2342    fn convert_cont_type(&self, ty: &wasmparser::ContType) -> WasmContType {
2343        if let WasmHeapType::ConcreteFunc(sigidx) = self.lookup_heap_type(ty.0.unpack()) {
2344            WasmContType::new(sigidx)
2345        } else {
2346            panic!("Failed to extract signature index for continuation type.")
2347        }
2348    }
2349
2350    fn convert_struct_type(&self, ty: &wasmparser::StructType) -> WasmResult<WasmStructType> {
2351        Ok(WasmStructType {
2352            fields: ty
2353                .fields
2354                .iter()
2355                .map(|f| self.convert_field_type(f))
2356                .collect::<WasmResult<_>>()?,
2357        })
2358    }
2359
2360    fn convert_array_type(&self, ty: &wasmparser::ArrayType) -> WasmResult<WasmArrayType> {
2361        Ok(WasmArrayType(self.convert_field_type(&ty.0)?))
2362    }
2363
2364    fn convert_field_type(&self, ty: &wasmparser::FieldType) -> WasmResult<WasmFieldType> {
2365        Ok(WasmFieldType {
2366            element_type: self.convert_storage_type(&ty.element_type)?,
2367            mutable: ty.mutable,
2368        })
2369    }
2370
2371    fn convert_storage_type(&self, ty: &wasmparser::StorageType) -> WasmResult<WasmStorageType> {
2372        Ok(match ty {
2373            wasmparser::StorageType::I8 => WasmStorageType::I8,
2374            wasmparser::StorageType::I16 => WasmStorageType::I16,
2375            wasmparser::StorageType::Val(v) => WasmStorageType::Val(self.convert_valtype(*v)?),
2376        })
2377    }
2378
2379    /// Converts a wasmparser function type to a wasmtime type
2380    fn convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmResult<WasmFuncType> {
2381        let params = ty
2382            .params()
2383            .iter()
2384            .map(|t| self.convert_valtype(*t))
2385            .collect::<WasmResult<_>>()?;
2386        let results = ty
2387            .results()
2388            .iter()
2389            .map(|t| self.convert_valtype(*t))
2390            .collect::<WasmResult<_>>()?;
2391        Ok(WasmFuncType::new(params, results))
2392    }
2393
2394    /// Converts a wasmparser value type to a wasmtime type
2395    fn convert_valtype(&self, ty: wasmparser::ValType) -> WasmResult<WasmValType> {
2396        Ok(match ty {
2397            wasmparser::ValType::I32 => WasmValType::I32,
2398            wasmparser::ValType::I64 => WasmValType::I64,
2399            wasmparser::ValType::F32 => WasmValType::F32,
2400            wasmparser::ValType::F64 => WasmValType::F64,
2401            wasmparser::ValType::V128 => WasmValType::V128,
2402            wasmparser::ValType::Ref(t) => WasmValType::Ref(self.convert_ref_type(t)?),
2403        })
2404    }
2405
2406    /// Converts a wasmparser reference type to a wasmtime type
2407    fn convert_ref_type(&self, ty: wasmparser::RefType) -> WasmResult<WasmRefType> {
2408        Ok(WasmRefType {
2409            nullable: ty.is_nullable(),
2410            heap_type: self.convert_heap_type(ty.heap_type())?,
2411        })
2412    }
2413
2414    /// Converts a wasmparser heap type to a wasmtime type
2415    fn convert_heap_type(&self, ty: wasmparser::HeapType) -> WasmResult<WasmHeapType> {
2416        Ok(match ty {
2417            wasmparser::HeapType::Concrete(i) => self.lookup_heap_type(i),
2418            wasmparser::HeapType::Abstract { ty, shared: false } => match ty {
2419                wasmparser::AbstractHeapType::Extern => WasmHeapType::Extern,
2420                wasmparser::AbstractHeapType::NoExtern => WasmHeapType::NoExtern,
2421                wasmparser::AbstractHeapType::Func => WasmHeapType::Func,
2422                wasmparser::AbstractHeapType::NoFunc => WasmHeapType::NoFunc,
2423                wasmparser::AbstractHeapType::Any => WasmHeapType::Any,
2424                wasmparser::AbstractHeapType::Eq => WasmHeapType::Eq,
2425                wasmparser::AbstractHeapType::I31 => WasmHeapType::I31,
2426                wasmparser::AbstractHeapType::Array => WasmHeapType::Array,
2427                wasmparser::AbstractHeapType::Struct => WasmHeapType::Struct,
2428                wasmparser::AbstractHeapType::None => WasmHeapType::None,
2429                wasmparser::AbstractHeapType::Cont => WasmHeapType::Cont,
2430                wasmparser::AbstractHeapType::NoCont => WasmHeapType::NoCont,
2431                wasmparser::AbstractHeapType::Exn => WasmHeapType::Exn,
2432                wasmparser::AbstractHeapType::NoExn => WasmHeapType::NoExn,
2433            },
2434            _ => return Err(wasm_unsupported!("unsupported heap type {ty:?}")),
2435        })
2436    }
2437
2438    /// Converts the specified type index from a heap type into a canonicalized
2439    /// heap type.
2440    fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType;
2441
2442    /// Converts the specified type index from a heap type into a canonicalized
2443    /// heap type.
2444    fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex;
2445}