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