1 use crate::prelude::*; 2 use crate::runtime::vm::{self, VMGlobalDefinition, VMGlobalKind, VMOpaqueContext}; 3 use 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 }; 9 use core::ptr; 10 use core::ptr::NonNull; 11 use wasmtime_environ::DefinedGlobalIndex; 12 13 /// A WebAssembly `global` value which can be read and written to. 14 /// 15 /// A `global` in WebAssembly is sort of like a global variable within an 16 /// [`Instance`](crate::Instance). The `global.get` and `global.set` 17 /// instructions will modify and read global values in a wasm module. Globals 18 /// can either be imported or exported from wasm modules. 19 /// 20 /// A [`Global`] "belongs" to the store that it was originally created within 21 /// (either via [`Global::new`] or via instantiating a 22 /// [`Module`](crate::Module)). Operations on a [`Global`] only work with the 23 /// store it belongs to, and if another store is passed in by accident then 24 /// methods will panic. 25 #[derive(Copy, Clone, Debug)] 26 #[repr(C)] // here for the C API 27 pub struct Global { 28 /// The store that this global belongs to. 29 store: StoreId, 30 /// Either `InstanceId` or `ComponentInstanceId` internals depending on 31 /// `kind` below. 32 instance: u32, 33 /// Which method of definition was used when creating this global. 34 kind: VMGlobalKind, 35 } 36 37 // Double-check that the C representation in `extern.h` matches our in-Rust 38 // representation here in terms of size/alignment/etc. 39 const _: () = { 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 47 impl Global { 48 /// Creates a new WebAssembly `global` value with the provide type `ty` and 49 /// initial value `val`. 50 /// 51 /// The `store` argument will be the owner of the [`Global`] returned. Using 52 /// the returned [`Global`] other items in the store may access this global. 53 /// For example this could be provided as an argument to 54 /// [`Instance::new`](crate::Instance::new) or 55 /// [`Linker::define`](crate::Linker::define). 56 /// 57 /// # Errors 58 /// 59 /// Returns an error if the `ty` provided does not match the type of the 60 /// value `val`, or if `val` comes from a different store than `store`. 61 /// 62 /// # Examples 63 /// 64 /// ``` 65 /// # use wasmtime::*; 66 /// # fn main() -> Result<()> { 67 /// let engine = Engine::default(); 68 /// let mut store = Store::new(&engine, ()); 69 /// 70 /// let ty = GlobalType::new(ValType::I32, Mutability::Const); 71 /// let i32_const = Global::new(&mut store, ty, 1i32.into())?; 72 /// let ty = GlobalType::new(ValType::F64, Mutability::Var); 73 /// let f64_mut = Global::new(&mut store, ty, 2.0f64.into())?; 74 /// 75 /// let module = Module::new( 76 /// &engine, 77 /// "(module 78 /// (global (import \"\" \"i32-const\") i32) 79 /// (global (import \"\" \"f64-mut\") (mut f64)) 80 /// )" 81 /// )?; 82 /// 83 /// let mut linker = Linker::new(&engine); 84 /// linker.define(&store, "", "i32-const", i32_const)?; 85 /// linker.define(&store, "", "f64-mut", f64_mut)?; 86 /// 87 /// let instance = linker.instantiate(&mut store, &module)?; 88 /// // ... 89 /// # Ok(()) 90 /// # } 91 /// ``` new(mut store: impl AsContextMut, ty: GlobalType, val: Val) -> Result<Global>92 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 _new(store: &mut StoreOpaque, ty: GlobalType, val: Val) -> Result<Global>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 new_host(store: &StoreOpaque, index: DefinedGlobalIndex) -> Global103 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 new_instance( store: &StoreOpaque, instance: InstanceId, index: DefinedGlobalIndex, ) -> Global111 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 /// Returns the underlying type of this `global`. 124 /// 125 /// # Panics 126 /// 127 /// Panics if `store` does not own this global. ty(&self, store: impl AsContext) -> GlobalType128 pub fn ty(&self, store: impl AsContext) -> GlobalType { 129 self._ty(store.as_context().0) 130 } 131 _ty(&self, store: &StoreOpaque) -> GlobalType132 pub(crate) fn _ty(&self, store: &StoreOpaque) -> GlobalType { 133 GlobalType::from_wasmtime_global(store.engine(), self.wasmtime_ty(store)) 134 } 135 136 /// Returns the current [`Val`] of this global. 137 /// 138 /// # Panics 139 /// 140 /// Panics if `store` does not own this global. get(&self, mut store: impl AsContextMut) -> Val141 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 _get(&self, store: &mut AutoAssertNoGc<'_>) -> Val146 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 // TODO(#10248) Required to support stack switching in the embedder API. 170 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 /// Attempts to set the current value of this global to [`Val`]. 212 /// 213 /// # Errors 214 /// 215 /// Returns an error if this global has a different type than `Val`, if 216 /// it's not a mutable global, or if `val` comes from a different store than 217 /// the one provided. 218 /// 219 /// # Panics 220 /// 221 /// Panics if `store` does not own this global. set(&self, mut store: impl AsContextMut, val: Val) -> Result<()>222 pub fn set(&self, mut store: impl AsContextMut, val: Val) -> Result<()> { 223 self._set(store.as_context_mut().0, val) 224 } 225 _set(&self, store: &mut StoreOpaque, val: Val) -> Result<()>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 // SAFETY: mutability and a type-check above makes this safe to perform. 235 unsafe { self.set_unchecked(store, &val) } 236 } 237 238 /// Sets this global to `val`. 239 /// 240 /// # Safety 241 /// 242 /// This function requires that `val` is of the correct type for this 243 /// global. Furthermore this requires that the global is mutable or this is 244 /// the first time the global is initialized. set_unchecked(&self, store: &mut StoreOpaque, val: &Val) -> Result<()>245 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 // Allow null continuation references for globals - these are just placeholders 285 definition.write_gc_ref(&mut store, None); 286 } 287 Val::ContRef(Some(_)) => { 288 // TODO(#10248): Implement non-null global continuation reference handling 289 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")] trace_root(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList)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 from_host(store: StoreId, index: DefinedGlobalIndex) -> Global313 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 from_core(instance: StoreInstanceId, index: DefinedGlobalIndex) -> Global321 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")] from_component_flags( instance: crate::component::store::StoreComponentInstanceId, index: wasmtime_environ::component::RuntimeComponentInstanceIndex, ) -> Global330 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")] from_task_may_block( instance: crate::component::store::StoreComponentInstanceId, ) -> Global342 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 wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Global352 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 vmimport(&self, store: &StoreOpaque) -> vm::VMGlobalImport373 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 comes_from_same_store(&self, store: &StoreOpaque) -> bool396 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { 397 store.id() == self.store 398 } 399 400 /// Returns a stable identifier for this global within its store. 401 /// 402 /// This allows distinguishing globals when introspecting them 403 /// e.g. via debug APIs. 404 #[cfg(feature = "debug")] debug_index_in_store(&self) -> u64405 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 #[cfg(feature = "component-model")] 414 VMGlobalKind::TaskMayBlock => u64::from(self.instance) << 32 | u64::from(u32::MAX), 415 } 416 } 417 418 /// Get a stable hash key for this global. 419 /// 420 /// Even if the same underlying global definition is added to the 421 /// `StoreData` multiple times and becomes multiple `wasmtime::Global`s, 422 /// this hash key will be consistent across all of these globals. 423 #[cfg(feature = "coredump")] hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<>424 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<> { 425 self.definition(store).as_ptr().addr() 426 } 427 definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition>428 fn definition(&self, store: &StoreOpaque) -> NonNull<VMGlobalDefinition> { 429 self.store.assert_belongs_to(store.id()); 430 match self.kind { 431 VMGlobalKind::Instance(index) => { 432 let instance = InstanceId::from_u32(self.instance); 433 store.instance(instance).global_ptr(index) 434 } 435 VMGlobalKind::Host(index) => unsafe { 436 NonNull::from(&mut store.host_globals()[index].get().as_mut().global) 437 }, 438 #[cfg(feature = "component-model")] 439 VMGlobalKind::ComponentFlags(index) => { 440 let instance = crate::component::ComponentInstanceId::from_u32(self.instance); 441 store 442 .component_instance(instance) 443 .instance_flags(index) 444 .as_raw() 445 } 446 #[cfg(feature = "component-model")] 447 VMGlobalKind::TaskMayBlock => store 448 .component_instance(crate::component::ComponentInstanceId::from_u32( 449 self.instance, 450 )) 451 .task_may_block(), 452 } 453 } 454 } 455 456 #[cfg(test)] 457 mod tests { 458 use super::*; 459 use crate::{Instance, Module, Store}; 460 461 #[test] hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()>462 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> { 463 let mut store = Store::<()>::default(); 464 let module = Module::new( 465 store.engine(), 466 r#" 467 (module 468 (global (export "g") (mut i32) (i32.const 0)) 469 ) 470 "#, 471 )?; 472 let instance = Instance::new(&mut store, &module, &[])?; 473 474 // Each time we `get_global`, we call `Global::from_wasmtime` which adds 475 // a new entry to `StoreData`, so `g1` and `g2` will have different 476 // indices into `StoreData`. 477 let g1 = instance.get_global(&mut store, "g").unwrap(); 478 let g2 = instance.get_global(&mut store, "g").unwrap(); 479 480 // That said, they really point to the same global. 481 assert_eq!(g1.get(&mut store).unwrap_i32(), 0); 482 assert_eq!(g2.get(&mut store).unwrap_i32(), 0); 483 g1.set(&mut store, Val::I32(42))?; 484 assert_eq!(g1.get(&mut store).unwrap_i32(), 42); 485 assert_eq!(g2.get(&mut store).unwrap_i32(), 42); 486 487 // And therefore their hash keys are the same. 488 assert!(g1.hash_key(&store.as_context().0) == g2.hash_key(&store.as_context().0)); 489 490 // But the hash keys are different from different globals. 491 let instance2 = Instance::new(&mut store, &module, &[])?; 492 let g3 = instance2.get_global(&mut store, "g").unwrap(); 493 assert!(g1.hash_key(&store.as_context().0) != g3.hash_key(&store.as_context().0)); 494 495 Ok(()) 496 } 497 } 498