wasmi_c_api/
func.rs

1use crate::{
2    wasm_extern_t,
3    wasm_functype_t,
4    wasm_store_t,
5    wasm_trap_t,
6    wasm_val_t,
7    wasm_val_vec_t,
8};
9use alloc::{boxed::Box, string::String, vec, vec::Vec};
10use core::{any::Any, ffi::c_void, hint, iter, panic::AssertUnwindSafe, ptr, str};
11use wasmi::{Error, Extern, Func, FuncRef, Val};
12
13/// A Wasm function.
14///
15/// Wraps [`Func`].
16#[derive(Clone)]
17#[repr(transparent)]
18pub struct wasm_func_t {
19    inner: wasm_extern_t,
20}
21
22wasmi_c_api_macros::declare_ref!(wasm_func_t);
23
24/// A Wasm host function callback.
25pub type wasm_func_callback_t = extern "C" fn(
26    params: *const wasm_val_vec_t,
27    results: *mut wasm_val_vec_t,
28) -> Option<Box<wasm_trap_t>>;
29
30/// A Wasm host function callback with access to environmental data.
31pub type wasm_func_callback_with_env_t = extern "C" fn(
32    env: *mut c_void,
33    params: *const wasm_val_vec_t,
34    results: *mut wasm_val_vec_t,
35) -> Option<Box<wasm_trap_t>>;
36
37impl wasm_func_t {
38    pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_func_t> {
39        match &e.which {
40            Extern::Func(_) => Some(unsafe { &*(e as *const _ as *const _) }),
41            _ => None,
42        }
43    }
44
45    pub(crate) fn try_from_mut(e: &mut wasm_extern_t) -> Option<&mut wasm_func_t> {
46        match &mut e.which {
47            Extern::Func(_) => Some(unsafe { &mut *(e as *mut _ as *mut _) }),
48            _ => None,
49        }
50    }
51
52    /// Returns the underlying [`Func`] of the [`wasm_func_t`].
53    pub(crate) fn func(&self) -> Func {
54        match self.inner.which {
55            Extern::Func(f) => f,
56            _ => unsafe { hint::unreachable_unchecked() },
57        }
58    }
59}
60
61/// Creates a [`wasm_func_t`] from the [`wasm_functype_t`] and C-like closure for the [`wasm_store_t`].
62///
63/// # Note
64///
65/// This is a convenience method that internally creates a trampoline Rust-like closure around
66/// that C-like closure to propagate the Wasm function call and do all marshalling that is required.
67///
68/// # Safety
69///
70/// It is the caller's responsibility not to alias the [`wasm_functype_t`]
71/// with its underlying, internal [`WasmStoreRef`](crate::WasmStoreRef).
72unsafe fn create_function(
73    store: &mut wasm_store_t,
74    ty: &wasm_functype_t,
75    func: impl Fn(*const wasm_val_vec_t, *mut wasm_val_vec_t) -> Option<Box<wasm_trap_t>>
76        + Send
77        + Sync
78        + 'static,
79) -> Box<wasm_func_t> {
80    let ty = ty.ty().ty.clone();
81    let func = Func::new(
82        store.inner.context_mut(),
83        ty,
84        move |_caller, params, results| {
85            let params: wasm_val_vec_t = params
86                .iter()
87                .cloned()
88                .map(wasm_val_t::from)
89                .collect::<Box<[_]>>()
90                .into();
91            let mut out_results: wasm_val_vec_t = vec![wasm_val_t::default(); results.len()].into();
92            if let Some(trap) = func(&params, &mut out_results) {
93                return Err(trap.error);
94            }
95            results
96                .iter_mut()
97                .zip(out_results.as_slice())
98                .for_each(|(result, out_results)| {
99                    *result = out_results.to_val();
100                });
101            Ok(())
102        },
103    );
104    Box::new(wasm_func_t {
105        inner: wasm_extern_t {
106            store: store.inner.clone(),
107            which: func.into(),
108        },
109    })
110}
111
112/// Creates a new [`wasm_func_t`] of type [`wasm_functype_t`] for the [`wasm_store_t`].
113///
114/// Calls the given [`wasm_func_callback_t`] when calling the returned [`wasm_func_t`].
115///
116/// Wraps [`Func::new`].
117///
118/// # Safety
119///
120/// It is the caller's responsibility not to alias the [`wasm_functype_t`]
121/// with its underlying, internal [`WasmStoreRef`](crate::WasmStoreRef).
122#[cfg_attr(not(feature = "prefix-symbols"), no_mangle)]
123#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
124pub unsafe extern "C" fn wasm_func_new(
125    store: &mut wasm_store_t,
126    ty: &wasm_functype_t,
127    callback: wasm_func_callback_t,
128) -> Box<wasm_func_t> {
129    #[allow(clippy::redundant_closure)] // TODO: remove once the `clippy` false-positive is fixed
130    create_function(store, ty, move |params, results| callback(params, results))
131}
132
133/// Creates a new [`wasm_func_t`] of type [`wasm_functype_t`] for the [`wasm_store_t`].
134///
135/// - Calls the given [`wasm_func_callback_t`] when calling the returned [`wasm_func_t`].
136/// - Unlike [`wasm_func_new`] this also allows to access environment data in the function closure.
137///
138/// Wraps [`Func::new`].
139///
140/// # Safety
141///
142/// It is the caller's responsibility not to alias the [`wasm_functype_t`]
143/// with its underlying, internal [`WasmStoreRef`](crate::WasmStoreRef).
144#[cfg_attr(not(feature = "prefix-symbols"), no_mangle)]
145#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
146pub unsafe extern "C" fn wasm_func_new_with_env(
147    store: &mut wasm_store_t,
148    ty: &wasm_functype_t,
149    callback: wasm_func_callback_with_env_t,
150    data: *mut c_void,
151    finalizer: Option<extern "C" fn(arg1: *mut c_void)>,
152) -> Box<wasm_func_t> {
153    let finalizer = crate::ForeignData { data, finalizer };
154    create_function(store, ty, move |params, results| {
155        let _ = &finalizer; // move entire finalizer into this closure
156        callback(finalizer.data, params, results)
157    })
158}
159
160/// Prepares `dst` to be populated with `params` and reserve space for `len_results`.
161///
162/// The parameters and results are returned as separate slices.
163fn prepare_params_and_results(
164    dst: &mut Vec<Val>,
165    params: impl ExactSizeIterator<Item = Val>,
166    len_results: usize,
167) -> (&[Val], &mut [Val]) {
168    debug_assert!(dst.is_empty());
169    let len_params = params.len();
170    dst.reserve(len_params + len_results);
171    dst.extend(params);
172    dst.extend(iter::repeat_n(Val::FuncRef(FuncRef::null()), len_results));
173    let (params, results) = dst.split_at_mut(len_params);
174    (params, results)
175}
176
177/// Calls the [`wasm_func_t`] with the given `params` and stores the result in `results`.
178///
179/// - Returns a [`wasm_trap_t`] if the Wasm function call failed or trapped.
180/// - Returns a `null` pointer if the Wasm function call succeeded.
181///
182/// Wraps [`Func::call`].
183///
184/// # Safety
185///
186/// It is the caller's responsibility not to alias the [`wasm_func_t`]
187/// with its underlying, internal [`WasmStoreRef`](crate::WasmStoreRef).
188#[cfg_attr(not(feature = "prefix-symbols"), no_mangle)]
189#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
190pub unsafe extern "C" fn wasm_func_call(
191    func: &mut wasm_func_t,
192    params: *const wasm_val_vec_t,
193    results: *mut wasm_val_vec_t,
194) -> *mut wasm_trap_t {
195    let f = func.func();
196    let results = (*results).as_uninit_slice();
197    let params = (*params).as_slice();
198    let mut dst = Vec::new();
199    let (wt_params, wt_results) =
200        prepare_params_and_results(&mut dst, params.iter().map(|i| i.to_val()), results.len());
201
202    let result = {
203        #[cfg(feature = "std")]
204        {
205            // We're calling arbitrary code here most of the time, and we in general
206            // want to try to insulate callers against bugs in wasmtime/wasi/etc if we
207            // can. As a result we catch panics here and transform them to traps to
208            // allow the caller to have any insulation possible against Rust panics.
209            std::panic::catch_unwind(AssertUnwindSafe(|| {
210                f.call(func.inner.store.context_mut(), wt_params, wt_results)
211            }))
212        }
213        #[cfg(not(feature = "std"))]
214        {
215            Ok(f.call(func.inner.store.context_mut(), wt_params, wt_results))
216        }
217    };
218    match result {
219        Ok(Ok(())) => {
220            for (slot, val) in results.iter_mut().zip(wt_results.iter().cloned()) {
221                crate::initialize(slot, wasm_val_t::from(val));
222            }
223            ptr::null_mut()
224        }
225        Ok(Err(err)) => Box::into_raw(Box::new(wasm_trap_t::new(err))),
226        Err(panic) => {
227            let err = error_from_panic(panic);
228            let trap = Box::new(wasm_trap_t::new(err));
229            Box::into_raw(trap)
230        }
231    }
232}
233
234/// Converts the panic data to a Wasmi [`Error`] as a best-effort basis.
235fn error_from_panic(panic: Box<dyn Any + Send>) -> Error {
236    if let Some(msg) = panic.downcast_ref::<String>() {
237        Error::new(msg.clone())
238    } else if let Some(msg) = panic.downcast_ref::<&'static str>() {
239        Error::new(*msg)
240    } else {
241        Error::new("panic happened on the Rust side")
242    }
243}
244
245/// Returns the [`wasm_functype_t`] of the [`wasm_func_t`].
246///
247/// Wraps [`Func::ty`].
248///
249/// # Safety
250///
251/// It is the caller's responsibility not to alias the [`wasm_func_t`]
252/// with its underlying, internal [`WasmStoreRef`](crate::WasmStoreRef).
253#[cfg_attr(not(feature = "prefix-symbols"), no_mangle)]
254#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
255pub unsafe extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box<wasm_functype_t> {
256    Box::new(wasm_functype_t::new(f.func().ty(f.inner.store.context())))
257}
258
259/// Returns the number of parameter types of the [`wasm_func_t`].
260///
261/// Wraps [`Func::ty`], followed by [`FuncType::params`] and a call to `len`.
262///
263/// # Safety
264///
265/// It is the caller's responsibility not to alias the [`wasm_func_t`]
266/// with its underlying, internal [`WasmStoreRef`](crate::WasmStoreRef).
267///
268/// [`FuncType::params`]: wasmi::FuncType::params
269#[cfg_attr(not(feature = "prefix-symbols"), no_mangle)]
270#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
271pub unsafe extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize {
272    f.func().ty(f.inner.store.context()).params().len()
273}
274
275/// Returns the number of result types of the [`wasm_func_t`].
276///
277/// Wraps [`Func::ty`], followed by [`FuncType::results`] and a call to `len`.
278///
279/// # Safety
280///
281/// It is the caller's responsibility not to alias the [`wasm_func_t`]
282/// with its underlying, internal [`WasmStoreRef`](crate::WasmStoreRef).
283///
284/// [`FuncType::results`]: wasmi::FuncType::results
285#[cfg_attr(not(feature = "prefix-symbols"), no_mangle)]
286#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
287pub unsafe extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize {
288    f.func().ty(f.inner.store.context()).results().len()
289}
290
291/// Returns the [`wasm_func_t`] as mutable reference to [`wasm_extern_t`].
292#[cfg_attr(not(feature = "prefix-symbols"), no_mangle)]
293#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
294pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t {
295    &mut f.inner
296}
297
298/// Returns the [`wasm_func_t`] as shared reference to [`wasm_extern_t`].
299#[cfg_attr(not(feature = "prefix-symbols"), no_mangle)]
300#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
301pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t {
302    &f.inner
303}