mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-04 18:10:18 +00:00
Improved tests to use available compilers
This commit is contained in:
parent
5f5928dfbd
commit
b301ac85be
@ -96,7 +96,7 @@ wabt = "0.9.1"
|
||||
# Don't add the backend features in default, please add them on the Makefile
|
||||
# since we might want to autoconfigure them depending on the availability on the host.
|
||||
default = ["wasi", "wabt"]
|
||||
"loader-kernel" = ["wasmer-kernel-loader"]
|
||||
loader-kernel = ["wasmer-kernel-loader"]
|
||||
debug = ["fern", "log/max_level_debug", "log/release_max_level_debug"]
|
||||
trace = ["fern", "log/max_level_trace", "log/release_max_level_trace"]
|
||||
docs = ["wasmer-runtime/docs"]
|
||||
|
@ -351,6 +351,10 @@ pub mod error {
|
||||
/// Idea for generic trait; consider rename; it will need to be moved somewhere else
|
||||
pub trait CompiledModule {
|
||||
fn new(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module>;
|
||||
fn new_with_compiler(
|
||||
bytes: impl AsRef<[u8]>,
|
||||
compiler: Box<dyn compiler::Compiler>,
|
||||
) -> error::CompileResult<Module>;
|
||||
fn from_binary(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module>;
|
||||
fn from_binary_unchecked(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module>;
|
||||
fn from_file(file: impl AsRef<std::path::Path>) -> Result<Module, error::CompileFromFileError>;
|
||||
@ -365,6 +369,14 @@ impl CompiledModule for Module {
|
||||
wasmer_runtime_core::compile_with(bytes, &compiler::default_compiler())
|
||||
}
|
||||
|
||||
fn new_with_compiler(
|
||||
bytes: impl AsRef<[u8]>,
|
||||
compiler: Box<dyn compiler::Compiler>,
|
||||
) -> error::CompileResult<Module> {
|
||||
let bytes = bytes.as_ref();
|
||||
wasmer_runtime_core::compile_with(bytes, &*compiler)
|
||||
}
|
||||
|
||||
fn from_binary(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module> {
|
||||
let bytes = bytes.as_ref();
|
||||
wasmer_runtime_core::compile_with(bytes, &compiler::default_compiler())
|
||||
|
10
tests/custom/stack-overflow.wast
Normal file
10
tests/custom/stack-overflow.wast
Normal file
@ -0,0 +1,10 @@
|
||||
(module
|
||||
(type (;0;) (func))
|
||||
(func (;0;) (type 0)
|
||||
i32.const 0
|
||||
call_indirect (type 0))
|
||||
(table (;0;) 1 anyfunc)
|
||||
(export "stack-overflow" (func 0))
|
||||
(elem (;0;) (i32.const 0) 0))
|
||||
|
||||
(assert_exhaustion (invoke "stack-overflow"))
|
@ -1,3 +1,8 @@
|
||||
#![cfg(test)]
|
||||
|
||||
#[macro_use]
|
||||
mod utils;
|
||||
|
||||
static TEST_WAT: &str = r#"
|
||||
(module
|
||||
(import "env" "outside-global" (global $outside-global (mut i32)))
|
||||
@ -47,151 +52,155 @@ fn append_custom_section(
|
||||
wasm.extend(custom_section_contents);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_section_parsing_works() {
|
||||
use wasmer::{CompiledModule, Module};
|
||||
let wasm = {
|
||||
let mut wasm = wabt::wat2wasm(TEST_WAT).unwrap();
|
||||
append_custom_section(&mut wasm, "test_custom_section", b"hello, world!");
|
||||
append_custom_section(&mut wasm, "test_custom_section", b"goodbye, world!");
|
||||
append_custom_section(&mut wasm, "different_name", b"different value");
|
||||
wasm
|
||||
};
|
||||
wasmer_backends! {
|
||||
use super::{TEST_WAT, append_custom_section};
|
||||
|
||||
let module = Module::new(wasm).unwrap();
|
||||
#[test]
|
||||
fn custom_section_parsing_works() {
|
||||
use wasmer::{CompiledModule, Module};
|
||||
let wasm = {
|
||||
let mut wasm = wabt::wat2wasm(TEST_WAT).unwrap();
|
||||
append_custom_section(&mut wasm, "test_custom_section", b"hello, world!");
|
||||
append_custom_section(&mut wasm, "test_custom_section", b"goodbye, world!");
|
||||
append_custom_section(&mut wasm, "different_name", b"different value");
|
||||
wasm
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
module.custom_sections("test_custom_section"),
|
||||
Some(&[b"hello, world!".to_vec(), b"goodbye, world!".to_vec()][..])
|
||||
);
|
||||
}
|
||||
let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
|
||||
|
||||
#[test]
|
||||
fn module_exports_are_ordered() {
|
||||
use wasmer::types::{ElementType, FuncSig, GlobalDescriptor, TableDescriptor, Type};
|
||||
use wasmer::{export, CompiledModule, Module};
|
||||
assert_eq!(
|
||||
module.custom_sections("test_custom_section"),
|
||||
Some(&[b"hello, world!".to_vec(), b"goodbye, world!".to_vec()][..])
|
||||
);
|
||||
}
|
||||
|
||||
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
|
||||
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
|
||||
// misleading, if so we may want to do something about it.
|
||||
let module = Module::new(wasm).unwrap();
|
||||
let exports = module.exports();
|
||||
assert_eq!(
|
||||
exports,
|
||||
vec![
|
||||
export::ExportDescriptor {
|
||||
name: "test-table",
|
||||
ty: export::ExternDescriptor::Table(TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: 2,
|
||||
maximum: None,
|
||||
}),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "test-global",
|
||||
ty: export::ExternDescriptor::Global(GlobalDescriptor {
|
||||
mutable: true,
|
||||
ty: Type::I32,
|
||||
}),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "ret_2",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![], vec![Type::I32])),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "ret_4",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![], vec![Type::I32])),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "set_test_global",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![Type::I32], vec![])),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "update_outside_global",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![], vec![])),
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn module_exports_are_ordered() {
|
||||
use wasmer::types::{ElementType, FuncSig, GlobalDescriptor, TableDescriptor, Type};
|
||||
use wasmer::{export, CompiledModule, Module};
|
||||
|
||||
#[test]
|
||||
fn module_imports_are_correct() {
|
||||
use wasmer::{CompiledModule, Module};
|
||||
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
|
||||
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
|
||||
// misleading, if so we may want to do something about it.
|
||||
let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
|
||||
let exports = module.exports();
|
||||
assert_eq!(
|
||||
exports,
|
||||
vec![
|
||||
export::ExportDescriptor {
|
||||
name: "test-table",
|
||||
ty: export::ExternDescriptor::Table(TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: 2,
|
||||
maximum: None,
|
||||
}),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "test-global",
|
||||
ty: export::ExternDescriptor::Global(GlobalDescriptor {
|
||||
mutable: true,
|
||||
ty: Type::I32,
|
||||
}),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "ret_2",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![], vec![Type::I32])),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "ret_4",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![], vec![Type::I32])),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "set_test_global",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![Type::I32], vec![])),
|
||||
},
|
||||
export::ExportDescriptor {
|
||||
name: "update_outside_global",
|
||||
ty: export::ExternDescriptor::Function(FuncSig::new(vec![], vec![])),
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
|
||||
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
|
||||
// misleading, if so we may want to do something about it.
|
||||
let module = Module::new(wasm).unwrap();
|
||||
#[test]
|
||||
fn module_imports_are_correct() {
|
||||
use wasmer::{CompiledModule, Module};
|
||||
|
||||
// TODO: test this more later
|
||||
let imports = module.imports();
|
||||
assert_eq!(imports.len(), 2);
|
||||
}
|
||||
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
|
||||
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
|
||||
// misleading, if so we may want to do something about it.
|
||||
let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
|
||||
|
||||
#[test]
|
||||
fn module_can_be_instantiated() {
|
||||
use wasmer::wasm::{Global, Value};
|
||||
use wasmer::{func, imports, CompiledModule, Module};
|
||||
// TODO: test this more later
|
||||
let imports = module.imports();
|
||||
assert_eq!(imports.len(), 2);
|
||||
}
|
||||
|
||||
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
|
||||
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
|
||||
// misleading, if so we may want to do something about it.
|
||||
let module = Module::new(wasm).unwrap();
|
||||
#[test]
|
||||
fn module_can_be_instantiated() {
|
||||
use wasmer::wasm::{Global, Value};
|
||||
use wasmer::{func, imports, CompiledModule, Module};
|
||||
|
||||
let outside_global = Global::new_mutable(Value::I32(15));
|
||||
let import_object = imports! {
|
||||
"env" => {
|
||||
"update-func" => func!(|x: i32| x * 2),
|
||||
"outside-global" => outside_global.clone(),
|
||||
}
|
||||
};
|
||||
let instance = module.instantiate(&import_object);
|
||||
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
|
||||
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
|
||||
// misleading, if so we may want to do something about it.
|
||||
let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
|
||||
|
||||
assert!(instance.is_ok());
|
||||
}
|
||||
let outside_global = Global::new_mutable(Value::I32(15));
|
||||
let import_object = imports! {
|
||||
"env" => {
|
||||
"update-func" => func!(|x: i32| x * 2),
|
||||
"outside-global" => outside_global.clone(),
|
||||
}
|
||||
};
|
||||
let instance = module.instantiate(&import_object);
|
||||
|
||||
#[test]
|
||||
fn exports_work() {
|
||||
use wasmer::wasm::{Global, Value};
|
||||
use wasmer::{func, imports, CompiledModule, Func, Module, Table};
|
||||
assert!(instance.is_ok());
|
||||
}
|
||||
|
||||
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
|
||||
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
|
||||
// misleading, if so we may want to do something about it.
|
||||
let module = Module::new(wasm).unwrap();
|
||||
#[test]
|
||||
fn exports_work() {
|
||||
use wasmer::wasm::{Global, Value};
|
||||
use wasmer::{func, imports, CompiledModule, Func, Module, Table};
|
||||
|
||||
let outside_global = Global::new_mutable(Value::I32(15));
|
||||
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
|
||||
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
|
||||
// misleading, if so we may want to do something about it.
|
||||
let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
|
||||
|
||||
let import_object = imports! {
|
||||
"env" => {
|
||||
"update-func" => func!(|x: i32| x * 2),
|
||||
"outside-global" => outside_global.clone(),
|
||||
}
|
||||
};
|
||||
let instance = module.instantiate(&import_object).unwrap();
|
||||
let outside_global = Global::new_mutable(Value::I32(15));
|
||||
|
||||
let ret_2: Func<(), i32> = instance.exports.get("ret_2").unwrap();
|
||||
let ret_4: Func<(), i32> = instance.exports.get("ret_4").unwrap();
|
||||
let set_test_global: Func<i32> = instance.exports.get("set_test_global").unwrap();
|
||||
let update_outside_global: Func = instance.exports.get("update_outside_global").unwrap();
|
||||
let import_object = imports! {
|
||||
"env" => {
|
||||
"update-func" => func!(|x: i32| x * 2),
|
||||
"outside-global" => outside_global.clone(),
|
||||
}
|
||||
};
|
||||
let instance = module.instantiate(&import_object).unwrap();
|
||||
|
||||
assert_eq!(ret_2.call(), Ok(2));
|
||||
assert_eq!(ret_4.call(), Ok(4));
|
||||
let ret_2: Func<(), i32> = instance.exports.get("ret_2").unwrap();
|
||||
let ret_4: Func<(), i32> = instance.exports.get("ret_4").unwrap();
|
||||
let set_test_global: Func<i32> = instance.exports.get("set_test_global").unwrap();
|
||||
let update_outside_global: Func = instance.exports.get("update_outside_global").unwrap();
|
||||
|
||||
let _test_table: Table = instance.exports.get("test-table").unwrap();
|
||||
// TODO: when table get is stablized test this
|
||||
assert_eq!(ret_2.call(), Ok(2));
|
||||
assert_eq!(ret_4.call(), Ok(4));
|
||||
|
||||
let test_global: Global = instance.exports.get("test-global").unwrap();
|
||||
// TODO: do we want to make global.get act like exports.get()?
|
||||
assert_eq!(test_global.get(), Value::I32(3));
|
||||
set_test_global.call(17).unwrap();
|
||||
assert_eq!(test_global.get(), Value::I32(17));
|
||||
let _test_table: Table = instance.exports.get("test-table").unwrap();
|
||||
// TODO: when table get is stablized test this
|
||||
|
||||
assert_eq!(outside_global.get(), Value::I32(15));
|
||||
update_outside_global.call().unwrap();
|
||||
assert_eq!(outside_global.get(), Value::I32(15 * 2));
|
||||
update_outside_global.call().unwrap();
|
||||
assert_eq!(outside_global.get(), Value::I32(15 * 2 * 2));
|
||||
let test_global: Global = instance.exports.get("test-global").unwrap();
|
||||
// TODO: do we want to make global.get act like exports.get()?
|
||||
assert_eq!(test_global.get(), Value::I32(3));
|
||||
set_test_global.call(17).unwrap();
|
||||
assert_eq!(test_global.get(), Value::I32(17));
|
||||
|
||||
assert_eq!(outside_global.get(), Value::I32(15));
|
||||
update_outside_global.call().unwrap();
|
||||
assert_eq!(outside_global.get(), Value::I32(15 * 2));
|
||||
update_outside_global.call().unwrap();
|
||||
assert_eq!(outside_global.get(), Value::I32(15 * 2 * 2));
|
||||
}
|
||||
}
|
||||
|
||||
// copied from Rust stdlib: https://doc.rust-lang.org/nightly/nightly-rustc/src/serialize/leb128.rs.html#4-14
|
||||
|
207
tests/imports.rs
207
tests/imports.rs
@ -1,3 +1,6 @@
|
||||
#[macro_use]
|
||||
mod utils;
|
||||
|
||||
use std::{convert::TryInto, sync::Arc};
|
||||
use wabt::wat2wasm;
|
||||
use wasmer::compiler::{compile_with, compiler_for_backend, Backend};
|
||||
@ -386,128 +389,106 @@ fn callback_fn_trap_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result<i32, Strin
|
||||
}
|
||||
|
||||
macro_rules! test {
|
||||
($backend:expr, $test_name:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
|
||||
($test_name:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
|
||||
#[cfg(all(unix, target_arch = "x86_64"))]
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
imported_functions_forms($backend, &|instance| {
|
||||
imported_functions_forms(get_backend(), &|instance| {
|
||||
call_and_assert!(instance, $function( $( $inputs ),* ) -> $output, ( $( $arguments ),* ) == $expected_value);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tests_for_backend {
|
||||
($backend:expr) => {
|
||||
test!($backend, test_fn, function_fn(i32) -> i32, (1) == Ok(2));
|
||||
test!($backend, test_closure, function_closure(i32) -> i32, (1) == Ok(2));
|
||||
test!($backend, test_fn_dynamic, function_fn_dynamic(i32) -> i32, (1) == Ok(2));
|
||||
test!($backend, test_fn_dynamic_panic, function_fn_dynamic_panic(i32) -> i32, (1) == Err(RuntimeError(Box::new("test"))));
|
||||
test!(
|
||||
$backend,
|
||||
test_closure_dynamic_0,
|
||||
function_closure_dynamic_0(()) -> (),
|
||||
() == Ok(())
|
||||
);
|
||||
test!(
|
||||
$backend,
|
||||
test_closure_dynamic_1,
|
||||
function_closure_dynamic_1(i32) -> i32,
|
||||
(1) == Ok(1 + shift + SHIFT)
|
||||
);
|
||||
test!(
|
||||
$backend,
|
||||
test_closure_dynamic_2,
|
||||
function_closure_dynamic_2(i32, i64) -> i64,
|
||||
(1, 2) == Ok(1 + 2 + shift as i64 + SHIFT as i64)
|
||||
);
|
||||
test!(
|
||||
$backend,
|
||||
test_closure_dynamic_3,
|
||||
function_closure_dynamic_3(i32, i64, f32) -> f32,
|
||||
(1, 2, 3.) == Ok(1. + 2. + 3. + shift as f32 + SHIFT as f32)
|
||||
);
|
||||
test!(
|
||||
$backend,
|
||||
test_closure_dynamic_4,
|
||||
function_closure_dynamic_4(i32, i64, f32, f64) -> f64,
|
||||
(1, 2, 3., 4.) == Ok(1. + 2. + 3. + 4. + shift as f64 + SHIFT as f64)
|
||||
);
|
||||
test!(
|
||||
$backend,
|
||||
test_closure_with_env,
|
||||
function_closure_with_env(i32) -> i32,
|
||||
(1) == Ok(2 + shift + SHIFT)
|
||||
);
|
||||
test!($backend, test_fn_with_vmctx, function_fn_with_vmctx(i32) -> i32, (1) == Ok(2 + SHIFT));
|
||||
test!(
|
||||
$backend,
|
||||
test_closure_with_vmctx,
|
||||
function_closure_with_vmctx(i32) -> i32,
|
||||
(1) == Ok(2 + SHIFT)
|
||||
);
|
||||
test!(
|
||||
$backend,
|
||||
test_closure_with_vmctx_and_env,
|
||||
function_closure_with_vmctx_and_env(i32) -> i32,
|
||||
(1) == Ok(2 + shift + SHIFT)
|
||||
);
|
||||
test!(
|
||||
$backend,
|
||||
test_fn_trap,
|
||||
function_fn_trap(i32) -> i32,
|
||||
(1) == Err(RuntimeError(Box::new(format!("foo {}", 2))))
|
||||
);
|
||||
test!(
|
||||
$backend,
|
||||
test_closure_trap,
|
||||
function_closure_trap(i32) -> i32,
|
||||
(1) == Err(RuntimeError(Box::new(format!("bar {}", 2))))
|
||||
);
|
||||
test!(
|
||||
$backend,
|
||||
test_fn_trap_with_vmctx,
|
||||
function_fn_trap_with_vmctx(i32) -> i32,
|
||||
(1) == Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT))))
|
||||
);
|
||||
test!(
|
||||
$backend,
|
||||
test_closure_trap_with_vmctx,
|
||||
function_closure_trap_with_vmctx(i32) -> i32,
|
||||
(1) == Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT))))
|
||||
);
|
||||
test!(
|
||||
$backend,
|
||||
test_closure_trap_with_vmctx_and_env,
|
||||
function_closure_trap_with_vmctx_and_env(i32) -> i32,
|
||||
(1) == Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT))))
|
||||
);
|
||||
wasmer_backends! {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn runtime_core_new_api() {
|
||||
runtime_core_new_api_works($backend)
|
||||
}
|
||||
test!( test_fn, function_fn(i32) -> i32, (1) == Ok(2));
|
||||
test!( test_closure, function_closure(i32) -> i32, (1) == Ok(2));
|
||||
test!( test_fn_dynamic, function_fn_dynamic(i32) -> i32, (1) == Ok(2));
|
||||
test!( test_fn_dynamic_panic, function_fn_dynamic_panic(i32) -> i32, (1) == Err(RuntimeError(Box::new("test"))));
|
||||
test!(
|
||||
|
||||
}
|
||||
test_closure_dynamic_0,
|
||||
function_closure_dynamic_0(()) -> (),
|
||||
() == Ok(())
|
||||
);
|
||||
test!(
|
||||
|
||||
test_closure_dynamic_1,
|
||||
function_closure_dynamic_1(i32) -> i32,
|
||||
(1) == Ok(1 + shift + SHIFT)
|
||||
);
|
||||
test!(
|
||||
|
||||
test_closure_dynamic_2,
|
||||
function_closure_dynamic_2(i32, i64) -> i64,
|
||||
(1, 2) == Ok(1 + 2 + shift as i64 + SHIFT as i64)
|
||||
);
|
||||
test!(
|
||||
|
||||
test_closure_dynamic_3,
|
||||
function_closure_dynamic_3(i32, i64, f32) -> f32,
|
||||
(1, 2, 3.) == Ok(1. + 2. + 3. + shift as f32 + SHIFT as f32)
|
||||
);
|
||||
test!(
|
||||
|
||||
test_closure_dynamic_4,
|
||||
function_closure_dynamic_4(i32, i64, f32, f64) -> f64,
|
||||
(1, 2, 3., 4.) == Ok(1. + 2. + 3. + 4. + shift as f64 + SHIFT as f64)
|
||||
);
|
||||
test!(
|
||||
|
||||
test_closure_with_env,
|
||||
function_closure_with_env(i32) -> i32,
|
||||
(1) == Ok(2 + shift + SHIFT)
|
||||
);
|
||||
test!( test_fn_with_vmctx, function_fn_with_vmctx(i32) -> i32, (1) == Ok(2 + SHIFT));
|
||||
test!(
|
||||
|
||||
test_closure_with_vmctx,
|
||||
function_closure_with_vmctx(i32) -> i32,
|
||||
(1) == Ok(2 + SHIFT)
|
||||
);
|
||||
test!(
|
||||
|
||||
test_closure_with_vmctx_and_env,
|
||||
function_closure_with_vmctx_and_env(i32) -> i32,
|
||||
(1) == Ok(2 + shift + SHIFT)
|
||||
);
|
||||
test!(
|
||||
|
||||
test_fn_trap,
|
||||
function_fn_trap(i32) -> i32,
|
||||
(1) == Err(RuntimeError(Box::new(format!("foo {}", 2))))
|
||||
);
|
||||
test!(
|
||||
|
||||
test_closure_trap,
|
||||
function_closure_trap(i32) -> i32,
|
||||
(1) == Err(RuntimeError(Box::new(format!("bar {}", 2))))
|
||||
);
|
||||
test!(
|
||||
|
||||
test_fn_trap_with_vmctx,
|
||||
function_fn_trap_with_vmctx(i32) -> i32,
|
||||
(1) == Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT))))
|
||||
);
|
||||
test!(
|
||||
|
||||
test_closure_trap_with_vmctx,
|
||||
function_closure_trap_with_vmctx(i32) -> i32,
|
||||
(1) == Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT))))
|
||||
);
|
||||
test!(
|
||||
|
||||
test_closure_trap_with_vmctx_and_env,
|
||||
function_closure_trap_with_vmctx_and_env(i32) -> i32,
|
||||
(1) == Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT))))
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn runtime_core_new_api() {
|
||||
runtime_core_new_api_works(get_backend())
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend-singlepass")]
|
||||
#[cfg(test)]
|
||||
mod singlepass {
|
||||
use super::*;
|
||||
tests_for_backend!(Backend::Singlepass);
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend-cranelift")]
|
||||
#[cfg(test)]
|
||||
mod cranelift {
|
||||
use super::*;
|
||||
tests_for_backend!(Backend::Cranelift);
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend-llvm")]
|
||||
#[cfg(test)]
|
||||
mod llvm {
|
||||
use super::*;
|
||||
tests_for_backend!(Backend::LLVM);
|
||||
}
|
||||
|
@ -9,79 +9,82 @@
|
||||
unreachable_patterns
|
||||
)]
|
||||
|
||||
use wabt::wat2wasm;
|
||||
use wasmer::compiler::Compiler;
|
||||
use wasmer_llvm_backend::LLVMCompiler;
|
||||
mod llvm {
|
||||
use wabt::wat2wasm;
|
||||
use wasmer::compiler::Compiler;
|
||||
use wasmer::compiler::CompilerConfig;
|
||||
use wasmer::compiler::{compile_with, compile_with_config_with, BackendCompilerConfig};
|
||||
use wasmer::imports;
|
||||
use wasmer_llvm_backend::LLVMCompiler;
|
||||
use wasmer_llvm_backend::{InkwellModule, LLVMBackendConfig, LLVMCallbacks};
|
||||
|
||||
pub fn get_compiler() -> impl Compiler {
|
||||
LLVMCompiler::new()
|
||||
}
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use wasmer::compiler::CompilerConfig;
|
||||
use wasmer::compiler::{compile_with, compile_with_config_with, BackendCompilerConfig};
|
||||
use wasmer::imports;
|
||||
use wasmer_llvm_backend::{InkwellModule, LLVMBackendConfig, LLVMCallbacks};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
fn crash_return_with_float_on_stack() {
|
||||
const MODULE: &str = r#"
|
||||
(module
|
||||
(type (func))
|
||||
(type (func (param f64) (result f64)))
|
||||
(func $_start (type 0))
|
||||
(func $fmod (type 1) (param f64) (result f64)
|
||||
local.get 0
|
||||
f64.const 0x0p+0
|
||||
f64.mul
|
||||
return))
|
||||
"#;
|
||||
let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed");
|
||||
let module = compile_with(&wasm_binary, &get_compiler()).unwrap();
|
||||
module.instantiate(&imports! {}).unwrap();
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RecordPreOptIR {
|
||||
preopt_ir: String,
|
||||
}
|
||||
|
||||
impl LLVMCallbacks for RecordPreOptIR {
|
||||
fn preopt_ir_callback(&mut self, module: &InkwellModule) {
|
||||
self.preopt_ir = module.print_to_string().to_string();
|
||||
pub fn get_compiler() -> impl Compiler {
|
||||
LLVMCompiler::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crash_select_with_mismatched_pending() {
|
||||
const WAT: &str = r#"
|
||||
(module
|
||||
(func (param f64) (result f64)
|
||||
f64.const 0x0p+0
|
||||
local.get 0
|
||||
f64.add
|
||||
f64.const 0x0p+0
|
||||
i32.const 0
|
||||
select))
|
||||
"#;
|
||||
let record_pre_opt_ir = Rc::new(RefCell::new(RecordPreOptIR::default()));
|
||||
let compiler_config = CompilerConfig {
|
||||
backend_specific_config: Some(BackendCompilerConfig(Box::new(LLVMBackendConfig {
|
||||
callbacks: Some(record_pre_opt_ir.clone()),
|
||||
}))),
|
||||
..Default::default()
|
||||
};
|
||||
let wasm_binary = wat2wasm(WAT.as_bytes()).expect("WAST not valid or malformed");
|
||||
let module = compile_with_config_with(&wasm_binary, compiler_config, &get_compiler()).unwrap();
|
||||
module.instantiate(&imports! {}).unwrap();
|
||||
const LLVM: &str = r#"
|
||||
#[test]
|
||||
fn crash_return_with_float_on_stack() {
|
||||
const MODULE: &str = r#"
|
||||
(module
|
||||
(type (func))
|
||||
(type (func (param f64) (result f64)))
|
||||
(func $_start (type 0))
|
||||
(func $fmod (type 1) (param f64) (result f64)
|
||||
local.get 0
|
||||
f64.const 0x0p+0
|
||||
f64.mul
|
||||
return))
|
||||
"#;
|
||||
let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed");
|
||||
let module = compile_with(&wasm_binary, &get_compiler()).unwrap();
|
||||
module.instantiate(&imports! {}).unwrap();
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RecordPreOptIR {
|
||||
preopt_ir: String,
|
||||
}
|
||||
|
||||
impl LLVMCallbacks for RecordPreOptIR {
|
||||
fn preopt_ir_callback(&mut self, module: &InkwellModule) {
|
||||
self.preopt_ir = module.print_to_string().to_string();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crash_select_with_mismatched_pending() {
|
||||
const WAT: &str = r#"
|
||||
(module
|
||||
(func (param f64) (result f64)
|
||||
f64.const 0x0p+0
|
||||
local.get 0
|
||||
f64.add
|
||||
f64.const 0x0p+0
|
||||
i32.const 0
|
||||
select))
|
||||
"#;
|
||||
let record_pre_opt_ir = Rc::new(RefCell::new(RecordPreOptIR::default()));
|
||||
let compiler_config = CompilerConfig {
|
||||
backend_specific_config: Some(BackendCompilerConfig(Box::new(LLVMBackendConfig {
|
||||
callbacks: Some(record_pre_opt_ir.clone()),
|
||||
}))),
|
||||
..Default::default()
|
||||
};
|
||||
let wasm_binary = wat2wasm(WAT.as_bytes()).expect("WAST not valid or malformed");
|
||||
let module =
|
||||
compile_with_config_with(&wasm_binary, compiler_config, &get_compiler()).unwrap();
|
||||
module.instantiate(&imports! {}).unwrap();
|
||||
const LLVM: &str = r#"
|
||||
%s3 = fadd double 0.000000e+00, %s2
|
||||
%nan = fcmp uno double %s3, 0.000000e+00
|
||||
%2 = select i1 %nan, double 0x7FF8000000000000, double %s3
|
||||
%s5 = select i1 false, double %2, double 0.000000e+00
|
||||
br label %return
|
||||
"#;
|
||||
assert!(&record_pre_opt_ir.borrow().preopt_ir.contains(LLVM));
|
||||
// println!("Compiler IR {}", record_pre_opt_ir.borrow().preopt_ir);
|
||||
assert!(&record_pre_opt_ir.borrow().preopt_ir.contains(LLVM));
|
||||
}
|
||||
}
|
||||
|
@ -1,64 +0,0 @@
|
||||
use wabt::wat2wasm;
|
||||
use wasmer::compiler::compile;
|
||||
use wasmer::{
|
||||
import::ImportObject,
|
||||
wasm::{Instance, Value},
|
||||
DynFunc,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn override_works() {
|
||||
let instance = create_module_1();
|
||||
let result = instance
|
||||
.exports
|
||||
.get::<DynFunc>("call-overwritten-element")
|
||||
.unwrap()
|
||||
.call(&[])
|
||||
.unwrap();
|
||||
assert_eq!(result, vec![Value::I32(66)]);
|
||||
println!("result: {:?}", result);
|
||||
}
|
||||
|
||||
fn create_module_1() -> Instance {
|
||||
let module_str = r#"(module
|
||||
(type (;0;) (func (result i32)))
|
||||
(import "spectest" "table" (table (;0;) 10 anyfunc))
|
||||
(func (;0;) (type 0) (result i32)
|
||||
i32.const 65)
|
||||
(func (;1;) (type 0) (result i32)
|
||||
i32.const 66)
|
||||
(func (;2;) (type 0) (result i32)
|
||||
i32.const 9
|
||||
call_indirect (type 0))
|
||||
(export "call-overwritten-element" (func 2))
|
||||
(elem (;0;) (i32.const 9) 0)
|
||||
(elem (;1;) (i32.const 9) 1))
|
||||
"#;
|
||||
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||
let module = compile(&wasm_binary[..]).expect("WASM can't be compiled");
|
||||
module
|
||||
.instantiate(&generate_imports())
|
||||
.expect("WASM can't be instantiated")
|
||||
}
|
||||
|
||||
static IMPORT_MODULE: &str = r#"
|
||||
(module
|
||||
(type $t0 (func (param i32)))
|
||||
(type $t1 (func))
|
||||
(func $print_i32 (export "print_i32") (type $t0) (param $lhs i32))
|
||||
(func $print (export "print") (type $t1))
|
||||
(table $table (export "table") 10 20 anyfunc)
|
||||
(memory $memory (export "memory") 1 2)
|
||||
(global $global_i32 (export "global_i32") i32 (i32.const 666)))
|
||||
"#;
|
||||
|
||||
pub fn generate_imports() -> ImportObject {
|
||||
let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed");
|
||||
let module = compile(&wasm_binary[..]).expect("WASM can't be compiled");
|
||||
let instance = module
|
||||
.instantiate(&ImportObject::new())
|
||||
.expect("WASM can't be instantiated");
|
||||
let mut imports = ImportObject::new();
|
||||
imports.register("spectest", instance);
|
||||
imports
|
||||
}
|
@ -1,14 +1,6 @@
|
||||
mod utils;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(not(any(
|
||||
feature = "backend-llvm",
|
||||
feature = "backend-cranelift",
|
||||
feature = "backend-singlepass"
|
||||
)))]
|
||||
compile_error!("No compiler backend detected: please specify at least one compiler backend!");
|
||||
|
||||
use wasmer_wast::Wast;
|
||||
|
||||
// The generated tests (from build.rs) look like:
|
||||
|
@ -1,39 +0,0 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use wabt::wat2wasm;
|
||||
use wasmer::error::{CallError, ExceptionCode, RuntimeError};
|
||||
use wasmer::import::ImportObject;
|
||||
|
||||
// The semantics of stack overflow are documented at:
|
||||
// https://webassembly.org/docs/semantics/#stack-overflow
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_stack_overflow() {
|
||||
let module_str = r#"(module
|
||||
(type (;0;) (func))
|
||||
(func (;0;) (type 0)
|
||||
i32.const 0
|
||||
call_indirect (type 0))
|
||||
(table (;0;) 1 anyfunc)
|
||||
(export "stack-overflow" (func 0))
|
||||
(elem (;0;) (i32.const 0) 0))
|
||||
"#;
|
||||
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||
let module = wasmer::compiler::compile(&wasm_binary[..]).expect("WASM can't be compiled");
|
||||
let instance = module
|
||||
.instantiate(&ImportObject::new())
|
||||
.expect("WASM can't be instantiated");
|
||||
let result = instance.call("stack-overflow", &[]);
|
||||
|
||||
match result {
|
||||
Err(err) => match err {
|
||||
CallError::Runtime(RuntimeError(e)) => {
|
||||
e.downcast::<ExceptionCode>()
|
||||
.expect("expecting exception code");
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
Ok(_) => panic!("should fail with error due to stack overflow"),
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ use wasmer::compiler::Backend;
|
||||
///
|
||||
/// This function errors if the backend doesn't exist or
|
||||
/// is not enabled.
|
||||
#[allow(dead_code)]
|
||||
pub fn get_backend_from_str(backend: &str) -> Result<Backend> {
|
||||
match backend {
|
||||
#[cfg(feature = "backend-singlepass")]
|
||||
|
47
tests/utils/macros.rs
Normal file
47
tests/utils/macros.rs
Normal file
@ -0,0 +1,47 @@
|
||||
#[macro_export]
|
||||
macro_rules! wasmer_backends {
|
||||
{ $($code:item)* } => {
|
||||
#[cfg(feature = "backend-singlepass")]
|
||||
#[cfg(test)]
|
||||
mod singlepass {
|
||||
use wasmer::compiler::{Backend, Compiler, compiler_for_backend};
|
||||
#[allow(dead_code)]
|
||||
fn get_backend() -> Backend {
|
||||
Backend::Singlepass
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
fn get_compiler() -> Box<dyn Compiler> {
|
||||
compiler_for_backend(get_backend()).expect("Backend must have a compiler")
|
||||
}
|
||||
$($code)*
|
||||
}
|
||||
#[cfg(feature = "backend-cranelift")]
|
||||
#[cfg(test)]
|
||||
mod cranelift {
|
||||
use wasmer::compiler::{Backend, Compiler, compiler_for_backend};
|
||||
#[allow(dead_code)]
|
||||
fn get_backend() -> Backend {
|
||||
Backend::Cranelift
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
fn get_compiler() -> Box<dyn Compiler> {
|
||||
compiler_for_backend(get_backend()).expect("Backend must have a compiler")
|
||||
}
|
||||
$($code)*
|
||||
}
|
||||
#[cfg(feature = "backend-llvm")]
|
||||
#[cfg(test)]
|
||||
mod llvm {
|
||||
use wasmer::compiler::{Backend, Compiler, compiler_for_backend};
|
||||
#[allow(dead_code)]
|
||||
fn get_backend() -> Backend {
|
||||
Backend::LLVM
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
fn get_compiler() -> Box<dyn Compiler> {
|
||||
compiler_for_backend(get_backend()).expect("Backend must have a compiler")
|
||||
}
|
||||
$($code)*
|
||||
}
|
||||
};
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
mod backend;
|
||||
mod file_descriptor;
|
||||
mod stdio;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub use backend::get_backend_from_str;
|
||||
pub use stdio::StdioCapturer;
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use super::file_descriptor::FileDescriptor;
|
||||
use libc;
|
||||
use std::io;
|
||||
|
@ -1,65 +1,70 @@
|
||||
#![cfg(test)]
|
||||
use wasmer::{compiler::compile, vm::Ctx, Func};
|
||||
use wasmer_wasi::{state::*, *};
|
||||
|
||||
use std::ffi::c_void;
|
||||
#[macro_use]
|
||||
mod utils;
|
||||
|
||||
// TODO: fix this test!
|
||||
#[ignore]
|
||||
#[cfg(not(feature = "singlepass"))]
|
||||
#[test]
|
||||
fn serializing_works() {
|
||||
let args = vec![
|
||||
b"program_name".into_iter().cloned().collect(),
|
||||
b"arg1".into_iter().cloned().collect(),
|
||||
];
|
||||
let envs = vec![
|
||||
b"PATH=/bin".into_iter().cloned().collect(),
|
||||
b"GOROOT=$HOME/.cargo/bin".into_iter().cloned().collect(),
|
||||
];
|
||||
let wasm_binary = include_bytes!("wasi_test_resources/unstable/fd_read.wasm");
|
||||
let module = compile(&wasm_binary[..])
|
||||
.map_err(|e| format!("Can't compile module: {:?}", e))
|
||||
.unwrap();
|
||||
wasmer_backends! {
|
||||
use wasmer::{compiler::compile_with, vm::Ctx, Func};
|
||||
use wasmer_wasi::{state::*, *};
|
||||
use std::ffi::c_void;
|
||||
|
||||
let wasi_version = get_wasi_version(&module, true).expect("WASI module");
|
||||
let import_object = generate_import_object_for_version(
|
||||
wasi_version,
|
||||
args.clone(),
|
||||
envs.clone(),
|
||||
vec![],
|
||||
vec![(
|
||||
".".to_string(),
|
||||
std::path::PathBuf::from("wasi_test_resources/test_fs/hamlet"),
|
||||
)],
|
||||
);
|
||||
// TODO: fix this test!
|
||||
#[ignore]
|
||||
#[cfg(not(feature = "singlepass"))]
|
||||
#[test]
|
||||
fn serializing_works() {
|
||||
let args = vec![
|
||||
b"program_name".into_iter().cloned().collect(),
|
||||
b"arg1".into_iter().cloned().collect(),
|
||||
];
|
||||
let envs = vec![
|
||||
b"PATH=/bin".into_iter().cloned().collect(),
|
||||
b"GOROOT=$HOME/.cargo/bin".into_iter().cloned().collect(),
|
||||
];
|
||||
let wasm_binary = include_bytes!("wasi_test_resources/unstable/fd_read.wasm");
|
||||
let module = compile_with(&wasm_binary[..], &*get_compiler())
|
||||
.map_err(|e| format!("Can't compile module: {:?}", e))
|
||||
.unwrap();
|
||||
|
||||
let state_bytes = {
|
||||
let instance = module.instantiate(&import_object).unwrap();
|
||||
let wasi_version = get_wasi_version(&module, true).expect("WASI module");
|
||||
let import_object = generate_import_object_for_version(
|
||||
wasi_version,
|
||||
args.clone(),
|
||||
envs.clone(),
|
||||
vec![],
|
||||
vec![(
|
||||
".".to_string(),
|
||||
std::path::PathBuf::from("wasi_test_resources/test_fs/hamlet"),
|
||||
)],
|
||||
);
|
||||
|
||||
let start: Func<(), ()> = instance.exports.get("_start").unwrap();
|
||||
start.call().unwrap();
|
||||
let state = get_wasi_state(instance.context());
|
||||
let state_bytes = {
|
||||
let instance = module.instantiate(&import_object).unwrap();
|
||||
|
||||
assert_eq!(state.args, args);
|
||||
assert_eq!(state.envs, envs);
|
||||
let bytes = state.freeze().unwrap();
|
||||
let start: Func<(), ()> = instance.exports.get("_start").unwrap();
|
||||
start.call().unwrap();
|
||||
let state = get_wasi_state(instance.context());
|
||||
|
||||
bytes
|
||||
};
|
||||
assert_eq!(state.args, args);
|
||||
assert_eq!(state.envs, envs);
|
||||
let bytes = state.freeze().unwrap();
|
||||
|
||||
let mut instance = module.instantiate(&import_object).unwrap();
|
||||
bytes
|
||||
};
|
||||
|
||||
let wasi_state = Box::new(WasiState::unfreeze(&state_bytes).unwrap());
|
||||
let mut instance = module.instantiate(&import_object).unwrap();
|
||||
|
||||
instance.context_mut().data = Box::into_raw(wasi_state) as *mut c_void;
|
||||
let wasi_state = Box::new(WasiState::unfreeze(&state_bytes).unwrap());
|
||||
|
||||
let second_entry: Func<(), i32> = instance.exports.get("second_entry").unwrap();
|
||||
let result = second_entry.call().unwrap();
|
||||
assert_eq!(result, true as i32);
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) fn get_wasi_state(ctx: &Ctx) -> &mut WasiState {
|
||||
unsafe { state::get_wasi_state(&mut *(ctx as *const Ctx as *mut Ctx)) }
|
||||
instance.context_mut().data = Box::into_raw(wasi_state) as *mut c_void;
|
||||
|
||||
let second_entry: Func<(), i32> = instance.exports.get("second_entry").unwrap();
|
||||
let result = second_entry.call().unwrap();
|
||||
assert_eq!(result, true as i32);
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) fn get_wasi_state(ctx: &Ctx) -> &mut WasiState {
|
||||
unsafe { state::get_wasi_state(&mut *(ctx as *const Ctx as *mut Ctx)) }
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user