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;
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> {
100 Global::_new(store.as_context_mut().0, ty, val)
101 }
102
103 fn _new(store: &mut StoreOpaque, ty: GlobalType, val: Val) -> Result<Global> {
104 val.ensure_matches_ty(store, ty.content()).context(
105 "type mismatch: initial value provided does not match the type of this global",
106 )?;
107 generate_global_export(store, ty, val)
108 }
109
110 pub(crate) fn new_host(store: &StoreOpaque, index: DefinedGlobalIndex) -> Global {
111 Global {
112 store: store.id(),
113 instance: 0,
114 kind: VMGlobalKind::Host(index),
115 }
116 }
117
118 pub(crate) fn new_instance(
119 store: &StoreOpaque,
120 instance: InstanceId,
121 index: DefinedGlobalIndex,
122 ) -> Global {
123 Global {
124 store: store.id(),
125 instance: instance.as_u32(),
126 kind: VMGlobalKind::Instance(index),
127 }
128 }
129
130 pub fn ty(&self, store: impl AsContext) -> GlobalType {
136 self._ty(store.as_context().0)
137 }
138
139 pub(crate) fn _ty(&self, store: &StoreOpaque) -> GlobalType {
140 GlobalType::from_wasmtime_global(store.engine(), self.wasmtime_ty(store))
141 }
142
143 pub fn get(&self, mut store: impl AsContextMut) -> Val {
149 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
150 self._get(&mut store)
151 }
152
153 pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>) -> Val {
154 unsafe {
155 let definition = self.definition(store).as_ref();
156 match self._ty(&store).content() {
157 ValType::I32 => Val::from(*definition.as_i32()),
158 ValType::I64 => Val::from(*definition.as_i64()),
159 ValType::F32 => Val::F32(*definition.as_u32()),
160 ValType::F64 => Val::F64(*definition.as_u64()),
161 ValType::V128 => Val::V128(definition.get_u128().into()),
162 ValType::Ref(ref_ty) => {
163 let reference: Ref = match ref_ty.heap_type() {
164 HeapType::Func | HeapType::ConcreteFunc(_) => {
165 Func::_from_raw(store, definition.as_func_ref().cast()).into()
166 }
167
168 HeapType::NoFunc => Ref::Func(None),
169
170 HeapType::Extern => Ref::Extern(definition.as_gc_ref().map(|r| {
171 let r = store.clone_gc_ref(r);
172 ExternRef::from_cloned_gc_ref(store, r)
173 })),
174
175 HeapType::NoCont | HeapType::ConcreteCont(_) | HeapType::Cont => {
176 unimplemented!()
178 }
179
180 HeapType::NoExtern => Ref::Extern(None),
181
182 HeapType::Exn | HeapType::ConcreteExn(_) => definition
183 .as_gc_ref()
184 .map(|r| {
185 let r = store.clone_gc_ref(r);
186 ExnRef::from_cloned_gc_ref(store, r)
187 })
188 .into(),
189
190 HeapType::Any
191 | HeapType::Eq
192 | HeapType::I31
193 | HeapType::Struct
194 | HeapType::ConcreteStruct(_)
195 | HeapType::Array
196 | HeapType::ConcreteArray(_) => definition
197 .as_gc_ref()
198 .map(|r| {
199 let r = store.clone_gc_ref(r);
200 AnyRef::from_cloned_gc_ref(store, r)
201 })
202 .into(),
203
204 HeapType::NoExn => Ref::Exn(None),
205
206 HeapType::None => Ref::Any(None),
207 };
208 debug_assert!(
209 ref_ty.is_nullable() || !reference.is_null(),
210 "if the type is non-nullable, we better have a non-null reference"
211 );
212 reference.into()
213 }
214 }
215 }
216 }
217
218 pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> {
234 self._set(store.as_context_mut().0, val)
235 }
236
237 pub(crate) fn _set(&self, store: &mut StoreOpaque, val: Val) -> Result<()> {
238 let global_ty = self._ty(&store);
239 if global_ty.mutability() != Mutability::Var {
240 bail!("immutable global cannot be set");
241 }
242 val.ensure_matches_ty(&store, global_ty.content())
243 .context("type mismatch: attempt to set global to value of wrong type")?;
244
245 unsafe { self.set_unchecked(store, &val) }
247 }
248
249 pub(crate) unsafe fn set_unchecked(&self, store: &mut StoreOpaque, val: &Val) -> Result<()> {
257 let mut store = AutoAssertNoGc::new(store);
258 unsafe {
259 let definition = self.definition(&store).as_mut();
260 match val {
261 Val::I32(i) => *definition.as_i32_mut() = *i,
262 Val::I64(i) => *definition.as_i64_mut() = *i,
263 Val::F32(f) => *definition.as_u32_mut() = *f,
264 Val::F64(f) => *definition.as_u64_mut() = *f,
265 Val::V128(i) => definition.set_u128((*i).into()),
266 Val::FuncRef(f) => {
267 *definition.as_func_ref_mut() =
268 f.map_or(ptr::null_mut(), |f| f.vm_func_ref(&store).as_ptr().cast());
269 }
270 Val::ExternRef(e) => {
271 let new = match e {
272 None => None,
273 Some(e) => Some(e.try_gc_ref(&store)?.unchecked_copy()),
274 };
275 let new = new.as_ref();
276 definition.write_gc_ref(&mut store, new)?;
277 }
278 Val::AnyRef(a) => {
279 let new = match a {
280 None => None,
281 Some(a) => Some(a.try_gc_ref(&store)?.unchecked_copy()),
282 };
283 let new = new.as_ref();
284 definition.write_gc_ref(&mut store, new)?;
285 }
286 Val::ExnRef(e) => {
287 let new = match e {
288 None => None,
289 Some(e) => Some(e.try_gc_ref(&store)?.unchecked_copy()),
290 };
291 let new = new.as_ref();
292 definition.write_gc_ref(&mut store, new)?;
293 }
294 Val::ContRef(None) => {
295 definition.write_gc_ref(&mut store, None)?;
297 }
298 Val::ContRef(Some(_)) => {
299 return Err(crate::format_err!(
301 "setting non-null continuation references in globals not yet supported"
302 ));
303 }
304 }
305 }
306 Ok(())
307 }
308
309 #[cfg(feature = "gc")]
310 pub(crate) fn trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
311 if let Some(ref_ty) = self._ty(store).content().as_ref() {
312 if !ref_ty.is_vmgcref_type_and_points_to_object() {
313 return;
314 }
315
316 if let Some(gc_ref) = unsafe { self.definition(store).as_mut().as_gc_ref_mut() } {
317 unsafe {
318 gc_roots_list.add_vmgcref_root(gc_ref.into(), "Wasm global");
319 }
320 }
321 }
322 }
323
324 pub(crate) fn from_host(store: StoreId, index: DefinedGlobalIndex) -> Global {
325 Global {
326 store,
327 instance: 0,
328 kind: VMGlobalKind::Host(index),
329 }
330 }
331
332 pub(crate) fn from_core(instance: StoreInstanceId, index: DefinedGlobalIndex) -> Global {
333 Global {
334 store: instance.store_id(),
335 instance: instance.instance().as_u32(),
336 kind: VMGlobalKind::Instance(index),
337 }
338 }
339
340 #[cfg(feature = "component-model")]
341 pub(crate) fn from_component_flags(
342 instance: crate::component::store::StoreComponentInstanceId,
343 index: wasmtime_environ::component::RuntimeComponentInstanceIndex,
344 ) -> Global {
345 Global {
346 store: instance.store_id(),
347 instance: instance.instance().as_u32(),
348 kind: VMGlobalKind::ComponentFlags(index),
349 }
350 }
351
352 #[cfg(feature = "component-model")]
353 pub(crate) fn from_task_may_block(
354 instance: crate::component::store::StoreComponentInstanceId,
355 ) -> Global {
356 Global {
357 store: instance.store_id(),
358 instance: instance.instance().as_u32(),
359 kind: VMGlobalKind::TaskMayBlock,
360 }
361 }
362
363 pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Global {
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 let module = store.instance(instance).env_module();
369 let index = module.global_index(index);
370 &module.globals[index]
371 }
372 VMGlobalKind::Host(index) => unsafe { &store.host_globals()[index].get().as_ref().ty },
373 #[cfg(feature = "component-model")]
374 VMGlobalKind::ComponentFlags(_) | VMGlobalKind::TaskMayBlock => {
375 const TY: wasmtime_environ::Global = wasmtime_environ::Global {
376 mutability: true,
377 wasm_ty: wasmtime_environ::WasmValType::I32,
378 };
379 &TY
380 }
381 }
382 }
383
384 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMGlobalImport {
385 let vmctx = match self.kind {
386 VMGlobalKind::Instance(_) => {
387 let instance = InstanceId::from_u32(self.instance);
388 Some(VMOpaqueContext::from_vmcontext(store.instance(instance).vmctx()).into())
389 }
390 VMGlobalKind::Host(_) => None,
391 #[cfg(feature = "component-model")]
392 VMGlobalKind::ComponentFlags(_) | VMGlobalKind::TaskMayBlock => {
393 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
394 Some(
395 VMOpaqueContext::from_vmcomponent(store.component_instance(instance).vmctx())
396 .into(),
397 )
398 }
399 };
400 vm::VMGlobalImport {
401 from: self.definition(store).into(),
402 vmctx,
403 kind: self.kind,
404 }
405 }
406
407 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
408 store.id() == self.store
409 }
410
411 #[cfg(feature = "debug")]
416 pub fn debug_index_in_store(&self) -> u64 {
417 match self.kind {
418 VMGlobalKind::Instance(idx) => u64::from(self.instance) << 32 | u64::from(idx.as_u32()),
419 VMGlobalKind::Host(idx) => u64::from(u32::MAX) << 32 | u64::from(idx.as_u32()),
420 #[cfg(feature = "component-model")]
421 VMGlobalKind::ComponentFlags(idx) => {
422 u64::from(self.instance) << 32 | u64::from(idx.as_u32())
423 }
424 #[cfg(feature = "component-model")]
425 VMGlobalKind::TaskMayBlock => u64::from(self.instance) << 32 | u64::from(u32::MAX),
426 }
427 }
428
429 #[cfg(feature = "coredump")]
435 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<> {
436 self.definition(store).as_ptr().addr()
437 }
438
439 fn definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition> {
440 self.store.assert_belongs_to(store.id());
441 match self.kind {
442 VMGlobalKind::Instance(index) => {
443 let instance = InstanceId::from_u32(self.instance);
444 store.instance(instance).global_ptr(index)
445 }
446 VMGlobalKind::Host(index) => unsafe {
447 NonNull::from(&mut store.host_globals()[index].get().as_mut().global)
448 },
449 #[cfg(feature = "component-model")]
450 VMGlobalKind::ComponentFlags(index) => {
451 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
452 store
453 .component_instance(instance)
454 .instance_flags(index)
455 .as_raw()
456 }
457 #[cfg(feature = "component-model")]
458 VMGlobalKind::TaskMayBlock => store
459 .component_instance(crate::component::ComponentInstanceId::from_u32(
460 self.instance,
461 ))
462 .task_may_block(),
463 }
464 }
465}
466
467#[cfg(test)]
468mod tests {
469 use super::*;
470 use crate::{Instance, Module, Store};
471
472 #[test]
473 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
474 let mut store = Store::<()>::default();
475 let module = Module::new(
476 store.engine(),
477 r#"
478 (module
479 (global (export "g") (mut i32) (i32.const 0))
480 )
481 "#,
482 )?;
483 let instance = Instance::new(&mut store, &module, &[])?;
484
485 let g1 = instance.get_global(&mut store, "g").unwrap();
489 let g2 = instance.get_global(&mut store, "g").unwrap();
490
491 assert_eq!(g1.get(&mut store).unwrap_i32(), 0);
493 assert_eq!(g2.get(&mut store).unwrap_i32(), 0);
494 g1.set(&mut store, Val::I32(42))?;
495 assert_eq!(g1.get(&mut store).unwrap_i32(), 42);
496 assert_eq!(g2.get(&mut store).unwrap_i32(), 42);
497
498 assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0));
500
501 let instance2 = Instance::new(&mut store, &module, &[])?;
503 let g3 = instance2.get_global(&mut store, "g").unwrap();
504 assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0));
505
506 Ok(())
507 }
508}