feat(runtime-core) Allow host functions without an explicit vm::Ctx argument.

This patch allows host functions to get a signature without an
explicit `vm::Ctx` argument.

It is for Rust only. The C API receives a function pointer and has no
clue whether a `vm::Ctx` argument is present or not, so it assumes it
is always declared.

From the backend point of view, the pointer to `vm::Ctx` is always
inserted in the stack, but it is not used by the function when the
argument is absent.
This commit is contained in:
Ivan Enderlin
2019-10-30 14:46:22 +01:00
parent bd9d4d9cc8
commit 95e1b85c56

View File

@ -124,16 +124,27 @@ pub trait WasmTypeList {
self, self,
f: NonNull<vm::Func>, f: NonNull<vm::Func>,
wasm: Wasm, wasm: Wasm,
ctx: *mut Ctx, ctx: *mut vm::Ctx,
) -> Result<Rets, RuntimeError> ) -> Result<Rets, RuntimeError>
where where
Rets: WasmTypeList; Rets: WasmTypeList;
} }
/// Empty trait to specify the kind of `ExternalFunction`: With or
/// without a `vm::Ctx` argument.
pub trait ExternalFunctionKind {}
pub struct ExplicitVmCtx {}
pub struct ImplicitVmCtx {}
impl ExternalFunctionKind for ExplicitVmCtx {}
impl ExternalFunctionKind for ImplicitVmCtx {}
/// Represents a function that can be converted to a `vm::Func` /// Represents a function that can be converted to a `vm::Func`
/// (function pointer) that can be called within WebAssembly. /// (function pointer) that can be called within WebAssembly.
pub trait ExternalFunction<Args, Rets> pub trait ExternalFunction<Kind, Args, Rets>
where where
Kind: ExternalFunctionKind,
Args: WasmTypeList, Args: WasmTypeList,
Rets: WasmTypeList, Rets: WasmTypeList,
{ {
@ -208,9 +219,10 @@ where
Args: WasmTypeList, Args: WasmTypeList,
Rets: WasmTypeList, Rets: WasmTypeList,
{ {
pub fn new<F>(f: F) -> Func<'a, Args, Rets, Host> pub fn new<F, Kind>(f: F) -> Func<'a, Args, Rets, Host>
where where
F: ExternalFunction<Args, Rets>, Kind: ExternalFunctionKind,
F: ExternalFunction<Kind, Args, Rets>,
{ {
Func { Func {
inner: Host(()), inner: Host(()),
@ -438,7 +450,7 @@ macro_rules! impl_traits {
} }
} }
impl< $( $x, )* Rets, Trap, FN > ExternalFunction<( $( $x ),* ), Rets> for FN impl< $( $x, )* Rets, Trap, FN > ExternalFunction<ExplicitVmCtx, ( $( $x ),* ), Rets> for FN
where where
$( $x: WasmExternType, )* $( $x: WasmExternType, )*
Rets: WasmTypeList, Rets: WasmTypeList,
@ -451,20 +463,20 @@ macro_rules! impl_traits {
/// This is required for the llvm backend to be able to unwind through this function. /// This is required for the llvm backend to be able to unwind through this function.
#[cfg_attr(nightly, unwind(allowed))] #[cfg_attr(nightly, unwind(allowed))]
extern fn wrap<$( $x, )* Rets, Trap, FN>( extern fn wrap<$( $x, )* Rets, Trap, FN>(
ctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )* vmctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )*
) -> Rets::CStruct ) -> Rets::CStruct
where where
$( $x: WasmExternType, )* $( $x: WasmExternType, )*
Rets: WasmTypeList, Rets: WasmTypeList,
Trap: TrapEarly<Rets>, Trap: TrapEarly<Rets>,
FN: Fn(&mut vm::Ctx $( , $x )*) -> Trap, FN: Fn(&mut vm::Ctx, $( $x, )*) -> Trap,
{ {
let f: FN = unsafe { mem::transmute_copy(&()) }; let f: FN = unsafe { mem::transmute_copy(&()) };
let err = match panic::catch_unwind( let err = match panic::catch_unwind(
panic::AssertUnwindSafe( panic::AssertUnwindSafe(
|| { || {
f(ctx $( , WasmExternType::from_native($x) )* ).report() f(vmctx $( , WasmExternType::from_native($x) )* ).report()
} }
) )
) { ) {
@ -477,7 +489,7 @@ macro_rules! impl_traits {
}; };
unsafe { unsafe {
(&*ctx.module).runnable_module.do_early_trap(err) (&*vmctx.module).runnable_module.do_early_trap(err)
} }
} }
@ -490,7 +502,65 @@ macro_rules! impl_traits {
); );
NonNull::new(unsafe { NonNull::new(unsafe {
::std::mem::transmute_copy::<_, *mut vm::Func>(self) mem::transmute_copy::<_, *mut vm::Func>(self)
}).unwrap()
}
}
}
impl< $( $x, )* Rets, Trap, FN > ExternalFunction<ImplicitVmCtx, ( $( $x ),* ), Rets> for FN
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
Trap: TrapEarly<Rets>,
FN: Fn($( $x, )*) -> Trap,
{
#[allow(non_snake_case)]
fn to_raw(&self) -> NonNull<vm::Func> {
if mem::size_of::<Self>() == 0 {
/// This is required for the llvm backend to be able to unwind through this function.
#[cfg_attr(nightly, unwind(allowed))]
extern fn wrap<$( $x, )* Rets, Trap, FN>(
vmctx: &mut vm::Ctx $( , $x: <$x as WasmExternType>::Native )*
) -> Rets::CStruct
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
Trap: TrapEarly<Rets>,
FN: Fn($( $x, )*) -> Trap,
{
let f: FN = unsafe { mem::transmute_copy(&()) };
let err = match panic::catch_unwind(
panic::AssertUnwindSafe(
|| {
f($( WasmExternType::from_native($x), )* ).report()
}
)
) {
Ok(Ok(returns)) => return returns.into_c_struct(),
Ok(Err(err)) => {
let b: Box<_> = err.into();
b as Box<dyn Any>
},
Err(err) => err,
};
unsafe {
(&*vmctx.module).runnable_module.do_early_trap(err)
}
}
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap()
} else {
assert_eq!(
mem::size_of::<Self>(),
mem::size_of::<usize>(),
"you cannot use a closure that captures state for `Func`."
);
NonNull::new(unsafe {
mem::transmute_copy::<_, *mut vm::Func>(self)
}).unwrap() }).unwrap()
} }
} }