Skip to main content

wasmer_types/
types.rs

1use crate::indexes::{FunctionIndex, GlobalIndex};
2use crate::units::Pages;
3use std::borrow::ToOwned;
4use std::boxed::Box;
5use std::fmt;
6use std::format;
7use std::string::{String, ToString};
8use std::vec::Vec;
9
10use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
11#[cfg(feature = "enable-serde")]
12use serde::{Deserialize, Serialize};
13
14// Type Representations
15
16// Value Types
17
18/// A list of all possible value types in WebAssembly.
19#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash)]
20#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
21#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
22#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
23#[rkyv(derive(Debug), compare(PartialEq))]
24#[repr(u8)]
25pub enum Type {
26    /// Signed 32 bit integer.
27    I32,
28    /// Signed 64 bit integer.
29    I64,
30    /// Floating point 32 bit integer.
31    F32,
32    /// Floating point 64 bit integer.
33    F64,
34    /// A 128 bit number.
35    V128,
36    /// A reference to opaque data in the Wasm instance.
37    ExternRef, /* = 128 */
38    /// A reference to a Wasm function.
39    FuncRef,
40    /// A reference to a Wasm exception.
41    ExceptionRef,
42}
43
44impl Type {
45    /// Returns true if `Type` matches any of the numeric types. (e.g. `I32`,
46    /// `I64`, `F32`, `F64`, `V128`).
47    pub fn is_num(self) -> bool {
48        matches!(
49            self,
50            Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128
51        )
52    }
53
54    /// Returns true if `Type` matches either of the reference types.
55    pub fn is_ref(self) -> bool {
56        matches!(self, Self::ExternRef | Self::FuncRef | Self::ExceptionRef)
57    }
58
59    /// Returns the size of this type in bits.
60    ///
61    /// `pointer_width` is the size of a native pointer in bits and determines
62    /// the size of `ExternRef` and `FuncRef`.
63    pub const fn bit_size(self, pointer_width: usize) -> usize {
64        match self {
65            Self::I32 | Self::F32 | Self::ExceptionRef => 32,
66            Self::I64 | Self::F64 => 64,
67            Self::ExternRef | Self::FuncRef => pointer_width,
68            Self::V128 => 128,
69        }
70    }
71}
72
73impl fmt::Display for Type {
74    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75        write!(f, "{self:?}")
76    }
77}
78
79/// The WebAssembly V128 type
80#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
81#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
82#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
83#[rkyv(derive(Debug), compare(PartialEq))]
84pub struct V128(pub(crate) [u8; 16]);
85
86#[cfg(feature = "artifact-size")]
87impl loupe::MemoryUsage for V128 {
88    fn size_of_val(&self, _tracker: &mut dyn loupe::MemoryUsageTracker) -> usize {
89        16 * 8
90    }
91}
92
93impl V128 {
94    /// Get the bytes corresponding to the V128 value
95    pub fn bytes(&self) -> &[u8; 16] {
96        &self.0
97    }
98    /// Iterate over the bytes in the constant.
99    pub fn iter(&self) -> impl Iterator<Item = &u8> {
100        self.0.iter()
101    }
102
103    /// Convert the immediate into a vector.
104    pub fn to_vec(self) -> Vec<u8> {
105        self.0.to_vec()
106    }
107
108    /// Convert the immediate into a slice.
109    pub fn as_slice(&self) -> &[u8] {
110        &self.0[..]
111    }
112}
113
114impl From<[u8; 16]> for V128 {
115    fn from(array: [u8; 16]) -> Self {
116        Self(array)
117    }
118}
119
120impl From<&[u8]> for V128 {
121    fn from(slice: &[u8]) -> Self {
122        assert_eq!(slice.len(), 16);
123        let mut buffer = [0; 16];
124        buffer.copy_from_slice(slice);
125        Self(buffer)
126    }
127}
128
129// External Types
130
131/// A list of all possible types which can be externally referenced from a
132/// WebAssembly module.
133///
134/// This list can be found in [`ImportType`] or [`ExportType`], so these types
135/// can either be imported or exported.
136#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
137#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
138#[rkyv(derive(Debug))]
139pub enum ExternType {
140    /// This external type is the type of a WebAssembly function.
141    Function(FunctionType),
142    /// This external type is the type of a WebAssembly global.
143    Global(GlobalType),
144    /// This external type is the type of a WebAssembly table.
145    Table(TableType),
146    /// This external type is the type of a WebAssembly memory.
147    Memory(MemoryType),
148    /// This external type is the type of a WebAssembly tag.
149    Tag(TagType),
150}
151
152fn is_global_compatible(exported: GlobalType, imported: GlobalType) -> bool {
153    let GlobalType {
154        ty: exported_ty,
155        mutability: exported_mutability,
156    } = exported;
157    let GlobalType {
158        ty: imported_ty,
159        mutability: imported_mutability,
160    } = imported;
161
162    exported_ty == imported_ty && imported_mutability == exported_mutability
163}
164
165fn is_table_element_type_compatible(exported_type: Type, imported_type: Type) -> bool {
166    match exported_type {
167        Type::FuncRef => true,
168        _ => imported_type == exported_type,
169    }
170}
171
172fn is_table_compatible(
173    exported: &TableType,
174    imported: &TableType,
175    imported_runtime_size: Option<u32>,
176) -> bool {
177    let TableType {
178        ty: exported_ty,
179        minimum: exported_minimum,
180        maximum: exported_maximum,
181        ..
182    } = exported;
183    let TableType {
184        ty: imported_ty,
185        minimum: imported_minimum,
186        maximum: imported_maximum,
187        ..
188    } = imported;
189
190    is_table_element_type_compatible(*exported_ty, *imported_ty)
191        && *imported_minimum <= imported_runtime_size.unwrap_or(*exported_minimum)
192        && (imported_maximum.is_none()
193            || (!exported_maximum.is_none()
194                && imported_maximum.unwrap() >= exported_maximum.unwrap()))
195}
196
197fn is_memory_compatible(
198    exported: &MemoryType,
199    imported: &MemoryType,
200    imported_runtime_size: Option<u32>,
201) -> bool {
202    let MemoryType {
203        minimum: exported_minimum,
204        maximum: exported_maximum,
205        shared: exported_shared,
206    } = exported;
207    let MemoryType {
208        minimum: imported_minimum,
209        maximum: imported_maximum,
210        shared: imported_shared,
211    } = imported;
212
213    imported_minimum.0 <= imported_runtime_size.unwrap_or(exported_minimum.0)
214        && (imported_maximum.is_none()
215            || (!exported_maximum.is_none()
216                && imported_maximum.unwrap() >= exported_maximum.unwrap()))
217        && exported_shared == imported_shared
218}
219
220macro_rules! accessors {
221    ($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($(
222        /// Attempt to return the underlying type of this external type,
223        /// returning `None` if it is a different type.
224        pub fn $get(&self) -> Option<&$ty> {
225            if let Self::$variant(e) = self {
226                Some(e)
227            } else {
228                None
229            }
230        }
231
232        /// Returns the underlying descriptor of this [`ExternType`], panicking
233        /// if it is a different type.
234        ///
235        /// # Panics
236        ///
237        /// Panics if `self` is not of the right type.
238        pub fn $unwrap(&self) -> &$ty {
239            self.$get().expect(concat!("expected ", stringify!($ty)))
240        }
241    )*)
242}
243
244impl ExternType {
245    accessors! {
246        (Function(FunctionType) func unwrap_func)
247        (Global(GlobalType) global unwrap_global)
248        (Table(TableType) table unwrap_table)
249        (Memory(MemoryType) memory unwrap_memory)
250    }
251    /// Check if two externs are compatible
252    pub fn is_compatible_with(&self, other: &Self, runtime_size: Option<u32>) -> bool {
253        match (self, other) {
254            (Self::Function(a), Self::Function(b)) => a == b,
255            (Self::Global(a), Self::Global(b)) => is_global_compatible(*a, *b),
256            (Self::Table(a), Self::Table(b)) => is_table_compatible(a, b, runtime_size),
257            (Self::Memory(a), Self::Memory(b)) => is_memory_compatible(a, b, runtime_size),
258            (Self::Tag(a), Self::Tag(b)) => a == b,
259            // The rest of possibilities, are not compatible
260            _ => false,
261        }
262    }
263}
264
265// TODO: `shrink_to_fit` these or change it to `Box<[Type]>` if not using
266// Cow or something else
267/// The signature of a function that is either implemented
268/// in a Wasm module or exposed to Wasm by the host.
269///
270/// WebAssembly functions can have 0 or more parameters and results.
271#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
272#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
273#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
274#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
275#[rkyv(derive(Debug))]
276pub struct FunctionType {
277    /// The parameters of the function
278    params: Box<[Type]>,
279    /// The return values of the function
280    results: Box<[Type]>,
281}
282
283impl FunctionType {
284    /// Creates a new Function Type with the given parameter and return types.
285    pub fn new<Params, Returns>(params: Params, returns: Returns) -> Self
286    where
287        Params: Into<Box<[Type]>>,
288        Returns: Into<Box<[Type]>>,
289    {
290        Self {
291            params: params.into(),
292            results: returns.into(),
293        }
294    }
295
296    /// Parameter types.
297    pub fn params(&self) -> &[Type] {
298        &self.params
299    }
300
301    /// Return types.
302    pub fn results(&self) -> &[Type] {
303        &self.results
304    }
305
306    /// Returns a stable 32-bit signature hash derived from the Wasm value types.
307    pub fn signature_hash(&self) -> u32 {
308        let mut hasher = crc32fast::Hasher::new();
309        hasher.update(&self.results.len().to_le_bytes());
310        hasher.update(&self.params.len().to_le_bytes());
311        for ty in self.results.iter().chain(self.params.iter()) {
312            hasher.update(&[*ty as u8]);
313        }
314        hasher.finalize()
315    }
316}
317
318impl fmt::Display for FunctionType {
319    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
320        let params = self
321            .params
322            .iter()
323            .map(|p| format!("{p:?}"))
324            .collect::<Vec<_>>()
325            .join(", ");
326        let results = self
327            .results
328            .iter()
329            .map(|p| format!("{p:?}"))
330            .collect::<Vec<_>>()
331            .join(", ");
332        write!(f, "[{params}] -> [{results}]")
333    }
334}
335
336// Macro needed until https://rust-lang.github.io/rfcs/2000-const-generics.html is stable.
337// See https://users.rust-lang.org/t/how-to-implement-trait-for-fixed-size-array-of-any-size/31494
338macro_rules! implement_from_pair_to_functiontype {
339    ($($N:literal,$M:literal)+) => {
340        $(
341            impl From<([Type; $N], [Type; $M])> for FunctionType {
342                fn from(pair: ([Type; $N], [Type; $M])) -> Self {
343                    Self::new(pair.0, pair.1)
344                }
345            }
346        )+
347    }
348}
349
350implement_from_pair_to_functiontype! {
351    0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9
352    1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9
353    2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9
354    3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 3,9
355    4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9
356    5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 5,9
357    6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 6,9
358    7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 7,9
359    8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 8,9
360    9,0 9,1 9,2 9,3 9,4 9,5 9,6 9,7 9,8 9,9
361}
362
363impl From<&Self> for FunctionType {
364    fn from(as_ref: &Self) -> Self {
365        as_ref.clone()
366    }
367}
368
369/// Indicator of whether a global is mutable or not
370#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
371#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
372#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
373#[rkyv(derive(Debug), compare(PartialOrd, PartialEq))]
374#[repr(u8)]
375pub enum Mutability {
376    /// The global is constant and its value does not change
377    Const,
378    /// The value of the global can change over time
379    Var,
380}
381
382impl Mutability {
383    /// Returns a boolean indicating if the enum is set to mutable.
384    pub fn is_mutable(self) -> bool {
385        self.into()
386    }
387}
388
389impl From<bool> for Mutability {
390    fn from(value: bool) -> Self {
391        if value { Self::Var } else { Self::Const }
392    }
393}
394
395impl From<Mutability> for bool {
396    fn from(value: Mutability) -> Self {
397        match value {
398            Mutability::Var => true,
399            Mutability::Const => false,
400        }
401    }
402}
403
404/// WebAssembly global.
405#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
406#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
407#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
408#[rkyv(derive(Debug), compare(PartialEq))]
409pub struct GlobalType {
410    /// The type of the value stored in the global.
411    pub ty: Type,
412    /// A flag indicating whether the value may change at runtime.
413    pub mutability: Mutability,
414}
415
416// Global Types
417
418/// A WebAssembly global descriptor.
419///
420/// This type describes an instance of a global in a WebAssembly
421/// module. Globals are local to an `Instance` and are either
422/// immutable or mutable.
423impl GlobalType {
424    /// Create a new Global variable
425    /// # Usage:
426    /// ```
427    /// use wasmer_types::{GlobalType, Type, Mutability};
428    ///
429    /// // An I32 constant global
430    /// let global = GlobalType::new(Type::I32, Mutability::Const);
431    /// // An I64 mutable global
432    /// let global = GlobalType::new(Type::I64, Mutability::Var);
433    /// ```
434    pub fn new(ty: Type, mutability: Mutability) -> Self {
435        Self { ty, mutability }
436    }
437}
438
439impl fmt::Display for GlobalType {
440    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
441        let mutability = match self.mutability {
442            Mutability::Const => "constant",
443            Mutability::Var => "mutable",
444        };
445        write!(f, "{} ({})", self.ty, mutability)
446    }
447}
448
449/// A serializable sequence of operators for init expressions in globals,
450/// element offsets and data offsets.
451#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
452#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
453#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
454#[rkyv(derive(Debug), compare(PartialEq))]
455pub struct InitExpr {
456    /// Operators in stack-machine order, excluding the terminating `end`.
457    pub ops: Box<[InitExprOp]>,
458}
459
460impl InitExpr {
461    /// Creates a new init expression.
462    pub fn new<Ops>(ops: Ops) -> Self
463    where
464        Ops: Into<Box<[InitExprOp]>>,
465    {
466        Self { ops: ops.into() }
467    }
468
469    /// Returns the operators that form this expression.
470    pub fn ops(&self) -> &[InitExprOp] {
471        &self.ops
472    }
473}
474
475/// Supported operators in serialized init expressions.
476#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
477#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
478#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
479#[rkyv(derive(Debug), compare(PartialEq))]
480#[repr(u8)]
481pub enum InitExprOp {
482    /// A `global.get` of an `i32` global.
483    GlobalGetI32(GlobalIndex),
484    /// A `global.get` of an `i64` global.
485    GlobalGetI64(GlobalIndex),
486    /// An `i32.const`.
487    I32Const(i32),
488    /// An `i32.add`.
489    I32Add,
490    /// An `i32.sub`.
491    I32Sub,
492    /// An `i32.mul`.
493    I32Mul,
494    /// An `i64.const`.
495    I64Const(i64),
496    /// An `i64.add`.
497    I64Add,
498    /// An `i64.sub`.
499    I64Sub,
500    /// An `i64.mul`.
501    I64Mul,
502}
503
504impl InitExprOp {
505    /// Return true if the expression is 32-bit
506    pub fn is_32bit_expression(&self) -> bool {
507        match self {
508            Self::GlobalGetI32(..)
509            | Self::I32Const(_)
510            | Self::I32Add
511            | Self::I32Sub
512            | Self::I32Mul => true,
513            Self::GlobalGetI64(_)
514            | Self::I64Const(_)
515            | Self::I64Add
516            | Self::I64Sub
517            | Self::I64Mul => false,
518        }
519    }
520}
521
522/// Globals are initialized via `const` operators, references, or a serialized
523/// expression.
524#[derive(Debug, Clone, PartialEq, RkyvSerialize, RkyvDeserialize, Archive)]
525#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
526#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
527#[rkyv(derive(Debug), compare(PartialEq))]
528#[repr(u8)]
529pub enum GlobalInit {
530    /// An `i32.const`.
531    I32Const(i32),
532    /// An `i64.const`.
533    I64Const(i64),
534    /// An `f32.const`.
535    F32Const(f32),
536    /// An `f64.const`.
537    F64Const(f64),
538    /// A `v128.const`.
539    V128Const(V128),
540    /// A `global.get` of another global.
541    GetGlobal(GlobalIndex),
542    // TODO(reftypes): `ref.null func` and `ref.null extern` seem to be 2 different
543    // things: we need to handle both. Perhaps this handled in context by the
544    // global knowing its own type?
545    /// A `ref.null`.
546    RefNullConst,
547    /// A `ref.func <index>`.
548    RefFunc(FunctionIndex),
549    /// A serialized init expression.
550    Expr(InitExpr),
551}
552
553// Tag Types
554
555/// The kind of a tag.
556///
557/// Currently, tags can only express exceptions.
558#[derive(Debug, Clone, PartialEq, Eq, Hash)]
559#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
560#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
561#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
562#[rkyv(derive(Debug))]
563pub enum TagKind {
564    /// This tag's event is an exception.
565    Exception,
566}
567
568/// The signature of a tag that is either implemented
569/// in a Wasm module or exposed to Wasm by the host.
570#[derive(Debug, Clone, PartialEq, Eq, Hash)]
571#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
572#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
573#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
574#[rkyv(derive(Debug))]
575pub struct TagType {
576    /// The kind of the tag.
577    pub kind: TagKind,
578    /// The parameters of the tag
579    pub params: Box<[Type]>,
580}
581
582impl TagType {
583    /// Creates a new [`TagType`] with the given kind, parameter and return types.
584    pub fn new<Params>(kind: TagKind, params: Params) -> Self
585    where
586        Params: Into<Box<[Type]>>,
587    {
588        Self {
589            kind,
590            params: params.into(),
591        }
592    }
593
594    /// Parameter types.
595    pub fn params(&self) -> &[Type] {
596        &self.params
597    }
598
599    /// Create a new [`TagType`] with the given kind and the associated type.
600    pub fn from_fn_type(kind: TagKind, ty: FunctionType) -> Self {
601        Self {
602            kind,
603            params: ty.params().into(),
604        }
605    }
606}
607
608impl fmt::Display for TagType {
609    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
610        write!(f, "({:?}) {:?}", self.kind, self.params(),)
611    }
612}
613
614// Table Types
615
616/// A descriptor for a table in a WebAssembly module.
617///
618/// Tables are contiguous chunks of a specific element, typically a `funcref` or
619/// an `externref`. The most common use for tables is a function table through
620/// which `call_indirect` can invoke other functions.
621#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
622#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
623#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
624#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
625#[rkyv(derive(Debug))]
626pub struct TableType {
627    /// The type of data stored in elements of the table.
628    pub ty: Type,
629    /// The minimum number of elements in the table.
630    pub minimum: u32,
631    /// The maximum number of elements in the table.
632    pub maximum: Option<u32>,
633    /// Whether the table is known to be immutable at runtime.
634    pub readonly: bool,
635}
636
637impl TableType {
638    /// Creates a new table descriptor which will contain the specified
639    /// `element` and have the `limits` applied to its length.
640    pub fn new(ty: Type, minimum: u32, maximum: Option<u32>) -> Self {
641        Self {
642            ty,
643            minimum,
644            maximum,
645            readonly: false,
646        }
647    }
648
649    /// Return true if it's a function reference table with a fixed number of elements.
650    pub fn is_fixed_funcref_table(&self) -> bool {
651        matches!(self.ty, Type::FuncRef) && self.maximum == Some(self.minimum)
652    }
653}
654
655impl fmt::Display for TableType {
656    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
657        if let Some(maximum) = self.maximum {
658            write!(f, "{} ({}..{})", self.ty, self.minimum, maximum)
659        } else {
660            write!(f, "{} ({}..)", self.ty, self.minimum)
661        }
662    }
663}
664
665// Memory Types
666
667/// A descriptor for a WebAssembly memory type.
668///
669/// Memories are described in units of pages (64KB) and represent contiguous
670/// chunks of addressable memory.
671#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
672#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
673#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
674#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
675#[rkyv(derive(Debug))]
676pub struct MemoryType {
677    /// The minimum number of pages in the memory.
678    pub minimum: Pages,
679    /// The maximum number of pages in the memory.
680    pub maximum: Option<Pages>,
681    /// Whether the memory may be shared between multiple threads.
682    pub shared: bool,
683}
684
685impl MemoryType {
686    /// Creates a new descriptor for a WebAssembly memory given the specified
687    /// limits of the memory.
688    pub fn new<IntoPages>(minimum: IntoPages, maximum: Option<IntoPages>, shared: bool) -> Self
689    where
690        IntoPages: Into<Pages>,
691    {
692        Self {
693            minimum: minimum.into(),
694            maximum: maximum.map(Into::into),
695            shared,
696        }
697    }
698}
699
700impl fmt::Display for MemoryType {
701    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
702        let shared = if self.shared { "shared" } else { "not shared" };
703        if let Some(maximum) = self.maximum {
704            write!(f, "{} ({:?}..{:?})", shared, self.minimum, maximum)
705        } else {
706            write!(f, "{} ({:?}..)", shared, self.minimum)
707        }
708    }
709}
710
711// Import Types
712
713/// A descriptor for an imported value into a wasm module.
714///
715/// This type is primarily accessed from the `Module::imports`
716/// API. Each `ImportType` describes an import into the wasm module
717/// with the module/name that it's imported from as well as the type
718/// of item that's being imported.
719#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
720#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
721pub struct ImportType<T = ExternType> {
722    module: String,
723    name: String,
724    ty: T,
725}
726
727impl<T> ImportType<T> {
728    /// Creates a new import descriptor which comes from `module` and `name` and
729    /// is of type `ty`.
730    pub fn new(module: &str, name: &str, ty: T) -> Self {
731        Self {
732            module: module.to_owned(),
733            name: name.to_owned(),
734            ty,
735        }
736    }
737
738    /// Returns the module name that this import is expected to come from.
739    pub fn module(&self) -> &str {
740        &self.module
741    }
742
743    /// Returns the field name of the module that this import is expected to
744    /// come from.
745    pub fn name(&self) -> &str {
746        &self.name
747    }
748
749    /// Returns the expected type of this import.
750    pub fn ty(&self) -> &T {
751        &self.ty
752    }
753}
754
755// Export Types
756
757/// A descriptor for an exported WebAssembly value.
758///
759/// This type is primarily accessed from the `Module::exports`
760/// accessor and describes what names are exported from a wasm module
761/// and the type of the item that is exported.
762///
763/// The `<T>` refefers to `ExternType`, however it can also refer to use
764/// `MemoryType`, `TableType`, `FunctionType` and `GlobalType` for ease of
765/// use.
766#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
767#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
768pub struct ExportType<T = ExternType> {
769    name: String,
770    ty: T,
771}
772
773impl<T> ExportType<T> {
774    /// Creates a new export which is exported with the given `name` and has the
775    /// given `ty`.
776    pub fn new(name: &str, ty: T) -> Self {
777        Self {
778            name: name.to_string(),
779            ty,
780        }
781    }
782
783    /// Returns the name by which this export is known by.
784    pub fn name(&self) -> &str {
785        &self.name
786    }
787
788    /// Returns the type of this export.
789    pub fn ty(&self) -> &T {
790        &self.ty
791    }
792}
793
794#[cfg(test)]
795mod tests {
796    use super::*;
797
798    const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []);
799    const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []);
800    const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]);
801    const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]);
802
803    #[test]
804    fn convert_tuple_to_functiontype() {
805        let ty: FunctionType = VOID_TO_VOID.into();
806        assert_eq!(ty.params().len(), 0);
807        assert_eq!(ty.results().len(), 0);
808
809        let ty: FunctionType = I32_I32_TO_VOID.into();
810        assert_eq!(ty.params().len(), 2);
811        assert_eq!(ty.params()[0], Type::I32);
812        assert_eq!(ty.params()[1], Type::I32);
813        assert_eq!(ty.results().len(), 0);
814
815        let ty: FunctionType = V128_I64_TO_I32.into();
816        assert_eq!(ty.params().len(), 2);
817        assert_eq!(ty.params()[0], Type::V128);
818        assert_eq!(ty.params()[1], Type::I64);
819        assert_eq!(ty.results().len(), 1);
820        assert_eq!(ty.results()[0], Type::I32);
821
822        let ty: FunctionType = NINE_V128_TO_NINE_I32.into();
823        assert_eq!(ty.params().len(), 9);
824        assert_eq!(ty.results().len(), 9);
825    }
826
827    #[test]
828    fn signature_hash_is_stable() {
829        let ty: FunctionType = ([Type::I32, Type::F64], [Type::ExternRef]).into();
830        assert_eq!(ty.signature_hash(), ty.signature_hash());
831    }
832
833    #[test]
834    fn signature_hash_distinguishes() {
835        let left: FunctionType = ([Type::I32], [Type::I64]).into();
836        let right: FunctionType = ([Type::I64], [Type::I32]).into();
837        assert_ne!(left.signature_hash(), right.signature_hash());
838
839        let left: FunctionType = ([], [Type::I32, Type::I64]).into();
840        let right: FunctionType = ([Type::I32], [Type::I64]).into();
841        assert_ne!(left.signature_hash(), right.signature_hash());
842    }
843}