wasmer_types/
types.rs

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