near_vm_vm/
global.rs

1use crate::vmcontext::VMGlobalDefinition;
2use near_vm_types::{GlobalType, Mutability, Type, Value, WasmValueType};
3use parking_lot::Mutex;
4use std::cell::UnsafeCell;
5use std::ptr::NonNull;
6use thiserror::Error;
7
8#[derive(Debug)]
9/// A Global instance
10pub struct Global {
11    ty: GlobalType,
12    // TODO: this box is unnecessary
13    vm_global_definition: Box<UnsafeCell<VMGlobalDefinition>>,
14    // used to synchronize gets/sets
15    lock: Mutex<()>,
16}
17
18/// # Safety
19/// This is safe to send between threads because there is no-thread specific logic.
20/// TODO: look into other reasons that make something not `Send`
21unsafe impl Send for Global {}
22/// # Safety
23/// This is safe to share between threads because it uses a `Mutex` internally.
24unsafe impl Sync for Global {}
25
26/// Error type describing things that can go wrong when operating on Wasm Globals.
27#[derive(Error, Debug, Clone, PartialEq, Hash)]
28pub enum GlobalError {
29    /// The error returned when attempting to set an immutable global.
30    #[error("Attempted to set an immutable global")]
31    ImmutableGlobalCannotBeSet,
32
33    /// The error returned when attempting to operate on a global as a specific type
34    /// that differs from the global's own type.
35    #[error("Attempted to operate on a global of type {expected} as a global of type {found}")]
36    IncorrectType {
37        /// The type that the global is.
38        expected: Type,
39        /// The type that we were asked to use it as.
40        found: Type,
41    },
42}
43
44impl Global {
45    /// Create a new, zero bit-pattern initialized global from a [`GlobalType`].
46    pub fn new(global_type: GlobalType) -> Self {
47        Self {
48            ty: global_type,
49            vm_global_definition: Box::new(UnsafeCell::new(VMGlobalDefinition::new())),
50            lock: Mutex::new(()),
51        }
52    }
53
54    /// Get the type of the global.
55    pub fn ty(&self) -> &GlobalType {
56        &self.ty
57    }
58
59    /// Get a pointer to the underlying definition used by the generated code.
60    pub fn vmglobal(&self) -> NonNull<VMGlobalDefinition> {
61        let ptr = self.vm_global_definition.as_ref() as *const UnsafeCell<VMGlobalDefinition>
62            as *const VMGlobalDefinition as *mut VMGlobalDefinition;
63        unsafe { NonNull::new_unchecked(ptr) }
64    }
65
66    /// Get a value from the global.
67    // TODO(reftypes): the `&dyn Any` here for `Store` is a work-around for the fact
68    // that `Store` is defined in `API` when we need it earlier. Ideally this should
69    // be removed.
70    pub fn get<T: WasmValueType>(&self, store: &dyn std::any::Any) -> Value<T> {
71        let _global_guard = self.lock.lock();
72        unsafe {
73            let definition = &*self.vm_global_definition.get();
74            match self.ty().ty {
75                Type::I32 => Value::I32(definition.to_i32()),
76                Type::I64 => Value::I64(definition.to_i64()),
77                Type::F32 => Value::F32(definition.to_f32()),
78                Type::F64 => Value::F64(definition.to_f64()),
79                Type::V128 => Value::V128(definition.to_u128()),
80                Type::ExternRef => Value::ExternRef(definition.to_externref().into()),
81                Type::FuncRef => {
82                    let p = definition.to_u128() as i128;
83                    if p as usize == 0 {
84                        Value::FuncRef(None)
85                    } else {
86                        let v = T::read_value_from(store, &p);
87                        Value::FuncRef(Some(v))
88                    }
89                }
90            }
91        }
92    }
93
94    /// Set a value for the global.
95    ///
96    /// # Safety
97    /// The caller should check that the `val` comes from the same store as this global.
98    pub unsafe fn set<T: WasmValueType>(&self, val: Value<T>) -> Result<(), GlobalError> {
99        let _global_guard = self.lock.lock();
100        if self.ty().mutability != Mutability::Var {
101            return Err(GlobalError::ImmutableGlobalCannotBeSet);
102        }
103        if val.ty() != self.ty().ty {
104            return Err(GlobalError::IncorrectType { expected: self.ty.ty, found: val.ty() });
105        }
106        unsafe { self.set_unchecked(val) }
107    }
108
109    /// Set a value from the global (unchecked)
110    ///
111    /// # Safety
112    /// The caller should check that the `val` comes from the same store as this global.
113    /// The caller should also ensure that this global is synchronized. Otherwise, use
114    /// `set` instead.
115    pub unsafe fn set_unchecked<T: WasmValueType>(&self, val: Value<T>) -> Result<(), GlobalError> {
116        // ideally we'd use atomics for the global value rather than needing to lock it
117        unsafe {
118            let definition = &mut *self.vm_global_definition.get();
119            match val {
120                Value::I32(i) => *definition.as_i32_mut() = i,
121                Value::I64(i) => *definition.as_i64_mut() = i,
122                Value::F32(f) => *definition.as_f32_mut() = f,
123                Value::F64(f) => *definition.as_f64_mut() = f,
124                Value::V128(x) => *definition.as_bytes_mut() = x.to_ne_bytes(),
125                Value::ExternRef(r) => {
126                    let extern_ref = definition.as_externref_mut();
127                    extern_ref.ref_drop();
128                    *extern_ref = r.into()
129                }
130                Value::FuncRef(None) => *definition.as_u128_mut() = 0,
131                Value::FuncRef(Some(r)) => {
132                    r.write_value_to(definition.as_u128_mut() as *mut u128 as *mut i128)
133                }
134            }
135        }
136        Ok(())
137    }
138}