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