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