Add typed functions and weird type parameter things

This commit is contained in:
Lachlan Sneff
2019-02-02 15:28:50 -08:00
parent 6c33aa5803
commit eba66f3b33
24 changed files with 706 additions and 463 deletions

View File

@ -3,12 +3,12 @@ use crate::types::{
};
use std::sync::Arc;
pub type Result<T> = std::result::Result<T, Box<Error>>;
pub type CompileResult<T> = std::result::Result<T, Box<CompileError>>;
pub type Result<T> = std::result::Result<T, Error>;
pub type CompileResult<T> = std::result::Result<T, CompileError>;
pub type LinkResult<T> = std::result::Result<T, Vec<LinkError>>;
pub type RuntimeResult<T> = std::result::Result<T, Box<RuntimeError>>;
pub type CallResult<T> = std::result::Result<T, Box<CallError>>;
pub type ResolveResult<T> = std::result::Result<T, Box<ResolveError>>;
pub type RuntimeResult<T> = std::result::Result<T, RuntimeError>;
pub type CallResult<T> = std::result::Result<T, CallError>;
pub type ResolveResult<T> = std::result::Result<T, ResolveError>;
/// This is returned when the chosen compiler is unable to
/// successfully compile the provided webassembly module into
@ -175,80 +175,44 @@ impl PartialEq for Error {
}
}
impl From<Box<CompileError>> for Box<Error> {
fn from(compile_err: Box<CompileError>) -> Self {
Box::new(Error::CompileError(*compile_err))
}
}
impl From<Vec<LinkError>> for Box<Error> {
fn from(link_err: Vec<LinkError>) -> Self {
Box::new(Error::LinkError(link_err))
}
}
impl From<Box<RuntimeError>> for Box<Error> {
fn from(runtime_err: Box<RuntimeError>) -> Self {
Box::new(Error::RuntimeError(*runtime_err))
}
}
impl From<Box<ResolveError>> for Box<Error> {
fn from(resolve_err: Box<ResolveError>) -> Self {
Box::new(Error::ResolveError(*resolve_err))
}
}
impl From<Box<CallError>> for Box<Error> {
fn from(call_err: Box<CallError>) -> Self {
Box::new(Error::CallError(*call_err))
}
}
impl From<Box<RuntimeError>> for Box<CallError> {
fn from(runtime_err: Box<RuntimeError>) -> Self {
Box::new(CallError::Runtime(*runtime_err))
}
}
impl From<Box<ResolveError>> for Box<CallError> {
fn from(resolve_err: Box<ResolveError>) -> Self {
Box::new(CallError::Resolve(*resolve_err))
}
}
impl From<CompileError> for Box<Error> {
impl From<CompileError> for Error {
fn from(compile_err: CompileError) -> Self {
Box::new(Error::CompileError(compile_err))
Error::CompileError(compile_err)
}
}
impl From<RuntimeError> for Box<Error> {
impl From<RuntimeError> for Error {
fn from(runtime_err: RuntimeError) -> Self {
Box::new(Error::RuntimeError(runtime_err))
Error::RuntimeError(runtime_err)
}
}
impl From<CallError> for Box<Error> {
impl From<CallError> for Error {
fn from(call_err: CallError) -> Self {
Box::new(Error::CallError(call_err))
Error::CallError(call_err)
}
}
impl From<CreationError> for Box<Error> {
impl From<CreationError> for Error {
fn from(creation_err: CreationError) -> Self {
Box::new(Error::CreationError(creation_err))
Error::CreationError(creation_err)
}
}
impl From<RuntimeError> for Box<CallError> {
impl From<Vec<LinkError>> for Error {
fn from(link_errs: Vec<LinkError>) -> Self {
Error::LinkError(link_errs)
}
}
impl From<RuntimeError> for CallError {
fn from(runtime_err: RuntimeError) -> Self {
Box::new(CallError::Runtime(runtime_err))
CallError::Runtime(runtime_err)
}
}
impl From<ResolveError> for Box<CallError> {
impl From<ResolveError> for CallError {
fn from(resolve_err: ResolveError) -> Self {
Box::new(CallError::Resolve(resolve_err))
CallError::Resolve(resolve_err)
}
}

View File

@ -28,11 +28,11 @@ impl IsExport for Export {
/// # use wasmer_runtime_core::vm::Ctx;
/// let import_object = imports! {
/// "env" => {
/// "foo" => func!(foo, [i32] -> [i32]),
/// "foo" => func!(foo),
/// },
/// };
///
/// extern fn foo(n: i32, _: &mut Ctx) -> i32 {
/// fn foo(n: i32, _: &mut Ctx) -> i32 {
/// n
/// }
/// ```

View File

@ -8,6 +8,7 @@ use crate::{
memory::Memory,
module::{ExportIndex, Module, ModuleInner},
table::Table,
typed_func::{Func, Safe, WasmTypeList},
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value},
vm,
};
@ -80,21 +81,30 @@ impl Instance {
Ok(instance)
}
/// This returns the representation of a function that can be called
/// safely.
/// Through generic magic and the awe-inspiring power of traits, we bring you...
///
/// # "Func"
///
/// A [`Func`] allows you to call functions exported from wasm with
/// near zero overhead.
///
/// [`Func`]: struct.Func.html
/// # Usage:
///
/// ```
/// # use wasmer_runtime_core::Instance;
/// # use wasmer_runtime_core::error::CallResult;
/// # fn call_foo(instance: &mut Instance) -> CallResult<()> {
/// instance
/// .func("foo")?
/// .call(&[])?;
/// # use wasmer_runtime_core::{Func, Instance, error::ResolveResult};
/// # fn typed_func(instance: Instance) -> ResolveResult<()> {
/// let func: Func<(i32, i32)> = instance.func("foo")?;
///
/// func.call(42, 43);
/// # Ok(())
/// # }
/// ```
pub fn func(&self, name: &str) -> ResolveResult<Function> {
pub fn func<Args, Rets>(&self, name: &str) -> ResolveResult<Func<Args, Rets, Safe>>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
let export_index =
self.module
.exports
@ -111,7 +121,76 @@ impl Instance {
.expect("broken invariant, incorrect func index");
let signature = self.module.sig_registry.lookup_signature(sig_index);
Ok(Function {
if signature.params() != Args::types() || signature.returns() != Rets::types() {
Err(ResolveError::Signature {
expected: Arc::clone(&signature),
found: Args::types().to_vec(),
})?;
}
let ctx = match func_index.local_or_import(&*self.module) {
LocalOrImport::Local(_) => self.inner.vmctx,
LocalOrImport::Import(imported_func_index) => {
self.inner.import_backing.vm_functions[imported_func_index].vmctx
}
};
let func_ptr = match func_index.local_or_import(&self.module) {
LocalOrImport::Local(local_func_index) => self
.module
.func_resolver
.get(&self.module, local_func_index)
.unwrap()
.as_ptr(),
LocalOrImport::Import(import_func_index) => {
self.inner.import_backing.vm_functions[import_func_index].func
}
};
let typed_func: Func<Args, Rets, Safe> =
unsafe { Func::new_from_ptr(func_ptr as _, ctx) };
Ok(typed_func)
} else {
Err(ResolveError::ExportWrongType {
name: name.to_string(),
}
.into())
}
}
/// This returns the representation of a function that can be called
/// safely.
///
/// # Usage:
/// ```
/// # use wasmer_runtime_core::Instance;
/// # use wasmer_runtime_core::error::CallResult;
/// # fn call_foo(instance: &mut Instance) -> CallResult<()> {
/// instance
/// .dyn_func("foo")?
/// .call(&[])?;
/// # Ok(())
/// # }
/// ```
pub fn dyn_func(&self, name: &str) -> ResolveResult<DynFunc> {
let export_index =
self.module
.exports
.get(name)
.ok_or_else(|| ResolveError::ExportNotFound {
name: name.to_string(),
})?;
if let ExportIndex::Func(func_index) = export_index {
let sig_index = *self
.module
.func_assoc
.get(*func_index)
.expect("broken invariant, incorrect func index");
let signature = self.module.sig_registry.lookup_signature(sig_index);
Ok(DynFunc {
signature,
module: &self.module,
instance_inner: &self.inner,
@ -343,14 +422,14 @@ impl LikeNamespace for Instance {
}
/// A representation of an exported WebAssembly function.
pub struct Function<'a> {
pub struct DynFunc<'a> {
pub(crate) signature: Arc<FuncSig>,
module: &'a ModuleInner,
pub(crate) instance_inner: &'a InstanceInner,
func_index: FuncIndex,
}
impl<'a> Function<'a> {
impl<'a> DynFunc<'a> {
/// Call an exported webassembly function safely.
///
/// Pass arguments by wrapping each one in the [`Value`] enum.

View File

@ -18,6 +18,7 @@ mod sig_registry;
pub mod structures;
mod sys;
pub mod table;
mod typed_func;
pub mod types;
pub mod units;
pub mod vm;
@ -31,6 +32,8 @@ pub use self::error::Result;
pub use self::instance::Instance;
#[doc(inline)]
pub use self::module::Module;
#[doc(inline)]
pub use self::typed_func::Func;
use std::sync::Arc;
pub mod prelude {

View File

@ -14,21 +14,8 @@ macro_rules! debug {
#[macro_export]
macro_rules! func {
($func:path, [ $( $params:ident ),* ] -> [ $( $returns:ident ),* ] ) => {{
use $crate::{
export::{Context, Export, FuncPointer},
types::{FuncSig, Type, WasmExternType},
vm,
};
let func: extern fn( $( $params, )* &mut vm::Ctx) -> ($( $returns )*) = $func;
Export::Function {
func: unsafe { FuncPointer::new(func as _) },
ctx: Context::Internal,
signature: FuncSig::new(
&[ $( <$params as WasmExternType>::TYPE, )* ] as &[Type],
&[ $( <$returns as WasmExternType>::TYPE, )* ] as &[Type],
).into(),
}
($func:path) => {{
$crate::Func::new($func)
}};
}
@ -47,11 +34,11 @@ macro_rules! func {
/// # use wasmer_runtime_core::vm::Ctx;
/// let import_object = imports! {
/// "env" => {
/// "foo" => func!(foo, [i32] -> [i32]),
/// "foo" => func!(foo),
/// },
/// };
///
/// extern fn foo(n: i32, _: &mut Ctx) -> i32 {
/// fn foo(n: i32, _: &mut Ctx) -> i32 {
/// n
/// }
/// ```

View File

@ -1,6 +1,6 @@
use crate::{
error::CreationError,
instance::Function,
instance::DynFunc,
sig_registry::SigRegistry,
structures::TypedIndex,
types::{FuncSig, TableDescriptor},
@ -14,7 +14,7 @@ enum AnyfuncInner<'a> {
ptr: *const vm::Func,
signature: Arc<FuncSig>,
},
Managed(Function<'a>),
Managed(DynFunc<'a>),
}
pub struct Anyfunc<'a> {
@ -35,8 +35,8 @@ impl<'a> Anyfunc<'a> {
}
}
impl<'a> From<Function<'a>> for Anyfunc<'a> {
fn from(function: Function<'a>) -> Self {
impl<'a> From<DynFunc<'a>> for Anyfunc<'a> {
fn from(function: DynFunc<'a>) -> Self {
Anyfunc {
inner: AnyfuncInner::Managed(function),
}

View File

@ -0,0 +1,232 @@
use crate::{
error::RuntimeError,
export::{Context, Export, FuncPointer},
import::IsExport,
types::{FuncSig, Type, WasmExternType},
vm::Ctx,
};
use std::{marker::PhantomData, mem, ptr, sync::Arc};
pub trait Safeness {}
pub struct Safe;
pub struct Unsafe;
impl Safeness for Safe {}
impl Safeness for Unsafe {}
pub trait WasmTypeList {
type CStruct;
fn from_c_struct(c_struct: Self::CStruct) -> Self;
fn into_c_struct(self) -> Self::CStruct;
fn types() -> &'static [Type];
unsafe fn call<Rets>(self, f: *const (), ctx: *mut Ctx) -> Rets
where
Rets: WasmTypeList;
}
pub trait ExternalFunction<Args, Rets>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn to_raw(self) -> *const ();
}
pub struct Func<'a, Args = (), Rets = (), Safety: Safeness = Safe> {
f: *const (),
ctx: *mut Ctx,
_phantom: PhantomData<(&'a (), Safety, Args, Rets)>,
}
impl<'a, Args, Rets> Func<'a, Args, Rets, Safe>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
pub(crate) unsafe fn new_from_ptr(f: *const (), ctx: *mut Ctx) -> Func<'a, Args, Rets, Safe> {
Func {
f,
ctx,
_phantom: PhantomData,
}
}
}
impl<'a, Args, Rets> Func<'a, Args, Rets, Unsafe>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
pub fn new<F>(f: F) -> Func<'a, Args, Rets, Unsafe>
where
F: ExternalFunction<Args, Rets>,
{
Func {
f: f.to_raw(),
ctx: ptr::null_mut(),
_phantom: PhantomData,
}
}
}
impl<'a, Args, Rets, Safety> Func<'a, Args, Rets, Safety>
where
Args: WasmTypeList,
Rets: WasmTypeList,
Safety: Safeness,
{
pub fn params(&self) -> &'static [Type] {
Args::types()
}
pub fn returns(&self) -> &'static [Type] {
Rets::types()
}
}
impl<A: WasmExternType> WasmTypeList for (A,) {
type CStruct = S1<A>;
fn from_c_struct(c_struct: Self::CStruct) -> Self {
let S1(a) = c_struct;
(a,)
}
fn into_c_struct(self) -> Self::CStruct {
#[allow(unused_parens, non_snake_case)]
let (a,) = self;
S1(a)
}
fn types() -> &'static [Type] {
&[A::TYPE]
}
#[allow(non_snake_case)]
unsafe fn call<Rets: WasmTypeList>(self, f: *const (), ctx: *mut Ctx) -> Rets {
let f: extern "C" fn(A, *mut Ctx) -> Rets = mem::transmute(f);
let (a,) = self;
f(a, ctx)
}
}
impl<'a, A: WasmExternType, Rets> Func<'a, (A,), Rets, Safe>
where
Rets: WasmTypeList,
{
pub fn call(&self, a: A) -> Result<Rets, RuntimeError> {
Ok(unsafe { <A as WasmTypeList>::call(a, self.f, self.ctx) })
}
}
macro_rules! impl_traits {
( $struct_name:ident, $( $x:ident ),* ) => {
#[repr(C)]
pub struct $struct_name <$( $x ),*> ( $( $x ),* );
impl< $( $x: WasmExternType, )* > WasmTypeList for ( $( $x ),* ) {
type CStruct = $struct_name<$( $x ),*>;
fn from_c_struct(c_struct: Self::CStruct) -> Self {
#[allow(non_snake_case)]
let $struct_name ( $( $x ),* ) = c_struct;
( $( $x ),* )
}
fn into_c_struct(self) -> Self::CStruct {
#[allow(unused_parens, non_snake_case)]
let ( $( $x ),* ) = self;
$struct_name ( $( $x ),* )
}
fn types() -> &'static [Type] {
&[$( $x::TYPE, )*]
}
#[allow(non_snake_case)]
unsafe fn call<Rets: WasmTypeList>(self, f: *const (), ctx: *mut Ctx) -> Rets {
let f: extern fn( $( $x, )* *mut Ctx) -> Rets::CStruct = mem::transmute(f);
#[allow(unused_parens)]
let ( $( $x ),* ) = self;
let c_struct = f( $( $x, )* ctx);
Rets::from_c_struct(c_struct)
}
}
impl< $( $x: WasmExternType, )* Rets: WasmTypeList, FN: Fn( $( $x, )* &mut Ctx) -> Rets> ExternalFunction<($( $x ),*), Rets> for FN {
#[allow(non_snake_case)]
fn to_raw(self) -> *const () {
assert_eq!(mem::size_of::<Self>(), 0, "you cannot use a closure that captures state for `Func`.");
extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, FN: Fn( $( $x, )* &mut Ctx) -> Rets>( $( $x: $x, )* ctx: &mut Ctx) -> Rets::CStruct {
let f: FN = unsafe { mem::transmute_copy(&()) };
let rets = f( $( $x, )* ctx);
rets.into_c_struct()
}
wrap::<$( $x, )* Rets, Self> as *const ()
}
}
impl<'a, $( $x: WasmExternType, )* Rets> Func<'a, ( $( $x ),* ), Rets, Safe>
where
Rets: WasmTypeList,
{
#[allow(non_snake_case)]
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
#[allow(unused_parens)]
Ok(unsafe { <( $( $x ),* ) as WasmTypeList>::call(( $($x),* ), self.f, self.ctx) })
}
}
};
}
impl_traits!(S0,);
impl_traits!(S1, A);
impl_traits!(S2, A, B);
impl_traits!(S3, A, B, C);
impl_traits!(S4, A, B, C, D);
impl_traits!(S5, A, B, C, D, E);
impl_traits!(S6, A, B, C, D, E, F);
impl_traits!(S7, A, B, C, D, E, F, G);
impl_traits!(S8, A, B, C, D, E, F, G, H);
impl_traits!(S9, A, B, C, D, E, F, G, H, I);
impl_traits!(S10, A, B, C, D, E, F, G, H, I, J);
impl_traits!(S11, A, B, C, D, E, F, G, H, I, J, K);
impl<'a, Args, Rets, Safety> IsExport for Func<'a, Args, Rets, Safety>
where
Args: WasmTypeList,
Rets: WasmTypeList,
Safety: Safeness,
{
fn to_export(&mut self) -> Export {
let func = unsafe { FuncPointer::new(self.f as _) };
let ctx = Context::Internal;
let signature = Arc::new(FuncSig::new(Args::types(), Rets::types()));
Export::Function {
func,
ctx,
signature,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_call() {
fn foo(a: i32, b: i32, _ctx: &mut Ctx) -> (i32, i32) {
(a, b)
}
let _f = Func::new(foo);
}
#[test]
fn test_imports() {
use crate::{func, imports};
fn foo(a: i32, _ctx: &mut Ctx) -> i32 {
a
}
let _import_object = imports! {
"env" => {
"foo" => func!(foo),
},
};
}
}