xref: /wasmtime-44.0.1/crates/c-api/src/func.rs (revision 331b0dee)
1 use crate::wasm_trap_t;
2 use crate::{
3     wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t, wasm_val_vec_t, wasmtime_error_t,
4     wasmtime_extern_t, wasmtime_val_t, wasmtime_val_union, CStoreContext, CStoreContextMut,
5 };
6 use anyhow::anyhow;
7 use std::ffi::c_void;
8 use std::mem::MaybeUninit;
9 use std::panic::{self, AssertUnwindSafe};
10 use std::ptr;
11 use std::str;
12 use wasmtime::{AsContextMut, Caller, Extern, Func, Trap, Val};
13 
14 #[derive(Clone)]
15 #[repr(transparent)]
16 pub struct wasm_func_t {
17     ext: wasm_extern_t,
18 }
19 
20 wasmtime_c_api_macros::declare_ref!(wasm_func_t);
21 
22 pub type wasm_func_callback_t = extern "C" fn(
23     args: *const wasm_val_vec_t,
24     results: *mut wasm_val_vec_t,
25 ) -> Option<Box<wasm_trap_t>>;
26 
27 pub type wasm_func_callback_with_env_t = extern "C" fn(
28     env: *mut std::ffi::c_void,
29     args: *const wasm_val_vec_t,
30     results: *mut wasm_val_vec_t,
31 ) -> Option<Box<wasm_trap_t>>;
32 
33 impl wasm_func_t {
34     pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_func_t> {
35         match &e.which {
36             Extern::Func(_) => Some(unsafe { &*(e as *const _ as *const _) }),
37             _ => None,
38         }
39     }
40 
41     pub(crate) fn func(&self) -> Func {
42         match self.ext.which {
43             Extern::Func(f) => f,
44             _ => unsafe { std::hint::unreachable_unchecked() },
45         }
46     }
47 }
48 
49 unsafe fn create_function(
50     store: &mut wasm_store_t,
51     ty: &wasm_functype_t,
52     func: impl Fn(*const wasm_val_vec_t, *mut wasm_val_vec_t) -> Option<Box<wasm_trap_t>>
53         + Send
54         + Sync
55         + 'static,
56 ) -> Box<wasm_func_t> {
57     let ty = ty.ty().ty.clone();
58     let func = Func::new(
59         store.store.context_mut(),
60         ty,
61         move |_caller, params, results| {
62             let params: wasm_val_vec_t = params
63                 .iter()
64                 .cloned()
65                 .map(|p| wasm_val_t::from_val(p))
66                 .collect::<Vec<_>>()
67                 .into();
68             let mut out_results: wasm_val_vec_t = vec![wasm_val_t::default(); results.len()].into();
69             let out = func(&params, &mut out_results);
70             if let Some(trap) = out {
71                 return Err(trap.trap.clone());
72             }
73 
74             let out_results = out_results.as_slice();
75             for i in 0..results.len() {
76                 results[i] = out_results[i].val();
77             }
78             Ok(())
79         },
80     );
81     Box::new(wasm_func_t {
82         ext: wasm_extern_t {
83             store: store.store.clone(),
84             which: func.into(),
85         },
86     })
87 }
88 
89 #[no_mangle]
90 pub unsafe extern "C" fn wasm_func_new(
91     store: &mut wasm_store_t,
92     ty: &wasm_functype_t,
93     callback: wasm_func_callback_t,
94 ) -> Box<wasm_func_t> {
95     create_function(store, ty, move |params, results| callback(params, results))
96 }
97 
98 #[no_mangle]
99 pub unsafe extern "C" fn wasm_func_new_with_env(
100     store: &mut wasm_store_t,
101     ty: &wasm_functype_t,
102     callback: wasm_func_callback_with_env_t,
103     data: *mut c_void,
104     finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
105 ) -> Box<wasm_func_t> {
106     let finalizer = crate::ForeignData { data, finalizer };
107     create_function(store, ty, move |params, results| {
108         callback(finalizer.data, params, results)
109     })
110 }
111 
112 #[no_mangle]
113 pub unsafe extern "C" fn wasm_func_call(
114     func: &mut wasm_func_t,
115     args: *const wasm_val_vec_t,
116     results: *mut wasm_val_vec_t,
117 ) -> *mut wasm_trap_t {
118     let f = func.func();
119     let results = (*results).as_uninit_slice();
120     let args = (*args).as_slice();
121     if results.len() != f.ty(func.ext.store.context()).results().len() {
122         return Box::into_raw(Box::new(wasm_trap_t::new(
123             anyhow!("wrong number of results provided").into(),
124         )));
125     }
126     let params = args.iter().map(|i| i.val()).collect::<Vec<_>>();
127 
128     // We're calling arbitrary code here most of the time, and we in general
129     // want to try to insulate callers against bugs in wasmtime/wasi/etc if we
130     // can. As a result we catch panics here and transform them to traps to
131     // allow the caller to have any insulation possible against Rust panics.
132     let result = panic::catch_unwind(AssertUnwindSafe(|| {
133         f.call(func.ext.store.context_mut(), &params)
134     }));
135     match result {
136         Ok(Ok(out)) => {
137             for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) {
138                 crate::initialize(slot, wasm_val_t::from_val(val));
139             }
140             ptr::null_mut()
141         }
142         Ok(Err(trap)) => match trap.downcast::<Trap>() {
143             Ok(trap) => Box::into_raw(Box::new(wasm_trap_t::new(trap))),
144             Err(err) => Box::into_raw(Box::new(wasm_trap_t::new(err.into()))),
145         },
146         Err(panic) => {
147             let trap = if let Some(msg) = panic.downcast_ref::<String>() {
148                 Trap::new(msg)
149             } else if let Some(msg) = panic.downcast_ref::<&'static str>() {
150                 Trap::new(*msg)
151             } else {
152                 Trap::new("rust panic happened")
153             };
154             let trap = Box::new(wasm_trap_t::new(trap));
155             Box::into_raw(trap)
156         }
157     }
158 }
159 
160 #[no_mangle]
161 pub unsafe extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box<wasm_functype_t> {
162     Box::new(wasm_functype_t::new(f.func().ty(f.ext.store.context())))
163 }
164 
165 #[no_mangle]
166 pub unsafe extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize {
167     f.func().ty(f.ext.store.context()).params().len()
168 }
169 
170 #[no_mangle]
171 pub unsafe extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize {
172     f.func().ty(f.ext.store.context()).results().len()
173 }
174 
175 #[no_mangle]
176 pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t {
177     &mut (*f).ext
178 }
179 
180 #[no_mangle]
181 pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t {
182     &(*f).ext
183 }
184 
185 #[repr(C)]
186 pub struct wasmtime_caller_t<'a> {
187     caller: Caller<'a, crate::StoreData>,
188 }
189 
190 pub type wasmtime_func_callback_t = extern "C" fn(
191     *mut c_void,
192     *mut wasmtime_caller_t,
193     *const wasmtime_val_t,
194     usize,
195     *mut wasmtime_val_t,
196     usize,
197 ) -> Option<Box<wasm_trap_t>>;
198 
199 #[no_mangle]
200 pub unsafe extern "C" fn wasmtime_func_new(
201     store: CStoreContextMut<'_>,
202     ty: &wasm_functype_t,
203     callback: wasmtime_func_callback_t,
204     data: *mut c_void,
205     finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
206     func: &mut Func,
207 ) {
208     let ty = ty.ty().ty.clone();
209     let cb = c_callback_to_rust_fn(callback, data, finalizer);
210     let f = Func::new(store, ty, cb);
211     *func = f;
212 }
213 
214 pub(crate) unsafe fn c_callback_to_rust_fn(
215     callback: wasmtime_func_callback_t,
216     data: *mut c_void,
217     finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
218 ) -> impl Fn(Caller<'_, crate::StoreData>, &[Val], &mut [Val]) -> Result<(), Trap> {
219     let foreign = crate::ForeignData { data, finalizer };
220     move |caller, params, results| {
221         let params = params
222             .iter()
223             .cloned()
224             .map(|p| wasmtime_val_t::from_val(p))
225             .collect::<Vec<_>>();
226         let mut out_results = (0..results.len())
227             .map(|_| wasmtime_val_t {
228                 kind: crate::WASMTIME_I32,
229                 of: wasmtime_val_union { i32: 0 },
230             })
231             .collect::<Vec<_>>();
232         let mut caller = wasmtime_caller_t { caller };
233         let out = callback(
234             foreign.data,
235             &mut caller,
236             params.as_ptr(),
237             params.len(),
238             out_results.as_mut_ptr(),
239             out_results.len(),
240         );
241         if let Some(trap) = out {
242             return Err(trap.trap);
243         }
244 
245         for (i, result) in out_results.iter().enumerate() {
246             results[i] = unsafe { result.to_val() };
247         }
248         Ok(())
249     }
250 }
251 
252 #[no_mangle]
253 pub unsafe extern "C" fn wasmtime_func_call(
254     store: CStoreContextMut<'_>,
255     func: &Func,
256     args: *const wasmtime_val_t,
257     nargs: usize,
258     results: *mut MaybeUninit<wasmtime_val_t>,
259     nresults: usize,
260     trap_ret: &mut *mut wasm_trap_t,
261 ) -> Option<Box<wasmtime_error_t>> {
262     if nresults != func.ty(&store).results().len() {
263         return Some(Box::new(wasmtime_error_t::from(anyhow!(
264             "wrong number of results provided"
265         ))));
266     }
267     let params = crate::slice_from_raw_parts(args, nargs)
268         .iter()
269         .map(|i| i.to_val())
270         .collect::<Vec<_>>();
271 
272     // We're calling arbitrary code here most of the time, and we in general
273     // want to try to insulate callers against bugs in wasmtime/wasi/etc if we
274     // can. As a result we catch panics here and transform them to traps to
275     // allow the caller to have any insulation possible against Rust panics.
276     let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(store, &params)));
277     match result {
278         Ok(Ok(out)) => {
279             let results = crate::slice_from_raw_parts_mut(results, nresults);
280             for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) {
281                 crate::initialize(slot, wasmtime_val_t::from_val(val));
282             }
283             None
284         }
285         Ok(Err(trap)) => match trap.downcast::<Trap>() {
286             Ok(trap) => {
287                 *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(trap)));
288                 None
289             }
290             Err(err) => Some(Box::new(wasmtime_error_t::from(err))),
291         },
292         Err(panic) => {
293             let trap = if let Some(msg) = panic.downcast_ref::<String>() {
294                 Trap::new(msg)
295             } else if let Some(msg) = panic.downcast_ref::<&'static str>() {
296                 Trap::new(*msg)
297             } else {
298                 Trap::new("rust panic happened")
299             };
300             *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(trap)));
301             None
302         }
303     }
304 }
305 
306 #[no_mangle]
307 pub extern "C" fn wasmtime_func_type(
308     store: CStoreContext<'_>,
309     func: &Func,
310 ) -> Box<wasm_functype_t> {
311     Box::new(wasm_functype_t::new(func.ty(store)))
312 }
313 
314 #[no_mangle]
315 pub extern "C" fn wasmtime_caller_context<'a>(
316     caller: &'a mut wasmtime_caller_t,
317 ) -> CStoreContextMut<'a> {
318     caller.caller.as_context_mut()
319 }
320 
321 #[no_mangle]
322 pub unsafe extern "C" fn wasmtime_caller_export_get(
323     caller: &mut wasmtime_caller_t,
324     name: *const u8,
325     name_len: usize,
326     item: &mut MaybeUninit<wasmtime_extern_t>,
327 ) -> bool {
328     let name = match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) {
329         Ok(name) => name,
330         Err(_) => return false,
331     };
332     let which = match caller.caller.get_export(name) {
333         Some(item) => item,
334         None => return false,
335     };
336     crate::initialize(item, which.into());
337     true
338 }
339