use std::ffi::CStr;
use std::os::raw::c_void;
use std::panic::{self, AssertUnwindSafe};
use wasm3x_sys as ffi;
use crate::error::{Error, Result};
use crate::store::{CallState, StoreId};
use crate::value::{FuncType, Val};
#[doc(hidden)]
pub struct RawCaller {
pub(crate) runtime: ffi::IM3Runtime,
pub(crate) store_id: StoreId,
pub(crate) data: *mut std::os::raw::c_void,
}
pub(crate) type HostValFn =
Box<dyn Fn(RawCaller, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static>;
#[doc(hidden)]
pub type HostRawFn = Box<dyn Fn(RawCaller, *mut u64) -> Result<()> + Send + Sync + 'static>;
pub(crate) enum HostImpl {
Val(HostValFn),
Raw(HostRawFn),
}
pub(crate) struct HostFuncEntry {
pub(crate) ty: FuncType,
pub(crate) func: HostImpl,
}
static HOST_TRAP_MESSAGE: &CStr = c"wasm3: host function trapped";
const INLINE_VAL_SLOTS: usize = 16;
pub(crate) unsafe extern "C" fn host_trampoline(
runtime: ffi::IM3Runtime,
ctx: ffi::IM3ImportContext,
sp: *mut u64,
_mem: *mut c_void,
) -> *const c_void {
unsafe {
let call_state = ffi::m3_GetUserData(runtime) as *const CallState;
let (store_id, data_ptr) = if call_state.is_null() {
(StoreId::PLACEHOLDER, core::ptr::null_mut())
} else {
((*call_state).store_id, (*call_state).data_ptr)
};
let outcome = panic::catch_unwind(AssertUnwindSafe(|| {
let entry = &*((*ctx).userdata as *const HostFuncEntry);
let caller = RawCaller {
runtime,
store_id,
data: data_ptr,
};
match &entry.func {
HostImpl::Raw(func) => func(caller, sp),
HostImpl::Val(func) => {
let num_results = entry.ty.results().len();
let total = num_results + entry.ty.params().len();
let mut inline = [Val::I32(0); INLINE_VAL_SLOTS];
let mut heap: Vec<Val>;
let buf: &mut [Val] = if total <= INLINE_VAL_SLOTS {
&mut inline[..total]
} else {
heap = vec![Val::I32(0); total];
heap.as_mut_slice()
};
let (results, args) = buf.split_at_mut(num_results);
for (i, (slot, ty)) in args.iter_mut().zip(entry.ty.params()).enumerate() {
*slot = Val::from_slot(*ty, *sp.add(num_results + i));
}
for (slot, ty) in results.iter_mut().zip(entry.ty.results()) {
*slot = Val::default_for_ty(*ty);
}
func(caller, args, results)?;
for (i, result) in results.iter().enumerate() {
*sp.add(i) = result.to_slot();
}
Ok(())
}
}
}));
match outcome {
Ok(Ok(())) => core::ptr::null(),
Ok(Err(error)) => stash_error(runtime, error),
Err(_) => stash_error(runtime, Error::new("host function panicked")),
}
}
}
unsafe fn stash_error(runtime: ffi::IM3Runtime, error: Error) -> *const c_void {
unsafe {
let call_state = ffi::m3_GetUserData(runtime) as *mut CallState;
if !call_state.is_null() {
(*call_state).last_host_error = Some(error);
}
HOST_TRAP_MESSAGE.as_ptr() as *const c_void
}
}