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