use crate::{
wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t, wasm_val_vec_t, wasmtime_error_t,
wasmtime_extern_t, wasmtime_val_t, wasmtime_val_union, WasmtimeStoreContext,
WasmtimeStoreContextMut,
};
use crate::{wasm_trap_t, WasmtimeCaller, WasmtimeStoreData};
use anyhow::{Error, Result};
use std::any::Any;
use std::ffi::c_void;
use std::mem::{self, MaybeUninit};
use std::panic::{self, AssertUnwindSafe};
use std::ptr;
use std::str;
use wasmtime::{
AsContext, AsContextMut, Extern, Func, RootScope, StoreContext, StoreContextMut, Trap, Val,
ValRaw,
};
#[derive(Clone)]
#[repr(transparent)]
pub struct wasm_func_t {
ext: wasm_extern_t,
}
wasmtime_c_api_macros::declare_ref!(wasm_func_t);
pub type wasm_func_callback_t = extern "C" fn(
args: *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 std::ffi::c_void,
args: *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 func(&self) -> Func {
match self.ext.which {
Extern::Func(f) => f,
_ => unsafe { std::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> {
let ty = ty.ty().ty(store.store.context().engine());
let func = Func::new(
store.store.context_mut(),
ty,
move |_caller, params, results| {
let params: wasm_val_vec_t = params
.iter()
.cloned()
.map(|p| wasm_val_t::from_val(p))
.collect::<Vec<_>>()
.into();
let mut out_results: wasm_val_vec_t = vec![wasm_val_t::default(); results.len()].into();
let out = func(¶ms, &mut out_results);
if let Some(trap) = out {
return Err(trap.error);
}
let out_results = out_results.as_slice();
for i in 0..results.len() {
results[i] = out_results[i].val();
}
Ok(())
},
);
Box::new(wasm_func_t {
ext: wasm_extern_t {
store: store.store.clone(),
which: func.into(),
},
})
}
#[no_mangle]
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> {
create_function(store, ty, move |params, results| callback(params, results))
}
#[no_mangle]
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 std::ffi::c_void)>,
) -> Box<wasm_func_t> {
let finalizer = crate::ForeignData { data, finalizer };
create_function(store, ty, move |params, results| {
let _ = &finalizer; callback(finalizer.data, params, results)
})
}
pub(crate) fn translate_args<'a>(
dst: &'a mut Vec<Val>,
args: impl ExactSizeIterator<Item = Val>,
results_size: usize,
) -> (&'a [Val], &'a mut [Val]) {
debug_assert!(dst.is_empty());
let num_args = args.len();
dst.reserve(args.len() + results_size);
dst.extend(args);
dst.extend((0..results_size).map(|_| Val::null_func_ref()));
let (a, b) = dst.split_at_mut(num_args);
(a, b)
}
#[no_mangle]
pub unsafe extern "C" fn wasm_func_call(
func: &mut wasm_func_t,
args: *const wasm_val_vec_t,
results: *mut wasm_val_vec_t,
) -> *mut wasm_trap_t {
let f = func.func();
let results = (*results).as_uninit_slice();
let args = (*args).as_slice();
let mut dst = Vec::new();
let (wt_params, wt_results) =
translate_args(&mut dst, args.iter().map(|i| i.val()), results.len());
let result = panic::catch_unwind(AssertUnwindSafe(|| {
f.call(func.ext.store.context_mut(), 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(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::msg(msg.clone())
} else if let Some(msg) = panic.downcast_ref::<&'static str>() {
Error::msg(*msg)
} else {
Error::msg("rust panic happened")
}
}
#[no_mangle]
pub unsafe extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box<wasm_functype_t> {
Box::new(wasm_functype_t::new(f.func().ty(f.ext.store.context())))
}
#[no_mangle]
pub unsafe extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize {
f.func().ty(f.ext.store.context()).params().len()
}
#[no_mangle]
pub unsafe extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize {
f.func().ty(f.ext.store.context()).results().len()
}
#[no_mangle]
pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t {
&mut (*f).ext
}
#[no_mangle]
pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t {
&(*f).ext
}
#[repr(C)]
pub struct wasmtime_caller_t<'a> {
pub(crate) caller: WasmtimeCaller<'a>,
}
impl AsContext for wasmtime_caller_t<'_> {
type Data = WasmtimeStoreData;
fn as_context(&self) -> StoreContext<'_, WasmtimeStoreData> {
self.caller.as_context()
}
}
impl AsContextMut for wasmtime_caller_t<'_> {
fn as_context_mut(&mut self) -> StoreContextMut<'_, WasmtimeStoreData> {
self.caller.as_context_mut()
}
}
pub type wasmtime_func_callback_t = extern "C" fn(
*mut c_void,
*mut wasmtime_caller_t,
*const wasmtime_val_t,
usize,
*mut wasmtime_val_t,
usize,
) -> Option<Box<wasm_trap_t>>;
pub type wasmtime_func_unchecked_callback_t = extern "C" fn(
*mut c_void,
*mut wasmtime_caller_t,
*mut ValRaw,
usize,
) -> Option<Box<wasm_trap_t>>;
#[no_mangle]
pub unsafe extern "C" fn wasmtime_func_new(
store: WasmtimeStoreContextMut<'_>,
ty: &wasm_functype_t,
callback: wasmtime_func_callback_t,
data: *mut c_void,
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
func: &mut Func,
) {
let ty = ty.ty().ty(store.engine());
let cb = c_callback_to_rust_fn(callback, data, finalizer);
let f = Func::new(store, ty, cb);
*func = f;
}
pub(crate) unsafe fn c_callback_to_rust_fn(
callback: wasmtime_func_callback_t,
data: *mut c_void,
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
) -> impl Fn(WasmtimeCaller<'_>, &[Val], &mut [Val]) -> Result<()> {
let foreign = crate::ForeignData { data, finalizer };
move |mut caller, params, results| {
let _ = &foreign; let mut vals = mem::take(&mut caller.data_mut().hostcall_val_storage);
debug_assert!(vals.is_empty());
vals.reserve(params.len() + results.len());
vals.extend(
params
.iter()
.cloned()
.map(|p| wasmtime_val_t::from_val_unscoped(&mut caller, p)),
);
vals.extend((0..results.len()).map(|_| wasmtime_val_t {
kind: crate::WASMTIME_I32,
of: wasmtime_val_union { i32: 0 },
}));
let (params, out_results) = vals.split_at_mut(params.len());
let mut caller = wasmtime_caller_t { caller };
let out = callback(
foreign.data,
&mut caller,
params.as_ptr(),
params.len(),
out_results.as_mut_ptr(),
out_results.len(),
);
if let Some(trap) = out {
return Err(trap.error);
}
for (i, result) in out_results.iter().enumerate() {
results[i] = result.to_val_unscoped(&mut caller);
}
vals.truncate(0);
caller.caller.data_mut().hostcall_val_storage = vals;
Ok(())
}
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_func_new_unchecked(
store: WasmtimeStoreContextMut<'_>,
ty: &wasm_functype_t,
callback: wasmtime_func_unchecked_callback_t,
data: *mut c_void,
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
func: &mut Func,
) {
let ty = ty.ty().ty(store.engine());
let cb = c_unchecked_callback_to_rust_fn(callback, data, finalizer);
*func = Func::new_unchecked(store, ty, cb);
}
pub(crate) unsafe fn c_unchecked_callback_to_rust_fn(
callback: wasmtime_func_unchecked_callback_t,
data: *mut c_void,
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
) -> impl Fn(WasmtimeCaller<'_>, &mut [ValRaw]) -> Result<()> {
let foreign = crate::ForeignData { data, finalizer };
move |caller, values| {
let _ = &foreign; let mut caller = wasmtime_caller_t { caller };
match callback(foreign.data, &mut caller, values.as_mut_ptr(), values.len()) {
None => Ok(()),
Some(trap) => Err(trap.error),
}
}
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_func_call(
mut store: WasmtimeStoreContextMut<'_>,
func: &Func,
args: *const wasmtime_val_t,
nargs: usize,
results: *mut MaybeUninit<wasmtime_val_t>,
nresults: usize,
trap_ret: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
let mut scope = RootScope::new(&mut store);
let mut params = mem::take(&mut scope.as_context_mut().data_mut().wasm_val_storage);
let (wt_params, wt_results) = translate_args(
&mut params,
crate::slice_from_raw_parts(args, nargs)
.iter()
.map(|i| i.to_val(&mut scope)),
nresults,
);
let result = panic::catch_unwind(AssertUnwindSafe(|| {
func.call(&mut scope, wt_params, wt_results)
}));
match result {
Ok(Ok(())) => {
let results = crate::slice_from_raw_parts_mut(results, nresults);
for (slot, val) in results.iter_mut().zip(wt_results.iter()) {
crate::initialize(slot, wasmtime_val_t::from_val(&mut scope, *val));
}
params.truncate(0);
scope.as_context_mut().data_mut().wasm_val_storage = params;
None
}
Ok(Err(trap)) => store_err(trap, trap_ret),
Err(panic) => {
let err = error_from_panic(panic);
*trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err)));
None
}
}
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_func_call_unchecked(
store: WasmtimeStoreContextMut<'_>,
func: &Func,
args_and_results: *mut ValRaw,
args_and_results_len: usize,
trap_ret: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
match func.call_unchecked(store, args_and_results, args_and_results_len) {
Ok(()) => None,
Err(trap) => store_err(trap, trap_ret),
}
}
fn store_err(err: Error, trap_ret: &mut *mut wasm_trap_t) -> Option<Box<wasmtime_error_t>> {
if err.is::<Trap>() {
*trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err)));
None
} else {
Some(Box::new(wasmtime_error_t::from(err)))
}
}
#[no_mangle]
pub extern "C" fn wasmtime_func_type(
store: WasmtimeStoreContext<'_>,
func: &Func,
) -> Box<wasm_functype_t> {
Box::new(wasm_functype_t::new(func.ty(store)))
}
#[no_mangle]
pub extern "C" fn wasmtime_caller_context<'a>(
caller: &'a mut wasmtime_caller_t,
) -> WasmtimeStoreContextMut<'a> {
caller.caller.as_context_mut()
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_caller_export_get(
caller: &mut wasmtime_caller_t,
name: *const u8,
name_len: usize,
item: &mut MaybeUninit<wasmtime_extern_t>,
) -> bool {
let name = match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) {
Ok(name) => name,
Err(_) => return false,
};
let which = match caller.caller.get_export(name) {
Some(item) => item,
None => return false,
};
crate::initialize(item, which.into());
true
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_func_from_raw(
store: WasmtimeStoreContextMut<'_>,
raw: *mut c_void,
func: &mut Func,
) {
*func = Func::from_raw(store, raw).unwrap();
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_func_to_raw(
store: WasmtimeStoreContextMut<'_>,
func: &Func,
) -> *mut c_void {
func.to_raw(store)
}