dusk_wasmtime/runtime/func/
typed.rs

1use super::{invoke_wasm_and_catch_traps, HostAbi};
2use crate::store::{AutoAssertNoGc, StoreOpaque};
3use crate::{
4    AsContext, AsContextMut, Engine, Func, FuncType, HeapType, NoFunc, RefType, StoreContextMut,
5    ValRaw, ValType,
6};
7use anyhow::{bail, Context, Result};
8use std::marker;
9use std::mem::{self, MaybeUninit};
10use std::num::NonZeroUsize;
11use std::os::raw::c_void;
12use std::ptr::{self, NonNull};
13use wasmtime_runtime::{
14    VMContext, VMFuncRef, VMNativeCallFunction, VMOpaqueContext, VMSharedTypeIndex,
15};
16
17/// A statically typed WebAssembly function.
18///
19/// Values of this type represent statically type-checked WebAssembly functions.
20/// The function within a [`TypedFunc`] is statically known to have `Params` as its
21/// parameters and `Results` as its results.
22///
23/// This structure is created via [`Func::typed`] or [`TypedFunc::new_unchecked`].
24/// For more documentation about this see those methods.
25pub struct TypedFunc<Params, Results> {
26    _a: marker::PhantomData<fn(Params) -> Results>,
27    ty: FuncType,
28    func: Func,
29}
30
31impl<Params, Results> Clone for TypedFunc<Params, Results> {
32    fn clone(&self) -> TypedFunc<Params, Results> {
33        Self {
34            _a: marker::PhantomData,
35            ty: self.ty.clone(),
36            func: self.func,
37        }
38    }
39}
40
41impl<Params, Results> TypedFunc<Params, Results>
42where
43    Params: WasmParams,
44    Results: WasmResults,
45{
46    /// An unchecked version of [`Func::typed`] which does not perform a
47    /// typecheck and simply assumes that the type declared here matches the
48    /// type of this function.
49    ///
50    /// The semantics of this function are the same as [`Func::typed`] except
51    /// that no error is returned because no typechecking is done.
52    ///
53    /// # Unsafety
54    ///
55    /// This function only safe to call if `typed` would otherwise return `Ok`
56    /// for the same `Params` and `Results` specified. If `typed` would return
57    /// an error then the returned `TypedFunc` is memory unsafe to invoke.
58    pub unsafe fn new_unchecked(store: impl AsContext, func: Func) -> TypedFunc<Params, Results> {
59        let store = store.as_context().0;
60        Self::_new_unchecked(store, func)
61    }
62
63    pub(crate) unsafe fn _new_unchecked(
64        store: &StoreOpaque,
65        func: Func,
66    ) -> TypedFunc<Params, Results> {
67        let ty = func.load_ty(store);
68        TypedFunc {
69            _a: marker::PhantomData,
70            ty,
71            func,
72        }
73    }
74
75    /// Returns the underlying [`Func`] that this is wrapping, losing the static
76    /// type information in the process.
77    pub fn func(&self) -> &Func {
78        &self.func
79    }
80
81    /// Invokes this WebAssembly function with the specified parameters.
82    ///
83    /// Returns either the results of the call, or a [`Trap`] if one happened.
84    ///
85    /// For more information, see the [`Func::typed`] and [`Func::call`]
86    /// documentation.
87    ///
88    /// # Errors
89    ///
90    /// For more information on errors see the documentation on [`Func::call`].
91    ///
92    /// # Panics
93    ///
94    /// This function will panic if it is called when the underlying [`Func`] is
95    /// connected to an asynchronous store.
96    ///
97    /// [`Trap`]: crate::Trap
98    pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results> {
99        let mut store = store.as_context_mut();
100        assert!(
101            !store.0.async_support(),
102            "must use `call_async` with async stores"
103        );
104        if Self::need_gc_before_call_raw(store.0, &params) {
105            store.0.gc();
106        }
107        let func = self.func.vm_func_ref(store.0);
108        unsafe { Self::call_raw(&mut store, &self.ty, func, params) }
109    }
110
111    /// Invokes this WebAssembly function with the specified parameters.
112    ///
113    /// Returns either the results of the call, or a [`Trap`] if one happened.
114    ///
115    /// For more information, see the [`Func::typed`] and [`Func::call_async`]
116    /// documentation.
117    ///
118    /// # Errors
119    ///
120    /// For more information on errors see the documentation on [`Func::call`].
121    ///
122    /// # Panics
123    ///
124    /// This function will panic if it is called when the underlying [`Func`] is
125    /// connected to a synchronous store.
126    ///
127    /// [`Trap`]: crate::Trap
128    #[cfg(feature = "async")]
129    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
130    pub async fn call_async<T>(
131        &self,
132        mut store: impl AsContextMut<Data = T>,
133        params: Params,
134    ) -> Result<Results>
135    where
136        T: Send,
137    {
138        let mut store = store.as_context_mut();
139        assert!(
140            store.0.async_support(),
141            "must use `call` with non-async stores"
142        );
143        if Self::need_gc_before_call_raw(store.0, &params) {
144            store.0.gc_async().await;
145        }
146        store
147            .on_fiber(|store| {
148                let func = self.func.vm_func_ref(store.0);
149                unsafe { Self::call_raw(store, &self.ty, func, params) }
150            })
151            .await?
152    }
153
154    #[inline]
155    pub(crate) fn need_gc_before_call_raw(_store: &StoreOpaque, _params: &Params) -> bool {
156        #[cfg(feature = "gc")]
157        {
158            // See the comment in `Func::call_impl_check_args`.
159            let num_gc_refs = _params.non_i31_gc_refs_count();
160            if let Some(num_gc_refs) = NonZeroUsize::new(num_gc_refs) {
161                return _store
162                    .unwrap_gc_store()
163                    .gc_heap
164                    .need_gc_before_entering_wasm(num_gc_refs);
165            }
166        }
167
168        false
169    }
170
171    /// Do a raw call of a typed function.
172    ///
173    /// # Safety
174    ///
175    /// `func` must be of the given type.
176    ///
177    /// If `Self::need_gc_before_call_raw`, then the caller must have done a GC
178    /// just before calling this method.
179    pub(crate) unsafe fn call_raw<T>(
180        store: &mut StoreContextMut<'_, T>,
181        ty: &FuncType,
182        func: ptr::NonNull<VMFuncRef>,
183        params: Params,
184    ) -> Result<Results> {
185        // double-check that params/results match for this function's type in
186        // debug mode.
187        if cfg!(debug_assertions) {
188            Self::debug_typecheck(store.0, func.as_ref().type_index);
189        }
190
191        // Validate that all runtime values flowing into this store indeed
192        // belong within this store, otherwise it would be unsafe for store
193        // values to cross each other.
194
195        let params = {
196            let mut store = AutoAssertNoGc::new(store.0);
197            params.into_abi(&mut store, ty)?
198        };
199
200        // Try to capture only a single variable (a tuple) in the closure below.
201        // This means the size of the closure is one pointer and is much more
202        // efficient to move in memory. This closure is actually invoked on the
203        // other side of a C++ shim, so it can never be inlined enough to make
204        // the memory go away, so the size matters here for performance.
205        let mut captures = (func, MaybeUninit::uninit(), params, false);
206
207        let result = invoke_wasm_and_catch_traps(store, |caller| {
208            let (func_ref, ret, params, returned) = &mut captures;
209            let func_ref = func_ref.as_ref();
210            let result =
211                Params::invoke::<Results>(func_ref.native_call, func_ref.vmctx, caller, *params);
212            ptr::write(ret.as_mut_ptr(), result);
213            *returned = true
214        });
215
216        let (_, ret, _, returned) = captures;
217        debug_assert_eq!(result.is_ok(), returned);
218        result?;
219
220        let mut store = AutoAssertNoGc::new(store.0);
221        Ok(Results::from_abi(&mut store, ret.assume_init()))
222    }
223
224    /// Purely a debug-mode assertion, not actually used in release builds.
225    fn debug_typecheck(store: &StoreOpaque, func: VMSharedTypeIndex) {
226        let ty = FuncType::from_shared_type_index(store.engine(), func);
227        Params::typecheck(store.engine(), ty.params(), TypeCheckPosition::Param)
228            .expect("params should match");
229        Results::typecheck(store.engine(), ty.results(), TypeCheckPosition::Result)
230            .expect("results should match");
231    }
232}
233
234#[doc(hidden)]
235#[derive(Copy, Clone)]
236pub enum TypeCheckPosition {
237    Param,
238    Result,
239}
240
241/// A trait implemented for types which can be arguments and results for
242/// closures passed to [`Func::wrap`] as well as parameters to [`Func::typed`].
243///
244/// This trait should not be implemented by user types. This trait may change at
245/// any time internally. The types which implement this trait, however, are
246/// stable over time.
247///
248/// For more information see [`Func::wrap`] and [`Func::typed`]
249pub unsafe trait WasmTy: Send {
250    // The raw ABI type that values of this type can be converted to and passed
251    // to Wasm, or given from Wasm and converted back from.
252    #[doc(hidden)]
253    type Abi: 'static + Copy;
254
255    // Do a "static" (aka at time of `func.typed::<P, R>()`) ahead-of-time type
256    // check for this type at the given position. You probably don't need to
257    // override this trait method.
258    #[doc(hidden)]
259    #[inline]
260    fn typecheck(engine: &Engine, actual: ValType, position: TypeCheckPosition) -> Result<()> {
261        let expected = Self::valtype();
262        debug_assert!(expected.comes_from_same_engine(engine));
263        debug_assert!(actual.comes_from_same_engine(engine));
264        match position {
265            // The caller is expecting to receive a `T` and the callee is
266            // actually returning a `U`, so ensure that `U <: T`.
267            TypeCheckPosition::Result => actual.ensure_matches(engine, &expected),
268            // The caller is expecting to pass a `T` and the callee is expecting
269            // to receive a `U`, so ensure that `T <: U`.
270            TypeCheckPosition::Param => match (expected.as_ref(), actual.as_ref()) {
271                // ... except that this technically-correct check would overly
272                // restrict the usefulness of our typed function APIs for the
273                // specific case of concrete reference types. Let's work through
274                // an example.
275                //
276                // Consider functions that take a `(ref param $some_func_type)`
277                // parameter:
278                //
279                // * We cannot have a static `wasmtime::SomeFuncTypeRef` type
280                //   that implements `WasmTy` specifically for `(ref null
281                //   $some_func_type)` because Wasm modules, and their types,
282                //   are loaded dynamically at runtime.
283                //
284                // * Therefore the embedder's only option for `T <: (ref null
285                //   $some_func_type)` is `T = (ref null nofunc)` aka
286                //   `Option<wasmtime::NoFunc>`.
287                //
288                // * But that static type means they can *only* pass in the null
289                //   function reference as an argument to the typed function.
290                //   This is way too restrictive! For ergonomics, we want them
291                //   to be able to pass in a `wasmtime::Func` whose type is
292                //   `$some_func_type`!
293                //
294                // To lift this constraint and enable better ergonomics for
295                // embedders, we allow `top(T) <: top(U)` -- i.e. they are part
296                // of the same type hierarchy and a dynamic cast could possibly
297                // succeed -- for the specific case of concrete heap type
298                // parameters, and fall back to dynamic type checks on the
299                // arguments passed to each invocation, as necessary.
300                (Some(expected_ref), Some(actual_ref)) if actual_ref.heap_type().is_concrete() => {
301                    expected_ref
302                        .heap_type()
303                        .top(engine)
304                        .ensure_matches(engine, &actual_ref.heap_type().top(engine))
305                }
306                _ => expected.ensure_matches(engine, &actual),
307            },
308        }
309    }
310
311    // The value type that this Type represents.
312    #[doc(hidden)]
313    fn valtype() -> ValType;
314
315    // Dynamic checks that this value is being used with the correct store
316    // context.
317    #[doc(hidden)]
318    fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
319
320    // Dynamic checks that `self <: actual` for concrete type arguments. See the
321    // comment above in `WasmTy::typecheck`.
322    //
323    // Only ever called for concrete reference type arguments, so any type which
324    // is not in a type hierarchy with concrete reference types can implement
325    // this with `unreachable!()`.
326    #[doc(hidden)]
327    fn dynamic_concrete_type_check(
328        &self,
329        store: &StoreOpaque,
330        nullable: bool,
331        actual: &FuncType,
332    ) -> Result<()>;
333
334    // Is this an externref?
335    #[doc(hidden)]
336    fn is_non_i31_gc_ref(&self) -> bool;
337
338    // Construct a `Self::Abi` from the given `ValRaw`.
339    #[doc(hidden)]
340    unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi;
341
342    // Stuff our given `Self::Abi` into a `ValRaw`.
343    #[doc(hidden)]
344    unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw);
345
346    // Convert `self` into `Self::Abi`.
347    //
348    // NB: We _must not_ trigger a GC when passing refs from host code into Wasm
349    // (e.g. returned from a host function or passed as arguments to a Wasm
350    // function). After insertion into the activations table, the reference is
351    // no longer rooted. If multiple references are being sent from the host
352    // into Wasm and we allowed GCs during insertion, then the following events
353    // could happen:
354    //
355    // * Reference A is inserted into the activations table. This does not
356    //   trigger a GC, but does fill the table to capacity.
357    //
358    // * The caller's reference to A is removed. Now the only reference to A is
359    //   from the activations table.
360    //
361    // * Reference B is inserted into the activations table. Because the table
362    //   is at capacity, a GC is triggered.
363    //
364    // * A is reclaimed because the only reference keeping it alive was the
365    //   activation table's reference (it isn't inside any Wasm frames on the
366    //   stack yet, so stack scanning and stack maps don't increment its
367    //   reference count).
368    //
369    // * We transfer control to Wasm, giving it A and B. Wasm uses A. That's a
370    //   use-after-free bug.
371    //
372    // In conclusion, to prevent uses-after-free bugs, we cannot GC while
373    // converting types into their raw ABI forms.
374    #[doc(hidden)]
375    fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi>;
376
377    // Convert back from `Self::Abi` into `Self`.
378    #[doc(hidden)]
379    unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self;
380}
381
382macro_rules! integers {
383    ($($primitive:ident/$get_primitive:ident => $ty:ident)*) => ($(
384        unsafe impl WasmTy for $primitive {
385            type Abi = $primitive;
386            #[inline]
387            fn valtype() -> ValType {
388                ValType::$ty
389            }
390            #[inline]
391            fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
392                true
393            }
394            #[inline]
395            fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
396                unreachable!()
397            }
398            #[inline]
399            fn is_non_i31_gc_ref(&self) -> bool {
400                false
401            }
402            #[inline]
403            unsafe fn abi_from_raw(raw: *mut ValRaw) -> $primitive {
404                (*raw).$get_primitive()
405            }
406            #[inline]
407            unsafe fn abi_into_raw(abi: $primitive, raw: *mut ValRaw) {
408                *raw = ValRaw::$primitive(abi);
409            }
410            #[inline]
411            fn into_abi(self, _store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi>
412            {
413                Ok(self)
414            }
415            #[inline]
416            unsafe fn from_abi(abi: Self::Abi, _store: &mut AutoAssertNoGc<'_>) -> Self {
417                abi
418            }
419        }
420    )*)
421}
422
423integers! {
424    i32/get_i32 => I32
425    i64/get_i64 => I64
426    u32/get_u32 => I32
427    u64/get_u64 => I64
428}
429
430macro_rules! floats {
431    ($($float:ident/$int:ident/$get_float:ident => $ty:ident)*) => ($(
432        unsafe impl WasmTy for $float {
433            type Abi = $float;
434            #[inline]
435            fn valtype() -> ValType {
436                ValType::$ty
437            }
438            #[inline]
439            fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
440                true
441            }
442            #[inline]
443            fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
444                unreachable!()
445            }
446            #[inline]
447            fn is_non_i31_gc_ref(&self) -> bool {
448                false
449            }
450            #[inline]
451            unsafe fn abi_from_raw(raw: *mut ValRaw) -> $float {
452                $float::from_bits((*raw).$get_float())
453            }
454            #[inline]
455            unsafe fn abi_into_raw(abi: $float, raw: *mut ValRaw) {
456                *raw = ValRaw::$float(abi.to_bits());
457            }
458            #[inline]
459            fn into_abi(self, _store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi>
460            {
461                Ok(self)
462            }
463            #[inline]
464            unsafe fn from_abi(abi: Self::Abi, _store: &mut AutoAssertNoGc<'_>) -> Self {
465                abi
466            }
467        }
468    )*)
469}
470
471floats! {
472    f32/u32/get_f32 => F32
473    f64/u64/get_f64 => F64
474}
475
476unsafe impl WasmTy for NoFunc {
477    type Abi = NoFunc;
478
479    #[inline]
480    fn valtype() -> ValType {
481        ValType::Ref(RefType::new(false, HeapType::NoFunc))
482    }
483
484    #[inline]
485    fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
486        match self._inner {}
487    }
488
489    #[inline]
490    fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &FuncType) -> Result<()> {
491        match self._inner {}
492    }
493
494    #[inline]
495    fn is_non_i31_gc_ref(&self) -> bool {
496        match self._inner {}
497    }
498
499    #[inline]
500    unsafe fn abi_from_raw(_raw: *mut ValRaw) -> Self::Abi {
501        unreachable!("NoFunc is uninhabited")
502    }
503
504    #[inline]
505    unsafe fn abi_into_raw(_abi: Self::Abi, _raw: *mut ValRaw) {
506        unreachable!("NoFunc is uninhabited")
507    }
508
509    #[inline]
510    fn into_abi(self, _store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
511        unreachable!("NoFunc is uninhabited")
512    }
513
514    #[inline]
515    unsafe fn from_abi(_abi: Self::Abi, _store: &mut AutoAssertNoGc<'_>) -> Self {
516        unreachable!("NoFunc is uninhabited")
517    }
518}
519
520unsafe impl WasmTy for Option<NoFunc> {
521    type Abi = *mut NoFunc;
522
523    #[inline]
524    fn valtype() -> ValType {
525        ValType::Ref(RefType::new(true, HeapType::NoFunc))
526    }
527
528    #[inline]
529    fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
530        true
531    }
532
533    #[inline]
534    fn dynamic_concrete_type_check(
535        &self,
536        _: &StoreOpaque,
537        nullable: bool,
538        func_ty: &FuncType,
539    ) -> Result<()> {
540        if nullable {
541            // `(ref null nofunc) <: (ref null $f)` for all function types `$f`.
542            Ok(())
543        } else {
544            bail!("argument type mismatch: expected (ref {func_ty}), found null reference")
545        }
546    }
547
548    #[inline]
549    fn is_non_i31_gc_ref(&self) -> bool {
550        false
551    }
552
553    #[inline]
554    unsafe fn abi_from_raw(_raw: *mut ValRaw) -> Self::Abi {
555        ptr::null_mut()
556    }
557
558    #[inline]
559    unsafe fn abi_into_raw(_abi: Self::Abi, raw: *mut ValRaw) {
560        *raw = ValRaw::funcref(ptr::null_mut());
561    }
562
563    #[inline]
564    fn into_abi(self, _store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
565        Ok(ptr::null_mut())
566    }
567
568    #[inline]
569    unsafe fn from_abi(_abi: Self::Abi, _store: &mut AutoAssertNoGc<'_>) -> Self {
570        None
571    }
572}
573
574unsafe impl WasmTy for Func {
575    type Abi = NonNull<wasmtime_runtime::VMFuncRef>;
576
577    #[inline]
578    fn valtype() -> ValType {
579        ValType::Ref(RefType::new(false, HeapType::Func))
580    }
581
582    #[inline]
583    fn compatible_with_store<'a>(&self, store: &StoreOpaque) -> bool {
584        store.store_data().contains(self.0)
585    }
586
587    #[inline]
588    fn dynamic_concrete_type_check(
589        &self,
590        store: &StoreOpaque,
591        _nullable: bool,
592        actual: &FuncType,
593    ) -> Result<()> {
594        self.ensure_matches_ty(store, actual)
595            .context("argument type mismatch for reference to concrete type")
596    }
597
598    #[inline]
599    fn is_non_i31_gc_ref(&self) -> bool {
600        false
601    }
602
603    #[inline]
604    unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
605        let p = (*raw).get_funcref();
606        debug_assert!(!p.is_null());
607        NonNull::new_unchecked(p.cast::<wasmtime_runtime::VMFuncRef>())
608    }
609
610    #[inline]
611    unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
612        *raw = ValRaw::funcref(abi.cast::<c_void>().as_ptr());
613    }
614
615    #[inline]
616    fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
617        Ok(self.vm_func_ref(store))
618    }
619
620    #[inline]
621    unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
622        Func::from_vm_func_ref(store, abi.as_ptr()).unwrap()
623    }
624}
625
626unsafe impl WasmTy for Option<Func> {
627    type Abi = *mut wasmtime_runtime::VMFuncRef;
628
629    #[inline]
630    fn valtype() -> ValType {
631        ValType::FUNCREF
632    }
633
634    #[inline]
635    fn compatible_with_store<'a>(&self, store: &StoreOpaque) -> bool {
636        if let Some(f) = self {
637            store.store_data().contains(f.0)
638        } else {
639            true
640        }
641    }
642
643    fn dynamic_concrete_type_check(
644        &self,
645        store: &StoreOpaque,
646        nullable: bool,
647        func_ty: &FuncType,
648    ) -> Result<()> {
649        if let Some(f) = self {
650            f.ensure_matches_ty(store, func_ty)
651                .context("argument type mismatch for reference to concrete type")
652        } else if nullable {
653            Ok(())
654        } else {
655            bail!("argument type mismatch: expected (ref {func_ty}), found null reference")
656        }
657    }
658
659    #[inline]
660    fn is_non_i31_gc_ref(&self) -> bool {
661        false
662    }
663
664    #[inline]
665    unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
666        (*raw).get_funcref() as Self::Abi
667    }
668
669    #[inline]
670    unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
671        *raw = ValRaw::funcref(abi.cast());
672    }
673
674    #[inline]
675    fn into_abi(self, store: &mut AutoAssertNoGc<'_>) -> Result<Self::Abi> {
676        Ok(if let Some(f) = self {
677            f.vm_func_ref(store).as_ptr()
678        } else {
679            ptr::null_mut()
680        })
681    }
682
683    #[inline]
684    unsafe fn from_abi(abi: Self::Abi, store: &mut AutoAssertNoGc<'_>) -> Self {
685        Func::from_vm_func_ref(store, abi)
686    }
687}
688
689/// A trait used for [`Func::typed`] and with [`TypedFunc`] to represent the set of
690/// parameters for wasm functions.
691///
692/// This is implemented for bare types that can be passed to wasm as well as
693/// tuples of those types.
694pub unsafe trait WasmParams: Send {
695    #[doc(hidden)]
696    type Abi: Copy;
697
698    #[doc(hidden)]
699    fn typecheck(
700        engine: &Engine,
701        params: impl ExactSizeIterator<Item = crate::ValType>,
702        position: TypeCheckPosition,
703    ) -> Result<()>;
704
705    #[doc(hidden)]
706    fn non_i31_gc_refs_count(&self) -> usize;
707
708    #[doc(hidden)]
709    fn into_abi(self, store: &mut AutoAssertNoGc<'_>, func_ty: &FuncType) -> Result<Self::Abi>;
710
711    #[doc(hidden)]
712    unsafe fn invoke<R: WasmResults>(
713        func: NonNull<VMNativeCallFunction>,
714        vmctx1: *mut VMOpaqueContext,
715        vmctx2: *mut VMContext,
716        abi: Self::Abi,
717    ) -> R::ResultAbi;
718}
719
720// Forward an impl from `T` to `(T,)` for convenience if there's only one
721// parameter.
722unsafe impl<T> WasmParams for T
723where
724    T: WasmTy,
725{
726    type Abi = <(T,) as WasmParams>::Abi;
727
728    fn typecheck(
729        engine: &Engine,
730        params: impl ExactSizeIterator<Item = crate::ValType>,
731        position: TypeCheckPosition,
732    ) -> Result<()> {
733        <(T,) as WasmParams>::typecheck(engine, params, position)
734    }
735
736    #[inline]
737    fn non_i31_gc_refs_count(&self) -> usize {
738        T::is_non_i31_gc_ref(self) as usize
739    }
740
741    #[inline]
742    fn into_abi(self, store: &mut AutoAssertNoGc<'_>, func_ty: &FuncType) -> Result<Self::Abi> {
743        <(T,) as WasmParams>::into_abi((self,), store, func_ty)
744    }
745
746    unsafe fn invoke<R: WasmResults>(
747        func: NonNull<VMNativeCallFunction>,
748        vmctx1: *mut VMOpaqueContext,
749        vmctx2: *mut VMContext,
750        abi: Self::Abi,
751    ) -> R::ResultAbi {
752        <(T,) as WasmParams>::invoke::<R>(func, vmctx1, vmctx2, abi)
753    }
754}
755
756macro_rules! impl_wasm_params {
757    ($n:tt $($t:ident)*) => {
758        #[allow(non_snake_case)]
759        unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) {
760            type Abi = ($($t::Abi,)*);
761
762            fn typecheck(
763                _engine: &Engine,
764                mut params: impl ExactSizeIterator<Item = crate::ValType>,
765                _position: TypeCheckPosition,
766            ) -> Result<()> {
767                let mut _n = 0;
768
769                $(
770                    match params.next() {
771                        Some(t) => {
772                            _n += 1;
773                            $t::typecheck(_engine, t, _position)?
774                        },
775                        None => bail!("expected {} types, found {}", $n, params.len() + _n),
776                    }
777                )*
778
779                match params.next() {
780                    None => Ok(()),
781                    Some(_) => {
782                        _n += 1;
783                        bail!("expected {} types, found {}", $n, params.len() + _n)
784                    },
785                }
786            }
787
788            #[inline]
789            fn non_i31_gc_refs_count(&self) -> usize {
790                let ($(ref $t,)*) = self;
791                0 $(
792                    + $t.is_non_i31_gc_ref() as usize
793                )*
794            }
795
796
797            #[inline]
798            fn into_abi(
799                self,
800                _store: &mut AutoAssertNoGc<'_>,
801                _func_ty: &FuncType,
802            ) -> Result<Self::Abi> {
803                let ($($t,)*) = self;
804
805                let mut _i = 0;
806                $(
807                    if !$t.compatible_with_store(_store) {
808                        bail!("attempt to pass cross-`Store` value to Wasm as function argument");
809                    }
810
811                    if $t::valtype().is_ref() {
812                        let p = _func_ty.param(_i).unwrap();
813                        let r = p.unwrap_ref();
814                        if let Some(c) = r.heap_type().as_concrete() {
815                            $t.dynamic_concrete_type_check(_store, r.is_nullable(), c)?;
816                        }
817                    }
818
819                    let $t = $t.into_abi(_store)?;
820
821                    _i += 1;
822                )*
823                Ok(($($t,)*))
824            }
825
826            unsafe fn invoke<R: WasmResults>(
827                func: NonNull<VMNativeCallFunction>,
828                vmctx1: *mut VMOpaqueContext,
829                vmctx2: *mut VMContext,
830                abi: Self::Abi,
831            ) -> R::ResultAbi {
832                let fnptr = mem::transmute::<
833                    NonNull<VMNativeCallFunction>,
834                    unsafe extern "C" fn(
835                        *mut VMOpaqueContext,
836                        *mut VMContext,
837                        $($t::Abi,)*
838                        <R::ResultAbi as HostAbi>::Retptr,
839                    ) -> <R::ResultAbi as HostAbi>::Abi,
840                    >(func);
841                let ($($t,)*) = abi;
842                // Use the `call` function to acquire a `retptr` which we'll
843                // forward to the native function. Once we have it we also
844                // convert all our arguments to abi arguments to go to the raw
845                // function.
846                //
847                // Upon returning `R::call` will convert all the returns back
848                // into `R`.
849                <R::ResultAbi as HostAbi>::call(|retptr| {
850                    fnptr(vmctx1, vmctx2, $($t,)* retptr)
851                })
852            }
853        }
854    };
855}
856
857for_each_function_signature!(impl_wasm_params);
858
859/// A trait used for [`Func::typed`] and with [`TypedFunc`] to represent the set of
860/// results for wasm functions.
861pub unsafe trait WasmResults: WasmParams {
862    #[doc(hidden)]
863    type ResultAbi: HostAbi;
864
865    #[doc(hidden)]
866    unsafe fn from_abi(store: &mut AutoAssertNoGc<'_>, abi: Self::ResultAbi) -> Self;
867}
868
869// Forwards from a bare type `T` to the 1-tuple type `(T,)`
870unsafe impl<T: WasmTy> WasmResults for T
871where
872    (T::Abi,): HostAbi,
873{
874    type ResultAbi = <(T,) as WasmResults>::ResultAbi;
875
876    unsafe fn from_abi(store: &mut AutoAssertNoGc<'_>, abi: Self::ResultAbi) -> Self {
877        <(T,) as WasmResults>::from_abi(store, abi).0
878    }
879}
880
881macro_rules! impl_wasm_results {
882    ($n:tt $($t:ident)*) => {
883        #[allow(non_snake_case, unused_variables)]
884        unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*)
885            where ($($t::Abi,)*): HostAbi
886        {
887            type ResultAbi = ($($t::Abi,)*);
888
889            #[inline]
890            unsafe fn from_abi(store: &mut AutoAssertNoGc<'_>, abi: Self::ResultAbi) -> Self {
891                let ($($t,)*) = abi;
892                ($($t::from_abi($t, store),)*)
893            }
894        }
895    };
896}
897
898for_each_function_signature!(impl_wasm_results);