wasmtime/runtime/externals/
global.rs

1use crate::prelude::*;
2use crate::runtime::vm::{self, VMGlobalDefinition, VMGlobalKind, VMOpaqueContext};
3use crate::{
4    AnyRef, AsContext, AsContextMut, ExnRef, ExternRef, Func, GlobalType, HeapType, Mutability,
5    Ref, RootedGcRefImpl, Val, ValType,
6    store::{AutoAssertNoGc, InstanceId, StoreId, StoreInstanceId, StoreOpaque},
7    trampoline::generate_global_export,
8};
9use core::ptr;
10use core::ptr::NonNull;
11use wasmtime_environ::DefinedGlobalIndex;
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(C)] // here for the C API
27pub struct Global {
28    /// The store that this global belongs to.
29    store: StoreId,
30    /// Either `InstanceId` or `ComponentInstanceId` internals depending on
31    /// `kind` below.
32    instance: u32,
33    /// Which method of definition was used when creating this global.
34    kind: VMGlobalKind,
35}
36
37// Double-check that the C representation in `extern.h` matches our in-Rust
38// representation here in terms of size/alignment/etc.
39const _: () = {
40    #[repr(C)]
41    struct C(u64, u32, u32, u32);
42    assert!(core::mem::size_of::<C>() == core::mem::size_of::<Global>());
43    assert!(core::mem::align_of::<C>() == core::mem::align_of::<Global>());
44    assert!(core::mem::offset_of!(Global, store) == 0);
45};
46
47impl Global {
48    /// Creates a new WebAssembly `global` value with the provide type `ty` and
49    /// initial value `val`.
50    ///
51    /// The `store` argument will be the owner of the [`Global`] returned. Using
52    /// the returned [`Global`] other items in the store may access this global.
53    /// For example this could be provided as an argument to
54    /// [`Instance::new`](crate::Instance::new) or
55    /// [`Linker::define`](crate::Linker::define).
56    ///
57    /// # Errors
58    ///
59    /// Returns an error if the `ty` provided does not match the type of the
60    /// value `val`, or if `val` comes from a different store than `store`.
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// # use wasmtime::*;
66    /// # fn main() -> anyhow::Result<()> {
67    /// let engine = Engine::default();
68    /// let mut store = Store::new(&engine, ());
69    ///
70    /// let ty = GlobalType::new(ValType::I32, Mutability::Const);
71    /// let i32_const = Global::new(&mut store, ty, 1i32.into())?;
72    /// let ty = GlobalType::new(ValType::F64, Mutability::Var);
73    /// let f64_mut = Global::new(&mut store, ty, 2.0f64.into())?;
74    ///
75    /// let module = Module::new(
76    ///     &engine,
77    ///     "(module
78    ///         (global (import \"\" \"i32-const\") i32)
79    ///         (global (import \"\" \"f64-mut\") (mut f64))
80    ///     )"
81    /// )?;
82    ///
83    /// let mut linker = Linker::new(&engine);
84    /// linker.define(&store, "", "i32-const", i32_const)?;
85    /// linker.define(&store, "", "f64-mut", f64_mut)?;
86    ///
87    /// let instance = linker.instantiate(&mut store, &module)?;
88    /// // ...
89    /// # Ok(())
90    /// # }
91    /// ```
92    pub fn new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result<Global> {
93        Global::_new(store.as_context_mut().0, ty, val)
94    }
95
96    fn _new(store: &mut StoreOpaque, ty: GlobalType, val: Val) -> Result<Global> {
97        val.ensure_matches_ty(store, ty.content()).context(
98            "type mismatch: initial value provided does not match the type of this global",
99        )?;
100        generate_global_export(store, ty, val)
101    }
102
103    pub(crate) fn new_host(store: &StoreOpaque, index: DefinedGlobalIndex) -> Global {
104        Global {
105            store: store.id(),
106            instance: 0,
107            kind: VMGlobalKind::Host(index),
108        }
109    }
110
111    pub(crate) fn new_instance(
112        store: &StoreOpaque,
113        instance: InstanceId,
114        index: DefinedGlobalIndex,
115    ) -> Global {
116        Global {
117            store: store.id(),
118            instance: instance.as_u32(),
119            kind: VMGlobalKind::Instance(index),
120        }
121    }
122
123    /// Returns the underlying type of this `global`.
124    ///
125    /// # Panics
126    ///
127    /// Panics if `store` does not own this global.
128    pub fn ty(&self, store: impl AsContext) -> GlobalType {
129        self._ty(store.as_context().0)
130    }
131
132    pub(crate) fn _ty(&self, store: &StoreOpaque) -> GlobalType {
133        GlobalType::from_wasmtime_global(store.engine(), self.wasmtime_ty(store))
134    }
135
136    /// Returns the current [`Val`] of this global.
137    ///
138    /// # Panics
139    ///
140    /// Panics if `store` does not own this global.
141    pub fn get(&self, mut store: impl AsContextMut) -> Val {
142        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
143        self._get(&mut store)
144    }
145
146    pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>) -> Val {
147        unsafe {
148            let definition = self.definition(store).as_ref();
149            match self._ty(&store).content() {
150                ValType::I32 => Val::from(*definition.as_i32()),
151                ValType::I64 => Val::from(*definition.as_i64()),
152                ValType::F32 => Val::F32(*definition.as_u32()),
153                ValType::F64 => Val::F64(*definition.as_u64()),
154                ValType::V128 => Val::V128(definition.get_u128().into()),
155                ValType::Ref(ref_ty) => {
156                    let reference: Ref = match ref_ty.heap_type() {
157                        HeapType::Func | HeapType::ConcreteFunc(_) => {
158                            Func::_from_raw(store, definition.as_func_ref().cast()).into()
159                        }
160
161                        HeapType::NoFunc => Ref::Func(None),
162
163                        HeapType::Extern => Ref::Extern(definition.as_gc_ref().map(|r| {
164                            let r = store.clone_gc_ref(r);
165                            ExternRef::from_cloned_gc_ref(store, r)
166                        })),
167
168                        HeapType::NoCont | HeapType::ConcreteCont(_) | HeapType::Cont => {
169                            // TODO(#10248) Required to support stack switching in the embedder API.
170                            unimplemented!()
171                        }
172
173                        HeapType::NoExtern => Ref::Extern(None),
174
175                        HeapType::Exn | HeapType::ConcreteExn(_) => definition
176                            .as_gc_ref()
177                            .map(|r| {
178                                let r = store.clone_gc_ref(r);
179                                ExnRef::from_cloned_gc_ref(store, r)
180                            })
181                            .into(),
182
183                        HeapType::Any
184                        | HeapType::Eq
185                        | HeapType::I31
186                        | HeapType::Struct
187                        | HeapType::ConcreteStruct(_)
188                        | HeapType::Array
189                        | HeapType::ConcreteArray(_) => definition
190                            .as_gc_ref()
191                            .map(|r| {
192                                let r = store.clone_gc_ref(r);
193                                AnyRef::from_cloned_gc_ref(store, r)
194                            })
195                            .into(),
196
197                        HeapType::NoExn => Ref::Exn(None),
198
199                        HeapType::None => Ref::Any(None),
200                    };
201                    debug_assert!(
202                        ref_ty.is_nullable() || !reference.is_null(),
203                        "if the type is non-nullable, we better have a non-null reference"
204                    );
205                    reference.into()
206                }
207            }
208        }
209    }
210
211    /// Attempts to set the current value of this global to [`Val`].
212    ///
213    /// # Errors
214    ///
215    /// Returns an error if this global has a different type than `Val`, if
216    /// it's not a mutable global, or if `val` comes from a different store than
217    /// the one provided.
218    ///
219    /// # Panics
220    ///
221    /// Panics if `store` does not own this global.
222    pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> {
223        self._set(store.as_context_mut().0, val)
224    }
225
226    pub(crate) fn _set(&self, store: &mut StoreOpaque, val: Val) -> Result<()> {
227        let global_ty = self._ty(&store);
228        if global_ty.mutability() != Mutability::Var {
229            bail!("immutable global cannot be set");
230        }
231        val.ensure_matches_ty(&store, global_ty.content())
232            .context("type mismatch: attempt to set global to value of wrong type")?;
233
234        // SAFETY: mutability and a type-check above makes this safe to perform.
235        unsafe { self.set_unchecked(store, &val) }
236    }
237
238    /// Sets this global to `val`.
239    ///
240    /// # Safety
241    ///
242    /// This function requires that `val` is of the correct type for this
243    /// global. Furthermore this requires that the global is mutable or this is
244    /// the first time the global is initialized.
245    pub(crate) unsafe fn set_unchecked(&self, store: &mut StoreOpaque, val: &Val) -> Result<()> {
246        let mut store = AutoAssertNoGc::new(store);
247        unsafe {
248            let definition = self.definition(&store).as_mut();
249            match val {
250                Val::I32(i) => *definition.as_i32_mut() = *i,
251                Val::I64(i) => *definition.as_i64_mut() = *i,
252                Val::F32(f) => *definition.as_u32_mut() = *f,
253                Val::F64(f) => *definition.as_u64_mut() = *f,
254                Val::V128(i) => definition.set_u128((*i).into()),
255                Val::FuncRef(f) => {
256                    *definition.as_func_ref_mut() =
257                        f.map_or(ptr::null_mut(), |f| f.vm_func_ref(&store).as_ptr().cast());
258                }
259                Val::ExternRef(e) => {
260                    let new = match e {
261                        None => None,
262                        Some(e) => Some(e.try_gc_ref(&store)?.unchecked_copy()),
263                    };
264                    let new = new.as_ref();
265                    definition.write_gc_ref(&mut store, new);
266                }
267                Val::AnyRef(a) => {
268                    let new = match a {
269                        None => None,
270                        Some(a) => Some(a.try_gc_ref(&store)?.unchecked_copy()),
271                    };
272                    let new = new.as_ref();
273                    definition.write_gc_ref(&mut store, new);
274                }
275                Val::ExnRef(e) => {
276                    let new = match e {
277                        None => None,
278                        Some(e) => Some(e.try_gc_ref(&store)?.unchecked_copy()),
279                    };
280                    let new = new.as_ref();
281                    definition.write_gc_ref(&mut store, new);
282                }
283            }
284        }
285        Ok(())
286    }
287
288    #[cfg(feature = "gc")]
289    pub(crate) fn trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
290        if let Some(ref_ty) = self._ty(store).content().as_ref() {
291            if !ref_ty.is_vmgcref_type_and_points_to_object() {
292                return;
293            }
294
295            if let Some(gc_ref) = unsafe { self.definition(store).as_ref().as_gc_ref() } {
296                unsafe {
297                    gc_roots_list.add_root(gc_ref.into(), "Wasm global");
298                }
299            }
300        }
301    }
302
303    pub(crate) fn from_host(store: StoreId, index: DefinedGlobalIndex) -> Global {
304        Global {
305            store,
306            instance: 0,
307            kind: VMGlobalKind::Host(index),
308        }
309    }
310
311    pub(crate) fn from_core(instance: StoreInstanceId, index: DefinedGlobalIndex) -> Global {
312        Global {
313            store: instance.store_id(),
314            instance: instance.instance().as_u32(),
315            kind: VMGlobalKind::Instance(index),
316        }
317    }
318
319    #[cfg(feature = "component-model")]
320    pub(crate) fn from_component_flags(
321        instance: crate::component::store::StoreComponentInstanceId,
322        index: wasmtime_environ::component::RuntimeComponentInstanceIndex,
323    ) -> Global {
324        Global {
325            store: instance.store_id(),
326            instance: instance.instance().as_u32(),
327            kind: VMGlobalKind::ComponentFlags(index),
328        }
329    }
330
331    pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Global {
332        self.store.assert_belongs_to(store.id());
333        match self.kind {
334            VMGlobalKind::Instance(index) => {
335                let instance = InstanceId::from_u32(self.instance);
336                let module = store.instance(instance).env_module();
337                let index = module.global_index(index);
338                &module.globals[index]
339            }
340            VMGlobalKind::Host(index) => unsafe { &store.host_globals()[index].get().as_ref().ty },
341            #[cfg(feature = "component-model")]
342            VMGlobalKind::ComponentFlags(_) => {
343                const TY: wasmtime_environ::Global = wasmtime_environ::Global {
344                    mutability: true,
345                    wasm_ty: wasmtime_environ::WasmValType::I32,
346                };
347                &TY
348            }
349        }
350    }
351
352    pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMGlobalImport {
353        let vmctx = match self.kind {
354            VMGlobalKind::Instance(_) => {
355                let instance = InstanceId::from_u32(self.instance);
356                Some(VMOpaqueContext::from_vmcontext(store.instance(instance).vmctx()).into())
357            }
358            VMGlobalKind::Host(_) => None,
359            #[cfg(feature = "component-model")]
360            VMGlobalKind::ComponentFlags(_) => {
361                let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
362                Some(
363                    VMOpaqueContext::from_vmcomponent(store.component_instance(instance).vmctx())
364                        .into(),
365                )
366            }
367        };
368        vm::VMGlobalImport {
369            from: self.definition(store).into(),
370            vmctx,
371            kind: self.kind,
372        }
373    }
374
375    pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
376        store.id() == self.store
377    }
378
379    /// Get a stable hash key for this global.
380    ///
381    /// Even if the same underlying global definition is added to the
382    /// `StoreData` multiple times and becomes multiple `wasmtime::Global`s,
383    /// this hash key will be consistent across all of these globals.
384    #[cfg(feature = "coredump")]
385    pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<> {
386        self.definition(store).as_ptr().addr()
387    }
388
389    fn definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition> {
390        self.store.assert_belongs_to(store.id());
391        match self.kind {
392            VMGlobalKind::Instance(index) => {
393                let instance = InstanceId::from_u32(self.instance);
394                store.instance(instance).global_ptr(index)
395            }
396            VMGlobalKind::Host(index) => unsafe {
397                NonNull::from(&mut store.host_globals()[index].get().as_mut().global)
398            },
399            #[cfg(feature = "component-model")]
400            VMGlobalKind::ComponentFlags(index) => {
401                let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
402                store
403                    .component_instance(instance)
404                    .instance_flags(index)
405                    .as_raw()
406            }
407        }
408    }
409}
410
411#[cfg(test)]
412mod tests {
413    use super::*;
414    use crate::{Instance, Module, Store};
415
416    #[test]
417    fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
418        let mut store = Store::<()>::default();
419        let module = Module::new(
420            store.engine(),
421            r#"
422                (module
423                    (global (export "g") (mut i32) (i32.const 0))
424                )
425            "#,
426        )?;
427        let instance = Instance::new(&mut store, &module, &[])?;
428
429        // Each time we `get_global`, we call `Global::from_wasmtime` which adds
430        // a new entry to `StoreData`, so `g1` and `g2` will have different
431        // indices into `StoreData`.
432        let g1 = instance.get_global(&mut store, "g").unwrap();
433        let g2 = instance.get_global(&mut store, "g").unwrap();
434
435        // That said, they really point to the same global.
436        assert_eq!(g1.get(&mut store).unwrap_i32(), 0);
437        assert_eq!(g2.get(&mut store).unwrap_i32(), 0);
438        g1.set(&mut store, Val::I32(42))?;
439        assert_eq!(g1.get(&mut store).unwrap_i32(), 42);
440        assert_eq!(g2.get(&mut store).unwrap_i32(), 42);
441
442        // And therefore their hash keys are the same.
443        assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0));
444
445        // But the hash keys are different from different globals.
446        let instance2 = Instance::new(&mut store, &module, &[])?;
447        let g3 = instance2.get_global(&mut store, "g").unwrap();
448        assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0));
449
450        Ok(())
451    }
452}