Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for const function signatures #1911

Merged
merged 3 commits into from
Dec 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* [#1881](https://github.com/wasmerio/wasmer/pull/1881) Added `UnsupportedTarget` error to `CompileError`
* [#1908](https://github.com/wasmerio/wasmer/pull/1908) Implemented `TryFrom<Value<T>>` for `i32`/`u32`/`i64`/`u64`/`f32`/`f64`
* [#1927](https://github.com/wasmerio/wasmer/pull/1927) Added mmap support in `Engine::deserialize_from_file` to speed up artifact loading
* [#1911](https://github.com/wasmerio/wasmer/pull/1911) Generalized signature type in `Function::new` and `Function::new_with_env` to accept owned and reference `FunctionType` as well as array pairs. This allows users to define signatures as constants. Implemented `From<([Type; $N], [Type; $M])>` for `FunctionType` to support this.

### Changed

Expand Down
49 changes: 43 additions & 6 deletions lib/api/src/externals/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub struct Function {
impl Function {
/// Creates a new host `Function` (dynamic) with the provided signature.
///
/// # Example
/// # Examples
///
/// ```
/// # use wasmer::{Function, FunctionType, Type, Store, Value};
Expand All @@ -82,11 +82,27 @@ impl Function {
/// Ok(vec![Value::I32(sum)])
/// });
/// ```
///
/// With constant signature:
///
/// ```
/// # use wasmer::{Function, FunctionType, Type, Store, Value};
/// # let store = Store::default();
/// #
/// const I32_I32_TO_I32: ([Type; 2], [Type; 1]) = ([Type::I32, Type::I32], [Type::I32]);
///
/// let f = Function::new(&store, I32_I32_TO_I32, |args| {
/// let sum = args[0].unwrap_i32() + args[1].unwrap_i32();
/// Ok(vec![Value::I32(sum)])
/// });
/// ```
#[allow(clippy::cast_ptr_alignment)]
pub fn new<F>(store: &Store, ty: &FunctionType, func: F) -> Self
pub fn new<FT, F>(store: &Store, ty: FT, func: F) -> Self
where
FT: Into<FunctionType>,
F: Fn(&[Val]) -> Result<Vec<Val>, RuntimeError> + 'static,
{
let ty: FunctionType = ty.into();
let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithoutEnv {
func: Box::new(func),
function_type: ty.clone(),
Expand All @@ -108,7 +124,7 @@ impl Function {
address,
kind: VMFunctionKind::Dynamic,
vmctx,
signature: ty.clone(),
signature: ty,
call_trampoline: None,
instance_allocator: None,
},
Expand All @@ -118,7 +134,7 @@ impl Function {

/// Creates a new host `Function` (dynamic) with the provided signature and environment.
///
/// # Example
/// # Examples
///
/// ```
/// # use wasmer::{Function, FunctionType, Type, Store, Value, WasmerEnv};
Expand All @@ -137,12 +153,33 @@ impl Function {
/// Ok(vec![Value::I32(result)])
/// });
/// ```
///
/// With constant signature:
///
/// ```
/// # use wasmer::{Function, FunctionType, Type, Store, Value, WasmerEnv};
/// # let store = Store::default();
/// const I32_I32_TO_I32: ([Type; 2], [Type; 1]) = ([Type::I32, Type::I32], [Type::I32]);
///
/// #[derive(WasmerEnv)]
/// struct Env {
/// multiplier: i32,
/// };
/// let env = Env { multiplier: 2 };
///
/// let f = Function::new_with_env(&store, I32_I32_TO_I32, env, |env, args| {
/// let result = env.multiplier * (args[0].unwrap_i32() + args[1].unwrap_i32());
/// Ok(vec![Value::I32(result)])
/// });
/// ```
#[allow(clippy::cast_ptr_alignment)]
pub fn new_with_env<F, Env>(store: &Store, ty: &FunctionType, env: Env, func: F) -> Self
pub fn new_with_env<FT, F, Env>(store: &Store, ty: FT, env: Env, func: F) -> Self
where
FT: Into<FunctionType>,
F: Fn(&Env, &[Val]) -> Result<Vec<Val>, RuntimeError> + 'static,
Env: Sized + WasmerEnv + 'static,
{
let ty: FunctionType = ty.into();
let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithEnv {
env: Box::new(env),
func: Box::new(func),
Expand Down Expand Up @@ -171,7 +208,7 @@ impl Function {
address,
kind: VMFunctionKind::Dynamic,
vmctx,
signature: ty.clone(),
signature: ty,
call_trampoline: None,
instance_allocator: None,
},
Expand Down
22 changes: 22 additions & 0 deletions lib/api/tests/externals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ fn function_new_env() -> Result<()> {
#[test]
fn function_new_dynamic() -> Result<()> {
let store = Store::default();

// Using &FunctionType signature
let function_type = FunctionType::new(vec![], vec![]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);
Expand All @@ -265,6 +267,13 @@ fn function_new_dynamic() -> Result<()> {
let function_type = FunctionType::new(vec![], vec![Type::I32, Type::I64, Type::F32, Type::F64]);
let function = Function::new(&store, &function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().clone(), function_type);

// Using array signature
let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]);
let function = Function::new(&store, function_type, |_values: &[Value]| unimplemented!());
assert_eq!(function.ty().params(), [Type::V128]);
assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]);

Ok(())
}

Expand All @@ -275,6 +284,7 @@ fn function_new_dynamic_env() -> Result<()> {
struct MyEnv {};
let my_env = MyEnv {};

// Using &FunctionType signature
let function_type = FunctionType::new(vec![], vec![]);
let function = Function::new_with_env(
&store,
Expand Down Expand Up @@ -315,6 +325,18 @@ fn function_new_dynamic_env() -> Result<()> {
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().clone(), function_type);

// Using array signature
let function_type = ([Type::V128], [Type::I32, Type::F32, Type::F64]);
let function = Function::new_with_env(
&store,
function_type,
my_env.clone(),
|_env: &MyEnv, _values: &[Value]| unimplemented!(),
);
assert_eq!(function.ty().params(), [Type::V128]);
assert_eq!(function.ty().results(), [Type::I32, Type::F32, Type::F64]);

Ok(())
}

Expand Down
4 changes: 2 additions & 2 deletions lib/c-api/src/wasm_c_api/externals/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub unsafe extern "C" fn wasm_func_new(

Ok(processed_results)
};
let function = Function::new(&store.inner, &func_sig, inner_callback);
let function = Function::new(&store.inner, func_sig, inner_callback);

Some(Box::new(wasm_func_t {
instance: None,
Expand Down Expand Up @@ -154,7 +154,7 @@ pub unsafe extern "C" fn wasm_func_new_with_env(

let function = Function::new_with_env(
&store.inner,
&func_sig,
func_sig,
WrapperEnv { env, finalizer },
inner_callback,
);
Expand Down
1 change: 1 addition & 0 deletions lib/deprecated/runtime-core/src/typed_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ impl DynamicFunc {

Self {
new_function: new::wasmer::Function::new_with_env::<
_,
fn(&DynamicCtx, &[Value]) -> Result<Vec<Value>, RuntimeError>,
DynamicCtx,
>(&get_global_store(), signature, ctx, inner),
Expand Down
2 changes: 1 addition & 1 deletion lib/wasi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ fn get_path_open_for_store(store: &Store, env: WasiEnv) -> Function {
#[cfg(all(target_os = "macos", target_arch = "aarch64",))]
let path_open = Function::new_with_env(
store,
&FunctionType::new(
FunctionType::new(
vec![
ValType::I32,
ValType::I32,
Expand Down
67 changes: 67 additions & 0 deletions lib/wasmer-types/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,39 @@ impl fmt::Display for FunctionType {
}
}

// Macro needed until https://rust-lang.github.io/rfcs/2000-const-generics.html is stable.
// See https://users.rust-lang.org/t/how-to-implement-trait-for-fixed-size-array-of-any-size/31494
macro_rules! implement_from_pair_to_functiontype {
($($N:literal,$M:literal)+) => {
$(
impl From<([Type; $N], [Type; $M])> for FunctionType {
fn from(pair: ([Type; $N], [Type; $M])) -> Self {
Self::new(pair.0, pair.1)
}
}
)+
}
}

implement_from_pair_to_functiontype! {
0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9
1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9
2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9
3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 3,9
4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9
5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 5,9
6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 6,9
7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 7,9
8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 8,9
9,0 9,1 9,2 9,3 9,4 9,5 9,6 9,7 9,8 9,9
}

impl From<&FunctionType> for FunctionType {
fn from(as_ref: &FunctionType) -> Self {
as_ref.clone()
}
}
Comment on lines +306 to +310
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


/// Indicator of whether a global is mutable or not
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
Expand Down Expand Up @@ -566,3 +599,37 @@ impl<T> ExportType<T> {
&self.ty
}
}

#[cfg(test)]
mod tests {
use super::*;

const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []);
const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []);
const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]);
const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]);

#[test]
fn convert_tuple_to_functiontype() {
let ty: FunctionType = VOID_TO_VOID.into();
assert_eq!(ty.params().len(), 0);
assert_eq!(ty.results().len(), 0);

let ty: FunctionType = I32_I32_TO_VOID.into();
assert_eq!(ty.params().len(), 2);
assert_eq!(ty.params()[0], Type::I32);
assert_eq!(ty.params()[1], Type::I32);
assert_eq!(ty.results().len(), 0);

let ty: FunctionType = V128_I64_TO_I32.into();
assert_eq!(ty.params().len(), 2);
assert_eq!(ty.params()[0], Type::V128);
assert_eq!(ty.params()[1], Type::I64);
assert_eq!(ty.results().len(), 1);
assert_eq!(ty.results()[0], Type::I32);

let ty: FunctionType = NINE_V128_TO_NINE_I32.into();
assert_eq!(ty.params().len(), 9);
assert_eq!(ty.results().len(), 9);
}
}
18 changes: 9 additions & 9 deletions tests/compilers/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,22 @@ fn dynamic_function() -> Result<()> {
&module,
&imports! {
"host" => {
"0" => Function::new(&store, &FunctionType::new(vec![], vec![]), |_values| {
"0" => Function::new(&store, FunctionType::new(vec![], vec![]), |_values| {
assert_eq!(HITS.fetch_add(1, SeqCst), 0);
Ok(vec![])
}),
"1" => Function::new(&store, &FunctionType::new(vec![ValType::I32], vec![ValType::I32]), |values| {
"1" => Function::new(&store, FunctionType::new(vec![ValType::I32], vec![ValType::I32]), |values| {
assert_eq!(values[0], Value::I32(0));
assert_eq!(HITS.fetch_add(1, SeqCst), 1);
Ok(vec![Value::I32(1)])
}),
"2" => Function::new(&store, &FunctionType::new(vec![ValType::I32, ValType::I64], vec![]), |values| {
"2" => Function::new(&store, FunctionType::new(vec![ValType::I32, ValType::I64], vec![]), |values| {
assert_eq!(values[0], Value::I32(2));
assert_eq!(values[1], Value::I64(3));
assert_eq!(HITS.fetch_add(1, SeqCst), 2);
Ok(vec![])
}),
"3" => Function::new(&store, &FunctionType::new(vec![ValType::I32, ValType::I64, ValType::I32, ValType::F32, ValType::F64], vec![]), |values| {
"3" => Function::new(&store, FunctionType::new(vec![ValType::I32, ValType::I64, ValType::I32, ValType::F32, ValType::F64], vec![]), |values| {
assert_eq!(values[0], Value::I32(100));
assert_eq!(values[1], Value::I64(200));
assert_eq!(values[2], Value::I32(300));
Expand Down Expand Up @@ -107,22 +107,22 @@ fn dynamic_function_with_env() -> Result<()> {
&module,
&imports! {
"host" => {
"0" => Function::new_with_env(&store, &FunctionType::new(vec![], vec![]), env.clone(), |env, _values| {
"0" => Function::new_with_env(&store, FunctionType::new(vec![], vec![]), env.clone(), |env, _values| {
assert_eq!(env.fetch_add(1, SeqCst), 0);
Ok(vec![])
}),
"1" => Function::new_with_env(&store, &FunctionType::new(vec![ValType::I32], vec![ValType::I32]), env.clone(), |env, values| {
"1" => Function::new_with_env(&store, FunctionType::new(vec![ValType::I32], vec![ValType::I32]), env.clone(), |env, values| {
assert_eq!(values[0], Value::I32(0));
assert_eq!(env.fetch_add(1, SeqCst), 1);
Ok(vec![Value::I32(1)])
}),
"2" => Function::new_with_env(&store, &FunctionType::new(vec![ValType::I32, ValType::I64], vec![]), env.clone(), |env, values| {
"2" => Function::new_with_env(&store, FunctionType::new(vec![ValType::I32, ValType::I64], vec![]), env.clone(), |env, values| {
assert_eq!(values[0], Value::I32(2));
assert_eq!(values[1], Value::I64(3));
assert_eq!(env.fetch_add(1, SeqCst), 2);
Ok(vec![])
}),
"3" => Function::new_with_env(&store, &FunctionType::new(vec![ValType::I32, ValType::I64, ValType::I32, ValType::F32, ValType::F64], vec![]), env.clone(), |env, values| {
"3" => Function::new_with_env(&store, FunctionType::new(vec![ValType::I32, ValType::I64, ValType::I32, ValType::F32, ValType::F64], vec![]), env.clone(), |env, values| {
assert_eq!(values[0], Value::I32(100));
assert_eq!(values[1], Value::I64(200));
assert_eq!(values[2], Value::I32(300));
Expand Down Expand Up @@ -332,7 +332,7 @@ fn dynamic_function_with_env_wasmer_env_init_works() -> Result<()> {
&module,
&imports! {
"host" => {
"fn" => Function::new_with_env(&store, &FunctionType::new(vec![], vec![]), env.clone(), |env, _values| {
"fn" => Function::new_with_env(&store, FunctionType::new(vec![], vec![]), env.clone(), |env, _values| {
assert!(env.memory_ref().is_some());
Ok(vec![])
}),
Expand Down
6 changes: 3 additions & 3 deletions tests/compilers/native_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ fn native_function_works_for_wasm_function_manyparams_dynamic() -> Result<()> {

let import_object = imports! {
"env" => {
"longf" => Function::new(&store, &FunctionType::new(vec![ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I64 , ValType::I64 ,ValType::I32, ValType::I32], vec![ValType::I64]), long_f_dynamic),
"longf" => Function::new(&store, FunctionType::new(vec![ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I64 , ValType::I64 ,ValType::I32, ValType::I32], vec![ValType::I64]), long_f_dynamic),
},
};

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

let f = Function::new(
&store,
&FunctionType::new(
FunctionType::new(
vec![ValType::I32, ValType::I64, ValType::F32, ValType::F64],
vec![ValType::F64, ValType::F32, ValType::I64, ValType::I32],
),
Expand Down Expand Up @@ -415,7 +415,7 @@ fn dynamic_host_function_with_env() -> anyhow::Result<()> {
let env = Env(Rc::new(RefCell::new(100)));
let f = Function::new_with_env(
&store,
&FunctionType::new(
FunctionType::new(
vec![ValType::I32, ValType::I64, ValType::F32, ValType::F64],
vec![ValType::F64, ValType::F32, ValType::I64, ValType::I32],
),
Expand Down