1 //! This file declares `VMContext` and several related structs which contain 2 //! fields that compiled wasm code accesses directly. 3 4 mod vm_host_func_context; 5 6 use crate::runtime::vm::{GcStore, VMGcRef}; 7 use sptr::Strict; 8 use std::cell::UnsafeCell; 9 use std::ffi::c_void; 10 use std::marker; 11 use std::mem; 12 use std::ptr::{self, NonNull}; 13 use std::sync::atomic::{AtomicUsize, Ordering}; 14 use std::u32; 15 pub use vm_host_func_context::{VMArrayCallHostFuncContext, VMNativeCallHostFuncContext}; 16 use wasmtime_environ::{ 17 BuiltinFunctionIndex, DefinedMemoryIndex, Unsigned, VMSharedTypeIndex, WasmHeapTopType, 18 WasmValType, VMCONTEXT_MAGIC, 19 }; 20 21 /// A function pointer that exposes the array calling convention. 22 /// 23 /// Regardless of the underlying Wasm function type, all functions using the 24 /// array calling convention have the same Rust signature. 25 /// 26 /// Arguments: 27 /// 28 /// * Callee `vmctx` for the function itself. 29 /// 30 /// * Caller's `vmctx` (so that host functions can access the linear memory of 31 /// their Wasm callers). 32 /// 33 /// * A pointer to a buffer of `ValRaw`s where both arguments are passed into 34 /// this function, and where results are returned from this function. 35 /// 36 /// * The capacity of the `ValRaw` buffer. Must always be at least 37 /// `max(len(wasm_params), len(wasm_results))`. 38 pub type VMArrayCallFunction = 39 unsafe extern "C" fn(*mut VMOpaqueContext, *mut VMOpaqueContext, *mut ValRaw, usize); 40 41 /// A function pointer that exposes the native calling convention. 42 /// 43 /// Different Wasm function types end up mapping to different Rust function 44 /// types, so this isn't simply a type alias the way that `VMArrayCallFunction` 45 /// is. 46 /// 47 /// This is the default calling convention for the target (e.g. System-V or 48 /// fast-call) except multiple return values are handled by returning the first 49 /// return value in a register and everything else through a return-pointer. 50 #[repr(transparent)] 51 pub struct VMNativeCallFunction(VMFunctionBody); 52 53 /// A function pointer that exposes the Wasm calling convention. 54 /// 55 /// In practice, different Wasm function types end up mapping to different Rust 56 /// function types, so this isn't simply a type alias the way that 57 /// `VMArrayCallFunction` is. However, the exact details of the calling 58 /// convention are left to the Wasm compiler (e.g. Cranelift or Winch). Runtime 59 /// code never does anything with these function pointers except shuffle them 60 /// around and pass them back to Wasm. 61 #[repr(transparent)] 62 pub struct VMWasmCallFunction(VMFunctionBody); 63 64 /// An imported function. 65 #[derive(Debug, Copy, Clone)] 66 #[repr(C)] 67 pub struct VMFunctionImport { 68 /// Function pointer to use when calling this imported function from Wasm. 69 pub wasm_call: NonNull<VMWasmCallFunction>, 70 71 /// Function pointer to use when calling this imported function from native code. 72 pub native_call: NonNull<VMNativeCallFunction>, 73 74 /// Function pointer to use when calling this imported function with the 75 /// "array" calling convention that `Func::new` et al use. 76 pub array_call: VMArrayCallFunction, 77 78 /// The VM state associated with this function. 79 /// 80 /// For Wasm functions defined by core wasm instances this will be `*mut 81 /// VMContext`, but for lifted/lowered component model functions this will 82 /// be a `VMComponentContext`, and for a host function it will be a 83 /// `VMHostFuncContext`, etc. 84 pub vmctx: *mut VMOpaqueContext, 85 } 86 87 // Declare that this type is send/sync, it's the responsibility of users of 88 // `VMFunctionImport` to uphold this guarantee. 89 unsafe impl Send for VMFunctionImport {} 90 unsafe impl Sync for VMFunctionImport {} 91 92 #[cfg(test)] 93 mod test_vmfunction_import { 94 use super::VMFunctionImport; 95 use memoffset::offset_of; 96 use std::mem::size_of; 97 use wasmtime_environ::{Module, VMOffsets}; 98 99 #[test] 100 fn check_vmfunction_import_offsets() { 101 let module = Module::new(); 102 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 103 assert_eq!( 104 size_of::<VMFunctionImport>(), 105 usize::from(offsets.size_of_vmfunction_import()) 106 ); 107 assert_eq!( 108 offset_of!(VMFunctionImport, wasm_call), 109 usize::from(offsets.vmfunction_import_wasm_call()) 110 ); 111 assert_eq!( 112 offset_of!(VMFunctionImport, native_call), 113 usize::from(offsets.vmfunction_import_native_call()) 114 ); 115 assert_eq!( 116 offset_of!(VMFunctionImport, array_call), 117 usize::from(offsets.vmfunction_import_array_call()) 118 ); 119 assert_eq!( 120 offset_of!(VMFunctionImport, vmctx), 121 usize::from(offsets.vmfunction_import_vmctx()) 122 ); 123 } 124 } 125 126 /// A placeholder byte-sized type which is just used to provide some amount of type 127 /// safety when dealing with pointers to JIT-compiled function bodies. Note that it's 128 /// deliberately not Copy, as we shouldn't be carelessly copying function body bytes 129 /// around. 130 #[repr(C)] 131 pub struct VMFunctionBody(u8); 132 133 #[cfg(test)] 134 mod test_vmfunction_body { 135 use super::VMFunctionBody; 136 use std::mem::size_of; 137 138 #[test] 139 fn check_vmfunction_body_offsets() { 140 assert_eq!(size_of::<VMFunctionBody>(), 1); 141 } 142 } 143 144 /// The fields compiled code needs to access to utilize a WebAssembly table 145 /// imported from another instance. 146 #[derive(Debug, Copy, Clone)] 147 #[repr(C)] 148 pub struct VMTableImport { 149 /// A pointer to the imported table description. 150 pub from: *mut VMTableDefinition, 151 152 /// A pointer to the `VMContext` that owns the table description. 153 pub vmctx: *mut VMContext, 154 } 155 156 // Declare that this type is send/sync, it's the responsibility of users of 157 // `VMTableImport` to uphold this guarantee. 158 unsafe impl Send for VMTableImport {} 159 unsafe impl Sync for VMTableImport {} 160 161 #[cfg(test)] 162 mod test_vmtable_import { 163 use super::VMTableImport; 164 use memoffset::offset_of; 165 use std::mem::size_of; 166 use wasmtime_environ::{Module, VMOffsets}; 167 168 #[test] 169 fn check_vmtable_import_offsets() { 170 let module = Module::new(); 171 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 172 assert_eq!( 173 size_of::<VMTableImport>(), 174 usize::from(offsets.size_of_vmtable_import()) 175 ); 176 assert_eq!( 177 offset_of!(VMTableImport, from), 178 usize::from(offsets.vmtable_import_from()) 179 ); 180 assert_eq!( 181 offset_of!(VMTableImport, vmctx), 182 usize::from(offsets.vmtable_import_vmctx()) 183 ); 184 } 185 } 186 187 /// The fields compiled code needs to access to utilize a WebAssembly linear 188 /// memory imported from another instance. 189 #[derive(Debug, Copy, Clone)] 190 #[repr(C)] 191 pub struct VMMemoryImport { 192 /// A pointer to the imported memory description. 193 pub from: *mut VMMemoryDefinition, 194 195 /// A pointer to the `VMContext` that owns the memory description. 196 pub vmctx: *mut VMContext, 197 198 /// The index of the memory in the containing `vmctx`. 199 pub index: DefinedMemoryIndex, 200 } 201 202 // Declare that this type is send/sync, it's the responsibility of users of 203 // `VMMemoryImport` to uphold this guarantee. 204 unsafe impl Send for VMMemoryImport {} 205 unsafe impl Sync for VMMemoryImport {} 206 207 #[cfg(test)] 208 mod test_vmmemory_import { 209 use super::VMMemoryImport; 210 use memoffset::offset_of; 211 use std::mem::size_of; 212 use wasmtime_environ::{Module, VMOffsets}; 213 214 #[test] 215 fn check_vmmemory_import_offsets() { 216 let module = Module::new(); 217 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 218 assert_eq!( 219 size_of::<VMMemoryImport>(), 220 usize::from(offsets.size_of_vmmemory_import()) 221 ); 222 assert_eq!( 223 offset_of!(VMMemoryImport, from), 224 usize::from(offsets.vmmemory_import_from()) 225 ); 226 assert_eq!( 227 offset_of!(VMMemoryImport, vmctx), 228 usize::from(offsets.vmmemory_import_vmctx()) 229 ); 230 } 231 } 232 233 /// The fields compiled code needs to access to utilize a WebAssembly global 234 /// variable imported from another instance. 235 /// 236 /// Note that unlike with functions, tables, and memories, `VMGlobalImport` 237 /// doesn't include a `vmctx` pointer. Globals are never resized, and don't 238 /// require a `vmctx` pointer to access. 239 #[derive(Debug, Copy, Clone)] 240 #[repr(C)] 241 pub struct VMGlobalImport { 242 /// A pointer to the imported global variable description. 243 pub from: *mut VMGlobalDefinition, 244 } 245 246 // Declare that this type is send/sync, it's the responsibility of users of 247 // `VMGlobalImport` to uphold this guarantee. 248 unsafe impl Send for VMGlobalImport {} 249 unsafe impl Sync for VMGlobalImport {} 250 251 #[cfg(test)] 252 mod test_vmglobal_import { 253 use super::VMGlobalImport; 254 use memoffset::offset_of; 255 use std::mem::size_of; 256 use wasmtime_environ::{Module, VMOffsets}; 257 258 #[test] 259 fn check_vmglobal_import_offsets() { 260 let module = Module::new(); 261 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 262 assert_eq!( 263 size_of::<VMGlobalImport>(), 264 usize::from(offsets.size_of_vmglobal_import()) 265 ); 266 assert_eq!( 267 offset_of!(VMGlobalImport, from), 268 usize::from(offsets.vmglobal_import_from()) 269 ); 270 } 271 } 272 273 /// The fields compiled code needs to access to utilize a WebAssembly linear 274 /// memory defined within the instance, namely the start address and the 275 /// size in bytes. 276 #[derive(Debug)] 277 #[repr(C)] 278 pub struct VMMemoryDefinition { 279 /// The start address. 280 pub base: *mut u8, 281 282 /// The current logical size of this linear memory in bytes. 283 /// 284 /// This is atomic because shared memories must be able to grow their length 285 /// atomically. For relaxed access, see 286 /// [`VMMemoryDefinition::current_length()`]. 287 pub current_length: AtomicUsize, 288 } 289 290 impl VMMemoryDefinition { 291 /// Return the current length of the [`VMMemoryDefinition`] by performing a 292 /// relaxed load; do not use this function for situations in which a precise 293 /// length is needed. Owned memories (i.e., non-shared) will always return a 294 /// precise result (since no concurrent modification is possible) but shared 295 /// memories may see an imprecise value--a `current_length` potentially 296 /// smaller than what some other thread observes. Since Wasm memory only 297 /// grows, this under-estimation may be acceptable in certain cases. 298 pub fn current_length(&self) -> usize { 299 self.current_length.load(Ordering::Relaxed) 300 } 301 302 /// Return a copy of the [`VMMemoryDefinition`] using the relaxed value of 303 /// `current_length`; see [`VMMemoryDefinition::current_length()`]. 304 pub unsafe fn load(ptr: *mut Self) -> Self { 305 let other = &*ptr; 306 VMMemoryDefinition { 307 base: other.base, 308 current_length: other.current_length().into(), 309 } 310 } 311 } 312 313 #[cfg(test)] 314 mod test_vmmemory_definition { 315 use super::VMMemoryDefinition; 316 use memoffset::offset_of; 317 use std::mem::size_of; 318 use wasmtime_environ::{Module, PtrSize, VMOffsets}; 319 320 #[test] 321 fn check_vmmemory_definition_offsets() { 322 let module = Module::new(); 323 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 324 assert_eq!( 325 size_of::<VMMemoryDefinition>(), 326 usize::from(offsets.ptr.size_of_vmmemory_definition()) 327 ); 328 assert_eq!( 329 offset_of!(VMMemoryDefinition, base), 330 usize::from(offsets.ptr.vmmemory_definition_base()) 331 ); 332 assert_eq!( 333 offset_of!(VMMemoryDefinition, current_length), 334 usize::from(offsets.ptr.vmmemory_definition_current_length()) 335 ); 336 /* TODO: Assert that the size of `current_length` matches. 337 assert_eq!( 338 size_of::<VMMemoryDefinition::current_length>(), 339 usize::from(offsets.size_of_vmmemory_definition_current_length()) 340 ); 341 */ 342 } 343 } 344 345 /// The fields compiled code needs to access to utilize a WebAssembly table 346 /// defined within the instance. 347 #[derive(Debug, Copy, Clone)] 348 #[repr(C)] 349 pub struct VMTableDefinition { 350 /// Pointer to the table data. 351 pub base: *mut u8, 352 353 /// The current number of elements in the table. 354 pub current_elements: u32, 355 } 356 357 #[cfg(test)] 358 mod test_vmtable_definition { 359 use super::VMTableDefinition; 360 use memoffset::offset_of; 361 use std::mem::size_of; 362 use wasmtime_environ::{Module, VMOffsets}; 363 364 #[test] 365 fn check_vmtable_definition_offsets() { 366 let module = Module::new(); 367 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 368 assert_eq!( 369 size_of::<VMTableDefinition>(), 370 usize::from(offsets.size_of_vmtable_definition()) 371 ); 372 assert_eq!( 373 offset_of!(VMTableDefinition, base), 374 usize::from(offsets.vmtable_definition_base()) 375 ); 376 assert_eq!( 377 offset_of!(VMTableDefinition, current_elements), 378 usize::from(offsets.vmtable_definition_current_elements()) 379 ); 380 } 381 } 382 383 /// The storage for a WebAssembly global defined within the instance. 384 /// 385 /// TODO: Pack the globals more densely, rather than using the same size 386 /// for every type. 387 #[derive(Debug)] 388 #[repr(C, align(16))] 389 pub struct VMGlobalDefinition { 390 storage: [u8; 16], 391 // If more elements are added here, remember to add offset_of tests below! 392 } 393 394 #[cfg(test)] 395 mod test_vmglobal_definition { 396 use super::VMGlobalDefinition; 397 use std::mem::{align_of, size_of}; 398 use wasmtime_environ::{Module, PtrSize, VMOffsets}; 399 400 #[test] 401 fn check_vmglobal_definition_alignment() { 402 assert!(align_of::<VMGlobalDefinition>() >= align_of::<i32>()); 403 assert!(align_of::<VMGlobalDefinition>() >= align_of::<i64>()); 404 assert!(align_of::<VMGlobalDefinition>() >= align_of::<f32>()); 405 assert!(align_of::<VMGlobalDefinition>() >= align_of::<f64>()); 406 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[u8; 16]>()); 407 } 408 409 #[test] 410 fn check_vmglobal_definition_offsets() { 411 let module = Module::new(); 412 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 413 assert_eq!( 414 size_of::<VMGlobalDefinition>(), 415 usize::from(offsets.ptr.size_of_vmglobal_definition()) 416 ); 417 } 418 419 #[test] 420 fn check_vmglobal_begins_aligned() { 421 let module = Module::new(); 422 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 423 assert_eq!(offsets.vmctx_globals_begin() % 16, 0); 424 } 425 426 #[test] 427 #[cfg(feature = "gc")] 428 fn check_vmglobal_can_contain_gc_ref() { 429 assert!(size_of::<crate::runtime::vm::VMGcRef>() <= size_of::<VMGlobalDefinition>()); 430 } 431 } 432 433 impl VMGlobalDefinition { 434 /// Construct a `VMGlobalDefinition`. 435 pub fn new() -> Self { 436 Self { storage: [0; 16] } 437 } 438 439 /// Create a `VMGlobalDefinition` from a `ValRaw`. 440 /// 441 /// # Unsafety 442 /// 443 /// This raw value's type must match the given `WasmValType`. 444 pub unsafe fn from_val_raw(wasm_ty: WasmValType, raw: ValRaw) -> Self { 445 let mut global = Self::new(); 446 match wasm_ty { 447 WasmValType::I32 => *global.as_i32_mut() = raw.get_i32(), 448 WasmValType::I64 => *global.as_i64_mut() = raw.get_i64(), 449 WasmValType::F32 => *global.as_f32_bits_mut() = raw.get_f32(), 450 WasmValType::F64 => *global.as_f64_bits_mut() = raw.get_f64(), 451 WasmValType::V128 => *global.as_u128_mut() = raw.get_v128(), 452 WasmValType::Ref(r) => match r.heap_type.top() { 453 WasmHeapTopType::Extern => { 454 global.init_gc_ref(VMGcRef::from_raw_u32(raw.get_externref())) 455 } 456 WasmHeapTopType::Any => global.init_gc_ref(VMGcRef::from_raw_u32(raw.get_anyref())), 457 WasmHeapTopType::Func => *global.as_func_ref_mut() = raw.get_funcref().cast(), 458 }, 459 } 460 global 461 } 462 463 /// Get this global's value as a `ValRaw`. 464 /// 465 /// # Unsafety 466 /// 467 /// This global's value's type must match the given `WasmValType`. 468 pub unsafe fn to_val_raw(&self, gc_store: &mut GcStore, wasm_ty: WasmValType) -> ValRaw { 469 match wasm_ty { 470 WasmValType::I32 => ValRaw::i32(*self.as_i32()), 471 WasmValType::I64 => ValRaw::i64(*self.as_i64()), 472 WasmValType::F32 => ValRaw::f32(*self.as_f32_bits()), 473 WasmValType::F64 => ValRaw::f64(*self.as_f64_bits()), 474 WasmValType::V128 => ValRaw::v128(*self.as_u128()), 475 WasmValType::Ref(r) => match r.heap_type.top() { 476 WasmHeapTopType::Extern => ValRaw::externref( 477 self.as_gc_ref() 478 .map_or(0, |r| gc_store.clone_gc_ref(r).as_raw_u32()), 479 ), 480 WasmHeapTopType::Any => ValRaw::anyref( 481 self.as_gc_ref() 482 .map_or(0, |r| gc_store.clone_gc_ref(r).as_raw_u32()), 483 ), 484 WasmHeapTopType::Func => ValRaw::funcref(self.as_func_ref().cast()), 485 }, 486 } 487 } 488 489 /// Return a reference to the value as an i32. 490 pub unsafe fn as_i32(&self) -> &i32 { 491 &*(self.storage.as_ref().as_ptr().cast::<i32>()) 492 } 493 494 /// Return a mutable reference to the value as an i32. 495 pub unsafe fn as_i32_mut(&mut self) -> &mut i32 { 496 &mut *(self.storage.as_mut().as_mut_ptr().cast::<i32>()) 497 } 498 499 /// Return a reference to the value as a u32. 500 pub unsafe fn as_u32(&self) -> &u32 { 501 &*(self.storage.as_ref().as_ptr().cast::<u32>()) 502 } 503 504 /// Return a mutable reference to the value as an u32. 505 pub unsafe fn as_u32_mut(&mut self) -> &mut u32 { 506 &mut *(self.storage.as_mut().as_mut_ptr().cast::<u32>()) 507 } 508 509 /// Return a reference to the value as an i64. 510 pub unsafe fn as_i64(&self) -> &i64 { 511 &*(self.storage.as_ref().as_ptr().cast::<i64>()) 512 } 513 514 /// Return a mutable reference to the value as an i64. 515 pub unsafe fn as_i64_mut(&mut self) -> &mut i64 { 516 &mut *(self.storage.as_mut().as_mut_ptr().cast::<i64>()) 517 } 518 519 /// Return a reference to the value as an u64. 520 pub unsafe fn as_u64(&self) -> &u64 { 521 &*(self.storage.as_ref().as_ptr().cast::<u64>()) 522 } 523 524 /// Return a mutable reference to the value as an u64. 525 pub unsafe fn as_u64_mut(&mut self) -> &mut u64 { 526 &mut *(self.storage.as_mut().as_mut_ptr().cast::<u64>()) 527 } 528 529 /// Return a reference to the value as an f32. 530 pub unsafe fn as_f32(&self) -> &f32 { 531 &*(self.storage.as_ref().as_ptr().cast::<f32>()) 532 } 533 534 /// Return a mutable reference to the value as an f32. 535 pub unsafe fn as_f32_mut(&mut self) -> &mut f32 { 536 &mut *(self.storage.as_mut().as_mut_ptr().cast::<f32>()) 537 } 538 539 /// Return a reference to the value as f32 bits. 540 pub unsafe fn as_f32_bits(&self) -> &u32 { 541 &*(self.storage.as_ref().as_ptr().cast::<u32>()) 542 } 543 544 /// Return a mutable reference to the value as f32 bits. 545 pub unsafe fn as_f32_bits_mut(&mut self) -> &mut u32 { 546 &mut *(self.storage.as_mut().as_mut_ptr().cast::<u32>()) 547 } 548 549 /// Return a reference to the value as an f64. 550 pub unsafe fn as_f64(&self) -> &f64 { 551 &*(self.storage.as_ref().as_ptr().cast::<f64>()) 552 } 553 554 /// Return a mutable reference to the value as an f64. 555 pub unsafe fn as_f64_mut(&mut self) -> &mut f64 { 556 &mut *(self.storage.as_mut().as_mut_ptr().cast::<f64>()) 557 } 558 559 /// Return a reference to the value as f64 bits. 560 pub unsafe fn as_f64_bits(&self) -> &u64 { 561 &*(self.storage.as_ref().as_ptr().cast::<u64>()) 562 } 563 564 /// Return a mutable reference to the value as f64 bits. 565 pub unsafe fn as_f64_bits_mut(&mut self) -> &mut u64 { 566 &mut *(self.storage.as_mut().as_mut_ptr().cast::<u64>()) 567 } 568 569 /// Return a reference to the value as an u128. 570 pub unsafe fn as_u128(&self) -> &u128 { 571 &*(self.storage.as_ref().as_ptr().cast::<u128>()) 572 } 573 574 /// Return a mutable reference to the value as an u128. 575 pub unsafe fn as_u128_mut(&mut self) -> &mut u128 { 576 &mut *(self.storage.as_mut().as_mut_ptr().cast::<u128>()) 577 } 578 579 /// Return a reference to the value as u128 bits. 580 pub unsafe fn as_u128_bits(&self) -> &[u8; 16] { 581 &*(self.storage.as_ref().as_ptr().cast::<[u8; 16]>()) 582 } 583 584 /// Return a mutable reference to the value as u128 bits. 585 pub unsafe fn as_u128_bits_mut(&mut self) -> &mut [u8; 16] { 586 &mut *(self.storage.as_mut().as_mut_ptr().cast::<[u8; 16]>()) 587 } 588 589 /// Return a reference to the global value as a borrowed GC reference. 590 pub unsafe fn as_gc_ref(&self) -> Option<&VMGcRef> { 591 let raw_ptr = self.storage.as_ref().as_ptr().cast::<Option<VMGcRef>>(); 592 let ret = (*raw_ptr).as_ref(); 593 assert!(cfg!(feature = "gc") || ret.is_none()); 594 ret 595 } 596 597 /// Initialize a global to the given GC reference. 598 pub unsafe fn init_gc_ref(&mut self, gc_ref: Option<VMGcRef>) { 599 assert!(cfg!(feature = "gc") || gc_ref.is_none()); 600 let raw_ptr = self.storage.as_mut().as_mut_ptr().cast::<Option<VMGcRef>>(); 601 ptr::write(raw_ptr, gc_ref); 602 } 603 604 /// Write a GC reference into this global value. 605 pub unsafe fn write_gc_ref(&mut self, gc_store: &mut GcStore, gc_ref: Option<&VMGcRef>) { 606 assert!(cfg!(feature = "gc") || gc_ref.is_none()); 607 608 let dest = &mut *(self.storage.as_mut().as_mut_ptr().cast::<Option<VMGcRef>>()); 609 assert!(cfg!(feature = "gc") || dest.is_none()); 610 611 gc_store.write_gc_ref(dest, gc_ref) 612 } 613 614 /// Return a reference to the value as a `VMFuncRef`. 615 pub unsafe fn as_func_ref(&self) -> *mut VMFuncRef { 616 *(self.storage.as_ref().as_ptr().cast::<*mut VMFuncRef>()) 617 } 618 619 /// Return a mutable reference to the value as a `VMFuncRef`. 620 pub unsafe fn as_func_ref_mut(&mut self) -> &mut *mut VMFuncRef { 621 &mut *(self.storage.as_mut().as_mut_ptr().cast::<*mut VMFuncRef>()) 622 } 623 } 624 625 #[cfg(test)] 626 mod test_vmshared_type_index { 627 use super::VMSharedTypeIndex; 628 use std::mem::size_of; 629 use wasmtime_environ::{Module, VMOffsets}; 630 631 #[test] 632 fn check_vmshared_type_index() { 633 let module = Module::new(); 634 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 635 assert_eq!( 636 size_of::<VMSharedTypeIndex>(), 637 usize::from(offsets.size_of_vmshared_type_index()) 638 ); 639 } 640 } 641 642 /// The VM caller-checked "funcref" record, for caller-side signature checking. 643 /// 644 /// It consists of function pointer(s), a type id to be checked by the 645 /// caller, and the vmctx closure associated with this function. 646 #[derive(Debug, Clone)] 647 #[repr(C)] 648 pub struct VMFuncRef { 649 /// Function pointer for this funcref if being called via the native calling 650 /// convention. 651 pub native_call: NonNull<VMNativeCallFunction>, 652 653 /// Function pointer for this funcref if being called via the "array" 654 /// calling convention that `Func::new` et al use. 655 pub array_call: VMArrayCallFunction, 656 657 /// Function pointer for this funcref if being called via the calling 658 /// convention we use when compiling Wasm. 659 /// 660 /// Most functions come with a function pointer that we can use when they 661 /// are called from Wasm. The notable exception is when we `Func::wrap` a 662 /// host function, and we don't have a Wasm compiler on hand to compile a 663 /// Wasm-to-native trampoline for the function. In this case, we leave 664 /// `wasm_call` empty until the function is passed as an import to Wasm (or 665 /// otherwise exposed to Wasm via tables/globals). At this point, we look up 666 /// a Wasm-to-native trampoline for the function in the the Wasm's compiled 667 /// module and use that fill in `VMFunctionImport::wasm_call`. **However** 668 /// there is no guarantee that the Wasm module has a trampoline for this 669 /// function's signature. The Wasm module only has trampolines for its 670 /// types, and if this function isn't of one of those types, then the Wasm 671 /// module will not have a trampoline for it. This is actually okay, because 672 /// it means that the Wasm cannot actually call this function. But it does 673 /// mean that this field needs to be an `Option` even though it is non-null 674 /// the vast vast vast majority of the time. 675 pub wasm_call: Option<NonNull<VMWasmCallFunction>>, 676 677 /// Function signature's type id. 678 pub type_index: VMSharedTypeIndex, 679 680 /// The VM state associated with this function. 681 /// 682 /// The actual definition of what this pointer points to depends on the 683 /// function being referenced: for core Wasm functions, this is a `*mut 684 /// VMContext`, for host functions it is a `*mut VMHostFuncContext`, and for 685 /// component functions it is a `*mut VMComponentContext`. 686 pub vmctx: *mut VMOpaqueContext, 687 // If more elements are added here, remember to add offset_of tests below! 688 } 689 690 unsafe impl Send for VMFuncRef {} 691 unsafe impl Sync for VMFuncRef {} 692 693 #[cfg(test)] 694 mod test_vm_func_ref { 695 use super::VMFuncRef; 696 use memoffset::offset_of; 697 use std::mem::size_of; 698 use wasmtime_environ::{Module, PtrSize, VMOffsets}; 699 700 #[test] 701 fn check_vm_func_ref_offsets() { 702 let module = Module::new(); 703 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 704 assert_eq!( 705 size_of::<VMFuncRef>(), 706 usize::from(offsets.ptr.size_of_vm_func_ref()) 707 ); 708 assert_eq!( 709 offset_of!(VMFuncRef, native_call), 710 usize::from(offsets.ptr.vm_func_ref_native_call()) 711 ); 712 assert_eq!( 713 offset_of!(VMFuncRef, array_call), 714 usize::from(offsets.ptr.vm_func_ref_array_call()) 715 ); 716 assert_eq!( 717 offset_of!(VMFuncRef, wasm_call), 718 usize::from(offsets.ptr.vm_func_ref_wasm_call()) 719 ); 720 assert_eq!( 721 offset_of!(VMFuncRef, type_index), 722 usize::from(offsets.ptr.vm_func_ref_type_index()) 723 ); 724 assert_eq!( 725 offset_of!(VMFuncRef, vmctx), 726 usize::from(offsets.ptr.vm_func_ref_vmctx()) 727 ); 728 } 729 } 730 731 macro_rules! define_builtin_array { 732 ( 733 $( 734 $( #[$attr:meta] )* 735 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?; 736 )* 737 ) => { 738 /// An array that stores addresses of builtin functions. We translate code 739 /// to use indirect calls. This way, we don't have to patch the code. 740 #[repr(C)] 741 pub struct VMBuiltinFunctionsArray { 742 $( 743 $name: unsafe extern "C" fn( 744 $(define_builtin_array!(@ty $param)),* 745 ) $( -> define_builtin_array!(@ty $result))?, 746 )* 747 } 748 749 impl VMBuiltinFunctionsArray { 750 #[allow(unused_doc_comments)] 751 pub const INIT: VMBuiltinFunctionsArray = VMBuiltinFunctionsArray { 752 $( 753 $name: crate::runtime::vm::libcalls::raw::$name, 754 )* 755 }; 756 } 757 }; 758 759 (@ty i32) => (u32); 760 (@ty i64) => (u64); 761 (@ty reference) => (*mut u8); 762 (@ty pointer) => (*mut u8); 763 (@ty vmctx) => (*mut VMContext); 764 } 765 766 wasmtime_environ::foreach_builtin_function!(define_builtin_array); 767 768 const _: () = { 769 assert!( 770 mem::size_of::<VMBuiltinFunctionsArray>() 771 == mem::size_of::<usize>() 772 * (BuiltinFunctionIndex::builtin_functions_total_number() as usize) 773 ) 774 }; 775 776 /// Structure used to control interrupting wasm code. 777 #[derive(Debug)] 778 #[repr(C)] 779 pub struct VMRuntimeLimits { 780 /// Current stack limit of the wasm module. 781 /// 782 /// For more information see `crates/cranelift/src/lib.rs`. 783 pub stack_limit: UnsafeCell<usize>, 784 785 /// Indicator of how much fuel has been consumed and is remaining to 786 /// WebAssembly. 787 /// 788 /// This field is typically negative and increments towards positive. Upon 789 /// turning positive a wasm trap will be generated. This field is only 790 /// modified if wasm is configured to consume fuel. 791 pub fuel_consumed: UnsafeCell<i64>, 792 793 /// Deadline epoch for interruption: if epoch-based interruption 794 /// is enabled and the global (per engine) epoch counter is 795 /// observed to reach or exceed this value, the guest code will 796 /// yield if running asynchronously. 797 pub epoch_deadline: UnsafeCell<u64>, 798 799 /// The value of the frame pointer register when we last called from Wasm to 800 /// the host. 801 /// 802 /// Maintained by our Wasm-to-host trampoline, and cleared just before 803 /// calling into Wasm in `catch_traps`. 804 /// 805 /// This member is `0` when Wasm is actively running and has not called out 806 /// to the host. 807 /// 808 /// Used to find the start of a a contiguous sequence of Wasm frames when 809 /// walking the stack. 810 pub last_wasm_exit_fp: UnsafeCell<usize>, 811 812 /// The last Wasm program counter before we called from Wasm to the host. 813 /// 814 /// Maintained by our Wasm-to-host trampoline, and cleared just before 815 /// calling into Wasm in `catch_traps`. 816 /// 817 /// This member is `0` when Wasm is actively running and has not called out 818 /// to the host. 819 /// 820 /// Used when walking a contiguous sequence of Wasm frames. 821 pub last_wasm_exit_pc: UnsafeCell<usize>, 822 823 /// The last host stack pointer before we called into Wasm from the host. 824 /// 825 /// Maintained by our host-to-Wasm trampoline, and cleared just before 826 /// calling into Wasm in `catch_traps`. 827 /// 828 /// This member is `0` when Wasm is actively running and has not called out 829 /// to the host. 830 /// 831 /// When a host function is wrapped into a `wasmtime::Func`, and is then 832 /// called from the host, then this member has the sentinal value of `-1 as 833 /// usize`, meaning that this contiguous sequence of Wasm frames is the 834 /// empty sequence, and it is not safe to dereference the 835 /// `last_wasm_exit_fp`. 836 /// 837 /// Used to find the end of a contiguous sequence of Wasm frames when 838 /// walking the stack. 839 pub last_wasm_entry_sp: UnsafeCell<usize>, 840 } 841 842 // The `VMRuntimeLimits` type is a pod-type with no destructor, and we don't 843 // access any fields from other threads, so add in these trait impls which are 844 // otherwise not available due to the `fuel_consumed` and `epoch_deadline` 845 // variables in `VMRuntimeLimits`. 846 unsafe impl Send for VMRuntimeLimits {} 847 unsafe impl Sync for VMRuntimeLimits {} 848 849 impl Default for VMRuntimeLimits { 850 fn default() -> VMRuntimeLimits { 851 VMRuntimeLimits { 852 stack_limit: UnsafeCell::new(usize::max_value()), 853 fuel_consumed: UnsafeCell::new(0), 854 epoch_deadline: UnsafeCell::new(0), 855 last_wasm_exit_fp: UnsafeCell::new(0), 856 last_wasm_exit_pc: UnsafeCell::new(0), 857 last_wasm_entry_sp: UnsafeCell::new(0), 858 } 859 } 860 } 861 862 #[cfg(test)] 863 mod test_vmruntime_limits { 864 use super::VMRuntimeLimits; 865 use memoffset::offset_of; 866 use std::mem::size_of; 867 use wasmtime_environ::{Module, PtrSize, VMOffsets}; 868 869 #[test] 870 fn vmctx_runtime_limits_offset() { 871 let module = Module::new(); 872 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 873 assert_eq!( 874 offsets.vmctx_runtime_limits(), 875 u32::from(offsets.ptr.vmcontext_runtime_limits()) 876 ); 877 } 878 879 #[test] 880 fn field_offsets() { 881 let module = Module::new(); 882 let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); 883 assert_eq!( 884 offset_of!(VMRuntimeLimits, stack_limit), 885 usize::from(offsets.ptr.vmruntime_limits_stack_limit()) 886 ); 887 assert_eq!( 888 offset_of!(VMRuntimeLimits, fuel_consumed), 889 usize::from(offsets.ptr.vmruntime_limits_fuel_consumed()) 890 ); 891 assert_eq!( 892 offset_of!(VMRuntimeLimits, epoch_deadline), 893 usize::from(offsets.ptr.vmruntime_limits_epoch_deadline()) 894 ); 895 assert_eq!( 896 offset_of!(VMRuntimeLimits, last_wasm_exit_fp), 897 usize::from(offsets.ptr.vmruntime_limits_last_wasm_exit_fp()) 898 ); 899 assert_eq!( 900 offset_of!(VMRuntimeLimits, last_wasm_exit_pc), 901 usize::from(offsets.ptr.vmruntime_limits_last_wasm_exit_pc()) 902 ); 903 assert_eq!( 904 offset_of!(VMRuntimeLimits, last_wasm_entry_sp), 905 usize::from(offsets.ptr.vmruntime_limits_last_wasm_entry_sp()) 906 ); 907 } 908 } 909 910 /// The VM "context", which is pointed to by the `vmctx` arg in Cranelift. 911 /// This has information about globals, memories, tables, and other runtime 912 /// state associated with the current instance. 913 /// 914 /// The struct here is empty, as the sizes of these fields are dynamic, and 915 /// we can't describe them in Rust's type system. Sufficient memory is 916 /// allocated at runtime. 917 #[derive(Debug)] 918 #[repr(C, align(16))] // align 16 since globals are aligned to that and contained inside 919 pub struct VMContext { 920 /// There's some more discussion about this within `wasmtime/src/lib.rs` but 921 /// the idea is that we want to tell the compiler that this contains 922 /// pointers which transitively refers to itself, to suppress some 923 /// optimizations that might otherwise assume this doesn't exist. 924 /// 925 /// The self-referential pointer we care about is the `*mut Store` pointer 926 /// early on in this context, which if you follow through enough levels of 927 /// nesting, eventually can refer back to this `VMContext` 928 pub _marker: marker::PhantomPinned, 929 } 930 931 impl VMContext { 932 /// Helper function to cast between context types using a debug assertion to 933 /// protect against some mistakes. 934 #[inline] 935 pub unsafe fn from_opaque(opaque: *mut VMOpaqueContext) -> *mut VMContext { 936 // Note that in general the offset of the "magic" field is stored in 937 // `VMOffsets::vmctx_magic`. Given though that this is a sanity check 938 // about converting this pointer to another type we ideally don't want 939 // to read the offset from potentially corrupt memory. Instead it would 940 // be better to catch errors here as soon as possible. 941 // 942 // To accomplish this the `VMContext` structure is laid out with the 943 // magic field at a statically known offset (here it's 0 for now). This 944 // static offset is asserted in `VMOffsets::from` and needs to be kept 945 // in sync with this line for this debug assertion to work. 946 // 947 // Also note that this magic is only ever invalid in the presence of 948 // bugs, meaning we don't actually read the magic and act differently 949 // at runtime depending what it is, so this is a debug assertion as 950 // opposed to a regular assertion. 951 debug_assert_eq!((*opaque).magic, VMCONTEXT_MAGIC); 952 opaque.cast() 953 } 954 } 955 956 /// A "raw" and unsafe representation of a WebAssembly value. 957 /// 958 /// This is provided for use with the `Func::new_unchecked` and 959 /// `Func::call_unchecked` APIs. In general it's unlikely you should be using 960 /// this from Rust, rather using APIs like `Func::wrap` and `TypedFunc::call`. 961 /// 962 /// This is notably an "unsafe" way to work with `Val` and it's recommended to 963 /// instead use `Val` where possible. An important note about this union is that 964 /// fields are all stored in little-endian format, regardless of the endianness 965 /// of the host system. 966 #[allow(missing_docs)] 967 #[repr(C)] 968 #[derive(Copy, Clone)] 969 pub union ValRaw { 970 /// A WebAssembly `i32` value. 971 /// 972 /// Note that the payload here is a Rust `i32` but the WebAssembly `i32` 973 /// type does not assign an interpretation of the upper bit as either signed 974 /// or unsigned. The Rust type `i32` is simply chosen for convenience. 975 /// 976 /// This value is always stored in a little-endian format. 977 i32: i32, 978 979 /// A WebAssembly `i64` value. 980 /// 981 /// Note that the payload here is a Rust `i64` but the WebAssembly `i64` 982 /// type does not assign an interpretation of the upper bit as either signed 983 /// or unsigned. The Rust type `i64` is simply chosen for convenience. 984 /// 985 /// This value is always stored in a little-endian format. 986 i64: i64, 987 988 /// A WebAssembly `f32` value. 989 /// 990 /// Note that the payload here is a Rust `u32`. This is to allow passing any 991 /// representation of NaN into WebAssembly without risk of changing NaN 992 /// payload bits as its gets passed around the system. Otherwise though this 993 /// `u32` value is the return value of `f32::to_bits` in Rust. 994 /// 995 /// This value is always stored in a little-endian format. 996 f32: u32, 997 998 /// A WebAssembly `f64` value. 999 /// 1000 /// Note that the payload here is a Rust `u64`. This is to allow passing any 1001 /// representation of NaN into WebAssembly without risk of changing NaN 1002 /// payload bits as its gets passed around the system. Otherwise though this 1003 /// `u64` value is the return value of `f64::to_bits` in Rust. 1004 /// 1005 /// This value is always stored in a little-endian format. 1006 f64: u64, 1007 1008 /// A WebAssembly `v128` value. 1009 /// 1010 /// The payload here is a Rust `[u8; 16]` which has the same number of bits 1011 /// but note that `v128` in WebAssembly is often considered a vector type 1012 /// such as `i32x4` or `f64x2`. This means that the actual interpretation 1013 /// of the underlying bits is left up to the instructions which consume 1014 /// this value. 1015 /// 1016 /// This value is always stored in a little-endian format. 1017 v128: [u8; 16], 1018 1019 /// A WebAssembly `funcref` value (or one of its subtypes). 1020 /// 1021 /// The payload here is a pointer which is runtime-defined. This is one of 1022 /// the main points of unsafety about the `ValRaw` type as the validity of 1023 /// the pointer here is not easily verified and must be preserved by 1024 /// carefully calling the correct functions throughout the runtime. 1025 /// 1026 /// This value is always stored in a little-endian format. 1027 funcref: *mut c_void, 1028 1029 /// A WebAssembly `externref` value (or one of its subtypes). 1030 /// 1031 /// The payload here is a compressed pointer value which is 1032 /// runtime-defined. This is one of the main points of unsafety about the 1033 /// `ValRaw` type as the validity of the pointer here is not easily verified 1034 /// and must be preserved by carefully calling the correct functions 1035 /// throughout the runtime. 1036 /// 1037 /// This value is always stored in a little-endian format. 1038 externref: u32, 1039 1040 /// A WebAssembly `anyref` value (or one of its subtypes). 1041 /// 1042 /// The payload here is a compressed pointer value which is 1043 /// runtime-defined. This is one of the main points of unsafety about the 1044 /// `ValRaw` type as the validity of the pointer here is not easily verified 1045 /// and must be preserved by carefully calling the correct functions 1046 /// throughout the runtime. 1047 /// 1048 /// This value is always stored in a little-endian format. 1049 anyref: u32, 1050 } 1051 1052 // The `ValRaw` type is matched as `wasmtime_val_raw_t` in the C API so these 1053 // are some simple assertions about the shape of the type which are additionally 1054 // matched in C. 1055 const _: () = { 1056 assert!(std::mem::size_of::<ValRaw>() == 16); 1057 assert!(std::mem::align_of::<ValRaw>() == 8); 1058 }; 1059 1060 // This type is just a bag-of-bits so it's up to the caller to figure out how 1061 // to safely deal with threading concerns and safely access interior bits. 1062 unsafe impl Send for ValRaw {} 1063 unsafe impl Sync for ValRaw {} 1064 1065 impl std::fmt::Debug for ValRaw { 1066 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 1067 struct Hex<T>(T); 1068 impl<T: std::fmt::LowerHex> std::fmt::Debug for Hex<T> { 1069 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 1070 let bytes = std::mem::size_of::<T>(); 1071 let hex_digits_per_byte = 2; 1072 let hex_digits = bytes * hex_digits_per_byte; 1073 write!(f, "0x{:0width$x}", self.0, width = hex_digits) 1074 } 1075 } 1076 1077 unsafe { 1078 f.debug_struct("ValRaw") 1079 .field("i32", &Hex(self.i32)) 1080 .field("i64", &Hex(self.i64)) 1081 .field("f32", &Hex(self.f32)) 1082 .field("f64", &Hex(self.f64)) 1083 .field("v128", &Hex(u128::from_le_bytes(self.v128))) 1084 .field("funcref", &self.funcref) 1085 .field("externref", &Hex(self.externref)) 1086 .field("anyref", &Hex(self.anyref)) 1087 .finish() 1088 } 1089 } 1090 } 1091 1092 impl ValRaw { 1093 /// Create a null reference that is compatible with any of 1094 /// `{any,extern,func}ref`. 1095 pub fn null() -> ValRaw { 1096 unsafe { 1097 let raw = mem::MaybeUninit::<Self>::zeroed().assume_init(); 1098 debug_assert_eq!(raw.get_anyref(), 0); 1099 debug_assert_eq!(raw.get_externref(), 0); 1100 debug_assert_eq!(raw.get_funcref(), ptr::null_mut()); 1101 raw 1102 } 1103 } 1104 1105 /// Creates a WebAssembly `i32` value 1106 #[inline] 1107 pub fn i32(i: i32) -> ValRaw { 1108 // Note that this is intentionally not setting the `i32` field, instead 1109 // setting the `i64` field with a zero-extended version of `i`. For more 1110 // information on this see the comments on `Lower for Result` in the 1111 // `wasmtime` crate. Otherwise though all `ValRaw` constructors are 1112 // otherwise constrained to guarantee that the initial 64-bits are 1113 // always initialized. 1114 ValRaw::u64(i.unsigned().into()) 1115 } 1116 1117 /// Creates a WebAssembly `i64` value 1118 #[inline] 1119 pub fn i64(i: i64) -> ValRaw { 1120 ValRaw { i64: i.to_le() } 1121 } 1122 1123 /// Creates a WebAssembly `i32` value 1124 #[inline] 1125 pub fn u32(i: u32) -> ValRaw { 1126 // See comments in `ValRaw::i32` for why this is setting the upper 1127 // 32-bits as well. 1128 ValRaw::u64(i.into()) 1129 } 1130 1131 /// Creates a WebAssembly `i64` value 1132 #[inline] 1133 pub fn u64(i: u64) -> ValRaw { 1134 ValRaw::i64(i as i64) 1135 } 1136 1137 /// Creates a WebAssembly `f32` value 1138 #[inline] 1139 pub fn f32(i: u32) -> ValRaw { 1140 // See comments in `ValRaw::i32` for why this is setting the upper 1141 // 32-bits as well. 1142 ValRaw::u64(i.into()) 1143 } 1144 1145 /// Creates a WebAssembly `f64` value 1146 #[inline] 1147 pub fn f64(i: u64) -> ValRaw { 1148 ValRaw { f64: i.to_le() } 1149 } 1150 1151 /// Creates a WebAssembly `v128` value 1152 #[inline] 1153 pub fn v128(i: u128) -> ValRaw { 1154 ValRaw { 1155 v128: i.to_le_bytes(), 1156 } 1157 } 1158 1159 /// Creates a WebAssembly `funcref` value 1160 #[inline] 1161 pub fn funcref(i: *mut c_void) -> ValRaw { 1162 ValRaw { 1163 funcref: Strict::map_addr(i, |i| i.to_le()), 1164 } 1165 } 1166 1167 /// Creates a WebAssembly `externref` value 1168 #[inline] 1169 pub fn externref(e: u32) -> ValRaw { 1170 assert!(cfg!(feature = "gc") || e == 0); 1171 ValRaw { 1172 externref: e.to_le(), 1173 } 1174 } 1175 1176 /// Creates a WebAssembly `anyref` value 1177 #[inline] 1178 pub fn anyref(r: u32) -> ValRaw { 1179 assert!(cfg!(feature = "gc") || r == 0); 1180 ValRaw { anyref: r.to_le() } 1181 } 1182 1183 /// Gets the WebAssembly `i32` value 1184 #[inline] 1185 pub fn get_i32(&self) -> i32 { 1186 unsafe { i32::from_le(self.i32) } 1187 } 1188 1189 /// Gets the WebAssembly `i64` value 1190 #[inline] 1191 pub fn get_i64(&self) -> i64 { 1192 unsafe { i64::from_le(self.i64) } 1193 } 1194 1195 /// Gets the WebAssembly `i32` value 1196 #[inline] 1197 pub fn get_u32(&self) -> u32 { 1198 self.get_i32().unsigned() 1199 } 1200 1201 /// Gets the WebAssembly `i64` value 1202 #[inline] 1203 pub fn get_u64(&self) -> u64 { 1204 self.get_i64().unsigned() 1205 } 1206 1207 /// Gets the WebAssembly `f32` value 1208 #[inline] 1209 pub fn get_f32(&self) -> u32 { 1210 unsafe { u32::from_le(self.f32) } 1211 } 1212 1213 /// Gets the WebAssembly `f64` value 1214 #[inline] 1215 pub fn get_f64(&self) -> u64 { 1216 unsafe { u64::from_le(self.f64) } 1217 } 1218 1219 /// Gets the WebAssembly `v128` value 1220 #[inline] 1221 pub fn get_v128(&self) -> u128 { 1222 unsafe { u128::from_le_bytes(self.v128) } 1223 } 1224 1225 /// Gets the WebAssembly `funcref` value 1226 #[inline] 1227 pub fn get_funcref(&self) -> *mut c_void { 1228 unsafe { Strict::map_addr(self.funcref, |i| usize::from_le(i)) } 1229 } 1230 1231 /// Gets the WebAssembly `externref` value 1232 #[inline] 1233 pub fn get_externref(&self) -> u32 { 1234 let externref = u32::from_le(unsafe { self.externref }); 1235 assert!(cfg!(feature = "gc") || externref == 0); 1236 externref 1237 } 1238 1239 /// Gets the WebAssembly `anyref` value 1240 #[inline] 1241 pub fn get_anyref(&self) -> u32 { 1242 let anyref = u32::from_le(unsafe { self.anyref }); 1243 assert!(cfg!(feature = "gc") || anyref == 0); 1244 anyref 1245 } 1246 } 1247 1248 /// An "opaque" version of `VMContext` which must be explicitly casted to a 1249 /// target context. 1250 /// 1251 /// This context is used to represent that contexts specified in 1252 /// `VMFuncRef` can have any type and don't have an implicit 1253 /// structure. Neither wasmtime nor cranelift-generated code can rely on the 1254 /// structure of an opaque context in general and only the code which configured 1255 /// the context is able to rely on a particular structure. This is because the 1256 /// context pointer configured for `VMFuncRef` is guaranteed to be 1257 /// the first parameter passed. 1258 /// 1259 /// Note that Wasmtime currently has a layout where all contexts that are casted 1260 /// to an opaque context start with a 32-bit "magic" which can be used in debug 1261 /// mode to debug-assert that the casts here are correct and have at least a 1262 /// little protection against incorrect casts. 1263 pub struct VMOpaqueContext { 1264 pub(crate) magic: u32, 1265 _marker: marker::PhantomPinned, 1266 } 1267 1268 impl VMOpaqueContext { 1269 /// Helper function to clearly indicate that casts are desired. 1270 #[inline] 1271 pub fn from_vmcontext(ptr: *mut VMContext) -> *mut VMOpaqueContext { 1272 ptr.cast() 1273 } 1274 1275 /// Helper function to clearly indicate that casts are desired. 1276 #[inline] 1277 pub fn from_vm_array_call_host_func_context( 1278 ptr: *mut VMArrayCallHostFuncContext, 1279 ) -> *mut VMOpaqueContext { 1280 ptr.cast() 1281 } 1282 1283 /// Helper function to clearly indicate that casts are desired. 1284 #[inline] 1285 pub fn from_vm_native_call_host_func_context( 1286 ptr: *mut VMNativeCallHostFuncContext, 1287 ) -> *mut VMOpaqueContext { 1288 ptr.cast() 1289 } 1290 } 1291