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