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