xref: /wasmtime-44.0.1/crates/environ/src/types.rs (revision b860c2c6)
1 use crate::{
2     PanicOnOom as _, Tunables, WasmResult, collections::TryCow, error::OutOfMemory, prelude::*,
3     wasm_unsupported,
4 };
5 use alloc::boxed::Box;
6 use core::{fmt, ops::Range};
7 use serde_derive::{Deserialize, Serialize};
8 use smallvec::SmallVec;
9 
10 /// A trait for things that can trace all type-to-type edges, aka all type
11 /// indices within this thing.
12 pub trait TypeTrace {
13     /// Visit each edge.
14     ///
15     /// The function can break out of tracing by returning `Err(E)`.
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), 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)`.
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), 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.
trace_engine_indices<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(VMSharedTypeIndex) -> Result<(), E>,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).
canonicalize_for_runtime_usage<F>(&mut self, module_to_engine: &mut F) where F: FnMut(ModuleInternedTypeIndex) -> VMSharedTypeIndex,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?
is_canonicalized_for_runtime_usage(&self) -> bool69     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).
canonicalize_for_hash_consing<F>( &mut self, rec_group_range: Range<ModuleInternedTypeIndex>, module_to_engine: &mut F, ) where F: FnMut(ModuleInternedTypeIndex) -> VMSharedTypeIndex,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?
is_canonicalized_for_hash_consing(&self) -> bool120     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)]
131 pub 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 
146 impl TryClone for WasmValType {
try_clone(&self) -> Result<Self, OutOfMemory>147     fn try_clone(&self) -> Result<Self, OutOfMemory> {
148         Ok(*self)
149     }
150 }
151 
152 impl fmt::Display for WasmValType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result153     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 
165 impl TypeTrace for WasmValType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,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 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,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 
195 impl WasmValType {
196     /// Is this a type that is represented as a `VMGcRef`?
197     #[inline]
is_vmgcref_type(&self) -> bool198     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]
is_vmgcref_type_and_not_i31(&self) -> bool211     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 
trampoline_type(&self) -> Self218     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.
int_from_bits(bits: u8) -> Self235     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
unwrap_ref_type(&self) -> WasmRefType246     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)]
256 pub 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 
263 impl TypeTrace for WasmRefType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,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 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,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 
279 impl 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]
is_vmgcref_type(&self) -> bool293     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]
is_vmgcref_type_and_not_i31(&self) -> bool303     pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
304         self.heap_type.is_vmgcref_type_and_not_i31()
305     }
306 }
307 
308 impl fmt::Display for WasmRefType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result309     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)]
329 pub 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 
344 impl From<ModuleInternedTypeIndex> for EngineOrModuleTypeIndex {
345     #[inline]
from(i: ModuleInternedTypeIndex) -> Self346     fn from(i: ModuleInternedTypeIndex) -> Self {
347         Self::Module(i)
348     }
349 }
350 
351 impl From<VMSharedTypeIndex> for EngineOrModuleTypeIndex {
352     #[inline]
from(i: VMSharedTypeIndex) -> Self353     fn from(i: VMSharedTypeIndex) -> Self {
354         Self::Engine(i)
355     }
356 }
357 
358 impl From<RecGroupRelativeTypeIndex> for EngineOrModuleTypeIndex {
359     #[inline]
from(i: RecGroupRelativeTypeIndex) -> Self360     fn from(i: RecGroupRelativeTypeIndex) -> Self {
361         Self::RecGroup(i)
362     }
363 }
364 
365 impl fmt::Display for EngineOrModuleTypeIndex {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result366     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 
375 impl EngineOrModuleTypeIndex {
376     /// Is this an engine-level type index?
is_engine_type_index(self) -> bool377     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]
as_engine_type_index(self) -> Option<VMSharedTypeIndex>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]
unwrap_engine_type_index(self) -> VMSharedTypeIndex393     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?
is_module_type_index(self) -> bool401     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.
as_module_type_index(self) -> Option<ModuleInternedTypeIndex>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]
unwrap_module_type_index(self) -> ModuleInternedTypeIndex415     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?
is_rec_group_type_index(self) -> bool423     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.
as_rec_group_type_index(self) -> Option<RecGroupRelativeTypeIndex>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]
unwrap_rec_group_type_index(self) -> RecGroupRelativeTypeIndex437     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")]
448 pub 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 
479 impl From<WasmHeapTopType> for WasmHeapType {
480     #[inline]
from(value: WasmHeapTopType) -> Self481     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 
492 impl From<WasmHeapBottomType> for WasmHeapType {
493     #[inline]
from(value: WasmHeapBottomType) -> Self494     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 
505 impl fmt::Display for WasmHeapType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result506     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 
531 impl TypeTrace for WasmHeapType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,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 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,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 
559 impl WasmHeapType {
560     /// Is this a type that is represented as a `VMGcRef`?
561     #[inline]
is_vmgcref_type(&self) -> bool562     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]
is_vmgcref_type_and_not_i31(&self) -> bool581     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]
is_top(&self) -> bool587     pub fn is_top(&self) -> bool {
588         *self == Self::from(self.top())
589     }
590 
591     /// Get this type's top type.
592     #[inline]
top(&self) -> WasmHeapTopType593     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]
is_bottom(&self) -> bool622     pub fn is_bottom(&self) -> bool {
623         *self == Self::from(self.bottom())
624     }
625 
626     /// Get this type's bottom type.
627     #[inline]
bottom(&self) -> WasmHeapBottomType628     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)]
658 pub 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)]
673 pub enum WasmHeapBottomType {
674     /// The common subtype of all external references.
675     NoExtern,
676     /// The common subtype of all internal references.
677     None,
678     /// The common subtype of all function references.
679     NoFunc,
680     /// The common subtype of all exception references.
681     NoExn,
682     /// The common subtype of all continuation references.
683     NoCont,
684 }
685 
686 /// WebAssembly function type -- equivalent of `wasmparser`'s FuncType.
687 #[derive(Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
688 pub struct WasmFuncType {
689     #[serde(deserialize_with = "WasmFuncType::deserialize_params_results")]
690     params_results: Box<[WasmValType]>,
691     params_len: u32,
692     non_i31_gc_ref_params_count: u32,
693     non_i31_gc_ref_results_count: u32,
694 }
695 
696 impl TryClone for WasmFuncType {
try_clone(&self) -> Result<Self, OutOfMemory>697     fn try_clone(&self) -> Result<Self, OutOfMemory> {
698         Ok(Self {
699             params_results: TryClone::try_clone(&self.params_results)?,
700             params_len: self.params_len,
701             non_i31_gc_ref_params_count: self.non_i31_gc_ref_params_count,
702             non_i31_gc_ref_results_count: self.non_i31_gc_ref_results_count,
703         })
704     }
705 }
706 
707 impl fmt::Display for WasmFuncType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result708     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
709         write!(f, "(func")?;
710         if !self.params().is_empty() {
711             write!(f, " (param")?;
712             for p in self.params() {
713                 write!(f, " {p}")?;
714             }
715             write!(f, ")")?;
716         }
717         if !self.results().is_empty() {
718             write!(f, " (result")?;
719             for r in self.results() {
720                 write!(f, " {r}")?;
721             }
722             write!(f, ")")?;
723         }
724         write!(f, ")")
725     }
726 }
727 
728 impl TypeTrace for WasmFuncType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,729     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
730     where
731         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
732     {
733         for ty in self.params_results.iter() {
734             ty.trace(func)?;
735         }
736         Ok(())
737     }
738 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,739     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
740     where
741         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
742     {
743         for ty in self.params_results.iter_mut() {
744             ty.trace_mut(func)?;
745         }
746         Ok(())
747     }
748 }
749 
750 impl WasmFuncType {
deserialize_params_results<'de, D>(deserializer: D) -> Result<Box<[WasmValType]>, D::Error> where D: serde::de::Deserializer<'de>,751     fn deserialize_params_results<'de, D>(deserializer: D) -> Result<Box<[WasmValType]>, D::Error>
752     where
753         D: serde::de::Deserializer<'de>,
754     {
755         let tys: crate::collections::TryVec<WasmValType> =
756             serde::Deserialize::deserialize(deserializer)?;
757         let tys = tys
758             .into_boxed_slice()
759             .map_err(|oom| serde::de::Error::custom(oom))?;
760         Ok(tys)
761     }
762 
763     /// Creates a new function type from the provided `params` and `returns`.
764     #[inline]
new( params: impl IntoIterator<Item = WasmValType>, results: impl IntoIterator<Item = WasmValType>, ) -> Result<Self, OutOfMemory>765     pub fn new(
766         params: impl IntoIterator<Item = WasmValType>,
767         results: impl IntoIterator<Item = WasmValType>,
768     ) -> Result<Self, OutOfMemory> {
769         let mut params_results: crate::collections::TryVec<_> = params.into_iter().try_collect()?;
770         let non_i31_gc_ref_params_count = params_results
771             .iter()
772             .filter(|p| p.is_vmgcref_type_and_not_i31())
773             .count();
774 
775         let params_len = params_results.len();
776         params_results.try_extend(results)?;
777         let non_i31_gc_ref_results_count = params_results[params_len..]
778             .iter()
779             .filter(|r| r.is_vmgcref_type_and_not_i31())
780             .count();
781 
782         let params_results = params_results.into_boxed_slice()?;
783         let params_len = u32::try_from(params_len).unwrap();
784         let non_i31_gc_ref_params_count = u32::try_from(non_i31_gc_ref_params_count).unwrap();
785         let non_i31_gc_ref_results_count = u32::try_from(non_i31_gc_ref_results_count).unwrap();
786 
787         Ok(Self {
788             params_results,
789             params_len,
790             non_i31_gc_ref_params_count,
791             non_i31_gc_ref_results_count,
792         })
793     }
794 
results_start(&self) -> usize795     fn results_start(&self) -> usize {
796         usize::try_from(self.params_len).unwrap()
797     }
798 
799     /// Function params types.
800     #[inline]
params(&self) -> &[WasmValType]801     pub fn params(&self) -> &[WasmValType] {
802         &self.params_results[..self.results_start()]
803     }
804 
805     /// How many `externref`s are in this function's params?
806     #[inline]
non_i31_gc_ref_params_count(&self) -> usize807     pub fn non_i31_gc_ref_params_count(&self) -> usize {
808         usize::try_from(self.non_i31_gc_ref_params_count).unwrap()
809     }
810 
811     /// Returns params types.
812     #[inline]
results(&self) -> &[WasmValType]813     pub fn results(&self) -> &[WasmValType] {
814         &self.params_results[self.results_start()..]
815     }
816 
817     /// How many `externref`s are in this function's returns?
818     #[inline]
non_i31_gc_ref_results_count(&self) -> usize819     pub fn non_i31_gc_ref_results_count(&self) -> usize {
820         usize::try_from(self.non_i31_gc_ref_results_count).unwrap()
821     }
822 
823     /// Is this function type compatible with trampoline usage in Wasmtime?
is_trampoline_type(&self) -> bool824     pub fn is_trampoline_type(&self) -> bool {
825         self.params().iter().all(|p| *p == p.trampoline_type())
826             && self.results().iter().all(|r| *r == r.trampoline_type())
827     }
828 
829     /// Get the version of this function type that is suitable for usage as a
830     /// trampoline in Wasmtime.
831     ///
832     /// If this function is suitable for trampoline usage as-is, then a borrowed
833     /// `Cow` is returned. If it must be tweaked for trampoline usage, then an
834     /// owned `Cow` is returned.
835     ///
836     /// ## What is a trampoline type?
837     ///
838     /// All reference types in parameters and results are mapped to their
839     /// nullable top type, e.g. `(ref $my_struct_type)` becomes `(ref null
840     /// any)`.
841     ///
842     /// This allows us to share trampolines between functions whose signatures
843     /// both map to the same trampoline type. It also allows the host to satisfy
844     /// a Wasm module's function import of type `S` with a function of type `T`
845     /// where `T <: S`, even when the Wasm module never defines the type `T`
846     /// (and might never even be able to!)
847     ///
848     /// The flip side is that this adds a constraint to our trampolines: they
849     /// can only pass references around (e.g. move a reference from one calling
850     /// convention's location to another's) and may not actually inspect the
851     /// references themselves (unless the trampolines start doing explicit,
852     /// fallible downcasts, but if we ever need that, then we might want to
853     /// redesign this stuff).
trampoline_type(&self) -> Result<TryCow<'_, Self>, OutOfMemory>854     pub fn trampoline_type(&self) -> Result<TryCow<'_, Self>, OutOfMemory> {
855         if self.is_trampoline_type() {
856             return Ok(TryCow::Borrowed(self));
857         }
858 
859         Ok(TryCow::Owned(Self::new(
860             self.params().iter().map(|p| p.trampoline_type()),
861             self.results().iter().map(|r| r.trampoline_type()),
862         )?))
863     }
864 }
865 
866 /// WebAssembly continuation type -- equivalent of `wasmparser`'s ContType.
867 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
868 pub struct WasmContType(EngineOrModuleTypeIndex);
869 
870 impl fmt::Display for WasmContType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result871     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
872         write!(f, "(cont {})", self.0)
873     }
874 }
875 
876 impl WasmContType {
877     /// Constructs a new continuation type.
new(idx: EngineOrModuleTypeIndex) -> Self878     pub fn new(idx: EngineOrModuleTypeIndex) -> Self {
879         WasmContType(idx)
880     }
881 
882     /// Returns the (module interned) index to the underlying function type.
unwrap_module_type_index(self) -> ModuleInternedTypeIndex883     pub fn unwrap_module_type_index(self) -> ModuleInternedTypeIndex {
884         match self.0 {
885             EngineOrModuleTypeIndex::Engine(_) => panic!("not module interned"),
886             EngineOrModuleTypeIndex::Module(idx) => idx,
887             EngineOrModuleTypeIndex::RecGroup(_) => todo!(),
888         }
889     }
890 }
891 
892 impl TypeTrace for WasmContType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,893     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
894     where
895         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
896     {
897         func(self.0)
898     }
899 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,900     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
901     where
902         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
903     {
904         func(&mut self.0)
905     }
906 }
907 
908 /// WebAssembly exception type.
909 ///
910 /// This "exception type" is not a Wasm language-level
911 /// concept. Instead, it denotes an *exception object signature* --
912 /// the types of the payload values.
913 ///
914 /// In contrast, at the Wasm language level, exception objects are
915 /// associated with specific tags, and these tags refer to their
916 /// signatures (function types). However, tags are *nominal*: like
917 /// memories and tables, a separate instance of a tag exists for every
918 /// instance of the defining module, and these tag instances can be
919 /// imported and exported. At runtime we handle tags like we do
920 /// memories and tables, but these runtime instances do not exist in
921 /// the type system here.
922 ///
923 /// Because the Wasm type system does not have concrete `exn` types
924 /// (i.e., the heap-type lattice has only top `exn` and bottom
925 /// `noexn`), we are free to decide what we mean by "concrete type"
926 /// here. Thus, we define an "exception type" to refer to the
927 /// type-level *signature*. When a particular *exception object* is
928 /// created in a store, it can be associated with a particular *tag
929 /// instance* also in that store, and the compatibility is checked
930 /// (the tag's function type must match the function type in the
931 /// associated WasmExnType).
932 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
933 pub struct WasmExnType {
934     /// The function type from which we get our signature. We hold
935     /// this directly so that we can efficiently derive a FuncType
936     /// without re-interning the field types.
937     pub func_ty: EngineOrModuleTypeIndex,
938     /// The fields (payload values) that make up this exception type.
939     ///
940     /// While we could obtain these by looking up the `func_ty` above,
941     /// we also need to be able to derive a GC object layout from this
942     /// type descriptor without referencing other type descriptors; so
943     /// we directly inline the information here.
944     pub fields: Box<[WasmFieldType]>,
945 }
946 
947 impl TryClone for WasmExnType {
try_clone(&self) -> Result<Self, OutOfMemory>948     fn try_clone(&self) -> Result<Self, OutOfMemory> {
949         Ok(Self {
950             func_ty: self.func_ty,
951             fields: self.fields.try_clone()?,
952         })
953     }
954 }
955 
956 impl fmt::Display for WasmExnType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result957     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
958         write!(f, "(exn ({})", self.func_ty)?;
959         for ty in self.fields.iter() {
960             write!(f, " {ty}")?;
961         }
962         write!(f, ")")
963     }
964 }
965 
966 impl TypeTrace for WasmExnType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,967     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
968     where
969         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
970     {
971         func(self.func_ty)?;
972         for f in self.fields.iter() {
973             f.trace(func)?;
974         }
975         Ok(())
976     }
977 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,978     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
979     where
980         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
981     {
982         func(&mut self.func_ty)?;
983         for f in self.fields.iter_mut() {
984             f.trace_mut(func)?;
985         }
986         Ok(())
987     }
988 }
989 
990 /// Represents storage types introduced in the GC spec for array and struct fields.
991 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
992 pub enum WasmStorageType {
993     /// The storage type is i8.
994     I8,
995     /// The storage type is i16.
996     I16,
997     /// The storage type is a value type.
998     Val(WasmValType),
999 }
1000 
1001 impl fmt::Display for WasmStorageType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1002     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1003         match self {
1004             WasmStorageType::I8 => write!(f, "i8"),
1005             WasmStorageType::I16 => write!(f, "i16"),
1006             WasmStorageType::Val(v) => fmt::Display::fmt(v, f),
1007         }
1008     }
1009 }
1010 
1011 impl TypeTrace for WasmStorageType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1012     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1013     where
1014         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1015     {
1016         match self {
1017             WasmStorageType::I8 | WasmStorageType::I16 => Ok(()),
1018             WasmStorageType::Val(v) => v.trace(func),
1019         }
1020     }
1021 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1022     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1023     where
1024         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1025     {
1026         match self {
1027             WasmStorageType::I8 | WasmStorageType::I16 => Ok(()),
1028             WasmStorageType::Val(v) => v.trace_mut(func),
1029         }
1030     }
1031 }
1032 
1033 impl WasmStorageType {
1034     /// Is this a type that is represented as a `VMGcRef` and is additionally
1035     /// not an `i31`?
1036     ///
1037     /// That is, is this a type that actually refers to an object allocated in a
1038     /// GC heap?
is_vmgcref_type_and_not_i31(&self) -> bool1039     pub fn is_vmgcref_type_and_not_i31(&self) -> bool {
1040         match self {
1041             WasmStorageType::I8 | WasmStorageType::I16 => false,
1042             WasmStorageType::Val(v) => v.is_vmgcref_type_and_not_i31(),
1043         }
1044     }
1045 }
1046 
1047 /// The type of a struct field or array element.
1048 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
1049 pub struct WasmFieldType {
1050     /// The field's element type.
1051     pub element_type: WasmStorageType,
1052 
1053     /// Whether this field can be mutated or not.
1054     pub mutable: bool,
1055 }
1056 
1057 impl TryClone for WasmFieldType {
try_clone(&self) -> Result<Self, OutOfMemory>1058     fn try_clone(&self) -> Result<Self, OutOfMemory> {
1059         Ok(*self)
1060     }
1061 }
1062 
1063 impl fmt::Display for WasmFieldType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1064     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1065         if self.mutable {
1066             write!(f, "(mut {})", self.element_type)
1067         } else {
1068             fmt::Display::fmt(&self.element_type, f)
1069         }
1070     }
1071 }
1072 
1073 impl TypeTrace for WasmFieldType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1074     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1075     where
1076         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1077     {
1078         self.element_type.trace(func)
1079     }
1080 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1081     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1082     where
1083         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1084     {
1085         self.element_type.trace_mut(func)
1086     }
1087 }
1088 
1089 /// A concrete array type.
1090 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
1091 pub struct WasmArrayType(pub WasmFieldType);
1092 
1093 impl fmt::Display for WasmArrayType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1094     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1095         write!(f, "(array {})", self.0)
1096     }
1097 }
1098 
1099 impl TypeTrace for WasmArrayType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1100     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1101     where
1102         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1103     {
1104         self.0.trace(func)
1105     }
1106 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1107     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1108     where
1109         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1110     {
1111         self.0.trace_mut(func)
1112     }
1113 }
1114 
1115 /// A concrete struct type.
1116 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
1117 pub struct WasmStructType {
1118     /// The fields that make up this struct type.
1119     pub fields: Box<[WasmFieldType]>,
1120 }
1121 
1122 impl TryClone for WasmStructType {
try_clone(&self) -> Result<Self, OutOfMemory>1123     fn try_clone(&self) -> Result<Self, OutOfMemory> {
1124         Ok(Self {
1125             fields: self.fields.try_clone()?,
1126         })
1127     }
1128 }
1129 
1130 impl fmt::Display for WasmStructType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1131     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1132         write!(f, "(struct")?;
1133         for ty in self.fields.iter() {
1134             write!(f, " {ty}")?;
1135         }
1136         write!(f, ")")
1137     }
1138 }
1139 
1140 impl TypeTrace for WasmStructType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1141     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1142     where
1143         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1144     {
1145         for f in self.fields.iter() {
1146             f.trace(func)?;
1147         }
1148         Ok(())
1149     }
1150 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1151     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1152     where
1153         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1154     {
1155         for f in self.fields.iter_mut() {
1156             f.trace_mut(func)?;
1157         }
1158         Ok(())
1159     }
1160 }
1161 
1162 #[derive(Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1163 #[expect(missing_docs, reason = "self-describing type")]
1164 pub struct WasmCompositeType {
1165     /// The type defined inside the composite type.
1166     pub inner: WasmCompositeInnerType,
1167     /// Is the composite type shared? This is part of the
1168     /// shared-everything-threads proposal.
1169     pub shared: bool,
1170 }
1171 
1172 impl TryClone for WasmCompositeType {
try_clone(&self) -> Result<Self, OutOfMemory>1173     fn try_clone(&self) -> Result<Self, OutOfMemory> {
1174         Ok(Self {
1175             inner: self.inner.try_clone()?,
1176             shared: self.shared,
1177         })
1178     }
1179 }
1180 
1181 impl fmt::Display for WasmCompositeType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1182     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1183         if self.shared {
1184             write!(f, "(shared ")?;
1185         }
1186         fmt::Display::fmt(&self.inner, f)?;
1187         if self.shared {
1188             write!(f, ")")?;
1189         }
1190         Ok(())
1191     }
1192 }
1193 
1194 /// A function, array, or struct type.
1195 #[derive(Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1196 #[expect(missing_docs, reason = "self-describing variants")]
1197 pub enum WasmCompositeInnerType {
1198     Array(WasmArrayType),
1199     Func(WasmFuncType),
1200     Struct(WasmStructType),
1201     Cont(WasmContType),
1202     Exn(WasmExnType),
1203 }
1204 
1205 impl TryClone for WasmCompositeInnerType {
try_clone(&self) -> Result<Self, OutOfMemory>1206     fn try_clone(&self) -> Result<Self, OutOfMemory> {
1207         Ok(match self {
1208             Self::Array(ty) => Self::Array(*ty),
1209             Self::Func(ty) => Self::Func(ty.try_clone()?),
1210             Self::Struct(ty) => Self::Struct(ty.try_clone()?),
1211             Self::Cont(ty) => Self::Cont(*ty),
1212             Self::Exn(ty) => Self::Exn(ty.try_clone()?),
1213         })
1214     }
1215 }
1216 
1217 impl fmt::Display for WasmCompositeInnerType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1218     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1219         match self {
1220             Self::Array(ty) => fmt::Display::fmt(ty, f),
1221             Self::Func(ty) => fmt::Display::fmt(ty, f),
1222             Self::Struct(ty) => fmt::Display::fmt(ty, f),
1223             Self::Cont(ty) => fmt::Display::fmt(ty, f),
1224             Self::Exn(ty) => fmt::Display::fmt(ty, f),
1225         }
1226     }
1227 }
1228 
1229 #[expect(missing_docs, reason = "self-describing functions")]
1230 impl WasmCompositeInnerType {
1231     #[inline]
is_array(&self) -> bool1232     pub fn is_array(&self) -> bool {
1233         matches!(self, Self::Array(_))
1234     }
1235 
1236     #[inline]
as_array(&self) -> Option<&WasmArrayType>1237     pub fn as_array(&self) -> Option<&WasmArrayType> {
1238         match self {
1239             Self::Array(f) => Some(f),
1240             _ => None,
1241         }
1242     }
1243 
1244     #[inline]
unwrap_array(&self) -> &WasmArrayType1245     pub fn unwrap_array(&self) -> &WasmArrayType {
1246         self.as_array().unwrap()
1247     }
1248 
1249     #[inline]
is_func(&self) -> bool1250     pub fn is_func(&self) -> bool {
1251         matches!(self, Self::Func(_))
1252     }
1253 
1254     #[inline]
as_func(&self) -> Option<&WasmFuncType>1255     pub fn as_func(&self) -> Option<&WasmFuncType> {
1256         match self {
1257             Self::Func(f) => Some(f),
1258             _ => None,
1259         }
1260     }
1261 
1262     #[inline]
unwrap_func(&self) -> &WasmFuncType1263     pub fn unwrap_func(&self) -> &WasmFuncType {
1264         self.as_func().unwrap()
1265     }
1266 
1267     #[inline]
is_struct(&self) -> bool1268     pub fn is_struct(&self) -> bool {
1269         matches!(self, Self::Struct(_))
1270     }
1271 
1272     #[inline]
as_struct(&self) -> Option<&WasmStructType>1273     pub fn as_struct(&self) -> Option<&WasmStructType> {
1274         match self {
1275             Self::Struct(f) => Some(f),
1276             _ => None,
1277         }
1278     }
1279 
1280     #[inline]
unwrap_struct(&self) -> &WasmStructType1281     pub fn unwrap_struct(&self) -> &WasmStructType {
1282         self.as_struct().unwrap()
1283     }
1284 
1285     #[inline]
is_cont(&self) -> bool1286     pub fn is_cont(&self) -> bool {
1287         matches!(self, Self::Cont(_))
1288     }
1289 
1290     #[inline]
as_cont(&self) -> Option<&WasmContType>1291     pub fn as_cont(&self) -> Option<&WasmContType> {
1292         match self {
1293             Self::Cont(f) => Some(f),
1294             _ => None,
1295         }
1296     }
1297 
1298     #[inline]
unwrap_cont(&self) -> &WasmContType1299     pub fn unwrap_cont(&self) -> &WasmContType {
1300         self.as_cont().unwrap()
1301     }
1302 
1303     #[inline]
is_exn(&self) -> bool1304     pub fn is_exn(&self) -> bool {
1305         matches!(self, Self::Exn(_))
1306     }
1307 
1308     #[inline]
as_exn(&self) -> Option<&WasmExnType>1309     pub fn as_exn(&self) -> Option<&WasmExnType> {
1310         match self {
1311             Self::Exn(f) => Some(f),
1312             _ => None,
1313         }
1314     }
1315 
1316     #[inline]
unwrap_exn(&self) -> &WasmExnType1317     pub fn unwrap_exn(&self) -> &WasmExnType {
1318         self.as_exn().unwrap()
1319     }
1320 }
1321 
1322 impl TypeTrace for WasmCompositeType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1323     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1324     where
1325         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1326     {
1327         match &self.inner {
1328             WasmCompositeInnerType::Array(a) => a.trace(func),
1329             WasmCompositeInnerType::Func(f) => f.trace(func),
1330             WasmCompositeInnerType::Struct(a) => a.trace(func),
1331             WasmCompositeInnerType::Cont(c) => c.trace(func),
1332             WasmCompositeInnerType::Exn(e) => e.trace(func),
1333         }
1334     }
1335 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1336     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1337     where
1338         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1339     {
1340         match &mut self.inner {
1341             WasmCompositeInnerType::Array(a) => a.trace_mut(func),
1342             WasmCompositeInnerType::Func(f) => f.trace_mut(func),
1343             WasmCompositeInnerType::Struct(a) => a.trace_mut(func),
1344             WasmCompositeInnerType::Cont(c) => c.trace_mut(func),
1345             WasmCompositeInnerType::Exn(e) => e.trace_mut(func),
1346         }
1347     }
1348 }
1349 
1350 /// A concrete, user-defined (or host-defined) Wasm type.
1351 #[derive(Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1352 pub struct WasmSubType {
1353     /// Whether this type is forbidden from being the supertype of any other
1354     /// type.
1355     pub is_final: bool,
1356 
1357     /// This type's supertype, if any.
1358     pub supertype: Option<EngineOrModuleTypeIndex>,
1359 
1360     /// The array, function, or struct that is defined.
1361     pub composite_type: WasmCompositeType,
1362 }
1363 
1364 impl TryClone for WasmSubType {
try_clone(&self) -> Result<Self, OutOfMemory>1365     fn try_clone(&self) -> Result<Self, OutOfMemory> {
1366         Ok(Self {
1367             is_final: self.is_final,
1368             supertype: self.supertype,
1369             composite_type: self.composite_type.try_clone()?,
1370         })
1371     }
1372 }
1373 
1374 impl fmt::Display for WasmSubType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1375     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1376         if self.is_final && self.supertype.is_none() {
1377             fmt::Display::fmt(&self.composite_type, f)
1378         } else {
1379             write!(f, "(sub")?;
1380             if self.is_final {
1381                 write!(f, " final")?;
1382             }
1383             if let Some(sup) = self.supertype {
1384                 write!(f, " {sup}")?;
1385             }
1386             write!(f, " {})", self.composite_type)
1387         }
1388     }
1389 }
1390 
1391 /// Implicitly define all of these helper functions to handle only unshared
1392 /// types; essentially, these act like `is_unshared_*` functions until shared
1393 /// support is implemented.
1394 #[expect(missing_docs, reason = "self-describing functions")]
1395 impl WasmSubType {
1396     #[inline]
is_func(&self) -> bool1397     pub fn is_func(&self) -> bool {
1398         self.composite_type.inner.is_func() && !self.composite_type.shared
1399     }
1400 
1401     #[inline]
as_func(&self) -> Option<&WasmFuncType>1402     pub fn as_func(&self) -> Option<&WasmFuncType> {
1403         if self.composite_type.shared {
1404             None
1405         } else {
1406             self.composite_type.inner.as_func()
1407         }
1408     }
1409 
1410     #[inline]
unwrap_func(&self) -> &WasmFuncType1411     pub fn unwrap_func(&self) -> &WasmFuncType {
1412         assert!(!self.composite_type.shared);
1413         self.composite_type.inner.unwrap_func()
1414     }
1415 
1416     #[inline]
is_array(&self) -> bool1417     pub fn is_array(&self) -> bool {
1418         self.composite_type.inner.is_array() && !self.composite_type.shared
1419     }
1420 
1421     #[inline]
as_array(&self) -> Option<&WasmArrayType>1422     pub fn as_array(&self) -> Option<&WasmArrayType> {
1423         if self.composite_type.shared {
1424             None
1425         } else {
1426             self.composite_type.inner.as_array()
1427         }
1428     }
1429 
1430     #[inline]
unwrap_array(&self) -> &WasmArrayType1431     pub fn unwrap_array(&self) -> &WasmArrayType {
1432         assert!(!self.composite_type.shared);
1433         self.composite_type.inner.unwrap_array()
1434     }
1435 
1436     #[inline]
is_struct(&self) -> bool1437     pub fn is_struct(&self) -> bool {
1438         self.composite_type.inner.is_struct() && !self.composite_type.shared
1439     }
1440 
1441     #[inline]
as_struct(&self) -> Option<&WasmStructType>1442     pub fn as_struct(&self) -> Option<&WasmStructType> {
1443         if self.composite_type.shared {
1444             None
1445         } else {
1446             self.composite_type.inner.as_struct()
1447         }
1448     }
1449 
1450     #[inline]
unwrap_struct(&self) -> &WasmStructType1451     pub fn unwrap_struct(&self) -> &WasmStructType {
1452         assert!(!self.composite_type.shared);
1453         self.composite_type.inner.unwrap_struct()
1454     }
1455 
1456     #[inline]
is_cont(&self) -> bool1457     pub fn is_cont(&self) -> bool {
1458         self.composite_type.inner.is_cont() && !self.composite_type.shared
1459     }
1460 
1461     #[inline]
as_cont(&self) -> Option<&WasmContType>1462     pub fn as_cont(&self) -> Option<&WasmContType> {
1463         if self.composite_type.shared {
1464             None
1465         } else {
1466             self.composite_type.inner.as_cont()
1467         }
1468     }
1469 
1470     #[inline]
unwrap_cont(&self) -> &WasmContType1471     pub fn unwrap_cont(&self) -> &WasmContType {
1472         assert!(!self.composite_type.shared);
1473         self.composite_type.inner.unwrap_cont()
1474     }
1475 
1476     #[inline]
is_exn(&self) -> bool1477     pub fn is_exn(&self) -> bool {
1478         self.composite_type.inner.is_exn() && !self.composite_type.shared
1479     }
1480 
1481     #[inline]
as_exn(&self) -> Option<&WasmExnType>1482     pub fn as_exn(&self) -> Option<&WasmExnType> {
1483         if self.composite_type.shared {
1484             None
1485         } else {
1486             self.composite_type.inner.as_exn()
1487         }
1488     }
1489 
1490     #[inline]
unwrap_exn(&self) -> &WasmExnType1491     pub fn unwrap_exn(&self) -> &WasmExnType {
1492         assert!(!self.composite_type.shared);
1493         self.composite_type.inner.unwrap_exn()
1494     }
1495 }
1496 
1497 impl TypeTrace for WasmSubType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1498     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1499     where
1500         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1501     {
1502         if let Some(sup) = self.supertype {
1503             func(sup)?;
1504         }
1505         self.composite_type.trace(func)
1506     }
1507 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1508     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1509     where
1510         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1511     {
1512         if let Some(sup) = self.supertype.as_mut() {
1513             func(sup)?;
1514         }
1515         self.composite_type.trace_mut(func)
1516     }
1517 }
1518 
1519 /// A recursive type group.
1520 ///
1521 /// Types within a recgroup can have forward references to each other, which
1522 /// allows for cyclic types, for example a function `$f` that returns a
1523 /// reference to a function `$g` which returns a reference to a function `$f`:
1524 ///
1525 /// ```ignore
1526 /// (rec (type (func $f (result (ref null $g))))
1527 ///      (type (func $g (result (ref null $f)))))
1528 /// ```
1529 #[derive(Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
1530 pub struct WasmRecGroup {
1531     /// The types inside of this recgroup.
1532     pub types: Box<[WasmSubType]>,
1533 }
1534 
1535 impl TypeTrace for WasmRecGroup {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1536     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1537     where
1538         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1539     {
1540         for ty in self.types.iter() {
1541             ty.trace(func)?;
1542         }
1543         Ok(())
1544     }
1545 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1546     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1547     where
1548         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1549     {
1550         for ty in self.types.iter_mut() {
1551             ty.trace_mut(func)?;
1552         }
1553         Ok(())
1554     }
1555 }
1556 
1557 macro_rules! entity_impl_with_try_clone {
1558     ( $ty:ident ) => {
1559         cranelift_entity::entity_impl!($ty);
1560 
1561         impl TryClone for $ty {
1562             #[inline]
1563             fn try_clone(&self) -> Result<Self, $crate::error::OutOfMemory> {
1564                 Ok(*self)
1565             }
1566         }
1567     };
1568 }
1569 
1570 /// Index type of a function (imported or defined) inside the WebAssembly module.
1571 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1572 pub struct FuncIndex(u32);
1573 entity_impl_with_try_clone!(FuncIndex);
1574 
1575 /// Index type of a defined function inside the WebAssembly module.
1576 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1577 pub struct DefinedFuncIndex(u32);
1578 entity_impl_with_try_clone!(DefinedFuncIndex);
1579 
1580 /// Index type of a defined table inside the WebAssembly module.
1581 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1582 pub struct DefinedTableIndex(u32);
1583 entity_impl_with_try_clone!(DefinedTableIndex);
1584 
1585 /// Index type of a defined memory inside the WebAssembly module.
1586 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1587 pub struct DefinedMemoryIndex(u32);
1588 entity_impl_with_try_clone!(DefinedMemoryIndex);
1589 
1590 /// Index type of a defined memory inside the WebAssembly module.
1591 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1592 pub struct OwnedMemoryIndex(u32);
1593 entity_impl_with_try_clone!(OwnedMemoryIndex);
1594 
1595 /// Index type of a defined global inside the WebAssembly module.
1596 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1597 pub struct DefinedGlobalIndex(u32);
1598 entity_impl_with_try_clone!(DefinedGlobalIndex);
1599 
1600 /// Index type of a table (imported or defined) inside the WebAssembly module.
1601 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1602 pub struct TableIndex(u32);
1603 entity_impl_with_try_clone!(TableIndex);
1604 
1605 /// Index type of a global variable (imported or defined) inside the WebAssembly module.
1606 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1607 pub struct GlobalIndex(u32);
1608 entity_impl_with_try_clone!(GlobalIndex);
1609 
1610 /// Index type of a linear memory (imported or defined) inside the WebAssembly module.
1611 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1612 pub struct MemoryIndex(u32);
1613 entity_impl_with_try_clone!(MemoryIndex);
1614 
1615 /// Index type of a canonicalized recursive type group inside a WebAssembly
1616 /// module (as opposed to canonicalized within the whole engine).
1617 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1618 pub struct ModuleInternedRecGroupIndex(u32);
1619 entity_impl_with_try_clone!(ModuleInternedRecGroupIndex);
1620 
1621 /// Index type of a canonicalized recursive type group inside the whole engine
1622 /// (as opposed to canonicalized within just a single Wasm module).
1623 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1624 pub struct EngineInternedRecGroupIndex(u32);
1625 entity_impl_with_try_clone!(EngineInternedRecGroupIndex);
1626 
1627 /// Index type of a type (imported or defined) inside the WebAssembly module.
1628 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1629 pub struct TypeIndex(u32);
1630 entity_impl_with_try_clone!(TypeIndex);
1631 
1632 /// A canonicalized type index referencing a type within a single recursion
1633 /// group from another type within that same recursion group.
1634 ///
1635 /// This is only suitable for use when hash consing and deduplicating rec
1636 /// groups.
1637 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1638 pub struct RecGroupRelativeTypeIndex(u32);
1639 entity_impl_with_try_clone!(RecGroupRelativeTypeIndex);
1640 
1641 /// A canonicalized type index for a type within a single WebAssembly module.
1642 ///
1643 /// Note that this is deduplicated only at the level of a single WebAssembly
1644 /// module, not at the level of a whole store or engine. This means that these
1645 /// indices are only unique within the context of a single Wasm module, and
1646 /// therefore are not suitable for runtime type checks (which, in general, may
1647 /// involve entities defined in different modules).
1648 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1649 pub struct ModuleInternedTypeIndex(u32);
1650 entity_impl_with_try_clone!(ModuleInternedTypeIndex);
1651 
1652 /// A canonicalized type index into an engine's shared type registry.
1653 ///
1654 /// This is canonicalized/deduped at the level of a whole engine, across all the
1655 /// modules loaded into that engine, not just at the level of a single
1656 /// particular module. This means that `VMSharedTypeIndex` is usable for
1657 /// e.g. checking that function signatures match during an indirect call
1658 /// (potentially to a function defined in a different module) at runtime.
1659 #[repr(transparent)] // Used directly by JIT code.
1660 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1661 pub struct VMSharedTypeIndex(u32);
1662 entity_impl_with_try_clone!(VMSharedTypeIndex);
1663 
1664 impl VMSharedTypeIndex {
1665     /// Create a new `VMSharedTypeIndex`.
1666     #[inline]
new(value: u32) -> Self1667     pub fn new(value: u32) -> Self {
1668         assert_ne!(
1669             value,
1670             u32::MAX,
1671             "u32::MAX is reserved for the default value"
1672         );
1673         Self(value)
1674     }
1675 
1676     /// Returns the underlying bits of the index.
1677     #[inline]
bits(&self) -> u321678     pub fn bits(&self) -> u32 {
1679         self.0
1680     }
1681 }
1682 
1683 impl Default for VMSharedTypeIndex {
1684     #[inline]
default() -> Self1685     fn default() -> Self {
1686         Self(u32::MAX)
1687     }
1688 }
1689 
1690 /// Index type of a passive data segment inside the WebAssembly module.
1691 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1692 pub struct DataIndex(u32);
1693 entity_impl_with_try_clone!(DataIndex);
1694 
1695 /// Index type of a passive element segment inside the WebAssembly module.
1696 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1697 pub struct ElemIndex(u32);
1698 entity_impl_with_try_clone!(ElemIndex);
1699 
1700 /// Index type of a defined tag inside the WebAssembly module.
1701 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1702 pub struct DefinedTagIndex(u32);
1703 entity_impl_with_try_clone!(DefinedTagIndex);
1704 
1705 /// Index type of an event inside the WebAssembly module.
1706 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1707 pub struct TagIndex(u32);
1708 entity_impl_with_try_clone!(TagIndex);
1709 
1710 /// Index into the global list of modules found within an entire component.
1711 ///
1712 /// Module translations are saved on the side to get fully compiled after
1713 /// the original component has finished being translated.
1714 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1715 pub struct StaticModuleIndex(u32);
1716 entity_impl_with_try_clone!(StaticModuleIndex);
1717 
1718 /// An index of an entity.
1719 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1720 pub enum EntityIndex {
1721     /// Function index.
1722     Function(FuncIndex),
1723     /// Table index.
1724     Table(TableIndex),
1725     /// Memory index.
1726     Memory(MemoryIndex),
1727     /// Global index.
1728     Global(GlobalIndex),
1729     /// Tag index.
1730     Tag(TagIndex),
1731 }
1732 
1733 impl From<FuncIndex> for EntityIndex {
from(idx: FuncIndex) -> EntityIndex1734     fn from(idx: FuncIndex) -> EntityIndex {
1735         EntityIndex::Function(idx)
1736     }
1737 }
1738 
1739 impl From<TableIndex> for EntityIndex {
from(idx: TableIndex) -> EntityIndex1740     fn from(idx: TableIndex) -> EntityIndex {
1741         EntityIndex::Table(idx)
1742     }
1743 }
1744 
1745 impl From<MemoryIndex> for EntityIndex {
from(idx: MemoryIndex) -> EntityIndex1746     fn from(idx: MemoryIndex) -> EntityIndex {
1747         EntityIndex::Memory(idx)
1748     }
1749 }
1750 
1751 impl From<GlobalIndex> for EntityIndex {
from(idx: GlobalIndex) -> EntityIndex1752     fn from(idx: GlobalIndex) -> EntityIndex {
1753         EntityIndex::Global(idx)
1754     }
1755 }
1756 
1757 impl From<TagIndex> for EntityIndex {
from(idx: TagIndex) -> EntityIndex1758     fn from(idx: TagIndex) -> EntityIndex {
1759         EntityIndex::Tag(idx)
1760     }
1761 }
1762 
1763 /// A type of an item in a wasm module where an item is typically something that
1764 /// can be exported.
1765 #[derive(Clone, Debug, Serialize, Deserialize)]
1766 pub enum EntityType {
1767     /// A global variable with the specified content type
1768     Global(Global),
1769     /// A linear memory with the specified limits
1770     Memory(Memory),
1771     /// An exception and control tag definition.
1772     Tag(Tag),
1773     /// A table with the specified element type and limits
1774     Table(Table),
1775     /// A function type where the index points to the type section and records a
1776     /// function signature.
1777     Function(EngineOrModuleTypeIndex),
1778 }
1779 
1780 impl TypeTrace for EntityType {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1781     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1782     where
1783         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1784     {
1785         match self {
1786             Self::Global(g) => g.trace(func),
1787             Self::Table(t) => t.trace(func),
1788             Self::Function(idx) => func(*idx),
1789             Self::Memory(_) => Ok(()),
1790             Self::Tag(t) => t.trace(func),
1791         }
1792     }
1793 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1794     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1795     where
1796         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1797     {
1798         match self {
1799             Self::Global(g) => g.trace_mut(func),
1800             Self::Table(t) => t.trace_mut(func),
1801             Self::Function(idx) => func(idx),
1802             Self::Memory(_) => Ok(()),
1803             Self::Tag(t) => t.trace_mut(func),
1804         }
1805     }
1806 }
1807 
1808 impl EntityType {
1809     /// Assert that this entity is a global
unwrap_global(&self) -> &Global1810     pub fn unwrap_global(&self) -> &Global {
1811         match self {
1812             EntityType::Global(g) => g,
1813             _ => panic!("not a global"),
1814         }
1815     }
1816 
1817     /// Assert that this entity is a memory
unwrap_memory(&self) -> &Memory1818     pub fn unwrap_memory(&self) -> &Memory {
1819         match self {
1820             EntityType::Memory(g) => g,
1821             _ => panic!("not a memory"),
1822         }
1823     }
1824 
1825     /// Assert that this entity is a tag
unwrap_tag(&self) -> &Tag1826     pub fn unwrap_tag(&self) -> &Tag {
1827         match self {
1828             EntityType::Tag(g) => g,
1829             _ => panic!("not a tag"),
1830         }
1831     }
1832 
1833     /// Assert that this entity is a table
unwrap_table(&self) -> &Table1834     pub fn unwrap_table(&self) -> &Table {
1835         match self {
1836             EntityType::Table(g) => g,
1837             _ => panic!("not a table"),
1838         }
1839     }
1840 
1841     /// Assert that this entity is a function
unwrap_func(&self) -> EngineOrModuleTypeIndex1842     pub fn unwrap_func(&self) -> EngineOrModuleTypeIndex {
1843         match self {
1844             EntityType::Function(g) => *g,
1845             _ => panic!("not a func"),
1846         }
1847     }
1848 }
1849 
1850 /// A WebAssembly global.
1851 ///
1852 /// Note that we record both the original Wasm type and the Cranelift IR type
1853 /// used to represent it. This is because multiple different kinds of Wasm types
1854 /// might be represented with the same Cranelift IR type. For example, both a
1855 /// Wasm `i64` and a `funcref` might be represented with a Cranelift `i64` on
1856 /// 64-bit architectures, and when GC is not required for func refs.
1857 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
1858 pub struct Global {
1859     /// The Wasm type of the value stored in the global.
1860     pub wasm_ty: crate::WasmValType,
1861     /// A flag indicating whether the value may change at runtime.
1862     pub mutability: bool,
1863 }
1864 
1865 impl TypeTrace for Global {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,1866     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
1867     where
1868         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
1869     {
1870         let Global {
1871             wasm_ty,
1872             mutability: _,
1873         } = self;
1874         wasm_ty.trace(func)
1875     }
1876 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,1877     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
1878     where
1879         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
1880     {
1881         let Global {
1882             wasm_ty,
1883             mutability: _,
1884         } = self;
1885         wasm_ty.trace_mut(func)
1886     }
1887 }
1888 
1889 /// A constant expression.
1890 ///
1891 /// These are used to initialize globals, table elements, etc...
1892 #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1893 pub struct ConstExpr {
1894     ops: SmallVec<[ConstOp; 2]>,
1895 }
1896 
1897 impl ConstExpr {
1898     /// Create a new const expression from the given opcodes.
1899     ///
1900     /// Does not do any validation that the const expression is well-typed.
1901     ///
1902     /// Panics if given zero opcodes.
new(ops: impl IntoIterator<Item = ConstOp>) -> Self1903     pub fn new(ops: impl IntoIterator<Item = ConstOp>) -> Self {
1904         let ops = ops.into_iter().collect::<SmallVec<[ConstOp; 2]>>();
1905         assert!(!ops.is_empty());
1906         ConstExpr { ops }
1907     }
1908 
1909     /// Create a new const expression from a `wasmparser` const expression.
1910     ///
1911     /// Returns the new const expression as well as the escaping function
1912     /// indices that appeared in `ref.func` instructions, if any.
from_wasmparser( env: &dyn TypeConvert, expr: wasmparser::ConstExpr<'_>, ) -> WasmResult<(Self, SmallVec<[FuncIndex; 1]>)>1913     pub fn from_wasmparser(
1914         env: &dyn TypeConvert,
1915         expr: wasmparser::ConstExpr<'_>,
1916     ) -> WasmResult<(Self, SmallVec<[FuncIndex; 1]>)> {
1917         let mut iter = expr
1918             .get_operators_reader()
1919             .into_iter_with_offsets()
1920             .peekable();
1921 
1922         let mut ops = SmallVec::<[ConstOp; 2]>::new();
1923         let mut escaped = SmallVec::<[FuncIndex; 1]>::new();
1924         while let Some(res) = iter.next() {
1925             let (op, offset) = res?;
1926 
1927             // If we reach an `end` instruction, and there are no more
1928             // instructions after that, then we are done reading this const
1929             // expression.
1930             if matches!(op, wasmparser::Operator::End) && iter.peek().is_none() {
1931                 break;
1932             }
1933 
1934             // Track any functions that appear in `ref.func` so that callers can
1935             // make sure to flag them as escaping.
1936             if let wasmparser::Operator::RefFunc { function_index } = &op {
1937                 escaped.push(FuncIndex::from_u32(*function_index));
1938             }
1939 
1940             ops.push(ConstOp::from_wasmparser(env, op, offset)?);
1941         }
1942         Ok((Self { ops }, escaped))
1943     }
1944 
1945     /// Get the opcodes that make up this const expression.
1946     #[inline]
ops(&self) -> &[ConstOp]1947     pub fn ops(&self) -> &[ConstOp] {
1948         &self.ops
1949     }
1950 
1951     /// Is this ConstExpr a provably nonzero integer value?
1952     ///
1953     /// This must be conservative: if the expression *might* be zero,
1954     /// it must return `false`. It is always allowed to return `false`
1955     /// for some expression kind that we don't support. However, if it
1956     /// returns `true`, the expression must be actually nonzero.
1957     ///
1958     /// We use this for certain table optimizations that rely on
1959     /// knowing for sure that index 0 is not referenced.
provably_nonzero_i32(&self) -> bool1960     pub fn provably_nonzero_i32(&self) -> bool {
1961         match self.const_eval() {
1962             Some(GlobalConstValue::I32(x)) => x != 0,
1963 
1964             // Conservatively return `false` for non-const-eval-able expressions
1965             // as well as everything else.
1966             _ => false,
1967         }
1968     }
1969 
1970     /// Attempt to evaluate the given const-expr at compile time.
const_eval(&self) -> Option<GlobalConstValue>1971     pub fn const_eval(&self) -> Option<GlobalConstValue> {
1972         // TODO: Actually maintain an evaluation stack and handle `i32.add`,
1973         // `i32.sub`, etc... const ops.
1974         match self.ops() {
1975             [ConstOp::I32Const(x)] => Some(GlobalConstValue::I32(*x)),
1976             [ConstOp::I64Const(x)] => Some(GlobalConstValue::I64(*x)),
1977             [ConstOp::F32Const(x)] => Some(GlobalConstValue::F32(*x)),
1978             [ConstOp::F64Const(x)] => Some(GlobalConstValue::F64(*x)),
1979             [ConstOp::V128Const(x)] => Some(GlobalConstValue::V128(*x)),
1980             _ => None,
1981         }
1982     }
1983 }
1984 
1985 /// A global's constant value, known at compile time.
1986 #[expect(missing_docs, reason = "self-describing variants")]
1987 #[derive(Clone, Copy)]
1988 pub enum GlobalConstValue {
1989     I32(i32),
1990     I64(i64),
1991     F32(u32),
1992     F64(u64),
1993     V128(u128),
1994 }
1995 
1996 /// The subset of Wasm opcodes that are constant.
1997 #[expect(missing_docs, reason = "self-describing variants")]
1998 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1999 pub enum ConstOp {
2000     I32Const(i32),
2001     I64Const(i64),
2002     F32Const(u32),
2003     F64Const(u64),
2004     V128Const(u128),
2005     GlobalGet(GlobalIndex),
2006     RefI31,
2007     RefNull(WasmHeapTopType),
2008     RefFunc(FuncIndex),
2009     I32Add,
2010     I32Sub,
2011     I32Mul,
2012     I64Add,
2013     I64Sub,
2014     I64Mul,
2015     StructNew {
2016         struct_type_index: TypeIndex,
2017     },
2018     StructNewDefault {
2019         struct_type_index: TypeIndex,
2020     },
2021     ArrayNew {
2022         array_type_index: TypeIndex,
2023     },
2024     ArrayNewDefault {
2025         array_type_index: TypeIndex,
2026     },
2027     ArrayNewFixed {
2028         array_type_index: TypeIndex,
2029         array_size: u32,
2030     },
2031     ExternConvertAny,
2032     AnyConvertExtern,
2033 }
2034 
2035 impl ConstOp {
2036     /// Convert a `wasmparser::Operator` to a `ConstOp`.
from_wasmparser( env: &dyn TypeConvert, op: wasmparser::Operator<'_>, offset: usize, ) -> WasmResult<Self>2037     pub fn from_wasmparser(
2038         env: &dyn TypeConvert,
2039         op: wasmparser::Operator<'_>,
2040         offset: usize,
2041     ) -> WasmResult<Self> {
2042         use wasmparser::Operator as O;
2043         Ok(match op {
2044             O::I32Const { value } => Self::I32Const(value),
2045             O::I64Const { value } => Self::I64Const(value),
2046             O::F32Const { value } => Self::F32Const(value.bits()),
2047             O::F64Const { value } => Self::F64Const(value.bits()),
2048             O::V128Const { value } => Self::V128Const(u128::from_le_bytes(*value.bytes())),
2049             O::RefNull { hty } => Self::RefNull(env.convert_heap_type(hty)?.top()),
2050             O::RefFunc { function_index } => Self::RefFunc(FuncIndex::from_u32(function_index)),
2051             O::GlobalGet { global_index } => Self::GlobalGet(GlobalIndex::from_u32(global_index)),
2052             O::RefI31 => Self::RefI31,
2053             O::I32Add => Self::I32Add,
2054             O::I32Sub => Self::I32Sub,
2055             O::I32Mul => Self::I32Mul,
2056             O::I64Add => Self::I64Add,
2057             O::I64Sub => Self::I64Sub,
2058             O::I64Mul => Self::I64Mul,
2059             O::StructNew { struct_type_index } => Self::StructNew {
2060                 struct_type_index: TypeIndex::from_u32(struct_type_index),
2061             },
2062             O::StructNewDefault { struct_type_index } => Self::StructNewDefault {
2063                 struct_type_index: TypeIndex::from_u32(struct_type_index),
2064             },
2065             O::ArrayNew { array_type_index } => Self::ArrayNew {
2066                 array_type_index: TypeIndex::from_u32(array_type_index),
2067             },
2068             O::ArrayNewDefault { array_type_index } => Self::ArrayNewDefault {
2069                 array_type_index: TypeIndex::from_u32(array_type_index),
2070             },
2071             O::ArrayNewFixed {
2072                 array_type_index,
2073                 array_size,
2074             } => Self::ArrayNewFixed {
2075                 array_type_index: TypeIndex::from_u32(array_type_index),
2076                 array_size,
2077             },
2078             O::ExternConvertAny => Self::ExternConvertAny,
2079             O::AnyConvertExtern => Self::AnyConvertExtern,
2080             op => {
2081                 return Err(wasm_unsupported!(
2082                     "unsupported opcode in const expression at offset {offset:#x}: {op:?}",
2083                 ));
2084             }
2085         })
2086     }
2087 }
2088 
2089 /// The type that can be used to index into [Memory] and [Table].
2090 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2091 #[expect(missing_docs, reason = "self-describing variants")]
2092 pub enum IndexType {
2093     I32,
2094     I64,
2095 }
2096 
2097 /// The size range of resizeable storage associated with [Memory] types and [Table] types.
2098 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2099 #[expect(missing_docs, reason = "self-describing fields")]
2100 pub struct Limits {
2101     pub min: u64,
2102     pub max: Option<u64>,
2103 }
2104 
2105 /// WebAssembly table.
2106 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2107 pub struct Table {
2108     /// The type of the index used to access the table.
2109     pub idx_type: IndexType,
2110     /// Tables are constrained by limits for their minimum and optionally maximum size.
2111     /// The limits are given in numbers of entries.
2112     pub limits: Limits,
2113     /// The table elements' Wasm type.
2114     pub ref_type: WasmRefType,
2115 }
2116 
2117 impl TypeTrace for Table {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,2118     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
2119     where
2120         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
2121     {
2122         let Table {
2123             ref_type: wasm_ty,
2124             idx_type: _,
2125             limits: _,
2126         } = self;
2127         wasm_ty.trace(func)
2128     }
2129 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,2130     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
2131     where
2132         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
2133     {
2134         let Table {
2135             ref_type: wasm_ty,
2136             idx_type: _,
2137             limits: _,
2138         } = self;
2139         wasm_ty.trace_mut(func)
2140     }
2141 }
2142 
2143 /// WebAssembly linear memory.
2144 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2145 pub struct Memory {
2146     /// The type of the index used to access the memory.
2147     pub idx_type: IndexType,
2148     /// The limits constrain the minimum and optionally the maximum size of a memory.
2149     /// The limits are given in units of page size.
2150     pub limits: Limits,
2151     /// Whether the memory may be shared between multiple threads.
2152     pub shared: bool,
2153     /// The log2 of this memory's page size, in bytes.
2154     ///
2155     /// By default the page size is 64KiB (0x10000; 2**16; 1<<16; 65536) but the
2156     /// custom-page-sizes proposal allows opting into a page size of `1`.
2157     pub page_size_log2: u8,
2158 }
2159 
2160 /// Maximum size, in bytes, of 32-bit memories (4G)
2161 pub const WASM32_MAX_SIZE: u64 = 1 << 32;
2162 
2163 impl Memory {
2164     /// WebAssembly page sizes are 64KiB by default.
2165     pub const DEFAULT_PAGE_SIZE: u32 = 0x10000;
2166 
2167     /// WebAssembly page sizes are 64KiB (or `2**16`) by default.
2168     pub const DEFAULT_PAGE_SIZE_LOG2: u8 = {
2169         let log2 = 16;
2170         assert!(1 << log2 == Memory::DEFAULT_PAGE_SIZE);
2171         log2
2172     };
2173 
2174     /// Returns the minimum size, in bytes, that this memory must be.
2175     ///
2176     /// # Errors
2177     ///
2178     /// Returns an error if the calculation of the minimum size overflows the
2179     /// `u64` return type. This means that the memory can't be allocated but
2180     /// it's deferred to the caller to how to deal with that.
minimum_byte_size(&self) -> Result<u64, SizeOverflow>2181     pub fn minimum_byte_size(&self) -> Result<u64, SizeOverflow> {
2182         self.limits
2183             .min
2184             .checked_mul(self.page_size())
2185             .ok_or(SizeOverflow)
2186     }
2187 
2188     /// Returns the maximum size, in bytes, that this memory is allowed to be.
2189     ///
2190     /// Note that the return value here is not an `Option` despite the maximum
2191     /// size of a linear memory being optional in wasm. If a maximum size
2192     /// is not present in the memory's type then a maximum size is selected for
2193     /// it. For example the maximum size of a 32-bit memory is `1<<32`. The
2194     /// maximum size of a 64-bit linear memory is chosen to be a value that
2195     /// won't ever be allowed at runtime.
2196     ///
2197     /// # Errors
2198     ///
2199     /// Returns an error if the calculation of the maximum size overflows the
2200     /// `u64` return type. This means that the memory can't be allocated but
2201     /// it's deferred to the caller to how to deal with that.
maximum_byte_size(&self) -> Result<u64, SizeOverflow>2202     pub fn maximum_byte_size(&self) -> Result<u64, SizeOverflow> {
2203         match self.limits.max {
2204             Some(max) => max.checked_mul(self.page_size()).ok_or(SizeOverflow),
2205             None => {
2206                 let min = self.minimum_byte_size()?;
2207                 Ok(min.max(self.max_size_based_on_index_type()))
2208             }
2209         }
2210     }
2211 
2212     /// Get the size of this memory's pages, in bytes.
page_size(&self) -> u642213     pub fn page_size(&self) -> u64 {
2214         debug_assert!(
2215             self.page_size_log2 == 16 || self.page_size_log2 == 0,
2216             "invalid page_size_log2: {}; must be 16 or 0",
2217             self.page_size_log2
2218         );
2219         1 << self.page_size_log2
2220     }
2221 
2222     /// Returns the maximum size memory is allowed to be only based on the
2223     /// index type used by this memory.
2224     ///
2225     /// For example 32-bit linear memories return `1<<32` from this method.
max_size_based_on_index_type(&self) -> u642226     pub fn max_size_based_on_index_type(&self) -> u64 {
2227         match self.idx_type {
2228             IndexType::I64 =>
2229             // Note that the true maximum size of a 64-bit linear memory, in
2230             // bytes, cannot be represented in a `u64`. That would require a u65
2231             // to store `1<<64`. Despite that no system can actually allocate a
2232             // full 64-bit linear memory so this is instead emulated as "what if
2233             // the kernel fit in a single Wasm page of linear memory". Shouldn't
2234             // ever actually be possible but it provides a number to serve as an
2235             // effective maximum.
2236             {
2237                 0_u64.wrapping_sub(self.page_size())
2238             }
2239             IndexType::I32 => WASM32_MAX_SIZE,
2240         }
2241     }
2242 
2243     /// Returns whether this memory can be implemented with virtual memory on
2244     /// a host with `host_page_size_log2`.
2245     ///
2246     /// When this function returns `true` then it means that signals such as
2247     /// SIGSEGV on the host are compatible with wasm and can be used to
2248     /// represent out-of-bounds memory accesses.
2249     ///
2250     /// When this function returns `false` then it means that this memory must,
2251     /// for example, have explicit bounds checks. This additionally means that
2252     /// virtual memory traps (e.g. SIGSEGV) cannot be relied on to implement
2253     /// linear memory semantics.
can_use_virtual_memory(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool2254     pub fn can_use_virtual_memory(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool {
2255         tunables.signals_based_traps && self.page_size_log2 >= host_page_size_log2
2256     }
2257 
2258     /// Returns whether this memory is a candidate for bounds check elision
2259     /// given the configuration and host page size.
2260     ///
2261     /// This function determines whether the given compilation configuration and
2262     /// hos enables possible bounds check elision for this memory. Bounds checks
2263     /// can only be elided if [`Memory::can_use_virtual_memory`] returns `true`
2264     /// for example but there are additionally requirements on the index size of
2265     /// this memory and the memory reservation in `tunables`.
2266     ///
2267     /// Currently the only case that supports bounds check elision is when all
2268     /// of these apply:
2269     ///
2270     /// * When [`Memory::can_use_virtual_memory`] returns `true`.
2271     /// * This is a 32-bit linear memory (e.g. not 64-bit)
2272     /// * `tunables.memory_reservation` is in excess of 4GiB
2273     ///
2274     /// In this situation all computable addresses fall within the reserved
2275     /// space (modulo static offsets factoring in guard pages) so bounds checks
2276     /// may be elidable.
can_elide_bounds_check(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool2277     pub fn can_elide_bounds_check(&self, tunables: &Tunables, host_page_size_log2: u8) -> bool {
2278         self.can_use_virtual_memory(tunables, host_page_size_log2)
2279             && self.idx_type == IndexType::I32
2280             && tunables.memory_reservation + tunables.memory_guard_size >= (1 << 32)
2281     }
2282 
2283     /// Returns the static size of this heap in bytes at runtime, if available.
2284     ///
2285     /// This is only computable when the minimum size equals the maximum size.
static_heap_size(&self) -> Option<u64>2286     pub fn static_heap_size(&self) -> Option<u64> {
2287         let min = self.minimum_byte_size().ok()?;
2288         let max = self.maximum_byte_size().ok()?;
2289         if min == max { Some(min) } else { None }
2290     }
2291 
2292     /// Returns whether or not the base pointer of this memory is allowed to be
2293     /// relocated at runtime.
2294     ///
2295     /// When this function returns `false` then it means that after the initial
2296     /// allocation the base pointer is constant for the entire lifetime of a
2297     /// memory. This can enable compiler optimizations, for example.
memory_may_move(&self, tunables: &Tunables) -> bool2298     pub fn memory_may_move(&self, tunables: &Tunables) -> bool {
2299         // Shared memories cannot ever relocate their base pointer so the
2300         // settings configured in the engine must be appropriate for them ahead
2301         // of time.
2302         if self.shared {
2303             return false;
2304         }
2305 
2306         // If movement is disallowed in engine configuration, then the answer is
2307         // "no".
2308         if !tunables.memory_may_move {
2309             return false;
2310         }
2311 
2312         // If its minimum and maximum are the same, then the memory will never
2313         // be resized, and therefore will never move.
2314         if self.limits.max.is_some_and(|max| self.limits.min == max) {
2315             return false;
2316         }
2317 
2318         // If the maximum size of this memory is above the threshold of the
2319         // initial memory reservation then the memory may move.
2320         let max = self.maximum_byte_size().unwrap_or(u64::MAX);
2321         max > tunables.memory_reservation
2322     }
2323 
2324     /// Tests whether this memory type is allowed to grow up to `size` bytes.
2325     ///
2326     /// This is only applicable to custom-page-size memories which have a page
2327     /// size of a single byte. In that situation growth beyond `-1i32 as u32`
2328     /// bytes is not allowed because at that point memory growth succeeding and
2329     /// failing would be indistinguishable in the return value of `memory.grow`,
2330     /// for example. To handle this 32-bit memories are only allowed to grow to
2331     /// `-2i32 as u32`, for example, and 64-bit memories with a page size of 1
2332     /// are allowed to grow up to the maximum size.
allow_growth_to(&self, size: usize) -> bool2333     pub fn allow_growth_to(&self, size: usize) -> bool {
2334         if self.page_size_log2 != 0 {
2335             return true;
2336         }
2337         match self.idx_type {
2338             // For a 32-bit memory using 1-byte pages the last 2 bytes of the
2339             // 32-bit address space are addressable but disallowed for now.  A
2340             // memory that is 4GiB in size cannot report its size via
2341             // `memory.size`, and a memory that is 4GiB-1 bytes in size cannot
2342             // be distinguished when 1 byte is added from an allocation
2343             // failure.  To handle this the memory is capped at 4GiB-2 which
2344             // means that all memory-related instructions and such will have
2345             // unambiguous return codes.
2346             IndexType::I32 => size < 0xffff_ffff,
2347 
2348             // Assume that for a 64-bit memory using 1-byte pages it's going to
2349             // exhaust system resources before a limit is actually reached.
2350             IndexType::I64 => true,
2351         }
2352     }
2353 }
2354 
2355 #[derive(Copy, Clone, Debug)]
2356 #[expect(missing_docs, reason = "self-describing error struct")]
2357 pub struct SizeOverflow;
2358 
2359 impl fmt::Display for SizeOverflow {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result2360     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2361         f.write_str("size overflow calculating memory size")
2362     }
2363 }
2364 
2365 impl core::error::Error for SizeOverflow {}
2366 
2367 impl From<wasmparser::MemoryType> for Memory {
from(ty: wasmparser::MemoryType) -> Memory2368     fn from(ty: wasmparser::MemoryType) -> Memory {
2369         let idx_type = match ty.memory64 {
2370             false => IndexType::I32,
2371             true => IndexType::I64,
2372         };
2373         let limits = Limits {
2374             min: ty.initial,
2375             max: ty.maximum,
2376         };
2377         let page_size_log2 = u8::try_from(ty.page_size_log2.unwrap_or(16)).unwrap();
2378         debug_assert!(
2379             page_size_log2 == 16 || page_size_log2 == 0,
2380             "invalid page_size_log2: {page_size_log2}; must be 16 or 0"
2381         );
2382         Memory {
2383             idx_type,
2384             limits,
2385             shared: ty.shared,
2386             page_size_log2,
2387         }
2388     }
2389 }
2390 
2391 /// WebAssembly exception and control tag.
2392 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
2393 pub struct Tag {
2394     /// The tag signature type.
2395     pub signature: EngineOrModuleTypeIndex,
2396     /// The corresponding exception type.
2397     pub exception: EngineOrModuleTypeIndex,
2398 }
2399 
2400 impl TypeTrace for Tag {
trace<F, E>(&self, func: &mut F) -> Result<(), E> where F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,2401     fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
2402     where
2403         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
2404     {
2405         func(self.signature)?;
2406         func(self.exception)?;
2407         Ok(())
2408     }
2409 
trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E> where F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,2410     fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
2411     where
2412         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
2413     {
2414         func(&mut self.signature)?;
2415         func(&mut self.exception)?;
2416         Ok(())
2417     }
2418 }
2419 
2420 /// Helpers used to convert a `wasmparser` type to a type in this crate.
2421 #[expect(missing_docs, reason = "self-describing functions")]
2422 pub trait TypeConvert {
2423     /// Converts a wasmparser table type into a wasmtime type
convert_global_type(&self, ty: &wasmparser::GlobalType) -> WasmResult<Global>2424     fn convert_global_type(&self, ty: &wasmparser::GlobalType) -> WasmResult<Global> {
2425         Ok(Global {
2426             wasm_ty: self.convert_valtype(ty.content_type)?,
2427             mutability: ty.mutable,
2428         })
2429     }
2430 
2431     /// Converts a wasmparser table type into a wasmtime type
convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table>2432     fn convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table> {
2433         let idx_type = match ty.table64 {
2434             false => IndexType::I32,
2435             true => IndexType::I64,
2436         };
2437         let limits = Limits {
2438             min: ty.initial,
2439             max: ty.maximum,
2440         };
2441         Ok(Table {
2442             idx_type,
2443             limits,
2444             ref_type: self.convert_ref_type(ty.element_type)?,
2445         })
2446     }
2447 
convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType>2448     fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> {
2449         Ok(WasmSubType {
2450             is_final: ty.is_final,
2451             supertype: ty.supertype_idx.map(|i| self.lookup_type_index(i.unpack())),
2452             composite_type: self.convert_composite_type(&ty.composite_type)?,
2453         })
2454     }
2455 
convert_composite_type( &self, ty: &wasmparser::CompositeType, ) -> WasmResult<WasmCompositeType>2456     fn convert_composite_type(
2457         &self,
2458         ty: &wasmparser::CompositeType,
2459     ) -> WasmResult<WasmCompositeType> {
2460         let inner = match &ty.inner {
2461             wasmparser::CompositeInnerType::Func(f) => {
2462                 WasmCompositeInnerType::Func(self.convert_func_type(f)?)
2463             }
2464             wasmparser::CompositeInnerType::Array(a) => {
2465                 WasmCompositeInnerType::Array(self.convert_array_type(a)?)
2466             }
2467             wasmparser::CompositeInnerType::Struct(s) => {
2468                 WasmCompositeInnerType::Struct(self.convert_struct_type(s)?)
2469             }
2470             wasmparser::CompositeInnerType::Cont(c) => {
2471                 WasmCompositeInnerType::Cont(self.convert_cont_type(c))
2472             }
2473         };
2474         Ok(WasmCompositeType {
2475             inner,
2476             shared: ty.shared,
2477         })
2478     }
2479 
2480     /// Converts a wasmparser continuation type to a wasmtime type
convert_cont_type(&self, ty: &wasmparser::ContType) -> WasmContType2481     fn convert_cont_type(&self, ty: &wasmparser::ContType) -> WasmContType {
2482         if let WasmHeapType::ConcreteFunc(sigidx) = self.lookup_heap_type(ty.0.unpack()) {
2483             WasmContType::new(sigidx)
2484         } else {
2485             panic!("Failed to extract signature index for continuation type.")
2486         }
2487     }
2488 
convert_struct_type(&self, ty: &wasmparser::StructType) -> WasmResult<WasmStructType>2489     fn convert_struct_type(&self, ty: &wasmparser::StructType) -> WasmResult<WasmStructType> {
2490         Ok(WasmStructType {
2491             fields: ty
2492                 .fields
2493                 .iter()
2494                 .map(|f| self.convert_field_type(f))
2495                 .collect::<WasmResult<_>>()?,
2496         })
2497     }
2498 
convert_array_type(&self, ty: &wasmparser::ArrayType) -> WasmResult<WasmArrayType>2499     fn convert_array_type(&self, ty: &wasmparser::ArrayType) -> WasmResult<WasmArrayType> {
2500         Ok(WasmArrayType(self.convert_field_type(&ty.0)?))
2501     }
2502 
convert_field_type(&self, ty: &wasmparser::FieldType) -> WasmResult<WasmFieldType>2503     fn convert_field_type(&self, ty: &wasmparser::FieldType) -> WasmResult<WasmFieldType> {
2504         Ok(WasmFieldType {
2505             element_type: self.convert_storage_type(&ty.element_type)?,
2506             mutable: ty.mutable,
2507         })
2508     }
2509 
convert_storage_type(&self, ty: &wasmparser::StorageType) -> WasmResult<WasmStorageType>2510     fn convert_storage_type(&self, ty: &wasmparser::StorageType) -> WasmResult<WasmStorageType> {
2511         Ok(match ty {
2512             wasmparser::StorageType::I8 => WasmStorageType::I8,
2513             wasmparser::StorageType::I16 => WasmStorageType::I16,
2514             wasmparser::StorageType::Val(v) => WasmStorageType::Val(self.convert_valtype(*v)?),
2515         })
2516     }
2517 
2518     /// Converts a wasmparser function type to a wasmtime type
convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmResult<WasmFuncType>2519     fn convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmResult<WasmFuncType> {
2520         let params = ty
2521             .params()
2522             .iter()
2523             .map(|t| self.convert_valtype(*t))
2524             .collect::<WasmResult<Vec<_>>>()?;
2525         let results = ty
2526             .results()
2527             .iter()
2528             .map(|t| self.convert_valtype(*t))
2529             .collect::<WasmResult<Vec<_>>>()?;
2530         Ok(WasmFuncType::new(params, results).panic_on_oom())
2531     }
2532 
2533     /// Converts a wasmparser value type to a wasmtime type
convert_valtype(&self, ty: wasmparser::ValType) -> WasmResult<WasmValType>2534     fn convert_valtype(&self, ty: wasmparser::ValType) -> WasmResult<WasmValType> {
2535         Ok(match ty {
2536             wasmparser::ValType::I32 => WasmValType::I32,
2537             wasmparser::ValType::I64 => WasmValType::I64,
2538             wasmparser::ValType::F32 => WasmValType::F32,
2539             wasmparser::ValType::F64 => WasmValType::F64,
2540             wasmparser::ValType::V128 => WasmValType::V128,
2541             wasmparser::ValType::Ref(t) => WasmValType::Ref(self.convert_ref_type(t)?),
2542         })
2543     }
2544 
2545     /// Converts a wasmparser reference type to a wasmtime type
convert_ref_type(&self, ty: wasmparser::RefType) -> WasmResult<WasmRefType>2546     fn convert_ref_type(&self, ty: wasmparser::RefType) -> WasmResult<WasmRefType> {
2547         Ok(WasmRefType {
2548             nullable: ty.is_nullable(),
2549             heap_type: self.convert_heap_type(ty.heap_type())?,
2550         })
2551     }
2552 
2553     /// Converts a wasmparser heap type to a wasmtime type
convert_heap_type(&self, ty: wasmparser::HeapType) -> WasmResult<WasmHeapType>2554     fn convert_heap_type(&self, ty: wasmparser::HeapType) -> WasmResult<WasmHeapType> {
2555         Ok(match ty {
2556             wasmparser::HeapType::Concrete(i) => self.lookup_heap_type(i),
2557             wasmparser::HeapType::Abstract { ty, shared: false } => match ty {
2558                 wasmparser::AbstractHeapType::Extern => WasmHeapType::Extern,
2559                 wasmparser::AbstractHeapType::NoExtern => WasmHeapType::NoExtern,
2560                 wasmparser::AbstractHeapType::Func => WasmHeapType::Func,
2561                 wasmparser::AbstractHeapType::NoFunc => WasmHeapType::NoFunc,
2562                 wasmparser::AbstractHeapType::Any => WasmHeapType::Any,
2563                 wasmparser::AbstractHeapType::Eq => WasmHeapType::Eq,
2564                 wasmparser::AbstractHeapType::I31 => WasmHeapType::I31,
2565                 wasmparser::AbstractHeapType::Array => WasmHeapType::Array,
2566                 wasmparser::AbstractHeapType::Struct => WasmHeapType::Struct,
2567                 wasmparser::AbstractHeapType::None => WasmHeapType::None,
2568                 wasmparser::AbstractHeapType::Cont => WasmHeapType::Cont,
2569                 wasmparser::AbstractHeapType::NoCont => WasmHeapType::NoCont,
2570                 wasmparser::AbstractHeapType::Exn => WasmHeapType::Exn,
2571                 wasmparser::AbstractHeapType::NoExn => WasmHeapType::NoExn,
2572             },
2573             _ => return Err(wasm_unsupported!("unsupported heap type {ty:?}")),
2574         })
2575     }
2576 
2577     /// Converts the specified type index from a heap type into a canonicalized
2578     /// heap type.
lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType2579     fn lookup_heap_type(&self, index: wasmparser::UnpackedIndex) -> WasmHeapType;
2580 
2581     /// Converts the specified type index from a heap type into a canonicalized
2582     /// heap type.
lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex2583     fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex;
2584 }
2585 
2586 #[cfg(test)]
2587 mod tests {
2588     use super::*;
2589 
2590     #[test]
wasm_func_type_new() -> Result<()>2591     fn wasm_func_type_new() -> Result<()> {
2592         let i32 = WasmValType::I32;
2593         let anyref = WasmValType::Ref(WasmRefType {
2594             nullable: true,
2595             heap_type: WasmHeapType::Any,
2596         });
2597         let ty = WasmFuncType::new([i32, i32, anyref, anyref], [i32, anyref])?;
2598         assert_eq!(ty.params(), &[i32, i32, anyref, anyref]);
2599         assert_eq!(ty.non_i31_gc_ref_params_count(), 2);
2600         assert_eq!(ty.results(), &[i32, anyref]);
2601         assert_eq!(ty.non_i31_gc_ref_results_count(), 1);
2602         Ok(())
2603     }
2604 }
2605