Skip to main content

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