mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-07 05:08:19 +00:00
Added support for native calls to dynamic functions
This commit is contained in:
34
lib/api/src/externals/function.rs
vendored
34
lib/api/src/externals/function.rs
vendored
@@ -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 {
|
||||
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,
|
||||
) {
|
||||
|
||||
@@ -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(¶ms_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");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
)?;
|
||||
|
||||
Reference in New Issue
Block a user