wasmtime/func/
typed.rs

1use super::{invoke_wasm_and_catch_traps, HostAbi};
2use crate::store::{AutoAssertNoGc, StoreOpaque};
3use crate::{AsContextMut, ExternRef, Func, FuncType, StoreContextMut, ValRaw, ValType};
4use anyhow::{bail, Result};
5use std::marker;
6use std::mem::{self, MaybeUninit};
7use std::ptr;
8use wasmtime_runtime::{
9    VMCallerCheckedFuncRef, VMContext, VMFunctionBody, VMOpaqueContext, VMSharedSignatureIndex,
10};
11
12/// A statically typed WebAssembly function.
13///
14/// Values of this type represent statically type-checked WebAssembly functions.
15/// The function within a [`TypedFunc`] is statically known to have `Params` as its
16/// parameters and `Results` as its results.
17///
18/// This structure is created via [`Func::typed`] or [`TypedFunc::new_unchecked`].
19/// For more documentation about this see those methods.
20#[repr(transparent)] // here for the C API
21pub struct TypedFunc<Params, Results> {
22    _a: marker::PhantomData<fn(Params) -> Results>,
23    func: Func,
24}
25
26impl<Params, Results> Copy for TypedFunc<Params, Results> {}
27
28impl<Params, Results> Clone for TypedFunc<Params, Results> {
29    fn clone(&self) -> TypedFunc<Params, Results> {
30        *self
31    }
32}
33
34impl<Params, Results> TypedFunc<Params, Results>
35where
36    Params: WasmParams,
37    Results: WasmResults,
38{
39    /// An unchecked version of [`Func::typed`] which does not perform a
40    /// typecheck and simply assumes that the type declared here matches the
41    /// type of this function.
42    ///
43    /// The semantics of this function are the same as [`Func::typed`] except
44    /// that no error is returned because no typechecking is done.
45    ///
46    /// # Unsafety
47    ///
48    /// This function only safe to call if `typed` would otherwise return `Ok`
49    /// for the same `Params` and `Results` specified. If `typed` would return
50    /// an error then the returned `TypedFunc` is memory unsafe to invoke.
51    pub unsafe fn new_unchecked(func: Func) -> TypedFunc<Params, Results> {
52        TypedFunc {
53            _a: marker::PhantomData,
54            func,
55        }
56    }
57
58    /// Returns the underlying [`Func`] that this is wrapping, losing the static
59    /// type information in the process.
60    pub fn func(&self) -> &Func {
61        &self.func
62    }
63
64    /// Invokes this WebAssembly function with the specified parameters.
65    ///
66    /// Returns either the results of the call, or a [`Trap`] if one happened.
67    ///
68    /// For more information, see the [`Func::typed`] and [`Func::call`]
69    /// documentation.
70    ///
71    /// # Errors
72    ///
73    /// For more information on errors see the documentation on [`Func::call`].
74    ///
75    /// # Panics
76    ///
77    /// This function will panic if it is called when the underlying [`Func`] is
78    /// connected to an asynchronous store.
79    ///
80    /// [`Trap`]: crate::Trap
81    pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results> {
82        let mut store = store.as_context_mut();
83        assert!(
84            !store.0.async_support(),
85            "must use `call_async` with async stores"
86        );
87        let func = self.func.caller_checked_anyfunc(store.0);
88        unsafe { Self::call_raw(&mut store, func, params) }
89    }
90
91    /// Invokes this WebAssembly function with the specified parameters.
92    ///
93    /// Returns either the results of the call, or a [`Trap`] if one happened.
94    ///
95    /// For more information, see the [`Func::typed`] and [`Func::call_async`]
96    /// documentation.
97    ///
98    /// # Errors
99    ///
100    /// For more information on errors see the documentation on [`Func::call`].
101    ///
102    /// # Panics
103    ///
104    /// This function will panic if it is called when the underlying [`Func`] is
105    /// connected to a synchronous store.
106    ///
107    /// [`Trap`]: crate::Trap
108    #[cfg(feature = "async")]
109    #[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
110    pub async fn call_async<T>(
111        &self,
112        mut store: impl AsContextMut<Data = T>,
113        params: Params,
114    ) -> Result<Results>
115    where
116        T: Send,
117    {
118        let mut store = store.as_context_mut();
119        assert!(
120            store.0.async_support(),
121            "must use `call` with non-async stores"
122        );
123        store
124            .on_fiber(|store| {
125                let func = self.func.caller_checked_anyfunc(store.0);
126                unsafe { Self::call_raw(store, func, params) }
127            })
128            .await?
129    }
130
131    pub(crate) unsafe fn call_raw<T>(
132        store: &mut StoreContextMut<'_, T>,
133        func: ptr::NonNull<VMCallerCheckedFuncRef>,
134        params: Params,
135    ) -> Result<Results> {
136        // double-check that params/results match for this function's type in
137        // debug mode.
138        if cfg!(debug_assertions) {
139            Self::debug_typecheck(store.0, func.as_ref().type_index);
140        }
141
142        // See the comment in `Func::call_impl`'s `write_params` function.
143        if params.externrefs_count()
144            > store
145                .0
146                .externref_activations_table()
147                .bump_capacity_remaining()
148        {
149            store.gc();
150        }
151
152        // Validate that all runtime values flowing into this store indeed
153        // belong within this store, otherwise it would be unsafe for store
154        // values to cross each other.
155
156        let params = {
157            // GC is not safe here, since we move refs into the activations
158            // table but don't hold a strong reference onto them until we enter
159            // the Wasm frame and they get referenced from the stack maps.
160            let mut store = AutoAssertNoGc::new(&mut **store.as_context_mut().0);
161
162            match params.into_abi(&mut store) {
163                Some(abi) => abi,
164                None => {
165                    bail!("attempt to pass cross-`Store` value to Wasm as function argument")
166                }
167            }
168        };
169
170        // Try to capture only a single variable (a tuple) in the closure below.
171        // This means the size of the closure is one pointer and is much more
172        // efficient to move in memory. This closure is actually invoked on the
173        // other side of a C++ shim, so it can never be inlined enough to make
174        // the memory go away, so the size matters here for performance.
175        let mut captures = (func, MaybeUninit::uninit(), params, false);
176
177        let result = invoke_wasm_and_catch_traps(store, |caller| {
178            let (anyfunc, ret, params, returned) = &mut captures;
179            let anyfunc = anyfunc.as_ref();
180            let result = Params::invoke::<Results>(
181                anyfunc.func_ptr.as_ptr(),
182                anyfunc.vmctx,
183                caller,
184                *params,
185            );
186            ptr::write(ret.as_mut_ptr(), result);
187            *returned = true
188        });
189        let (_, ret, _, returned) = captures;
190        debug_assert_eq!(result.is_ok(), returned);
191        result?;
192        Ok(Results::from_abi(store.0, ret.assume_init()))
193    }
194
195    /// Purely a debug-mode assertion, not actually used in release builds.
196    fn debug_typecheck(store: &StoreOpaque, func: VMSharedSignatureIndex) {
197        let ty = FuncType::from_wasm_func_type(
198            store
199                .engine()
200                .signatures()
201                .lookup_type(func)
202                .expect("signature should be registered"),
203        );
204        Params::typecheck(ty.params()).expect("params should match");
205        Results::typecheck(ty.results()).expect("results should match");
206    }
207}
208
209/// A trait implemented for types which can be arguments and results for
210/// closures passed to [`Func::wrap`] as well as parameters to [`Func::typed`].
211///
212/// This trait should not be implemented by user types. This trait may change at
213/// any time internally. The types which implement this trait, however, are
214/// stable over time.
215///
216/// For more information see [`Func::wrap`] and [`Func::typed`]
217pub unsafe trait WasmTy: Send {
218    #[doc(hidden)]
219    type Abi: Copy;
220    #[doc(hidden)]
221    #[inline]
222    fn typecheck(ty: crate::ValType) -> Result<()> {
223        if ty == Self::valtype() {
224            Ok(())
225        } else {
226            bail!("expected {} found {}", Self::valtype(), ty)
227        }
228    }
229    #[doc(hidden)]
230    fn valtype() -> ValType;
231    #[doc(hidden)]
232    fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
233    #[doc(hidden)]
234    fn is_externref(&self) -> bool;
235    #[doc(hidden)]
236    unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi;
237    #[doc(hidden)]
238    unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw);
239    #[doc(hidden)]
240    fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi;
241    #[doc(hidden)]
242    unsafe fn from_abi(abi: Self::Abi, store: &mut StoreOpaque) -> Self;
243}
244
245macro_rules! integers {
246    ($($primitive:ident/$get_primitive:ident => $ty:ident in $raw:ident)*) => ($(
247        unsafe impl WasmTy for $primitive {
248            type Abi = $primitive;
249            #[inline]
250            fn valtype() -> ValType {
251                ValType::$ty
252            }
253            #[inline]
254            fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
255                true
256            }
257            #[inline]
258            fn is_externref(&self) -> bool {
259                false
260            }
261            #[inline]
262            unsafe fn abi_from_raw(raw: *mut ValRaw) -> $primitive {
263                (*raw).$get_primitive()
264            }
265            #[inline]
266            unsafe fn abi_into_raw(abi: $primitive, raw: *mut ValRaw) {
267                *raw = ValRaw::$primitive(abi);
268            }
269            #[inline]
270            fn into_abi(self, _store: &mut StoreOpaque) -> Self::Abi {
271                self
272            }
273            #[inline]
274            unsafe fn from_abi(abi: Self::Abi, _store: &mut StoreOpaque) -> Self {
275                abi
276            }
277        }
278    )*)
279}
280
281integers! {
282    i32/get_i32 => I32 in i32
283    i64/get_i64 => I64 in i64
284    u32/get_u32 => I32 in i32
285    u64/get_u64 => I64 in i64
286}
287
288macro_rules! floats {
289    ($($float:ident/$int:ident/$get_float:ident => $ty:ident)*) => ($(
290        unsafe impl WasmTy for $float {
291            type Abi = $float;
292            #[inline]
293            fn valtype() -> ValType {
294                ValType::$ty
295            }
296            #[inline]
297            fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
298                true
299            }
300            #[inline]
301            fn is_externref(&self) -> bool {
302                false
303            }
304            #[inline]
305            unsafe fn abi_from_raw(raw: *mut ValRaw) -> $float {
306                $float::from_bits((*raw).$get_float())
307            }
308            #[inline]
309            unsafe fn abi_into_raw(abi: $float, raw: *mut ValRaw) {
310                *raw = ValRaw::$float(abi.to_bits());
311            }
312            #[inline]
313            fn into_abi(self, _store: &mut StoreOpaque) -> Self::Abi {
314                self
315            }
316            #[inline]
317            unsafe fn from_abi(abi: Self::Abi, _store: &mut StoreOpaque) -> Self {
318                abi
319            }
320        }
321    )*)
322}
323
324floats! {
325    f32/u32/get_f32 => F32
326    f64/u64/get_f64 => F64
327}
328
329unsafe impl WasmTy for Option<ExternRef> {
330    type Abi = *mut u8;
331
332    #[inline]
333    fn valtype() -> ValType {
334        ValType::ExternRef
335    }
336
337    #[inline]
338    fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
339        true
340    }
341
342    #[inline]
343    fn is_externref(&self) -> bool {
344        true
345    }
346
347    #[inline]
348    unsafe fn abi_from_raw(raw: *mut ValRaw) -> *mut u8 {
349        (*raw).get_externref() as *mut u8
350    }
351
352    #[inline]
353    unsafe fn abi_into_raw(abi: *mut u8, raw: *mut ValRaw) {
354        *raw = ValRaw::externref(abi as usize);
355    }
356
357    #[inline]
358    fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi {
359        if let Some(x) = self {
360            let abi = x.inner.as_raw();
361            unsafe {
362                // NB: We _must not_ trigger a GC when passing refs from host
363                // code into Wasm (e.g. returned from a host function or passed
364                // as arguments to a Wasm function). After insertion into the
365                // table, this reference is no longer rooted. If multiple
366                // references are being sent from the host into Wasm and we
367                // allowed GCs during insertion, then the following events could
368                // happen:
369                //
370                // * Reference A is inserted into the activations
371                //   table. This does not trigger a GC, but does fill the table
372                //   to capacity.
373                //
374                // * The caller's reference to A is removed. Now the only
375                //   reference to A is from the activations table.
376                //
377                // * Reference B is inserted into the activations table. Because
378                //   the table is at capacity, a GC is triggered.
379                //
380                // * A is reclaimed because the only reference keeping it alive
381                //   was the activation table's reference (it isn't inside any
382                //   Wasm frames on the stack yet, so stack scanning and stack
383                //   maps don't increment its reference count).
384                //
385                // * We transfer control to Wasm, giving it A and B. Wasm uses
386                //   A. That's a use after free.
387                //
388                // In conclusion, to prevent uses after free, we cannot GC
389                // during this insertion.
390                let mut store = AutoAssertNoGc::new(store);
391                store.insert_vmexternref_without_gc(x.inner);
392            }
393            abi
394        } else {
395            ptr::null_mut()
396        }
397    }
398
399    #[inline]
400    unsafe fn from_abi(abi: Self::Abi, _store: &mut StoreOpaque) -> Self {
401        if abi.is_null() {
402            None
403        } else {
404            Some(ExternRef {
405                inner: wasmtime_runtime::VMExternRef::clone_from_raw(abi),
406            })
407        }
408    }
409}
410
411unsafe impl WasmTy for Option<Func> {
412    type Abi = *mut wasmtime_runtime::VMCallerCheckedFuncRef;
413
414    #[inline]
415    fn valtype() -> ValType {
416        ValType::FuncRef
417    }
418
419    #[inline]
420    fn compatible_with_store<'a>(&self, store: &StoreOpaque) -> bool {
421        if let Some(f) = self {
422            store.store_data().contains(f.0)
423        } else {
424            true
425        }
426    }
427
428    #[inline]
429    fn is_externref(&self) -> bool {
430        false
431    }
432
433    #[inline]
434    unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi {
435        (*raw).get_funcref() as Self::Abi
436    }
437
438    #[inline]
439    unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) {
440        *raw = ValRaw::funcref(abi as usize);
441    }
442
443    #[inline]
444    fn into_abi(self, store: &mut StoreOpaque) -> Self::Abi {
445        if let Some(f) = self {
446            f.caller_checked_anyfunc(store).as_ptr()
447        } else {
448            ptr::null_mut()
449        }
450    }
451
452    #[inline]
453    unsafe fn from_abi(abi: Self::Abi, store: &mut StoreOpaque) -> Self {
454        Func::from_caller_checked_anyfunc(store, abi)
455    }
456}
457
458/// A trait used for [`Func::typed`] and with [`TypedFunc`] to represent the set of
459/// parameters for wasm functions.
460///
461/// This is implemented for bare types that can be passed to wasm as well as
462/// tuples of those types.
463pub unsafe trait WasmParams: Send {
464    #[doc(hidden)]
465    type Abi: Copy;
466
467    #[doc(hidden)]
468    fn typecheck(params: impl ExactSizeIterator<Item = crate::ValType>) -> Result<()>;
469
470    #[doc(hidden)]
471    fn externrefs_count(&self) -> usize;
472
473    #[doc(hidden)]
474    fn into_abi(self, store: &mut StoreOpaque) -> Option<Self::Abi>;
475
476    #[doc(hidden)]
477    unsafe fn invoke<R: WasmResults>(
478        func: *const VMFunctionBody,
479        vmctx1: *mut VMOpaqueContext,
480        vmctx2: *mut VMContext,
481        abi: Self::Abi,
482    ) -> R::ResultAbi;
483}
484
485// Forward an impl from `T` to `(T,)` for convenience if there's only one
486// parameter.
487unsafe impl<T> WasmParams for T
488where
489    T: WasmTy,
490{
491    type Abi = <(T,) as WasmParams>::Abi;
492
493    fn typecheck(params: impl ExactSizeIterator<Item = crate::ValType>) -> Result<()> {
494        <(T,) as WasmParams>::typecheck(params)
495    }
496
497    #[inline]
498    fn externrefs_count(&self) -> usize {
499        T::is_externref(self) as usize
500    }
501
502    #[inline]
503    fn into_abi(self, store: &mut StoreOpaque) -> Option<Self::Abi> {
504        <(T,) as WasmParams>::into_abi((self,), store)
505    }
506
507    unsafe fn invoke<R: WasmResults>(
508        func: *const VMFunctionBody,
509        vmctx1: *mut VMOpaqueContext,
510        vmctx2: *mut VMContext,
511        abi: Self::Abi,
512    ) -> R::ResultAbi {
513        <(T,) as WasmParams>::invoke::<R>(func, vmctx1, vmctx2, abi)
514    }
515}
516
517macro_rules! impl_wasm_params {
518    ($n:tt $($t:ident)*) => {
519        #[allow(non_snake_case)]
520        unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) {
521            type Abi = ($($t::Abi,)*);
522
523            fn typecheck(mut params: impl ExactSizeIterator<Item = crate::ValType>) -> Result<()> {
524                let mut _n = 0;
525
526                $(
527                    match params.next() {
528                        Some(t) => {
529                            _n += 1;
530                            $t::typecheck(t)?
531                        },
532                        None => bail!("expected {} types, found {}", $n, params.len() + _n),
533                    }
534                )*
535
536                match params.next() {
537                    None => Ok(()),
538                    Some(_) => {
539                        _n += 1;
540                        bail!("expected {} types, found {}", $n, params.len() + _n)
541                    },
542                }
543            }
544
545            #[inline]
546            fn externrefs_count(&self) -> usize {
547                let ($(ref $t,)*) = self;
548                0 $(
549                    + $t.is_externref() as usize
550                )*
551            }
552
553
554            #[inline]
555            fn into_abi(self, _store: &mut StoreOpaque) -> Option<Self::Abi> {
556                let ($($t,)*) = self;
557                $(
558                    let $t = if $t.compatible_with_store(_store) {
559                        $t.into_abi(_store)
560                    } else {
561                        return None;
562                    };
563                )*
564                Some(($($t,)*))
565            }
566
567            unsafe fn invoke<R: WasmResults>(
568                func: *const VMFunctionBody,
569                vmctx1: *mut VMOpaqueContext,
570                vmctx2: *mut VMContext,
571                abi: Self::Abi,
572            ) -> R::ResultAbi {
573                let fnptr = mem::transmute::<
574                    *const VMFunctionBody,
575                    unsafe extern "C" fn(
576                        *mut VMOpaqueContext,
577                        *mut VMContext,
578                        $($t::Abi,)*
579                        <R::ResultAbi as HostAbi>::Retptr,
580                    ) -> <R::ResultAbi as HostAbi>::Abi,
581                    >(func);
582                let ($($t,)*) = abi;
583                // Use the `call` function to acquire a `retptr` which we'll
584                // forward to the native function. Once we have it we also
585                // convert all our arguments to abi arguments to go to the raw
586                // function.
587                //
588                // Upon returning `R::call` will convert all the returns back
589                // into `R`.
590                <R::ResultAbi as HostAbi>::call(|retptr| {
591                    let fnptr = wasmtime_runtime::prepare_host_to_wasm_trampoline(vmctx2, fnptr);
592                    fnptr(vmctx1, vmctx2, $($t,)* retptr)
593                })
594            }
595        }
596    };
597}
598
599for_each_function_signature!(impl_wasm_params);
600
601/// A trait used for [`Func::typed`] and with [`TypedFunc`] to represent the set of
602/// results for wasm functions.
603///
604/// This is currently only implemented for `()` and for bare types that can be
605/// returned. This is not yet implemented for tuples because a multi-value
606/// `TypedFunc` is not currently supported.
607pub unsafe trait WasmResults: WasmParams {
608    #[doc(hidden)]
609    type ResultAbi: HostAbi;
610    #[doc(hidden)]
611    unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self;
612}
613
614// Forwards from a bare type `T` to the 1-tuple type `(T,)`
615unsafe impl<T: WasmTy> WasmResults for T
616where
617    (T::Abi,): HostAbi,
618{
619    type ResultAbi = <(T,) as WasmResults>::ResultAbi;
620
621    unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self {
622        <(T,) as WasmResults>::from_abi(store, abi).0
623    }
624}
625
626macro_rules! impl_wasm_results {
627    ($n:tt $($t:ident)*) => {
628        #[allow(non_snake_case, unused_variables)]
629        unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*)
630            where ($($t::Abi,)*): HostAbi
631        {
632            type ResultAbi = ($($t::Abi,)*);
633
634            #[inline]
635            unsafe fn from_abi(store: &mut StoreOpaque, abi: Self::ResultAbi) -> Self {
636                let ($($t,)*) = abi;
637                ($($t::from_abi($t, store),)*)
638            }
639        }
640    };
641}
642
643for_each_function_signature!(impl_wasm_results);