wasmtime/runtime/externals/
global.rs

1use crate::prelude::*;
2use crate::runtime::vm::{GcRootsList, SendSyncPtr};
3use crate::{
4    store::{AutoAssertNoGc, StoreData, StoreOpaque, Stored},
5    trampoline::generate_global_export,
6    AnyRef, AsContext, AsContextMut, ExternRef, Func, GlobalType, HeapType, Mutability, Ref,
7    RootedGcRefImpl, Val, ValType,
8};
9use core::ptr;
10use core::ptr::NonNull;
11use wasmtime_environ::TypeTrace;
12
13/// A WebAssembly `global` value which can be read and written to.
14///
15/// A `global` in WebAssembly is sort of like a global variable within an
16/// [`Instance`](crate::Instance). The `global.get` and `global.set`
17/// instructions will modify and read global values in a wasm module. Globals
18/// can either be imported or exported from wasm modules.
19///
20/// A [`Global`] "belongs" to the store that it was originally created within
21/// (either via [`Global::new`] or via instantiating a
22/// [`Module`](crate::Module)). Operations on a [`Global`] only work with the
23/// store it belongs to, and if another store is passed in by accident then
24/// methods will panic.
25#[derive(Copy, Clone, Debug)]
26#[repr(transparent)] // here for the C API
27pub struct Global(pub(super) Stored<crate::runtime::vm::ExportGlobal>);
28
29impl Global {
30    /// Creates a new WebAssembly `global` value with the provide type `ty` and
31    /// initial value `val`.
32    ///
33    /// The `store` argument will be the owner of the [`Global`] returned. Using
34    /// the returned [`Global`] other items in the store may access this global.
35    /// For example this could be provided as an argument to
36    /// [`Instance::new`](crate::Instance::new) or
37    /// [`Linker::define`](crate::Linker::define).
38    ///
39    /// # Errors
40    ///
41    /// Returns an error if the `ty` provided does not match the type of the
42    /// value `val`, or if `val` comes from a different store than `store`.
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// # use wasmtime::*;
48    /// # fn main() -> anyhow::Result<()> {
49    /// let engine = Engine::default();
50    /// let mut store = Store::new(&engine, ());
51    ///
52    /// let ty = GlobalType::new(ValType::I32, Mutability::Const);
53    /// let i32_const = Global::new(&mut store, ty, 1i32.into())?;
54    /// let ty = GlobalType::new(ValType::F64, Mutability::Var);
55    /// let f64_mut = Global::new(&mut store, ty, 2.0f64.into())?;
56    ///
57    /// let module = Module::new(
58    ///     &engine,
59    ///     "(module
60    ///         (global (import \"\" \"i32-const\") i32)
61    ///         (global (import \"\" \"f64-mut\") (mut f64))
62    ///     )"
63    /// )?;
64    ///
65    /// let mut linker = Linker::new(&engine);
66    /// linker.define(&store, "", "i32-const", i32_const)?;
67    /// linker.define(&store, "", "f64-mut", f64_mut)?;
68    ///
69    /// let instance = linker.instantiate(&mut store, &module)?;
70    /// // ...
71    /// # Ok(())
72    /// # }
73    /// ```
74    pub fn new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result<Global> {
75        Global::_new(store.as_context_mut().0, ty, val)
76    }
77
78    fn _new(store: &mut StoreOpaque, ty: GlobalType, val: Val) -> Result<Global> {
79        val.ensure_matches_ty(store, ty.content()).context(
80            "type mismatch: initial value provided does not match the type of this global",
81        )?;
82        unsafe {
83            let wasmtime_export = generate_global_export(store, ty, val)?;
84            Ok(Global::from_wasmtime_global(wasmtime_export, store))
85        }
86    }
87
88    /// Returns the underlying type of this `global`.
89    ///
90    /// # Panics
91    ///
92    /// Panics if `store` does not own this global.
93    pub fn ty(&self, store: impl AsContext) -> GlobalType {
94        self._ty(store.as_context().0)
95    }
96
97    pub(crate) fn _ty(&self, store: &StoreOpaque) -> GlobalType {
98        let ty = &store[self.0].global;
99        GlobalType::from_wasmtime_global(store.engine(), &ty)
100    }
101
102    /// Returns the current [`Val`] of this global.
103    ///
104    /// # Panics
105    ///
106    /// Panics if `store` does not own this global.
107    pub fn get(&self, mut store: impl AsContextMut) -> Val {
108        unsafe {
109            let store = store.as_context_mut();
110            let mut store = AutoAssertNoGc::new(store.0);
111            let definition = &*store[self.0].definition;
112            match self._ty(&store).content() {
113                ValType::I32 => Val::from(*definition.as_i32()),
114                ValType::I64 => Val::from(*definition.as_i64()),
115                ValType::F32 => Val::F32(*definition.as_u32()),
116                ValType::F64 => Val::F64(*definition.as_u64()),
117                ValType::V128 => Val::V128((*definition.as_u128()).into()),
118                ValType::Ref(ref_ty) => {
119                    let reference: Ref = match ref_ty.heap_type() {
120                        HeapType::Func | HeapType::ConcreteFunc(_) => {
121                            Func::_from_raw(&mut store, definition.as_func_ref().cast()).into()
122                        }
123
124                        HeapType::NoFunc => Ref::Func(None),
125
126                        HeapType::Extern => Ref::Extern(
127                            definition
128                                .as_gc_ref()
129                                .map(|r| {
130                                    let r = store.unwrap_gc_store_mut().clone_gc_ref(r);
131                                    ExternRef::from_cloned_gc_ref(&mut store, r)
132                                })
133                                .into(),
134                        ),
135
136                        HeapType::NoExtern => Ref::Extern(None),
137
138                        HeapType::Any
139                        | HeapType::Eq
140                        | HeapType::I31
141                        | HeapType::Struct
142                        | HeapType::ConcreteStruct(_)
143                        | HeapType::Array
144                        | HeapType::ConcreteArray(_) => definition
145                            .as_gc_ref()
146                            .map(|r| {
147                                let r = store.unwrap_gc_store_mut().clone_gc_ref(r);
148                                AnyRef::from_cloned_gc_ref(&mut store, r)
149                            })
150                            .into(),
151
152                        HeapType::None => Ref::Any(None),
153                    };
154                    debug_assert!(
155                        ref_ty.is_nullable() || !reference.is_null(),
156                        "if the type is non-nullable, we better have a non-null reference"
157                    );
158                    reference.into()
159                }
160            }
161        }
162    }
163
164    /// Attempts to set the current value of this global to [`Val`].
165    ///
166    /// # Errors
167    ///
168    /// Returns an error if this global has a different type than `Val`, if
169    /// it's not a mutable global, or if `val` comes from a different store than
170    /// the one provided.
171    ///
172    /// # Panics
173    ///
174    /// Panics if `store` does not own this global.
175    pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> {
176        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
177        let global_ty = self._ty(&store);
178        if global_ty.mutability() != Mutability::Var {
179            bail!("immutable global cannot be set");
180        }
181        val.ensure_matches_ty(&store, global_ty.content())
182            .context("type mismatch: attempt to set global to value of wrong type")?;
183        unsafe {
184            let definition = &mut *store[self.0].definition;
185            match val {
186                Val::I32(i) => *definition.as_i32_mut() = i,
187                Val::I64(i) => *definition.as_i64_mut() = i,
188                Val::F32(f) => *definition.as_u32_mut() = f,
189                Val::F64(f) => *definition.as_u64_mut() = f,
190                Val::V128(i) => *definition.as_u128_mut() = i.into(),
191                Val::FuncRef(f) => {
192                    *definition.as_func_ref_mut() = f.map_or(ptr::null_mut(), |f| {
193                        f.vm_func_ref(&mut store).as_ptr().cast()
194                    });
195                }
196                Val::ExternRef(e) => {
197                    let new = match e {
198                        None => None,
199                        #[cfg_attr(not(feature = "gc"), allow(unreachable_patterns))]
200                        Some(e) => Some(e.try_gc_ref(&mut store)?.unchecked_copy()),
201                    };
202                    let new = new.as_ref();
203                    definition.write_gc_ref(store.unwrap_gc_store_mut(), new);
204                }
205                Val::AnyRef(a) => {
206                    let new = match a {
207                        None => None,
208                        #[cfg_attr(not(feature = "gc"), allow(unreachable_patterns))]
209                        Some(a) => Some(a.try_gc_ref(&mut store)?.unchecked_copy()),
210                    };
211                    let new = new.as_ref();
212                    definition.write_gc_ref(store.unwrap_gc_store_mut(), new);
213                }
214            }
215        }
216        Ok(())
217    }
218
219    pub(crate) fn trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut GcRootsList) {
220        if let Some(ref_ty) = self._ty(store).content().as_ref() {
221            if !ref_ty.is_vmgcref_type_and_points_to_object() {
222                return;
223            }
224
225            if let Some(gc_ref) = unsafe { (*store[self.0].definition).as_gc_ref() } {
226                let gc_ref = NonNull::from(gc_ref);
227                let gc_ref = SendSyncPtr::new(gc_ref);
228                unsafe {
229                    gc_roots_list.add_root(gc_ref, "Wasm global");
230                }
231            }
232        }
233    }
234
235    pub(crate) unsafe fn from_wasmtime_global(
236        mut wasmtime_export: crate::runtime::vm::ExportGlobal,
237        store: &mut StoreOpaque,
238    ) -> Global {
239        wasmtime_export
240            .global
241            .wasm_ty
242            .canonicalize_for_runtime_usage(&mut |module_index| {
243                crate::runtime::vm::Instance::from_vmctx(wasmtime_export.vmctx, |instance| {
244                    instance.engine_type_index(module_index)
245                })
246            });
247
248        Global(store.store_data_mut().insert(wasmtime_export))
249    }
250
251    pub(crate) fn wasmtime_ty<'a>(&self, data: &'a StoreData) -> &'a wasmtime_environ::Global {
252        &data[self.0].global
253    }
254
255    pub(crate) fn vmimport(&self, store: &StoreOpaque) -> crate::runtime::vm::VMGlobalImport {
256        crate::runtime::vm::VMGlobalImport {
257            from: store[self.0].definition,
258        }
259    }
260
261    /// Get a stable hash key for this global.
262    ///
263    /// Even if the same underlying global definition is added to the
264    /// `StoreData` multiple times and becomes multiple `wasmtime::Global`s,
265    /// this hash key will be consistent across all of these globals.
266    pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq {
267        store[self.0].definition as usize
268    }
269}
270
271#[cfg(test)]
272mod tests {
273    use super::*;
274    use crate::{Instance, Module, Store};
275
276    #[test]
277    fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
278        let mut store = Store::<()>::default();
279        let module = Module::new(
280            store.engine(),
281            r#"
282                (module
283                    (global (export "g") (mut i32) (i32.const 0))
284                )
285            "#,
286        )?;
287        let instance = Instance::new(&mut store, &module, &[])?;
288
289        // Each time we `get_global`, we call `Global::from_wasmtime` which adds
290        // a new entry to `StoreData`, so `g1` and `g2` will have different
291        // indices into `StoreData`.
292        let g1 = instance.get_global(&mut store, "g").unwrap();
293        let g2 = instance.get_global(&mut store, "g").unwrap();
294
295        // That said, they really point to the same global.
296        assert_eq!(g1.get(&mut store).unwrap_i32(), 0);
297        assert_eq!(g2.get(&mut store).unwrap_i32(), 0);
298        g1.set(&mut store, Val::I32(42))?;
299        assert_eq!(g1.get(&mut store).unwrap_i32(), 42);
300        assert_eq!(g2.get(&mut store).unwrap_i32(), 42);
301
302        // And therefore their hash keys are the same.
303        assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0));
304
305        // But the hash keys are different from different globals.
306        let instance2 = Instance::new(&mut store, &module, &[])?;
307        let g3 = instance2.get_global(&mut store, "g").unwrap();
308        assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0));
309
310        Ok(())
311    }
312}