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]
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 #[cfg(feature = "async")]
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 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 unsafe {
158 if cfg!(debug_assertions) {
159 Self::debug_typecheck(store.0, func.as_ref().type_index);
160 }
161 }
162
163 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 let dst: &mut MaybeUninit<_> = unsafe { &mut storage.params };
182 params.store(&mut store, ty, dst)?;
183 }
184
185 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 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 unsafe { Ok(Results::load(&mut store, &storage.results)) }
215 }
216
217 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)]
229pub enum TypeCheckPosition {
230 Param,
231 Result,
232}
233
234pub unsafe trait WasmTy: Send {
243 #[doc(hidden)]
247 #[inline]
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 TypeCheckPosition::Result => actual.ensure_matches(engine, &expected),
256 TypeCheckPosition::Param => match (expected.as_ref(), actual.as_ref()) {
259 (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 #[doc(hidden)]
301 fn valtype() -> ValType;
302
303 #[doc(hidden)]
304 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 #[doc(hidden)]
314 fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
315
316 #[doc(hidden)]
323 fn dynamic_concrete_type_check(
324 &self,
325 store: &StoreOpaque,
326 nullable: bool,
327 actual: &HeapType,
328 ) -> Result<()>;
329
330 #[doc(hidden)]
338 #[inline]
339 fn is_vmgcref_and_points_to_object(&self) -> bool {
340 Self::valtype().is_vmgcref_type_and_points_to_object()
341 }
342
343 #[doc(hidden)]
372 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>;
373
374 #[doc(hidden)]
381 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self;
382}
383
384macro_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
412integers! {
413 i32/get_i32 => I32
414 i64/get_i64 => I64
415 u32/get_u32 => I32
416 u64/get_u64 => I64
417}
418
419macro_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
447floats! {
448 f32/u32/get_f32 => F32
449 f64/u64/get_f64 => F64
450}
451
452unsafe impl WasmTy for NoFunc {
453 #[inline]
454 fn valtype() -> ValType {
455 ValType::Ref(RefType::new(false, HeapType::NoFunc))
456 }
457
458 #[inline]
459 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
460 match self._inner {}
461 }
462
463 #[inline]
464 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
465 match self._inner {}
466 }
467
468 #[inline]
469 fn is_vmgcref_and_points_to_object(&self) -> bool {
470 match self._inner {}
471 }
472
473 #[inline]
474 fn store(self, _store: &mut AutoAssertNoGc<'_>, _ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
475 match self._inner {}
476 }
477
478 #[inline]
479 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
480 unreachable!("NoFunc is uninhabited")
481 }
482}
483
484unsafe impl WasmTy for Option<NoFunc> {
485 #[inline]
486 fn valtype() -> ValType {
487 ValType::Ref(RefType::new(true, HeapType::NoFunc))
488 }
489
490 #[inline]
491 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
492 true
493 }
494
495 #[inline]
496 fn dynamic_concrete_type_check(
497 &self,
498 _: &StoreOpaque,
499 nullable: bool,
500 ty: &HeapType,
501 ) -> Result<()> {
502 if nullable {
503 Ok(())
505 } else {
506 bail!("argument type mismatch: expected non-nullable (ref {ty}), found null reference")
507 }
508 }
509
510 #[inline]
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]
517 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
518 None
519 }
520}
521
522unsafe impl WasmTy for Func {
523 #[inline]
524 fn valtype() -> ValType {
525 ValType::Ref(RefType::new(false, HeapType::Func))
526 }
527
528 #[inline]
529 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
530 self.store == store.id()
531 }
532
533 #[inline]
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]
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]
553 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
554 let p = NonNull::new(ptr.get_funcref()).unwrap().cast();
555
556 unsafe { Func::from_vm_func_ref(store.id(), p) }
559 }
560}
561
562unsafe impl WasmTy for Option<Func> {
563 #[inline]
564 fn valtype() -> ValType {
565 ValType::FUNCREF
566 }
567
568 #[inline]
569 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
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]
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]
608 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
609 let ptr = NonNull::new(ptr.get_funcref())?.cast();
610
611 unsafe { Some(Func::from_vm_func_ref(store.id(), ptr)) }
614 }
615}
616
617pub unsafe trait WasmParams: Send {
623 #[doc(hidden)]
624 type ValRawStorage: Copy;
625
626 #[doc(hidden)]
627 fn typecheck(
628 engine: &Engine,
629 params: impl ExactSizeIterator<Item = crate::ValType>,
630 position: TypeCheckPosition,
631 ) -> Result<()>;
632
633 #[doc(hidden)]
634 fn vmgcref_pointing_to_object_count(&self) -> usize;
635
636 #[doc(hidden)]
637 fn store(
638 self,
639 store: &mut AutoAssertNoGc<'_>,
640 func_ty: &FuncType,
641 dst: &mut MaybeUninit<Self::ValRawStorage>,
642 ) -> Result<()>;
643}
644
645unsafe impl<T> WasmParams for T
648where
649 T: WasmTy,
650{
651 type ValRawStorage = <(T,) as WasmParams>::ValRawStorage;
652
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]
662 fn vmgcref_pointing_to_object_count(&self) -> usize {
663 T::is_vmgcref_and_points_to_object(self) as usize
664 }
665
666 #[inline]
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
677macro_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
753for_each_function_signature!(impl_wasm_params);
754
755pub unsafe trait WasmResults: WasmParams {
758 #[doc(hidden)]
759 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self;
760}
761
762unsafe impl<T: WasmTy> WasmResults for T {
764 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
765 unsafe { <(T,) as WasmResults>::load(store, abi).0 }
768 }
769}
770
771macro_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 $(unsafe { $t::load(_store, $t) },)*
782 )
783 }
784 }
785 };
786}
787
788for_each_function_signature!(impl_wasm_results);