wasmer_c_api/wasm_c_api/externals/
function.rs

1use super::super::store::{StoreRef, wasm_store_t};
2use super::super::trap::wasm_trap_t;
3use super::super::types::{wasm_functype_t, wasm_ref_t, wasm_valkind_enum};
4use super::super::value::{wasm_val_inner, wasm_val_t, wasm_val_vec_t};
5use super::wasm_extern_t;
6use crate::error::update_last_error;
7use crate::wasm_c_api::function_env::FunctionCEnv;
8use libc::c_void;
9use std::mem::MaybeUninit;
10use std::sync::{Arc, Mutex};
11use wasmer_api::{Extern, Function, FunctionEnv, FunctionEnvMut, RuntimeError, Value};
12
13#[derive(Clone)]
14#[allow(non_camel_case_types)]
15#[repr(C)]
16pub struct wasm_func_t {
17    pub(crate) extern_: wasm_extern_t,
18}
19
20impl wasm_func_t {
21    pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_func_t> {
22        match &e.inner {
23            Extern::Function(_) => Some(unsafe { &*(e as *const _ as *const _) }),
24            _ => None,
25        }
26    }
27}
28
29#[allow(non_camel_case_types)]
30pub type wasm_func_callback_t = unsafe extern "C" fn(
31    args: &wasm_val_vec_t,
32    results: &mut wasm_val_vec_t,
33) -> Option<Box<wasm_trap_t>>;
34
35#[allow(non_camel_case_types)]
36pub type wasm_func_callback_with_env_t = unsafe extern "C" fn(
37    env: *mut c_void,
38    args: &wasm_val_vec_t,
39    results: &mut wasm_val_vec_t,
40) -> Option<Box<wasm_trap_t>>;
41
42#[allow(non_camel_case_types)]
43pub type wasm_env_finalizer_t = unsafe extern "C" fn(*mut c_void);
44
45/// Convert host-callback argument [`Value`]s into C `wasm_val_t`s, boxing
46/// reference values into `store`.
47fn callback_args_to_wasm(args: &[Value], store: &StoreRef) -> Result<wasm_val_vec_t, RuntimeError> {
48    let vals = args
49        .iter()
50        .map(|v| wasm_val_t::from_value(v, store))
51        .collect::<Result<Vec<wasm_val_t>, &'static str>>()
52        .map_err(RuntimeError::new)?;
53    Ok(vals.into())
54}
55
56/// Convert the C `wasm_val_t`s produced by a host callback back into [`Value`]s.
57fn callback_results_to_values(results: Vec<wasm_val_t>) -> Result<Vec<Value>, RuntimeError> {
58    results
59        .into_iter()
60        .map(Value::try_from)
61        .collect::<Result<Vec<Value>, &'static str>>()
62        .map_err(RuntimeError::new)
63}
64
65#[unsafe(no_mangle)]
66pub unsafe extern "C" fn wasm_func_new(
67    store: Option<&mut wasm_store_t>,
68    function_type: Option<&wasm_functype_t>,
69    callback: Option<wasm_func_callback_t>,
70) -> Option<Box<wasm_func_t>> {
71    let function_type = function_type?;
72    let callback = callback?;
73    let store = store?;
74    // Capture a weak store handle (not a strong `StoreRef`) to avoid a
75    // store → function → store cycle; upgrade it per call to box ref args.
76    let store_weak = store.inner.downgrade();
77    let mut store_mut = unsafe { store.inner.store_mut() };
78
79    let func_sig = &function_type.inner().function_type;
80    let num_rets = func_sig.results().len();
81    let inner_callback = move |mut _env: FunctionEnvMut<'_, FunctionCEnv>,
82                               args: &[Value]|
83          -> Result<Vec<Value>, RuntimeError> {
84        let store = store_weak
85            .upgrade()
86            .ok_or_else(|| RuntimeError::new("store was dropped"))?;
87        let processed_args = callback_args_to_wasm(args, &store)?;
88
89        let mut results: wasm_val_vec_t = vec![
90            wasm_val_t {
91                kind: wasm_valkind_enum::WASM_I64 as _,
92                of: wasm_val_inner { int64_t: 0 },
93            };
94            num_rets
95        ]
96        .into();
97
98        let trap = unsafe { callback(&processed_args, &mut results) };
99
100        if let Some(trap) = trap {
101            return Err(trap.inner);
102        }
103
104        callback_results_to_values(results.take())
105    };
106    let env = FunctionEnv::new(&mut store_mut, FunctionCEnv::default());
107    let function = Function::new_with_env(&mut store_mut, &env, func_sig, inner_callback);
108    Some(Box::new(wasm_func_t {
109        extern_: wasm_extern_t::new(store.inner.clone(), function.into()),
110    }))
111}
112
113#[unsafe(no_mangle)]
114pub unsafe extern "C" fn wasm_func_new_with_env(
115    store: Option<&mut wasm_store_t>,
116    function_type: Option<&wasm_functype_t>,
117    callback: Option<wasm_func_callback_with_env_t>,
118    env: *mut c_void,
119    env_finalizer: Option<wasm_env_finalizer_t>,
120) -> Option<Box<wasm_func_t>> {
121    let function_type = function_type?;
122    let callback = callback?;
123    let store = store?;
124    let store_weak = store.inner.downgrade();
125    let mut store_mut = unsafe { store.inner.store_mut() };
126
127    let func_sig = &function_type.inner().function_type;
128    let num_rets = func_sig.results().len();
129
130    #[derive(Clone)]
131    #[repr(C)]
132    struct WrapperEnv {
133        env: FunctionCEnv,
134        env_finalizer: Arc<Mutex<Option<wasm_env_finalizer_t>>>,
135    }
136
137    // Only relevant when using multiple threads in the C API;
138    // Synchronization will be done via the C API / on the C side.
139    unsafe impl Send for WrapperEnv {}
140    unsafe impl Sync for WrapperEnv {}
141
142    impl Drop for WrapperEnv {
143        fn drop(&mut self) {
144            if let Ok(mut guard) = self.env_finalizer.lock()
145                && Arc::strong_count(&self.env_finalizer) == 1
146                && let Some(env_finalizer) = guard.take()
147            {
148                unsafe { (env_finalizer)(self.env.as_ptr()) };
149            }
150        }
151    }
152    let inner_callback = move |env: FunctionEnvMut<'_, WrapperEnv>,
153                               args: &[Value]|
154          -> Result<Vec<Value>, RuntimeError> {
155        let store = store_weak
156            .upgrade()
157            .ok_or_else(|| RuntimeError::new("store was dropped"))?;
158        let processed_args = callback_args_to_wasm(args, &store)?;
159
160        let mut results: wasm_val_vec_t = vec![
161            wasm_val_t {
162                kind: wasm_valkind_enum::WASM_I64 as _,
163                of: wasm_val_inner { int64_t: 0 },
164            };
165            num_rets
166        ]
167        .into();
168
169        let trap = unsafe { callback(env.data().env.as_ptr(), &processed_args, &mut results) };
170
171        if let Some(trap) = trap {
172            return Err(trap.inner);
173        }
174
175        callback_results_to_values(results.take())
176    };
177    let env = FunctionEnv::new(
178        &mut store_mut,
179        WrapperEnv {
180            env: FunctionCEnv::new(c_try!(
181                std::ptr::NonNull::new(env),
182                "Function environment cannot be a null pointer."
183            )),
184            env_finalizer: Arc::new(Mutex::new(env_finalizer)),
185        },
186    );
187    let function = Function::new_with_env(&mut store_mut, &env, func_sig, inner_callback);
188    Some(Box::new(wasm_func_t {
189        extern_: wasm_extern_t::new(store.inner.clone(), function.into()),
190    }))
191}
192
193#[unsafe(no_mangle)]
194pub extern "C" fn wasm_func_copy(func: &wasm_func_t) -> Box<wasm_func_t> {
195    Box::new(func.clone())
196}
197
198#[unsafe(no_mangle)]
199pub unsafe extern "C" fn wasm_func_delete(_func: Option<Box<wasm_func_t>>) {}
200
201// A `funcref` view of a function, and back. Per `wasm.h` these are non-owning
202// views; since our `wasm_ref_t` is a distinct allocation the returned ref is
203// typically never freed and leaks. It holds only a weak store handle, so it
204// does not pin the store.
205//
206// NOTE: storing the resulting funcref into a table or global works only for
207// *static* functions. Dynamic host functions (created via `wasm_func_new`) have
208// no funcref representation in the sys VM and will abort on `table.set` — a
209// separate VM limitation, not something this shim can work around.
210
211#[unsafe(no_mangle)]
212pub unsafe extern "C" fn wasm_func_as_ref(
213    func: Option<&mut wasm_func_t>,
214) -> Option<Box<wasm_ref_t>> {
215    let func = func?;
216    wasm_ref_t::new(
217        func.extern_.store.clone(),
218        Value::FuncRef(Some(func.extern_.function())),
219    )
220}
221
222#[unsafe(no_mangle)]
223pub unsafe extern "C" fn wasm_func_as_ref_const(
224    func: Option<&wasm_func_t>,
225) -> Option<Box<wasm_ref_t>> {
226    let func = func?;
227    wasm_ref_t::new(
228        func.extern_.store.clone(),
229        Value::FuncRef(Some(func.extern_.function())),
230    )
231}
232
233#[unsafe(no_mangle)]
234pub unsafe extern "C" fn wasm_ref_as_func(
235    ref_: Option<&mut wasm_ref_t>,
236) -> Option<Box<wasm_func_t>> {
237    let ref_ = ref_?;
238    let func = match &ref_.inner {
239        Value::FuncRef(Some(f)) => f.clone(),
240        _ => return None,
241    };
242    let store = ref_.store.upgrade()?;
243    Some(Box::new(wasm_func_t {
244        extern_: wasm_extern_t::new(store, func.into()),
245    }))
246}
247
248#[unsafe(no_mangle)]
249pub unsafe extern "C" fn wasm_ref_as_func_const(
250    ref_: Option<&wasm_ref_t>,
251) -> Option<Box<wasm_func_t>> {
252    let ref_ = ref_?;
253    let func = match &ref_.inner {
254        Value::FuncRef(Some(f)) => f.clone(),
255        _ => return None,
256    };
257    let store = ref_.store.upgrade()?;
258    Some(Box::new(wasm_func_t {
259        extern_: wasm_extern_t::new(store, func.into()),
260    }))
261}
262
263#[unsafe(no_mangle)]
264pub unsafe extern "C" fn wasm_func_call(
265    func: Option<&mut wasm_func_t>,
266    args: Option<&wasm_val_vec_t>,
267    results: &mut wasm_val_vec_t,
268) -> Option<Box<wasm_trap_t>> {
269    let func = func?;
270    let args = args?;
271    let store_ref = func.extern_.store.clone();
272    let mut store = func.extern_.store.clone();
273    let mut store_mut = unsafe { store.store_mut() };
274    // Convert by reference (not `.cloned()`): a shallow clone of a ref-carrying
275    // `wasm_val_t` would double-free the boxed `wasm_ref_t`.
276    let params = c_try!(
277        args.as_slice()
278            .iter()
279            .map(Value::try_from)
280            .collect::<Result<Vec<Value>, _>>()
281    );
282
283    match func.extern_.function().call(&mut store_mut, &params) {
284        Ok(wasm_results) => {
285            for (slot, val) in results
286                .as_uninit_slice()
287                .iter_mut()
288                .zip(wasm_results.iter())
289            {
290                let converted = c_try!(wasm_val_t::from_value(val, &store_ref));
291                *slot = MaybeUninit::new(converted);
292            }
293
294            None
295        }
296        Err(e) => Some(Box::new(e.into())),
297    }
298}
299
300#[unsafe(no_mangle)]
301pub unsafe extern "C" fn wasm_func_param_arity(func: Option<&wasm_func_t>) -> usize {
302    let Some(func) = func else {
303        update_last_error("func pointer is null");
304        return 0;
305    };
306    let store_ref = unsafe { func.extern_.store.store() };
307    func.extern_.function().ty(&store_ref).params().len()
308}
309
310#[unsafe(no_mangle)]
311pub unsafe extern "C" fn wasm_func_result_arity(func: Option<&wasm_func_t>) -> usize {
312    let Some(func) = func else {
313        update_last_error("func pointer is null");
314        return 0;
315    };
316    let store_ref = unsafe { func.extern_.store.store() };
317    func.extern_.function().ty(&store_ref).results().len()
318}
319
320#[unsafe(no_mangle)]
321pub unsafe extern "C" fn wasm_func_type(
322    func: Option<&wasm_func_t>,
323) -> Option<Box<wasm_functype_t>> {
324    let func = func?;
325    let store_ref = unsafe { func.extern_.store.store() };
326    Some(Box::new(wasm_functype_t::new(
327        func.extern_.function().ty(&store_ref),
328    )))
329}