1use super::invoke_wasm_and_catch_traps;
2use crate::prelude::*;
3use crate::runtime::vm::VMFuncRef;
4use crate::store::{AutoAssertNoGc, StoreOpaque};
5use crate::{
6 AsContext, AsContextMut, Engine, Func, FuncType, HeapType, NoFunc, RefType, StoreContextMut,
7 ValRaw, ValType,
8};
9use core::ffi::c_void;
10use core::marker;
11use core::mem::{self, MaybeUninit};
12use core::ptr::{self, NonNull};
13use wasmtime_environ::VMSharedTypeIndex;
14
15pub struct TypedFunc<Params, Results> {
24 _a: marker::PhantomData<fn(Params) -> Results>,
25 ty: FuncType,
26 func: Func,
27}
28
29impl<Params, Results> Clone for 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
39impl<Params, Results> TypedFunc<Params, Results>
40where
41 Params: WasmParams,
42 Results: WasmResults,
43{
44 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
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 pub fn func(&self) -> &Func {
76 &self.func
77 }
78
79 #[inline]
100 pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results> {
101 let mut store = store.as_context_mut();
102 store.0.validate_sync_call()?;
103 let func = self.func.vm_func_ref(store.0);
104 unsafe { Self::call_raw(&mut store, &self.ty, func, params) }
105 }
106
107 #[cfg(feature = "async")]
129 pub async fn call_async(
130 &self,
131 mut store: impl AsContextMut<Data: Send>,
132 params: Params,
133 ) -> Result<Results>
134 where
135 Params: Sync,
136 Results: Sync,
137 {
138 let mut store = store.as_context_mut();
139
140 store
141 .on_fiber(|store| {
142 let func = self.func.vm_func_ref(store.0);
143 unsafe { Self::call_raw(store, &self.ty, func, params) }
144 })
145 .await?
146 }
147
148 pub(crate) unsafe fn call_raw<T>(
155 store: &mut StoreContextMut<'_, T>,
156 ty: &FuncType,
157 func: ptr::NonNull<VMFuncRef>,
158 params: Params,
159 ) -> Result<Results> {
160 unsafe {
166 if cfg!(debug_assertions) {
167 Self::debug_typecheck(store.0, func.as_ref().type_index);
168 }
169 }
170
171 union Storage<T: Copy, U: Copy> {
176 params: MaybeUninit<T>,
177 results: U,
178 }
179
180 let mut storage = Storage::<Params::ValRawStorage, Results::ValRawStorage> {
181 params: MaybeUninit::uninit(),
182 };
183
184 {
185 let mut store = AutoAssertNoGc::new(store.0);
186 let dst: &mut MaybeUninit<_> = unsafe { &mut storage.params };
190 params.store(&mut store, ty, dst)?;
191 }
192
193 let mut captures = (func, storage);
199
200 let result = invoke_wasm_and_catch_traps(store, |caller, vm| {
201 let (func_ref, storage) = &mut captures;
202 let storage_len = mem::size_of_val::<Storage<_, _>>(storage) / mem::size_of::<ValRaw>();
203 let storage: *mut Storage<_, _> = storage;
204 let storage = storage.cast::<ValRaw>();
205 let storage = core::ptr::slice_from_raw_parts_mut(storage, storage_len);
206 let storage = NonNull::new(storage).unwrap();
207
208 unsafe { VMFuncRef::array_call(*func_ref, vm, caller, storage) }
212 });
213
214 let (_, storage) = captures;
215 result?;
216
217 let mut store = AutoAssertNoGc::new(store.0);
218 unsafe { Ok(Results::load(&mut store, &storage.results)) }
223 }
224
225 fn debug_typecheck(store: &StoreOpaque, func: VMSharedTypeIndex) {
227 let ty = FuncType::from_shared_type_index(store.engine(), func);
228 Params::typecheck(store.engine(), ty.params(), TypeCheckPosition::Param)
229 .expect("params should match");
230 Results::typecheck(store.engine(), ty.results(), TypeCheckPosition::Result)
231 .expect("results should match");
232 }
233}
234
235#[doc(hidden)]
236#[derive(Copy, Clone)]
237pub enum TypeCheckPosition {
238 Param,
239 Result,
240}
241
242pub unsafe trait WasmTy: Send {
251 #[doc(hidden)]
255 #[inline]
256 fn typecheck(engine: &Engine, actual: ValType, position: TypeCheckPosition) -> Result<()> {
257 let expected = Self::valtype();
258 debug_assert!(expected.comes_from_same_engine(engine));
259 debug_assert!(actual.comes_from_same_engine(engine));
260 match position {
261 TypeCheckPosition::Result => actual.ensure_matches(engine, &expected),
264 TypeCheckPosition::Param => match (expected.as_ref(), actual.as_ref()) {
267 (Some(expected_ref), Some(actual_ref)) if actual_ref.heap_type().is_concrete() => {
297 let expected_top = HeapType::from(expected_ref.heap_type().top());
298 let actual_top = HeapType::from(actual_ref.heap_type().top());
299 expected_top.ensure_matches(engine, &actual_top)
300 }
301 _ => expected.ensure_matches(engine, &actual),
302 },
303 }
304 }
305
306 #[doc(hidden)]
308 fn valtype() -> ValType;
309
310 #[doc(hidden)]
311 fn may_gc() -> bool {
312 match Self::valtype() {
313 ValType::Ref(_) => true,
314 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => false,
315 }
316 }
317
318 #[doc(hidden)]
321 fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
322
323 #[doc(hidden)]
330 fn dynamic_concrete_type_check(
331 &self,
332 store: &StoreOpaque,
333 nullable: bool,
334 actual: &HeapType,
335 ) -> Result<()>;
336
337 #[doc(hidden)]
345 #[inline]
346 fn is_vmgcref_and_points_to_object(&self) -> bool {
347 Self::valtype().is_vmgcref_type_and_points_to_object()
348 }
349
350 #[doc(hidden)]
379 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>;
380
381 #[doc(hidden)]
388 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self;
389}
390
391macro_rules! integers {
392 ($($primitive:ident/$get_primitive:ident => $ty:ident)*) => ($(
393 unsafe impl WasmTy for $primitive {
394 #[inline]
395 fn valtype() -> ValType {
396 ValType::$ty
397 }
398 #[inline]
399 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
400 true
401 }
402 #[inline]
403 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
404 unreachable!()
405 }
406 #[inline]
407 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
408 ptr.write(ValRaw::$primitive(self));
409 Ok(())
410 }
411 #[inline]
412 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
413 ptr.$get_primitive()
414 }
415 }
416 )*)
417}
418
419integers! {
420 i32/get_i32 => I32
421 i64/get_i64 => I64
422 u32/get_u32 => I32
423 u64/get_u64 => I64
424}
425
426macro_rules! floats {
427 ($($float:ident/$int:ident/$get_float:ident => $ty:ident)*) => ($(
428 unsafe impl WasmTy for $float {
429 #[inline]
430 fn valtype() -> ValType {
431 ValType::$ty
432 }
433 #[inline]
434 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
435 true
436 }
437 #[inline]
438 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
439 unreachable!()
440 }
441 #[inline]
442 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
443 ptr.write(ValRaw::$float(self.to_bits()));
444 Ok(())
445 }
446 #[inline]
447 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
448 $float::from_bits(ptr.$get_float())
449 }
450 }
451 )*)
452}
453
454floats! {
455 f32/u32/get_f32 => F32
456 f64/u64/get_f64 => F64
457}
458
459unsafe impl WasmTy for NoFunc {
460 #[inline]
461 fn valtype() -> ValType {
462 ValType::Ref(RefType::new(false, HeapType::NoFunc))
463 }
464
465 #[inline]
466 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
467 match self._inner {}
468 }
469
470 #[inline]
471 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
472 match self._inner {}
473 }
474
475 #[inline]
476 fn is_vmgcref_and_points_to_object(&self) -> bool {
477 match self._inner {}
478 }
479
480 #[inline]
481 fn store(self, _store: &mut AutoAssertNoGc<'_>, _ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
482 match self._inner {}
483 }
484
485 #[inline]
486 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
487 unreachable!("NoFunc is uninhabited")
488 }
489}
490
491unsafe impl WasmTy for Option<NoFunc> {
492 #[inline]
493 fn valtype() -> ValType {
494 ValType::Ref(RefType::new(true, HeapType::NoFunc))
495 }
496
497 #[inline]
498 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
499 true
500 }
501
502 #[inline]
503 fn dynamic_concrete_type_check(
504 &self,
505 _: &StoreOpaque,
506 nullable: bool,
507 ty: &HeapType,
508 ) -> Result<()> {
509 if nullable {
510 Ok(())
512 } else {
513 bail!("argument type mismatch: expected non-nullable (ref {ty}), found null reference")
514 }
515 }
516
517 #[inline]
518 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
519 ptr.write(ValRaw::funcref(ptr::null_mut()));
520 Ok(())
521 }
522
523 #[inline]
524 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
525 None
526 }
527}
528
529unsafe impl WasmTy for Func {
530 #[inline]
531 fn valtype() -> ValType {
532 ValType::Ref(RefType::new(false, HeapType::Func))
533 }
534
535 #[inline]
536 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
537 self.store == store.id()
538 }
539
540 #[inline]
541 fn dynamic_concrete_type_check(
542 &self,
543 store: &StoreOpaque,
544 _nullable: bool,
545 expected: &HeapType,
546 ) -> Result<()> {
547 let expected = expected.unwrap_concrete_func();
548 self.ensure_matches_ty(store, expected)
549 .context("argument type mismatch for reference to concrete type")
550 }
551
552 #[inline]
553 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
554 let abi = self.vm_func_ref(store);
555 ptr.write(ValRaw::funcref(abi.cast::<c_void>().as_ptr()));
556 Ok(())
557 }
558
559 #[inline]
560 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
561 let p = NonNull::new(ptr.get_funcref()).unwrap().cast();
562
563 unsafe { Func::from_vm_func_ref(store.id(), p) }
566 }
567}
568
569unsafe impl WasmTy for Option<Func> {
570 #[inline]
571 fn valtype() -> ValType {
572 ValType::FUNCREF
573 }
574
575 #[inline]
576 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
577 if let Some(f) = self {
578 f.compatible_with_store(store)
579 } else {
580 true
581 }
582 }
583
584 fn dynamic_concrete_type_check(
585 &self,
586 store: &StoreOpaque,
587 nullable: bool,
588 expected: &HeapType,
589 ) -> Result<()> {
590 if let Some(f) = self {
591 let expected = expected.unwrap_concrete_func();
592 f.ensure_matches_ty(store, expected)
593 .context("argument type mismatch for reference to concrete type")
594 } else if nullable {
595 Ok(())
596 } else {
597 bail!(
598 "argument type mismatch: expected non-nullable (ref {expected}), found null reference"
599 )
600 }
601 }
602
603 #[inline]
604 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
605 let raw = if let Some(f) = self {
606 f.vm_func_ref(store).as_ptr()
607 } else {
608 ptr::null_mut()
609 };
610 ptr.write(ValRaw::funcref(raw.cast::<c_void>()));
611 Ok(())
612 }
613
614 #[inline]
615 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
616 let ptr = NonNull::new(ptr.get_funcref())?.cast();
617
618 unsafe { Some(Func::from_vm_func_ref(store.id(), ptr)) }
621 }
622}
623
624pub unsafe trait WasmParams: Send {
630 #[doc(hidden)]
631 type ValRawStorage: Copy;
632
633 #[doc(hidden)]
634 fn typecheck(
635 engine: &Engine,
636 params: impl ExactSizeIterator<Item = crate::ValType>,
637 position: TypeCheckPosition,
638 ) -> Result<()>;
639
640 #[doc(hidden)]
641 fn vmgcref_pointing_to_object_count(&self) -> usize;
642
643 #[doc(hidden)]
644 fn store(
645 self,
646 store: &mut AutoAssertNoGc<'_>,
647 func_ty: &FuncType,
648 dst: &mut MaybeUninit<Self::ValRawStorage>,
649 ) -> Result<()>;
650}
651
652unsafe impl<T> WasmParams for T
655where
656 T: WasmTy,
657{
658 type ValRawStorage = <(T,) as WasmParams>::ValRawStorage;
659
660 fn typecheck(
661 engine: &Engine,
662 params: impl ExactSizeIterator<Item = crate::ValType>,
663 position: TypeCheckPosition,
664 ) -> Result<()> {
665 <(T,) as WasmParams>::typecheck(engine, params, position)
666 }
667
668 #[inline]
669 fn vmgcref_pointing_to_object_count(&self) -> usize {
670 T::is_vmgcref_and_points_to_object(self) as usize
671 }
672
673 #[inline]
674 fn store(
675 self,
676 store: &mut AutoAssertNoGc<'_>,
677 func_ty: &FuncType,
678 dst: &mut MaybeUninit<Self::ValRawStorage>,
679 ) -> Result<()> {
680 <(T,) as WasmParams>::store((self,), store, func_ty, dst)
681 }
682}
683
684macro_rules! impl_wasm_params {
685 ($n:tt $($t:ident)*) => {
686 #[allow(non_snake_case, reason = "macro-generated code")]
687 unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) {
688 type ValRawStorage = [ValRaw; $n];
689
690 fn typecheck(
691 _engine: &Engine,
692 mut params: impl ExactSizeIterator<Item = crate::ValType>,
693 _position: TypeCheckPosition,
694 ) -> Result<()> {
695 let mut _n = 0;
696
697 $(
698 match params.next() {
699 Some(t) => {
700 _n += 1;
701 $t::typecheck(_engine, t, _position)?
702 },
703 None => bail!("expected {} types, found {}", $n, params.len() + _n),
704 }
705 )*
706
707 match params.next() {
708 None => Ok(()),
709 Some(_) => {
710 _n += 1;
711 bail!("expected {} types, found {}", $n, params.len() + _n)
712 },
713 }
714 }
715
716 #[inline]
717 fn vmgcref_pointing_to_object_count(&self) -> usize {
718 let ($($t,)*) = self;
719 0 $(
720 + $t.is_vmgcref_and_points_to_object() as usize
721 )*
722 }
723
724
725 #[inline]
726 fn store(
727 self,
728 _store: &mut AutoAssertNoGc<'_>,
729 _func_ty: &FuncType,
730 _ptr: &mut MaybeUninit<Self::ValRawStorage>,
731 ) -> Result<()> {
732 let ($($t,)*) = self;
733
734 let mut _i = 0;
735 $(
736 if !$t.compatible_with_store(_store) {
737 bail!("attempt to pass cross-`Store` value to Wasm as function argument");
738 }
739
740 if $t::valtype().is_ref() {
741 let param_ty = _func_ty.param(_i).unwrap();
742 let ref_ty = param_ty.unwrap_ref();
743 let heap_ty = ref_ty.heap_type();
744 if heap_ty.is_concrete() {
745 $t.dynamic_concrete_type_check(_store, ref_ty.is_nullable(), heap_ty)?;
746 }
747 }
748
749 let dst = map_maybe_uninit!(_ptr[_i]);
750 $t.store(_store, dst)?;
751
752 _i += 1;
753 )*
754 Ok(())
755 }
756 }
757 };
758}
759
760for_each_function_signature!(impl_wasm_params);
761
762pub unsafe trait WasmResults: WasmParams {
765 #[doc(hidden)]
766 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self;
767}
768
769unsafe impl<T: WasmTy> WasmResults for T {
771 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
772 unsafe { <(T,) as WasmResults>::load(store, abi).0 }
775 }
776}
777
778macro_rules! impl_wasm_results {
779 ($n:tt $($t:ident)*) => {
780 #[allow(non_snake_case, reason = "macro-generated code")]
781 unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*) {
782 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
783 let [$($t,)*] = abi;
784
785 (
786 $(unsafe { $t::load(_store, $t) },)*
789 )
790 }
791 }
792 };
793}
794
795for_each_function_signature!(impl_wasm_results);