wasmtime_c_api/
func.rs

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