use crate::engine::Engine;
use crate::error::{Error, Result, error_message};
use crate::instance::Instance;
use crate::module::Module;
use crate::trampoline::{HostFunc, trampoline};
use crate::value::{FuncType, Val};
use fizzyx_sys as sys;
use std::ffi::CString;
use std::rc::Rc;
struct HostImport {
module: CString,
name: CString,
host: Rc<HostFunc>,
}
pub struct Linker {
engine: Engine,
imports: Vec<HostImport>,
}
impl Linker {
pub fn new(engine: &Engine) -> Self {
Self {
engine: engine.clone(),
imports: Vec::new(),
}
}
pub fn engine(&self) -> &Engine {
&self.engine
}
pub fn func_new<F>(
&mut self,
module: &str,
name: &str,
ty: FuncType,
func: F,
) -> Result<&mut Self>
where
F: Fn(&[Val], &mut [Val]) + 'static,
{
let module = CString::new(module).map_err(|_| {
Error::Instantiation("module name contains an interior NUL byte".into())
})?;
let name = CString::new(name).map_err(|_| {
Error::Instantiation("function name contains an interior NUL byte".into())
})?;
self.imports.push(HostImport {
module,
name,
host: Rc::new(HostFunc::new(ty, Box::new(func))),
});
Ok(self)
}
pub fn instantiate(&self, module: &Module) -> Result<Instance> {
let inputs: Vec<Vec<sys::FizzyValueType>> = self
.imports
.iter()
.map(|imp| imp.host.ty().to_sys_parts().0)
.collect();
let imported: Vec<sys::FizzyImportedFunction> = self
.imports
.iter()
.zip(&inputs)
.map(|(imp, inputs)| {
let (_, output) = imp.host.ty().to_sys_parts();
let func_type = sys::FizzyFunctionType {
output,
inputs: if inputs.is_empty() {
core::ptr::null()
} else {
inputs.as_ptr()
},
inputs_size: inputs.len(),
};
sys::FizzyImportedFunction {
module: imp.module.as_ptr(),
name: imp.name.as_ptr(),
external_function: sys::FizzyExternalFunction {
type_: func_type,
function: Some(trampoline),
context: Rc::as_ptr(&imp.host) as *mut core::ffi::c_void,
},
}
})
.collect();
let module_ptr = module.clone_raw();
if module_ptr.is_null() {
return Err(Error::Instantiation("failed to clone module".into()));
}
let mut error = sys::FizzyError::default();
let inst = unsafe {
sys::fizzy_resolve_instantiate(
module_ptr,
if imported.is_empty() {
core::ptr::null()
} else {
imported.as_ptr()
},
imported.len(),
core::ptr::null(),
core::ptr::null(),
core::ptr::null(),
0,
self.engine.memory_pages_limit(),
&mut error,
)
};
if inst.is_null() {
return Err(Error::Instantiation(error_message(&error)));
}
let keepalive: Vec<Rc<HostFunc>> =
self.imports.iter().map(|imp| imp.host.clone()).collect();
Ok(unsafe { Instance::from_raw(inst, keepalive) })
}
}