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