xref: /wasmtime-44.0.1/crates/c-api/src/func.rs (revision e4e9cabe)
1 use crate::{WasmtimeCaller, WasmtimeStoreData, wasm_trap_t};
2 use 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 };
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::{
14     AsContext, AsContextMut, Error, Extern, Func, Result, RootScope, StoreContext, StoreContextMut,
15     Trap, Val, ValRaw,
16 };
17 
18 #[derive(Clone)]
19 #[repr(transparent)]
20 pub struct wasm_func_t {
21     ext: wasm_extern_t,
22 }
23 
24 wasmtime_c_api_macros::declare_ref!(wasm_func_t);
25 
26 pub 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 
31 pub 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 
37 impl 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 
53 unsafe 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)]
94 pub 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)]
103 pub 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.
119 pub(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)]
134 pub 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 
169 fn 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)]
180 pub 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)]
185 pub 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)]
190 pub 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)]
195 pub 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)]
200 pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t {
201     &(*f).ext
202 }
203 
204 #[repr(C)]
205 pub struct wasmtime_caller_t<'a> {
206     pub(crate) caller: WasmtimeCaller<'a>,
207 }
208 
209 impl 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 
217 impl AsContextMut for wasmtime_caller_t<'_> {
218     fn as_context_mut(&mut self) -> StoreContextMut<'_, WasmtimeStoreData> {
219         self.caller.as_context_mut()
220     }
221 }
222 
223 pub 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 
232 pub 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)]
240 pub 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 
254 pub(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)]
310 pub 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 
323 pub(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)]
345 pub 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)]
391 pub 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 
405 fn 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)]
415 pub 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)]
423 pub 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)]
430 pub 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)]
449 pub 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)]
458 pub unsafe extern "C" fn wasmtime_func_to_raw(
459     store: WasmtimeStoreContextMut<'_>,
460     func: &Func,
461 ) -> *mut c_void {
462     func.to_raw(store)
463 }
464