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