mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-06 20:58:28 +00:00
Get NativeFunc passing basic tests
This commit is contained in:
@@ -116,6 +116,14 @@ impl Exports {
|
||||
self.get(name)
|
||||
}
|
||||
|
||||
/*
|
||||
/// Get an export as a `Func`.
|
||||
pub fn get_native_function::<Args, Rets>(&self, name: &str) -> Result<&NativeFunction, ExportError> {
|
||||
// TODO:
|
||||
//self.get(name)
|
||||
}
|
||||
*/
|
||||
|
||||
/// Get an export as an `Extern`.
|
||||
pub fn get_extern(&self, name: &str) -> Option<&Extern> {
|
||||
self.map.get(name)
|
||||
|
||||
35
lib/api/src/externals/function.rs
vendored
35
lib/api/src/externals/function.rs
vendored
@@ -3,6 +3,7 @@ use crate::externals::Extern;
|
||||
use crate::store::Store;
|
||||
use crate::types::Val;
|
||||
use crate::FunctionType;
|
||||
use crate::NativeFunc;
|
||||
use crate::RuntimeError;
|
||||
use std::cmp::max;
|
||||
use wasm_common::{HostFunction, WasmTypeList, WithEnv, WithoutEnv};
|
||||
@@ -15,7 +16,7 @@ use wasmer_runtime::{
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct WasmFunctionDefinition {
|
||||
// The trampoline to do the call
|
||||
trampoline: VMTrampoline,
|
||||
pub(crate) trampoline: VMTrampoline,
|
||||
}
|
||||
|
||||
/// The inner helper
|
||||
@@ -30,11 +31,11 @@ pub enum FunctionDefinition {
|
||||
/// A WebAssembly `function`.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Function {
|
||||
store: Store,
|
||||
definition: FunctionDefinition,
|
||||
pub(crate) store: Store,
|
||||
pub(crate) definition: FunctionDefinition,
|
||||
// If the Function is owned by the Store, not the instance
|
||||
owned_by_store: bool,
|
||||
exported: ExportFunction,
|
||||
pub(crate) owned_by_store: bool,
|
||||
pub(crate) exported: ExportFunction,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
@@ -286,6 +287,30 @@ impl Function {
|
||||
vmctx: self.exported.vmctx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn native<'a, Args, Rets>(self) -> Option<NativeFunc<'a, Args, Rets>>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
// type check
|
||||
if self.exported.signature.params() != Args::wasm_types() {
|
||||
// todo: error param types don't match
|
||||
return None;
|
||||
}
|
||||
if self.exported.signature.results() != Rets::wasm_types() {
|
||||
// todo: error result types don't match
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NativeFunc::new(
|
||||
self.store,
|
||||
self.exported.address,
|
||||
self.exported.vmctx,
|
||||
self.exported.kind,
|
||||
self.definition,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Function {
|
||||
|
||||
2
lib/api/src/externals/mod.rs
vendored
2
lib/api/src/externals/mod.rs
vendored
@@ -1,4 +1,4 @@
|
||||
mod function;
|
||||
pub(crate) mod function;
|
||||
mod global;
|
||||
mod memory;
|
||||
mod table;
|
||||
|
||||
@@ -20,6 +20,7 @@ pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace
|
||||
pub use crate::instance::Instance;
|
||||
pub use crate::memory_view::{Atomically, MemoryView};
|
||||
pub use crate::module::Module;
|
||||
pub use crate::native::NativeFunc;
|
||||
pub use crate::ordered_resolver::OrderedResolver;
|
||||
pub use crate::ptr::{Array, Item, WasmPtr};
|
||||
pub use crate::store::{Store, StoreObject};
|
||||
|
||||
@@ -1,14 +1,49 @@
|
||||
// Native Funcs
|
||||
// use wasmer_runtime::ExportFunction;
|
||||
use std::marker::PhantomData;
|
||||
use wasm_common::{Func, WasmExternType, WasmTypeList};
|
||||
|
||||
use crate::exports::{ExportError, Exportable};
|
||||
use crate::externals::function::{FunctionDefinition, WasmFunctionDefinition};
|
||||
use crate::{Extern, Function, FunctionType, RuntimeError, Store};
|
||||
use wasm_common::{NativeWasmType, WasmExternType, WasmTypeList};
|
||||
use wasmer_runtime::{
|
||||
wasmer_call_trampoline, ExportFunction, VMContext, VMFunctionBody, VMFunctionKind, VMTrampoline,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UnprovidedArgs;
|
||||
#[derive(Clone)]
|
||||
pub struct UnprovidedRets;
|
||||
|
||||
/// This is just an empty trait to constrict that types that
|
||||
/// can be put into the third/fourth (depending if you include lifetimes)
|
||||
/// of the `NativeFunc` struct.
|
||||
pub trait Kind {}
|
||||
|
||||
/// TODO(lachlan): Naming TBD.
|
||||
/// This contains the trampoline and invoke functions for a specific signature,
|
||||
/// as well as the environment that the invoke function may or may not require.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Wasm {
|
||||
pub(crate) trampoline: VMTrampoline,
|
||||
//pub(crate) invoke: Invoke,
|
||||
//pub(crate) invoke_env: Option<NonNull<c_void>>,
|
||||
}
|
||||
|
||||
impl Kind for Wasm {}
|
||||
|
||||
/// This type, as part of the `NativeFunc` type signature, represents a function that is created
|
||||
/// by the host.
|
||||
pub struct Host(());
|
||||
|
||||
impl Kind for Host {}
|
||||
|
||||
pub struct NativeFunc<'a, Args = UnprovidedArgs, Rets = UnprovidedRets> {
|
||||
definition: FunctionDefinition,
|
||||
store: Store,
|
||||
address: *const VMFunctionBody,
|
||||
vmctx: *mut VMContext,
|
||||
arg_kind: VMFunctionKind,
|
||||
// exported: ExportFunction,
|
||||
_phantom: PhantomData<(&'a (), Args, Rets)>,
|
||||
}
|
||||
@@ -16,29 +51,69 @@ pub struct NativeFunc<'a, Args = UnprovidedArgs, Rets = UnprovidedRets> {
|
||||
unsafe impl<'a, Args, Rets> Send for NativeFunc<'a, Args, Rets> {}
|
||||
|
||||
impl<'a, Args, Rets> NativeFunc<'a, Args, Rets> {
|
||||
fn from_export() -> Self {
|
||||
pub(crate) fn new(
|
||||
store: Store,
|
||||
address: *const VMFunctionBody,
|
||||
vmctx: *mut VMContext,
|
||||
arg_kind: VMFunctionKind,
|
||||
definition: FunctionDefinition,
|
||||
) -> Self {
|
||||
Self {
|
||||
// exported,
|
||||
definition,
|
||||
store,
|
||||
address,
|
||||
vmctx,
|
||||
arg_kind,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*impl<'a, Args, Rets> From<NativeFunc<'a, Args, Rets>> for Func
|
||||
/*
|
||||
impl<'a, Args, Rets> Exportable for NativeFunc<'a, Args, Rets>
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn from(other: NativeFunc<'a, Args, Rets>) -> Func {
|
||||
Func {
|
||||
fn to_export(&self) -> Export {
|
||||
todo!("implement this")
|
||||
}
|
||||
|
||||
// Cannot be implemented because of the return type `&Self` TODO:
|
||||
fn get_self_from_extern(extern_: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
match extern_ {
|
||||
// TODO: review error return type in failure of `f.native()`
|
||||
Extern::Function(f) => f.native().ok_or_else(|| ExportError::IncompatibleType),
|
||||
_ => Err(ExportError::IncompatibleType),
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
*/
|
||||
|
||||
impl<'a, Args, Rets> From<NativeFunc<'a, Args, Rets>> for Function
|
||||
where
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
{
|
||||
fn from(other: NativeFunc<'a, Args, Rets>) -> Function {
|
||||
let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
|
||||
Function {
|
||||
store: other.store,
|
||||
definition: other.definition,
|
||||
owned_by_store: true, // todo
|
||||
exported: ExportFunction {
|
||||
address: other.address,
|
||||
vmctx: other.vmctx,
|
||||
signature,
|
||||
kind: other.arg_kind,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_native_traits {
|
||||
( $( $x:ident ),* ) => {
|
||||
#[allow(unused_parens)]
|
||||
#[allow(unused_parens, non_snake_case)]
|
||||
impl<'a $( , $x )*, Rets> NativeFunc<'a, ( $( $x, )* ), Rets>
|
||||
where
|
||||
$( $x: WasmExternType, )*
|
||||
@@ -46,7 +121,60 @@ macro_rules! impl_native_traits {
|
||||
{
|
||||
/// Call the typed func and return results.
|
||||
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, ()> {
|
||||
unimplemented!("");
|
||||
let mut rets = Rets::empty_array();
|
||||
|
||||
let params = [ $( $x.to_native().to_binary() ),* ];
|
||||
let mut values_vec: Vec<i128> = vec![0; std::cmp::max(params.len(), Rets::wasm_types().len())];
|
||||
|
||||
for (i, &arg) in params.iter().enumerate() {
|
||||
values_vec[i] = arg;
|
||||
}
|
||||
|
||||
match self.definition {
|
||||
FunctionDefinition::Wasm(WasmFunctionDefinition {
|
||||
trampoline
|
||||
}) => {
|
||||
if let Err(error) = unsafe {
|
||||
wasmer_call_trampoline(
|
||||
self.vmctx,
|
||||
trampoline,
|
||||
self.address,
|
||||
values_vec.as_mut_ptr() as *mut u8,
|
||||
)
|
||||
} {
|
||||
dbg!(error);
|
||||
return Err(());
|
||||
} else {
|
||||
let mut results = Rets::empty_array();
|
||||
let num_results = Rets::wasm_types().len();
|
||||
if num_results > 0 {
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(values_vec.as_ptr(),
|
||||
&mut results.as_mut()[0] as *mut i128,
|
||||
num_results);
|
||||
}
|
||||
}
|
||||
return Ok(Rets::from_array(results));
|
||||
}
|
||||
}
|
||||
FunctionDefinition::Host => {
|
||||
/*unsafe {
|
||||
let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Result<Rets, RuntimeError>>(self.address);
|
||||
match f( self.vmctx, $( $x, )* ) {
|
||||
Err(error) => {
|
||||
dbg!(error);
|
||||
return Err(());
|
||||
}
|
||||
Ok(results) => {
|
||||
return Ok(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
todo!("host functions not yet implemented")
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +183,10 @@ macro_rules! impl_native_traits {
|
||||
}
|
||||
|
||||
// impl_native_traits!();
|
||||
impl_native_traits!();
|
||||
impl_native_traits!(A1);
|
||||
impl_native_traits!(A1, A2);
|
||||
impl_native_traits!(A1, A2, A3);
|
||||
impl_native_traits!(A1, A2, A3, A4);
|
||||
|
||||
// impl_native_traits!(A1, A2, A3);
|
||||
|
||||
@@ -106,6 +106,8 @@ fn table_set() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: review, was this working before?
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn table_grow() -> Result<()> {
|
||||
let store = Store::default();
|
||||
@@ -330,3 +332,32 @@ fn function_new_dynamic_env() -> Result<()> {
|
||||
assert_eq!(function.ty().clone(), function_type);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn native_function_works() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let function = Function::new(&store, || {});
|
||||
let native_function: NativeFunc<(), ()> = function.native().unwrap();
|
||||
let result = native_function.call();
|
||||
dbg!(&result);
|
||||
assert!(result.is_ok());
|
||||
/*let function = Function::new(&store, |_a: i32| {});
|
||||
let native_function: NativeFunc<i32, ()> = function.native().unwrap();
|
||||
assert!(native_function.call(3).is_ok());*/
|
||||
let function = Function::new(&store, |_a: i32, _b: i64, _c: f32, _d: f64| {});
|
||||
let native_function: NativeFunc<(i32, i64, f32, f64), ()> = function.native().unwrap();
|
||||
assert!(native_function.call(3, 4, 1., 5.).is_ok());
|
||||
/*
|
||||
let function = Function::new(&store, || -> i32 { 1 });
|
||||
assert_eq!(
|
||||
function.ty().clone(),
|
||||
FunctionType::new(vec![], vec![Type::I32])
|
||||
);
|
||||
let function = Function::new(&store, || -> (i32, i64, f32, f64) { (1, 2, 3.0, 4.0) });
|
||||
assert_eq!(
|
||||
function.ty().clone(),
|
||||
FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64])
|
||||
);*/
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -156,3 +156,48 @@ fn exports() -> Result<()> {
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_function_works_for_wasm() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let wat = r#"(module
|
||||
(func $multiply (import "env" "multiply") (param i32 i32) (result i32))
|
||||
(func (export "add") (param i32 i32) (result i32)
|
||||
(i32.add (local.get 0)
|
||||
(local.get 1)))
|
||||
(func (export "double_then_add") (param i32 i32) (result i32)
|
||||
(i32.add (call $multiply (local.get 0) (i32.const 2))
|
||||
(call $multiply (local.get 1) (i32.const 2))))
|
||||
)"#;
|
||||
let module = Module::new(&store, wat)?;
|
||||
|
||||
let import_object = imports! {
|
||||
"env" => {
|
||||
"multiply" => Function::new(&store, |a: i32, b: i32| a * b),
|
||||
},
|
||||
};
|
||||
|
||||
let instance = Instance::new(&module, &import_object).unwrap();
|
||||
|
||||
// TODO:
|
||||
//let f: NativeFunc<(i32, i32), i32> = instance.exports.get("add").unwrap();
|
||||
let dyn_f: &Function = instance.exports.get("add").unwrap();
|
||||
let dyn_result = dyn_f.call(&[Val::I32(4), Val::I32(6)]).unwrap();
|
||||
assert_eq!(dyn_result[0], Val::I32(10));
|
||||
|
||||
let f: NativeFunc<(i32, i32), i32> = dyn_f.clone().native().unwrap();
|
||||
|
||||
let result = f.call(4, 6).unwrap();
|
||||
assert_eq!(result, 10);
|
||||
|
||||
let dyn_f: &Function = instance.exports.get("double_then_add").unwrap();
|
||||
let dyn_result = dyn_f.call(&[Val::I32(4), Val::I32(6)]).unwrap();
|
||||
assert_eq!(dyn_result[0], Val::I32(20));
|
||||
|
||||
let f: NativeFunc<(i32, i32), i32> = dyn_f.clone().native().unwrap();
|
||||
|
||||
let result = f.call(4, 6).unwrap();
|
||||
assert_eq!(result, 20);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -30,31 +30,102 @@ pub trait NativeWasmType {
|
||||
|
||||
#[doc(hidden)]
|
||||
fn into_abi(self) -> Self::Abi;
|
||||
|
||||
/// Convert self to i128 binary representation.
|
||||
fn to_binary(self) -> i128;
|
||||
|
||||
/// Convert to self from i128 binary representation.
|
||||
fn from_binary(binary: i128) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! wasm_native_type {
|
||||
($type:ty => $native_type:expr) => {
|
||||
impl NativeWasmType for $type {
|
||||
const WASM_TYPE: Type = $native_type;
|
||||
type Abi = Self;
|
||||
impl NativeWasmType for i32 {
|
||||
const WASM_TYPE: Type = Type::I32;
|
||||
type Abi = Self;
|
||||
|
||||
#[inline]
|
||||
fn from_abi(abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
#[inline]
|
||||
fn from_abi(abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
}
|
||||
};
|
||||
#[inline]
|
||||
fn into_abi(self) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_binary(self) -> i128 {
|
||||
self as _
|
||||
}
|
||||
|
||||
fn from_binary(bits: i128) -> Self {
|
||||
bits as _
|
||||
}
|
||||
}
|
||||
impl NativeWasmType for i64 {
|
||||
const WASM_TYPE: Type = Type::I64;
|
||||
type Abi = Self;
|
||||
|
||||
wasm_native_type!(i32 => Type::I32);
|
||||
wasm_native_type!(i64 => Type::I64);
|
||||
wasm_native_type!(f32 => Type::F32);
|
||||
wasm_native_type!(f64 => Type::F64);
|
||||
#[inline]
|
||||
fn from_abi(abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_binary(self) -> i128 {
|
||||
self as _
|
||||
}
|
||||
|
||||
fn from_binary(bits: i128) -> Self {
|
||||
bits as _
|
||||
}
|
||||
}
|
||||
impl NativeWasmType for f32 {
|
||||
const WASM_TYPE: Type = Type::F32;
|
||||
type Abi = Self;
|
||||
|
||||
#[inline]
|
||||
fn from_abi(abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_binary(self) -> i128 {
|
||||
self.to_bits() as _
|
||||
}
|
||||
|
||||
fn from_binary(bits: i128) -> Self {
|
||||
Self::from_bits(bits as _)
|
||||
}
|
||||
}
|
||||
impl NativeWasmType for f64 {
|
||||
const WASM_TYPE: Type = Type::F64;
|
||||
type Abi = Self;
|
||||
|
||||
#[inline]
|
||||
fn from_abi(abi: Self::Abi) -> Self {
|
||||
abi
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self) -> Self::Abi {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_binary(self) -> i128 {
|
||||
self.to_bits() as _
|
||||
}
|
||||
|
||||
fn from_binary(bits: i128) -> Self {
|
||||
Self::from_bits(bits as _)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_native_type {
|
||||
@@ -166,7 +237,7 @@ pub trait WasmTypeList {
|
||||
type CStruct;
|
||||
|
||||
/// Array of return values.
|
||||
type Array: AsMut<[u64]>;
|
||||
type Array: AsMut<[i128]>;
|
||||
|
||||
/// Construct `Self` based on an array of returned values.
|
||||
fn from_array(array: Self::Array) -> Self;
|
||||
@@ -299,7 +370,7 @@ where
|
||||
|
||||
impl WasmTypeList for Infallible {
|
||||
type CStruct = Self;
|
||||
type Array = [u64; 0];
|
||||
type Array = [i128; 0];
|
||||
|
||||
fn from_array(_: Self::Array) -> Self {
|
||||
unreachable!()
|
||||
@@ -351,20 +422,19 @@ macro_rules! impl_traits {
|
||||
{
|
||||
type CStruct = $struct_name<$( $x ),*>;
|
||||
|
||||
type Array = [u64; count_idents!( $( $x ),* )];
|
||||
type Array = [i128; count_idents!( $( $x ),* )];
|
||||
|
||||
fn from_array(_array: Self::Array) -> Self {
|
||||
unimplemented!("from array");
|
||||
// #[allow(non_snake_case)]
|
||||
// let [ $( $x ),* ] = array;
|
||||
fn from_array(array: Self::Array) -> Self {
|
||||
#[allow(non_snake_case)]
|
||||
let [ $( $x ),* ] = array;
|
||||
|
||||
// ( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
|
||||
( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* )
|
||||
}
|
||||
|
||||
fn into_array(self) -> Self::Array {
|
||||
unimplemented!("into array");
|
||||
// let ( $( $x ),* ) = self;
|
||||
// [ $( WasmExternType::to_native($x).to_binary() ),* ]
|
||||
#[allow(non_snake_case)]
|
||||
let ( $( $x ),* ) = self;
|
||||
[ $( WasmExternType::to_native($x).to_binary() ),* ]
|
||||
}
|
||||
|
||||
fn empty_array() -> Self::Array {
|
||||
|
||||
Reference in New Issue
Block a user