1use super::{invoke_wasm_and_catch_traps, HostAbi};
2use crate::store::{AutoAssertNoGc, StoreOpaque};
3use crate::{
4 AsContext, AsContextMut, Engine, Func, FuncType, HeapType, NoFunc, RefType, StoreContextMut,
5 ValRaw, ValType,
6};
7use anyhow::{bail, Context, Result};
8use std::marker;
9use std::mem::{self, MaybeUninit};
10use std::num::NonZeroUsize;
11use std::os::raw::c_void;
12use std::ptr::{self, NonNull};
13use wasmtime_runtime::{
14 VMContext, VMFuncRef, VMNativeCallFunction, VMOpaqueContext, VMSharedTypeIndex,
15};
16
17pub struct TypedFunc<Params, Results> {
26 _a: marker::PhantomData<fn(Params) -> Results>,
27 ty: FuncType,
28 func: Func,
29}
30
31impl<Params, Results> Clone for TypedFunc<Params, Results> {
32 fn clone(&self) -> TypedFunc<Params, Results> {
33 Self {
34 _a: marker::PhantomData,
35 ty: self.ty.clone(),
36 func: self.func,
37 }
38 }
39}
40
41impl<Params, Results> TypedFunc<Params, Results>
42where
43 Params: WasmParams,
44 Results: WasmResults,
45{
46 pub unsafe fn new_unchecked(store: impl AsContext, func: Func) -> TypedFunc<Params, Results> {
59 let store = store.as_context().0;
60 Self::_new_unchecked(store, func)
61 }
62
63 pub(crate) unsafe fn _new_unchecked(
64 store: &StoreOpaque,
65 func: Func,
66 ) -> TypedFunc<Params, Results> {
67 let ty = func.load_ty(store);
68 TypedFunc {
69 _a: marker::PhantomData,
70 ty,
71 func,
72 }
73 }
74
75 pub fn func(&self) -> &Func {
78 &self.func
79 }
80
81 pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results> {
99 let mut store = store.as_context_mut();
100 assert!(
101 !store.0.async_support(),
102 "must use `call_async` with async stores"
103 );
104 if Self::need_gc_before_call_raw(store.0, ¶ms) {
105 store.0.gc();
106 }
107 let func = self.func.vm_func_ref(store.0);
108 unsafe { Self::call_raw(&mut store, &self.ty, func, params) }
109 }
110
111 #[cfg(feature = "async")]
129 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
130 pub async fn call_async<T>(
131 &self,
132 mut store: impl AsContextMut<Data = T>,
133 params: Params,
134 ) -> Result<Results>
135 where
136 T: Send,
137 {
138 let mut store = store.as_context_mut();
139 assert!(
140 store.0.async_support(),
141 "must use `call` with non-async stores"
142 );
143 if Self::need_gc_before_call_raw(store.0, ¶ms) {
144 store.0.gc_async().await;
145 }
146 store
147 .on_fiber(|store| {
148 let func = self.func.vm_func_ref(store.0);
149 unsafe { Self::call_raw(store, &self.ty, func, params) }
150 })
151 .await?
152 }
153
154 #[inline]
155 pub(crate) fn need_gc_before_call_raw(_store: &StoreOpaque, _params: &Params) -> bool {
156 #[cfg(feature = "gc")]
157 {
158 let num_gc_refs = _params.non_i31_gc_refs_count();
160 if let Some(num_gc_refs) = NonZeroUsize::new(num_gc_refs) {
161 return _store
162 .unwrap_gc_store()
163 .gc_heap
164 .need_gc_before_entering_wasm(num_gc_refs);
165 }
166 }
167
168 false
169 }
170
171 pub(crate) unsafe fn call_raw<T>(
180 store: &mut StoreContextMut<'_, T>,
181 ty: &FuncType,
182 func: ptr::NonNull<VMFuncRef>,
183 params: Params,
184 ) -> Result<Results> {
185 if cfg!(debug_assertions) {
188 Self::debug_typecheck(store.0, func.as_ref().type_index);
189 }
190
191 let params = {
196 let mut store = AutoAssertNoGc::new(store.0);
197 params.into_abi(&mut store, ty)?
198 };
199
200 let mut captures = (func, MaybeUninit::uninit(), params, false);
206
207 let result = invoke_wasm_and_catch_traps(store, |caller| {
208 let (func_ref, ret, params, returned) = &mut captures;
209 let func_ref = func_ref.as_ref();
210 let result =
211 Params::invoke::<Results>(func_ref.native_call, func_ref.vmctx, caller, *params);
212 ptr::write(ret.as_mut_ptr(), result);
213 *returned = true
214 });
215
216 let (_, ret, _, returned) = captures;
217 debug_assert_eq!(result.is_ok(), returned);
218 result?;
219
220 let mut store = AutoAssertNoGc::new(store.0);
221 Ok(Results::from_abi(&mut store, ret.assume_init()))
222 }
223
224 fn debug_typecheck(store: &StoreOpaque, func: VMSharedTypeIndex) {
226 let ty = FuncType::from_shared_type_index(store.engine(), func);
227 Params::typecheck(store.engine(), ty.params(), TypeCheckPosition::Param)
228 .expect("params should match");
229 Results::typecheck(store.engine(), ty.results(), TypeCheckPosition::Result)
230 .expect("results should match");
231 }
232}
233
234#[doc(hidden)]
235#[derive(Copy, Clone)]
236pub enum TypeCheckPosition {
237 Param,
238 Result,
239}
240
241pub unsafe trait WasmTy: Send {
250 #[doc(hidden)]
253 type Abi: 'static + Copy;
254
255 #[doc(hidden)]
259 #[inline]
260 fn typecheck(engine: &Engine, actual: ValType, position: TypeCheckPosition) -> Result<()> {
261 let expected = Self::valtype();
262 debug_assert!(expected.comes_from_same_engine(engine));
263 debug_assert!(actual.comes_from_same_engine(engine));
264 match position {
265 TypeCheckPosition::Result => actual.ensure_matches(engine, &expected),
268 TypeCheckPosition::Param => match (expected.as_ref(), actual.as_ref()) {
271 (Some(expected_ref), Some(actual_ref)) if actual_ref.heap_type().is_concrete() => {
301 expected_ref
302 .heap_type()
303 .top(engine)
304 .ensure_matches(engine, &actual_ref.heap_type().top(engine))
305 }
306 _ => expected.ensure_matches(engine, &actual),
307 },
308 }
309 }
310
311 #[doc(hidden)]
313 fn valtype() -> ValType;
314
315 #[doc(hidden)]
318 fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
319
320 #[doc(hidden)]
327 fn dynamic_concrete_type_check(
328 &self,
329 store: &StoreOpaque,
330 nullable: bool,
331 actual: &FuncType,
332 ) -> Result<()>;
333
334 #[doc(hidden)]
336 fn is_non_i31_gc_ref(&self) -> bool;
337
338 #[doc(hidden)]
340 unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi;
341
342 #[doc(hidden)]
344 unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw);
345
346 #[doc(hidden)]
375 fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi>;
376
377 #[doc(hidden)]
379 unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self;
380}
381
382macro_rules! integers {
383 ($($primitive:ident/$get_primitive:ident => $ty:ident)*) => ($(
384 unsafe impl WasmTy for $primitive {
385 type Abi = $primitive;
386 #[inline]
387 fn valtype() -> ValType {
388 ValType::$ty
389 }
390 #[inline]
391 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
392 true
393 }
394 #[inline]
395 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
396 unreachable!()
397 }
398 #[inline]
399 fn is_non_i31_gc_ref(&self) -> bool {
400 false
401 }
402 #[inline]
403 unsafe fn abi_from_raw(raw: *mut ValRaw) -> $primitive {
404 (*raw).$get_primitive()
405 }
406 #[inline]
407 unsafe fn abi_into_raw(abi: $primitive, raw: *mut ValRaw) {
408 *raw = ValRaw::$primitive(abi);
409 }
410 #[inline]
411 fn into_abi(self, _store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi>
412 {
413 Ok(self)
414 }
415 #[inline]
416 unsafe fn from_abi(abi: Self::Abi, _store: &mut AutoAssertNoGc<'_>) -> Self {
417 abi
418 }
419 }
420 )*)
421}
422
423integers! {
424 i32/get_i32 => I32
425 i64/get_i64 => I64
426 u32/get_u32 => I32
427 u64/get_u64 => I64
428}
429
430macro_rules! floats {
431 ($($float:ident/$int:ident/$get_float:ident => $ty:ident)*) => ($(
432 unsafe impl WasmTy for $float {
433 type Abi = $float;
434 #[inline]
435 fn valtype() -> ValType {
436 ValType::$ty
437 }
438 #[inline]
439 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
440 true
441 }
442 #[inline]
443 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
444 unreachable!()
445 }
446 #[inline]
447 fn is_non_i31_gc_ref(&self) -> bool {
448 false
449 }
450 #[inline]
451 unsafe fn abi_from_raw(raw: *mut ValRaw) -> $float {
452 $float::from_bits((*raw).$get_float())
453 }
454 #[inline]
455 unsafe fn abi_into_raw(abi: $float, raw: *mut ValRaw) {
456 *raw = ValRaw::$float(abi.to_bits());
457 }
458 #[inline]
459 fn into_abi(self, _store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi>
460 {
461 Ok(self)
462 }
463 #[inline]
464 unsafe fn from_abi(abi: Self::Abi, _store: &mut AutoAssertNoGc<'_>) -> Self {
465 abi
466 }
467 }
468 )*)
469}
470
471floats! {
472 f32/u32/get_f32 => F32
473 f64/u64/get_f64 => F64
474}
475
476unsafe impl WasmTy for NoFunc {
477 type Abi = NoFunc;
478
479 #[inline]
480 fn valtype() -> ValType {
481 ValType::Ref(RefType::new(false, HeapType::NoFunc))
482 }
483
484 #[inline]
485 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
486 match self._inner {}
487 }
488
489 #[inline]
490 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
491 match self._inner {}
492 }
493
494 #[inline]
495 fn is_non_i31_gc_ref(&self) -> bool {
496 match self._inner {}
497 }
498
499 #[inline]
500 unsafe fn abi_from_raw(_raw: *mut ValRaw) -> Self::Abi {
501 unreachable!("NoFunc is uninhabited")
502 }
503
504 #[inline]
505 unsafe fn abi_into_raw(_abi: Self::Abi, _raw: *mut ValRaw) {
506 unreachable!("NoFunc is uninhabited")
507 }
508
509 #[inline]
510 fn into_abi(self, _store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
511 unreachable!("NoFunc is uninhabited")
512 }
513
514 #[inline]
515 unsafe fn from_abi(_abi: Self::Abi, _store: &mut AutoAssertNoGc<'_>) -> Self {
516 unreachable!("NoFunc is uninhabited")
517 }
518}
519
520unsafe impl WasmTy for Option<NoFunc> {
521 type Abi = *mut NoFunc;
522
523 #[inline]
524 fn valtype() -> ValType {
525 ValType::Ref(RefType::new(true, HeapType::NoFunc))
526 }
527
528 #[inline]
529 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
530 true
531 }
532
533 #[inline]
534 fn dynamic_concrete_type_check(
535 &self,
536 _: &StoreOpaque,
537 nullable: bool,
538 func_ty: &FuncType,
539 ) -> Result<()> {
540 if nullable {
541 Ok(())
543 } else {
544 bail!("argument type mismatch: expected (ref {func_ty}), found null reference")
545 }
546 }
547
548 #[inline]
549 fn is_non_i31_gc_ref(&self) -> bool {
550 false
551 }
552
553 #[inline]
554 unsafe fn abi_from_raw(_raw: *mut ValRaw) -> Self::Abi {
555 ptr::null_mut()
556 }
557
558 #[inline]
559 unsafe fn abi_into_raw(_abi: Self::Abi, raw: *mut ValRaw) {
560 *raw = ValRaw::funcref(ptr::null_mut());
561 }
562
563 #[inline]
564 fn into_abi(self, _store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
565 Ok(ptr::null_mut())
566 }
567
568 #[inline]
569 unsafe fn from_abi(_abi: Self::Abi, _store: &mut AutoAssertNoGc<'_>) -> Self {
570 None
571 }
572}
573
574unsafe impl WasmTy for Func {
575 type Abi = NonNull<wasmtime_runtime::VMFuncRef>;
576
577 #[inline]
578 fn valtype() -> ValType {
579 ValType::Ref(RefType::new(false, HeapType::Func))
580 }
581
582 #[inline]
583 fn compatible_with_store<'a>(&self, store: &StoreOpaque) -> bool {
584 store.store_data().contains(self.0)
585 }
586
587 #[inline]
588 fn dynamic_concrete_type_check(
589 &self,
590 store: &StoreOpaque,
591 _nullable: bool,
592 actual: &FuncType,
593 ) -> Result<()> {
594 self.ensure_matches_ty(store, actual)
595 .context("argument type mismatch for reference to concrete type")
596 }
597
598 #[inline]
599 fn is_non_i31_gc_ref(&self) -> bool {
600 false
601 }
602
603 #[inline]
604 unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
605 let p = (*raw).get_funcref();
606 debug_assert!(!p.is_null());
607 NonNull::new_unchecked(p.cast::<wasmtime_runtime::VMFuncRef>())
608 }
609
610 #[inline]
611 unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
612 *raw = ValRaw::funcref(abi.cast::<c_void>().as_ptr());
613 }
614
615 #[inline]
616 fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
617 Ok(self.vm_func_ref(store))
618 }
619
620 #[inline]
621 unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
622 Func::from_vm_func_ref(store, abi.as_ptr()).unwrap()
623 }
624}
625
626unsafe impl WasmTy for Option<Func> {
627 type Abi = *mut wasmtime_runtime::VMFuncRef;
628
629 #[inline]
630 fn valtype() -> ValType {
631 ValType::FUNCREF
632 }
633
634 #[inline]
635 fn compatible_with_store<'a>(&self, store: &StoreOpaque) -> bool {
636 if let Some(f) = self {
637 store.store_data().contains(f.0)
638 } else {
639 true
640 }
641 }
642
643 fn dynamic_concrete_type_check(
644 &self,
645 store: &StoreOpaque,
646 nullable: bool,
647 func_ty: &FuncType,
648 ) -> Result<()> {
649 if let Some(f) = self {
650 f.ensure_matches_ty(store, func_ty)
651 .context("argument type mismatch for reference to concrete type")
652 } else if nullable {
653 Ok(())
654 } else {
655 bail!("argument type mismatch: expected (ref {func_ty}), found null reference")
656 }
657 }
658
659 #[inline]
660 fn is_non_i31_gc_ref(&self) -> bool {
661 false
662 }
663
664 #[inline]
665 unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
666 (*raw).get_funcref() as Self::Abi
667 }
668
669 #[inline]
670 unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
671 *raw = ValRaw::funcref(abi.cast());
672 }
673
674 #[inline]
675 fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
676 Ok(if let Some(f) = self {
677 f.vm_func_ref(store).as_ptr()
678 } else {
679 ptr::null_mut()
680 })
681 }
682
683 #[inline]
684 unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
685 Func::from_vm_func_ref(store, abi)
686 }
687}
688
689pub unsafe trait WasmParams: Send {
695 #[doc(hidden)]
696 type Abi: Copy;
697
698 #[doc(hidden)]
699 fn typecheck(
700 engine: &Engine,
701 params: impl ExactSizeIterator<Item = crate::ValType>,
702 position: TypeCheckPosition,
703 ) -> Result<()>;
704
705 #[doc(hidden)]
706 fn non_i31_gc_refs_count(&self) -> usize;
707
708 #[doc(hidden)]
709 fn into_abi(self, store: &mut AutoAssertNoGc<'_>, func_ty: &FuncType) -> Result<Self::Abi>;
710
711 #[doc(hidden)]
712 unsafe fn invoke<R: WasmResults>(
713 func: NonNull<VMNativeCallFunction>,
714 vmctx1: *mut VMOpaqueContext,
715 vmctx2: *mut VMContext,
716 abi: Self::Abi,
717 ) -> R::ResultAbi;
718}
719
720unsafe impl<T> WasmParams for T
723where
724 T: WasmTy,
725{
726 type Abi = <(T,) as WasmParams>::Abi;
727
728 fn typecheck(
729 engine: &Engine,
730 params: impl ExactSizeIterator<Item = crate::ValType>,
731 position: TypeCheckPosition,
732 ) -> Result<()> {
733 <(T,) as WasmParams>::typecheck(engine, params, position)
734 }
735
736 #[inline]
737 fn non_i31_gc_refs_count(&self) -> usize {
738 T::is_non_i31_gc_ref(self) as usize
739 }
740
741 #[inline]
742 fn into_abi(self, store: &mut AutoAssertNoGc<'_>, func_ty: &FuncType) -> Result<Self::Abi> {
743 <(T,) as WasmParams>::into_abi((self,), store, func_ty)
744 }
745
746 unsafe fn invoke<R: WasmResults>(
747 func: NonNull<VMNativeCallFunction>,
748 vmctx1: *mut VMOpaqueContext,
749 vmctx2: *mut VMContext,
750 abi: Self::Abi,
751 ) -> R::ResultAbi {
752 <(T,) as WasmParams>::invoke::<R>(func, vmctx1, vmctx2, abi)
753 }
754}
755
756macro_rules! impl_wasm_params {
757 ($n:tt $($t:ident)*) => {
758 #[allow(non_snake_case)]
759 unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) {
760 type Abi = ($($t::Abi,)*);
761
762 fn typecheck(
763 _engine: &Engine,
764 mut params: impl ExactSizeIterator<Item = crate::ValType>,
765 _position: TypeCheckPosition,
766 ) -> Result<()> {
767 let mut _n = 0;
768
769 $(
770 match params.next() {
771 Some(t) => {
772 _n += 1;
773 $t::typecheck(_engine, t, _position)?
774 },
775 None => bail!("expected {} types, found {}", $n, params.len() + _n),
776 }
777 )*
778
779 match params.next() {
780 None => Ok(()),
781 Some(_) => {
782 _n += 1;
783 bail!("expected {} types, found {}", $n, params.len() + _n)
784 },
785 }
786 }
787
788 #[inline]
789 fn non_i31_gc_refs_count(&self) -> usize {
790 let ($(ref $t,)*) = self;
791 0 $(
792 + $t.is_non_i31_gc_ref() as usize
793 )*
794 }
795
796
797 #[inline]
798 fn into_abi(
799 self,
800 _store: &mut AutoAssertNoGc<'_>,
801 _func_ty: &FuncType,
802 ) -> Result<Self::Abi> {
803 let ($($t,)*) = self;
804
805 let mut _i = 0;
806 $(
807 if !$t.compatible_with_store(_store) {
808 bail!("attempt to pass cross-`Store` value to Wasm as function argument");
809 }
810
811 if $t::valtype().is_ref() {
812 let p = _func_ty.param(_i).unwrap();
813 let r = p.unwrap_ref();
814 if let Some(c) = r.heap_type().as_concrete() {
815 $t.dynamic_concrete_type_check(_store, r.is_nullable(), c)?;
816 }
817 }
818
819 let $t = $t.into_abi(_store)?;
820
821 _i += 1;
822 )*
823 Ok(($($t,)*))
824 }
825
826 unsafe fn invoke<R: WasmResults>(
827 func: NonNull<VMNativeCallFunction>,
828 vmctx1: *mut VMOpaqueContext,
829 vmctx2: *mut VMContext,
830 abi: Self::Abi,
831 ) -> R::ResultAbi {
832 let fnptr = mem::transmute::<
833 NonNull<VMNativeCallFunction>,
834 unsafe extern "C" fn(
835 *mut VMOpaqueContext,
836 *mut VMContext,
837 $($t::Abi,)*
838 <R::ResultAbi as HostAbi>::Retptr,
839 ) -> <R::ResultAbi as HostAbi>::Abi,
840 >(func);
841 let ($($t,)*) = abi;
842 <R::ResultAbi as HostAbi>::call(|retptr| {
850 fnptr(vmctx1, vmctx2, $($t,)* retptr)
851 })
852 }
853 }
854 };
855}
856
857for_each_function_signature!(impl_wasm_params);
858
859pub unsafe trait WasmResults: WasmParams {
862 #[doc(hidden)]
863 type ResultAbi: HostAbi;
864
865 #[doc(hidden)]
866 unsafe fn from_abi(store: &mut AutoAssertNoGc<'_>, abi: Self::ResultAbi) -> Self;
867}
868
869unsafe impl<T: WasmTy> WasmResults for T
871where
872 (T::Abi,): HostAbi,
873{
874 type ResultAbi = <(T,) as WasmResults>::ResultAbi;
875
876 unsafe fn from_abi(store: &mut AutoAssertNoGc<'_>, abi: Self::ResultAbi) -> Self {
877 <(T,) as WasmResults>::from_abi(store, abi).0
878 }
879}
880
881macro_rules! impl_wasm_results {
882 ($n:tt $($t:ident)*) => {
883 #[allow(non_snake_case, unused_variables)]
884 unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*)
885 where ($($t::Abi,)*): HostAbi
886 {
887 type ResultAbi = ($($t::Abi,)*);
888
889 #[inline]
890 unsafe fn from_abi(store: &mut AutoAssertNoGc<'_>, abi: Self::ResultAbi) -> Self {
891 let ($($t,)*) = abi;
892 ($($t::from_abi($t, store),)*)
893 }
894 }
895 };
896}
897
898for_each_function_signature!(impl_wasm_results);