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