Skip to main content

wasmtime_environ/
types.rs

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