wasmtime/runtime/func/
typed.rs

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::num::NonZeroUsize;
13use core::ptr::{self};
14use wasmtime_environ::VMSharedTypeIndex;
15
16/// A statically typed WebAssembly function.
17///
18/// Values of this type represent statically type-checked WebAssembly functions.
19/// The function within a [`TypedFunc`] is statically known to have `Params` as its
20/// parameters and `Results` as its results.
21///
22/// This structure is created via [`Func::typed`] or [`TypedFunc::new_unchecked`].
23/// For more documentation about this see those methods.
24pub struct TypedFunc<Params, Results> {
25    _a: marker::PhantomData<fn(Params) -> Results>,
26    ty: FuncType,
27    func: Func,
28}
29
30impl<Params, Results> Clone for TypedFunc<Params, Results> {
31    fn clone(&self) -> TypedFunc<Params, Results> {
32        Self {
33            _a: marker::PhantomData,
34            ty: self.ty.clone(),
35            func: self.func,
36        }
37    }
38}
39
40impl<Params, Results> TypedFunc<Params, Results>
41where
42    Params: WasmParams,
43    Results: WasmResults,
44{
45    /// An unchecked version of [`Func::typed`] which does not perform a
46    /// typecheck and simply assumes that the type declared here matches the
47    /// type of this function.
48    ///
49    /// The semantics of this function are the same as [`Func::typed`] except
50    /// that no error is returned because no typechecking is done.
51    ///
52    /// # Unsafety
53    ///
54    /// This function only safe to call if `typed` would otherwise return `Ok`
55    /// for the same `Params` and `Results` specified. If `typed` would return
56    /// an error then the returned `TypedFunc` is memory unsafe to invoke.
57    pub unsafe fn new_unchecked(store: impl AsContext, func: Func) -> TypedFunc<Params, Results> {
58        let store = store.as_context().0;
59        Self::_new_unchecked(store, func)
60    }
61
62    pub(crate) unsafe fn _new_unchecked(
63        store: &StoreOpaque,
64        func: Func,
65    ) -> TypedFunc<Params, Results> {
66        let ty = func.load_ty(store);
67        TypedFunc {
68            _a: marker::PhantomData,
69            ty,
70            func,
71        }
72    }
73
74    /// Returns the underlying [`Func`] that this is wrapping, losing the static
75    /// type information in the process.
76    pub fn func(&self) -> &Func {
77        &self.func
78    }
79
80    /// Invokes this WebAssembly function with the specified parameters.
81    ///
82    /// Returns either the results of the call, or a [`Trap`] if one happened.
83    ///
84    /// For more information, see the [`Func::typed`] and [`Func::call`]
85    /// documentation.
86    ///
87    /// # Errors
88    ///
89    /// For more information on errors see the documentation on [`Func::call`].
90    ///
91    /// # Panics
92    ///
93    /// This function will panic if it is called when the underlying [`Func`] is
94    /// connected to an asynchronous store.
95    ///
96    /// [`Trap`]: crate::Trap
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        if Self::need_gc_before_call_raw(store.0, &params) {
104            store.0.gc();
105        }
106        let func = self.func.vm_func_ref(store.0);
107        unsafe { Self::call_raw(&mut store, &self.ty, func, params) }
108    }
109
110    /// Invokes this WebAssembly function with the specified parameters.
111    ///
112    /// Returns either the results of the call, or a [`Trap`] if one happened.
113    ///
114    /// For more information, see the [`Func::typed`] and [`Func::call_async`]
115    /// documentation.
116    ///
117    /// # Errors
118    ///
119    /// For more information on errors see the documentation on [`Func::call`].
120    ///
121    /// # Panics
122    ///
123    /// This function will panic if it is called when the underlying [`Func`] is
124    /// connected to a synchronous store.
125    ///
126    /// [`Trap`]: crate::Trap
127    #[cfg(feature = "async")]
128    pub async fn call_async<T>(
129        &self,
130        mut store: impl AsContextMut<Data = T>,
131        params: Params,
132    ) -> Result<Results>
133    where
134        T: Send,
135    {
136        let mut store = store.as_context_mut();
137        assert!(
138            store.0.async_support(),
139            "must use `call` with non-async stores"
140        );
141        if Self::need_gc_before_call_raw(store.0, &params) {
142            store.0.gc_async().await;
143        }
144        store
145            .on_fiber(|store| {
146                let func = self.func.vm_func_ref(store.0);
147                unsafe { Self::call_raw(store, &self.ty, func, params) }
148            })
149            .await?
150    }
151
152    #[inline]
153    pub(crate) fn need_gc_before_call_raw(_store: &StoreOpaque, _params: &Params) -> bool {
154        #[cfg(feature = "gc")]
155        {
156            // See the comment in `Func::call_impl_check_args`.
157            let num_gc_refs = _params.vmgcref_pointing_to_object_count();
158            if let Some(num_gc_refs) = NonZeroUsize::new(num_gc_refs) {
159                return _store
160                    .unwrap_gc_store()
161                    .gc_heap
162                    .need_gc_before_entering_wasm(num_gc_refs);
163            }
164        }
165
166        false
167    }
168
169    /// Do a raw call of a typed function.
170    ///
171    /// # Safety
172    ///
173    /// `func` must be of the given type.
174    ///
175    /// If `Self::need_gc_before_call_raw`, then the caller must have done a GC
176    /// just before calling this method.
177    pub(crate) unsafe fn call_raw<T>(
178        store: &mut StoreContextMut<'_, T>,
179        ty: &FuncType,
180        func: ptr::NonNull<VMFuncRef>,
181        params: Params,
182    ) -> Result<Results> {
183        // double-check that params/results match for this function's type in
184        // debug mode.
185        if cfg!(debug_assertions) {
186            Self::debug_typecheck(store.0, func.as_ref().type_index);
187        }
188
189        // Validate that all runtime values flowing into this store indeed
190        // belong within this store, otherwise it would be unsafe for store
191        // values to cross each other.
192
193        union Storage<T: Copy, U: Copy> {
194            params: MaybeUninit<T>,
195            results: U,
196        }
197
198        let mut storage = Storage::<Params::ValRawStorage, Results::ValRawStorage> {
199            params: MaybeUninit::uninit(),
200        };
201
202        {
203            let mut store = AutoAssertNoGc::new(store.0);
204            params.store(&mut store, ty, &mut storage.params)?;
205        }
206
207        // Try to capture only a single variable (a tuple) in the closure below.
208        // This means the size of the closure is one pointer and is much more
209        // efficient to move in memory. This closure is actually invoked on the
210        // other side of a C++ shim, so it can never be inlined enough to make
211        // the memory go away, so the size matters here for performance.
212        let mut captures = (func, storage);
213
214        let result = invoke_wasm_and_catch_traps(store, |caller| {
215            let (func_ref, storage) = &mut captures;
216            let func_ref = func_ref.as_ref();
217            (func_ref.array_call)(
218                func_ref.vmctx,
219                VMOpaqueContext::from_vmcontext(caller),
220                (storage as *mut Storage<_, _>) as *mut ValRaw,
221                mem::size_of_val::<Storage<_, _>>(storage) / mem::size_of::<ValRaw>(),
222            );
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    /// Purely a debug-mode assertion, not actually used in release builds.
233    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
249/// A trait implemented for types which can be arguments and results for
250/// closures passed to [`Func::wrap`] as well as parameters to [`Func::typed`].
251///
252/// This trait should not be implemented by user types. This trait may change at
253/// any time internally. The types which implement this trait, however, are
254/// stable over time.
255///
256/// For more information see [`Func::wrap`] and [`Func::typed`]
257pub unsafe trait WasmTy: Send {
258    // Do a "static" (aka at time of `func.typed::<P, R>()`) ahead-of-time type
259    // check for this type at the given position. You probably don't need to
260    // override this trait method.
261    #[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            // The caller is expecting to receive a `T` and the callee is
269            // actually returning a `U`, so ensure that `U <: T`.
270            TypeCheckPosition::Result => actual.ensure_matches(engine, &expected),
271            // The caller is expecting to pass a `T` and the callee is expecting
272            // to receive a `U`, so ensure that `T <: U`.
273            TypeCheckPosition::Param => match (expected.as_ref(), actual.as_ref()) {
274                // ... except that this technically-correct check would overly
275                // restrict the usefulness of our typed function APIs for the
276                // specific case of concrete reference types. Let's work through
277                // an example.
278                //
279                // Consider functions that take a `(ref param $some_func_type)`
280                // parameter:
281                //
282                // * We cannot have a static `wasmtime::SomeFuncTypeRef` type
283                //   that implements `WasmTy` specifically for `(ref null
284                //   $some_func_type)` because Wasm modules, and their types,
285                //   are loaded dynamically at runtime.
286                //
287                // * Therefore the embedder's only option for `T <: (ref null
288                //   $some_func_type)` is `T = (ref null nofunc)` aka
289                //   `Option<wasmtime::NoFunc>`.
290                //
291                // * But that static type means they can *only* pass in the null
292                //   function reference as an argument to the typed function.
293                //   This is way too restrictive! For ergonomics, we want them
294                //   to be able to pass in a `wasmtime::Func` whose type is
295                //   `$some_func_type`!
296                //
297                // To lift this constraint and enable better ergonomics for
298                // embedders, we allow `top(T) <: top(U)` -- i.e. they are part
299                // of the same type hierarchy and a dynamic cast could possibly
300                // succeed -- for the specific case of concrete heap type
301                // parameters, and fall back to dynamic type checks on the
302                // arguments passed to each invocation, as necessary.
303                (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    // The value type that this Type represents.
315    #[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    // Dynamic checks that this value is being used with the correct store
327    // context.
328    #[doc(hidden)]
329    fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
330
331    // Dynamic checks that `self <: actual` for concrete type arguments. See the
332    // comment above in `WasmTy::typecheck`.
333    //
334    // Only ever called for concrete reference type arguments, so any type which
335    // is not in a type hierarchy with concrete reference types can implement
336    // this with `unreachable!()`.
337    #[doc(hidden)]
338    fn dynamic_concrete_type_check(
339        &self,
340        store: &StoreOpaque,
341        nullable: bool,
342        actual: &HeapType,
343    ) -> Result<()>;
344
345    // Is this a GC-managed reference that actually points to a GC object? That
346    // is, `self` is *not* an `i31`, null reference, or uninhabited type.
347    //
348    // Note that it is okay if this returns false positives (i.e. `true` for
349    // `Rooted<AnyRef>` without actually looking up the rooted `anyref` in the
350    // store and reflecting on it to determine whether it is actually an
351    // `i31`). However, it is not okay if this returns false negatives.
352    #[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    // Store `self` into `ptr`.
359    //
360    // NB: We _must not_ trigger a GC when passing refs from host code into Wasm
361    // (e.g. returned from a host function or passed as arguments to a Wasm
362    // function). After insertion into the activations table, the reference is
363    // no longer rooted. If multiple references are being sent from the host
364    // into Wasm and we allowed GCs during insertion, then the following events
365    // could happen:
366    //
367    // * Reference A is inserted into the activations table. This does not
368    //   trigger a GC, but does fill the table to capacity.
369    //
370    // * The caller's reference to A is removed. Now the only reference to A is
371    //   from the activations table.
372    //
373    // * Reference B is inserted into the activations table. Because the table
374    //   is at capacity, a GC is triggered.
375    //
376    // * A is reclaimed because the only reference keeping it alive was the
377    //   activation table's reference (it isn't inside any Wasm frames on the
378    //   stack yet, so stack scanning and stack maps don't increment its
379    //   reference count).
380    //
381    // * We transfer control to Wasm, giving it A and B. Wasm uses A. That's a
382    //   use-after-free bug.
383    //
384    // In conclusion, to prevent uses-after-free bugs, we cannot GC while
385    // converting types into their raw ABI forms.
386    #[doc(hidden)]
387    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>;
388
389    // Load a version of `Self` from the `ptr` provided.
390    //
391    // # Safety
392    //
393    // This function is unsafe as it's up to the caller to ensure that `ptr` is
394    // valid for this given type.
395    #[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            // `(ref null nofunc) <: (ref null $f)` for all function types `$f`.
519            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 = ptr.get_funcref();
570        debug_assert!(!p.is_null());
571        Func::from_vm_func_ref(store, p.cast()).unwrap()
572    }
573}
574
575unsafe impl WasmTy for Option<Func> {
576    #[inline]
577    fn valtype() -> ValType {
578        ValType::FUNCREF
579    }
580
581    #[inline]
582    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
583        if let Some(f) = self {
584            store.store_data().contains(f.0)
585        } else {
586            true
587        }
588    }
589
590    fn dynamic_concrete_type_check(
591        &self,
592        store: &StoreOpaque,
593        nullable: bool,
594        expected: &HeapType,
595    ) -> Result<()> {
596        if let Some(f) = self {
597            let expected = expected.unwrap_concrete_func();
598            f.ensure_matches_ty(store, expected)
599                .context("argument type mismatch for reference to concrete type")
600        } else if nullable {
601            Ok(())
602        } else {
603            bail!("argument type mismatch: expected non-nullable (ref {expected}), found null reference")
604        }
605    }
606
607    #[inline]
608    fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
609        let raw = if let Some(f) = self {
610            f.vm_func_ref(store).as_ptr()
611        } else {
612            ptr::null_mut()
613        };
614        ptr.write(ValRaw::funcref(raw.cast::<c_void>()));
615        Ok(())
616    }
617
618    #[inline]
619    unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
620        Func::from_vm_func_ref(store, ptr.get_funcref().cast())
621    }
622}
623
624/// A trait used for [`Func::typed`] and with [`TypedFunc`] to represent the set of
625/// parameters for wasm functions.
626///
627/// This is implemented for bare types that can be passed to wasm as well as
628/// tuples of those types.
629pub 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
652// Forward an impl from `T` to `(T,)` for convenience if there's only one
653// parameter.
654unsafe 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 ($(ref $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
762/// A trait used for [`Func::typed`] and with [`TypedFunc`] to represent the set of
763/// results for wasm functions.
764pub unsafe trait WasmResults: WasmParams {
765    #[doc(hidden)]
766    unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self;
767}
768
769// Forwards from a bare type `T` to the 1-tuple type `(T,)`
770unsafe 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);