Improved tests to use available compilers

This commit is contained in:
Syrus 2020-04-16 11:52:11 -07:00
parent 5f5928dfbd
commit b301ac85be
14 changed files with 428 additions and 467 deletions

View File

@ -96,7 +96,7 @@ wabt = "0.9.1"
# Don't add the backend features in default, please add them on the Makefile # 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. # since we might want to autoconfigure them depending on the availability on the host.
default = ["wasi", "wabt"] default = ["wasi", "wabt"]
"loader-kernel" = ["wasmer-kernel-loader"] loader-kernel = ["wasmer-kernel-loader"]
debug = ["fern", "log/max_level_debug", "log/release_max_level_debug"] debug = ["fern", "log/max_level_debug", "log/release_max_level_debug"]
trace = ["fern", "log/max_level_trace", "log/release_max_level_trace"] trace = ["fern", "log/max_level_trace", "log/release_max_level_trace"]
docs = ["wasmer-runtime/docs"] docs = ["wasmer-runtime/docs"]

View File

@ -351,6 +351,10 @@ pub mod error {
/// Idea for generic trait; consider rename; it will need to be moved somewhere else /// Idea for generic trait; consider rename; it will need to be moved somewhere else
pub trait CompiledModule { pub trait CompiledModule {
fn new(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module>; 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(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module>;
fn from_binary_unchecked(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>; 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()) 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> { fn from_binary(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module> {
let bytes = bytes.as_ref(); let bytes = bytes.as_ref();
wasmer_runtime_core::compile_with(bytes, &compiler::default_compiler()) wasmer_runtime_core::compile_with(bytes, &compiler::default_compiler())

View 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"))

View File

@ -1,3 +1,8 @@
#![cfg(test)]
#[macro_use]
mod utils;
static TEST_WAT: &str = r#" static TEST_WAT: &str = r#"
(module (module
(import "env" "outside-global" (global $outside-global (mut i32))) (import "env" "outside-global" (global $outside-global (mut i32)))
@ -47,151 +52,155 @@ fn append_custom_section(
wasm.extend(custom_section_contents); wasm.extend(custom_section_contents);
} }
#[test] wasmer_backends! {
fn custom_section_parsing_works() { use super::{TEST_WAT, append_custom_section};
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
};
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!( let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
module.custom_sections("test_custom_section"),
Some(&[b"hello, world!".to_vec(), b"goodbye, world!".to_vec()][..])
);
}
#[test] assert_eq!(
fn module_exports_are_ordered() { module.custom_sections("test_custom_section"),
use wasmer::types::{ElementType, FuncSig, GlobalDescriptor, TableDescriptor, Type}; Some(&[b"hello, world!".to_vec(), b"goodbye, world!".to_vec()][..])
use wasmer::{export, CompiledModule, Module}; );
}
let wasm = wabt::wat2wasm(TEST_WAT).unwrap(); #[test]
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be fn module_exports_are_ordered() {
// misleading, if so we may want to do something about it. use wasmer::types::{ElementType, FuncSig, GlobalDescriptor, TableDescriptor, Type};
let module = Module::new(wasm).unwrap(); use wasmer::{export, CompiledModule, Module};
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] let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
fn module_imports_are_correct() { // TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
use wasmer::{CompiledModule, Module}; // 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(); #[test]
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be fn module_imports_are_correct() {
// misleading, if so we may want to do something about it. use wasmer::{CompiledModule, Module};
let module = Module::new(wasm).unwrap();
// TODO: test this more later let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
let imports = module.imports(); // TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
assert_eq!(imports.len(), 2); // misleading, if so we may want to do something about it.
} let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
#[test] // TODO: test this more later
fn module_can_be_instantiated() { let imports = module.imports();
use wasmer::wasm::{Global, Value}; assert_eq!(imports.len(), 2);
use wasmer::{func, imports, CompiledModule, Module}; }
let wasm = wabt::wat2wasm(TEST_WAT).unwrap(); #[test]
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be fn module_can_be_instantiated() {
// misleading, if so we may want to do something about it. use wasmer::wasm::{Global, Value};
let module = Module::new(wasm).unwrap(); use wasmer::{func, imports, CompiledModule, Module};
let outside_global = Global::new_mutable(Value::I32(15)); let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
let import_object = imports! { // TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
"env" => { // misleading, if so we may want to do something about it.
"update-func" => func!(|x: i32| x * 2), let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
"outside-global" => outside_global.clone(),
}
};
let instance = module.instantiate(&import_object);
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] assert!(instance.is_ok());
fn exports_work() { }
use wasmer::wasm::{Global, Value};
use wasmer::{func, imports, CompiledModule, Func, Module, Table};
let wasm = wabt::wat2wasm(TEST_WAT).unwrap(); #[test]
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be fn exports_work() {
// misleading, if so we may want to do something about it. use wasmer::wasm::{Global, Value};
let module = Module::new(wasm).unwrap(); 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! { let outside_global = Global::new_mutable(Value::I32(15));
"env" => {
"update-func" => func!(|x: i32| x * 2),
"outside-global" => outside_global.clone(),
}
};
let instance = module.instantiate(&import_object).unwrap();
let ret_2: Func<(), i32> = instance.exports.get("ret_2").unwrap(); let import_object = imports! {
let ret_4: Func<(), i32> = instance.exports.get("ret_4").unwrap(); "env" => {
let set_test_global: Func<i32> = instance.exports.get("set_test_global").unwrap(); "update-func" => func!(|x: i32| x * 2),
let update_outside_global: Func = instance.exports.get("update_outside_global").unwrap(); "outside-global" => outside_global.clone(),
}
};
let instance = module.instantiate(&import_object).unwrap();
assert_eq!(ret_2.call(), Ok(2)); let ret_2: Func<(), i32> = instance.exports.get("ret_2").unwrap();
assert_eq!(ret_4.call(), Ok(4)); 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(); assert_eq!(ret_2.call(), Ok(2));
// TODO: when table get is stablized test this assert_eq!(ret_4.call(), Ok(4));
let test_global: Global = instance.exports.get("test-global").unwrap(); let _test_table: Table = instance.exports.get("test-table").unwrap();
// TODO: do we want to make global.get act like exports.get()? // TODO: when table get is stablized test this
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)); let test_global: Global = instance.exports.get("test-global").unwrap();
update_outside_global.call().unwrap(); // TODO: do we want to make global.get act like exports.get()?
assert_eq!(outside_global.get(), Value::I32(15 * 2)); assert_eq!(test_global.get(), Value::I32(3));
update_outside_global.call().unwrap(); set_test_global.call(17).unwrap();
assert_eq!(outside_global.get(), Value::I32(15 * 2 * 2)); 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 // copied from Rust stdlib: https://doc.rust-lang.org/nightly/nightly-rustc/src/serialize/leb128.rs.html#4-14

View File

@ -1,3 +1,6 @@
#[macro_use]
mod utils;
use std::{convert::TryInto, sync::Arc}; use std::{convert::TryInto, sync::Arc};
use wabt::wat2wasm; use wabt::wat2wasm;
use wasmer::compiler::{compile_with, compiler_for_backend, Backend}; 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 { 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"))] #[cfg(all(unix, target_arch = "x86_64"))]
#[test] #[test]
fn $test_name() { fn $test_name() {
imported_functions_forms($backend, &|instance| { imported_functions_forms(get_backend(), &|instance| {
call_and_assert!(instance, $function( $( $inputs ),* ) -> $output, ( $( $arguments ),* ) == $expected_value); call_and_assert!(instance, $function( $( $inputs ),* ) -> $output, ( $( $arguments ),* ) == $expected_value);
}); });
} }
};
} }
}
macro_rules! tests_for_backend { wasmer_backends! {
($backend:expr) => { use super::*;
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))))
);
#[test] test!( test_fn, function_fn(i32) -> i32, (1) == Ok(2));
fn runtime_core_new_api() { test!( test_closure, function_closure(i32) -> i32, (1) == Ok(2));
runtime_core_new_api_works($backend) 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);
} }

