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