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 { Self::Var } else { Self::Const }
364    }
365}
366
367impl From<Mutability> for bool {
368    fn from(value: Mutability) -> Self {
369        match value {
370            Mutability::Var => true,
371            Mutability::Const => false,
372        }
373    }
374}
375
376/// WebAssembly global.
377#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
378#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
379#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
380#[rkyv(derive(Debug), compare(PartialEq))]
381pub struct GlobalType {
382    /// The type of the value stored in the global.
383    pub ty: Type,
384    /// A flag indicating whether the value may change at runtime.
385    pub mutability: Mutability,
386}
387
388// Global Types
389
390/// A WebAssembly global descriptor.
391///
392/// This type describes an instance of a global in a WebAssembly
393/// module. Globals are local to an `Instance` and are either
394/// immutable or mutable.
395impl GlobalType {
396    /// Create a new Global variable
397    /// # Usage:
398    /// ```
399    /// use wasmer_types::{GlobalType, Type, Mutability};
400    ///
401    /// // An I32 constant global
402    /// let global = GlobalType::new(Type::I32, Mutability::Const);
403    /// // An I64 mutable global
404    /// let global = GlobalType::new(Type::I64, Mutability::Var);
405    /// ```
406    pub fn new(ty: Type, mutability: Mutability) -> Self {
407        Self { ty, mutability }
408    }
409}
410
411impl fmt::Display for GlobalType {
412    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
413        let mutability = match self.mutability {
414            Mutability::Const => "constant",
415            Mutability::Var => "mutable",
416        };
417        write!(f, "{} ({})", self.ty, mutability)
418    }
419}
420
421/// Globals are initialized via the `const` operators or by referring to another import.
422#[derive(Debug, Clone, Copy, PartialEq, RkyvSerialize, RkyvDeserialize, Archive)]
423#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
424#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
425#[rkyv(derive(Debug), compare(PartialEq))]
426#[repr(u8)]
427pub enum GlobalInit {
428    /// An `i32.const`.
429    I32Const(i32),
430    /// An `i64.const`.
431    I64Const(i64),
432    /// An `f32.const`.
433    F32Const(f32),
434    /// An `f64.const`.
435    F64Const(f64),
436    /// A `v128.const`.
437    V128Const(V128),
438    /// A `global.get` of another global.
439    GetGlobal(GlobalIndex),
440    // TODO(reftypes): `ref.null func` and `ref.null extern` seem to be 2 different
441    // things: we need to handle both. Perhaps this handled in context by the
442    // global knowing its own type?
443    /// A `ref.null`.
444    RefNullConst,
445    /// A `ref.func <index>`.
446    RefFunc(FunctionIndex),
447}
448
449// Tag Types
450
451/// The kind of a tag.
452///
453/// Currently, tags can only express exceptions.
454#[derive(Debug, Clone, PartialEq, Eq, Hash)]
455#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
456#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
457#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
458#[rkyv(derive(Debug))]
459pub enum TagKind {
460    /// This tag's event is an exception.
461    Exception,
462}
463
464/// The signature of a tag that is either implemented
465/// in a Wasm module or exposed to Wasm by the host.
466#[derive(Debug, Clone, PartialEq, Eq, Hash)]
467#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
468#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
469#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
470#[rkyv(derive(Debug))]
471pub struct TagType {
472    /// The kind of the tag.
473    pub kind: TagKind,
474    /// The parameters of the function
475    pub params: Box<[Type]>,
476}
477
478impl TagType {
479    /// Creates a new [`TagType`] with the given kind, parameter and return types.
480    pub fn new<Params>(kind: TagKind, params: Params) -> Self
481    where
482        Params: Into<Box<[Type]>>,
483    {
484        Self {
485            kind,
486            params: params.into(),
487        }
488    }
489
490    /// Parameter types.
491    pub fn params(&self) -> &[Type] {
492        &self.params
493    }
494
495    /// Create a new [`TagType`] with the given kind and the associated type.
496    pub fn from_fn_type(kind: TagKind, ty: FunctionType) -> Self {
497        Self {
498            kind,
499            params: ty.params().into(),
500        }
501    }
502}
503
504impl fmt::Display for TagType {
505    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
506        write!(f, "({:?}) {:?}", self.kind, self.params(),)
507    }
508}
509
510// Table Types
511
512/// A descriptor for a table in a WebAssembly module.
513///
514/// Tables are contiguous chunks of a specific element, typically a `funcref` or
515/// an `externref`. The most common use for tables is a function table through
516/// which `call_indirect` can invoke other functions.
517#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
518#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
519#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
520#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
521#[rkyv(derive(Debug))]
522pub struct TableType {
523    /// The type of data stored in elements of the table.
524    pub ty: Type,
525    /// The minimum number of elements in the table.
526    pub minimum: u32,
527    /// The maximum number of elements in the table.
528    pub maximum: Option<u32>,
529}
530
531impl TableType {
532    /// Creates a new table descriptor which will contain the specified
533    /// `element` and have the `limits` applied to its length.
534    pub fn new(ty: Type, minimum: u32, maximum: Option<u32>) -> Self {
535        Self {
536            ty,
537            minimum,
538            maximum,
539        }
540    }
541}
542
543impl fmt::Display for TableType {
544    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
545        if let Some(maximum) = self.maximum {
546            write!(f, "{} ({}..{})", self.ty, self.minimum, maximum)
547        } else {
548            write!(f, "{} ({}..)", self.ty, self.minimum)
549        }
550    }
551}
552
553// Memory Types
554
555/// A descriptor for a WebAssembly memory type.
556///
557/// Memories are described in units of pages (64KB) and represent contiguous
558/// chunks of addressable memory.
559#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
560#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
561#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
562#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
563#[rkyv(derive(Debug))]
564pub struct MemoryType {
565    /// The minimum number of pages in the memory.
566    pub minimum: Pages,
567    /// The maximum number of pages in the memory.
568    pub maximum: Option<Pages>,
569    /// Whether the memory may be shared between multiple threads.
570    pub shared: bool,
571}
572
573impl MemoryType {
574    /// Creates a new descriptor for a WebAssembly memory given the specified
575    /// limits of the memory.
576    pub fn new<IntoPages>(minimum: IntoPages, maximum: Option<IntoPages>, shared: bool) -> Self
577    where
578        IntoPages: Into<Pages>,
579    {
580        Self {
581            minimum: minimum.into(),
582            maximum: maximum.map(Into::into),
583            shared,
584        }
585    }
586}
587
588impl fmt::Display for MemoryType {
589    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
590        let shared = if self.shared { "shared" } else { "not shared" };
591        if let Some(maximum) = self.maximum {
592            write!(f, "{} ({:?}..{:?})", shared, self.minimum, maximum)
593        } else {
594            write!(f, "{} ({:?}..)", shared, self.minimum)
595        }
596    }
597}
598
599// Import Types
600
601/// A descriptor for an imported value into a wasm module.
602///
603/// This type is primarily accessed from the `Module::imports`
604/// API. Each `ImportType` describes an import into the wasm module
605/// with the module/name that it's imported from as well as the type
606/// of item that's being imported.
607#[derive(Debug, Clone, PartialEq, Eq, Hash)]
608#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
609pub struct ImportType<T = ExternType> {
610    module: String,
611    name: String,
612    ty: T,
613}
614
615impl<T> ImportType<T> {
616    /// Creates a new import descriptor which comes from `module` and `name` and
617    /// is of type `ty`.
618    pub fn new(module: &str, name: &str, ty: T) -> Self {
619        Self {
620            module: module.to_owned(),
621            name: name.to_owned(),
622            ty,
623        }
624    }
625
626    /// Returns the module name that this import is expected to come from.
627    pub fn module(&self) -> &str {
628        &self.module
629    }
630
631    /// Returns the field name of the module that this import is expected to
632    /// come from.
633    pub fn name(&self) -> &str {
634        &self.name
635    }
636
637    /// Returns the expected type of this import.
638    pub fn ty(&self) -> &T {
639        &self.ty
640    }
641}
642
643// Export Types
644
645/// A descriptor for an exported WebAssembly value.
646///
647/// This type is primarily accessed from the `Module::exports`
648/// accessor and describes what names are exported from a wasm module
649/// and the type of the item that is exported.
650///
651/// The `<T>` refefers to `ExternType`, however it can also refer to use
652/// `MemoryType`, `TableType`, `FunctionType` and `GlobalType` for ease of
653/// use.
654#[derive(Debug, Clone, PartialEq, Eq, Hash)]
655#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
656pub struct ExportType<T = ExternType> {
657    name: String,
658    ty: T,
659}
660
661impl<T> ExportType<T> {
662    /// Creates a new export which is exported with the given `name` and has the
663    /// given `ty`.
664    pub fn new(name: &str, ty: T) -> Self {
665        Self {
666            name: name.to_string(),
667            ty,
668        }
669    }
670
671    /// Returns the name by which this export is known by.
672    pub fn name(&self) -> &str {
673        &self.name
674    }
675
676    /// Returns the type of this export.
677    pub fn ty(&self) -> &T {
678        &self.ty
679    }
680}
681
682#[cfg(test)]
683mod tests {
684    use super::*;
685
686    const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []);
687    const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []);
688    const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]);
689    const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]);
690
691    #[test]
692    fn convert_tuple_to_functiontype() {
693        let ty: FunctionType = VOID_TO_VOID.into();
694        assert_eq!(ty.params().len(), 0);
695        assert_eq!(ty.results().len(), 0);
696
697        let ty: FunctionType = I32_I32_TO_VOID.into();
698        assert_eq!(ty.params().len(), 2);
699        assert_eq!(ty.params()[0], Type::I32);
700        assert_eq!(ty.params()[1], Type::I32);
701        assert_eq!(ty.results().len(), 0);
702
703        let ty: FunctionType = V128_I64_TO_I32.into();
704        assert_eq!(ty.params().len(), 2);
705        assert_eq!(ty.params()[0], Type::V128);
706        assert_eq!(ty.params()[1], Type::I64);
707        assert_eq!(ty.results().len(), 1);
708        assert_eq!(ty.results()[0], Type::I32);
709
710        let ty: FunctionType = NINE_V128_TO_NINE_I32.into();
711        assert_eq!(ty.params().len(), 9);
712        assert_eq!(ty.results().len(), 9);
713    }
714}