wasmer_runtime_core_fl/
types.rs

1//! The runtime types modules represent type used within the wasm runtime and helper functions to
2//! convert to other represenations.
3
4use crate::{memory::MemoryType, module::ModuleInfo, structures::TypedIndex, units::Pages};
5use std::{borrow::Cow, convert::TryFrom};
6
7/// Represents a WebAssembly type.
8#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum Type {
10    /// The `i32` type.
11    I32,
12    /// The `i64` type.
13    I64,
14    /// The `f32` type.
15    F32,
16    /// The `f64` type.
17    F64,
18    /// The `v128` type.
19    V128,
20}
21
22impl std::fmt::Display for Type {
23    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
24        write!(f, "{:?}", self)
25    }
26}
27
28/// Represents a WebAssembly value.
29///
30/// As the number of types in WebAssembly expand,
31/// this structure will expand as well.
32#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
33pub enum Value {
34    /// The `i32` type.
35    I32(i32),
36    /// The `i64` type.
37    I64(i64),
38    /// The `f32` type.
39    F32(f32),
40    /// The `f64` type.
41    F64(f64),
42    /// The `v128` type.
43    V128(u128),
44}
45
46impl Value {
47    /// The `Type` of this `Value`.
48    pub fn ty(&self) -> Type {
49        match self {
50            Value::I32(_) => Type::I32,
51            Value::I64(_) => Type::I64,
52            Value::F32(_) => Type::F32,
53            Value::F64(_) => Type::F64,
54            Value::V128(_) => Type::V128,
55        }
56    }
57
58    /// Convert this `Value` to a u128 binary representation.
59    pub fn to_u128(&self) -> u128 {
60        match *self {
61            Value::I32(x) => x as u128,
62            Value::I64(x) => x as u128,
63            Value::F32(x) => f32::to_bits(x) as u128,
64            Value::F64(x) => f64::to_bits(x) as u128,
65            Value::V128(x) => x,
66        }
67    }
68}
69
70macro_rules! value_conversions {
71    ($native_type:ty, $value_variant:ident) => {
72        impl From<$native_type> for Value {
73            fn from(n: $native_type) -> Self {
74                Self::$value_variant(n)
75            }
76        }
77
78        impl TryFrom<&Value> for $native_type {
79            type Error = &'static str;
80
81            fn try_from(value: &Value) -> Result<Self, Self::Error> {
82                match value {
83                    Value::$value_variant(value) => Ok(*value),
84                    _ => Err("Invalid cast."),
85                }
86            }
87        }
88    };
89}
90
91value_conversions!(i32, I32);
92value_conversions!(i64, I64);
93value_conversions!(f32, F32);
94value_conversions!(f64, F64);
95value_conversions!(u128, V128);
96
97/// Represents a native wasm type.
98pub unsafe trait NativeWasmType: Copy + Into<Value>
99where
100    Self: Sized,
101{
102    /// Type for this `NativeWasmType`.
103    const TYPE: Type;
104
105    /// Convert from u64 bites to self.
106    fn from_binary(bits: u64) -> Self;
107
108    /// Convert self to u64 binary representation.
109    fn to_binary(self) -> u64;
110}
111
112unsafe impl NativeWasmType for i32 {
113    const TYPE: Type = Type::I32;
114
115    fn from_binary(bits: u64) -> Self {
116        bits as _
117    }
118
119    fn to_binary(self) -> u64 {
120        self as _
121    }
122}
123
124unsafe impl NativeWasmType for i64 {
125    const TYPE: Type = Type::I64;
126
127    fn from_binary(bits: u64) -> Self {
128        bits as _
129    }
130
131    fn to_binary(self) -> u64 {
132        self as _
133    }
134}
135
136unsafe impl NativeWasmType for f32 {
137    const TYPE: Type = Type::F32;
138
139    fn from_binary(bits: u64) -> Self {
140        f32::from_bits(bits as u32)
141    }
142
143    fn to_binary(self) -> u64 {
144        self.to_bits() as _
145    }
146}
147
148unsafe impl NativeWasmType for f64 {
149    const TYPE: Type = Type::F64;
150
151    fn from_binary(bits: u64) -> Self {
152        f64::from_bits(bits)
153    }
154
155    fn to_binary(self) -> u64 {
156        self.to_bits()
157    }
158}
159
160/// A trait to represent a wasm extern type.
161pub unsafe trait WasmExternType: Copy
162where
163    Self: Sized,
164{
165    /// Native wasm type for this `WasmExternType`.
166    type Native: NativeWasmType;
167
168    /// Convert from given `Native` type to self.
169    fn from_native(native: Self::Native) -> Self;
170
171    /// Convert self to `Native` type.
172    fn to_native(self) -> Self::Native;
173}
174
175macro_rules! wasm_extern_type {
176    ($type:ty => $native_type:ty) => {
177        unsafe impl WasmExternType for $type {
178            type Native = $native_type;
179
180            fn from_native(native: Self::Native) -> Self {
181                native as _
182            }
183
184            fn to_native(self) -> Self::Native {
185                self as _
186            }
187        }
188    };
189}
190
191wasm_extern_type!(i8 => i32);
192wasm_extern_type!(u8 => i32);
193wasm_extern_type!(i16 => i32);
194wasm_extern_type!(u16 => i32);
195wasm_extern_type!(i32 => i32);
196wasm_extern_type!(u32 => i32);
197wasm_extern_type!(i64 => i64);
198wasm_extern_type!(u64 => i64);
199wasm_extern_type!(f32 => f32);
200wasm_extern_type!(f64 => f64);
201
202// pub trait IntegerAtomic
203// where
204//     Self: Sized
205// {
206//     type Primitive;
207
208//     fn add(&self, other: Self::Primitive) -> Self::Primitive;
209//     fn sub(&self, other: Self::Primitive) -> Self::Primitive;
210//     fn and(&self, other: Self::Primitive) -> Self::Primitive;
211//     fn or(&self, other: Self::Primitive) -> Self::Primitive;
212//     fn xor(&self, other: Self::Primitive) -> Self::Primitive;
213//     fn load(&self) -> Self::Primitive;
214//     fn store(&self, other: Self::Primitive) -> Self::Primitive;
215//     fn compare_exchange(&self, expected: Self::Primitive, new: Self::Primitive) -> Self::Primitive;
216//     fn swap(&self, other: Self::Primitive) -> Self::Primitive;
217// }
218
219/// Trait for a Value type. A Value type is a type that is always valid and may
220/// be safely copied.
221///
222/// That is, for all possible bit patterns a valid Value type can be constructed
223/// from those bits.
224///
225/// Concretely a `u32` is a Value type because every combination of 32 bits is
226/// a valid `u32`. However a `bool` is _not_ a Value type because any bit patterns
227/// other than `0` and `1` are invalid in Rust and may cause undefined behavior if
228/// a `bool` is constructed from those bytes.
229pub unsafe trait ValueType: Copy
230where
231    Self: Sized,
232{
233}
234
235macro_rules! convert_value_impl {
236    ($t:ty) => {
237        unsafe impl ValueType for $t {}
238    };
239    ( $($t:ty),* ) => {
240        $(
241            convert_value_impl!($t);
242        )*
243    };
244}
245
246convert_value_impl!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64);
247
248/// Kinds of element types.
249#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
250pub enum ElementType {
251    /// Any wasm function.
252    Anyfunc,
253}
254
255/// Describes the properties of a table including the element types, minimum and optional maximum,
256/// number of elements in the table.
257#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
258pub struct TableDescriptor {
259    /// Type of data stored in this table.
260    pub element: ElementType,
261    /// The minimum number of elements that must be stored in this table.
262    pub minimum: u32,
263    /// The maximum number of elements in this table.
264    pub maximum: Option<u32>,
265}
266
267impl TableDescriptor {
268    pub(crate) fn fits_in_imported(&self, imported: TableDescriptor) -> bool {
269        // TODO: We should define implementation limits.
270        let imported_max = imported.maximum.unwrap_or(u32::max_value());
271        let self_max = self.maximum.unwrap_or(u32::max_value());
272        self.element == imported.element
273            && imported_max <= self_max
274            && self.minimum <= imported.minimum
275    }
276}
277
278/// A const value initializer.
279/// Over time, this will be able to represent more and more
280/// complex expressions.
281#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
282pub enum Initializer {
283    /// Corresponds to a `const.*` instruction.
284    Const(Value),
285    /// Corresponds to a `get_global` instruction.
286    GetGlobal(ImportedGlobalIndex),
287}
288
289/// Describes the mutability and type of a Global
290#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
291pub struct GlobalDescriptor {
292    /// Mutable flag.
293    pub mutable: bool,
294    /// Wasm type.
295    pub ty: Type,
296}
297
298/// A wasm global.
299#[derive(Serialize, Deserialize, Debug, Clone)]
300pub struct GlobalInit {
301    /// Global descriptor.
302    pub desc: GlobalDescriptor,
303    /// Global initializer.
304    pub init: Initializer,
305}
306
307/// A wasm memory descriptor.
308#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
309pub struct MemoryDescriptor {
310    /// The minimum number of allowed pages.
311    pub minimum: Pages,
312    /// The maximum number of allowed pages.
313    pub maximum: Option<Pages>,
314    /// This memory can be shared between wasm threads.
315    pub shared: bool,
316    /// The type of the memory
317    pub memory_type: MemoryType,
318}
319
320impl MemoryDescriptor {
321    /// Create a new memory descriptor with the given min/max pages and shared flag.
322    pub fn new(minimum: Pages, maximum: Option<Pages>, shared: bool) -> Result<Self, String> {
323        let memory_type = match (maximum.is_some(), shared) {
324            (true, true) => MemoryType::SharedStatic,
325            (true, false) => MemoryType::Static,
326            (false, false) => MemoryType::Dynamic,
327            (false, true) => {
328                return Err("Max number of pages is required for shared memory".to_string());
329            }
330        };
331        Ok(MemoryDescriptor {
332            minimum,
333            maximum,
334            shared,
335            memory_type,
336        })
337    }
338
339    /// Returns the `MemoryType` for this descriptor.
340    pub fn memory_type(&self) -> MemoryType {
341        self.memory_type
342    }
343
344    pub(crate) fn fits_in_imported(&self, imported: MemoryDescriptor) -> bool {
345        let imported_max = imported.maximum.unwrap_or(Pages(65_536));
346        let self_max = self.maximum.unwrap_or(Pages(65_536));
347
348        self.shared == imported.shared
349            && imported_max <= self_max
350            && self.minimum <= imported.minimum
351    }
352}
353
354/// The signature of a function that is either implemented
355/// in a wasm module or exposed to wasm by the host.
356#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
357pub struct FuncSig {
358    params: Cow<'static, [Type]>,
359    returns: Cow<'static, [Type]>,
360}
361
362/// Information about a function.
363pub type FuncDescriptor = FuncSig;
364
365impl FuncSig {
366    /// Creates a new function signatures with the given parameter and return types.
367    pub fn new<Params, Returns>(params: Params, returns: Returns) -> Self
368    where
369        Params: Into<Cow<'static, [Type]>>,
370        Returns: Into<Cow<'static, [Type]>>,
371    {
372        Self {
373            params: params.into(),
374            returns: returns.into(),
375        }
376    }
377
378    /// Parameter types.
379    pub fn params(&self) -> &[Type] {
380        &self.params
381    }
382
383    /// Return types.
384    pub fn returns(&self) -> &[Type] {
385        &self.returns
386    }
387
388    /// Returns true if parameter types match the function signature.
389    pub fn check_param_value_types(&self, params: &[Value]) -> bool {
390        self.params.len() == params.len()
391            && self
392                .params
393                .iter()
394                .zip(params.iter().map(|val| val.ty()))
395                .all(|(t0, ref t1)| t0 == t1)
396    }
397}
398
399impl std::fmt::Display for FuncSig {
400    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
401        let params = self
402            .params
403            .iter()
404            .map(|p| p.to_string())
405            .collect::<Vec<_>>()
406            .join(", ");
407        let returns = self
408            .returns
409            .iter()
410            .map(|p| p.to_string())
411            .collect::<Vec<_>>()
412            .join(", ");
413        write!(f, "[{}] -> [{}]", params, returns)
414    }
415}
416
417/// Trait that represents Local or Import.
418pub trait LocalImport {
419    /// Local type.
420    type Local: TypedIndex;
421    /// Import type.
422    type Import: TypedIndex;
423}
424
425#[rustfmt::skip]
426macro_rules! define_map_index {
427    ($ty:ident) => {
428        /// Typed Index
429        #[derive(Serialize, Deserialize)]
430        #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
431        pub struct $ty (u32);
432        impl TypedIndex for $ty {
433            #[doc(hidden)]
434            fn new(index: usize) -> Self {
435                $ty (index as _)
436            }
437
438            #[doc(hidden)]
439            fn index(&self) -> usize {
440                self.0 as usize
441            }
442        }
443    };
444    ($($normal_ty:ident,)* | local: $($local_ty:ident,)* | imported: $($imported_ty:ident,)*) => {
445        $(
446            define_map_index!($normal_ty);
447            define_map_index!($local_ty);
448            define_map_index!($imported_ty);
449
450            impl LocalImport for $normal_ty {
451                type Local = $local_ty;
452                type Import = $imported_ty;
453            }
454        )*
455    };
456}
457
458#[rustfmt::skip]
459define_map_index![
460    FuncIndex, MemoryIndex, TableIndex, GlobalIndex,
461    | local: LocalFuncIndex, LocalMemoryIndex, LocalTableIndex, LocalGlobalIndex,
462    | imported: ImportedFuncIndex, ImportedMemoryIndex, ImportedTableIndex, ImportedGlobalIndex,
463];
464
465#[rustfmt::skip]
466macro_rules! define_local_or_import {
467    ($ty:ident, $local_ty:ident, $imported_ty:ident, $imports:ident) => {
468        impl $ty {
469            /// Converts self into `LocalOrImport`.
470            pub fn local_or_import(self, info: &ModuleInfo) -> LocalOrImport<$ty> {
471                if self.index() < info.$imports.len() {
472                    LocalOrImport::Import(<Self as LocalImport>::Import::new(self.index()))
473                } else {
474                    LocalOrImport::Local(<Self as LocalImport>::Local::new(self.index() - info.$imports.len()))
475                }
476            }
477        }
478
479        impl $local_ty {
480            /// Convert up.
481            pub fn convert_up(self, info: &ModuleInfo) -> $ty {
482                $ty ((self.index() + info.$imports.len()) as u32)
483            }
484        }
485
486        impl $imported_ty {
487            /// Convert up.
488            pub fn convert_up(self, _info: &ModuleInfo) -> $ty {
489                $ty (self.index() as u32)
490            }
491        }
492    };
493    ($(($ty:ident | ($local_ty:ident, $imported_ty:ident): $imports:ident),)*) => {
494        $(
495            define_local_or_import!($ty, $local_ty, $imported_ty, $imports);
496        )*
497    };
498}
499
500#[rustfmt::skip]
501define_local_or_import![
502    (FuncIndex | (LocalFuncIndex, ImportedFuncIndex): imported_functions),
503    (MemoryIndex | (LocalMemoryIndex, ImportedMemoryIndex): imported_memories),
504    (TableIndex | (LocalTableIndex, ImportedTableIndex): imported_tables),
505    (GlobalIndex | (LocalGlobalIndex, ImportedGlobalIndex): imported_globals),
506];
507
508/// Index for signature.
509#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)]
510pub struct SigIndex(u32);
511impl TypedIndex for SigIndex {
512    #[doc(hidden)]
513    fn new(index: usize) -> Self {
514        SigIndex(index as _)
515    }
516
517    #[doc(hidden)]
518    fn index(&self) -> usize {
519        self.0 as usize
520    }
521}
522
523/// Kind of local or import type.
524pub enum LocalOrImport<T>
525where
526    T: LocalImport,
527{
528    /// Local.
529    Local(T::Local),
530    /// Import.
531    Import(T::Import),
532}
533
534impl<T> LocalOrImport<T>
535where
536    T: LocalImport,
537{
538    /// Returns `Some` if self is local,  `None` if self is an import.
539    pub fn local(self) -> Option<T::Local> {
540        match self {
541            LocalOrImport::Local(local) => Some(local),
542            LocalOrImport::Import(_) => None,
543        }
544    }
545
546    /// Returns `Some` if self is an import,  `None` if self is local.
547    pub fn import(self) -> Option<T::Import> {
548        match self {
549            LocalOrImport::Import(import) => Some(import),
550            LocalOrImport::Local(_) => None,
551        }
552    }
553}
554
555/// Information about an import such as its type and metadata.
556#[derive(Debug, Clone, PartialEq, Eq)]
557pub enum ExternDescriptor {
558    /// The import is a function.
559    Function(FuncDescriptor),
560    /// The import is a global variable.
561    Global(GlobalDescriptor),
562    /// The import is a Wasm linear memory.
563    Memory(MemoryDescriptor),
564    /// The import is a Wasm table.
565    Table(TableDescriptor),
566}
567
568impl From<FuncDescriptor> for ExternDescriptor {
569    fn from(other: FuncDescriptor) -> Self {
570        ExternDescriptor::Function(other)
571    }
572}
573impl From<&FuncDescriptor> for ExternDescriptor {
574    fn from(other: &FuncDescriptor) -> Self {
575        ExternDescriptor::Function(other.clone())
576    }
577}
578impl From<MemoryDescriptor> for ExternDescriptor {
579    fn from(other: MemoryDescriptor) -> Self {
580        ExternDescriptor::Memory(other)
581    }
582}
583impl From<&MemoryDescriptor> for ExternDescriptor {
584    fn from(other: &MemoryDescriptor) -> Self {
585        ExternDescriptor::Memory(*other)
586    }
587}
588
589impl From<TableDescriptor> for ExternDescriptor {
590    fn from(other: TableDescriptor) -> Self {
591        ExternDescriptor::Table(other)
592    }
593}
594impl From<&TableDescriptor> for ExternDescriptor {
595    fn from(other: &TableDescriptor) -> Self {
596        ExternDescriptor::Table(*other)
597    }
598}
599impl From<GlobalDescriptor> for ExternDescriptor {
600    fn from(other: GlobalDescriptor) -> Self {
601        ExternDescriptor::Global(other)
602    }
603}
604impl From<&GlobalDescriptor> for ExternDescriptor {
605    fn from(other: &GlobalDescriptor) -> Self {
606        ExternDescriptor::Global(*other)
607    }
608}
609
610/// A type describing an import that a [`Module`] needs to be instantiated.
611#[derive(Debug, Clone, PartialEq, Eq)]
612pub struct ImportDescriptor {
613    /// The namespace that this import is in.
614    pub namespace: String,
615    /// The name of the import.
616    pub name: String,
617    /// The type of the import and information about the import.
618    pub ty: ExternDescriptor,
619}
620
621/// Type describing an export that the [`Module`] provides.
622#[derive(Debug, Clone, PartialEq, Eq)]
623pub struct ExportDescriptor<'a> {
624    /// The name identifying the export.
625    pub name: &'a str,
626    /// The type of the export.
627    pub ty: ExternDescriptor,
628}
629
630#[cfg(test)]
631mod tests {
632    use crate::types::NativeWasmType;
633    use crate::types::WasmExternType;
634
635    #[test]
636    fn test_native_types_round_trip() {
637        assert_eq!(
638            42i32,
639            i32::from_native(i32::from_binary((42i32).to_native().to_binary()))
640        );
641
642        assert_eq!(
643            -42i32,
644            i32::from_native(i32::from_binary((-42i32).to_native().to_binary()))
645        );
646
647        use std::i64;
648        let xi64 = i64::MAX;
649        assert_eq!(
650            xi64,
651            i64::from_native(i64::from_binary((xi64).to_native().to_binary()))
652        );
653        let yi64 = i64::MIN;
654        assert_eq!(
655            yi64,
656            i64::from_native(i64::from_binary((yi64).to_native().to_binary()))
657        );
658
659        assert_eq!(
660            16.5f32,
661            f32::from_native(f32::from_binary((16.5f32).to_native().to_binary()))
662        );
663
664        assert_eq!(
665            -16.5f32,
666            f32::from_native(f32::from_binary((-16.5f32).to_native().to_binary()))
667        );
668
669        use std::f64;
670        let xf64: f64 = f64::MAX;
671        assert_eq!(
672            xf64,
673            f64::from_native(f64::from_binary((xf64).to_native().to_binary()))
674        );
675
676        let yf64: f64 = f64::MIN;
677        assert_eq!(
678            yf64,
679            f64::from_native(f64::from_binary((yf64).to_native().to_binary()))
680        );
681    }
682}