Skip to main content

wasmtime_c_api/
func.rs

1use crate::{WasmtimeCaller, WasmtimeStoreData, wasm_trap_t};
2use crate::{
3    WasmtimeStoreContext, WasmtimeStoreContextMut, wasm_extern_t, wasm_functype_t, wasm_store_t,
4    wasm_val_t, wasm_val_vec_t, wasmtime_error_t, wasmtime_extern_t, wasmtime_val_t,
5    wasmtime_val_union,
6};
7use std::any::Any;
8use std::ffi::c_void;
9use std::mem::{self, MaybeUninit};
10use std::panic::{self, AssertUnwindSafe};
11use std::ptr;
12use std::str;
13use wasmtime::{
14    AsContext, AsContextMut, Error, Extern, Func, Result, StoreContext, StoreContextMut, Trap, Val,
15    ValRaw,
16};
17#[cfg(feature = "gc")]
18use wasmtime::{RootScope, ThrownException};
19
20#[derive(Clone)]
21#[repr(transparent)]
22pub struct wasm_func_t {
23    ext: wasm_extern_t,
24}
25
26wasmtime_c_api_macros::declare_ref!(wasm_func_t);
27
28pub type wasm_func_callback_t = extern "C" fn(
29    args: *const wasm_val_vec_t,
30    results: *mut wasm_val_vec_t,
31) -> Option<Box<wasm_trap_t>>;
32
33pub type wasm_func_callback_with_env_t = extern "C" fn(
34    env: *mut std::ffi::c_void,
35    args: *const wasm_val_vec_t,
36    results: *mut wasm_val_vec_t,
37) -> Option<Box<wasm_trap_t>>;
38
39impl wasm_func_t {
40    pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_func_t> {
41        match &e.which {
42            Extern::Func(_) => Some(unsafe { &*(e as *const _ as *const _) }),
43            _ => None,
44        }
45    }
46
47    pub(crate) fn func(&self) -> Func {
48        match self.ext.which {
49            Extern::Func(f) => f,
50            _ => unsafe { std::hint::unreachable_unchecked() },
51        }
52    }
53}
54
55unsafe fn create_function(
56    store: &mut wasm_store_t,
57    ty: &wasm_functype_t,
58    func: impl Fn(*const wasm_val_vec_t, *mut wasm_val_vec_t) -> Option<Box<wasm_trap_t>>
59    + Send
60    + Sync
61    + 'static,
62) -> Box<wasm_func_t> {
63    let ty = ty.ty().ty(store.store.context().engine());
64    let func = Func::new(
65        store.store.context_mut(),
66        ty,
67        move |_caller, params, results| {
68            let params: wasm_val_vec_t = params
69                .iter()
70                .cloned()
71                .map(|p| wasm_val_t::from_val(p))
72                .collect::<Vec<_>>()
73                .into();
74            let mut out_results: wasm_val_vec_t = vec![wasm_val_t::default(); results.len()].into();
75            let out = func(&params, &mut out_results);
76            if let Some(trap) = out {
77                return Err(trap.error);
78            }
79
80            let out_results = out_results.as_slice();
81            for i in 0..results.len() {
82                results[i] = out_results[i].val();
83            }
84            Ok(())
85        },
86    );
87    Box::new(wasm_func_t {
88        ext: wasm_extern_t {
89            store: store.store.clone(),
90            which: func.into(),
91        },
92    })
93}
94
95#[unsafe(no_mangle)]
96pub unsafe extern "C" fn wasm_func_new(
97    store: &mut wasm_store_t,
98    ty: &wasm_functype_t,
99    callback: wasm_func_callback_t,
100) -> Box<wasm_func_t> {
101    create_function(store, ty, move |params, results| callback(params, results))
102}
103
104#[unsafe(no_mangle)]
105pub unsafe extern "C" fn wasm_func_new_with_env(
106    store: &mut wasm_store_t,
107    ty: &wasm_functype_t,
108    callback: wasm_func_callback_with_env_t,
109    data: *mut c_void,
110    finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
111) -> Box<wasm_func_t> {
112    let finalizer = crate::ForeignData { data, finalizer };
113    create_function(store, ty, move |params, results| {
114        let _ = &finalizer; // move entire finalizer into this closure
115        callback(finalizer.data, params, results)
116    })
117}
118
119/// Places the `args` into `dst` and additionally reserves space in `dst` for `results_size`
120/// returns. The params/results slices are then returned separately.
121pub(crate) fn translate_args<'a>(
122    dst: &'a mut Vec<Val>,
123    args: impl ExactSizeIterator<Item = Val>,
124    results_size: usize,
125) -> (&'a [Val], &'a mut [Val]) {
126    debug_assert!(dst.is_empty());
127    let num_args = args.len();
128    dst.reserve(args.len() + results_size);
129    dst.extend(args);
130    dst.extend((0..results_size).map(|_| Val::null_func_ref()));
131    let (a, b) = dst.split_at_mut(num_args);
132    (a, b)
133}
134
135#[unsafe(no_mangle)]
136pub unsafe extern "C" fn wasm_func_call(
137    func: &mut wasm_func_t,
138    args: *const wasm_val_vec_t,
139    results: *mut wasm_val_vec_t,
140) -> *mut wasm_trap_t {
141    let f = func.func();
142    let results = (*results).as_uninit_slice();
143    let args = (*args).as_slice();
144    let mut dst = Vec::new();
145    let (wt_params, wt_results) =
146        translate_args(&mut dst, args.iter().map(|i| i.val()), results.len());
147
148    // We're calling arbitrary code here most of the time, and we in general
149    // want to try to insulate callers against bugs in wasmtime/wasi/etc if we
150    // can. As a result we catch panics here and transform them to traps to
151    // allow the caller to have any insulation possible against Rust panics.
152    let result = panic::catch_unwind(AssertUnwindSafe(|| {
153        f.call(func.ext.store.context_mut(), wt_params, wt_results)
154    }));
155    match result {
156        Ok(Ok(())) => {
157            for (slot, val) in results.iter_mut().zip(wt_results.iter().cloned()) {
158                crate::initialize(slot, wasm_val_t::from_val(val));
159            }
160            ptr::null_mut()
161        }
162        Ok(Err(err)) => Box::into_raw(Box::new(wasm_trap_t::new(err))),
163        Err(panic) => {
164            let err = error_from_panic(panic);
165            let trap = Box::new(wasm_trap_t::new(err));
166            Box::into_raw(trap)
167        }
168    }
169}
170
171fn error_from_panic(panic: Box<dyn Any + Send>) -> Error {
172    if let Some(msg) = panic.downcast_ref::<String>() {
173        Error::msg(msg.clone())
174    } else if let Some(msg) = panic.downcast_ref::<&'static str>() {
175        Error::msg(*msg)
176    } else {
177        Error::msg("rust panic happened")
178    }
179}
180
181#[unsafe(no_mangle)]
182pub unsafe extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box<wasm_functype_t> {
183    Box::new(wasm_functype_t::new(f.func().ty(f.ext.store.context())))
184}
185
186#[unsafe(no_mangle)]
187pub unsafe extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize {
188    f.func().ty(f.ext.store.context()).params().len()
189}
190
191#[unsafe(no_mangle)]
192pub unsafe extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize {
193    f.func().ty(f.ext.store.context()).results().len()
194}
195
196#[unsafe(no_mangle)]
197pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t {
198    &mut (*f).ext
199}
200
201#[unsafe(no_mangle)]
202pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t {
203    &(*f).ext
204}
205
206#[repr(C)]
207pub struct wasmtime_caller_t<'a> {
208    pub(crate) caller: WasmtimeCaller<'a>,
209}
210
211impl AsContext for wasmtime_caller_t<'_> {
212    type Data = WasmtimeStoreData;
213
214    fn as_context(&self) -> StoreContext<'_, WasmtimeStoreData> {
215        self.caller.as_context()
216    }
217}
218
219impl AsContextMut for wasmtime_caller_t<'_> {
220    fn as_context_mut(&mut self) -> StoreContextMut<'_, WasmtimeStoreData> {
221        self.caller.as_context_mut()
222    }
223}
224
225pub type wasmtime_func_callback_t = extern "C" fn(
226    *mut c_void,
227    *mut wasmtime_caller_t,
228    *const wasmtime_val_t,
229    usize,
230    *mut wasmtime_val_t,
231    usize,
232) -> Option<Box<wasm_trap_t>>;
233
234pub type wasmtime_func_unchecked_callback_t = extern "C" fn(
235    *mut c_void,
236    *mut wasmtime_caller_t,
237    *mut ValRaw,
238    usize,
239) -> Option<Box<wasm_trap_t>>;
240
241#[unsafe(no_mangle)]
242pub unsafe extern "C" fn wasmtime_func_new(
243    store: WasmtimeStoreContextMut<'_>,
244    ty: &wasm_functype_t,
245    callback: wasmtime_func_callback_t,
246    data: *mut c_void,
247    finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
248    func: &mut Func,
249) {
250    let ty = ty.ty().ty(store.engine());
251    let cb = c_callback_to_rust_fn(callback, data, finalizer);
252    let f = Func::new(store, ty, cb);
253    *func = f;
254}
255
256pub(crate) unsafe fn c_callback_to_rust_fn(
257    callback: wasmtime_func_callback_t,
258    data: *mut c_void,
259    finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
260) -> impl Fn(WasmtimeCaller<'_>, &[Val], &mut [Val]) -> Result<()> {
261    let foreign = crate::ForeignData { data, finalizer };
262    move |mut caller, params, results| {
263        let _ = &foreign; // move entire foreign into this closure
264
265        // Convert `params/results` to `wasmtime_val_t`. Use the previous
266        // storage in `hostcall_val_storage` to help avoid allocations all the
267        // time.
268        let mut vals = mem::take(&mut caller.data_mut().hostcall_val_storage);
269        debug_assert!(vals.is_empty());
270        vals.reserve(params.len() + results.len());
271        vals.extend(
272            params
273                .iter()
274                .cloned()
275                .map(|p| wasmtime_val_t::from_val_unscoped(&mut caller, p)),
276        );
277        vals.extend((0..results.len()).map(|_| wasmtime_val_t {
278            kind: crate::WASMTIME_I32,
279            of: wasmtime_val_union { i32: 0 },
280        }));
281        let (params, out_results) = vals.split_at_mut(params.len());
282
283        // Invoke the C function pointer, getting the results.
284        let mut caller = wasmtime_caller_t { caller };
285        let out = callback(
286            foreign.data,
287            &mut caller,
288            params.as_ptr(),
289            params.len(),
290            out_results.as_mut_ptr(),
291            out_results.len(),
292        );
293        if let Some(trap) = out {
294            return Err(trap.error);
295        }
296
297        // Translate the `wasmtime_val_t` results into the `results` space
298        for (i, result) in out_results.iter().enumerate() {
299            results[i] = result.to_val_unscoped(&mut caller);
300        }
301
302        // Move our `vals` storage back into the store now that we no longer
303        // need it. This'll get picked up by the next hostcall and reuse our
304        // same storage.
305        vals.truncate(0);
306        caller.caller.data_mut().hostcall_val_storage = vals;
307        Ok(())
308    }
309}
310
311#[unsafe(no_mangle)]
312pub unsafe extern "C" fn wasmtime_func_new_unchecked(
313    store: WasmtimeStoreContextMut<'_>,
314    ty: &wasm_functype_t,
315    callback: wasmtime_func_unchecked_callback_t,
316    data: *mut c_void,
317    finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
318    func: &mut Func,
319) {
320    let ty = ty.ty().ty(store.engine());
321    let cb = c_unchecked_callback_to_rust_fn(callback, data, finalizer);
322    *func = Func::new_unchecked(store, ty, cb);
323}
324
325pub(crate) unsafe fn c_unchecked_callback_to_rust_fn(
326    callback: wasmtime_func_unchecked_callback_t,
327    data: *mut c_void,
328    finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
329) -> impl Fn(WasmtimeCaller<'_>, &mut [MaybeUninit<ValRaw>]) -> Result<()> {
330    let foreign = crate::ForeignData { data, finalizer };
331    move |caller, values| {
332        let _ = &foreign; // move entire foreign into this closure
333        let mut caller = wasmtime_caller_t { caller };
334        match callback(
335            foreign.data,
336            &mut caller,
337            values.as_mut_ptr().cast(),
338            values.len(),
339        ) {
340            None => Ok(()),
341            Some(trap) => Err(trap.error),
342        }
343    }
344}
345
346#[unsafe(no_mangle)]
347pub unsafe extern "C" fn wasmtime_func_call(
348    mut store: WasmtimeStoreContextMut<'_>,
349    func: &Func,
350    args: *const wasmtime_val_t,
351    nargs: usize,
352    results: *mut MaybeUninit<wasmtime_val_t>,
353    nresults: usize,
354    trap_ret: &mut *mut wasm_trap_t,
355) -> Option<Box<wasmtime_error_t>> {
356    #[cfg(feature = "gc")]
357    let mut store = RootScope::new(&mut store);
358    let mut params = mem::take(&mut store.as_context_mut().data_mut().wasm_val_storage);
359
360    let (wt_params, wt_results) = translate_args(
361        &mut params,
362        crate::slice_from_raw_parts(args, nargs)
363            .iter()
364            .map(|i| i.to_val(&mut store)),
365        nresults,
366    );
367
368    // We're calling arbitrary code here most of the time, and we in general
369    // want to try to insulate callers against bugs in wasmtime/wasi/etc if we
370    // can. As a result we catch panics here and transform them to traps to
371    // allow the caller to have any insulation possible against Rust panics.
372    let result = panic::catch_unwind(AssertUnwindSafe(|| {
373        func.call(&mut store, wt_params, wt_results)
374    }));
375    match result {
376        Ok(Ok(())) => {
377            let results = crate::slice_from_raw_parts_mut(results, nresults);
378            for (slot, val) in results.iter_mut().zip(wt_results.iter()) {
379                crate::initialize(slot, wasmtime_val_t::from_val(&mut store, *val));
380            }
381            params.truncate(0);
382            store.as_context_mut().data_mut().wasm_val_storage = params;
383            None
384        }
385        Ok(Err(trap)) => store_err(trap, trap_ret),
386        Err(panic) => {
387            let err = error_from_panic(panic);
388            *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err)));
389            None
390        }
391    }
392}
393
394#[unsafe(no_mangle)]
395pub unsafe extern "C" fn wasmtime_func_call_unchecked(
396    store: WasmtimeStoreContextMut<'_>,
397    func: &Func,
398    args_and_results: *mut ValRaw,
399    args_and_results_len: usize,
400    trap_ret: &mut *mut wasm_trap_t,
401) -> Option<Box<wasmtime_error_t>> {
402    let slice = std::ptr::slice_from_raw_parts_mut(args_and_results, args_and_results_len);
403    match func.call_unchecked(store, slice) {
404        Ok(()) => None,
405        Err(trap) => store_err(trap, trap_ret),
406    }
407}
408
409#[cfg(feature = "gc")]
410fn is_trap_like_impl(err: &Error) -> bool {
411    err.is::<Trap>() || err.is::<ThrownException>()
412}
413
414#[cfg(not(feature = "gc"))]
415fn is_trap_like_impl(err: &Error) -> bool {
416    err.is::<Trap>()
417}
418
419fn store_err(err: Error, trap_ret: &mut *mut wasm_trap_t) -> Option<Box<wasmtime_error_t>> {
420    if is_trap_like_impl(&err) {
421        *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err)));
422        None
423    } else {
424        Some(Box::new(wasmtime_error_t::from(err)))
425    }
426}
427
428#[unsafe(no_mangle)]
429pub extern "C" fn wasmtime_func_type(
430    store: WasmtimeStoreContext<'_>,
431    func: &Func,
432) -> Box<wasm_functype_t> {
433    Box::new(wasm_functype_t::new(func.ty(store)))
434}
435
436#[unsafe(no_mangle)]
437pub extern "C" fn wasmtime_caller_context<'a>(
438    caller: &'a mut wasmtime_caller_t,
439) -> WasmtimeStoreContextMut<'a> {
440    caller.caller.as_context_mut()
441}
442
443#[unsafe(no_mangle)]
444pub unsafe extern "C" fn wasmtime_caller_export_get(
445    caller: &mut wasmtime_caller_t,
446    name: *const u8,
447    name_len: usize,
448    item: &mut MaybeUninit<wasmtime_extern_t>,
449) -> bool {
450    let name = match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) {
451        Ok(name) => name,
452        Err(_) => return false,
453    };
454    let which = match caller.caller.get_export(name) {
455        Some(item) => item,
456        None => return false,
457    };
458    crate::initialize(item, which.into());
459    true
460}
461
462#[unsafe(no_mangle)]
463pub unsafe extern "C" fn wasmtime_func_from_raw(
464    store: WasmtimeStoreContextMut<'_>,
465    raw: *mut c_void,
466    func: &mut Func,
467) {
468    *func = Func::from_raw(store, raw).unwrap();
469}
470
471#[unsafe(no_mangle)]
472pub unsafe extern "C" fn wasmtime_func_to_raw(
473    store: WasmtimeStoreContextMut<'_>,
474    func: &Func,
475) -> *mut c_void {
476    func.to_raw(store)
477}