use crate::config::InstanceConfig;
use crate::engine::Engine;
use crate::error::{Error, Result};
use crate::instance::Instance;
use crate::module::Module;
use crate::value::{FuncType, Val};
use std::cell::RefCell;
use std::ffi::{CString, c_void};
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::rc::Rc;
use wamrx_sys as sys;
pub type HostFunc = Box<dyn Fn(&[Val], &mut [Val]) + 'static>;
struct HostFuncCtx {
ty: FuncType,
func: HostFunc,
}
struct NativeEntry {
module_name: CString,
_field: CString,
_signature: CString,
_context: Box<HostFuncCtx>,
symbol: Box<[sys::NativeSymbol; 1]>,
}
impl NativeEntry {
fn register(module: &str, name: &str, ty: FuncType, func: HostFunc) -> Result<NativeEntry> {
let module_name = CString::new(module).map_err(|_| interior_nul("module name"))?;
let field = CString::new(name).map_err(|_| interior_nul("function name"))?;
let signature = ty.signature_cstring();
let context = Box::new(HostFuncCtx { ty, func });
let mut symbol = Box::new([sys::NativeSymbol {
symbol: field.as_ptr(),
func_ptr: raw_trampoline as *mut c_void,
signature: signature.as_ptr(),
attachment: (&*context as *const HostFuncCtx) as *mut c_void,
}]);
let ok = unsafe {
sys::wasm_runtime_register_natives_raw(module_name.as_ptr(), symbol.as_mut_ptr(), 1)
};
if !ok {
return Err(Error::Instantiate(
"failed to register host function with WAMR".to_string(),
));
}
Ok(NativeEntry {
module_name,
_field: field,
_signature: signature,
_context: context,
symbol,
})
}
}
fn interior_nul(what: &str) -> Error {
Error::Instantiate(format!("{what} contains an interior NUL byte"))
}
pub struct LinkerState {
_engine: Engine,
entries: RefCell<Vec<NativeEntry>>,
}
impl Drop for LinkerState {
fn drop(&mut self) {
for entry in self.entries.borrow_mut().iter_mut() {
unsafe {
sys::wasm_runtime_unregister_natives(
entry.module_name.as_ptr(),
entry.symbol.as_mut_ptr(),
);
}
}
}
}
pub struct Linker {
state: Rc<LinkerState>,
}
impl Linker {
pub fn new(engine: &Engine) -> Linker {
Linker {
state: Rc::new(LinkerState {
_engine: engine.clone(),
entries: RefCell::new(Vec::new()),
}),
}
}
pub fn define_func(
&mut self,
module: &str,
name: &str,
ty: FuncType,
func: impl Fn(&[Val], &mut [Val]) + 'static,
) -> Result<&mut Self> {
let entry = NativeEntry::register(module, name, ty, Box::new(func))?;
self.state.entries.borrow_mut().push(entry);
Ok(self)
}
pub fn instantiate(&self, module: Module) -> Result<Instance> {
self.instantiate_with(module, &InstanceConfig::default())
}
pub fn instantiate_with(&self, module: Module, config: &InstanceConfig) -> Result<Instance> {
Instance::new(module, Rc::clone(&self.state), config)
}
}
unsafe extern "C" fn raw_trampoline(exec_env: sys::wasm_exec_env_t, argv: *mut u64) {
unsafe {
let attachment = sys::wasm_runtime_get_function_attachment(exec_env);
if attachment.is_null() {
return;
}
let ctx = &*(attachment as *const HostFuncCtx);
let params: Vec<Val> = ctx
.ty
.params()
.iter()
.enumerate()
.map(|(i, &ty)| Val::from_raw_slot(ty, *argv.add(i)))
.collect();
let mut results: Vec<Val> = ctx.ty.results().iter().map(|t| t.default_value()).collect();
let outcome = catch_unwind(AssertUnwindSafe(|| {
(ctx.func)(¶ms, &mut results);
}));
match outcome {
Ok(()) => {
if let Some(result) = results.first() {
*argv = result.to_raw_slot();
}
}
Err(_) => {
let module_inst = sys::wasm_runtime_get_module_inst(exec_env);
let msg = CString::new("host function panicked").unwrap();
sys::wasm_runtime_set_exception(module_inst, msg.as_ptr());
}
}
}
}