wasmtime/runtime/externals/
global.rs

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