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 wasmer_runtime::{
wasmer_call_trampoline, Export, ExportFunction, VMCallerCheckedAnyfunc, VMContext,
VMDynamicFunctionImportContext, VMFunctionBody, VMFunctionKind, VMTrampoline,
VMDynamicFunctionContext, VMFunctionBody, VMFunctionKind, VMTrampoline,
};
/// A function defined in the Wasm module
@@ -35,6 +35,7 @@ pub struct Function {
pub(crate) definition: FunctionDefinition,
// If the Function is owned by the Store, not the instance
pub(crate) owned_by_store: bool,
pub(crate) has_env: bool,
pub(crate) exported: ExportFunction,
}
@@ -58,6 +59,7 @@ impl Function {
store: store.clone(),
owned_by_store: true,
definition: FunctionDefinition::Host,
has_env: false,
exported: ExportFunction {
address,
vmctx,
@@ -72,17 +74,20 @@ impl Function {
where
F: Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static,
{
let dynamic_ctx =
VMDynamicFunctionImportContext::from_context(VMDynamicFunctionWithoutEnv {
func: Box::new(func),
function_type: ty.clone(),
});
let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithoutEnv {
func: Box::new(func),
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 vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext;
Self {
store: store.clone(),
owned_by_store: true,
definition: FunctionDefinition::Host,
has_env: false,
exported: ExportFunction {
address,
kind: VMFunctionKind::Dynamic,
@@ -98,17 +103,21 @@ impl Function {
F: Fn(&mut Env, &[Val]) -> Result<Vec<Val>, RuntimeError> + 'static,
Env: Sized,
{
let dynamic_ctx = VMDynamicFunctionImportContext::from_context(VMDynamicFunctionWithEnv {
let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithEnv {
env,
func: Box::new(func),
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 vmctx = Box::into_raw(Box::new(dynamic_ctx)) as *mut VMContext;
Self {
store: store.clone(),
owned_by_store: true,
definition: FunctionDefinition::Host,
has_env: true,
exported: ExportFunction {
address,
kind: VMFunctionKind::Dynamic,
@@ -143,6 +152,7 @@ impl Function {
store: store.clone(),
owned_by_store: true,
definition: FunctionDefinition::Host,
has_env: true,
exported: ExportFunction {
address,
kind: VMFunctionKind::Static,
@@ -269,6 +279,7 @@ impl Function {
Self {
store: store.clone(),
owned_by_store: false,
has_env: true,
definition: FunctionDefinition::Wasm(WasmFunctionDefinition { trampoline }),
exported: wasmer_export,
}
@@ -307,6 +318,7 @@ impl Function {
self.exported.vmctx,
self.exported.kind,
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.
trait VMDynamicFunction {
pub(crate) trait VMDynamicFunction {
fn call(&self, args: &[Val]) -> Result<Vec<Val>, RuntimeError>;
fn function_type(&self) -> &FunctionType;
}
struct VMDynamicFunctionWithoutEnv {
pub(crate) struct VMDynamicFunctionWithoutEnv {
#[allow(clippy::type_complexity)]
func: Box<dyn Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static>,
function_type: FunctionType,
@@ -350,7 +362,7 @@ impl VMDynamicFunction for VMDynamicFunctionWithoutEnv {
}
}
struct VMDynamicFunctionWithEnv<Env>
pub(crate) struct VMDynamicFunctionWithEnv<Env>
where
Env: Sized,
{
@@ -372,13 +384,13 @@ where
}
}
trait VMDynamicFunctionImportCall<T: VMDynamicFunction> {
trait VMDynamicFunctionCall<T: VMDynamicFunction> {
fn from_context(ctx: T) -> Self;
fn address_ptr() -> *const VMFunctionBody;
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 {
Self {
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
// reverse trampoline signature
unsafe fn func_wrapper(
// Note: we use the trick that the first param to this function is the `VMDynamicFunctionImportContext`
// itself, so rather than doing `dynamic_ctx: &VMDynamicFunctionImportContext<T>`, we simplify it a bit
// Note: we use the trick that the first param to this function is the `VMDynamicFunctionContext`
// itself, so rather than doing `dynamic_ctx: &VMDynamicFunctionContext<T>`, we simplify it a bit
&self,
values_vec: *mut i128,
) {

View File

@@ -9,11 +9,14 @@
//! ```
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 wasm_common::{NativeWasmType, WasmExternType, WasmTypeList};
use wasmer_runtime::{
wasmer_call_trampoline, ExportFunction, VMContext, VMFunctionBody, VMFunctionKind,
wasmer_call_trampoline, ExportFunction, VMContext, VMDynamicFunctionContext, VMFunctionBody,
VMFunctionKind,
};
pub struct NativeFunc<'a, Args = (), Rets = ()> {
@@ -22,6 +25,7 @@ pub struct NativeFunc<'a, Args = (), Rets = ()> {
address: *const VMFunctionBody,
vmctx: *mut VMContext,
arg_kind: VMFunctionKind,
has_env: bool,
// exported: ExportFunction,
_phantom: PhantomData<(&'a (), Args, Rets)>,
}
@@ -39,6 +43,7 @@ where
vmctx: *mut VMContext,
arg_kind: VMFunctionKind,
definition: FunctionDefinition,
has_env: bool,
) -> Self {
Self {
definition,
@@ -46,6 +51,7 @@ where
address,
vmctx,
arg_kind,
has_env,
_phantom: PhantomData,
}
}
@@ -78,6 +84,7 @@ where
store: other.store,
definition: other.definition,
owned_by_store: true, // todo
has_env: other.has_env,
exported: ExportFunction {
address: other.address,
vmctx: other.vmctx,
@@ -169,7 +176,21 @@ macro_rules! impl_native_traits {
return Ok(results);
}
} 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::trap::*;
pub use crate::vmcontext::{
VMBuiltinFunctionIndex, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionImportContext,
VMBuiltinFunctionIndex, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext,
VMFunctionBody, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport,
VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport,
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`).
/// A dynamic function is a function for which we don't know the signature
/// until runtime.
@@ -55,7 +55,7 @@ mod test_vmfunction_import {
/// containing the relevant context for running the function indicated
/// in `address`.
#[repr(C)]
pub struct VMDynamicFunctionImportContext<T: Sized> {
pub struct VMDynamicFunctionContext<T: Sized> {
/// The address of the inner dynamic function.
///
/// Note: The function must be on the form of
@@ -68,7 +68,7 @@ pub struct VMDynamicFunctionImportContext<T: Sized> {
#[cfg(test)]
mod test_vmdynamicfunction_import_context {
use super::VMDynamicFunctionImportContext;
use super::VMDynamicFunctionContext;
use crate::{ModuleInfo, VMOffsets};
use memoffset::offset_of;
use std::mem::size_of;
@@ -78,15 +78,15 @@ mod test_vmdynamicfunction_import_context {
let module = ModuleInfo::new();
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
assert_eq!(
size_of::<VMDynamicFunctionImportContext<usize>>(),
size_of::<VMDynamicFunctionContext<usize>>(),
usize::from(offsets.size_of_vmdynamicfunction_import_context())
);
assert_eq!(
offset_of!(VMDynamicFunctionImportContext<usize>, address),
offset_of!(VMDynamicFunctionContext<usize>, address),
usize::from(offsets.vmdynamicfunction_import_context_address())
);
assert_eq!(
offset_of!(VMDynamicFunctionImportContext<usize>, ctx),
offset_of!(VMDynamicFunctionContext<usize>, 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 {
/// The offset of the `address` field.
#[allow(clippy::erasing_op)]
@@ -126,9 +126,9 @@ impl VMOffsets {
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 {
2 * self.pointer_size
}

View File

@@ -2,6 +2,7 @@
//! easily in Rust, thanks to it's advanced typing system.
use crate::types::{FunctionType, Type};
use crate::values::Value;
use std::convert::Infallible;
use std::marker::PhantomData;
@@ -34,6 +35,15 @@ pub trait NativeWasmType {
/// Convert self to i128 binary representation.
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.
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> {
// fn from(val: T) -> Self {
// Self::FuncRef(val)

View File

@@ -39,3 +39,32 @@ fn native_function_works_for_wasm() -> Result<()> {
assert_eq!(result, 20);
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<()> {
let store = get_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(
&module,
&wasmer::imports! {
"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
}
}
)?;