1 use crate::prelude::*;
2 use crate::runtime::vm::{self, VMGlobalDefinition, VMGlobalKind, VMOpaqueContext};
3 use 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 };
9 use core::ptr;
10 use core::ptr::NonNull;
11 use 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
27 pub 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.
39 const _: () = {
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 
47 impl 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() -> 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     /// ```
new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result<Global>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 
_new(store: &mut StoreOpaque, ty: GlobalType, val: Val) -> Result<Global>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 
new_host(store: &StoreOpaque, index: DefinedGlobalIndex) -> Global103     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 
new_instance( store: &StoreOpaque, instance: InstanceId, index: DefinedGlobalIndex, ) -> Global111     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.
ty(&self, store: impl AsContext) -> GlobalType128     pub fn ty(&self, store: impl AsContext) -> GlobalType {
129         self._ty(store.as_context().0)
130     }
131 
_ty(&self, store: &StoreOpaque) -> GlobalType132     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.
get(&self, mut store: impl AsContextMut) -> Val141     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 
_get(&self, store: &mut AutoAssertNoGc<'_>) -> Val146     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.
set(&self, mut store: impl AsContextMut, val: Val) -> Result<()>222     pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> {
223         self._set(store.as_context_mut().0, val)
224     }
225 
_set(&self, store: &mut StoreOpaque, val: Val) -> Result<()>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.
set_unchecked(&self, store: &mut StoreOpaque, val: &Val) -> Result<()>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                 Val::ContRef(None) => {
284                     // Allow null continuation references for globals - these are just placeholders
285                     definition.write_gc_ref(&mut store, None);
286                 }
287                 Val::ContRef(Some(_)) => {
288                     // TODO(#10248): Implement non-null global continuation reference handling
289                     return Err(crate::format_err!(
290                         "setting non-null continuation references in globals not yet supported"
291                     ));
292                 }
293             }
294         }
295         Ok(())
296     }
297 
298     #[cfg(feature = "gc")]
trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList)299     pub(crate) fn trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
300         if let Some(ref_ty) = self._ty(store).content().as_ref() {
301             if !ref_ty.is_vmgcref_type_and_points_to_object() {
302                 return;
303             }
304 
305             if let Some(gc_ref) = unsafe { self.definition(store).as_ref().as_gc_ref() } {
306                 unsafe {
307                     gc_roots_list.add_root(gc_ref.into(), "Wasm global");
308                 }
309             }
310         }
311     }
312 
from_host(store: StoreId, index: DefinedGlobalIndex) -> Global313     pub(crate) fn from_host(store: StoreId, index: DefinedGlobalIndex) -> Global {
314         Global {
315             store,
316             instance: 0,
317             kind: VMGlobalKind::Host(index),
318         }
319     }
320 
from_core(instance: StoreInstanceId, index: DefinedGlobalIndex) -> Global321     pub(crate) fn from_core(instance: StoreInstanceId, index: DefinedGlobalIndex) -> Global {
322         Global {
323             store: instance.store_id(),
324             instance: instance.instance().as_u32(),
325             kind: VMGlobalKind::Instance(index),
326         }
327     }
328 
329     #[cfg(feature = "component-model")]
from_component_flags( instance: crate::component::store::StoreComponentInstanceId, index: wasmtime_environ::component::RuntimeComponentInstanceIndex, ) -> Global330     pub(crate) fn from_component_flags(
331         instance: crate::component::store::StoreComponentInstanceId,
332         index: wasmtime_environ::component::RuntimeComponentInstanceIndex,
333     ) -> Global {
334         Global {
335             store: instance.store_id(),
336             instance: instance.instance().as_u32(),
337             kind: VMGlobalKind::ComponentFlags(index),
338         }
339     }
340 
341     #[cfg(feature = "component-model")]
from_task_may_block( instance: crate::component::store::StoreComponentInstanceId, ) -> Global342     pub(crate) fn from_task_may_block(
343         instance: crate::component::store::StoreComponentInstanceId,
344     ) -> Global {
345         Global {
346             store: instance.store_id(),
347             instance: instance.instance().as_u32(),
348             kind: VMGlobalKind::TaskMayBlock,
349         }
350     }
351 
wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Global352     pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Global {
353         self.store.assert_belongs_to(store.id());
354         match self.kind {
355             VMGlobalKind::Instance(index) => {
356                 let instance = InstanceId::from_u32(self.instance);
357                 let module = store.instance(instance).env_module();
358                 let index = module.global_index(index);
359                 &module.globals[index]
360             }
361             VMGlobalKind::Host(index) => unsafe { &store.host_globals()[index].get().as_ref().ty },
362             #[cfg(feature = "component-model")]
363             VMGlobalKind::ComponentFlags(_) | VMGlobalKind::TaskMayBlock => {
364                 const TY: wasmtime_environ::Global = wasmtime_environ::Global {
365                     mutability: true,
366                     wasm_ty: wasmtime_environ::WasmValType::I32,
367                 };
368                 &TY
369             }
370         }
371     }
372 
vmimport(&self, store: &StoreOpaque) -> vm::VMGlobalImport373     pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMGlobalImport {
374         let vmctx = match self.kind {
375             VMGlobalKind::Instance(_) => {
376                 let instance = InstanceId::from_u32(self.instance);
377                 Some(VMOpaqueContext::from_vmcontext(store.instance(instance).vmctx()).into())
378             }
379             VMGlobalKind::Host(_) => None,
380             #[cfg(feature = "component-model")]
381             VMGlobalKind::ComponentFlags(_) | VMGlobalKind::TaskMayBlock => {
382                 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
383                 Some(
384                     VMOpaqueContext::from_vmcomponent(store.component_instance(instance).vmctx())
385                         .into(),
386                 )
387             }
388         };
389         vm::VMGlobalImport {
390             from: self.definition(store).into(),
391             vmctx,
392             kind: self.kind,
393         }
394     }
395 
comes_from_same_store(&self, store: &StoreOpaque) -> bool396     pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
397         store.id() == self.store
398     }
399 
400     /// Returns a stable identifier for this global within its store.
401     ///
402     /// This allows distinguishing globals when introspecting them
403     /// e.g. via debug APIs.
404     #[cfg(feature = "debug")]
debug_index_in_store(&self) -> u64405     pub fn debug_index_in_store(&self) -> u64 {
406         match self.kind {
407             VMGlobalKind::Instance(idx) => u64::from(self.instance) << 32 | u64::from(idx.as_u32()),
408             VMGlobalKind::Host(idx) => u64::from(u32::MAX) << 32 | u64::from(idx.as_u32()),
409             #[cfg(feature = "component-model")]
410             VMGlobalKind::ComponentFlags(idx) => {
411                 u64::from(self.instance) << 32 | u64::from(idx.as_u32())
412             }
413             #[cfg(feature = "component-model")]
414             VMGlobalKind::TaskMayBlock => u64::from(self.instance) << 32 | u64::from(u32::MAX),
415         }
416     }
417 
418     /// Get a stable hash key for this global.
419     ///
420     /// Even if the same underlying global definition is added to the
421     /// `StoreData` multiple times and becomes multiple `wasmtime::Global`s,
422     /// this hash key will be consistent across all of these globals.
423     #[cfg(feature = "coredump")]
hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<>424     pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<> {
425         self.definition(store).as_ptr().addr()
426     }
427 
definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition>428     fn definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition> {
429         self.store.assert_belongs_to(store.id());
430         match self.kind {
431             VMGlobalKind::Instance(index) => {
432                 let instance = InstanceId::from_u32(self.instance);
433                 store.instance(instance).global_ptr(index)
434             }
435             VMGlobalKind::Host(index) => unsafe {
436                 NonNull::from(&mut store.host_globals()[index].get().as_mut().global)
437             },
438             #[cfg(feature = "component-model")]
439             VMGlobalKind::ComponentFlags(index) => {
440                 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
441                 store
442                     .component_instance(instance)
443                     .instance_flags(index)
444                     .as_raw()
445             }
446             #[cfg(feature = "component-model")]
447             VMGlobalKind::TaskMayBlock => store
448                 .component_instance(crate::component::ComponentInstanceId::from_u32(
449                     self.instance,
450                 ))
451                 .task_may_block(),
452         }
453     }
454 }
455 
456 #[cfg(test)]
457 mod tests {
458     use super::*;
459     use crate::{Instance, Module, Store};
460 
461     #[test]
hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()>462     fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
463         let mut store = Store::<()>::default();
464         let module = Module::new(
465             store.engine(),
466             r#"
467                 (module
468                     (global (export "g") (mut i32) (i32.const 0))
469                 )
470             "#,
471         )?;
472         let instance = Instance::new(&mut store, &module, &[])?;
473 
474         // Each time we `get_global`, we call `Global::from_wasmtime` which adds
475         // a new entry to `StoreData`, so `g1` and `g2` will have different
476         // indices into `StoreData`.
477         let g1 = instance.get_global(&mut store, "g").unwrap();
478         let g2 = instance.get_global(&mut store, "g").unwrap();
479 
480         // That said, they really point to the same global.
481         assert_eq!(g1.get(&mut store).unwrap_i32(), 0);
482         assert_eq!(g2.get(&mut store).unwrap_i32(), 0);
483         g1.set(&mut store, Val::I32(42))?;
484         assert_eq!(g1.get(&mut store).unwrap_i32(), 42);
485         assert_eq!(g2.get(&mut store).unwrap_i32(), 42);
486 
487         // And therefore their hash keys are the same.
488         assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0));
489 
490         // But the hash keys are different from different globals.
491         let instance2 = Instance::new(&mut store, &module, &[])?;
492         let g3 = instance2.get_global(&mut store, "g").unwrap();
493         assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0));
494 
495         Ok(())
496     }
497 }
498