1use super::invoke_wasm_and_catch_traps;
2use crate::prelude::*;
3use crate::runtime::vm::{UncaughtException, 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 {
105 Self::call_raw(
106 &mut store,
107 &self.ty,
108 func,
109 params,
110 UncaughtException::Propagate,
111 )
112 }
113 }
114
115 #[cfg(feature = "async")]
137 pub async fn call_async(
138 &self,
139 mut store: impl AsContextMut<Data: Send>,
140 params: Params,
141 ) -> Result<Results>
142 where
143 Params: Sync,
144 Results: Sync,
145 {
146 let mut store = store.as_context_mut();
147
148 store
149 .on_fiber(|store| {
150 let func = self.func.vm_func_ref(store.0);
151 unsafe {
152 Self::call_raw(store, &self.ty, func, params, UncaughtException::Propagate)
153 }
154 })
155 .await?
156 }
157
158 pub(crate) unsafe fn call_raw<T>(
165 store: &mut StoreContextMut<'_, T>,
166 ty: &FuncType,
167 func: ptr::NonNull<VMFuncRef>,
168 params: Params,
169 uncaught_exception: UncaughtException,
170 ) -> Result<Results> {
171 unsafe {
177 if cfg!(debug_assertions) {
178 Self::debug_typecheck(store.0, func.as_ref().type_index);
179 }
180 }
181
182 union Storage<T: Copy, U: Copy> {
187 params: MaybeUninit<T>,
188 results: U,
189 }
190
191 let mut storage = Storage::<Params::ValRawStorage, Results::ValRawStorage> {
192 params: MaybeUninit::uninit(),
193 };
194
195 {
196 let mut store = AutoAssertNoGc::new(store.0);
197 let dst: &mut MaybeUninit<_> = unsafe { &mut storage.params };
201 params.store(&mut store, ty, dst)?;
202 }
203
204 let mut captures = (func, storage);
210
211 let result = invoke_wasm_and_catch_traps(store, uncaught_exception, |caller, vm| {
212 let (func_ref, storage) = &mut captures;
213 let storage_len = mem::size_of_val::<Storage<_, _>>(storage) / mem::size_of::<ValRaw>();
214 let storage: *mut Storage<_, _> = storage;
215 let storage = storage.cast::<ValRaw>();
216 let storage = core::ptr::slice_from_raw_parts_mut(storage, storage_len);
217 let storage = NonNull::new(storage).unwrap();
218
219 unsafe { VMFuncRef::array_call(*func_ref, vm, caller, storage) }
223 });
224
225 let (_, storage) = captures;
226 result?;
227
228 let mut store = AutoAssertNoGc::new(store.0);
229 unsafe { Ok(Results::load(&mut store, &storage.results)) }
234 }
235
236 fn debug_typecheck(store: &StoreOpaque, func: VMSharedTypeIndex) {
238 let ty = FuncType::from_shared_type_index(store.engine(), func).unwrap();
239 Params::typecheck(store.engine(), ty.params(), TypeCheckPosition::Param)
240 .expect("params should match");
241 Results::typecheck(store.engine(), ty.results(), TypeCheckPosition::Result)
242 .expect("results should match");
243 }
244}
245
246#[doc(hidden)]
247#[derive(Copy, Clone)]
248pub enum TypeCheckPosition {
249 Param,
250 Result,
251}
252
253pub unsafe trait WasmTy: Send {
262 #[doc(hidden)]
266 #[inline]
267 fn typecheck(engine: &Engine, actual: ValType, position: TypeCheckPosition) -> Result<()> {
268 let expected = Self::valtype();
269 debug_assert!(expected.comes_from_same_engine(engine));
270 debug_assert!(actual.comes_from_same_engine(engine));
271 match position {
272 TypeCheckPosition::Result => actual.ensure_matches(engine, &expected),
275 TypeCheckPosition::Param => match (expected.as_ref(), actual.as_ref()) {
278 (Some(expected_ref), Some(actual_ref)) if actual_ref.heap_type().is_concrete() => {
308 let expected_top = HeapType::from(expected_ref.heap_type().top());
309 let actual_top = HeapType::from(actual_ref.heap_type().top());
310 expected_top.ensure_matches(engine, &actual_top)
311 }
312 _ => expected.ensure_matches(engine, &actual),
313 },
314 }
315 }
316
317 #[doc(hidden)]
319 fn valtype() -> ValType;
320
321 #[doc(hidden)]
322 fn may_gc() -> bool {
323 match Self::valtype() {
324 ValType::Ref(_) => true,
325 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => false,
326 }
327 }
328
329 #[doc(hidden)]
332 fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
333
334 #[doc(hidden)]
343 fn dynamic_concrete_type_check(
344 &self,
345 store: &StoreOpaque,
346 nullable: bool,
347 actual: &HeapType,
348 ) -> Result<()>;
349
350 #[doc(hidden)]
358 #[inline]
359 fn is_vmgcref_and_points_to_object(&self) -> bool {
360 Self::valtype().is_vmgcref_type_and_points_to_object()
361 }
362
363 #[doc(hidden)]
392 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>;
393
394 #[doc(hidden)]
401 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self;
402}
403
404macro_rules! integers {
405 ($($primitive:ident/$get_primitive:ident => $ty:ident)*) => ($(
406 unsafe impl WasmTy for $primitive {
407 #[inline]
408 fn valtype() -> ValType {
409 ValType::$ty
410 }
411 #[inline]
412 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
413 true
414 }
415 #[inline]
416 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
417 unreachable!()
418 }
419 #[inline]
420 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
421 ptr.write(ValRaw::$primitive(self));
422 Ok(())
423 }
424 #[inline]
425 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
426 ptr.$get_primitive()
427 }
428 }
429 )*)
430}
431
432integers! {
433 i32/get_i32 => I32
434 i64/get_i64 => I64
435 u32/get_u32 => I32
436 u64/get_u64 => I64
437}
438
439macro_rules! floats {
440 ($($float:ident/$int:ident/$get_float:ident => $ty:ident)*) => ($(
441 unsafe impl WasmTy for $float {
442 #[inline]
443 fn valtype() -> ValType {
444 ValType::$ty
445 }
446 #[inline]
447 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
448 true
449 }
450 #[inline]
451 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
452 unreachable!()
453 }
454 #[inline]
455 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
456 ptr.write(ValRaw::$float(self.to_bits()));
457 Ok(())
458 }
459 #[inline]
460 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
461 $float::from_bits(ptr.$get_float())
462 }
463 }
464 )*)
465}
466
467floats! {
468 f32/u32/get_f32 => F32
469 f64/u64/get_f64 => F64
470}
471
472unsafe impl WasmTy for NoFunc {
473 #[inline]
474 fn valtype() -> ValType {
475 ValType::Ref(RefType::new(false, HeapType::NoFunc))
476 }
477
478 #[inline]
479 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
480 match self._inner {}
481 }
482
483 #[inline]
484 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
485 match self._inner {}
486 }
487
488 #[inline]
489 fn is_vmgcref_and_points_to_object(&self) -> bool {
490 match self._inner {}
491 }
492
493 #[inline]
494 fn store(self, _store: &mut AutoAssertNoGc<'_>, _ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
495 match self._inner {}
496 }
497
498 #[inline]
499 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
500 unreachable!("NoFunc is uninhabited")
501 }
502}
503
504unsafe impl WasmTy for Option<NoFunc> {
505 #[inline]
506 fn valtype() -> ValType {
507 ValType::Ref(RefType::new(true, HeapType::NoFunc))
508 }
509
510 #[inline]
511 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
512 true
513 }
514
515 #[inline]
516 fn dynamic_concrete_type_check(
517 &self,
518 _: &StoreOpaque,
519 nullable: bool,
520 ty: &HeapType,
521 ) -> Result<()> {
522 if nullable {
523 Ok(())
525 } else {
526 bail!("argument type mismatch: expected non-nullable (ref {ty}), found null reference")
527 }
528 }
529
530 #[inline]
531 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
532 ptr.write(ValRaw::funcref(ptr::null_mut()));
533 Ok(())
534 }
535
536 #[inline]
537 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
538 None
539 }
540}
541
542unsafe impl WasmTy for Func {
543 #[inline]
544 fn valtype() -> ValType {
545 ValType::Ref(RefType::new(false, HeapType::Func))
546 }
547
548 #[inline]
549 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
550 self.store == store.id()
551 }
552
553 #[inline]
554 fn dynamic_concrete_type_check(
555 &self,
556 store: &StoreOpaque,
557 _nullable: bool,
558 expected: &HeapType,
559 ) -> Result<()> {
560 let expected = expected.unwrap_concrete_func();
561 self.ensure_matches_ty(store, expected)
562 .context("argument type mismatch for reference to concrete type")
563 }
564
565 #[inline]
566 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
567 let abi = self.vm_func_ref(store);
568 ptr.write(ValRaw::funcref(abi.cast::<c_void>().as_ptr()));
569 Ok(())
570 }
571
572 #[inline]
573 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
574 let p = NonNull::new(ptr.get_funcref()).unwrap().cast();
575
576 unsafe { Func::from_vm_func_ref(store.id(), p) }
579 }
580}
581
582unsafe impl WasmTy for Option<Func> {
583 #[inline]
584 fn valtype() -> ValType {
585 ValType::FUNCREF
586 }
587
588 #[inline]
589 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
590 if let Some(f) = self {
591 f.compatible_with_store(store)
592 } else {
593 true
594 }
595 }
596
597 fn dynamic_concrete_type_check(
598 &self,
599 store: &StoreOpaque,
600 nullable: bool,
601 expected: &HeapType,
602 ) -> Result<()> {
603 if let Some(f) = self {
604 let expected = expected.unwrap_concrete_func();
605 f.ensure_matches_ty(store, expected)
606 .context("argument type mismatch for reference to concrete type")
607 } else if nullable {
608 Ok(())
609 } else {
610 bail!(
611 "argument type mismatch: expected non-nullable (ref {expected}), found null reference"
612 )
613 }
614 }
615
616 #[inline]
617 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
618 let raw = if let Some(f) = self {
619 f.vm_func_ref(store).as_ptr()
620 } else {
621 ptr::null_mut()
622 };
623 ptr.write(ValRaw::funcref(raw.cast::<c_void>()));
624 Ok(())
625 }
626
627 #[inline]
628 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
629 let ptr = NonNull::new(ptr.get_funcref())?.cast();
630
631 unsafe { Some(Func::from_vm_func_ref(store.id(), ptr)) }
634 }
635}
636
637pub unsafe trait WasmParams: Send {
643 #[doc(hidden)]
644 type ValRawStorage: Copy;
645
646 #[doc(hidden)]
647 fn typecheck(
648 engine: &Engine,
649 params: impl ExactSizeIterator<Item = crate::ValType>,
650 position: TypeCheckPosition,
651 ) -> Result<()>;
652
653 #[doc(hidden)]
654 fn vmgcref_pointing_to_object_count(&self) -> usize;
655
656 #[doc(hidden)]
657 fn store(
658 self,
659 store: &mut AutoAssertNoGc<'_>,
660 func_ty: &FuncType,
661 dst: &mut MaybeUninit<Self::ValRawStorage>,
662 ) -> Result<()>;
663}
664
665unsafe impl<T> WasmParams for T
668where
669 T: WasmTy,
670{
671 type ValRawStorage = <(T,) as WasmParams>::ValRawStorage;
672
673 fn typecheck(
674 engine: &Engine,
675 params: impl ExactSizeIterator<Item = crate::ValType>,
676 position: TypeCheckPosition,
677 ) -> Result<()> {
678 <(T,) as WasmParams>::typecheck(engine, params, position)
679 }
680
681 #[inline]
682 fn vmgcref_pointing_to_object_count(&self) -> usize {
683 T::is_vmgcref_and_points_to_object(self) as usize
684 }
685
686 #[inline]
687 fn store(
688 self,
689 store: &mut AutoAssertNoGc<'_>,
690 func_ty: &FuncType,
691 dst: &mut MaybeUninit<Self::ValRawStorage>,
692 ) -> Result<()> {
693 <(T,) as WasmParams>::store((self,), store, func_ty, dst)
694 }
695}
696
697macro_rules! impl_wasm_params {
698 ($n:tt $($t:ident)*) => {
699 #[allow(non_snake_case, reason = "macro-generated code")]
700 unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) {
701 type ValRawStorage = [ValRaw; $n];
702
703 fn typecheck(
704 _engine: &Engine,
705 mut params: impl ExactSizeIterator<Item = crate::ValType>,
706 _position: TypeCheckPosition,
707 ) -> Result<()> {
708 let mut _n = 0;
709
710 $(
711 match params.next() {
712 Some(t) => {
713 _n += 1;
714 $t::typecheck(_engine, t, _position)?
715 },
716 None => bail!("expected {} types, found {}", $n, params.len() + _n),
717 }
718 )*
719
720 match params.next() {
721 None => Ok(()),
722 Some(_) => {
723 _n += 1;
724 bail!("expected {} types, found {}", $n, params.len() + _n)
725 },
726 }
727 }
728
729 #[inline]
730 fn vmgcref_pointing_to_object_count(&self) -> usize {
731 let ($($t,)*) = self;
732 0 $(
733 + $t.is_vmgcref_and_points_to_object() as usize
734 )*
735 }
736
737
738 #[inline]
739 fn store(
740 self,
741 _store: &mut AutoAssertNoGc<'_>,
742 _func_ty: &FuncType,
743 _ptr: &mut MaybeUninit<Self::ValRawStorage>,
744 ) -> Result<()> {
745 let ($($t,)*) = self;
746
747 let mut _i = 0;
748 $(
749 if !$t.compatible_with_store(_store) {
750 bail!("attempt to pass cross-`Store` value to Wasm as function argument");
751 }
752
753 if $t::valtype().is_ref() {
754 let param_ty = _func_ty.param(_i).unwrap();
755 let ref_ty = param_ty.unwrap_ref();
756 let heap_ty = ref_ty.heap_type();
757 if heap_ty.is_concrete() {
758 $t.dynamic_concrete_type_check(_store, ref_ty.is_nullable(), heap_ty)?;
759 }
760 }
761
762 let dst = map_maybe_uninit!(_ptr[_i]);
763 $t.store(_store, dst)?;
764
765 _i += 1;
766 )*
767 Ok(())
768 }
769 }
770 };
771}
772
773for_each_function_signature!(impl_wasm_params);
774
775pub unsafe trait WasmResults: WasmParams {
778 #[doc(hidden)]
779 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self;
780}
781
782unsafe impl<T: WasmTy> WasmResults for T {
784 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
785 unsafe { <(T,) as WasmResults>::load(store, abi).0 }
788 }
789}
790
791macro_rules! impl_wasm_results {
792 ($n:tt $($t:ident)*) => {
793 #[allow(non_snake_case, reason = "macro-generated code")]
794 unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*) {
795 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
796 let [$($t,)*] = abi;
797
798 (
799 $(unsafe { $t::load(_store, $t) },)*
802 )
803 }
804 }
805 };
806}
807
808for_each_function_signature!(impl_wasm_results);