wasmtime/runtime/externals/
global.rs1use 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::NonNull;
10use wasmtime_environ::DefinedGlobalIndex;
11
12#[derive(Copy, Clone, Debug)]
25#[repr(C)] pub struct Global {
27 store: StoreId,
29 instance: u32,
32 kind: VMGlobalKind,
34}
35
36const _: () = {
39 #[repr(C)]
40 struct C(u64, u32, u32, u32);
41 assert!(core::mem::size_of::<C>() == core::mem::size_of::<Global>());
42 assert!(core::mem::align_of::<C>() == core::mem::align_of::<Global>());
43 assert!(core::mem::offset_of!(Global, store) == 0);
44};
45
46impl Global {
47 pub fn new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result<Global> {
99 Global::_new(store.as_context_mut().0, ty, val)
100 }
101
102 fn _new(store: &mut StoreOpaque, ty: GlobalType, val: Val) -> Result<Global> {
103 val.ensure_matches_ty(store, ty.content()).context(
104 "type mismatch: initial value provided does not match the type of this global",
105 )?;
106 generate_global_export(store, ty, val)
107 }
108
109 pub(crate) fn new_host(store: &StoreOpaque, index: DefinedGlobalIndex) -> Global {
110 Global {
111 store: store.id(),
112 instance: 0,
113 kind: VMGlobalKind::Host(index),
114 }
115 }
116
117 pub(crate) fn new_instance(
118 store: &StoreOpaque,
119 instance: InstanceId,
120 index: DefinedGlobalIndex,
121 ) -> Global {
122 Global {
123 store: store.id(),
124 instance: instance.as_u32(),
125 kind: VMGlobalKind::Instance(index),
126 }
127 }
128
129 pub fn ty(&self, store: impl AsContext) -> GlobalType {
135 self._ty(store.as_context().0)
136 }
137
138 pub(crate) fn _ty(&self, store: &StoreOpaque) -> GlobalType {
139 GlobalType::from_wasmtime_global(store.engine(), self.wasmtime_ty(store))
140 }
141
142 pub fn get(&self, mut store: impl AsContextMut) -> Val {
148 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
149 self._get(&mut store)
150 }
151
152 pub fn get_numeric(&self, store: impl AsContext) -> Option<Val> {
165 self._get_numeric(store.as_context().0)
166 }
167
168 fn _get_numeric(&self, store: &StoreOpaque) -> Option<Val> {
169 unsafe {
170 let definition = self.definition(store).as_ref();
171 match self._ty(store).content() {
172 ValType::I32 => Some(Val::from(definition.get_i32())),
173 ValType::I64 => Some(Val::from(definition.get_i64())),
174 ValType::F32 => Some(Val::F32(definition.get_f32_bits())),
175 ValType::F64 => Some(Val::F64(definition.get_f64_bits())),
176 ValType::V128 => Some(Val::V128(definition.get_u128().into())),
177 ValType::Ref(_) => None,
178 }
179 }
180 }
181
182 pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>) -> Val {
183 unsafe {
184 let definition = self.definition(store).as_ref();
185 match self._ty(&store).content() {
186 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => self
187 ._get_numeric(store)
188 .expect("numeric globals always have a numeric value"),
189 ValType::Ref(ref_ty) => {
190 let reference: Ref = match ref_ty.heap_type() {
191 HeapType::Func | HeapType::ConcreteFunc(_) => Ref::Func(
192 definition
193 .get_func_ref()
194 .map(|f| Func::from_vm_func_ref(store.id(), f)),
195 ),
196
197 HeapType::NoFunc => Ref::Func(None),
198
199 HeapType::Extern => Ref::Extern(definition.as_gc_ref().map(|r| {
200 let r = store.clone_gc_ref(r);
201 ExternRef::from_cloned_gc_ref(store, r)
202 })),
203
204 HeapType::NoCont | HeapType::ConcreteCont(_) | HeapType::Cont => {
205 unimplemented!()
207 }
208
209 HeapType::NoExtern => Ref::Extern(None),
210
211 HeapType::Exn | HeapType::ConcreteExn(_) => definition
212 .as_gc_ref()
213 .map(|r| {
214 let r = store.clone_gc_ref(r);
215 ExnRef::from_cloned_gc_ref(store, r)
216 })
217 .into(),
218
219 HeapType::Any
220 | HeapType::Eq
221 | HeapType::I31
222 | HeapType::Struct
223 | HeapType::ConcreteStruct(_)
224 | HeapType::Array
225 | HeapType::ConcreteArray(_) => definition
226 .as_gc_ref()
227 .map(|r| {
228 let r = store.clone_gc_ref(r);
229 AnyRef::from_cloned_gc_ref(store, r)
230 })
231 .into(),
232
233 HeapType::NoExn => Ref::Exn(None),
234
235 HeapType::None => Ref::Any(None),
236 };
237 debug_assert!(
238 ref_ty.is_nullable() || !reference.is_null(),
239 "if the type is non-nullable, we better have a non-null reference"
240 );
241 reference.into()
242 }
243 }
244 }
245 }
246
247 pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> {
263 self._set(store.as_context_mut().0, val)
264 }
265
266 pub(crate) fn _set(&self, store: &mut StoreOpaque, val: Val) -> Result<()> {
267 let global_ty = self._ty(&store);
268 if global_ty.mutability() != Mutability::Var {
269 bail!("immutable global cannot be set");
270 }
271 val.ensure_matches_ty(&store, global_ty.content())
272 .context("type mismatch: attempt to set global to value of wrong type")?;
273
274 unsafe { self.set_unchecked(store, &val) }
276 }
277
278 pub(crate) unsafe fn set_unchecked(&self, store: &mut StoreOpaque, val: &Val) -> Result<()> {
286 let mut store = AutoAssertNoGc::new(store);
287 unsafe {
288 let definition = self.definition(&store).as_mut();
289 match val {
290 Val::I32(i) => definition.set_i32(*i),
291 Val::I64(i) => definition.set_i64(*i),
292 Val::F32(f) => definition.set_f32_bits(*f),
293 Val::F64(f) => definition.set_f64_bits(*f),
294 Val::V128(i) => definition.set_u128((*i).into()),
295 Val::FuncRef(f) => {
296 definition.set_func_ref(f.map(|f| f.vm_func_ref(&store)));
297 }
298 Val::ExternRef(e) => {
299 let new = match e {
300 None => None,
301 Some(e) => Some(e.try_gc_ref(&store)?.unchecked_copy()),
302 };
303 let new = new.as_ref();
304 definition.write_gc_ref(&mut store, new)?;
305 }
306 Val::AnyRef(a) => {
307 let new = match a {
308 None => None,
309 Some(a) => Some(a.try_gc_ref(&store)?.unchecked_copy()),
310 };
311 let new = new.as_ref();
312 definition.write_gc_ref(&mut store, new)?;
313 }
314 Val::ExnRef(e) => {
315 let new = match e {
316 None => None,
317 Some(e) => Some(e.try_gc_ref(&store)?.unchecked_copy()),
318 };
319 let new = new.as_ref();
320 definition.write_gc_ref(&mut store, new)?;
321 }
322 Val::ContRef(None) => {
323 definition.write_gc_ref(&mut store, None)?;
325 }
326 Val::ContRef(Some(_)) => {
327 return Err(crate::format_err!(
329 "setting non-null continuation references in globals not yet supported"
330 ));
331 }
332 }
333 }
334 Ok(())
335 }
336
337 #[cfg(feature = "gc")]
338 pub(crate) fn trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
339 if let Some(ref_ty) = self._ty(store).content().as_ref() {
340 if !ref_ty.is_vmgcref_type_and_points_to_object() {
341 return;
342 }
343
344 if let Some(gc_ref) = unsafe { self.definition(store).as_mut().as_gc_ref_mut() } {
345 unsafe {
346 gc_roots_list.add_vmgcref_root(gc_ref.into(), "Wasm global");
347 }
348 }
349 }
350 }
351
352 pub(crate) fn from_host(store: StoreId, index: DefinedGlobalIndex) -> Global {
353 Global {
354 store,
355 instance: 0,
356 kind: VMGlobalKind::Host(index),
357 }
358 }
359
360 pub(crate) fn from_core(instance: StoreInstanceId, index: DefinedGlobalIndex) -> Global {
361 Global {
362 store: instance.store_id(),
363 instance: instance.instance().as_u32(),
364 kind: VMGlobalKind::Instance(index),
365 }
366 }
367
368 #[cfg(feature = "component-model")]
369 pub(crate) fn from_component_flags(
370 instance: crate::component::store::StoreComponentInstanceId,
371 index: wasmtime_environ::component::RuntimeComponentInstanceIndex,
372 ) -> Global {
373 Global {
374 store: instance.store_id(),
375 instance: instance.instance().as_u32(),
376 kind: VMGlobalKind::ComponentFlags(index),
377 }
378 }
379
380 pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Global {
381 self.store.assert_belongs_to(store.id());
382 match self.kind {
383 VMGlobalKind::Instance(index) => {
384 let instance = InstanceId::from_u32(self.instance);
385 let module = store.instance(instance).env_module();
386 let index = module.global_index(index);
387 &module.globals[index]
388 }
389 VMGlobalKind::Host(index) => unsafe { &store.host_globals()[index].get().as_ref().ty },
390 #[cfg(feature = "component-model")]
391 VMGlobalKind::ComponentFlags(_) => {
392 const TY: wasmtime_environ::Global = wasmtime_environ::Global {
393 mutability: true,
394 wasm_ty: wasmtime_environ::WasmValType::I32,
395 };
396 &TY
397 }
398 }
399 }
400
401 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMGlobalImport {
402 let vmctx = match self.kind {
403 VMGlobalKind::Instance(_) => {
404 let instance = InstanceId::from_u32(self.instance);
405 Some(VMOpaqueContext::from_vmcontext(store.instance(instance).vmctx()).into())
406 }
407 VMGlobalKind::Host(_) => None,
408 #[cfg(feature = "component-model")]
409 VMGlobalKind::ComponentFlags(_) => {
410 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
411 Some(
412 VMOpaqueContext::from_vmcomponent(store.component_instance(instance).vmctx())
413 .into(),
414 )
415 }
416 };
417 vm::VMGlobalImport {
418 from: self.definition(store).into(),
419 vmctx,
420 kind: self.kind,
421 }
422 }
423
424 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
425 store.id() == self.store
426 }
427
428 #[cfg(feature = "debug")]
433 pub fn debug_index_in_store(&self) -> u64 {
434 match self.kind {
435 VMGlobalKind::Instance(idx) => u64::from(self.instance) << 32 | u64::from(idx.as_u32()),
436 VMGlobalKind::Host(idx) => u64::from(u32::MAX) << 32 | u64::from(idx.as_u32()),
437 #[cfg(feature = "component-model")]
438 VMGlobalKind::ComponentFlags(idx) => {
439 u64::from(self.instance) << 32 | u64::from(idx.as_u32())
440 }
441 }
442 }
443
444 #[cfg(feature = "coredump")]
450 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<> {
451 self.definition(store).as_ptr().addr()
452 }
453
454 fn definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition> {
455 self.store.assert_belongs_to(store.id());
456 match self.kind {
457 VMGlobalKind::Instance(index) => {
458 let instance = InstanceId::from_u32(self.instance);
459 store.instance(instance).global_ptr(index)
460 }
461 VMGlobalKind::Host(index) => unsafe {
462 NonNull::from(&mut store.host_globals()[index].get().as_mut().global)
463 },
464 #[cfg(feature = "component-model")]
465 VMGlobalKind::ComponentFlags(index) => {
466 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
467 store
468 .component_instance(instance)
469 .instance_flags(index)
470 .as_raw()
471 }
472 }
473 }
474}
475
476#[cfg(test)]
477mod tests {
478 use super::*;
479 use crate::{Instance, Module, Store};
480
481 #[test]
482 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
483 let mut store = Store::<()>::default();
484 let module = Module::new(
485 store.engine(),
486 r#"
487 (module
488 (global (export "g") (mut i32) (i32.const 0))
489 )
490 "#,
491 )?;
492 let instance = Instance::new(&mut store, &module, &[])?;
493
494 let g1 = instance.get_global(&mut store, "g").unwrap();
498 let g2 = instance.get_global(&mut store, "g").unwrap();
499
500 assert_eq!(g1.get(&mut store).unwrap_i32(), 0);
502 assert_eq!(g2.get(&mut store).unwrap_i32(), 0);
503 g1.set(&mut store, Val::I32(42))?;
504 assert_eq!(g1.get(&mut store).unwrap_i32(), 42);
505 assert_eq!(g2.get(&mut store).unwrap_i32(), 42);
506
507 assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0));
509
510 let instance2 = Instance::new(&mut store, &module, &[])?;
512 let g3 = instance2.get_global(&mut store, "g").unwrap();
513 assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0));
514
515 Ok(())
516 }
517}