unc_vm_types/
types.rs

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