wasmtime/runtime/externals/
global.rs1use crate::prelude::*;
2use crate::runtime::vm::{
3 self, ExportGlobalKind, VMGlobalDefinition, VMGlobalKind, VMOpaqueContext,
4};
5use crate::{
6 AnyRef, AsContext, AsContextMut, ExternRef, Func, GlobalType, HeapType, Mutability, Ref,
7 RootedGcRefImpl, Val, ValType,
8 store::{AutoAssertNoGc, InstanceId, StoreId, StoreOpaque},
9 trampoline::generate_global_export,
10};
11use core::ptr;
12use core::ptr::NonNull;
13use wasmtime_environ::{DefinedGlobalIndex, TypeTrace};
14
15#[derive(Copy, Clone, Debug)]
28#[repr(C)] pub struct Global {
30 store: StoreId,
32 instance: u32,
35 kind: VMGlobalKind,
37}
38
39const _: () = {
42 #[repr(C)]
43 struct C(u64, u32, u32, u32);
44 assert!(core::mem::size_of::<C>() == core::mem::size_of::<Global>());
45 assert!(core::mem::align_of::<C>() == core::mem::align_of::<Global>());
46 assert!(core::mem::offset_of!(Global, store) == 0);
47};
48
49impl Global {
50 pub fn new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result<Global> {
95 Global::_new(store.as_context_mut().0, ty, val)
96 }
97
98 fn _new(store: &mut StoreOpaque, ty: GlobalType, val: Val) -> Result<Global> {
99 val.ensure_matches_ty(store, ty.content()).context(
100 "type mismatch: initial value provided does not match the type of this global",
101 )?;
102 unsafe {
103 let wasmtime_export = generate_global_export(store, ty, val)?;
104 Ok(Global::from_wasmtime_global(wasmtime_export, store))
105 }
106 }
107
108 pub(crate) fn new_host(store: &StoreOpaque, index: DefinedGlobalIndex) -> Global {
109 Global {
110 store: store.id(),
111 instance: 0,
112 kind: VMGlobalKind::Host(index),
113 }
114 }
115
116 pub(crate) fn new_instance(
117 store: &StoreOpaque,
118 instance: InstanceId,
119 index: DefinedGlobalIndex,
120 ) -> Global {
121 Global {
122 store: store.id(),
123 instance: instance.as_u32(),
124 kind: VMGlobalKind::Instance(index),
125 }
126 }
127
128 pub fn ty(&self, store: impl AsContext) -> GlobalType {
134 self._ty(store.as_context().0)
135 }
136
137 pub(crate) fn _ty(&self, store: &StoreOpaque) -> GlobalType {
138 GlobalType::from_wasmtime_global(store.engine(), self.wasmtime_ty(store))
139 }
140
141 pub fn get(&self, mut store: impl AsContextMut) -> Val {
147 unsafe {
148 let store = store.as_context_mut();
149 let definition = self.definition(store.0).as_ref();
150 let mut store = AutoAssertNoGc::new(store.0);
151 match self._ty(&store).content() {
152 ValType::I32 => Val::from(*definition.as_i32()),
153 ValType::I64 => Val::from(*definition.as_i64()),
154 ValType::F32 => Val::F32(*definition.as_u32()),
155 ValType::F64 => Val::F64(*definition.as_u64()),
156 ValType::V128 => Val::V128(definition.get_u128().into()),
157 ValType::Ref(ref_ty) => {
158 let reference: Ref = match ref_ty.heap_type() {
159 HeapType::Func | HeapType::ConcreteFunc(_) => {
160 Func::_from_raw(&mut store, definition.as_func_ref().cast()).into()
161 }
162
163 HeapType::NoFunc => Ref::Func(None),
164
165 HeapType::Extern => Ref::Extern(definition.as_gc_ref().map(|r| {
166 let r = store.unwrap_gc_store_mut().clone_gc_ref(r);
167 ExternRef::from_cloned_gc_ref(&mut store, r)
168 })),
169
170 HeapType::NoCont | HeapType::ConcreteCont(_) | HeapType::Cont => {
171 unimplemented!()
173 }
174
175 HeapType::NoExtern => Ref::Extern(None),
176
177 HeapType::Any
178 | HeapType::Eq
179 | HeapType::I31
180 | HeapType::Struct
181 | HeapType::ConcreteStruct(_)
182 | HeapType::Array
183 | HeapType::ConcreteArray(_) => definition
184 .as_gc_ref()
185 .map(|r| {
186 let r = store.unwrap_gc_store_mut().clone_gc_ref(r);
187 AnyRef::from_cloned_gc_ref(&mut store, r)
188 })
189 .into(),
190
191 HeapType::None => Ref::Any(None),
192 };
193 debug_assert!(
194 ref_ty.is_nullable() || !reference.is_null(),
195 "if the type is non-nullable, we better have a non-null reference"
196 );
197 reference.into()
198 }
199 }
200 }
201 }
202
203 pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> {
215 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
216 let global_ty = self._ty(&store);
217 if global_ty.mutability() != Mutability::Var {
218 bail!("immutable global cannot be set");
219 }
220 val.ensure_matches_ty(&store, global_ty.content())
221 .context("type mismatch: attempt to set global to value of wrong type")?;
222 unsafe {
223 let definition = self.definition(&store).as_mut();
224 match val {
225 Val::I32(i) => *definition.as_i32_mut() = i,
226 Val::I64(i) => *definition.as_i64_mut() = i,
227 Val::F32(f) => *definition.as_u32_mut() = f,
228 Val::F64(f) => *definition.as_u64_mut() = f,
229 Val::V128(i) => definition.set_u128(i.into()),
230 Val::FuncRef(f) => {
231 *definition.as_func_ref_mut() =
232 f.map_or(ptr::null_mut(), |f| f.vm_func_ref(&store).as_ptr().cast());
233 }
234 Val::ExternRef(e) => {
235 let new = match e {
236 None => None,
237 #[cfg_attr(not(feature = "gc"), allow(unreachable_patterns))]
238 Some(e) => Some(e.try_gc_ref(&store)?.unchecked_copy()),
239 };
240 let new = new.as_ref();
241 definition.write_gc_ref(store.unwrap_gc_store_mut(), new);
242 }
243 Val::AnyRef(a) => {
244 let new = match a {
245 None => None,
246 #[cfg_attr(not(feature = "gc"), allow(unreachable_patterns))]
247 Some(a) => Some(a.try_gc_ref(&store)?.unchecked_copy()),
248 };
249 let new = new.as_ref();
250 definition.write_gc_ref(store.unwrap_gc_store_mut(), new);
251 }
252 }
253 }
254 Ok(())
255 }
256
257 #[cfg(feature = "gc")]
258 pub(crate) fn trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
259 if let Some(ref_ty) = self._ty(store).content().as_ref() {
260 if !ref_ty.is_vmgcref_type_and_points_to_object() {
261 return;
262 }
263
264 if let Some(gc_ref) = unsafe { self.definition(store).as_ref().as_gc_ref() } {
265 unsafe {
266 gc_roots_list.add_root(gc_ref.into(), "Wasm global");
267 }
268 }
269 }
270 }
271
272 pub(crate) unsafe fn from_wasmtime_global(
273 wasmtime_export: vm::ExportGlobal,
274 store: &StoreOpaque,
275 ) -> Global {
276 debug_assert!(
277 wasmtime_export
278 .global
279 .wasm_ty
280 .is_canonicalized_for_runtime_usage()
281 );
282 let (instance, kind) = match wasmtime_export.kind {
283 ExportGlobalKind::Host(index) => (0, VMGlobalKind::Host(index)),
284 ExportGlobalKind::Instance(vmctx, index) => (
285 vm::Instance::from_vmctx(vmctx, |i| i.id().as_u32()),
286 VMGlobalKind::Instance(index),
287 ),
288 #[cfg(feature = "component-model")]
289 ExportGlobalKind::ComponentFlags(vmctx, index) => (
290 vm::component::ComponentInstance::from_vmctx(vmctx, |_, i| {
291 i.id().instance().as_u32()
292 }),
293 VMGlobalKind::ComponentFlags(index),
294 ),
295 };
296 Global {
297 store: store.id(),
298 instance,
299 kind,
300 }
301 }
302
303 pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Global {
304 self.store.assert_belongs_to(store.id());
305 match self.kind {
306 VMGlobalKind::Instance(index) => {
307 let instance = InstanceId::from_u32(self.instance);
308 let module = store.instance(instance).env_module();
309 let index = module.global_index(index);
310 &module.globals[index]
311 }
312 VMGlobalKind::Host(index) => unsafe { &store.host_globals()[index].get().as_ref().ty },
313 #[cfg(feature = "component-model")]
314 VMGlobalKind::ComponentFlags(_) => {
315 const TY: wasmtime_environ::Global = wasmtime_environ::Global {
316 mutability: true,
317 wasm_ty: wasmtime_environ::WasmValType::I32,
318 };
319 &TY
320 }
321 }
322 }
323
324 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMGlobalImport {
325 let vmctx = match self.kind {
326 VMGlobalKind::Instance(_) => {
327 let instance = InstanceId::from_u32(self.instance);
328 Some(VMOpaqueContext::from_vmcontext(store.instance(instance).vmctx()).into())
329 }
330 VMGlobalKind::Host(_) => None,
331 #[cfg(feature = "component-model")]
332 VMGlobalKind::ComponentFlags(_) => {
333 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
334 Some(
335 VMOpaqueContext::from_vmcomponent(store.component_instance(instance).vmctx())
336 .into(),
337 )
338 }
339 };
340 vm::VMGlobalImport {
341 from: self.definition(store).into(),
342 vmctx,
343 kind: self.kind,
344 }
345 }
346
347 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
348 store.id() == self.store
349 }
350
351 #[cfg(feature = "coredump")]
357 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<> {
358 self.definition(store).as_ptr().addr()
359 }
360
361 fn definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition> {
362 self.store.assert_belongs_to(store.id());
363 match self.kind {
364 VMGlobalKind::Instance(index) => {
365 let instance = InstanceId::from_u32(self.instance);
366 store.instance(instance).global_ptr(index)
367 }
368 VMGlobalKind::Host(index) => unsafe {
369 NonNull::from(&mut store.host_globals()[index].get().as_mut().global)
370 },
371 #[cfg(feature = "component-model")]
372 VMGlobalKind::ComponentFlags(index) => {
373 let instance = crate::component::ComponentInstanceId::from_u32(self.instance);
374 store
375 .component_instance(instance)
376 .instance_flags(index)
377 .as_raw()
378 }
379 }
380 }
381}
382
383#[cfg(test)]
384mod tests {
385 use super::*;
386 use crate::{Instance, Module, Store};
387
388 #[test]
389 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
390 let mut store = Store::<()>::default();
391 let module = Module::new(
392 store.engine(),
393 r#"
394 (module
395 (global (export "g") (mut i32) (i32.const 0))
396 )
397 "#,
398 )?;
399 let instance = Instance::new(&mut store, &module, &[])?;
400
401 let g1 = instance.get_global(&mut store, "g").unwrap();
405 let g2 = instance.get_global(&mut store, "g").unwrap();
406
407 assert_eq!(g1.get(&mut store).unwrap_i32(), 0);
409 assert_eq!(g2.get(&mut store).unwrap_i32(), 0);
410 g1.set(&mut store, Val::I32(42))?;
411 assert_eq!(g1.get(&mut store).unwrap_i32(), 42);
412 assert_eq!(g2.get(&mut store).unwrap_i32(), 42);
413
414 assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0));
416
417 let instance2 = Instance::new(&mut store, &module, &[])?;
419 let g3 = instance2.get_global(&mut store, "g").unwrap();
420 assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0));
421
422 Ok(())
423 }
424}