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 pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Global {
353 self.store.assert_belongs_to(store.id());
354 match self.kind {
355 VMGlobalKind::Instance(index) => {
356 let instance = InstanceId::from_u32(self.instance);
357 let module = store.instance(instance).env_module();
358 let index = module.global_index(index);
359 &module.globals[index]
360 }
361 VMGlobalKind::Host(index) => unsafe { &store.host_globals()[index].get().as_ref().ty },
362 #[cfg(feature = "component-model")]
363 VMGlobalKind::ComponentFlags(_) => {
364 const TY: wasmtime_environ::Global = wasmtime_environ::Global {
365 mutability: true,
366 wasm_ty: wasmtime_environ::WasmValType::I32,
367 };
368 &TY
369 }
370 }
371 }
372
373 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMGlobalImport {
374 let vmctx = match self.kind {
375 VMGlobalKind::Instance(_) => {
376 let instance = InstanceId::from_u32(self.instance);
377 Some(VMOpaqueContext::from_vmcontext(store.instance(instance).vmctx()).into())
378 }
379 VMGlobalKind::Host(_) => None,
380 #[cfg(feature = "component-model")]
381 VMGlobalKind::ComponentFlags(_) => {
382 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
383 Some(
384 VMOpaqueContext::from_vmcomponent(store.component_instance(instance).vmctx())
385 .into(),
386 )
387 }
388 };
389 vm::VMGlobalImport {
390 from: self.definition(store).into(),
391 vmctx,
392 kind: self.kind,
393 }
394 }
395
396 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
397 store.id() == self.store
398 }
399
400 #[cfg(feature = "debug")]
405 pub fn debug_index_in_store(&self) -> u64 {
406 match self.kind {
407 VMGlobalKind::Instance(idx) => u64::from(self.instance) << 32 | u64::from(idx.as_u32()),
408 VMGlobalKind::Host(idx) => u64::from(u32::MAX) << 32 | u64::from(idx.as_u32()),
409 #[cfg(feature = "component-model")]
410 VMGlobalKind::ComponentFlags(idx) => {
411 u64::from(self.instance) << 32 | u64::from(idx.as_u32())
412 }
413 }
414 }
415
416 #[cfg(feature = "coredump")]
422 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<> {
423 self.definition(store).as_ptr().addr()
424 }
425
426 fn definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition> {
427 self.store.assert_belongs_to(store.id());
428 match self.kind {
429 VMGlobalKind::Instance(index) => {
430 let instance = InstanceId::from_u32(self.instance);
431 store.instance(instance).global_ptr(index)
432 }
433 VMGlobalKind::Host(index) => unsafe {
434 NonNull::from(&mut store.host_globals()[index].get().as_mut().global)
435 },
436 #[cfg(feature = "component-model")]
437 VMGlobalKind::ComponentFlags(index) => {
438 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
439 store
440 .component_instance(instance)
441 .instance_flags(index)
442 .as_raw()
443 }
444 }
445 }
446}
447
448#[cfg(test)]
449mod tests {
450 use super::*;
451 use crate::{Instance, Module, Store};
452
453 #[test]
454 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
455 let mut store = Store::<()>::default();
456 let module = Module::new(
457 store.engine(),
458 r#"
459 (module
460 (global (export "g") (mut i32) (i32.const 0))
461 )
462 "#,
463 )?;
464 let instance = Instance::new(&mut store, &module, &[])?;
465
466 let g1 = instance.get_global(&mut store, "g").unwrap();
470 let g2 = instance.get_global(&mut store, "g").unwrap();
471
472 assert_eq!(g1.get(&mut store).unwrap_i32(), 0);
474 assert_eq!(g2.get(&mut store).unwrap_i32(), 0);
475 g1.set(&mut store, Val::I32(42))?;
476 assert_eq!(g1.get(&mut store).unwrap_i32(), 42);
477 assert_eq!(g2.get(&mut store).unwrap_i32(), 42);
478
479 assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0));
481
482 let instance2 = Instance::new(&mut store, &module, &[])?;
484 let g3 = instance2.get_global(&mut store, "g").unwrap();
485 assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0));
486
487 Ok(())
488 }
489}