1 use super::invoke_wasm_and_catch_traps; 2 use crate::prelude::*; 3 use crate::runtime::vm::VMFuncRef; 4 use crate::store::{AutoAssertNoGc, StoreOpaque}; 5 use crate::{ 6 AsContext, AsContextMut, Engine, Func, FuncType, HeapType, NoFunc, RefType, StoreContextMut, 7 ValRaw, ValType, 8 }; 9 use core::ffi::c_void; 10 use core::marker; 11 use core::mem::{self, MaybeUninit}; 12 use core::ptr::{self, NonNull}; 13 use wasmtime_environ::VMSharedTypeIndex; 14 15 /// A statically typed WebAssembly function. 16 /// 17 /// Values of this type represent statically type-checked WebAssembly functions. 18 /// The function within a [`TypedFunc`] is statically known to have `Params` as its 19 /// parameters and `Results` as its results. 20 /// 21 /// This structure is created via [`Func::typed`] or [`TypedFunc::new_unchecked`]. 22 /// For more documentation about this see those methods. 23 pub struct TypedFunc<Params, Results> { 24 _a: marker::PhantomData<fn(Params) -> Results>, 25 ty: FuncType, 26 func: Func, 27 } 28 29 impl<Params, Results> Clone for TypedFunc<Params, Results> { clone(&self) -> TypedFunc<Params, Results>30 fn clone(&self) -> TypedFunc<Params, Results> { 31 Self { 32 _a: marker::PhantomData, 33 ty: self.ty.clone(), 34 func: self.func, 35 } 36 } 37 } 38 39 impl<Params, Results> TypedFunc<Params, Results> 40 where 41 Params: WasmParams, 42 Results: WasmResults, 43 { 44 /// An unchecked version of [`Func::typed`] which does not perform a 45 /// typecheck and simply assumes that the type declared here matches the 46 /// type of this function. 47 /// 48 /// The semantics of this function are the same as [`Func::typed`] except 49 /// that no error is returned because no typechecking is done. 50 /// 51 /// # Unsafety 52 /// 53 /// This function only safe to call if `typed` would otherwise return `Ok` 54 /// for the same `Params` and `Results` specified. If `typed` would return 55 /// an error then the returned `TypedFunc` is memory unsafe to invoke. new_unchecked(store: impl AsContext, func: Func) -> TypedFunc<Params, Results>56 pub unsafe fn new_unchecked(store: impl AsContext, func: Func) -> TypedFunc<Params, Results> { 57 let store = store.as_context().0; 58 unsafe { Self::_new_unchecked(store, func) } 59 } 60 _new_unchecked( store: &StoreOpaque, func: Func, ) -> TypedFunc<Params, Results>61 pub(crate) unsafe fn _new_unchecked( 62 store: &StoreOpaque, 63 func: Func, 64 ) -> TypedFunc<Params, Results> { 65 let ty = func.load_ty(store); 66 TypedFunc { 67 _a: marker::PhantomData, 68 ty, 69 func, 70 } 71 } 72 73 /// Returns the underlying [`Func`] that this is wrapping, losing the static 74 /// type information in the process. func(&self) -> &Func75 pub fn func(&self) -> &Func { 76 &self.func 77 } 78 79 /// Invokes this WebAssembly function with the specified parameters. 80 /// 81 /// Returns either the results of the call, or a [`Trap`] if one happened. 82 /// 83 /// For more information, see the [`Func::typed`] and [`Func::call`] 84 /// documentation. 85 /// 86 /// # Errors 87 /// 88 /// For more information on errors see the documentation on [`Func::call`]. 89 /// 90 /// # Panics 91 /// 92 /// Panics if `store` does not contain this function. 93 /// 94 /// [`Trap`]: crate::Trap 95 #[inline] call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results>96 pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results> { 97 let mut store = store.as_context_mut(); 98 store.0.validate_sync_call()?; 99 let func = self.func.vm_func_ref(store.0); 100 unsafe { Self::call_raw(&mut store, &self.ty, func, params) } 101 } 102 103 /// Invokes this WebAssembly function with the specified parameters. 104 /// 105 /// Returns either the results of the call, or a [`Trap`] if one happened. 106 /// 107 /// For more information, see the [`Func::typed`] and [`Func::call_async`] 108 /// documentation. 109 /// 110 /// # Errors 111 /// 112 /// For more information on errors see the documentation on [`Func::call`]. 113 /// 114 /// # Panics 115 /// 116 /// This function will panic if it is called when the underlying [`Func`] is 117 /// connected to a synchronous store. 118 /// 119 /// [`Trap`]: crate::Trap 120 #[cfg(feature = "async")] call_async( &self, mut store: impl AsContextMut<Data: Send>, params: Params, ) -> Result<Results> where Params: Sync, Results: Sync,121 pub async fn call_async( 122 &self, 123 mut store: impl AsContextMut<Data: Send>, 124 params: Params, 125 ) -> Result<Results> 126 where 127 Params: Sync, 128 Results: Sync, 129 { 130 let mut store = store.as_context_mut(); 131 132 store 133 .on_fiber(|store| { 134 let func = self.func.vm_func_ref(store.0); 135 unsafe { Self::call_raw(store, &self.ty, func, params) } 136 }) 137 .await? 138 } 139 140 /// Do a raw call of a typed function. 141 /// 142 /// # Safety 143 /// 144 /// `func` must be of the given type, and it additionally must be a valid 145 /// store-owned pointer within the `store` provided. call_raw<T>( store: &mut StoreContextMut<'_, T>, ty: &FuncType, func: ptr::NonNull<VMFuncRef>, params: Params, ) -> Result<Results>146 pub(crate) unsafe fn call_raw<T>( 147 store: &mut StoreContextMut<'_, T>, 148 ty: &FuncType, 149 func: ptr::NonNull<VMFuncRef>, 150 params: Params, 151 ) -> Result<Results> { 152 // double-check that params/results match for this function's type in 153 // debug mode. 154 // 155 // SAFETY: this function requires that `ptr` is a valid function 156 // pointer. 157 unsafe { 158 if cfg!(debug_assertions) { 159 Self::debug_typecheck(store.0, func.as_ref().type_index); 160 } 161 } 162 163 // Validate that all runtime values flowing into this store indeed 164 // belong within this store, otherwise it would be unsafe for store 165 // values to cross each other. 166 167 union Storage<T: Copy, U: Copy> { 168 params: MaybeUninit<T>, 169 results: U, 170 } 171 172 let mut storage = Storage::<Params::ValRawStorage, Results::ValRawStorage> { 173 params: MaybeUninit::uninit(), 174 }; 175 176 { 177 let mut store = AutoAssertNoGc::new(store.0); 178 // SAFETY: it's safe to use a union field here as the field itself 179 // is `MaybeUninit<_>` meaning nothing is accidentally considered 180 // initialized. 181 let dst: &mut MaybeUninit<_> = unsafe { &mut storage.params }; 182 params.store(&mut store, ty, dst)?; 183 } 184 185 // Try to capture only a single variable (a tuple) in the closure below. 186 // This means the size of the closure is one pointer and is much more 187 // efficient to move in memory. This closure is actually invoked on the 188 // other side of a C++ shim, so it can never be inlined enough to make 189 // the memory go away, so the size matters here for performance. 190 let mut captures = (func, storage); 191 192 let result = invoke_wasm_and_catch_traps(store, |caller, vm| { 193 let (func_ref, storage) = &mut captures; 194 let storage_len = mem::size_of_val::<Storage<_, _>>(storage) / mem::size_of::<ValRaw>(); 195 let storage: *mut Storage<_, _> = storage; 196 let storage = storage.cast::<ValRaw>(); 197 let storage = core::ptr::slice_from_raw_parts_mut(storage, storage_len); 198 let storage = NonNull::new(storage).unwrap(); 199 200 // SAFETY: this function's own contract is that `func_ref` is safe 201 // to call and additionally that the params/results are correctly 202 // ascribed for this function call to be safe. 203 unsafe { VMFuncRef::array_call(*func_ref, vm, caller, storage) } 204 }); 205 206 let (_, storage) = captures; 207 result?; 208 209 let mut store = AutoAssertNoGc::new(store.0); 210 // SAFETY: this function is itself unsafe to ensure that the result type 211 // ascription is correct for `Results` and matches the actual function. 212 // Additionally given the correct type ascription all of the `results` 213 // accessed here should be validly initialized. 214 unsafe { Ok(Results::load(&mut store, &storage.results)) } 215 } 216 217 /// Purely a debug-mode assertion, not actually used in release builds. debug_typecheck(store: &StoreOpaque, func: VMSharedTypeIndex)218 fn debug_typecheck(store: &StoreOpaque, func: VMSharedTypeIndex) { 219 let ty = FuncType::from_shared_type_index(store.engine(), func); 220 Params::typecheck(store.engine(), ty.params(), TypeCheckPosition::Param) 221 .expect("params should match"); 222 Results::typecheck(store.engine(), ty.results(), TypeCheckPosition::Result) 223 .expect("results should match"); 224 } 225 } 226 227 #[doc(hidden)] 228 #[derive(Copy, Clone)] 229 pub enum TypeCheckPosition { 230 Param, 231 Result, 232 } 233 234 /// A trait implemented for types which can be arguments and results for 235 /// closures passed to [`Func::wrap`] as well as parameters to [`Func::typed`]. 236 /// 237 /// This trait should not be implemented by user types. This trait may change at 238 /// any time internally. The types which implement this trait, however, are 239 /// stable over time. 240 /// 241 /// For more information see [`Func::wrap`] and [`Func::typed`] 242 pub unsafe trait WasmTy: Send { 243 // Do a "static" (aka at time of `func.typed::<P, R>()`) ahead-of-time type 244 // check for this type at the given position. You probably don't need to 245 // override this trait method. 246 #[doc(hidden)] 247 #[inline] typecheck(engine: &Engine, actual: ValType, position: TypeCheckPosition) -> Result<()>248 fn typecheck(engine: &Engine, actual: ValType, position: TypeCheckPosition) -> Result<()> { 249 let expected = Self::valtype(); 250 debug_assert!(expected.comes_from_same_engine(engine)); 251 debug_assert!(actual.comes_from_same_engine(engine)); 252 match position { 253 // The caller is expecting to receive a `T` and the callee is 254 // actually returning a `U`, so ensure that `U <: T`. 255 TypeCheckPosition::Result => actual.ensure_matches(engine, &expected), 256 // The caller is expecting to pass a `T` and the callee is expecting 257 // to receive a `U`, so ensure that `T <: U`. 258 TypeCheckPosition::Param => match (expected.as_ref(), actual.as_ref()) { 259 // ... except that this technically-correct check would overly 260 // restrict the usefulness of our typed function APIs for the 261 // specific case of concrete reference types. Let's work through 262 // an example. 263 // 264 // Consider functions that take a `(ref param $some_func_type)` 265 // parameter: 266 // 267 // * We cannot have a static `wasmtime::SomeFuncTypeRef` type 268 // that implements `WasmTy` specifically for `(ref null 269 // $some_func_type)` because Wasm modules, and their types, 270 // are loaded dynamically at runtime. 271 // 272 // * Therefore the embedder's only option for `T <: (ref null 273 // $some_func_type)` is `T = (ref null nofunc)` aka 274 // `Option<wasmtime::NoFunc>`. 275 // 276 // * But that static type means they can *only* pass in the null 277 // function reference as an argument to the typed function. 278 // This is way too restrictive! For ergonomics, we want them 279 // to be able to pass in a `wasmtime::Func` whose type is 280 // `$some_func_type`! 281 // 282 // To lift this constraint and enable better ergonomics for 283 // embedders, we allow `top(T) <: top(U)` -- i.e. they are part 284 // of the same type hierarchy and a dynamic cast could possibly 285 // succeed -- for the specific case of concrete heap type 286 // parameters, and fall back to dynamic type checks on the 287 // arguments passed to each invocation, as necessary. 288 (Some(expected_ref), Some(actual_ref)) if actual_ref.heap_type().is_concrete() => { 289 expected_ref 290 .heap_type() 291 .top() 292 .ensure_matches(engine, &actual_ref.heap_type().top()) 293 } 294 _ => expected.ensure_matches(engine, &actual), 295 }, 296 } 297 } 298 299 // The value type that this Type represents. 300 #[doc(hidden)] valtype() -> ValType301 fn valtype() -> ValType; 302 303 #[doc(hidden)] may_gc() -> bool304 fn may_gc() -> bool { 305 match Self::valtype() { 306 ValType::Ref(_) => true, 307 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => false, 308 } 309 } 310 311 // Dynamic checks that this value is being used with the correct store 312 // context. 313 #[doc(hidden)] compatible_with_store(&self, store: &StoreOpaque) -> bool314 fn compatible_with_store(&self, store: &StoreOpaque) -> bool; 315 316 // Dynamic checks that `self <: actual` for concrete type arguments. See the 317 // comment above in `WasmTy::typecheck`. 318 // 319 // Only ever called for concrete reference type arguments, so any type which 320 // is not in a type hierarchy with concrete reference types can implement 321 // this with `unreachable!()`. 322 #[doc(hidden)] dynamic_concrete_type_check( &self, store: &StoreOpaque, nullable: bool, actual: &HeapType, ) -> Result<()>323 fn dynamic_concrete_type_check( 324 &self, 325 store: &StoreOpaque, 326 nullable: bool, 327 actual: &HeapType, 328 ) -> Result<()>; 329 330 // Is this a GC-managed reference that actually points to a GC object? That 331 // is, `self` is *not* an `i31`, null reference, or uninhabited type. 332 // 333 // Note that it is okay if this returns false positives (i.e. `true` for 334 // `Rooted<AnyRef>` without actually looking up the rooted `anyref` in the 335 // store and reflecting on it to determine whether it is actually an 336 // `i31`). However, it is not okay if this returns false negatives. 337 #[doc(hidden)] 338 #[inline] is_vmgcref_and_points_to_object(&self) -> bool339 fn is_vmgcref_and_points_to_object(&self) -> bool { 340 Self::valtype().is_vmgcref_type_and_points_to_object() 341 } 342 343 // Store `self` into `ptr`. 344 // 345 // NB: We _must not_ trigger a GC when passing refs from host code into Wasm 346 // (e.g. returned from a host function or passed as arguments to a Wasm 347 // function). After insertion into the activations table, the reference is 348 // no longer rooted. If multiple references are being sent from the host 349 // into Wasm and we allowed GCs during insertion, then the following events 350 // could happen: 351 // 352 // * Reference A is inserted into the activations table. This does not 353 // trigger a GC, but does fill the table to capacity. 354 // 355 // * The caller's reference to A is removed. Now the only reference to A is 356 // from the activations table. 357 // 358 // * Reference B is inserted into the activations table. Because the table 359 // is at capacity, a GC is triggered. 360 // 361 // * A is reclaimed because the only reference keeping it alive was the 362 // activation table's reference (it isn't inside any Wasm frames on the 363 // stack yet, so stack scanning and stack maps don't increment its 364 // reference count). 365 // 366 // * We transfer control to Wasm, giving it A and B. Wasm uses A. That's a 367 // use-after-free bug. 368 // 369 // In conclusion, to prevent uses-after-free bugs, we cannot GC while 370 // converting types into their raw ABI forms. 371 #[doc(hidden)] store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>372 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>; 373 374 // Load a version of `Self` from the `ptr` provided. 375 // 376 // # Safety 377 // 378 // This function is unsafe as it's up to the caller to ensure that `ptr` is 379 // valid for this given type. 380 #[doc(hidden)] load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self381 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self; 382 } 383 384 macro_rules! integers { 385 ($($primitive:ident/$get_primitive:ident => $ty:ident)*) => ($( 386 unsafe impl WasmTy for $primitive { 387 #[inline] 388 fn valtype() -> ValType { 389 ValType::$ty 390 } 391 #[inline] 392 fn compatible_with_store(&self, _: &StoreOpaque) -> bool { 393 true 394 } 395 #[inline] 396 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> { 397 unreachable!() 398 } 399 #[inline] 400 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 401 ptr.write(ValRaw::$primitive(self)); 402 Ok(()) 403 } 404 #[inline] 405 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 406 ptr.$get_primitive() 407 } 408 } 409 )*) 410 } 411 412 integers! { 413 i32/get_i32 => I32 414 i64/get_i64 => I64 415 u32/get_u32 => I32 416 u64/get_u64 => I64 417 } 418 419 macro_rules! floats { 420 ($($float:ident/$int:ident/$get_float:ident => $ty:ident)*) => ($( 421 unsafe impl WasmTy for $float { 422 #[inline] 423 fn valtype() -> ValType { 424 ValType::$ty 425 } 426 #[inline] 427 fn compatible_with_store(&self, _: &StoreOpaque) -> bool { 428 true 429 } 430 #[inline] 431 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> { 432 unreachable!() 433 } 434 #[inline] 435 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 436 ptr.write(ValRaw::$float(self.to_bits())); 437 Ok(()) 438 } 439 #[inline] 440 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 441 $float::from_bits(ptr.$get_float()) 442 } 443 } 444 )*) 445 } 446 447 floats! { 448 f32/u32/get_f32 => F32 449 f64/u64/get_f64 => F64 450 } 451 452 unsafe impl WasmTy for NoFunc { 453 #[inline] valtype() -> ValType454 fn valtype() -> ValType { 455 ValType::Ref(RefType::new(false, HeapType::NoFunc)) 456 } 457 458 #[inline] compatible_with_store(&self, _store: &StoreOpaque) -> bool459 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool { 460 match self._inner {} 461 } 462 463 #[inline] dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()>464 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> { 465 match self._inner {} 466 } 467 468 #[inline] is_vmgcref_and_points_to_object(&self) -> bool469 fn is_vmgcref_and_points_to_object(&self) -> bool { 470 match self._inner {} 471 } 472 473 #[inline] store(self, _store: &mut AutoAssertNoGc<'_>, _ptr: &mut MaybeUninit<ValRaw>) -> Result<()>474 fn store(self, _store: &mut AutoAssertNoGc<'_>, _ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 475 match self._inner {} 476 } 477 478 #[inline] load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self479 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self { 480 unreachable!("NoFunc is uninhabited") 481 } 482 } 483 484 unsafe impl WasmTy for Option<NoFunc> { 485 #[inline] valtype() -> ValType486 fn valtype() -> ValType { 487 ValType::Ref(RefType::new(true, HeapType::NoFunc)) 488 } 489 490 #[inline] compatible_with_store(&self, _store: &StoreOpaque) -> bool491 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool { 492 true 493 } 494 495 #[inline] dynamic_concrete_type_check( &self, _: &StoreOpaque, nullable: bool, ty: &HeapType, ) -> Result<()>496 fn dynamic_concrete_type_check( 497 &self, 498 _: &StoreOpaque, 499 nullable: bool, 500 ty: &HeapType, 501 ) -> Result<()> { 502 if nullable { 503 // `(ref null nofunc) <: (ref null $f)` for all function types `$f`. 504 Ok(()) 505 } else { 506 bail!("argument type mismatch: expected non-nullable (ref {ty}), found null reference") 507 } 508 } 509 510 #[inline] store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>511 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 512 ptr.write(ValRaw::funcref(ptr::null_mut())); 513 Ok(()) 514 } 515 516 #[inline] load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self517 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self { 518 None 519 } 520 } 521 522 unsafe impl WasmTy for Func { 523 #[inline] valtype() -> ValType524 fn valtype() -> ValType { 525 ValType::Ref(RefType::new(false, HeapType::Func)) 526 } 527 528 #[inline] compatible_with_store(&self, store: &StoreOpaque) -> bool529 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 530 self.store == store.id() 531 } 532 533 #[inline] dynamic_concrete_type_check( &self, store: &StoreOpaque, _nullable: bool, expected: &HeapType, ) -> Result<()>534 fn dynamic_concrete_type_check( 535 &self, 536 store: &StoreOpaque, 537 _nullable: bool, 538 expected: &HeapType, 539 ) -> Result<()> { 540 let expected = expected.unwrap_concrete_func(); 541 self.ensure_matches_ty(store, expected) 542 .context("argument type mismatch for reference to concrete type") 543 } 544 545 #[inline] store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>546 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 547 let abi = self.vm_func_ref(store); 548 ptr.write(ValRaw::funcref(abi.cast::<c_void>().as_ptr())); 549 Ok(()) 550 } 551 552 #[inline] load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self553 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 554 let p = NonNull::new(ptr.get_funcref()).unwrap().cast(); 555 556 // SAFETY: it's an unsafe contract of `load` that it's only provided 557 // valid wasm values owned by `store`. 558 unsafe { Func::from_vm_func_ref(store.id(), p) } 559 } 560 } 561 562 unsafe impl WasmTy for Option<Func> { 563 #[inline] valtype() -> ValType564 fn valtype() -> ValType { 565 ValType::FUNCREF 566 } 567 568 #[inline] compatible_with_store(&self, store: &StoreOpaque) -> bool569 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 570 if let Some(f) = self { 571 f.compatible_with_store(store) 572 } else { 573 true 574 } 575 } 576 dynamic_concrete_type_check( &self, store: &StoreOpaque, nullable: bool, expected: &HeapType, ) -> Result<()>577 fn dynamic_concrete_type_check( 578 &self, 579 store: &StoreOpaque, 580 nullable: bool, 581 expected: &HeapType, 582 ) -> Result<()> { 583 if let Some(f) = self { 584 let expected = expected.unwrap_concrete_func(); 585 f.ensure_matches_ty(store, expected) 586 .context("argument type mismatch for reference to concrete type") 587 } else if nullable { 588 Ok(()) 589 } else { 590 bail!( 591 "argument type mismatch: expected non-nullable (ref {expected}), found null reference" 592 ) 593 } 594 } 595 596 #[inline] store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>597 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 598 let raw = if let Some(f) = self { 599 f.vm_func_ref(store).as_ptr() 600 } else { 601 ptr::null_mut() 602 }; 603 ptr.write(ValRaw::funcref(raw.cast::<c_void>())); 604 Ok(()) 605 } 606 607 #[inline] load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self608 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 609 let ptr = NonNull::new(ptr.get_funcref())?.cast(); 610 611 // SAFETY: it's an unsafe contract of `load` that it's only provided 612 // valid wasm values owned by `store`. 613 unsafe { Some(Func::from_vm_func_ref(store.id(), ptr)) } 614 } 615 } 616 617 /// A trait used for [`Func::typed`] and with [`TypedFunc`] to represent the set of 618 /// parameters for wasm functions. 619 /// 620 /// This is implemented for bare types that can be passed to wasm as well as 621 /// tuples of those types. 622 pub unsafe trait WasmParams: Send { 623 #[doc(hidden)] 624 type ValRawStorage: Copy; 625 626 #[doc(hidden)] typecheck( engine: &Engine, params: impl ExactSizeIterator<Item = crate::ValType>, position: TypeCheckPosition, ) -> Result<()>627 fn typecheck( 628 engine: &Engine, 629 params: impl ExactSizeIterator<Item = crate::ValType>, 630 position: TypeCheckPosition, 631 ) -> Result<()>; 632 633 #[doc(hidden)] vmgcref_pointing_to_object_count(&self) -> usize634 fn vmgcref_pointing_to_object_count(&self) -> usize; 635 636 #[doc(hidden)] store( self, store: &mut AutoAssertNoGc<'_>, func_ty: &FuncType, dst: &mut MaybeUninit<Self::ValRawStorage>, ) -> Result<()>637 fn store( 638 self, 639 store: &mut AutoAssertNoGc<'_>, 640 func_ty: &FuncType, 641 dst: &mut MaybeUninit<Self::ValRawStorage>, 642 ) -> Result<()>; 643 } 644 645 // Forward an impl from `T` to `(T,)` for convenience if there's only one 646 // parameter. 647 unsafe impl<T> WasmParams for T 648 where 649 T: WasmTy, 650 { 651 type ValRawStorage = <(T,) as WasmParams>::ValRawStorage; 652 typecheck( engine: &Engine, params: impl ExactSizeIterator<Item = crate::ValType>, position: TypeCheckPosition, ) -> Result<()>653 fn typecheck( 654 engine: &Engine, 655 params: impl ExactSizeIterator<Item = crate::ValType>, 656 position: TypeCheckPosition, 657 ) -> Result<()> { 658 <(T,) as WasmParams>::typecheck(engine, params, position) 659 } 660 661 #[inline] vmgcref_pointing_to_object_count(&self) -> usize662 fn vmgcref_pointing_to_object_count(&self) -> usize { 663 T::is_vmgcref_and_points_to_object(self) as usize 664 } 665 666 #[inline] store( self, store: &mut AutoAssertNoGc<'_>, func_ty: &FuncType, dst: &mut MaybeUninit<Self::ValRawStorage>, ) -> Result<()>667 fn store( 668 self, 669 store: &mut AutoAssertNoGc<'_>, 670 func_ty: &FuncType, 671 dst: &mut MaybeUninit<Self::ValRawStorage>, 672 ) -> Result<()> { 673 <(T,) as WasmParams>::store((self,), store, func_ty, dst) 674 } 675 } 676 677 macro_rules! impl_wasm_params { 678 ($n:tt $($t:ident)*) => { 679 #[allow(non_snake_case, reason = "macro-generated code")] 680 unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) { 681 type ValRawStorage = [ValRaw; $n]; 682 683 fn typecheck( 684 _engine: &Engine, 685 mut params: impl ExactSizeIterator<Item = crate::ValType>, 686 _position: TypeCheckPosition, 687 ) -> Result<()> { 688 let mut _n = 0; 689 690 $( 691 match params.next() { 692 Some(t) => { 693 _n += 1; 694 $t::typecheck(_engine, t, _position)? 695 }, 696 None => bail!("expected {} types, found {}", $n, params.len() + _n), 697 } 698 )* 699 700 match params.next() { 701 None => Ok(()), 702 Some(_) => { 703 _n += 1; 704 bail!("expected {} types, found {}", $n, params.len() + _n) 705 }, 706 } 707 } 708 709 #[inline] 710 fn vmgcref_pointing_to_object_count(&self) -> usize { 711 let ($($t,)*) = self; 712 0 $( 713 + $t.is_vmgcref_and_points_to_object() as usize 714 )* 715 } 716 717 718 #[inline] 719 fn store( 720 self, 721 _store: &mut AutoAssertNoGc<'_>, 722 _func_ty: &FuncType, 723 _ptr: &mut MaybeUninit<Self::ValRawStorage>, 724 ) -> Result<()> { 725 let ($($t,)*) = self; 726 727 let mut _i = 0; 728 $( 729 if !$t.compatible_with_store(_store) { 730 bail!("attempt to pass cross-`Store` value to Wasm as function argument"); 731 } 732 733 if $t::valtype().is_ref() { 734 let param_ty = _func_ty.param(_i).unwrap(); 735 let ref_ty = param_ty.unwrap_ref(); 736 let heap_ty = ref_ty.heap_type(); 737 if heap_ty.is_concrete() { 738 $t.dynamic_concrete_type_check(_store, ref_ty.is_nullable(), heap_ty)?; 739 } 740 } 741 742 let dst = map_maybe_uninit!(_ptr[_i]); 743 $t.store(_store, dst)?; 744 745 _i += 1; 746 )* 747 Ok(()) 748 } 749 } 750 }; 751 } 752 753 for_each_function_signature!(impl_wasm_params); 754 755 /// A trait used for [`Func::typed`] and with [`TypedFunc`] to represent the set of 756 /// results for wasm functions. 757 pub unsafe trait WasmResults: WasmParams { 758 #[doc(hidden)] load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self759 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self; 760 } 761 762 // Forwards from a bare type `T` to the 1-tuple type `(T,)` 763 unsafe impl<T: WasmTy> WasmResults for T { load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self764 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self { 765 // SAFETY: the one-element tuple and single-type impls behave the same 766 // way. 767 unsafe { <(T,) as WasmResults>::load(store, abi).0 } 768 } 769 } 770 771 macro_rules! impl_wasm_results { 772 ($n:tt $($t:ident)*) => { 773 #[allow(non_snake_case, reason = "macro-generated code")] 774 unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*) { 775 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self { 776 let [$($t,)*] = abi; 777 778 ( 779 // SAFETY: this is forwarding the unsafe contract of the outer 780 // function to the inner functions here. 781 $(unsafe { $t::load(_store, $t) },)* 782 ) 783 } 784 } 785 }; 786 } 787 788 for_each_function_signature!(impl_wasm_results); 789