wasmtime/runtime/externals/
global.rs

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