1use super::invoke_wasm_and_catch_traps;
2use crate::prelude::*;
3use crate::runtime::vm::{VMFuncRef, VMOpaqueContext};
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 pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results> {
97 let mut store = store.as_context_mut();
98 assert!(
99 !store.0.async_support(),
100 "must use `call_async` with async stores"
101 );
102 if Self::need_gc_before_call_raw(store.0, ¶ms) {
103 store.0.gc();
104 }
105 let func = self.func.vm_func_ref(store.0);
106 unsafe { Self::call_raw(&mut store, &self.ty, func, params) }
107 }
108
109 #[cfg(feature = "async")]
127 pub async fn call_async<T>(
128 &self,
129 mut store: impl AsContextMut<Data = T>,
130 params: Params,
131 ) -> Result<Results>
132 where
133 T: Send,
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 if Self::need_gc_before_call_raw(store.0, ¶ms) {
141 store.0.gc_async().await;
142 }
143 store
144 .on_fiber(|store| {
145 let func = self.func.vm_func_ref(store.0);
146 unsafe { Self::call_raw(store, &self.ty, func, params) }
147 })
148 .await?
149 }
150
151 #[inline]
152 pub(crate) fn need_gc_before_call_raw(_store: &StoreOpaque, _params: &Params) -> bool {
153 #[cfg(feature = "gc")]
154 {
155 let num_gc_refs = _params.vmgcref_pointing_to_object_count();
157 if let Some(num_gc_refs) = core::num::NonZeroUsize::new(num_gc_refs) {
158 return _store
159 .unwrap_gc_store()
160 .gc_heap
161 .need_gc_before_entering_wasm(num_gc_refs);
162 }
163 }
164
165 false
166 }
167
168 pub(crate) unsafe fn call_raw<T>(
177 store: &mut StoreContextMut<'_, T>,
178 ty: &FuncType,
179 func: ptr::NonNull<VMFuncRef>,
180 params: Params,
181 ) -> Result<Results> {
182 if cfg!(debug_assertions) {
185 Self::debug_typecheck(store.0, func.as_ref().type_index);
186 }
187
188 union Storage<T: Copy, U: Copy> {
193 params: MaybeUninit<T>,
194 results: U,
195 }
196
197 let mut storage = Storage::<Params::ValRawStorage, Results::ValRawStorage> {
198 params: MaybeUninit::uninit(),
199 };
200
201 {
202 let mut store = AutoAssertNoGc::new(store.0);
203 params.store(&mut store, ty, &mut storage.params)?;
204 }
205
206 let mut captures = (func, storage);
212
213 let result = invoke_wasm_and_catch_traps(store, |caller, vm| {
214 let (func_ref, storage) = &mut captures;
215 let storage_len = mem::size_of_val::<Storage<_, _>>(storage) / mem::size_of::<ValRaw>();
216 let storage: *mut Storage<_, _> = storage;
217 let storage = storage.cast::<ValRaw>();
218 let storage = core::ptr::slice_from_raw_parts_mut(storage, storage_len);
219 let storage = NonNull::new(storage).unwrap();
220 func_ref
221 .as_ref()
222 .array_call(vm, VMOpaqueContext::from_vmcontext(caller), storage)
223 });
224
225 let (_, storage) = captures;
226 result?;
227
228 let mut store = AutoAssertNoGc::new(store.0);
229 Ok(Results::load(&mut store, &storage.results))
230 }
231
232 fn debug_typecheck(store: &StoreOpaque, func: VMSharedTypeIndex) {
234 let ty = FuncType::from_shared_type_index(store.engine(), func);
235 Params::typecheck(store.engine(), ty.params(), TypeCheckPosition::Param)
236 .expect("params should match");
237 Results::typecheck(store.engine(), ty.results(), TypeCheckPosition::Result)
238 .expect("results should match");
239 }
240}
241
242#[doc(hidden)]
243#[derive(Copy, Clone)]
244pub enum TypeCheckPosition {
245 Param,
246 Result,
247}
248
249pub unsafe trait WasmTy: Send {
258 #[doc(hidden)]
262 #[inline]
263 fn typecheck(engine: &Engine, actual: ValType, position: TypeCheckPosition) -> Result<()> {
264 let expected = Self::valtype();
265 debug_assert!(expected.comes_from_same_engine(engine));
266 debug_assert!(actual.comes_from_same_engine(engine));
267 match position {
268 TypeCheckPosition::Result => actual.ensure_matches(engine, &expected),
271 TypeCheckPosition::Param => match (expected.as_ref(), actual.as_ref()) {
274 (Some(expected_ref), Some(actual_ref)) if actual_ref.heap_type().is_concrete() => {
304 expected_ref
305 .heap_type()
306 .top()
307 .ensure_matches(engine, &actual_ref.heap_type().top())
308 }
309 _ => expected.ensure_matches(engine, &actual),
310 },
311 }
312 }
313
314 #[doc(hidden)]
316 fn valtype() -> ValType;
317
318 #[doc(hidden)]
319 fn may_gc() -> bool {
320 match Self::valtype() {
321 ValType::Ref(_) => true,
322 ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => false,
323 }
324 }
325
326 #[doc(hidden)]
329 fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
330
331 #[doc(hidden)]
338 fn dynamic_concrete_type_check(
339 &self,
340 store: &StoreOpaque,
341 nullable: bool,
342 actual: &HeapType,
343 ) -> Result<()>;
344
345 #[doc(hidden)]
353 #[inline]
354 fn is_vmgcref_and_points_to_object(&self) -> bool {
355 Self::valtype().is_vmgcref_type_and_points_to_object()
356 }
357
358 #[doc(hidden)]
387 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>;
388
389 #[doc(hidden)]
396 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self;
397}
398
399macro_rules! integers {
400 ($($primitive:ident/$get_primitive:ident => $ty:ident)*) => ($(
401 unsafe impl WasmTy for $primitive {
402 #[inline]
403 fn valtype() -> ValType {
404 ValType::$ty
405 }
406 #[inline]
407 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
408 true
409 }
410 #[inline]
411 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
412 unreachable!()
413 }
414 #[inline]
415 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
416 ptr.write(ValRaw::$primitive(self));
417 Ok(())
418 }
419 #[inline]
420 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
421 ptr.$get_primitive()
422 }
423 }
424 )*)
425}
426
427integers! {
428 i32/get_i32 => I32
429 i64/get_i64 => I64
430 u32/get_u32 => I32
431 u64/get_u64 => I64
432}
433
434macro_rules! floats {
435 ($($float:ident/$int:ident/$get_float:ident => $ty:ident)*) => ($(
436 unsafe impl WasmTy for $float {
437 #[inline]
438 fn valtype() -> ValType {
439 ValType::$ty
440 }
441 #[inline]
442 fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
443 true
444 }
445 #[inline]
446 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
447 unreachable!()
448 }
449 #[inline]
450 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
451 ptr.write(ValRaw::$float(self.to_bits()));
452 Ok(())
453 }
454 #[inline]
455 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
456 $float::from_bits(ptr.$get_float())
457 }
458 }
459 )*)
460}
461
462floats! {
463 f32/u32/get_f32 => F32
464 f64/u64/get_f64 => F64
465}
466
467unsafe impl WasmTy for NoFunc {
468 #[inline]
469 fn valtype() -> ValType {
470 ValType::Ref(RefType::new(false, HeapType::NoFunc))
471 }
472
473 #[inline]
474 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
475 match self._inner {}
476 }
477
478 #[inline]
479 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
480 match self._inner {}
481 }
482
483 #[inline]
484 fn is_vmgcref_and_points_to_object(&self) -> bool {
485 match self._inner {}
486 }
487
488 #[inline]
489 fn store(self, _store: &mut AutoAssertNoGc<'_>, _ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
490 match self._inner {}
491 }
492
493 #[inline]
494 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
495 unreachable!("NoFunc is uninhabited")
496 }
497}
498
499unsafe impl WasmTy for Option<NoFunc> {
500 #[inline]
501 fn valtype() -> ValType {
502 ValType::Ref(RefType::new(true, HeapType::NoFunc))
503 }
504
505 #[inline]
506 fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
507 true
508 }
509
510 #[inline]
511 fn dynamic_concrete_type_check(
512 &self,
513 _: &StoreOpaque,
514 nullable: bool,
515 ty: &HeapType,
516 ) -> Result<()> {
517 if nullable {
518 Ok(())
520 } else {
521 bail!("argument type mismatch: expected non-nullable (ref {ty}), found null reference")
522 }
523 }
524
525 #[inline]
526 fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
527 ptr.write(ValRaw::funcref(ptr::null_mut()));
528 Ok(())
529 }
530
531 #[inline]
532 unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
533 None
534 }
535}
536
537unsafe impl WasmTy for Func {
538 #[inline]
539 fn valtype() -> ValType {
540 ValType::Ref(RefType::new(false, HeapType::Func))
541 }
542
543 #[inline]
544 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
545 store.store_data().contains(self.0)
546 }
547
548 #[inline]
549 fn dynamic_concrete_type_check(
550 &self,
551 store: &StoreOpaque,
552 _nullable: bool,
553 expected: &HeapType,
554 ) -> Result<()> {
555 let expected = expected.unwrap_concrete_func();
556 self.ensure_matches_ty(store, expected)
557 .context("argument type mismatch for reference to concrete type")
558 }
559
560 #[inline]
561 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
562 let abi = self.vm_func_ref(store);
563 ptr.write(ValRaw::funcref(abi.cast::<c_void>().as_ptr()));
564 Ok(())
565 }
566
567 #[inline]
568 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
569 let p = NonNull::new(ptr.get_funcref()).unwrap().cast();
570 Func::from_vm_func_ref(store, p)
571 }
572}
573
574unsafe impl WasmTy for Option<Func> {
575 #[inline]
576 fn valtype() -> ValType {
577 ValType::FUNCREF
578 }
579
580 #[inline]
581 fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
582 if let Some(f) = self {
583 store.store_data().contains(f.0)
584 } else {
585 true
586 }
587 }
588
589 fn dynamic_concrete_type_check(
590 &self,
591 store: &StoreOpaque,
592 nullable: bool,
593 expected: &HeapType,
594 ) -> Result<()> {
595 if let Some(f) = self {
596 let expected = expected.unwrap_concrete_func();
597 f.ensure_matches_ty(store, expected)
598 .context("argument type mismatch for reference to concrete type")
599 } else if nullable {
600 Ok(())
601 } else {
602 bail!("argument type mismatch: expected non-nullable (ref {expected}), found null reference")
603 }
604 }
605
606 #[inline]
607 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
608 let raw = if let Some(f) = self {
609 f.vm_func_ref(store).as_ptr()
610 } else {
611 ptr::null_mut()
612 };
613 ptr.write(ValRaw::funcref(raw.cast::<c_void>()));
614 Ok(())
615 }
616
617 #[inline]
618 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
619 let ptr = NonNull::new(ptr.get_funcref())?.cast();
620 Some(Func::from_vm_func_ref(store, ptr))
621 }
622}
623
624pub unsafe trait WasmParams: Send {
630 #[doc(hidden)]
631 type ValRawStorage: Copy;
632
633 #[doc(hidden)]
634 fn typecheck(
635 engine: &Engine,
636 params: impl ExactSizeIterator<Item = crate::ValType>,
637 position: TypeCheckPosition,
638 ) -> Result<()>;
639
640 #[doc(hidden)]
641 fn vmgcref_pointing_to_object_count(&self) -> usize;
642
643 #[doc(hidden)]
644 fn store(
645 self,
646 store: &mut AutoAssertNoGc<'_>,
647 func_ty: &FuncType,
648 dst: &mut MaybeUninit<Self::ValRawStorage>,
649 ) -> Result<()>;
650}
651
652unsafe impl<T> WasmParams for T
655where
656 T: WasmTy,
657{
658 type ValRawStorage = <(T,) as WasmParams>::ValRawStorage;
659
660 fn typecheck(
661 engine: &Engine,
662 params: impl ExactSizeIterator<Item = crate::ValType>,
663 position: TypeCheckPosition,
664 ) -> Result<()> {
665 <(T,) as WasmParams>::typecheck(engine, params, position)
666 }
667
668 #[inline]
669 fn vmgcref_pointing_to_object_count(&self) -> usize {
670 T::is_vmgcref_and_points_to_object(self) as usize
671 }
672
673 #[inline]
674 fn store(
675 self,
676 store: &mut AutoAssertNoGc<'_>,
677 func_ty: &FuncType,
678 dst: &mut MaybeUninit<Self::ValRawStorage>,
679 ) -> Result<()> {
680 <(T,) as WasmParams>::store((self,), store, func_ty, dst)
681 }
682}
683
684macro_rules! impl_wasm_params {
685 ($n:tt $($t:ident)*) => {
686 #[allow(non_snake_case)]
687 unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) {
688 type ValRawStorage = [ValRaw; $n];
689
690 fn typecheck(
691 _engine: &Engine,
692 mut params: impl ExactSizeIterator<Item = crate::ValType>,
693 _position: TypeCheckPosition,
694 ) -> Result<()> {
695 let mut _n = 0;
696
697 $(
698 match params.next() {
699 Some(t) => {
700 _n += 1;
701 $t::typecheck(_engine, t, _position)?
702 },
703 None => bail!("expected {} types, found {}", $n, params.len() + _n),
704 }
705 )*
706
707 match params.next() {
708 None => Ok(()),
709 Some(_) => {
710 _n += 1;
711 bail!("expected {} types, found {}", $n, params.len() + _n)
712 },
713 }
714 }
715
716 #[inline]
717 fn vmgcref_pointing_to_object_count(&self) -> usize {
718 let ($($t,)*) = self;
719 0 $(
720 + $t.is_vmgcref_and_points_to_object() as usize
721 )*
722 }
723
724
725 #[inline]
726 fn store(
727 self,
728 _store: &mut AutoAssertNoGc<'_>,
729 _func_ty: &FuncType,
730 _ptr: &mut MaybeUninit<Self::ValRawStorage>,
731 ) -> Result<()> {
732 let ($($t,)*) = self;
733
734 let mut _i = 0;
735 $(
736 if !$t.compatible_with_store(_store) {
737 bail!("attempt to pass cross-`Store` value to Wasm as function argument");
738 }
739
740 if $t::valtype().is_ref() {
741 let param_ty = _func_ty.param(_i).unwrap();
742 let ref_ty = param_ty.unwrap_ref();
743 let heap_ty = ref_ty.heap_type();
744 if heap_ty.is_concrete() {
745 $t.dynamic_concrete_type_check(_store, ref_ty.is_nullable(), heap_ty)?;
746 }
747 }
748
749 let dst = map_maybe_uninit!(_ptr[_i]);
750 $t.store(_store, dst)?;
751
752 _i += 1;
753 )*
754 Ok(())
755 }
756 }
757 };
758}
759
760for_each_function_signature!(impl_wasm_params);
761
762pub unsafe trait WasmResults: WasmParams {
765 #[doc(hidden)]
766 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self;
767}
768
769unsafe impl<T: WasmTy> WasmResults for T {
771 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
772 <(T,) as WasmResults>::load(store, abi).0
773 }
774}
775
776macro_rules! impl_wasm_results {
777 ($n:tt $($t:ident)*) => {
778 #[allow(non_snake_case, unused_variables)]
779 unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*) {
780 unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
781 let [$($t,)*] = abi;
782 ($($t::load(store, $t),)*)
783 }
784 }
785 };
786}
787
788for_each_function_signature!(impl_wasm_results);