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 }
284 }
285 Ok(())
286 }
287
288 #[cfg(feature = "gc")]
289 pub(crate) fn trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
290 if let Some(ref_ty) = self._ty(store).content().as_ref() {
291 if !ref_ty.is_vmgcref_type_and_points_to_object() {
292 return;
293 }
294
295 if let Some(gc_ref) = unsafe { self.definition(store).as_ref().as_gc_ref() } {
296 unsafe {
297 gc_roots_list.add_root(gc_ref.into(), "Wasm global");
298 }
299 }
300 }
301 }
302
303 pub(crate) fn from_host(store: StoreId, index: DefinedGlobalIndex) -> Global {
304 Global {
305 store,
306 instance: 0,
307 kind: VMGlobalKind::Host(index),
308 }
309 }
310
311 pub(crate) fn from_core(instance: StoreInstanceId, index: DefinedGlobalIndex) -> Global {
312 Global {
313 store: instance.store_id(),
314 instance: instance.instance().as_u32(),
315 kind: VMGlobalKind::Instance(index),
316 }
317 }
318
319 #[cfg(feature = "component-model")]
320 pub(crate) fn from_component_flags(
321 instance: crate::component::store::StoreComponentInstanceId,
322 index: wasmtime_environ::component::RuntimeComponentInstanceIndex,
323 ) -> Global {
324 Global {
325 store: instance.store_id(),
326 instance: instance.instance().as_u32(),
327 kind: VMGlobalKind::ComponentFlags(index),
328 }
329 }
330
331 pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Global {
332 self.store.assert_belongs_to(store.id());
333 match self.kind {
334 VMGlobalKind::Instance(index) => {
335 let instance = InstanceId::from_u32(self.instance);
336 let module = store.instance(instance).env_module();
337 let index = module.global_index(index);
338 &module.globals[index]
339 }
340 VMGlobalKind::Host(index) => unsafe { &store.host_globals()[index].get().as_ref().ty },
341 #[cfg(feature = "component-model")]
342 VMGlobalKind::ComponentFlags(_) => {
343 const TY: wasmtime_environ::Global = wasmtime_environ::Global {
344 mutability: true,
345 wasm_ty: wasmtime_environ::WasmValType::I32,
346 };
347 &TY
348 }
349 }
350 }
351
352 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMGlobalImport {
353 let vmctx = match self.kind {
354 VMGlobalKind::Instance(_) => {
355 let instance = InstanceId::from_u32(self.instance);
356 Some(VMOpaqueContext::from_vmcontext(store.instance(instance).vmctx()).into())
357 }
358 VMGlobalKind::Host(_) => None,
359 #[cfg(feature = "component-model")]
360 VMGlobalKind::ComponentFlags(_) => {
361 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
362 Some(
363 VMOpaqueContext::from_vmcomponent(store.component_instance(instance).vmctx())
364 .into(),
365 )
366 }
367 };
368 vm::VMGlobalImport {
369 from: self.definition(store).into(),
370 vmctx,
371 kind: self.kind,
372 }
373 }
374
375 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
376 store.id() == self.store
377 }
378
379 #[cfg(feature = "coredump")]
385 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<> {
386 self.definition(store).as_ptr().addr()
387 }
388
389 fn definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition> {
390 self.store.assert_belongs_to(store.id());
391 match self.kind {
392 VMGlobalKind::Instance(index) => {
393 let instance = InstanceId::from_u32(self.instance);
394 store.instance(instance).global_ptr(index)
395 }
396 VMGlobalKind::Host(index) => unsafe {
397 NonNull::from(&mut store.host_globals()[index].get().as_mut().global)
398 },
399 #[cfg(feature = "component-model")]
400 VMGlobalKind::ComponentFlags(index) => {
401 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
402 store
403 .component_instance(instance)
404 .instance_flags(index)
405 .as_raw()
406 }
407 }
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 use super::*;
414 use crate::{Instance, Module, Store};
415
416 #[test]
417 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
418 let mut store = Store::<()>::default();
419 let module = Module::new(
420 store.engine(),
421 r#"
422 (module
423 (global (export "g") (mut i32) (i32.const 0))
424 )
425 "#,
426 )?;
427 let instance = Instance::new(&mut store, &module, &[])?;
428
429 let g1 = instance.get_global(&mut store, "g").unwrap();
433 let g2 = instance.get_global(&mut store, "g").unwrap();
434
435 assert_eq!(g1.get(&mut store).unwrap_i32(), 0);
437 assert_eq!(g2.get(&mut store).unwrap_i32(), 0);
438 g1.set(&mut store, Val::I32(42))?;
439 assert_eq!(g1.get(&mut store).unwrap_i32(), 42);
440 assert_eq!(g2.get(&mut store).unwrap_i32(), 42);
441
442 assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0));
444
445 let instance2 = Instance::new(&mut store, &module, &[])?;
447 let g3 = instance2.get_global(&mut store, "g").unwrap();
448 assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0));
449
450 Ok(())
451 }
452}