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> {
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 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
143 self._get(&mut store)
144 }
145
146 pub(crate) fn _get(&self, store: &mut AutoAssertNoGc<'_>) -> Val {
147 unsafe {
148 let definition = self.definition(store).as_ref();
149 match self._ty(&store).content() {
150 ValType::I32 => Val::from(*definition.as_i32()),
151 ValType::I64 => Val::from(*definition.as_i64()),
152 ValType::F32 => Val::F32(*definition.as_u32()),
153 ValType::F64 => Val::F64(*definition.as_u64()),
154 ValType::V128 => Val::V128(definition.get_u128().into()),
155 ValType::Ref(ref_ty) => {
156 let reference: Ref = match ref_ty.heap_type() {
157 HeapType::Func | HeapType::ConcreteFunc(_) => {
158 Func::_from_raw(store, definition.as_func_ref().cast()).into()
159 }
160
161 HeapType::NoFunc => Ref::Func(None),
162
163 HeapType::Extern => Ref::Extern(definition.as_gc_ref().map(|r| {
164 let r = store.clone_gc_ref(r);
165 ExternRef::from_cloned_gc_ref(store, r)
166 })),
167
168 HeapType::NoCont | HeapType::ConcreteCont(_) | HeapType::Cont => {
169 unimplemented!()
171 }
172
173 HeapType::NoExtern => Ref::Extern(None),
174
175 HeapType::Exn | HeapType::ConcreteExn(_) => definition
176 .as_gc_ref()
177 .map(|r| {
178 let r = store.clone_gc_ref(r);
179 ExnRef::from_cloned_gc_ref(store, r)
180 })
181 .into(),
182
183 HeapType::Any
184 | HeapType::Eq
185 | HeapType::I31
186 | HeapType::Struct
187 | HeapType::ConcreteStruct(_)
188 | HeapType::Array
189 | HeapType::ConcreteArray(_) => definition
190 .as_gc_ref()
191 .map(|r| {
192 let r = store.clone_gc_ref(r);
193 AnyRef::from_cloned_gc_ref(store, r)
194 })
195 .into(),
196
197 HeapType::NoExn => Ref::Exn(None),
198
199 HeapType::None => Ref::Any(None),
200 };
201 debug_assert!(
202 ref_ty.is_nullable() || !reference.is_null(),
203 "if the type is non-nullable, we better have a non-null reference"
204 );
205 reference.into()
206 }
207 }
208 }
209 }
210
211 pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> {
223 self._set(store.as_context_mut().0, val)
224 }
225
226 pub(crate) fn _set(&self, store: &mut StoreOpaque, val: Val) -> Result<()> {
227 let global_ty = self._ty(&store);
228 if global_ty.mutability() != Mutability::Var {
229 bail!("immutable global cannot be set");
230 }
231 val.ensure_matches_ty(&store, global_ty.content())
232 .context("type mismatch: attempt to set global to value of wrong type")?;
233
234 unsafe { self.set_unchecked(store, &val) }
236 }
237
238 pub(crate) unsafe fn set_unchecked(&self, store: &mut StoreOpaque, val: &Val) -> Result<()> {
246 let mut store = AutoAssertNoGc::new(store);
247 unsafe {
248 let definition = self.definition(&store).as_mut();
249 match val {
250 Val::I32(i) => *definition.as_i32_mut() = *i,
251 Val::I64(i) => *definition.as_i64_mut() = *i,
252 Val::F32(f) => *definition.as_u32_mut() = *f,
253 Val::F64(f) => *definition.as_u64_mut() = *f,
254 Val::V128(i) => definition.set_u128((*i).into()),
255 Val::FuncRef(f) => {
256 *definition.as_func_ref_mut() =
257 f.map_or(ptr::null_mut(), |f| f.vm_func_ref(&store).as_ptr().cast());
258 }
259 Val::ExternRef(e) => {
260 let new = match e {
261 None => None,
262 Some(e) => Some(e.try_gc_ref(&store)?.unchecked_copy()),
263 };
264 let new = new.as_ref();
265 definition.write_gc_ref(&mut store, new);
266 }
267 Val::AnyRef(a) => {
268 let new = match a {
269 None => None,
270 Some(a) => Some(a.try_gc_ref(&store)?.unchecked_copy()),
271 };
272 let new = new.as_ref();
273 definition.write_gc_ref(&mut store, new);
274 }
275 Val::ExnRef(e) => {
276 let new = match e {
277 None => None,
278 Some(e) => Some(e.try_gc_ref(&store)?.unchecked_copy()),
279 };
280 let new = new.as_ref();
281 definition.write_gc_ref(&mut store, new);
282 }
283 Val::ContRef(None) => {
284 definition.write_gc_ref(&mut store, None);
286 }
287 Val::ContRef(Some(_)) => {
288 return Err(crate::format_err!(
290 "setting non-null continuation references in globals not yet supported"
291 ));
292 }
293 }
294 }
295 Ok(())
296 }
297
298 #[cfg(feature = "gc")]
299 pub(crate) fn trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
300 if let Some(ref_ty) = self._ty(store).content().as_ref() {
301 if !ref_ty.is_vmgcref_type_and_points_to_object() {
302 return;
303 }
304
305 if let Some(gc_ref) = unsafe { self.definition(store).as_ref().as_gc_ref() } {
306 unsafe {
307 gc_roots_list.add_root(gc_ref.into(), "Wasm global");
308 }
309 }
310 }
311 }
312
313 pub(crate) fn from_host(store: StoreId, index: DefinedGlobalIndex) -> Global {
314 Global {
315 store,
316 instance: 0,
317 kind: VMGlobalKind::Host(index),
318 }
319 }
320
321 pub(crate) fn from_core(instance: StoreInstanceId, index: DefinedGlobalIndex) -> Global {
322 Global {
323 store: instance.store_id(),
324 instance: instance.instance().as_u32(),
325 kind: VMGlobalKind::Instance(index),
326 }
327 }
328
329 #[cfg(feature = "component-model")]
330 pub(crate) fn from_component_flags(
331 instance: crate::component::store::StoreComponentInstanceId,
332 index: wasmtime_environ::component::RuntimeComponentInstanceIndex,
333 ) -> Global {
334 Global {
335 store: instance.store_id(),
336 instance: instance.instance().as_u32(),
337 kind: VMGlobalKind::ComponentFlags(index),
338 }
339 }
340
341 #[cfg(feature = "component-model")]
342 pub(crate) fn from_task_may_block(
343 instance: crate::component::store::StoreComponentInstanceId,
344 ) -> Global {
345 Global {
346 store: instance.store_id(),
347 instance: instance.instance().as_u32(),
348 kind: VMGlobalKind::TaskMayBlock,
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(_) | VMGlobalKind::TaskMayBlock => {
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(_) | VMGlobalKind::TaskMayBlock => {
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 = "coredump")]
406 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<> {
407 self.definition(store).as_ptr().addr()
408 }
409
410 fn definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition> {
411 self.store.assert_belongs_to(store.id());
412 match self.kind {
413 VMGlobalKind::Instance(index) => {
414 let instance = InstanceId::from_u32(self.instance);
415 store.instance(instance).global_ptr(index)
416 }
417 VMGlobalKind::Host(index) => unsafe {
418 NonNull::from(&mut store.host_globals()[index].get().as_mut().global)
419 },
420 #[cfg(feature = "component-model")]
421 VMGlobalKind::ComponentFlags(index) => {
422 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
423 store
424 .component_instance(instance)
425 .instance_flags(index)
426 .as_raw()
427 }
428 #[cfg(feature = "component-model")]
429 VMGlobalKind::TaskMayBlock => store
430 .component_instance(crate::component::ComponentInstanceId::from_u32(
431 self.instance,
432 ))
433 .task_may_block(),
434 }
435 }
436}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441 use crate::{Instance, Module, Store};
442
443 #[test]
444 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
445 let mut store = Store::<()>::default();
446 let module = Module::new(
447 store.engine(),
448 r#"
449 (module
450 (global (export "g") (mut i32) (i32.const 0))
451 )
452 "#,
453 )?;
454 let instance = Instance::new(&mut store, &module, &[])?;
455
456 let g1 = instance.get_global(&mut store, "g").unwrap();
460 let g2 = instance.get_global(&mut store, "g").unwrap();
461
462 assert_eq!(g1.get(&mut store).unwrap_i32(), 0);
464 assert_eq!(g2.get(&mut store).unwrap_i32(), 0);
465 g1.set(&mut store, Val::I32(42))?;
466 assert_eq!(g1.get(&mut store).unwrap_i32(), 42);
467 assert_eq!(g2.get(&mut store).unwrap_i32(), 42);
468
469 assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0));
471
472 let instance2 = Instance::new(&mut store, &module, &[])?;
474 let g3 = instance2.get_global(&mut store, "g").unwrap();
475 assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0));
476
477 Ok(())
478 }
479}