use crate::{
wasm_extern_t,
wasm_functype_t,
wasm_store_t,
wasm_trap_t,
wasm_val_t,
wasm_val_vec_t,
};
use alloc::{boxed::Box, string::String, vec, vec::Vec};
use core::{any::Any, ffi::c_void, hint, iter, ptr, str};
use wasmi::{Error, Extern, Func, Nullable, Val};
#[cfg(feature = "std")]
use core::panic::AssertUnwindSafe;
#[derive(Clone)]
#[repr(transparent)]
pub struct wasm_func_t {
inner: wasm_extern_t,
}
wasmi_c_api_macros::declare_ref!(wasm_func_t);
pub type wasm_func_callback_t = extern "C" fn(
params: *const wasm_val_vec_t,
results: *mut wasm_val_vec_t,
) -> Option<Box<wasm_trap_t>>;
pub type wasm_func_callback_with_env_t = extern "C" fn(
env: *mut c_void,
params: *const wasm_val_vec_t,
results: *mut wasm_val_vec_t,
) -> Option<Box<wasm_trap_t>>;
impl wasm_func_t {
pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_func_t> {
match &e.which {
Extern::Func(_) => Some(unsafe { &*(e as *const _ as *const _) }),
_ => None,
}
}
pub(crate) fn try_from_mut(e: &mut wasm_extern_t) -> Option<&mut wasm_func_t> {
match &mut e.which {
Extern::Func(_) => Some(unsafe { &mut *(e as *mut _ as *mut _) }),
_ => None,
}
}
pub(crate) fn func(&self) -> Func {
match self.inner.which {
Extern::Func(f) => f,
_ => unsafe { hint::unreachable_unchecked() },
}
}
}
unsafe fn create_function(
store: &mut wasm_store_t,
ty: &wasm_functype_t,
func: impl Fn(*const wasm_val_vec_t, *mut wasm_val_vec_t) -> Option<Box<wasm_trap_t>>
+ Send
+ Sync
+ 'static,
) -> Box<wasm_func_t> {
unsafe {
let ty = ty.ty().ty.clone();
let func = Func::new(
store.inner.context_mut(),
ty,
move |_caller, params, results| {
let params: wasm_val_vec_t = params
.iter()
.cloned()
.map(wasm_val_t::from)
.collect::<Box<[_]>>()
.into();
let mut out_results: wasm_val_vec_t =
vec![wasm_val_t::default(); results.len()].into();
if let Some(trap) = func(¶ms, &mut out_results) {
return Err(trap.error);
}
results
.iter_mut()
.zip(out_results.as_slice())
.for_each(|(result, out_results)| {
*result = out_results.to_val();
});
Ok(())
},
);
Box::new(wasm_func_t {
inner: wasm_extern_t {
store: store.inner.clone(),
which: func.into(),
},
})
}
}
#[cfg_attr(not(feature = "prefix-symbols"), unsafe(no_mangle))]
#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
pub unsafe extern "C" fn wasm_func_new(
store: &mut wasm_store_t,
ty: &wasm_functype_t,
callback: wasm_func_callback_t,
) -> Box<wasm_func_t> {
unsafe { create_function(store, ty, move |params, results| callback(params, results)) }
}
#[cfg_attr(not(feature = "prefix-symbols"), unsafe(no_mangle))]
#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
pub unsafe extern "C" fn wasm_func_new_with_env(
store: &mut wasm_store_t,
ty: &wasm_functype_t,
callback: wasm_func_callback_with_env_t,
data: *mut c_void,
finalizer: Option<extern "C" fn(arg1: *mut c_void)>,
) -> Box<wasm_func_t> {
unsafe {
let finalizer = crate::ForeignData { data, finalizer };
create_function(store, ty, move |params, results| {
let _ = &finalizer; callback(finalizer.data, params, results)
})
}
}
fn prepare_params_and_results(
dst: &mut Vec<Val>,
params: impl ExactSizeIterator<Item = Val>,
len_results: usize,
) -> (&[Val], &mut [Val]) {
debug_assert!(dst.is_empty());
let len_params = params.len();
dst.reserve(len_params + len_results);
dst.extend(params);
dst.extend(iter::repeat_n(
Val::FuncRef(<Nullable<Func>>::Null),
len_results,
));
let (params, results) = dst.split_at_mut(len_params);
(params, results)
}
#[cfg_attr(not(feature = "prefix-symbols"), unsafe(no_mangle))]
#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
pub unsafe extern "C" fn wasm_func_call(
func: &mut wasm_func_t,
params: *const wasm_val_vec_t,
results: *mut wasm_val_vec_t,
) -> *mut wasm_trap_t {
let f = func.func();
let results = unsafe { (*results).as_uninit_slice() };
let params = unsafe { (*params).as_slice() };
let mut dst = Vec::new();
let (wt_params, wt_results) =
prepare_params_and_results(&mut dst, params.iter().map(|i| i.to_val()), results.len());
let result = {
let ctx = unsafe { func.inner.store.context_mut() };
#[cfg(feature = "std")]
{
std::panic::catch_unwind(AssertUnwindSafe(|| f.call(ctx, wt_params, wt_results)))
}
#[cfg(not(feature = "std"))]
{
Ok(f.call(ctx, wt_params, wt_results))
}
};
match result {
Ok(Ok(())) => {
for (slot, val) in results.iter_mut().zip(wt_results.iter().cloned()) {
crate::initialize(slot, wasm_val_t::from(val));
}
ptr::null_mut()
}
Ok(Err(err)) => Box::into_raw(Box::new(wasm_trap_t::new(err))),
Err(panic) => {
let err = error_from_panic(panic);
let trap = Box::new(wasm_trap_t::new(err));
Box::into_raw(trap)
}
}
}
fn error_from_panic(panic: Box<dyn Any + Send>) -> Error {
if let Some(msg) = panic.downcast_ref::<String>() {
Error::new(msg.clone())
} else if let Some(msg) = panic.downcast_ref::<&'static str>() {
Error::new(*msg)
} else {
Error::new("panic happened on the Rust side")
}
}
#[cfg_attr(not(feature = "prefix-symbols"), unsafe(no_mangle))]
#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
pub unsafe extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box<wasm_functype_t> {
unsafe { Box::new(wasm_functype_t::new(f.func().ty(f.inner.store.context()))) }
}
#[cfg_attr(not(feature = "prefix-symbols"), unsafe(no_mangle))]
#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
pub unsafe extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize {
unsafe { f.func().ty(f.inner.store.context()).params().len() }
}
#[cfg_attr(not(feature = "prefix-symbols"), unsafe(no_mangle))]
#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
pub unsafe extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize {
unsafe { f.func().ty(f.inner.store.context()).results().len() }
}
#[cfg_attr(not(feature = "prefix-symbols"), unsafe(no_mangle))]
#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t {
&mut f.inner
}
#[cfg_attr(not(feature = "prefix-symbols"), unsafe(no_mangle))]
#[cfg_attr(feature = "prefix-symbols", wasmi_c_api_macros::prefix_symbol)]
pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t {
&f.inner
}