Skip to main content

wasmtime/runtime/func/
typed.rs

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