Native function now works fully

This commit is contained in:
Syrus Akbary
2021-07-13 19:16:49 -07:00
parent e7670b58b6
commit 917667ad8c
6 changed files with 147 additions and 258 deletions

View File

@@ -1,7 +1,7 @@
use crate::export::Export;
use crate::externals::{Extern, Function /* , Global, Table */, Memory};
use crate::externals::{Extern, Function, Global, Memory, Table};
use crate::import_object::LikeNamespace;
// use crate::native::NativeFunc;
use crate::native::NativeFunc;
use crate::WasmTypeList;
use indexmap::IndexMap;
use std::fmt;
@@ -118,39 +118,39 @@ impl Exports {
}
}
// /// Get an export as a `Global`.
// pub fn get_global(&self, name: &str) -> Result<&Global, ExportError> {
// self.get(name)
// }
/// Get an export as a `Global`.
pub fn get_global(&self, name: &str) -> Result<&Global, ExportError> {
self.get(name)
}
/// Get an export as a `Memory`.
pub fn get_memory(&self, name: &str) -> Result<&Memory, ExportError> {
self.get(name)
}
// /// Get an export as a `Table`.
// pub fn get_table(&self, name: &str) -> Result<&Table, ExportError> {
// self.get(name)
// }
/// Get an export as a `Table`.
pub fn get_table(&self, name: &str) -> Result<&Table, ExportError> {
self.get(name)
}
/// Get an export as a `Func`.
pub fn get_function(&self, name: &str) -> Result<&Function, ExportError> {
self.get(name)
}
// /// Get an export as a `NativeFunc`.
// pub fn get_native_function<Args, Rets>(
// &self,
// name: &str,
// ) -> Result<NativeFunc<Args, Rets>, ExportError>
// where
// Args: WasmTypeList,
// Rets: WasmTypeList,
// {
// self.get_function(name)?
// .native()
// .map_err(|_| ExportError::IncompatibleType)
// }
/// Get an export as a `NativeFunc`.
pub fn get_native_function<Args, Rets>(
&self,
name: &str,
) -> Result<NativeFunc<Args, Rets>, ExportError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
self.get_function(name)?
.native()
.map_err(|_| ExportError::IncompatibleType)
}
/// Hack to get this working with nativefunc too
pub fn get_with_generics<'a, T, Args, Rets>(&'a self, name: &str) -> Result<T, ExportError>

View File

