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 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]
97 pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results> {
98 let mut store = store.as_context_mut();
99 assert!(
100 !store.0.async_support(),
101 "must use `call_async` with async stores"
102 );
103
104 let func = self.func.vm_func_ref(store.0);
105 unsafe { Self::call_raw(&mut store, &self.ty, func, params) }
106 }
107
108 #[cfg(feature = "async")]
126 pub async fn call_async(
127 &self,
128 mut store: impl AsContextMut<Data: Send>,
129 params: Params,
130 ) -> Result<Results>
131 where
132 Params: Sync,
133 Results: Sync,
134 {
135 let mut store = store.as_context_mut();
136 assert!(
137 store.0.async_support(),
138 "must use `call` with non-async stores"
139 );
140
141 store
142 .on_fiber(|store| {
143 let func = self.func.vm_func_ref(store.0);
144 unsafe { Self::call_raw(store, &self.ty, func, params) }
145 })
146 .await?
147 }
148
149 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 if cfg!(debug_assertions) {
163 Self::debug_typecheck(store.0, func.as_ref().type_index);
164 }
165
166 union Storage<T: Copy, U: Copy> {
171 params: MaybeUninit<T>,
172 results: U,
173 }
174
175 let mut storage = Storage::<Params::ValRawStorage, Results::ValRawStorage> {
176 params: MaybeUninit::uninit(),
177 };
178
179 {
180 let mut store = AutoAssertNoGc::new(store.0);
181 params.store(&mut store, ty, &mut storage.params)?;
182 }
183
184 let mut captures = (func, storage);
190
191 let result = invoke_wasm_and_catch_traps(store, |caller, vm| {
192 let (func_ref, storage) = &mut captures;
193 let storage_len = mem::size_of_val::<Storage<_, _>>(storage) / mem::size_of::<ValRaw>();
194 let storage: *mut Storage<_, _> = storage;
195 let storage = storage.cast::<ValRaw>();
196 let storage = core::ptr::slice_from_raw_parts_mut(storage, storage_len);
197 let storage = NonNull::new(storage).unwrap();
198 VMFuncRef::array_call(*func_ref, vm, caller, storage)
199 });
200
201 let (_, storage) = captures;
202 result?;
203
204 let mut store = AutoAssertNoGc::new(store.0);
205 Ok(Results::load(&mut store, &storage.results))
206 }
207
208 fn debug_typecheck(store: &StoreOpaque, func: VMSharedTypeIndex) {
210 let ty = FuncType::from_shared_type_index(store.engine(), func);
211 Params::typecheck(store.engine(), ty.params(), TypeCheckPosition::Param)
212 .expect("params should match");
213 Results::typecheck(store.engine(), ty.results(), TypeCheckPosition::Result)
214 .expect("results should match");
215 }
216}
217
218#[doc(hidden)]
219#[derive(Copy, Clone)]
220pub enum TypeCheckPosition {
221 Param,
222 Result,
223}
224
225pub unsafe trait WasmTy: Send {
234 #[doc(hidden)]
238 #[inline]
239 fn typecheck(engine: &Engine, actual: ValType, position: TypeCheckPosition) -> Result<()> {
240 let expected = Self::valtype();
241 debug_assert!(expected.comes_from_same_engine(engine));
242 debug_assert!(actual.comes_from_same_engine(engine));
243 match position {
244 TypeCheckPosition::Result => actual.ensure_matches(engine, &expected),
247 TypeCheckPosition::Param => match (expected.as_ref(), actual.as_ref()) {
250 (Some(expected_ref), Some(actual_ref)) if actual_ref.heap_type().is_concrete() => {
280 expected_ref
281 .heap_type()
282 .top()
283 .ensure_matches(engine, &actual_ref.heap_type().top())
284 }
285 _ => expected.ensure_matches(engine, &actual),
286 },
287 }
288 }
289
290 #[doc(hidden)]
292 fn valtype() -> ValType;
293
294 #[doc(hidden)]
295 fn may_gc() -> bool {
296 match Self::valtype() {
297 ValType::Ref(_) => true,
298 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => false,
299 }
300 }
301
302 #[doc(hidden)]
305 fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
306
307 #[doc(hidden)]
314 fn dynamic_concrete_type_check(
315 &self,
316 store: &StoreOpaque,
317 nullable: bool,
318 actual: &HeapType,
319 ) -> Result<()>;
320
321 #[doc(hidden)]
329 #[inline]
330 fn is_vmgcref_and_points_to_object(&self) -> bool {
331 Self::valtype().is_vmgcref_type_and_points_to_object()
332 }
333
334 #[doc(hidden)]
363 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>;
364
365 #[doc(hidden)]
372 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self;
373}
374
375macro_rules! integers {
376 ($($primitive:ident/$get_primitive:ident => $ty:ident)*) => ($(
377 unsafe impl WasmTy for $primitive {
378 #[inline]
379 fn valtype() -> ValType {
380 ValType::$ty
381 }
382 #[inline]
383 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
384 true
385 }
386 #[inline]
387 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
388 unreachable!()
389 }
390 #[inline]
391 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
392 ptr.write(ValRaw::$primitive(self));
393 Ok(())
394 }
395 #[inline]
396 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
397 ptr.$get_primitive()
398 }
399 }
400 )*)
401}
402
403integers! {
404 i32/get_i32 => I32
405 i64/get_i64 => I64
406 u32/get_u32 => I32
407 u64/get_u64 => I64
408}
409
410macro_rules! floats {
411 ($($float:ident/$int:ident/$get_float:ident => $ty:ident)*) => ($(
412 unsafe impl WasmTy for $float {
413 #[inline]
414 fn valtype() -> ValType {
415 ValType::$ty
416 }
417 #[inline]
418 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
419 true
420 }
421 #[inline]
422 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
423 unreachable!()
424 }
425 #[inline]
426 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
427 ptr.write(ValRaw::$float(self.to_bits()));
428 Ok(())
429 }
430 #[inline]
431 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
432 $float::from_bits(ptr.$get_float())
433 }
434 }
435 )*)
436}
437
438floats! {
439 f32/u32/get_f32 => F32
440 f64/u64/get_f64 => F64
441}
442
443unsafe impl WasmTy for NoFunc {
444 #[inline]
445 fn valtype() -> ValType {
446 ValType::Ref(RefType::new(false, HeapType::NoFunc))
447 }
448
449 #[inline]
450 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
451 match self._inner {}
452 }
453
454 #[inline]
455 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
456 match self._inner {}
457 }
458
459 #[inline]
460 fn is_vmgcref_and_points_to_object(&self) -> bool {
461 match self._inner {}
462 }
463
464 #[inline]
465 fn store(self, _store: &mut AutoAssertNoGc<'_>, _ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
466 match self._inner {}
467 }
468
469 #[inline]
470 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
471 unreachable!("NoFunc is uninhabited")
472 }
473}
474
475unsafe impl WasmTy for Option<NoFunc> {
476 #[inline]
477 fn valtype() -> ValType {
478 ValType::Ref(RefType::new(true, HeapType::NoFunc))
479 }
480
481 #[inline]
482 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
483 true
484 }
485
486 #[inline]
487 fn dynamic_concrete_type_check(
488 &self,
489 _: &StoreOpaque,
490 nullable: bool,
491 ty: &HeapType,
492 ) -> Result<()> {
493 if nullable {
494 Ok(())
496 } else {
497 bail!("argument type mismatch: expected non-nullable (ref {ty}), found null reference")
498 }
499 }
500
501 #[inline]
502 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
503 ptr.write(ValRaw::funcref(ptr::null_mut()));
504 Ok(())
505 }
506
507 #[inline]
508 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
509 None
510 }
511}
512
513unsafe impl WasmTy for Func {
514 #[inline]
515 fn valtype() -> ValType {
516 ValType::Ref(RefType::new(false, HeapType::Func))
517 }
518
519 #[inline]
520 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
521 self.store == store.id()
522 }
523
524 #[inline]
525 fn dynamic_concrete_type_check(
526 &self,
527 store: &StoreOpaque,
528 _nullable: bool,
529 expected: &HeapType,
530 ) -> Result<()> {
531 let expected = expected.unwrap_concrete_func();
532 self.ensure_matches_ty(store, expected)
533 .context("argument type mismatch for reference to concrete type")
534 }
535
536 #[inline]
537 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
538 let abi = self.vm_func_ref(store);
539 ptr.write(ValRaw::funcref(abi.cast::<c_void>().as_ptr()));
540 Ok(())
541 }
542
543 #[inline]
544 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
545 let p = NonNull::new(ptr.get_funcref()).unwrap().cast();
546 Func::from_vm_func_ref(store, p)
547 }
548}
549
550unsafe impl WasmTy for Option<Func> {
551 #[inline]
552 fn valtype() -> ValType {
553 ValType::FUNCREF
554 }
555
556 #[inline]
557 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
558 if let Some(f) = self {
559 f.compatible_with_store(store)
560 } else {
561 true
562 }
563 }
564
565 fn dynamic_concrete_type_check(
566 &self,
567 store: &StoreOpaque,
568 nullable: bool,
569 expected: &HeapType,
570 ) -> Result<()> {
571 if let Some(f) = self {
572 let expected = expected.unwrap_concrete_func();
573 f.ensure_matches_ty(store, expected)
574 .context("argument type mismatch for reference to concrete type")
575 } else if nullable {
576 Ok(())
577 } else {
578 bail!(
579 "argument type mismatch: expected non-nullable (ref {expected}), found null reference"
580 )
581 }
582 }
583
584 #[inline]
585 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
586 let raw = if let Some(f) = self {
587 f.vm_func_ref(store).as_ptr()
588 } else {
589 ptr::null_mut()
590 };
591 ptr.write(ValRaw::funcref(raw.cast::<c_void>()));
592 Ok(())
593 }
594
595 #[inline]
596 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
597 let ptr = NonNull::new(ptr.get_funcref())?.cast();
598 Some(Func::from_vm_func_ref(store, ptr))
599 }
600}
601
602pub unsafe trait WasmParams: Send {
608 #[doc(hidden)]
609 type ValRawStorage: Copy;
610
611 #[doc(hidden)]
612 fn typecheck(
613 engine: &Engine,
614 params: impl ExactSizeIterator<Item = crate::ValType>,
615 position: TypeCheckPosition,
616 ) -> Result<()>;
617
618 #[doc(hidden)]
619 fn vmgcref_pointing_to_object_count(&self) -> usize;
620
621 #[doc(hidden)]
622 fn store(
623 self,
624 store: &mut AutoAssertNoGc<'_>,
625 func_ty: &FuncType,
626 dst: &mut MaybeUninit<Self::ValRawStorage>,
627 ) -> Result<()>;
628}
629
630unsafe impl<T> WasmParams for T
633where
634 T: WasmTy,
635{
636 type ValRawStorage = <(T,) as WasmParams>::ValRawStorage;
637
638 fn typecheck(
639 engine: &Engine,
640 params: impl ExactSizeIterator<Item = crate::ValType>,
641 position: TypeCheckPosition,
642 ) -> Result<()> {
643 <(T,) as WasmParams>::typecheck(engine, params, position)
644 }
645
646 #[inline]
647 fn vmgcref_pointing_to_object_count(&self) -> usize {
648 T::is_vmgcref_and_points_to_object(self) as usize
649 }
650
651 #[inline]
652 fn store(
653 self,
654 store: &mut AutoAssertNoGc<'_>,
655 func_ty: &FuncType,
656 dst: &mut MaybeUninit<Self::ValRawStorage>,
657 ) -> Result<()> {
658 <(T,) as WasmParams>::store((self,), store, func_ty, dst)
659 }
660}
661
662macro_rules! impl_wasm_params {
663 ($n:tt $($t:ident)*) => {
664 #[allow(non_snake_case, reason = "macro-generated code")]
665 unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) {
666 type ValRawStorage = [ValRaw; $n];
667
668 fn typecheck(
669 _engine: &Engine,
670 mut params: impl ExactSizeIterator<Item = crate::ValType>,
671 _position: TypeCheckPosition,
672 ) -> Result<()> {
673 let mut _n = 0;
674
675 $(
676 match params.next() {
677 Some(t) => {
678 _n += 1;
679 $t::typecheck(_engine, t, _position)?
680 },
681 None => bail!("expected {} types, found {}", $n, params.len() + _n),
682 }
683 )*
684
685 match params.next() {
686 None => Ok(()),
687 Some(_) => {
688 _n += 1;
689 bail!("expected {} types, found {}", $n, params.len() + _n)
690 },
691 }
692 }
693
694 #[inline]
695 fn vmgcref_pointing_to_object_count(&self) -> usize {
696 let ($($t,)*) = self;
697 0 $(
698 + $t.is_vmgcref_and_points_to_object() as usize
699 )*
700 }
701
702
703 #[inline]
704 fn store(
705 self,
706 _store: &mut AutoAssertNoGc<'_>,
707 _func_ty: &FuncType,
708 _ptr: &mut MaybeUninit<Self::ValRawStorage>,
709 ) -> Result<()> {
710 let ($($t,)*) = self;
711
712 let mut _i = 0;
713 $(
714 if !$t.compatible_with_store(_store) {
715 bail!("attempt to pass cross-`Store` value to Wasm as function argument");
716 }
717
718 if $t::valtype().is_ref() {
719 let param_ty = _func_ty.param(_i).unwrap();
720 let ref_ty = param_ty.unwrap_ref();
721 let heap_ty = ref_ty.heap_type();
722 if heap_ty.is_concrete() {
723 $t.dynamic_concrete_type_check(_store, ref_ty.is_nullable(), heap_ty)?;
724 }
725 }
726
727 let dst = map_maybe_uninit!(_ptr[_i]);
728 $t.store(_store, dst)?;
729
730 _i += 1;
731 )*
732 Ok(())
733 }
734 }
735 };
736}
737
738for_each_function_signature!(impl_wasm_params);
739
740pub unsafe trait WasmResults: WasmParams {
743 #[doc(hidden)]
744 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self;
745}
746
747unsafe impl<T: WasmTy> WasmResults for T {
749 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
750 <(T,) as WasmResults>::load(store, abi).0
751 }
752}
753
754macro_rules! impl_wasm_results {
755 ($n:tt $($t:ident)*) => {
756 #[allow(non_snake_case, reason = "macro-generated code")]
757 unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*) {
758 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
759 let [$($t,)*] = abi;
760 ($($t::load(_store, $t),)*)
761 }
762 }
763 };
764}
765
766for_each_function_signature!(impl_wasm_results);