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