wasmtime/runtime/externals/
global.rs1use crate::prelude::*;
2use crate::runtime::vm::{self, VMGlobalDefinition, VMGlobalKind, VMOpaqueContext};
3use crate::{
4 AnyRef, AsContext, AsContextMut, ExternRef, Func, GlobalType, HeapType, Mutability, Ref,
5 RootedGcRefImpl, 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#[derive(Copy, Clone, Debug)]
26#[repr(C)] pub struct Global {
28 store: StoreId,
30 instance: u32,
33 kind: VMGlobalKind,
35}
36
37const _: () = {
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 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
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
103 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
111 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 pub fn ty(&self, store: impl AsContext) -> GlobalType {
129 self._ty(store.as_context().0)
130 }
131
132 pub(crate) fn _ty(&self, store: &StoreOpaque) -> GlobalType {
133 GlobalType::from_wasmtime_global(store.engine(), self.wasmtime_ty(store))
134 }
135
136 pub fn get(&self, mut store: impl AsContextMut) -> Val {
142 unsafe {
143 let store = store.as_context_mut();
144 let definition = self.definition(store.0).as_ref();
145 let mut store = AutoAssertNoGc::new(store.0);
146 match self._ty(&store).content() {
147 ValType::I32 => Val::from(*definition.as_i32()),
148 ValType::I64 => Val::from(*definition.as_i64()),
149 ValType::F32 => Val::F32(*definition.as_u32()),
150 ValType::F64 => Val::F64(*definition.as_u64()),
151 ValType::V128 => Val::V128(definition.get_u128().into()),
152 ValType::Ref(ref_ty) => {
153 let reference: Ref = match ref_ty.heap_type() {
154 HeapType::Func | HeapType::ConcreteFunc(_) => {
155 Func::_from_raw(&mut store, definition.as_func_ref().cast()).into()
156 }
157
158 HeapType::NoFunc => Ref::Func(None),
159
160 HeapType::Extern => Ref::Extern(definition.as_gc_ref().map(|r| {
161 let r = store.unwrap_gc_store_mut().clone_gc_ref(r);
162 ExternRef::from_cloned_gc_ref(&mut store, r)
163 })),
164
165 HeapType::NoCont | HeapType::ConcreteCont(_) | HeapType::Cont => {
166 unimplemented!()
168 }
169
170 HeapType::NoExtern => Ref::Extern(None),
171
172 HeapType::Any
173 | HeapType::Eq
174 | HeapType::I31
175 | HeapType::Struct
176 | HeapType::ConcreteStruct(_)
177 | HeapType::Array
178 | HeapType::ConcreteArray(_)
179 | HeapType::Exn
180 | HeapType::ConcreteExn(_) => definition
181 .as_gc_ref()
182 .map(|r| {
183 let r = store.unwrap_gc_store_mut().clone_gc_ref(r);
184 AnyRef::from_cloned_gc_ref(&mut store, r)
185 })
186 .into(),
187
188 HeapType::NoExn => Ref::Exn(None),
189
190 HeapType::None => Ref::Any(None),
191 };
192 debug_assert!(
193 ref_ty.is_nullable() || !reference.is_null(),
194 "if the type is non-nullable, we better have a non-null reference"
195 );
196 reference.into()
197 }
198 }
199 }
200 }
201
202 pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> {
214 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
215 let global_ty = self._ty(&store);
216 if global_ty.mutability() != Mutability::Var {
217 bail!("immutable global cannot be set");
218 }
219 val.ensure_matches_ty(&store, global_ty.content())
220 .context("type mismatch: attempt to set global to value of wrong type")?;
221 unsafe {
222 let definition = self.definition(&store).as_mut();
223 match val {
224 Val::I32(i) => *definition.as_i32_mut() = i,
225 Val::I64(i) => *definition.as_i64_mut() = i,
226 Val::F32(f) => *definition.as_u32_mut() = f,
227 Val::F64(f) => *definition.as_u64_mut() = f,
228 Val::V128(i) => definition.set_u128(i.into()),
229 Val::FuncRef(f) => {
230 *definition.as_func_ref_mut() =
231 f.map_or(ptr::null_mut(), |f| f.vm_func_ref(&store).as_ptr().cast());
232 }
233 Val::ExternRef(e) => {
234 let new = match e {
235 None => None,
236 Some(e) => Some(e.try_gc_ref(&store)?.unchecked_copy()),
237 };
238 let new = new.as_ref();
239 definition.write_gc_ref(store.unwrap_gc_store_mut(), new);
240 }
241 Val::AnyRef(a) => {
242 let new = match a {
243 None => None,
244 Some(a) => Some(a.try_gc_ref(&store)?.unchecked_copy()),
245 };
246 let new = new.as_ref();
247 definition.write_gc_ref(store.unwrap_gc_store_mut(), new);
248 }
249 Val::ExnRef(e) => {
250 let new = match e {
251 None => None,
252 Some(e) => Some(e.try_gc_ref(&store)?.unchecked_copy()),
253 };
254 let new = new.as_ref();
255 definition.write_gc_ref(store.unwrap_gc_store_mut(), new);
256 }
257 }
258 }
259 Ok(())
260 }
261
262 #[cfg(feature = "gc")]
263 pub(crate) fn trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
264 if let Some(ref_ty) = self._ty(store).content().as_ref() {
265 if !ref_ty.is_vmgcref_type_and_points_to_object() {
266 return;
267 }
268
269 if let Some(gc_ref) = unsafe { self.definition(store).as_ref().as_gc_ref() } {
270 unsafe {
271 gc_roots_list.add_root(gc_ref.into(), "Wasm global");
272 }
273 }
274 }
275 }
276
277 pub(crate) fn from_host(store: StoreId, index: DefinedGlobalIndex) -> Global {
278 Global {
279 store,
280 instance: 0,
281 kind: VMGlobalKind::Host(index),
282 }
283 }
284
285 pub(crate) fn from_core(instance: StoreInstanceId, index: DefinedGlobalIndex) -> Global {
286 Global {
287 store: instance.store_id(),
288 instance: instance.instance().as_u32(),
289 kind: VMGlobalKind::Instance(index),
290 }
291 }
292
293 #[cfg(feature = "component-model")]
294 pub(crate) fn from_component_flags(
295 instance: crate::component::store::StoreComponentInstanceId,
296 index: wasmtime_environ::component::RuntimeComponentInstanceIndex,
297 ) -> Global {
298 Global {
299 store: instance.store_id(),
300 instance: instance.instance().as_u32(),
301 kind: VMGlobalKind::ComponentFlags(index),
302 }
303 }
304
305 pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Global {
306 self.store.assert_belongs_to(store.id());
307 match self.kind {
308 VMGlobalKind::Instance(index) => {
309 let instance = InstanceId::from_u32(self.instance);
310 let module = store.instance(instance).env_module();
311 let index = module.global_index(index);
312 &module.globals[index]
313 }
314 VMGlobalKind::Host(index) => unsafe { &store.host_globals()[index].get().as_ref().ty },
315 #[cfg(feature = "component-model")]
316 VMGlobalKind::ComponentFlags(_) => {
317 const TY: wasmtime_environ::Global = wasmtime_environ::Global {
318 mutability: true,
319 wasm_ty: wasmtime_environ::WasmValType::I32,
320 };
321 &TY
322 }
323 }
324 }
325
326 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMGlobalImport {
327 let vmctx = match self.kind {
328 VMGlobalKind::Instance(_) => {
329 let instance = InstanceId::from_u32(self.instance);
330 Some(VMOpaqueContext::from_vmcontext(store.instance(instance).vmctx()).into())
331 }
332 VMGlobalKind::Host(_) => None,
333 #[cfg(feature = "component-model")]
334 VMGlobalKind::ComponentFlags(_) => {
335 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
336 Some(
337 VMOpaqueContext::from_vmcomponent(store.component_instance(instance).vmctx())
338 .into(),
339 )
340 }
341 };
342 vm::VMGlobalImport {
343 from: self.definition(store).into(),
344 vmctx,
345 kind: self.kind,
346 }
347 }
348
349 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
350 store.id() == self.store
351 }
352
353 #[cfg(feature = "coredump")]
359 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<> {
360 self.definition(store).as_ptr().addr()
361 }
362
363 fn definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition> {
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 store.instance(instance).global_ptr(index)
369 }
370 VMGlobalKind::Host(index) => unsafe {
371 NonNull::from(&mut store.host_globals()[index].get().as_mut().global)
372 },
373 #[cfg(feature = "component-model")]
374 VMGlobalKind::ComponentFlags(index) => {
375 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
376 store
377 .component_instance(instance)
378 .instance_flags(index)
379 .as_raw()
380 }
381 }
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use crate::{Instance, Module, Store};
389
390 #[test]
391 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
392 let mut store = Store::<()>::default();
393 let module = Module::new(
394 store.engine(),
395 r#"
396 (module
397 (global (export "g") (mut i32) (i32.const 0))
398 )
399 "#,
400 )?;
401 let instance = Instance::new(&mut store, &module, &[])?;
402
403 let g1 = instance.get_global(&mut store, "g").unwrap();
407 let g2 = instance.get_global(&mut store, "g").unwrap();
408
409 assert_eq!(g1.get(&mut store).unwrap_i32(), 0);
411 assert_eq!(g2.get(&mut store).unwrap_i32(), 0);
412 g1.set(&mut store, Val::I32(42))?;
413 assert_eq!(g1.get(&mut store).unwrap_i32(), 42);
414 assert_eq!(g2.get(&mut store).unwrap_i32(), 42);
415
416 assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0));
418
419 let instance2 = Instance::new(&mut store, &module, &[])?;
421 let g3 = instance2.get_global(&mut store, "g").unwrap();
422 assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0));
423
424 Ok(())
425 }
426}