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 expected_ref
298 .heap_type()
299 .top()
300 .ensure_matches(engine, &actual_ref.heap_type().top())
301 }
302 _ => expected.ensure_matches(engine, &actual),
303 },
304 }
305 }
306
307 #[doc(hidden)]
309 fn valtype() -> ValType;
310
311 #[doc(hidden)]
312 fn may_gc() -> bool {
313 match Self::valtype() {
314 ValType::Ref(_) => true,
315 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => false,
316 }
317 }
318
319 #[doc(hidden)]
322 fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
323
324 #[doc(hidden)]
331 fn dynamic_concrete_type_check(
332 &self,
333 store: &StoreOpaque,
334 nullable: bool,
335 actual: &HeapType,
336 ) -> Result<()>;
337
338 #[doc(hidden)]
346 #[inline]
347 fn is_vmgcref_and_points_to_object(&self) -> bool {
348 Self::valtype().is_vmgcref_type_and_points_to_object()
349 }
350
351 #[doc(hidden)]
380 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>;
381
382 #[doc(hidden)]
389 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self;
390}
391
392macro_rules! integers {
393 ($($primitive:ident/$get_primitive:ident => $ty:ident)*) => ($(
394 unsafe impl WasmTy for $primitive {
395 #[inline]
396 fn valtype() -> ValType {
397 ValType::$ty
398 }
399 #[inline]
400 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
401 true
402 }
403 #[inline]
404 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
405 unreachable!()
406 }
407 #[inline]
408 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
409 ptr.write(ValRaw::$primitive(self));
410 Ok(())
411 }
412 #[inline]
413 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
414 ptr.$get_primitive()
415 }
416 }
417 )*)
418}
419
420integers! {
421 i32/get_i32 => I32
422 i64/get_i64 => I64
423 u32/get_u32 => I32
424 u64/get_u64 => I64
425}
426
427macro_rules! floats {
428 ($($float:ident/$int:ident/$get_float:ident => $ty:ident)*) => ($(
429 unsafe impl WasmTy for $float {
430 #[inline]
431 fn valtype() -> ValType {
432 ValType::$ty
433 }
434 #[inline]
435 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
436 true
437 }
438 #[inline]
439 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
440 unreachable!()
441 }
442 #[inline]
443 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
444 ptr.write(ValRaw::$float(self.to_bits()));
445 Ok(())
446 }
447 #[inline]
448 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
449 $float::from_bits(ptr.$get_float())
450 }
451 }
452 )*)
453}
454
455floats! {
456 f32/u32/get_f32 => F32
457 f64/u64/get_f64 => F64
458}
459
460unsafe impl WasmTy for NoFunc {
461 #[inline]
462 fn valtype() -> ValType {
463 ValType::Ref(RefType::new(false, HeapType::NoFunc))
464 }
465
466 #[inline]
467 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
468 match self._inner {}
469 }
470
471 #[inline]
472 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
473 match self._inner {}
474 }
475
476 #[inline]
477 fn is_vmgcref_and_points_to_object(&self) -> bool {
478 match self._inner {}
479 }
480
481 #[inline]
482 fn store(self, _store: &mut AutoAssertNoGc<'_>, _ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
483 match self._inner {}
484 }
485
486 #[inline]
487 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
488 unreachable!("NoFunc is uninhabited")
489 }
490}
491
492unsafe impl WasmTy for Option<NoFunc> {
493 #[inline]
494 fn valtype() -> ValType {
495 ValType::Ref(RefType::new(true, HeapType::NoFunc))
496 }
497
498 #[inline]
499 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
500 true
501 }
502
503 #[inline]
504 fn dynamic_concrete_type_check(
505 &self,
506 _: &StoreOpaque,
507 nullable: bool,
508 ty: &HeapType,
509 ) -> Result<()> {
510 if nullable {
511 Ok(())
513 } else {
514 bail!("argument type mismatch: expected non-nullable (ref {ty}), found null reference")
515 }
516 }
517
518 #[inline]
519 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
520 ptr.write(ValRaw::funcref(ptr::null_mut()));
521 Ok(())
522 }
523
524 #[inline]
525 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
526 None
527 }
528}
529
530unsafe impl WasmTy for Func {
531 #[inline]
532 fn valtype() -> ValType {
533 ValType::Ref(RefType::new(false, HeapType::Func))
534 }
535
536 #[inline]
537 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
538 self.store == store.id()
539 }
540
541 #[inline]
542 fn dynamic_concrete_type_check(
543 &self,
544 store: &StoreOpaque,
545 _nullable: bool,
546 expected: &HeapType,
547 ) -> Result<()> {
548 let expected = expected.unwrap_concrete_func();
549 self.ensure_matches_ty(store, expected)
550 .context("argument type mismatch for reference to concrete type")
551 }
552
553 #[inline]
554 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
555 let abi = self.vm_func_ref(store);
556 ptr.write(ValRaw::funcref(abi.cast::<c_void>().as_ptr()));
557 Ok(())
558 }
559
560 #[inline]
561 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
562 let p = NonNull::new(ptr.get_funcref()).unwrap().cast();
563
564 unsafe { Func::from_vm_func_ref(store.id(), p) }
567 }
568}
569
570unsafe impl WasmTy for Option<Func> {
571 #[inline]
572 fn valtype() -> ValType {
573 ValType::FUNCREF
574 }
575
576 #[inline]
577 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
578 if let Some(f) = self {
579 f.compatible_with_store(store)
580 } else {
581 true
582 }
583 }
584
585 fn dynamic_concrete_type_check(
586 &self,
587 store: &StoreOpaque,
588 nullable: bool,
589 expected: &HeapType,
590 ) -> Result<()> {
591 if let Some(f) = self {
592 let expected = expected.unwrap_concrete_func();
593 f.ensure_matches_ty(store, expected)
594 .context("argument type mismatch for reference to concrete type")
595 } else if nullable {
596 Ok(())
597 } else {
598 bail!(
599 "argument type mismatch: expected non-nullable (ref {expected}), found null reference"
600 )
601 }
602 }
603
604 #[inline]
605 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
606 let raw = if let Some(f) = self {
607 f.vm_func_ref(store).as_ptr()
608 } else {
609 ptr::null_mut()
610 };
611 ptr.write(ValRaw::funcref(raw.cast::<c_void>()));
612 Ok(())
613 }
614
615 #[inline]
616 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
617 let ptr = NonNull::new(ptr.get_funcref())?.cast();
618
619 unsafe { Some(Func::from_vm_func_ref(store.id(), ptr)) }
622 }
623}
624
625pub unsafe trait WasmParams: Send {
631 #[doc(hidden)]
632 type ValRawStorage: Copy;
633
634 #[doc(hidden)]
635 fn typecheck(
636 engine: &Engine,
637 params: impl ExactSizeIterator<Item = crate::ValType>,
638 position: TypeCheckPosition,
639 ) -> Result<()>;
640
641 #[doc(hidden)]
642 fn vmgcref_pointing_to_object_count(&self) -> usize;
643
644 #[doc(hidden)]
645 fn store(
646 self,
647 store: &mut AutoAssertNoGc<'_>,
648 func_ty: &FuncType,
649 dst: &mut MaybeUninit<Self::ValRawStorage>,
650 ) -> Result<()>;
651}
652
653unsafe impl<T> WasmParams for T
656where
657 T: WasmTy,
658{
659 type ValRawStorage = <(T,) as WasmParams>::ValRawStorage;
660
661 fn typecheck(
662 engine: &Engine,
663 params: impl ExactSizeIterator<Item = crate::ValType>,
664 position: TypeCheckPosition,
665 ) -> Result<()> {
666 <(T,) as WasmParams>::typecheck(engine, params, position)
667 }
668
669 #[inline]
670 fn vmgcref_pointing_to_object_count(&self) -> usize {
671 T::is_vmgcref_and_points_to_object(self) as usize
672 }
673
674 #[inline]
675 fn store(
676 self,
677 store: &mut AutoAssertNoGc<'_>,
678 func_ty: &FuncType,
679 dst: &mut MaybeUninit<Self::ValRawStorage>,
680 ) -> Result<()> {
681 <(T,) as WasmParams>::store((self,), store, func_ty, dst)
682 }
683}
684
685macro_rules! impl_wasm_params {
686 ($n:tt $($t:ident)*) => {
687 #[allow(non_snake_case, reason = "macro-generated code")]
688 unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) {
689 type ValRawStorage = [ValRaw; $n];
690
691 fn typecheck(
692 _engine: &Engine,
693 mut params: impl ExactSizeIterator<Item = crate::ValType>,
694 _position: TypeCheckPosition,
695 ) -> Result<()> {
696 let mut _n = 0;
697
698 $(
699 match params.next() {
700 Some(t) => {
701 _n += 1;
702 $t::typecheck(_engine, t, _position)?
703 },
704 None => bail!("expected {} types, found {}", $n, params.len() + _n),
705 }
706 )*
707
708 match params.next() {
709 None => Ok(()),
710 Some(_) => {
711 _n += 1;
712 bail!("expected {} types, found {}", $n, params.len() + _n)
713 },
714 }
715 }
716
717 #[inline]
718 fn vmgcref_pointing_to_object_count(&self) -> usize {
719 let ($($t,)*) = self;
720 0 $(
721 + $t.is_vmgcref_and_points_to_object() as usize
722 )*
723 }
724
725
726 #[inline]
727 fn store(
728 self,
729 _store: &mut AutoAssertNoGc<'_>,
730 _func_ty: &FuncType,
731 _ptr: &mut MaybeUninit<Self::ValRawStorage>,
732 ) -> Result<()> {
733 let ($($t,)*) = self;
734
735 let mut _i = 0;
736 $(
737 if !$t.compatible_with_store(_store) {
738 bail!("attempt to pass cross-`Store` value to Wasm as function argument");
739 }
740
741 if $t::valtype().is_ref() {
742 let param_ty = _func_ty.param(_i).unwrap();
743 let ref_ty = param_ty.unwrap_ref();
744 let heap_ty = ref_ty.heap_type();
745 if heap_ty.is_concrete() {
746 $t.dynamic_concrete_type_check(_store, ref_ty.is_nullable(), heap_ty)?;
747 }
748 }
749
750 let dst = map_maybe_uninit!(_ptr[_i]);
751 $t.store(_store, dst)?;
752
753 _i += 1;
754 )*
755 Ok(())
756 }
757 }
758 };
759}
760
761for_each_function_signature!(impl_wasm_params);
762
763pub unsafe trait WasmResults: WasmParams {
766 #[doc(hidden)]
767 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self;
768}
769
770unsafe impl<T: WasmTy> WasmResults for T {
772 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
773 unsafe { <(T,) as WasmResults>::load(store, abi).0 }
776 }
777}
778
779macro_rules! impl_wasm_results {
780 ($n:tt $($t:ident)*) => {
781 #[allow(non_snake_case, reason = "macro-generated code")]
782 unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*) {
783 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
784 let [$($t,)*] = abi;
785
786 (
787 $(unsafe { $t::load(_store, $t) },)*
790 )
791 }
792 }
793 };
794}
795
796for_each_function_signature!(impl_wasm_results);