From 3ea88174a13072cf5e5ecba330328faa698c054f Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 24 Nov 2020 12:40:51 -0800 Subject: [PATCH] Disable closures as host functions for now + docs + tests --- lib/api/src/externals/function.rs | 24 ++++++ tests/compilers/native_functions.rs | 112 ++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/lib/api/src/externals/function.rs b/lib/api/src/externals/function.rs index b65462e3af3..7ba6ac58653 100644 --- a/lib/api/src/externals/function.rs +++ b/lib/api/src/externals/function.rs @@ -51,6 +51,11 @@ pub enum FunctionDefinition { /// during execution of the function. /// /// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#function-instances +/// +/// # Panics +/// - Closures (functions with captured environments) are not currently supported. +/// Attempting to create a `Function` with one will result in a panic. +/// [Closures as host functions tracking issue](https://github.com/wasmerio/wasmer/issues/1840) #[derive(Clone, PartialEq)] pub struct Function { pub(crate) store: Store, @@ -79,6 +84,9 @@ impl Function { where F: Fn(&[Val]) -> Result, RuntimeError> + 'static, { + if std::mem::size_of::() != 0 { + Self::closures_unsupported_panic(); + } let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithoutEnv { func: Box::new(func), function_type: ty.clone(), @@ -130,6 +138,9 @@ impl Function { F: Fn(&Env, &[Val]) -> Result, RuntimeError> + 'static, Env: Sized + 'static, { + if std::mem::size_of::() != 0 { + Self::closures_unsupported_panic(); + } let dynamic_ctx = VMDynamicFunctionContext::from_context(VMDynamicFunctionWithEnv { env: Box::new(env), func: Box::new(func), @@ -180,6 +191,9 @@ impl Function { Rets: WasmTypeList, Env: Sized + 'static, { + if std::mem::size_of::() != 0 { + Self::closures_unsupported_panic(); + } let function = inner::Function::::new(func); let address = function.address() as *const VMFunctionBody; let vmctx = VMFunctionEnvironment { @@ -229,6 +243,9 @@ impl Function { Rets: WasmTypeList, Env: Sized + 'static, { + if std::mem::size_of::() != 0 { + Self::closures_unsupported_panic(); + } let function = inner::Function::::new(func); let address = function.address(); @@ -275,6 +292,9 @@ impl Function { Rets: WasmTypeList, Env: UnsafeMutableEnv + 'static, { + if std::mem::size_of::() != 0 { + Self::closures_unsupported_panic(); + } let function = inner::Function::::new(func); let address = function.address(); @@ -620,6 +640,10 @@ impl Function { self.definition.clone(), )) } + + fn closures_unsupported_panic() -> ! { + unimplemented!("Closures (functions with captured environments) are currently unsupported. See: https://github.com/wasmerio/wasmer/issues/1840") + } } impl<'a> Exportable<'a> for Function { diff --git a/tests/compilers/native_functions.rs b/tests/compilers/native_functions.rs index cd2600eac1f..66085327eb8 100644 --- a/tests/compilers/native_functions.rs +++ b/tests/compilers/native_functions.rs @@ -78,6 +78,118 @@ fn native_function_works_for_wasm() -> Result<()> { Ok(()) } +#[test] +#[should_panic( + expected = "Closures (functions with captured environments) are currently unsupported. See: https://github.com/wasmerio/wasmer/issues/1840" +)] +fn host_function_closure_panics() { + let store = get_store(false); + let state = 3; + let ty = FunctionType::new(vec![], vec![]); + Function::new(&store, &ty, move |_args| { + println!("{}", state); + Ok(vec![]) + }); +} + +#[test] +#[should_panic( + expected = "Closures (functions with captured environments) are currently unsupported. See: https://github.com/wasmerio/wasmer/issues/1840" +)] +fn env_host_function_closure_panics() { + let store = get_store(false); + let state = 3; + let ty = FunctionType::new(vec![], vec![]); + let env = 4; + Function::new_with_env(&store, &ty, env, move |_env, _args| { + println!("{}", state); + Ok(vec![]) + }); +} + +#[test] +#[should_panic( + expected = "Closures (functions with captured environments) are currently unsupported. See: https://github.com/wasmerio/wasmer/issues/1840" +)] +fn native_host_function_closure_panics() { + let store = get_store(false); + let state = 3; + Function::new_native(&store, move |_: i32| { + println!("{}", state); + }); +} + +#[test] +#[should_panic( + expected = "Closures (functions with captured environments) are currently unsupported. See: https://github.com/wasmerio/wasmer/issues/1840" +)] +fn native_with_env_host_function_closure_panics() { + let store = get_store(false); + let state = 3; + let env = 4; + Function::new_native_with_env(&store, env, move |_env: &i32, _: i32| { + println!("{}", state); + }); +} + +#[test] +fn lambdas_with_no_env_work() -> Result<()> { + let store = get_store(false); + let wat = r#"(module + (func $multiply1 (import "env" "multiply1") (param i32 i32) (result i32)) + (func $multiply2 (import "env" "multiply2") (param i32 i32) (result i32)) + (func $multiply3 (import "env" "multiply3") (param i32 i32) (result i32)) + (func $multiply4 (import "env" "multiply4") (param i32 i32) (result i32)) + + (func (export "test") (param i32 i32 i32 i32 i32) (result i32) + (call $multiply4 + (call $multiply3 + (call $multiply2 + (call $multiply1 + (local.get 0) + (local.get 1)) + (local.get 2)) + (local.get 3)) + (local.get 4))) +)"#; + let module = Module::new(&store, wat).unwrap(); + + let ty = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]); + let env = 10; + let import_object = imports! { + "env" => { + "multiply1" => Function::new(&store, &ty, |args| { + if let (Value::I32(v1), Value::I32(v2)) = (&args[0], &args[1]) { + Ok(vec![Value::I32(v1 * v2)]) + } else { + panic!("Invalid arguments"); + } + }), + "multiply2" => Function::new_with_env(&store, &ty, env, |&env, args| { + if let (Value::I32(v1), Value::I32(v2)) = (&args[0], &args[1]) { + Ok(vec![Value::I32(v1 * v2 * env)]) + } else { + panic!("Invalid arguments"); + } + }), + "multiply3" => Function::new_native(&store, |arg1: i32, arg2: i32| -> i32 + {arg1 * arg2 }), + "multiply4" => Function::new_native_with_env(&store, env, |&env: &i32, arg1: i32, arg2: i32| -> i32 + {arg1 * arg2 * env }), + }, + }; + + let instance = Instance::new(&module, &import_object)?; + + let test: NativeFunc<(i32, i32, i32, i32, i32), i32> = + instance.exports.get_native_function("test")?; + + let result = test.call(2, 3, 4, 5, 6)?; + let manually_computed_result = 6 * (5 * (4 * (3 * 2) * 10)) * 10; + assert_eq!(result, manually_computed_result); + Ok(()) +} + // The native ABI for functions fails when defining a function natively in // macos (Darwin) with the Apple Silicon ARM chip // TODO: Cranelift should have a good ABI for the ABI