wasmer_types/
types.rs

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