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