use core::cmp::{Eq, PartialEq};
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use core::ptr::{self, NonNull};
use core::slice;
use core::str;
use crate::error::{Error, Result};
use crate::runtime::Runtime;
use crate::utils::cstr_to_str;
use crate::wasm3_priv;
use crate::{WasmArgs, WasmType};
pub struct CallContext {
runtime: NonNull<ffi::M3Runtime>,
}
impl CallContext {
pub(crate) fn from_rt(runtime: NonNull<ffi::M3Runtime>) -> CallContext {
CallContext { runtime }
}
unsafe fn mallocated(&self) -> *mut ffi::M3MemoryHeader {
self.runtime.as_ref().memory.mallocated
}
pub unsafe fn memory(&self) -> *const [u8] {
let mallocated = self.mallocated();
let len = (*mallocated).length as usize;
let data = if len == 0 {
ptr::NonNull::dangling().as_ptr()
} else {
mallocated.offset(1).cast()
};
ptr::slice_from_raw_parts(data, len)
}
pub unsafe fn memory_mut(&self) -> *mut [u8] {
let mallocated = self.mallocated();
let len = (*mallocated).length as usize;
let data = if len == 0 {
ptr::NonNull::dangling().as_ptr()
} else {
mallocated.offset(1).cast()
};
ptr::slice_from_raw_parts_mut(data, len)
}
}
pub type RawCall = unsafe extern "C" fn(
runtime: ffi::IM3Runtime,
_sp: ffi::m3stack_t,
_mem: *mut cty::c_void,
) -> *const cty::c_void;
pub(crate) type NNM3Function = NonNull<ffi::M3Function>;
#[derive(Debug, Copy, Clone)]
pub struct Function<'rt, Args, Ret> {
raw: NNM3Function,
rt: &'rt Runtime,
_pd: PhantomData<*const (Args, Ret)>,
}
impl<'rt, Args, Ret> Eq for Function<'rt, Args, Ret> {}
impl<'rt, Args, Ret> PartialEq for Function<'rt, Args, Ret> {
fn eq(&self, other: &Self) -> bool {
self.raw == other.raw
}
}
impl<'rt, Args, Ret> Hash for Function<'rt, Args, Ret> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.raw.hash(state);
}
}
impl<'rt, Args, Ret> Function<'rt, Args, Ret>
where
Args: WasmArgs,
Ret: WasmType,
{
pub fn import_module_name(&self) -> &str {
unsafe { cstr_to_str(self.raw.as_ref().import.moduleUtf8) }
}
pub fn name(&self) -> &str {
unsafe { cstr_to_str(self.raw.as_ref().name) }
}
}
impl<'rt, Args, Ret> Function<'rt, Args, Ret>
where
Args: WasmArgs,
Ret: WasmType,
{
pub(crate) fn validate_sig(mut func: NNM3Function) -> Result<()> {
let &ffi::M3FuncType {
returnType: ret,
argTypes: ref args,
numArgs: num,
..
} = unsafe { &*func.as_mut().funcType };
let args = unsafe { slice::from_raw_parts(args.as_ptr(), num as usize) };
match Ret::TYPE_INDEX == ret && Args::validate_types(args) {
true => Ok(()),
false => Err(Error::InvalidFunctionSignature),
}
}
#[inline]
pub(crate) fn from_raw(rt: &'rt Runtime, raw: NNM3Function) -> Result<Self> {
Self::validate_sig(raw)?;
let this = Function {
raw,
rt,
_pd: PhantomData,
};
this.compile()
}
#[inline]
pub(crate) fn compile(self) -> Result<Self> {
unsafe {
if self.raw.as_ref().compiled.is_null() {
Error::from_ffi_res(wasm3_priv::Compile_Function(self.raw.as_ptr()))?;
}
};
Ok(self)
}
fn call_impl(&self, args: Args) -> Result<Ret> {
let stack = self.rt.stack_mut();
let ret = unsafe {
args.push_on_stack(stack);
Self::call_impl_(
self.raw.as_ref().compiled,
stack.cast(),
self.rt.mallocated(),
0,
0.0,
)
};
Error::from_ffi_res(ret.cast()).map(|()| unsafe { Ret::pop_from_stack(stack.cast()) })
}
#[inline]
unsafe fn call_impl_(
_pc: ffi::pc_t,
_sp: ffi::m3stack_t,
_mem: *mut ffi::M3MemoryHeader,
_r0: ffi::m3reg_t,
_fp0: f64,
) -> ffi::m3ret_t {
let possible_trap = ffi::m3_Yield();
if !possible_trap.is_null() {
possible_trap.cast()
} else {
(*_pc.cast::<ffi::IM3Operation>()).expect("IM3Operation was null")(
_pc.add(1),
_sp,
_mem,
_r0,
_fp0,
)
}
}
}
macro_rules! func_call_impl {
($($types:ident),*) => { func_call_impl!(@rec [$($types,)*] []); };
(@rec [] [$($types:ident,)*]) => { func_call_impl!(@do_impl $($types,)*); };
(@rec [$head:ident, $($tail:ident,)*] [$($types:ident,)*]) => {
func_call_impl!(@do_impl $($types,)*);
func_call_impl!(@rec [$($tail,)*] [$($types,)* $head,]);
};
(@do_impl) => {};
(@do_impl $($types:ident,)*) => {
#[doc(hidden)]
impl<'rt, $($types,)* Ret> Function<'rt, ($($types,)*), Ret>
where
Ret: WasmType,
($($types,)*): WasmArgs,
{
#[inline]
#[allow(non_snake_case, clippy::too_many_arguments)]
pub fn call(&self, $($types: $types),*) -> Result<Ret> {
self.call_impl(($($types,)*))
}
}
};
}
func_call_impl!(A, B, C, D, E, F, G, H, J, K, L, M, N, O, P, Q);
impl<'rt, ARG, Ret> Function<'rt, ARG, Ret>
where
Ret: WasmType,
ARG: crate::WasmArg,
{
#[inline]
pub fn call(&self, arg: ARG) -> Result<Ret> {
self.call_impl(arg)
}
}
impl<'rt, Ret> Function<'rt, (), Ret>
where
Ret: WasmType,
{
#[inline]
pub fn call(&self) -> Result<Ret> {
self.call_impl(())
}
}