View File

@ -9,79 +9,82 @@
unreachable_patterns unreachable_patterns
)] )]
use wabt::wat2wasm; mod llvm {
use wasmer::compiler::Compiler; use wabt::wat2wasm;
use wasmer_llvm_backend::LLVMCompiler; 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 { use std::cell::RefCell;
LLVMCompiler::new() use std::rc::Rc;
}
use wasmer::compiler::CompilerConfig; pub fn get_compiler() -> impl Compiler {
use wasmer::compiler::{compile_with, compile_with_config_with, BackendCompilerConfig}; LLVMCompiler::new()
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();
} }
}
#[test] #[test]
fn crash_select_with_mismatched_pending() { fn crash_return_with_float_on_stack() {
const WAT: &str = r#" const MODULE: &str = r#"
(module (module
(func (param f64) (result f64) (type (func))
f64.const 0x0p+0 (type (func (param f64) (result f64)))
local.get 0 (func $_start (type 0))
f64.add (func $fmod (type 1) (param f64) (result f64)
f64.const 0x0p+0 local.get 0
i32.const 0 f64.const 0x0p+0
select)) f64.mul
"#; return))
let record_pre_opt_ir = Rc::new(RefCell::new(RecordPreOptIR::default())); "#;
let compiler_config = CompilerConfig { let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed");
backend_specific_config: Some(BackendCompilerConfig(Box::new(LLVMBackendConfig { let module = compile_with(&wasm_binary, &get_compiler()).unwrap();
callbacks: Some(record_pre_opt_ir.clone()), module.instantiate(&imports! {}).unwrap();
}))), }
..Default::default()
}; #[derive(Debug, Default)]
let wasm_binary = wat2wasm(WAT.as_bytes()).expect("WAST not valid or malformed"); pub struct RecordPreOptIR {
let module = compile_with_config_with(&wasm_binary, compiler_config, &get_compiler()).unwrap(); preopt_ir: String,
module.instantiate(&imports! {}).unwrap(); }
const LLVM: &str = r#"
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 %s3 = fadd double 0.000000e+00, %s2
%nan = fcmp uno double %s3, 0.000000e+00 %nan = fcmp uno double %s3, 0.000000e+00
%2 = select i1 %nan, double 0x7FF8000000000000, double %s3 %2 = select i1 %nan, double 0x7FF8000000000000, double %s3
%s5 = select i1 false, double %2, double 0.000000e+00 %s5 = select i1 false, double %2, double 0.000000e+00
br label %return 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));
}
} }

