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