Skip to content

Commit

Permalink
No longer require FunctionEnvMut for new_typed(…) callback function
Browse files Browse the repository at this point in the history
  • Loading branch information
silwol committed Aug 4, 2022
1 parent fa9bc56 commit 70d279e
Show file tree
Hide file tree
Showing 19 changed files with 379 additions and 214 deletions.
4 changes: 2 additions & 2 deletions examples/early_exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use anyhow::bail;
use std::fmt;
use wasmer::{imports, wat2wasm, Function, FunctionEnvMut, Instance, Module, Store, TypedFunction};
use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, TypedFunction};
use wasmer_compiler_cranelift::Cranelift;

// First we need to create an error type that we'll use to signal the end of execution.
Expand Down Expand Up @@ -61,7 +61,7 @@ fn main() -> anyhow::Result<()> {
let module = Module::new(&store, wasm_bytes)?;

// We declare the host function that we'll use to terminate execution.
fn early_exit(_env: FunctionEnvMut<()>) -> Result<(), ExitCode> {
fn early_exit() -> Result<(), ExitCode> {
// This is where it happens.
Err(ExitCode(1))
}
Expand Down
4 changes: 2 additions & 2 deletions examples/hello_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//! cargo run --example hello-world --release --features "cranelift"
//! ```
use wasmer::{imports, wat2wasm, Function, FunctionEnvMut, Instance, Module, Store, TypedFunction};
use wasmer::{imports, wat2wasm, Function, Instance, Module, Store, TypedFunction};
use wasmer_compiler_cranelift::Cranelift;

fn main() -> anyhow::Result<()> {
Expand Down Expand Up @@ -51,7 +51,7 @@ fn main() -> anyhow::Result<()> {

// We define a function to act as our "env" "say_hello" function imported in the
// Wasm program above.
fn say_hello_world(_env: FunctionEnvMut<'_, ()>) {
fn say_hello_world() {
println!("Hello, world!")
}

Expand Down
5 changes: 2 additions & 3 deletions examples/table.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use wasmer::{
imports, wat2wasm, Function, FunctionEnvMut, Instance, Module, Store, TableType, Type,
TypedFunction, Value,
imports, wat2wasm, Function, Instance, Module, Store, TableType, Type, TypedFunction, Value,
};
use wasmer_compiler_cranelift::Cranelift;

/// A function we'll call through a table.
fn host_callback(_env: FunctionEnvMut<()>, arg1: i32, arg2: i32) -> i32 {
fn host_callback(arg1: i32, arg2: i32) -> i32 {
arg1 + arg2
}

Expand Down
2 changes: 1 addition & 1 deletion lib/api/src/js/exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ impl Exports {
Rets: WasmTypeList,
{
self.get_function(name)?
.native(store)
.typed(store)
.map_err(|_| ExportError::IncompatibleType)
}

Expand Down
133 changes: 121 additions & 12 deletions lib/api/src/js/externals/function.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub use self::inner::{FromToNativeWasmType, HostFunction, WasmTypeList};
pub use self::inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv};
use crate::js::exports::{ExportError, Exportable};
use crate::js::externals::Extern;
use crate::js::function_env::FunctionEnvMut;
Expand Down Expand Up @@ -65,7 +65,6 @@ impl Function {
///
/// If you know the signature of the host function at compile time,
/// consider using [`Function::new_typed`] for less runtime overhead.
#[cfg(feature = "compiler")]
pub fn new<FT, F>(store: &mut impl AsStoreMut, ty: FT, func: F) -> Self
where
FT: Into<FunctionType>,
Expand Down Expand Up @@ -133,7 +132,7 @@ impl Function {
let wrapped_func: JsValue = match function_type.results().len() {
0 => Closure::wrap(Box::new(move |args: &Array| {
let mut store: StoreMut = unsafe { StoreMut::from_raw(raw_store as _) };
let mut env: FunctionEnvMut<T> = raw_env.clone().into_mut(&mut store);
let env: FunctionEnvMut<T> = raw_env.clone().into_mut(&mut store);
let wasm_arguments = function_type
.params()
.iter()
Expand All @@ -147,7 +146,7 @@ impl Function {
.into_js_value(),
1 => Closure::wrap(Box::new(move |args: &Array| {
let mut store: StoreMut = unsafe { StoreMut::from_raw(raw_store as _) };
let mut env: FunctionEnvMut<T> = raw_env.clone().into_mut(&mut store);
let env: FunctionEnvMut<T> = raw_env.clone().into_mut(&mut store);
let wasm_arguments = function_type
.params()
.iter()
Expand All @@ -161,7 +160,7 @@ impl Function {
.into_js_value(),
_n => Closure::wrap(Box::new(move |args: &Array| {
let mut store: StoreMut = unsafe { StoreMut::from_raw(raw_store as _) };
let mut env: FunctionEnvMut<T> = raw_env.clone().into_mut(&mut store);
let env: FunctionEnvMut<T> = raw_env.clone().into_mut(&mut store);
let wasm_arguments = function_type
.params()
.iter()
Expand All @@ -182,6 +181,49 @@ impl Function {
Self::from_vm_export(&mut store, vm_function)
}

#[deprecated(
since = "3.0.0",
note = "new_native() has been renamed to new_typed()."
)]
/// Creates a new host `Function` from a native function.
pub fn new_native<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
where
F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync,
Args: WasmTypeList,
Rets: WasmTypeList,
{
Self::new_typed(store, func)
}

/// Creates a new host `Function` from a native function.
pub fn new_typed<F, Args, Rets>(store: &mut impl AsStoreMut, func: F) -> Self
where
F: HostFunction<(), Args, Rets, WithoutEnv> + 'static + Send + Sync,
Args: WasmTypeList,
Rets: WasmTypeList,
{
let mut store = store.as_store_mut();
if std::mem::size_of::<F>() != 0 {
Self::closures_unsupported_panic();
}
let function = inner::Function::<Args, Rets>::new(func);
let address = function.address() as usize as u32;

let ft = wasm_bindgen::function_table();
let as_table = ft.unchecked_ref::<js_sys::WebAssembly::Table>();
let func = as_table.get(address).unwrap();

let binded_func = func.bind1(
&JsValue::UNDEFINED,
&JsValue::from_f64(store.as_raw() as *mut u8 as usize as f64),
);
let ty = function.ty();
let vm_function = VMFunction::new(binded_func, ty);
Self {
handle: StoreHandle::new(store.objects_mut(), vm_function),
}
}

#[deprecated(
since = "3.0.0",
note = "new_native_with_env() has been renamed to new_typed_with_env()."
Expand All @@ -193,7 +235,7 @@ impl Function {
func: F,
) -> Self
where
F: HostFunction<T, Args, Rets>,
F: HostFunction<T, Args, Rets, WithEnv>,
Args: WasmTypeList,
Rets: WasmTypeList,
{
Expand Down Expand Up @@ -223,7 +265,7 @@ impl Function {
func: F,
) -> Self
where
F: HostFunction<T, Args, Rets>,
F: HostFunction<T, Args, Rets, WithEnv>,
Args: WasmTypeList,
Rets: WasmTypeList,
{
Expand Down Expand Up @@ -840,17 +882,39 @@ mod inner {
/// can be used as host function. To uphold this statement, it is
/// necessary for a function to be transformed into a pointer to
/// `VMFunctionBody`.
pub trait HostFunction<T, Args, Rets>
pub trait HostFunction<T, Args, Rets, Kind>
where
Args: WasmTypeList,
Rets: WasmTypeList,
Kind: HostFunctionKind,
T: Sized,
Self: Sized,
{
/// Get the pointer to the function body.
fn function_body_ptr(self) -> *const VMFunctionBody;
}

/// Empty trait to specify the kind of `HostFunction`: With or
/// without an environment.
///
/// This trait is never aimed to be used by a user. It is used by
/// the trait system to automatically generate the appropriate
/// host functions.
#[doc(hidden)]
pub trait HostFunctionKind {}

/// An empty struct to help Rust typing to determine
/// when a `HostFunction` does have an environment.
pub struct WithEnv;

impl HostFunctionKind for WithEnv {}

/// An empty struct to help Rust typing to determine
/// when a `HostFunction` does not have an environment.
pub struct WithoutEnv;

impl HostFunctionKind for WithoutEnv {}

/// Represents a low-level Wasm static host function. See
/// `super::Function::new` and `super::Function::new_env` to learn
/// more.
Expand All @@ -869,9 +933,9 @@ mod inner {
{
/// Creates a new `Function`.
#[allow(dead_code)]
pub fn new<F, T>(function: F) -> Self
pub fn new<F, T, Kind: HostFunctionKind>(function: F) -> Self
where
F: HostFunction<T, Args, Rets>,
F: HostFunction<T, Args, Rets, Kind>,
T: Sized,
{
Self {
Expand Down Expand Up @@ -1013,10 +1077,11 @@ mod inner {
}
}

// Implement `HostFunction` for a function that has the same arity than the tuple.
// Implement `HostFunction` for a function with a [`FunctionEnvMut`] that has the same
// arity than the tuple.
#[allow(unused_parens)]
impl< $( $x, )* Rets, RetsAsResult, T, Func >
HostFunction<T, ( $( $x ),* ), Rets>
HostFunction<T, ( $( $x ),* ), Rets, WithEnv>
for
Func
where
Expand Down Expand Up @@ -1061,6 +1126,50 @@ mod inner {
func_wrapper::< T, $( $x, )* Rets, RetsAsResult, Self > as *const VMFunctionBody
}
}

// Implement `HostFunction` for a function that has the same arity than the tuple.
#[allow(unused_parens)]
impl< $( $x, )* Rets, RetsAsResult, Func >
HostFunction<(), ( $( $x ),* ), Rets, WithoutEnv>
for
Func
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Func: Fn($( $x , )*) -> RetsAsResult + 'static,
{
#[allow(non_snake_case)]
fn function_body_ptr(self) -> *const VMFunctionBody {
/// This is a function that wraps the real host
/// function. Its address will be used inside the
/// runtime.
unsafe extern "C" fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( store_ptr: usize, $( $x: <$x::Native as NativeWasmType>::Abi, )* ) -> Rets::CStruct
where
$( $x: FromToNativeWasmType, )*
Rets: WasmTypeList,
RetsAsResult: IntoResult<Rets>,
Func: Fn($( $x , )*) -> RetsAsResult + 'static,
{
// let env: &Env = unsafe { &*(ptr as *const u8 as *const Env) };
let func: &Func = &*(&() as *const () as *const Func);
let mut store = StoreMut::from_raw(store_ptr as *mut _);

let result = panic::catch_unwind(AssertUnwindSafe(|| {
func($( FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)) ),* ).into_result()
}));

match result {
Ok(Ok(result)) => return result.into_c_struct(&mut store),
#[allow(deprecated)]
Ok(Err(trap)) => RuntimeError::raise(Box::new(trap)),
Err(_panic) => unimplemented!(),
}
}

func_wrapper::< $( $x, )* Rets, RetsAsResult, Self > as *const VMFunctionBody
}
}
};
}

Expand Down
10 changes: 3 additions & 7 deletions lib/api/src/js/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//!
//! ```ignore
//! let add_one = instance.exports.get_function("function_name")?;
//! let add_one_native: TypedFunction<i32, i32> = add_one.native().unwrap();
//! let add_one_native: TypedFunction<i32, i32> = add_one.typed().unwrap();
//! ```
use std::marker::PhantomData;

Expand Down Expand Up @@ -37,11 +37,7 @@ where
Rets: WasmTypeList,
{
#[allow(dead_code)]
pub(crate) fn new<T>(
store: &mut impl AsStoreMut,
env: &FunctionEnv<T>,
vm_function: VMFunction,
) -> Self {
pub(crate) fn new<T>(store: &mut impl AsStoreMut, vm_function: VMFunction) -> Self {
Self {
handle: StoreHandle::new(store.as_store_mut().objects_mut(), vm_function),
_phantom: PhantomData,
Expand Down Expand Up @@ -108,7 +104,7 @@ macro_rules! impl_native_traits {
{
fn get_self_from_extern_with_generics(store: &impl AsStoreRef, _extern: &crate::js::externals::Extern) -> Result<Self, crate::js::exports::ExportError> {
use crate::js::exports::Exportable;
crate::js::Function::get_self_from_extern(_extern)?.native(store).map_err(|_| crate::js::exports::ExportError::IncompatibleType)
crate::js::Function::get_self_from_extern(_extern)?.typed(store).map_err(|_| crate::js::exports::ExportError::IncompatibleType)
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion lib/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@
//! let memory = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap();
//! imports! {
//! "env" => {
//! "my_function" => Function::new_typed(&mut store, |_env: FunctionEnvMut<()>| println!("Hello")),
//! "my_function" => Function::new_typed(&mut store, || println!("Hello")),
//! "memory" => memory,
//! }
//! }
Expand Down
Loading

0 comments on commit 70d279e

Please sign in to comment.