@@ -1,16 +1,16 @@
use crate::exports::{ExportError, Exportable};
use crate::externals::Extern;
use crate::store::Store;
use crate::types::{AsJs /* ValFuncRef */, Val};
use crate::{FunctionType, ValType};
use js_sys::{Array, Function as JSFunction};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
// use crate::NativeFunc;
use crate::types::{param_from_js, AsJs /* ValFuncRef */, Val};
use crate::NativeFunc;
use crate::RuntimeError;
use crate::WasmerEnv;
use crate::{FunctionType, ValType};
pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv};
use js_sys::{Array, Function as JSFunction};
use std::iter::FromIterator;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use crate::export::{Export, VMFunction};
use std::fmt;
@@ -18,17 +18,6 @@ use std::fmt;
#[repr(C)]
pub struct VMFunctionBody(u8);
#[inline]
fn param_from_js(ty: &ValType, js_val: &JsValue) -> Val {
match ty {
ValType::I32 => Val::I32(js_val.as_f64().unwrap() as _),
ValType::I64 => Val::I64(js_val.as_f64().unwrap() as _),
ValType::F32 => Val::F32(js_val.as_f64().unwrap() as _),
ValType::F64 => Val::F64(js_val.as_f64().unwrap()),
_ => unimplemented!("The type is not yet supported in the JS Function API"),
}
}
#[inline]
fn result_to_js(val: &Val) -> JsValue {
match val {
@@ -731,42 +720,41 @@ impl Function {
/// // This results in an error: `RuntimeError`
/// let sum_native = sum.native::<(i32, i32), i64>().unwrap();
/// ```
// pub fn native<Args, Rets>(&self) -> Result<NativeFunc<Args, Rets>, RuntimeError>
// where
// Args: WasmTypeList,
// Rets: WasmTypeList,
// {
// unimplemented!();
// // // type check
// // {
// // let expected = self.exported.vm_function.signature.params();
// // let given = Args::wasm_types();
pub fn native<Args, Rets>(&self) -> Result<NativeFunc<Args, Rets>, RuntimeError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
// type check
{
let expected = self.exported.ty.params();
let given = Args::wasm_types();
// // if expected != given {
// // return Err(RuntimeError::new(format!(
// // "given types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)",
// // given,
// // expected,
// // )));
// // }
// // }
if expected != given {
return Err(RuntimeError::from_str(&format!(
"given types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)",
given,
expected,
)));
}
}
// // {
// // let expected = self.exported.vm_function.signature.results();
// // let given = Rets::wasm_types();
{
let expected = self.exported.ty.results();
let given = Rets::wasm_types();
// // if expected != given {
// // // todo: error result types don't match
// // return Err(RuntimeError::new(format!(
// // "given types (`{:?}`) for the function results don't match the actual types (`{:?}`)",
// // given,
// // expected,
// // )));
// // }
// // }
if expected != given {
// todo: error result types don't match
return Err(RuntimeError::from_str(&format!(
"given types (`{:?}`) for the function results don't match the actual types (`{:?}`)",
given,
expected,
)));
}
}
// // Ok(NativeFunc::new(self.store.clone(), self.exported.clone()))
// }
Ok(NativeFunc::new(self.store.clone(), self.exported.clone()))
}
#[track_caller]
fn closures_unsupported_panic() -> ! {
@@ -1058,6 +1046,9 @@ mod inner {
/// Note that all values are stored in their binary form.
type Array: AsMut<[i128]>;
/// The size of the array
fn size() -> u32;
/// Constructs `Self` based on an array of values.
fn from_array(array: Self::Array) -> Self;
@@ -1279,6 +1270,10 @@ mod inner {
type Array = [i128; count_idents!( $( $x ),* )];
fn size() -> u32 {
count_idents!( $( $x ),* ) as _
}
fn from_array(array: Self::Array) -> Self {
// Unpack items of the array.
#[allow(non_snake_case)]
@@ -1487,6 +1482,10 @@ mod inner {
type CStruct = Self;
type Array = [i128; 0];
fn size() -> u32 {
0
}
fn from_array(_: Self::Array) -> Self {
unreachable!()
}

View File

@@ -278,9 +278,6 @@ mod lib {
}
}
// #[cfg(test)]
// wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
mod cell;
mod env;
mod error;
@@ -293,13 +290,13 @@ mod iterators;
mod module;
#[cfg(feature = "wasm-types-polyfill")]
mod module_info_polyfill;
mod resolver;
mod wasm_bindgen_polyfill;
// mod native;
mod native;
mod ptr;
mod resolver;
mod store;
mod types;
mod utils;
mod wasm_bindgen_polyfill;
/// Implement [`WasmerEnv`] for your type with `#[derive(WasmerEnv)]`.
///
@@ -316,10 +313,10 @@ pub use crate::externals::{
pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace};
pub use crate::instance::{Instance, InstantiationError};
pub use crate::module::{Module, ModuleTypeHints};
pub use wasm_bindgen::JsValue as RuntimeError;
// pub use crate::native::NativeFunc;
pub use crate::native::NativeFunc;
pub use crate::ptr::{Array, Item, WasmPtr};
pub use crate::resolver::{ChainableNamedResolver, NamedResolver, NamedResolverChain, Resolver};
pub use wasm_bindgen::JsValue as RuntimeError;
pub use crate::store::{Store, StoreObject};
pub use crate::types::{
@@ -342,6 +339,9 @@ pub use wat::parse_bytes as wat2wasm;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
// #[cfg(test)]
// wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
// use wasm_bindgen::prelude::*;
// #[wasm_bindgen]

View File

@@ -9,18 +9,21 @@
//! ```
use std::marker::PhantomData;
use crate::externals::function::{DynamicFunction, VMDynamicFunction};
use crate::{FromToNativeWasmType, Function, RuntimeError, Store, WasmTypeList};
use std::panic::{catch_unwind, AssertUnwindSafe};
use wasmer_engine::ExportFunction;
// use std::panic::{catch_unwind, AssertUnwindSafe};
use crate::export::VMFunction;
use crate::types::{param_from_js, AsJs};
use js_sys::Array;
use std::iter::FromIterator;
use wasm_bindgen::JsValue;
use wasmer_types::NativeWasmType;
use wasmer_vm::{VMDynamicFunctionContext, VMFunctionBody, VMFunctionEnvironment, VMFunctionKind};
/// A WebAssembly function that can be called natively
/// (using the Native ABI).
#[derive(Clone)]
pub struct NativeFunc<Args = (), Rets = ()> {
store: Store,
exported: ExportFunction,
exported: VMFunction,
_phantom: PhantomData<(Args, Rets)>,
}
@@ -31,76 +34,16 @@ where
Args: WasmTypeList,
Rets: WasmTypeList,
{
pub(crate) fn new(store: Store, exported: ExportFunction) -> Self {
pub(crate) fn new(store: Store, exported: VMFunction) -> Self {
Self {
store,
exported,
_phantom: PhantomData,
}
}
pub(crate) fn is_host(&self) -> bool {
self.exported.vm_function.instance_ref.is_none()
}
pub(crate) fn vmctx(&self) -> VMFunctionEnvironment {
self.exported.vm_function.vmctx
}
pub(crate) fn address(&self) -> *const VMFunctionBody {
self.exported.vm_function.address
}
pub(crate) fn arg_kind(&self) -> VMFunctionKind {
self.exported.vm_function.kind
}
/// Get access to the backing VM value for this extern. This function is for
/// tests it should not be called by users of the Wasmer API.
///
/// # Safety
/// This function is unsafe to call outside of tests for the wasmer crate
/// because there is no stability guarantee for the returned type and we may
/// make breaking changes to it at any time or remove this method.
#[doc(hidden)]
pub unsafe fn get_vm_function(&self) -> &wasmer_vm::VMFunction {
&self.exported.vm_function
}
}
/*
impl<Args, Rets> From<&NativeFunc<Args, Rets>> for VMFunction
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
fn from(other: &NativeFunc<Args, Rets>) -> Self {
let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
Self {
address: other.address,
vmctx: other.vmctx,
signature,
kind: other.arg_kind,
call_trampoline: None,
instance_ref: None,
}
}
}*/
impl<Args: WasmTypeList, Rets: WasmTypeList> Clone for NativeFunc<Args, Rets> {
fn clone(&self) -> Self {
let mut exported = self.exported.clone();
exported.vm_function.upgrade_instance_ref().unwrap();
Self {
store: self.store.clone(),
exported,
_phantom: PhantomData,
}
}
}
impl<Args, Rets> From<&NativeFunc<Args, Rets>> for ExportFunction
where
Args: WasmTypeList,
Rets: WasmTypeList,
@@ -133,91 +76,33 @@ macro_rules! impl_native_traits {
{
/// Call the typed func and return results.
pub fn call(&self, $( $x: $x, )* ) -> Result<Rets, RuntimeError> {
if !self.is_host() {
// We assume the trampoline is always going to be present for
// Wasm functions
let trampoline = self.exported.vm_function.call_trampoline.expect("Call trampoline not found in wasm function");
// TODO: when `const fn` related features mature more, we can declare a single array
// of the correct size here.
let mut params_list = [ $( $x.to_native().to_binary() ),* ];
let mut rets_list_array = Rets::empty_array();
let rets_list = rets_list_array.as_mut();
let using_rets_array;
let args_rets: &mut [i128] = if params_list.len() > rets_list.len() {
using_rets_array = false;
params_list.as_mut()
} else {
using_rets_array = true;
for (i, &arg) in params_list.iter().enumerate() {
rets_list[i] = arg;
}
rets_list.as_mut()
};
unsafe {
wasmer_vm::wasmer_call_trampoline(
&self.store,
self.vmctx(),
trampoline,
self.address(),
args_rets.as_mut_ptr() as *mut u8,
)
}?;
let num_rets = rets_list.len();
if !using_rets_array && num_rets > 0 {
let src_pointer = params_list.as_ptr();
let rets_list = &mut rets_list_array.as_mut()[0] as *mut i128;
unsafe {
// TODO: we can probably remove this copy by doing some clever `transmute`s.
// we know it's not overlapping because `using_rets_array` is false
std::ptr::copy_nonoverlapping(src_pointer,
rets_list,
num_rets);
}
let params_list: Vec<JsValue> = vec![ $( JsValue::from_f64($x.to_native().to_binary() as f64) ),* ];
let results = self.exported.function.apply(
&JsValue::UNDEFINED,
&Array::from_iter(params_list.iter())
).unwrap();
let mut rets_list_array = Rets::empty_array();
let mut_rets = rets_list_array.as_mut() as *mut [i128] as *mut i128;
match Rets::size() {
0 => {},
1 => unsafe {
let ty = Rets::wasm_types()[0];
let val = param_from_js(&ty, &results);
val.write_value_to(mut_rets);
}
Ok(Rets::from_array(rets_list_array))
// TODO: When the Host ABI and Wasm ABI are the same, we could do this instead:
// but we can't currently detect whether that's safe.
//
// let results = unsafe {
// wasmer_vm::catch_traps_with_result(self.vmctx, || {
// let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address());
// // We always pass the vmctx
// f( self.vmctx, $( $x, )* )
// }).map_err(RuntimeError::from_trap)?
// };
// Ok(Rets::from_c_struct(results))
}
else {
match self.arg_kind() {
VMFunctionKind::Static => {
let results = catch_unwind(AssertUnwindSafe(|| unsafe {
let f = std::mem::transmute::<_, unsafe extern "C" fn( VMFunctionEnvironment, $( $x, )*) -> Rets::CStruct>(self.address());
// We always pass the vmctx
f( self.vmctx(), $( $x, )* )
})).map_err(|e| RuntimeError::new(format!("{:?}", e)))?;
Ok(Rets::from_c_struct(results))
},
VMFunctionKind::Dynamic => {
let params_list = [ $( $x.to_native().to_value() ),* ];
let results = {
type VMContextWithEnv = VMDynamicFunctionContext<DynamicFunction<std::ffi::c_void>>;
unsafe {
let ctx = self.vmctx().host_env as *mut VMContextWithEnv;
(*ctx).ctx.call(&params_list)?
}
};
let mut rets_list_array = Rets::empty_array();
let mut_rets = rets_list_array.as_mut() as *mut [i128] as *mut i128;
for (i, ret) in results.iter().enumerate() {
unsafe {
ret.write_value_to(mut_rets.add(i));
}
n => {
let results: Array = results.into();
for (i, ret_type) in Rets::wasm_types().iter().enumerate() {
let ret = results.get(i as u32);
unsafe {
let val = param_from_js(&ret_type, &ret);
let p = mut_rets.add(i);
val.write_value_to(mut_rets.add(i));
}
Ok(Rets::from_array(rets_list_array))
}
}
}
Ok(Rets::from_array(rets_list_array))
}
}
@@ -232,10 +117,6 @@ macro_rules! impl_native_traits {
use crate::exports::Exportable;
crate::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::exports::ExportError::IncompatibleType)
}
fn into_weak_instance_ref(&mut self) {
self.exported.vm_function.instance_ref.as_mut().map(|v| *v = v.downgrade());
}
}
};
}

View File

@@ -22,6 +22,17 @@ pub trait AsJs {
fn as_jsvalue(&self) -> JsValue;
}
#[inline]
pub fn param_from_js(ty: &ValType, js_val: &JsValue) -> Val {
match ty {
ValType::I32 => Val::I32(js_val.as_f64().unwrap() as _),
ValType::I64 => Val::I64(js_val.as_f64().unwrap() as _),
ValType::F32 => Val::F32(js_val.as_f64().unwrap() as _),
ValType::F64 => Val::F64(js_val.as_f64().unwrap()),
_ => unimplemented!("The type is not yet supported in the JS Function API"),
}
}
impl AsJs for Val {
fn as_jsvalue(&self) -> JsValue {
match self {