Skip to main content

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