wasmtime/runtime/externals/
global.rs1use crate::prelude::*;
2use crate::runtime::vm::{GcRootsList, SendSyncPtr};
3use crate::{
4 store::{AutoAssertNoGc, StoreData, StoreOpaque, Stored},
5 trampoline::generate_global_export,
6 AnyRef, AsContext, AsContextMut, ExternRef, Func, GlobalType, HeapType, Mutability, Ref,
7 RootedGcRefImpl, Val, ValType,
8};
9use core::ptr;
10use core::ptr::NonNull;
11use wasmtime_environ::TypeTrace;
12
13#[derive(Copy, Clone, Debug)]
26#[repr(transparent)] pub struct Global(pub(super) Stored<crate::runtime::vm::ExportGlobal>);
28
29impl Global {
30 pub fn new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result<Global> {
75 Global::_new(store.as_context_mut().0, ty, val)
76 }
77
78 fn _new(store: &mut StoreOpaque, ty: GlobalType, val: Val) -> Result<Global> {
79 val.ensure_matches_ty(store, ty.content()).context(
80 "type mismatch: initial value provided does not match the type of this global",
81 )?;
82 unsafe {
83 let wasmtime_export = generate_global_export(store, ty, val)?;
84 Ok(Global::from_wasmtime_global(wasmtime_export, store))
85 }
86 }
87
88 pub fn ty(&self, store: impl AsContext) -> GlobalType {
94 self._ty(store.as_context().0)
95 }
96
97 pub(crate) fn _ty(&self, store: &StoreOpaque) -> GlobalType {
98 let ty = &store[self.0].global;
99 GlobalType::from_wasmtime_global(store.engine(), &ty)
100 }
101
102 pub fn get(&self, mut store: impl AsContextMut) -> Val {
108 unsafe {
109 let store = store.as_context_mut();
110 let mut store = AutoAssertNoGc::new(store.0);
111 let definition = &*store[self.0].definition;
112 match self._ty(&store).content() {
113 ValType::I32 => Val::from(*definition.as_i32()),
114 ValType::I64 => Val::from(*definition.as_i64()),
115 ValType::F32 => Val::F32(*definition.as_u32()),
116 ValType::F64 => Val::F64(*definition.as_u64()),
117 ValType::V128 => Val::V128((*definition.as_u128()).into()),
118 ValType::Ref(ref_ty) => {
119 let reference: Ref = match ref_ty.heap_type() {
120 HeapType::Func | HeapType::ConcreteFunc(_) => {
121 Func::_from_raw(&mut store, definition.as_func_ref().cast()).into()
122 }
123
124 HeapType::NoFunc => Ref::Func(None),
125
126 HeapType::Extern => Ref::Extern(
127 definition
128 .as_gc_ref()
129 .map(|r| {
130 let r = store.unwrap_gc_store_mut().clone_gc_ref(r);
131 ExternRef::from_cloned_gc_ref(&mut store, r)
132 })
133 .into(),
134 ),
135
136 HeapType::NoExtern => Ref::Extern(None),
137
138 HeapType::Any
139 | HeapType::Eq
140 | HeapType::I31
141 | HeapType::Struct
142 | HeapType::ConcreteStruct(_)
143 | HeapType::Array
144 | HeapType::ConcreteArray(_) => definition
145 .as_gc_ref()
146 .map(|r| {
147 let r = store.unwrap_gc_store_mut().clone_gc_ref(r);
148 AnyRef::from_cloned_gc_ref(&mut store, r)
149 })
150 .into(),
151
152 HeapType::None => Ref::Any(None),
153 };
154 debug_assert!(
155 ref_ty.is_nullable() || !reference.is_null(),
156 "if the type is non-nullable, we better have a non-null reference"
157 );
158 reference.into()
159 }
160 }
161 }
162 }
163
164 pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> {
176 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
177 let global_ty = self._ty(&store);
178 if global_ty.mutability() != Mutability::Var {
179 bail!("immutable global cannot be set");
180 }
181 val.ensure_matches_ty(&store, global_ty.content())
182 .context("type mismatch: attempt to set global to value of wrong type")?;
183 unsafe {
184 let definition = &mut *store[self.0].definition;
185 match val {
186 Val::I32(i) => *definition.as_i32_mut() = i,
187 Val::I64(i) => *definition.as_i64_mut() = i,
188 Val::F32(f) => *definition.as_u32_mut() = f,
189 Val::F64(f) => *definition.as_u64_mut() = f,
190 Val::V128(i) => *definition.as_u128_mut() = i.into(),
191 Val::FuncRef(f) => {
192 *definition.as_func_ref_mut() = f.map_or(ptr::null_mut(), |f| {
193 f.vm_func_ref(&mut store).as_ptr().cast()
194 });
195 }
196 Val::ExternRef(e) => {
197 let new = match e {
198 None => None,
199 #[cfg_attr(not(feature = "gc"), allow(unreachable_patterns))]
200 Some(e) => Some(e.try_gc_ref(&mut store)?.unchecked_copy()),
201 };
202 let new = new.as_ref();
203 definition.write_gc_ref(store.unwrap_gc_store_mut(), new);
204 }
205 Val::AnyRef(a) => {
206 let new = match a {
207 None => None,
208 #[cfg_attr(not(feature = "gc"), allow(unreachable_patterns))]
209 Some(a) => Some(a.try_gc_ref(&mut store)?.unchecked_copy()),
210 };
211 let new = new.as_ref();
212 definition.write_gc_ref(store.unwrap_gc_store_mut(), new);
213 }
214 }
215 }
216 Ok(())
217 }
218
219 pub(crate) fn trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut GcRootsList) {
220 if let Some(ref_ty) = self._ty(store).content().as_ref() {
221 if !ref_ty.is_vmgcref_type_and_points_to_object() {
222 return;
223 }
224
225 if let Some(gc_ref) = unsafe { (*store[self.0].definition).as_gc_ref() } {
226 let gc_ref = NonNull::from(gc_ref);
227 let gc_ref = SendSyncPtr::new(gc_ref);
228 unsafe {
229 gc_roots_list.add_root(gc_ref, "Wasm global");
230 }
231 }
232 }
233 }
234
235 pub(crate) unsafe fn from_wasmtime_global(
236 mut wasmtime_export: crate::runtime::vm::ExportGlobal,
237 store: &mut StoreOpaque,
238 ) -> Global {
239 wasmtime_export
240 .global
241 .wasm_ty
242 .canonicalize_for_runtime_usage(&mut |module_index| {
243 crate::runtime::vm::Instance::from_vmctx(wasmtime_export.vmctx, |instance| {
244 instance.engine_type_index(module_index)
245 })
246 });
247
248 Global(store.store_data_mut().insert(wasmtime_export))
249 }
250
251 pub(crate) fn wasmtime_ty<'a>(&self, data: &'a StoreData) -> &'a wasmtime_environ::Global {
252 &data[self.0].global
253 }
254
255 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> crate::runtime::vm::VMGlobalImport {
256 crate::runtime::vm::VMGlobalImport {
257 from: store[self.0].definition,
258 }
259 }
260
261 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq {
267 store[self.0].definition as usize
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274 use crate::{Instance, Module, Store};
275
276 #[test]
277 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
278 let mut store = Store::<()>::default();
279 let module = Module::new(
280 store.engine(),
281 r#"
282 (module
283 (global (export "g") (mut i32) (i32.const 0))
284 )
285 "#,
286 )?;
287 let instance = Instance::new(&mut store, &module, &[])?;
288
289 let g1 = instance.get_global(&mut store, "g").unwrap();
293 let g2 = instance.get_global(&mut store, "g").unwrap();
294
295 assert_eq!(g1.get(&mut store).unwrap_i32(), 0);
297 assert_eq!(g2.get(&mut store).unwrap_i32(), 0);
298 g1.set(&mut store, Val::I32(42))?;
299 assert_eq!(g1.get(&mut store).unwrap_i32(), 42);
300 assert_eq!(g2.get(&mut store).unwrap_i32(), 42);
301
302 assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0));
304
305 let instance2 = Instance::new(&mut store, &module, &[])?;
307 let g3 = instance2.get_global(&mut store, "g").unwrap();
308 assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0));
309
310 Ok(())
311 }
312}