use alloc::boxed::Box;
use core::ptr::{self, NonNull};
use core::slice;
use crate::environment::Environment;
use crate::error::{Error, Result};
use crate::function::{CallContext, Function, NNM3Function, RawCall};
use crate::runtime::Runtime;
use crate::utils::{cstr_to_str, eq_cstr_str};
use crate::wasm3_priv;
pub struct ParsedModule {
raw: ffi::IM3Module,
env: Environment,
}
impl ParsedModule {
pub fn parse(env: &Environment, bytes: &[u8]) -> Result<Self> {
assert!(bytes.len() <= !0u32 as usize);
let mut module = ptr::null_mut();
let res = unsafe {
ffi::m3_ParseModule(
env.as_ptr(),
&mut module,
bytes.as_ptr(),
bytes.len() as u32,
)
};
Error::from_ffi_res(res).map(|_| ParsedModule {
raw: module,
env: env.clone(),
})
}
pub(crate) fn as_ptr(&self) -> ffi::IM3Module {
self.raw
}
pub fn environment(&self) -> &Environment {
&self.env
}
}
impl Drop for ParsedModule {
fn drop(&mut self) {
unsafe { ffi::m3_FreeModule(self.raw) };
}
}
pub struct Module<'rt> {
raw: ffi::IM3Module,
rt: &'rt Runtime,
}
impl<'rt> Module<'rt> {
#[inline]
pub fn parse(environment: &Environment, bytes: &[u8]) -> Result<ParsedModule> {
ParsedModule::parse(environment, bytes)
}
pub fn link_function<Args, Ret>(
&mut self,
module_name: &str,
function_name: &str,
f: RawCall,
) -> Result<()>
where
Args: crate::WasmArgs,
Ret: crate::WasmType,
{
let func = self.find_import_function(module_name, function_name)?;
Function::<'_, Args, Ret>::validate_sig(func)
.and_then(|_| unsafe { self.link_func_impl(func, f) })
}
pub fn link_closure<Args, Ret, F>(
&mut self,
module_name: &str,
function_name: &str,
closure: F,
) -> Result<()>
where
Args: crate::WasmArgs,
Ret: crate::WasmType,
F: for<'cc> FnMut(&'cc CallContext, Args) -> Ret + 'static,
{
let func = self.find_import_function(module_name, function_name)?;
Function::<'_, Args, Ret>::validate_sig(func)?;
let mut closure = Box::pin(closure);
unsafe { self.link_closure_impl(func, closure.as_mut().get_unchecked_mut()) }?;
self.rt.push_closure(closure);
Ok(())
}
pub fn find_function<Args, Ret>(&self, function_name: &str) -> Result<Function<'rt, Args, Ret>>
where
Args: crate::WasmArgs,
Ret: crate::WasmType,
{
let func = unsafe {
slice::from_raw_parts_mut(
if (*self.raw).functions.is_null() {
NonNull::dangling().as_ptr()
} else {
(*self.raw).functions
},
(*self.raw).numFunctions as usize,
)
.iter_mut()
.find(|func| eq_cstr_str(func.name, function_name))
.map(NonNull::from)
.ok_or(Error::FunctionNotFound)?
};
Function::from_raw(self.rt, func).and_then(Function::compile)
}
pub fn function<Args, Ret>(&self, function_index: usize) -> Result<Function<'rt, Args, Ret>>
where
Args: crate::WasmArgs,
Ret: crate::WasmType,
{
let func = unsafe {
slice::from_raw_parts_mut(
if (*self.raw).functions.is_null() {
NonNull::dangling().as_ptr()
} else {
(*self.raw).functions
},
(*self.raw).numFunctions as usize,
)
.get(function_index)
.map(NonNull::from)
.ok_or(Error::FunctionNotFound)?
};
Function::from_raw(self.rt, func).and_then(Function::compile)
}
pub fn name(&self) -> &str {
unsafe { cstr_to_str((*self.raw).name) }
}
#[cfg(feature = "wasi")]
pub fn link_wasi(&mut self) -> Result<()> {
unsafe { Error::from_ffi_res(ffi::m3_LinkWASI(self.raw)) }
}
pub fn link_libc(&mut self) -> Result<()> {
unsafe { Error::from_ffi_res(ffi::m3_LinkLibC(self.raw)) }
}
}
impl<'rt> Module<'rt> {
pub(crate) fn from_raw(rt: &'rt Runtime, raw: ffi::IM3Module) -> Self {
Module { raw, rt }
}
unsafe fn link_func_impl(&self, mut m3_func: NNM3Function, func: RawCall) -> Result<()> {
let page = wasm3_priv::AcquireCodePageWithCapacity(self.rt.as_ptr(), 2);
if page.is_null() {
Error::from_ffi_res(ffi::m3Err_mallocFailedCodePage)
} else {
m3_func.as_mut().compiled = wasm3_priv::GetPagePC(page);
m3_func.as_mut().module = self.raw;
wasm3_priv::EmitWord_impl(page, crate::wasm3_priv::op_CallRawFunction as _);
wasm3_priv::EmitWord_impl(page, func as _);
wasm3_priv::ReleaseCodePage(self.rt.as_ptr(), page);
Ok(())
}
}
unsafe fn link_closure_impl<Args, Ret, F>(
&self,
mut m3_func: NNM3Function,
closure: *mut F,
) -> Result<()>
where
Args: crate::WasmArgs,
Ret: crate::WasmType,
F: for<'cc> FnMut(&'cc CallContext, Args) -> Ret + 'static,
{
unsafe extern "C" fn _impl<Args, Ret, F>(
runtime: ffi::IM3Runtime,
sp: ffi::m3stack_t,
mem: *mut cty::c_void,
closure: *mut cty::c_void,
) -> *const cty::c_void
where
Args: crate::WasmArgs,
Ret: crate::WasmType,
F: for<'cc> FnMut(&'cc CallContext, Args) -> Ret + 'static,
{
let stack_base = (*runtime).stack as ffi::m3stack_t;
let stack_occupied =
(sp as usize - stack_base as usize) / core::mem::size_of::<ffi::m3slot_t>();
let stack = ptr::slice_from_raw_parts_mut(
sp,
(*runtime).numStackSlots as usize - stack_occupied,
);
let args = Args::pop_from_stack(stack);
let context = CallContext::from_rt_mem(
NonNull::new_unchecked(runtime),
NonNull::new_unchecked(mem.cast()),
);
let ret = (&mut *closure.cast::<F>())(&context, args);
ret.push_on_stack(stack.cast());
ffi::m3Err_none as _
}
let page = wasm3_priv::AcquireCodePageWithCapacity(self.rt.as_ptr(), 3);
if page.is_null() {
Error::from_ffi_res(ffi::m3Err_mallocFailedCodePage)
} else {
m3_func.as_mut().compiled = wasm3_priv::GetPagePC(page);
m3_func.as_mut().module = self.raw;
wasm3_priv::EmitWord_impl(page, crate::wasm3_priv::op_CallRawFunctionEx as _);
wasm3_priv::EmitWord_impl(page, _impl::<Args, Ret, F> as _);
wasm3_priv::EmitWord_impl(page, closure.cast());
wasm3_priv::ReleaseCodePage(self.rt.as_ptr(), page);
Ok(())
}
}
fn find_import_function(&self, module_name: &str, function_name: &str) -> Result<NNM3Function> {
unsafe {
slice::from_raw_parts_mut(
if (*self.raw).functions.is_null() {
NonNull::dangling().as_ptr()
} else {
(*self.raw).functions
},
(*self.raw).numFunctions as usize,
)
.iter_mut()
.filter(|func| eq_cstr_str(func.import.moduleUtf8, module_name))
.find(|func| eq_cstr_str(func.import.fieldUtf8, function_name))
.map(NonNull::from)
.ok_or(Error::FunctionNotFound)
}
}
}
#[test]
fn module_parse() {
let env = Environment::new().expect("env alloc failure");
let fib32 = [
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, 0x01, 0x7f, 0x01,
0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x66, 0x69, 0x62, 0x00, 0x00, 0x0a,
0x1f, 0x01, 0x1d, 0x00, 0x20, 0x00, 0x41, 0x02, 0x49, 0x04, 0x40, 0x20, 0x00, 0x0f, 0x0b,
0x20, 0x00, 0x41, 0x02, 0x6b, 0x10, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6b, 0x10, 0x00, 0x6a,
0x0f, 0x0b,
];
let _ = Module::parse(&env, &fib32[..]).unwrap();
}