Added support for native calls to dynamic functions

This commit is contained in:
Syrus
2020-06-12 20:50:24 -07:00
parent 21389f34ce
commit cd1138d520
10 changed files with 129 additions and 32 deletions

View File

@@ -9,7 +9,7 @@ use std::cmp::max;
use wasm_common::{HostFunction, WasmTypeList, WithEnv, WithoutEnv}; use wasm_common::{HostFunction, WasmTypeList, WithEnv, WithoutEnv};
use wasmer_runtime::{ use wasmer_runtime::{
wasmer_call_trampoline, Export, ExportFunction, VMCallerCheckedAnyfunc, VMContext, wasmer_call_trampoline, Export, ExportFunction, VMCallerCheckedAnyfunc, VMContext,
VMDynamicFunctionImportContext, VMFunctionBody, VMFunctionKind, VMTrampoline, VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind, VMTrampoline,
}; };
/// A function defined in the Wasm module /// A function defined in the Wasm module
@@ -35,6 +35,7 @@ pub struct Function {
pub(crate) definition: FunctionDefinition, pub(crate) definition: FunctionDefinition,
// If the Function is owned by the Store, not the instance // If the Function is owned by the Store, not the instance
pub(crate) owned_by_store: bool, pub(crate) owned_by_store: bool,
pub(crate) has_env: bool,
pub(crate) exported: ExportFunction, pub(crate) exported: ExportFunction,
} }
@@ -58,6 +59,7 @@ impl Function {
store: store.clone(), store: store.clone(),
owned_by_store: true, owned_by_store: true,
definition: FunctionDefinition::Host, definition: FunctionDefinition::Host,
has_env: false,
exported: ExportFunction { exported: ExportFunction {
address, address,
vmctx, vmctx,
@@ -72,17 +74,20 @@ impl Function {
where where
F: Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static, F: Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static,
{ {
let dynamic_ctx = let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithoutEnv {
VMDynamicFunctionImportContext::from_context(VMDynamicFunctionWithoutEnv {
func: Box::new(func), func: Box::new(func),
function_type: ty.clone(), function_type: ty.clone(),
}); });
// We don't yet have the address with the Wasm ABI signature.
// The engine linker will replace the address with one pointing to a
// generated dynamic trampoline.
let address = std::ptr::null() as *const VMFunctionBody; let address = std::ptr::null() as *const VMFunctionBody;
let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext; let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext;
Self { Self {
store: store.clone(), store: store.clone(),
owned_by_store: true, owned_by_store: true,
definition: FunctionDefinition::Host, definition: FunctionDefinition::Host,
has_env: false,
exported: ExportFunction { exported: ExportFunction {
address, address,
kind: VMFunctionKind::Dynamic, kind: VMFunctionKind::Dynamic,
@@ -98,17 +103,21 @@ impl Function {
F: Fn(&mut Env, &[Val]) -> Result<Vec<Val>, RuntimeError> + 'static, F: Fn(&mut Env, &[Val]) -> Result<Vec<Val>, RuntimeError> + 'static,
Env: Sized, Env: Sized,
{ {
let dynamic_ctx = VMDynamicFunctionImportContext::from_context(VMDynamicFunctionWithEnv { let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithEnv {
env, env,
func: Box::new(func), func: Box::new(func),
function_type: ty.clone(), function_type: ty.clone(),
}); });
// We don't yet have the address with the Wasm ABI signature.
// The engine linker will replace the address with one pointing to a
// generated dynamic trampoline.
let address = std::ptr::null() as *const VMFunctionBody; let address = std::ptr::null() as *const VMFunctionBody;
let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext; let vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext;
Self { Self {
store: store.clone(), store: store.clone(),
owned_by_store: true, owned_by_store: true,
definition: FunctionDefinition::Host, definition: FunctionDefinition::Host,
has_env: true,
exported: ExportFunction { exported: ExportFunction {
address, address,
kind: VMFunctionKind::Dynamic, kind: VMFunctionKind::Dynamic,
@@ -143,6 +152,7 @@ impl Function {
store: store.clone(), store: store.clone(),
owned_by_store: true, owned_by_store: true,
definition: FunctionDefinition::Host, definition: FunctionDefinition::Host,
has_env: true,
exported: ExportFunction { exported: ExportFunction {
address, address,
kind: VMFunctionKind::Static, kind: VMFunctionKind::Static,
@@ -269,6 +279,7 @@ impl Function {
Self { Self {
store: store.clone(), store: store.clone(),
owned_by_store: false, owned_by_store: false,
has_env: true,
definition: FunctionDefinition::Wasm(WasmFunctionDefinition { trampoline }), definition: FunctionDefinition::Wasm(WasmFunctionDefinition { trampoline }),
exported: wasmer_export, exported: wasmer_export,
} }
@@ -307,6 +318,7 @@ impl Function {
self.exported.vmctx, self.exported.vmctx,
self.exported.kind, self.exported.kind,
self.definition.clone(), self.definition.clone(),
self.has_env,
)) ))
} }
} }
@@ -330,12 +342,12 @@ impl std::fmt::Debug for Function {
} }
/// This trait is one that all dynamic functions must fulfill. /// This trait is one that all dynamic functions must fulfill.
trait VMDynamicFunction { pub(crate) trait VMDynamicFunction {
fn call(&self, args: &[Val]) -> Result<Vec<Val>, RuntimeError>; fn call(&self, args: &[Val]) -> Result<Vec<Val>, RuntimeError>;
fn function_type(&self) -> &FunctionType; fn function_type(&self) -> &FunctionType;
} }
struct VMDynamicFunctionWithoutEnv { pub(crate) struct VMDynamicFunctionWithoutEnv {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
func: Box<dyn Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static>, func: Box<dyn Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static>,
function_type: FunctionType, function_type: FunctionType,
@@ -350,7 +362,7 @@ impl VMDynamicFunction for VMDynamicFunctionWithoutEnv {
} }
} }
struct VMDynamicFunctionWithEnv<Env> pub(crate) struct VMDynamicFunctionWithEnv<Env>
where where
Env: Sized, Env: Sized,
{ {
@@ -372,13 +384,13 @@ where
} }
} }
trait VMDynamicFunctionImportCall<T: VMDynamicFunction> { trait VMDynamicFunctionCall<T: VMDynamicFunction> {
fn from_context(ctx: T) -> Self; fn from_context(ctx: T) -> Self;
fn address_ptr() -> *const VMFunctionBody; fn address_ptr() -> *const VMFunctionBody;
unsafe fn func_wrapper(&self, values_vec: *mut i128); unsafe fn func_wrapper(&self, values_vec: *mut i128);
} }
impl<T: VMDynamicFunction> VMDynamicFunctionImportCall<T> for VMDynamicFunctionImportContext<T> { impl<T: VMDynamicFunction> VMDynamicFunctionCall<T> for VMDynamicFunctionContext<T> {
fn from_context(ctx: T) -> Self { fn from_context(ctx: T) -> Self {
Self { Self {
address: Self::address_ptr(), address: Self::address_ptr(),
@@ -393,8 +405,8 @@ impl<T: VMDynamicFunction> VMDynamicFunctionImportCall<T> for VMDynamicFunctionI
// This function wraps our func, to make it compatible with the // This function wraps our func, to make it compatible with the
// reverse trampoline signature // reverse trampoline signature
unsafe fn func_wrapper( unsafe fn func_wrapper(
// Note: we use the trick that the first param to this function is the `VMDynamicFunctionImportContext` // Note: we use the trick that the first param to this function is the `VMDynamicFunctionContext`
// itself, so rather than doing `dynamic_ctx: &VMDynamicFunctionImportContext<T>`, we simplify it a bit // itself, so rather than doing `dynamic_ctx: &VMDynamicFunctionContext<T>`, we simplify it a bit
&self, &self,
values_vec: *mut i128, values_vec: *mut i128,
) { ) {

View File

@@ -9,11 +9,14 @@
//! ``` //! ```
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::externals::function::{FunctionDefinition, WasmFunctionDefinition}; use crate::externals::function::{
FunctionDefinition, VMDynamicFunction, VMDynamicFunctionWithoutEnv, WasmFunctionDefinition,
};
use crate::{Function, FunctionType, RuntimeError, Store}; use crate::{Function, FunctionType, RuntimeError, Store};
use wasm_common::{NativeWasmType, WasmExternType, WasmTypeList}; use wasm_common::{NativeWasmType, WasmExternType, WasmTypeList};
use wasmer_runtime::{ use wasmer_runtime::{
wasmer_call_trampoline, ExportFunction, VMContext, VMFunctionBody, VMFunctionKind, wasmer_call_trampoline, ExportFunction, VMContext, VMDynamicFunctionContext, VMFunctionBody,
VMFunctionKind,
}; };
pub struct NativeFunc<'a, Args = (), Rets = ()> { pub struct NativeFunc<'a, Args = (), Rets = ()> {
@@ -22,6 +25,7 @@ pub struct NativeFunc<'a, Args = (), Rets = ()> {
address: *const VMFunctionBody, address: *const VMFunctionBody,
vmctx: *mut VMContext, vmctx: *mut VMContext,
arg_kind: VMFunctionKind, arg_kind: VMFunctionKind,
has_env: bool,
// exported: ExportFunction, // exported: ExportFunction,
_phantom: PhantomData<(&'a (), Args, Rets)>, _phantom: PhantomData<(&'a (), Args, Rets)>,
} }
@@ -39,6 +43,7 @@ where
vmctx: *mut VMContext, vmctx: *mut VMContext,
arg_kind: VMFunctionKind, arg_kind: VMFunctionKind,
definition: FunctionDefinition, definition: FunctionDefinition,
has_env: bool,
) -> Self { ) -> Self {
Self { Self {
definition, definition,
@@ -46,6 +51,7 @@ where
address, address,
vmctx, vmctx,
arg_kind, arg_kind,
has_env,
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
@@ -78,6 +84,7 @@ where
store: other.store, store: other.store,
definition: other.definition, definition: other.definition,
owned_by_store: true, // todo owned_by_store: true, // todo
has_env: other.has_env,
exported: ExportFunction { exported: ExportFunction {
address: other.address, address: other.address,
vmctx: other.vmctx, vmctx: other.vmctx,
@@ -169,7 +176,21 @@ macro_rules! impl_native_traits {
return Ok(results); return Ok(results);
} }
} else { } else {
todo!("dynamic host functions not yet implemented") /// Is a dynamic function
if !self.has_env {
let ctx = self.vmctx as *mut VMDynamicFunctionContext<VMDynamicFunctionWithoutEnv>;
let params_list = [ $( $x.to_native().to_value() ),* ];
let results = unsafe { (*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));
}
}
return Ok(Rets::from_array(rets_list_array));
}
unimplemented!("native calls to dynamic function with env not yet implemented");
} }
}, },
} }

View File

@@ -47,7 +47,7 @@ pub use crate::sig_registry::SignatureRegistry;
pub use crate::table::{Table, TableElements, TablePlan, TableStyle}; pub use crate::table::{Table, TableElements, TablePlan, TableStyle};
pub use crate::trap::*; pub use crate::trap::*;
pub use crate::vmcontext::{ pub use crate::vmcontext::{
VMBuiltinFunctionIndex, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionImportContext, VMBuiltinFunctionIndex, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext,
VMFunctionBody, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, VMFunctionBody, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport,
VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport,
VMTrampoline, VMTrampoline,

View File

@@ -46,7 +46,7 @@ mod test_vmfunction_import {
} }
} }
/// The `VMDynamicFunctionImportContext` is the context that dynamic /// The `VMDynamicFunctionContext` is the context that dynamic
/// functions will receive when called (rather than `vmctx`). /// functions will receive when called (rather than `vmctx`).
/// A dynamic function is a function for which we don't know the signature /// A dynamic function is a function for which we don't know the signature
/// until runtime. /// until runtime.
@@ -55,7 +55,7 @@ mod test_vmfunction_import {
/// containing the relevant context for running the function indicated /// containing the relevant context for running the function indicated
/// in `address`. /// in `address`.
#[repr(C)] #[repr(C)]
pub struct VMDynamicFunctionImportContext<T: Sized> { pub struct VMDynamicFunctionContext<T: Sized> {
/// The address of the inner dynamic function. /// The address of the inner dynamic function.
/// ///
/// Note: The function must be on the form of /// Note: The function must be on the form of
@@ -68,7 +68,7 @@ pub struct VMDynamicFunctionImportContext<T: Sized> {
#[cfg(test)] #[cfg(test)]
mod test_vmdynamicfunction_import_context { mod test_vmdynamicfunction_import_context {
use super::VMDynamicFunctionImportContext; use super::VMDynamicFunctionContext;
use crate::{ModuleInfo, VMOffsets}; use crate::{ModuleInfo, VMOffsets};
use memoffset::offset_of; use memoffset::offset_of;
use std::mem::size_of; use std::mem::size_of;
@@ -78,15 +78,15 @@ mod test_vmdynamicfunction_import_context {
let module = ModuleInfo::new(); let module = ModuleInfo::new();
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
assert_eq!( assert_eq!(
size_of::<VMDynamicFunctionImportContext<usize>>(), size_of::<VMDynamicFunctionContext<usize>>(),
usize::from(offsets.size_of_vmdynamicfunction_import_context()) usize::from(offsets.size_of_vmdynamicfunction_import_context())
); );
assert_eq!( assert_eq!(
offset_of!(VMDynamicFunctionImportContext<usize>, address), offset_of!(VMDynamicFunctionContext<usize>, address),
usize::from(offsets.vmdynamicfunction_import_context_address()) usize::from(offsets.vmdynamicfunction_import_context_address())
); );
assert_eq!( assert_eq!(
offset_of!(VMDynamicFunctionImportContext<usize>, ctx), offset_of!(VMDynamicFunctionContext<usize>, ctx),
usize::from(offsets.vmdynamicfunction_import_context_ctx()) usize::from(offsets.vmdynamicfunction_import_context_ctx())
); );
} }

View File

@@ -110,9 +110,9 @@ impl VMOffsets {
} }
} }
/// Offsets for [`VMDynamicFunctionImportContext`]. /// Offsets for [`VMDynamicFunctionContext`].
/// ///
/// [`VMDynamicFunctionImportContext`]: crate::vmcontext::VMDynamicFunctionImportContext /// [`VMDynamicFunctionContext`]: crate::vmcontext::VMDynamicFunctionContext
impl VMOffsets { impl VMOffsets {
/// The offset of the `address` field. /// The offset of the `address` field.
#[allow(clippy::erasing_op)] #[allow(clippy::erasing_op)]
@@ -126,9 +126,9 @@ impl VMOffsets {
1 * self.pointer_size 1 * self.pointer_size
} }
/// Return the size of [`VMDynamicFunctionImportContext`]. /// Return the size of [`VMDynamicFunctionContext`].
/// ///
/// [`VMDynamicFunctionImportContext`]: crate::vmcontext::VMDynamicFunctionImportContext /// [`VMDynamicFunctionContext`]: crate::vmcontext::VMDynamicFunctionContext
pub fn size_of_vmdynamicfunction_import_context(&self) -> u8 { pub fn size_of_vmdynamicfunction_import_context(&self) -> u8 {
2 * self.pointer_size 2 * self.pointer_size
} }

View File

@@ -2,6 +2,7 @@
//! easily in Rust, thanks to it's advanced typing system. //! easily in Rust, thanks to it's advanced typing system.
use crate::types::{FunctionType, Type}; use crate::types::{FunctionType, Type};
use crate::values::Value;
use std::convert::Infallible; use std::convert::Infallible;
use std::marker::PhantomData; use std::marker::PhantomData;
@@ -34,6 +35,15 @@ pub trait NativeWasmType {
/// Convert self to i128 binary representation. /// Convert self to i128 binary representation.
fn to_binary(self) -> i128; fn to_binary(self) -> i128;
/// Convert self to a `Value`
fn to_value<T>(self) -> Value<T>
where
Self: std::marker::Sized,
{
let binary = self.to_binary();
unsafe { Value::read_value_from(&binary, Self::WASM_TYPE) }
}
/// Convert to self from i128 binary representation. /// Convert to self from i128 binary representation.
fn from_binary(binary: i128) -> Self; fn from_binary(binary: i128) -> Self;
} }

View File

@@ -199,6 +199,30 @@ impl<T> From<AnyRef> for Value<T> {
} }
} }
impl<T> Into<i32> for Value<T> {
fn into(self) -> i32 {
self.unwrap_i32()
}
}
impl<T> Into<i64> for Value<T> {
fn into(self) -> i64 {
self.unwrap_i64()
}
}
impl<T> Into<f32> for Value<T> {
fn into(self) -> f32 {
self.unwrap_f32()
}
}
impl<T> Into<f64> for Value<T> {
fn into(self) -> f64 {
self.unwrap_f64()
}
}
// impl<T> From<T> for Value<T> { // impl<T> From<T> for Value<T> {
// fn from(val: T) -> Self { // fn from(val: T) -> Self {
// Self::FuncRef(val) // Self::FuncRef(val)

View File

@@ -39,3 +39,32 @@ fn native_function_works_for_wasm() -> Result<()> {
assert_eq!(result, 20); assert_eq!(result, 20);
Ok(()) Ok(())
} }
fn dynamic_raw_call_no_env() -> anyhow::Result<()> {
let store = get_store();
let reverse_duplicate = wasmer::Function::new_dynamic(
&store,
&wasmer::FunctionType::new(
vec![
wasmer::ValType::I32,
wasmer::ValType::I64,
wasmer::ValType::F32,
wasmer::ValType::F64,
],
vec![wasmer::ValType::F64],
),
|values| {
Ok(vec![
Value::F64(values[3].unwrap_f64() * 2.0),
Value::F32(values[2].unwrap_f32() * 2.0),
Value::I64(values[2].unwrap_i64() * 2.0),
Value::I32(values[2].unwrap_i32() * 2.0),
])
},
);
let reverse_duplicate_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> =
reverse_duplicate.native().unwrap();
let result = reverse_duplicate_native.call(1, 3, 5.0, 7.0)?;
assert_eq!(result, (14.0, 10.0, 6, 2));
Ok(())
}

View File

@@ -67,11 +67,12 @@ macro_rules! mvr_test {
fn dynamic() -> anyhow::Result<()> { fn dynamic() -> anyhow::Result<()> {
let store = get_store(); let store = get_store();
let module = get_module(&store)?; let module = get_module(&store)?;
let callback_fn = wasmer::Function::new_dynamic(&store, &wasmer::FunctionType::new(vec![wasmer::ValType::I32], vec![ $( <$result_type>::expected_valtype() ),* ]), dynamic_callback_fn);
let instance = wasmer::Instance::new( let instance = wasmer::Instance::new(
&module, &module,
&wasmer::imports! { &wasmer::imports! {
"host" => { "host" => {
"callback_fn" => wasmer::Function::new_dynamic(&store, &wasmer::FunctionType::new(vec![wasmer::ValType::I32], vec![ $( <$result_type>::expected_valtype() ),* ]), dynamic_callback_fn) "callback_fn" => callback_fn
} }
} }
)?; )?;