View File

@ -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
}

View File

@ -1,14 +1,6 @@
mod utils; mod utils;
use std::path::Path; 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; use wasmer_wast::Wast;
// The generated tests (from build.rs) look like: // The generated tests (from build.rs) look like:

View File

@ -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"),
}
}
}

View File

@ -7,6 +7,7 @@ use wasmer::compiler::Backend;
/// ///
/// This function errors if the backend doesn't exist or /// This function errors if the backend doesn't exist or
/// is not enabled. /// is not enabled.
#[allow(dead_code)]
pub fn get_backend_from_str(backend: &str) -> Result<Backend> { pub fn get_backend_from_str(backend: &str) -> Result<Backend> {
match backend { match backend {
#[cfg(feature = "backend-singlepass")] #[cfg(feature = "backend-singlepass")]

47
tests/utils/macros.rs Normal file
View 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)*
}
};
}

View File

@ -1,6 +1,8 @@
mod backend; mod backend;
mod file_descriptor; mod file_descriptor;
mod stdio; mod stdio;
#[macro_use]
mod macros;
pub use backend::get_backend_from_str; pub use backend::get_backend_from_str;
pub use stdio::StdioCapturer; pub use stdio::StdioCapturer;

View File

@ -1,3 +1,5 @@
#![allow(dead_code)]
use super::file_descriptor::FileDescriptor; use super::file_descriptor::FileDescriptor;
use libc; use libc;
use std::io; use std::io;

View File

@ -1,65 +1,70 @@
#![cfg(test)] #![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! wasmer_backends! {
#[ignore] use wasmer::{compiler::compile_with, vm::Ctx, Func};
#[cfg(not(feature = "singlepass"))] use wasmer_wasi::{state::*, *};
#[test] use std::ffi::c_void;
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();
let wasi_version = get_wasi_version(&module, true).expect("WASI module"); // TODO: fix this test!
let import_object = generate_import_object_for_version( #[ignore]
wasi_version, #[cfg(not(feature = "singlepass"))]
args.clone(), #[test]
envs.clone(), fn serializing_works() {
vec![], let args = vec![
vec![( b"program_name".into_iter().cloned().collect(),
".".to_string(), b"arg1".into_iter().cloned().collect(),
std::path::PathBuf::from("wasi_test_resources/test_fs/hamlet"), ];
)], 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 wasi_version = get_wasi_version(&module, true).expect("WASI module");
let instance = module.instantiate(&import_object).unwrap(); 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(); let state_bytes = {
start.call().unwrap(); let instance = module.instantiate(&import_object).unwrap();
let state = get_wasi_state(instance.context());
assert_eq!(state.args, args); let start: Func<(), ()> = instance.exports.get("_start").unwrap();
assert_eq!(state.envs, envs); start.call().unwrap();
let bytes = state.freeze().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(); instance.context_mut().data = Box::into_raw(wasi_state) as *mut c_void;
let result = second_entry.call().unwrap();
assert_eq!(result, true as i32); 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)) } #[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)) }
}
} }