mirror of
https://github.com/mii443/wasmer.git
synced 2025-08-22 16:35:33 +00:00
Compiler Test refactor now fully works
This commit is contained in:
47
Cargo.lock
generated
47
Cargo.lock
generated
@ -345,6 +345,17 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compiler-test-derive"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"pretty_assertions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"trybuild",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compiletest_rs"
|
||||
version = "0.6.0"
|
||||
@ -1363,6 +1374,15 @@ version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85"
|
||||
|
||||
[[package]]
|
||||
name = "output_vt100"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.1"
|
||||
@ -1484,6 +1504,18 @@ dependencies = [
|
||||
"treeline",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"ctor",
|
||||
"difference",
|
||||
"output_vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
@ -2143,6 +2175,20 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
|
||||
|
||||
[[package]]
|
||||
name = "trybuild"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1768998d9a3b179411618e377dbb134c58a88cda284b0aa71c42c40660127d46"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"lazy_static",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"termcolor",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.13.0"
|
||||
@ -2716,6 +2762,7 @@ dependencies = [
|
||||
"blake3",
|
||||
"build-deps",
|
||||
"cfg-if 1.0.0",
|
||||
"compiler-test-derive",
|
||||
"criterion",
|
||||
"glob",
|
||||
"lazy_static",
|
||||
|
@ -49,6 +49,7 @@ members = [
|
||||
"lib/wasi-experimental-io-devices",
|
||||
"lib/types",
|
||||
"tests/lib/wast",
|
||||
"tests/lib/compiler-test-derive",
|
||||
"tests/integration/cli",
|
||||
]
|
||||
exclude = [
|
||||
@ -68,6 +69,7 @@ blake3 = "0.3"
|
||||
criterion = "0.3"
|
||||
lazy_static = "1.4"
|
||||
wasmer-engine-dummy = { path = "tests/lib/engine-dummy" }
|
||||
compiler-test-derive = { path = "tests/lib/compiler-test-derive" }
|
||||
tempfile = "3.1"
|
||||
loupe = "0.1"
|
||||
|
||||
|
63
build.rs
63
build.rs
@ -9,12 +9,11 @@ use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use test_generator::{
|
||||
build_ignores_from_textfile, test_directory, test_directory_module, wasi_processor,
|
||||
wast_processor, with_features, with_test_module, Testsuite,
|
||||
test_directory, test_directory_module, wasi_processor, wast_processor, with_test_module,
|
||||
Testsuite,
|
||||
};
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
println!("cargo:rerun-if-changed=tests/ignores.txt");
|
||||
// As rerun-if-changed doesn't support globs, we use another crate
|
||||
// to check changes in directories.
|
||||
build_deps::rerun_if_changed_paths("tests/wasi-wast/wasi/unstable/*")
|
||||
@ -25,33 +24,27 @@ fn main() -> anyhow::Result<()> {
|
||||
let out_dir = PathBuf::from(
|
||||
env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
|
||||
);
|
||||
let ignores = build_ignores_from_textfile("tests/ignores.txt".into())?;
|
||||
let compilers = ["singlepass", "cranelift", "llvm"];
|
||||
|
||||
// Spectests test generation
|
||||
{
|
||||
let mut spectests = Testsuite {
|
||||
buffer: String::new(),
|
||||
path: vec![],
|
||||
ignores: ignores.clone(),
|
||||
};
|
||||
|
||||
with_features(&mut spectests, &compilers, |mut spectests| {
|
||||
with_test_module(&mut spectests, "spec", |spectests| {
|
||||
let _spec_tests = test_directory(spectests, "tests/wast/spec", wast_processor)?;
|
||||
test_directory_module(
|
||||
spectests,
|
||||
"tests/wast/spec/proposals/multi-value",
|
||||
wast_processor,
|
||||
)?;
|
||||
test_directory_module(spectests, "tests/wast/spec/proposals/simd", wast_processor)?;
|
||||
// test_directory_module(spectests, "tests/wast/spec/proposals/bulk-memory-operations", wast_processor)?;
|
||||
Ok(())
|
||||
})?;
|
||||
with_test_module(&mut spectests, "wasmer", |spectests| {
|
||||
let _spec_tests = test_directory(spectests, "tests/wast/wasmer", wast_processor)?;
|
||||
Ok(())
|
||||
})?;
|
||||
with_test_module(&mut spectests, "spec", |spectests| {
|
||||
let _spec_tests = test_directory(spectests, "tests/wast/spec", wast_processor)?;
|
||||
test_directory_module(
|
||||
spectests,
|
||||
"tests/wast/spec/proposals/multi-value",
|
||||
wast_processor,
|
||||
)?;
|
||||
test_directory_module(spectests, "tests/wast/spec/proposals/simd", wast_processor)?;
|
||||
// test_directory_module(spectests, "tests/wast/spec/proposals/bulk-memory-operations", wast_processor)?;
|
||||
Ok(())
|
||||
})?;
|
||||
with_test_module(&mut spectests, "wasmer", |spectests| {
|
||||
let _spec_tests = test_directory(spectests, "tests/wast/wasmer", wast_processor)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
@ -70,23 +63,19 @@ fn main() -> anyhow::Result<()> {
|
||||
let mut wasitests = Testsuite {
|
||||
buffer: String::new(),
|
||||
path: vec![],
|
||||
ignores,
|
||||
};
|
||||
let wasi_versions = ["unstable", "snapshot1"];
|
||||
with_features(&mut wasitests, &compilers, |mut wasitests| {
|
||||
with_test_module(&mut wasitests, "wasitests", |wasitests| {
|
||||
for wasi_version in &wasi_versions {
|
||||
with_test_module(wasitests, wasi_version, |wasitests| {
|
||||
let _wasi_tests = test_directory(
|
||||
wasitests,
|
||||
format!("tests/wasi-wast/wasi/{}", wasi_version),
|
||||
wasi_processor,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
with_test_module(&mut wasitests, "wasitests", |wasitests| {
|
||||
for wasi_version in &wasi_versions {
|
||||
with_test_module(wasitests, wasi_version, |wasitests| {
|
||||
let _wasi_tests = test_directory(
|
||||
wasitests,
|
||||
format!("tests/wasi-wast/wasi/{}", wasi_version),
|
||||
wasi_processor,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
140
tests/compilers/config.rs
Normal file
140
tests/compilers/config.rs
Normal file
@ -0,0 +1,140 @@
|
||||
use std::sync::Arc;
|
||||
use wasmer::{CompilerConfig, Engine as WasmerEngine, Features, ModuleMiddleware, Store};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Compiler {
|
||||
LLVM,
|
||||
Cranelift,
|
||||
Singlepass,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Engine {
|
||||
Native,
|
||||
JIT,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Config {
|
||||
pub compiler: Compiler,
|
||||
pub engine: Engine,
|
||||
pub features: Features,
|
||||
pub middlewares: Vec<Arc<dyn ModuleMiddleware>>,
|
||||
pub canonicalize_nans: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new(engine: Engine, compiler: Compiler) -> Self {
|
||||
Self {
|
||||
compiler,
|
||||
engine,
|
||||
features: Default::default(),
|
||||
canonicalize_nans: false,
|
||||
middlewares: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_middlewares(&mut self, middlewares: Vec<Arc<dyn ModuleMiddleware>>) {
|
||||
self.middlewares = middlewares;
|
||||
}
|
||||
|
||||
pub fn set_features(&mut self, features: Features) {
|
||||
self.features = features;
|
||||
}
|
||||
|
||||
pub fn set_nan_canonicalization(&mut self, canonicalize_nans: bool) {
|
||||
self.canonicalize_nans = canonicalize_nans;
|
||||
}
|
||||
|
||||
pub fn store(&self) -> Store {
|
||||
let compiler_config = self.compiler_config(self.canonicalize_nans);
|
||||
let engine = self.engine(compiler_config);
|
||||
Store::new(&*engine)
|
||||
}
|
||||
|
||||
pub fn headless_store(&self) -> Store {
|
||||
let engine = self.engine_headless();
|
||||
Store::new(&*engine)
|
||||
}
|
||||
|
||||
pub fn engine(&self, compiler_config: Box<dyn CompilerConfig>) -> Box<dyn WasmerEngine> {
|
||||
#[cfg(not(feature = "engine"))]
|
||||
compile_error!("Plese enable at least one engine via the features");
|
||||
match &self.engine {
|
||||
#[cfg(feature = "native")]
|
||||
Engine::Native => Box::new(
|
||||
wasmer_engine_native::Native::new(compiler_config)
|
||||
.features(self.features.clone())
|
||||
.engine(),
|
||||
),
|
||||
#[cfg(feature = "jit")]
|
||||
Engine::JIT => Box::new(
|
||||
wasmer_engine_jit::JIT::new(compiler_config)
|
||||
.features(self.features.clone())
|
||||
.engine(),
|
||||
),
|
||||
engine => panic!(
|
||||
"The {:?} Engine is not enabled. Please enable it using the features",
|
||||
engine
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn engine_headless(&self) -> Box<dyn WasmerEngine> {
|
||||
match &self.engine {
|
||||
#[cfg(feature = "native")]
|
||||
Engine::Native => Box::new(wasmer_engine_native::Native::headless().engine()),
|
||||
#[cfg(feature = "jit")]
|
||||
Engine::JIT => Box::new(wasmer_engine_jit::JIT::headless().engine()),
|
||||
engine => panic!(
|
||||
"The {:?} Engine is not enabled. Please enable it using the features",
|
||||
engine
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compiler_config(&self, canonicalize_nans: bool) -> Box<dyn CompilerConfig> {
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
compile_error!("Plese enable at least one compiler via the features");
|
||||
|
||||
match &self.compiler {
|
||||
#[cfg(feature = "cranelift")]
|
||||
Compiler::Cranelift => {
|
||||
let mut compiler = wasmer_compiler_cranelift::Cranelift::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler.enable_verifier();
|
||||
self.add_middlewares(&mut compiler);
|
||||
Box::new(compiler)
|
||||
}
|
||||
#[cfg(feature = "llvm")]
|
||||
Compiler::LLVM => {
|
||||
let mut compiler = wasmer_compiler_llvm::LLVM::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler.enable_verifier();
|
||||
self.add_middlewares(&mut compiler);
|
||||
Box::new(compiler)
|
||||
}
|
||||
#[cfg(feature = "singlepass")]
|
||||
Compiler::SinglePass => {
|
||||
let mut compiler = wasmer_compiler_singlepass::Singlepass::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler.enable_verifier();
|
||||
self.add_middlewares(&mut compiler);
|
||||
Box::new(compiler)
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
compiler => {
|
||||
panic!(
|
||||
"The {:?} Compiler is not enabled. Enable it via the features",
|
||||
compiler
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_middlewares(&self, config: &mut dyn CompilerConfig) {
|
||||
for middleware in self.middlewares.iter() {
|
||||
config.push_middleware(middleware.clone());
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
//! This tests checks that the provided functions (both native and
|
||||
//! dynamic ones) work properly.
|
||||
|
||||
use crate::utils::get_store;
|
||||
use anyhow::Result;
|
||||
use std::convert::Infallible;
|
||||
use std::sync::{
|
||||
@ -43,9 +42,9 @@ fn get_module(store: &Store) -> Result<Module> {
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_function() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
#[compiler_test(imports)]
|
||||
fn dynamic_function(config: crate::Config) -> Result<()> {
|
||||
let store = config.store();
|
||||
let module = get_module(&store)?;
|
||||
static HITS: AtomicUsize = AtomicUsize::new(0);
|
||||
Instance::new(
|
||||
@ -83,9 +82,9 @@ fn dynamic_function() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_function_with_env() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
#[compiler_test(imports)]
|
||||
fn dynamic_function_with_env(config: crate::Config) -> Result<()> {
|
||||
let store = config.store();
|
||||
let module = get_module(&store)?;
|
||||
|
||||
#[derive(WasmerEnv, Clone)]
|
||||
@ -138,9 +137,9 @@ fn dynamic_function_with_env() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_function() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
#[compiler_test(imports)]
|
||||
fn static_function(config: crate::Config) -> Result<()> {
|
||||
let store = config.store();
|
||||
let module = get_module(&store)?;
|
||||
|
||||
static HITS: AtomicUsize = AtomicUsize::new(0);
|
||||
@ -176,9 +175,9 @@ fn static_function() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_function_with_results() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
#[compiler_test(imports)]
|
||||
fn static_function_with_results(config: crate::Config) -> Result<()> {
|
||||
let store = config.store();
|
||||
let module = get_module(&store)?;
|
||||
|
||||
static HITS: AtomicUsize = AtomicUsize::new(0);
|
||||
@ -214,9 +213,9 @@ fn static_function_with_results() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_function_with_env() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
#[compiler_test(imports)]
|
||||
fn static_function_with_env(config: crate::Config) -> Result<()> {
|
||||
let store = config.store();
|
||||
let module = get_module(&store)?;
|
||||
|
||||
#[derive(WasmerEnv, Clone)]
|
||||
@ -262,9 +261,9 @@ fn static_function_with_env() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_function_that_fails() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
#[compiler_test(imports)]
|
||||
fn static_function_that_fails(config: crate::Config) -> Result<()> {
|
||||
let store = config.store();
|
||||
let wat = r#"
|
||||
(import "host" "0" (func))
|
||||
|
||||
@ -313,9 +312,9 @@ fn get_module2(store: &Store) -> Result<Module> {
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_function_with_env_wasmer_env_init_works() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
#[compiler_test(imports)]
|
||||
fn dynamic_function_with_env_wasmer_env_init_works(config: crate::Config) -> Result<()> {
|
||||
let store = config.store();
|
||||
let module = get_module2(&store)?;
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -344,9 +343,9 @@ fn dynamic_function_with_env_wasmer_env_init_works() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_use_host_fn_manages_memory_correctly() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
#[compiler_test(imports)]
|
||||
fn multi_use_host_fn_manages_memory_correctly(config: crate::Config) -> Result<()> {
|
||||
let store = config.store();
|
||||
let module = get_module2(&store)?;
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -391,9 +390,9 @@ fn multi_use_host_fn_manages_memory_correctly() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instance_local_memory_lifetime() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
#[compiler_test(imports)]
|
||||
fn instance_local_memory_lifetime(config: crate::Config) -> Result<()> {
|
||||
let store = config.store();
|
||||
|
||||
let memory: Memory = {
|
||||
let wat = r#"(module
|
||||
|
@ -1,20 +1,21 @@
|
||||
#![cfg(feature = "compiler")]
|
||||
|
||||
//! This test suite does all the tests that involve any compiler
|
||||
//! implementation, such as: singlepass, cranelift or llvm depending
|
||||
//! on what's available on the target.
|
||||
|
||||
#[macro_use]
|
||||
extern crate compiler_test_derive;
|
||||
|
||||
mod config;
|
||||
mod imports;
|
||||
mod metering;
|
||||
mod middlewares;
|
||||
mod multi_value_imports;
|
||||
mod native_functions;
|
||||
// mod multi_value_imports;
|
||||
// mod native_functions;
|
||||
mod serialize;
|
||||
mod traps;
|
||||
mod utils;
|
||||
// mod traps;
|
||||
mod wasi;
|
||||
mod wast;
|
||||
|
||||
pub use crate::utils::get_compiler;
|
||||
pub use crate::config::{Compiler, Config, Engine};
|
||||
pub use crate::wasi::run_wasi;
|
||||
pub use crate::wast::run_wast;
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::utils::get_store_with_middlewares;
|
||||
use anyhow::Result;
|
||||
use wasmer_middlewares::Metering;
|
||||
|
||||
@ -10,11 +9,11 @@ fn cost_always_one(_: &Operator) -> u64 {
|
||||
1
|
||||
}
|
||||
|
||||
fn run_add_with_limit(limit: u64) -> Result<()> {
|
||||
let store = get_store_with_middlewares(std::iter::once(Arc::new(Metering::new(
|
||||
limit,
|
||||
cost_always_one,
|
||||
)) as Arc<dyn ModuleMiddleware>));
|
||||
fn run_add_with_limit(mut config: crate::Config, limit: u64) -> Result<()> {
|
||||
config
|
||||
.middlewares
|
||||
.push(Arc::new(Metering::new(limit, cost_always_one)));
|
||||
let store = config.store();
|
||||
let wat = r#"(module
|
||||
(func (export "add") (param i32 i32) (result i32)
|
||||
(i32.add (local.get 0)
|
||||
@ -31,11 +30,11 @@ fn run_add_with_limit(limit: u64) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_loop(limit: u64, iter_count: i32) -> Result<()> {
|
||||
let store = get_store_with_middlewares(std::iter::once(Arc::new(Metering::new(
|
||||
limit,
|
||||
cost_always_one,
|
||||
)) as Arc<dyn ModuleMiddleware>));
|
||||
fn run_loop(mut config: crate::Config, limit: u64, iter_count: i32) -> Result<()> {
|
||||
config
|
||||
.middlewares
|
||||
.push(Arc::new(Metering::new(limit, cost_always_one)));
|
||||
let store = config.store();
|
||||
let wat = r#"(module
|
||||
(func (export "test") (param i32)
|
||||
(local i32)
|
||||
@ -62,35 +61,35 @@ fn run_loop(limit: u64, iter_count: i32) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metering_ok() -> Result<()> {
|
||||
assert!(run_add_with_limit(4).is_ok());
|
||||
#[compiler_test(metering)]
|
||||
fn metering_ok(config: crate::Config) -> Result<()> {
|
||||
assert!(run_add_with_limit(config, 4).is_ok());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metering_fail() -> Result<()> {
|
||||
assert!(run_add_with_limit(3).is_err());
|
||||
#[compiler_test(metering)]
|
||||
fn metering_fail(config: crate::Config) -> Result<()> {
|
||||
assert!(run_add_with_limit(config, 3).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_once() -> Result<()> {
|
||||
assert!(run_loop(12, 1).is_ok());
|
||||
assert!(run_loop(11, 1).is_err());
|
||||
#[compiler_test(metering)]
|
||||
fn loop_once(config: crate::Config) -> Result<()> {
|
||||
assert!(run_loop(config.clone(), 12, 1).is_ok());
|
||||
assert!(run_loop(config, 11, 1).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_twice() -> Result<()> {
|
||||
assert!(run_loop(19, 2).is_ok());
|
||||
assert!(run_loop(18, 2).is_err());
|
||||
#[compiler_test(metering)]
|
||||
fn loop_twice(config: crate::Config) -> Result<()> {
|
||||
assert!(run_loop(config.clone(), 19, 2).is_ok());
|
||||
assert!(run_loop(config, 18, 2).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ported from https://github.com/wasmerio/wasmer/blob/master/tests/middleware_common.rs
|
||||
#[test]
|
||||
fn complex_loop() -> Result<()> {
|
||||
#[compiler_test(metering)]
|
||||
fn complex_loop(mut config: crate::Config) -> Result<()> {
|
||||
// Assemblyscript
|
||||
// export function add_to(x: i32, y: i32): i32 {
|
||||
// for(var i = 0; i < x; i++){
|
||||
@ -149,10 +148,11 @@ fn complex_loop() -> Result<()> {
|
||||
(global $g0 i32 (i32.const 8))
|
||||
(elem (i32.const 0) $f1))
|
||||
"#;
|
||||
let store = get_store_with_middlewares(std::iter::once(Arc::new(Metering::new(
|
||||
100,
|
||||
cost_always_one,
|
||||
)) as Arc<dyn ModuleMiddleware>));
|
||||
config
|
||||
.middlewares
|
||||
.push(Arc::new(Metering::new(100, cost_always_one)));
|
||||
let store = config.store();
|
||||
|
||||
let module = Module::new(&store, WAT).unwrap();
|
||||
|
||||
let import_object = imports! {};
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::utils::get_store_with_middlewares;
|
||||
use anyhow::Result;
|
||||
|
||||
use loupe::MemoryUsage;
|
||||
@ -89,11 +88,12 @@ impl FunctionMiddleware for Fusion {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn middleware_basic() -> Result<()> {
|
||||
let store = get_store_with_middlewares(std::iter::once(
|
||||
#[compiler_test(middlewares)]
|
||||
fn middleware_basic(mut config: crate::Config) -> Result<()> {
|
||||
config.set_middlewares(vec![
|
||||
Arc::new(Add2MulGen { value_off: 0 }) as Arc<dyn ModuleMiddleware>
|
||||
));
|
||||
]);
|
||||
let store = config.store();
|
||||
let wat = r#"(module
|
||||
(func (export "add") (param i32 i32) (result i32)
|
||||
(i32.add (local.get 0)
|
||||
@ -111,11 +111,12 @@ fn middleware_basic() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn middleware_one_to_multi() -> Result<()> {
|
||||
let store = get_store_with_middlewares(std::iter::once(
|
||||
#[compiler_test(middlewares)]
|
||||
fn middleware_one_to_multi(mut config: crate::Config) -> Result<()> {
|
||||
config.set_middlewares(vec![
|
||||
Arc::new(Add2MulGen { value_off: 1 }) as Arc<dyn ModuleMiddleware>
|
||||
));
|
||||
]);
|
||||
let store = config.store();
|
||||
let wat = r#"(module
|
||||
(func (export "add") (param i32 i32) (result i32)
|
||||
(i32.add (local.get 0)
|
||||
@ -133,11 +134,10 @@ fn middleware_one_to_multi() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn middleware_multi_to_one() -> Result<()> {
|
||||
let store = get_store_with_middlewares(std::iter::once(
|
||||
Arc::new(FusionGen) as Arc<dyn ModuleMiddleware>
|
||||
));
|
||||
#[compiler_test(middlewares)]
|
||||
fn middleware_multi_to_one(mut config: crate::Config) -> Result<()> {
|
||||
config.set_middlewares(vec![Arc::new(FusionGen) as Arc<dyn ModuleMiddleware>]);
|
||||
let store = config.store();
|
||||
let wat = r#"(module
|
||||
(func (export "testfunc") (param i32 i32) (result i32)
|
||||
(local.get 0)
|
||||
@ -158,15 +158,13 @@ fn middleware_multi_to_one() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn middleware_chain_order_1() -> Result<()> {
|
||||
let store = get_store_with_middlewares(
|
||||
vec![
|
||||
Arc::new(Add2MulGen { value_off: 0 }) as Arc<dyn ModuleMiddleware>,
|
||||
Arc::new(Add2MulGen { value_off: 2 }) as Arc<dyn ModuleMiddleware>,
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
#[compiler_test(middlewares)]
|
||||
fn middleware_chain_order_1(mut config: crate::Config) -> Result<()> {
|
||||
config.set_middlewares(vec![
|
||||
Arc::new(Add2MulGen { value_off: 0 }) as Arc<dyn ModuleMiddleware>,
|
||||
Arc::new(Add2MulGen { value_off: 2 }) as Arc<dyn ModuleMiddleware>,
|
||||
]);
|
||||
let store = config.store();
|
||||
let wat = r#"(module
|
||||
(func (export "add") (param i32 i32) (result i32)
|
||||
(i32.add (local.get 0)
|
||||
@ -184,15 +182,13 @@ fn middleware_chain_order_1() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn middleware_chain_order_2() -> Result<()> {
|
||||
let store = get_store_with_middlewares(
|
||||
vec![
|
||||
Arc::new(Add2MulGen { value_off: 2 }) as Arc<dyn ModuleMiddleware>,
|
||||
Arc::new(Add2MulGen { value_off: 0 }) as Arc<dyn ModuleMiddleware>,
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
#[compiler_test(middlewares)]
|
||||
fn middleware_chain_order_2(mut config: crate::Config) -> Result<()> {
|
||||
config.set_middlewares(vec![
|
||||
Arc::new(Add2MulGen { value_off: 2 }) as Arc<dyn ModuleMiddleware>,
|
||||
Arc::new(Add2MulGen { value_off: 0 }) as Arc<dyn ModuleMiddleware>,
|
||||
]);
|
||||
let store = config.store();
|
||||
let wat = r#"(module
|
||||
(func (export "add") (param i32 i32) (result i32)
|
||||
(i32.add (local.get 0)
|
||||
|
@ -1,208 +0,0 @@
|
||||
//! Testing the imports with different provided functions.
|
||||
//! This tests checks that the provided functions (both native and
|
||||
//! dynamic ones) work properly.
|
||||
|
||||
use crate::utils::get_store;
|
||||
use wasmer::*;
|
||||
|
||||
macro_rules! mvr_test {
|
||||
($test_name:ident, $( $result_type:ty ),* ) => {
|
||||
mod $test_name {
|
||||
use super::*;
|
||||
|
||||
fn get_module(store: &Store) -> anyhow::Result<wasmer::Module> {
|
||||
let wat: String = r#"
|
||||
(type $type (func (param i32) (result
|
||||
"#.to_string() +
|
||||
&stringify!( $( $result_type ),* ).replace(",", "").replace("(", "").replace(")", "") + &r#")))
|
||||
(import "host" "callback_fn" (func $callback_fn (type $type)))
|
||||
(func (export "test_call") (type $type)
|
||||
get_local 0
|
||||
call $callback_fn)
|
||||
(func (export "test_call_indirect") (type $type)
|
||||
(i32.const 1)
|
||||
(call_indirect (type $type) (i32.const 0))
|
||||
)
|
||||
(table funcref
|
||||
(elem
|
||||
$callback_fn
|
||||
)
|
||||
)
|
||||
"#.to_string();
|
||||
Ok(wasmer::Module::new(&store, &wat)?)
|
||||
}
|
||||
|
||||
fn callback_fn(n: i32) -> ( $( $result_type ),* ) {
|
||||
( $( <$result_type>::expected_value(n) ),* )
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(feature = "test-cranelift", feature="test-singlepass"), ignore)]
|
||||
fn native() -> anyhow::Result<()> {
|
||||
let store = get_store(false);
|
||||
let module = get_module(&store)?;
|
||||
let instance = wasmer::Instance::new(
|
||||
&module,
|
||||
&wasmer::imports! {
|
||||
"host" => {
|
||||
"callback_fn" => wasmer::Function::new_native(&store, callback_fn)
|
||||
}
|
||||
}
|
||||
)?;
|
||||
let expected_value = vec![ $( <$result_type>::expected_val(1) ),* ].into_boxed_slice();
|
||||
assert_eq!(instance.exports.get_function("test_call")?.call(&[wasmer::Val::I32(1)])?,
|
||||
expected_value);
|
||||
assert_eq!(instance.exports.get_function("test_call_indirect")?.call(&[wasmer::Val::I32(1)])?,
|
||||
expected_value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dynamic_callback_fn(values: &[wasmer::Value]) -> Result<Vec<wasmer::Val>, wasmer::RuntimeError> {
|
||||
assert_eq!(values[0], wasmer::Value::I32(1));
|
||||
Ok(vec![ $( <$result_type>::expected_val(1) ),* ])
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(feature="test-singlepass", ignore)]
|
||||
fn dynamic() -> anyhow::Result<()> {
|
||||
let store = get_store(false);
|
||||
let module = get_module(&store)?;
|
||||
let callback_fn = wasmer::Function::new(&store, &wasmer::FunctionType::new(vec![wasmer::ValType::I32], vec![ $( <$result_type>::expected_valtype() ),* ]), dynamic_callback_fn);
|
||||
let instance = wasmer::Instance::new(
|
||||
&module,
|
||||
&wasmer::imports! {
|
||||
"host" => {
|
||||
"callback_fn" => callback_fn
|
||||
}
|
||||
}
|
||||
)?;
|
||||
let expected_value = vec![ $( <$result_type>::expected_val(1) ),* ].into_boxed_slice();
|
||||
assert_eq!(instance.exports.get_function("test_call")?.call(&[wasmer::Val::I32(1)])?,
|
||||
expected_value);
|
||||
assert_eq!(instance.exports.get_function("test_call_indirect")?.call(&[wasmer::Val::I32(1)])?,
|
||||
expected_value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait ExpectedExpr {
|
||||
fn expected_value(n: i32) -> Self;
|
||||
fn expected_val(n: i32) -> wasmer::Val;
|
||||
fn expected_valtype() -> wasmer::ValType;
|
||||
}
|
||||
impl ExpectedExpr for i32 {
|
||||
fn expected_value(n: i32) -> i32 {
|
||||
n + 1
|
||||
}
|
||||
fn expected_val(n: i32) -> wasmer::Val {
|
||||
wasmer::Val::I32(Self::expected_value(n))
|
||||
}
|
||||
fn expected_valtype() -> wasmer::ValType {
|
||||
wasmer::ValType::I32
|
||||
}
|
||||
}
|
||||
impl ExpectedExpr for i64 {
|
||||
fn expected_value(n: i32) -> i64 {
|
||||
n as i64 + 2i64
|
||||
}
|
||||
fn expected_val(n: i32) -> wasmer::Val {
|
||||
wasmer::Val::I64(Self::expected_value(n))
|
||||
}
|
||||
fn expected_valtype() -> wasmer::ValType {
|
||||
wasmer::ValType::I64
|
||||
}
|
||||
}
|
||||
impl ExpectedExpr for f32 {
|
||||
fn expected_value(n: i32) -> f32 {
|
||||
n as f32 * 0.1
|
||||
}
|
||||
fn expected_val(n: i32) -> wasmer::Val {
|
||||
wasmer::Val::F32(Self::expected_value(n))
|
||||
}
|
||||
fn expected_valtype() -> wasmer::ValType {
|
||||
wasmer::ValType::F32
|
||||
}
|
||||
}
|
||||
impl ExpectedExpr for f64 {
|
||||
fn expected_value(n: i32) -> f64 {
|
||||
n as f64 * 0.12
|
||||
}
|
||||
fn expected_val(n: i32) -> wasmer::Val {
|
||||
wasmer::Val::F64(Self::expected_value(n))
|
||||
}
|
||||
fn expected_valtype() -> wasmer::ValType {
|
||||
wasmer::ValType::F64
|
||||
}
|
||||
}
|
||||
|
||||
mvr_test!(test_mvr_i32_i32, i32, i32);
|
||||
mvr_test!(test_mvr_i32_f32, i32, f32);
|
||||
mvr_test!(test_mvr_f32_i32, f32, i32);
|
||||
mvr_test!(test_mvr_f32_f32, f32, f32);
|
||||
|
||||
mvr_test!(test_mvr_i64_i32, i64, i32);
|
||||
mvr_test!(test_mvr_i64_f32, i64, f32);
|
||||
mvr_test!(test_mvr_f64_i32, f64, i32);
|
||||
mvr_test!(test_mvr_f64_f32, f64, f32);
|
||||
|
||||
mvr_test!(test_mvr_i32_i64, i32, i64);
|
||||
mvr_test!(test_mvr_f32_i64, f32, i64);
|
||||
mvr_test!(test_mvr_i32_f64, i32, f64);
|
||||
mvr_test!(test_mvr_f32_f64, f32, f64);
|
||||
|
||||
mvr_test!(test_mvr_i32_i32_i32, i32, i32, i32);
|
||||
mvr_test!(test_mvr_i32_i32_f32, i32, i32, f32);
|
||||
mvr_test!(test_mvr_i32_f32_i32, i32, f32, i32);
|
||||
mvr_test!(test_mvr_i32_f32_f32, i32, f32, f32);
|
||||
mvr_test!(test_mvr_f32_i32_i32, f32, i32, i32);
|
||||
mvr_test!(test_mvr_f32_i32_f32, f32, i32, f32);
|
||||
mvr_test!(test_mvr_f32_f32_i32, f32, f32, i32);
|
||||
mvr_test!(test_mvr_f32_f32_f32, f32, f32, f32);
|
||||
|
||||
mvr_test!(test_mvr_i32_i32_i64, i32, i32, i64);
|
||||
mvr_test!(test_mvr_i32_f32_i64, i32, f32, i64);
|
||||
mvr_test!(test_mvr_f32_i32_i64, f32, i32, i64);
|
||||
mvr_test!(test_mvr_f32_f32_i64, f32, f32, i64);
|
||||
mvr_test!(test_mvr_i32_i32_f64, i32, i32, f64);
|
||||
mvr_test!(test_mvr_i32_f32_f64, i32, f32, f64);
|
||||
mvr_test!(test_mvr_f32_i32_f64, f32, i32, f64);
|
||||
mvr_test!(test_mvr_f32_f32_f64, f32, f32, f64);
|
||||
|
||||
mvr_test!(test_mvr_i32_i64_i32, i32, i64, i32);
|
||||
mvr_test!(test_mvr_i32_i64_f32, i32, i64, f32);
|
||||
mvr_test!(test_mvr_f32_i64_i32, f32, i64, i32);
|
||||
mvr_test!(test_mvr_f32_i64_f32, f32, i64, f32);
|
||||
mvr_test!(test_mvr_i32_f64_i32, i32, f64, i32);
|
||||
mvr_test!(test_mvr_i32_f64_f32, i32, f64, f32);
|
||||
mvr_test!(test_mvr_f32_f64_i32, f32, f64, i32);
|
||||
mvr_test!(test_mvr_f32_f64_f32, f32, f64, f32);
|
||||
|
||||
mvr_test!(test_mvr_i64_i32_i32, i64, i32, i32);
|
||||
mvr_test!(test_mvr_i64_i32_f32, i64, i32, f32);
|
||||
mvr_test!(test_mvr_i64_f32_i32, i64, f32, i32);
|
||||
mvr_test!(test_mvr_i64_f32_f32, i64, f32, f32);
|
||||
mvr_test!(test_mvr_f64_i32_i32, f64, i32, i32);
|
||||
mvr_test!(test_mvr_f64_i32_f32, f64, i32, f32);
|
||||
mvr_test!(test_mvr_f64_f32_i32, f64, f32, i32);
|
||||
mvr_test!(test_mvr_f64_f32_f32, f64, f32, f32);
|
||||
|
||||
mvr_test!(test_mvr_i32_i32_i32_i32, i32, i32, i32, i32);
|
||||
mvr_test!(test_mvr_i32_i32_i32_f32, i32, i32, i32, f32);
|
||||
mvr_test!(test_mvr_i32_i32_f32_i32, i32, i32, f32, i32);
|
||||
mvr_test!(test_mvr_i32_i32_f32_f32, i32, i32, f32, f32);
|
||||
mvr_test!(test_mvr_i32_f32_i32_i32, i32, f32, i32, i32);
|
||||
mvr_test!(test_mvr_i32_f32_i32_f32, i32, f32, i32, f32);
|
||||
mvr_test!(test_mvr_i32_f32_f32_i32, i32, f32, f32, i32);
|
||||
mvr_test!(test_mvr_i32_f32_f32_f32, i32, f32, f32, f32);
|
||||
mvr_test!(test_mvr_f32_i32_i32_i32, f32, i32, i32, i32);
|
||||
mvr_test!(test_mvr_f32_i32_i32_f32, f32, i32, i32, f32);
|
||||
mvr_test!(test_mvr_f32_i32_f32_i32, f32, i32, f32, i32);
|
||||
mvr_test!(test_mvr_f32_i32_f32_f32, f32, i32, f32, f32);
|
||||
mvr_test!(test_mvr_f32_f32_i32_i32, f32, f32, i32, i32);
|
||||
mvr_test!(test_mvr_f32_f32_i32_f32, f32, f32, i32, f32);
|
||||
mvr_test!(test_mvr_f32_f32_f32_i32, f32, f32, f32, i32);
|
||||
mvr_test!(test_mvr_f32_f32_f32_f32, f32, f32, f32, f32);
|
||||
|
||||
mvr_test!(test_mvr_i32_i32_i32_i32_i32, i32, i32, i32, i32, i32);
|
@ -1,450 +0,0 @@
|
||||
use crate::utils::get_store;
|
||||
use anyhow::Result;
|
||||
use std::convert::Infallible;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use wasmer::*;
|
||||
|
||||
fn long_f(a: u32, b: u32, c: u32, d: u32, e: u32, f: u16, g: u64, h: u64, i: u16, j: u32) -> u64 {
|
||||
j as u64
|
||||
+ i as u64 * 10
|
||||
+ h * 100
|
||||
+ g * 1000
|
||||
+ f as u64 * 10000
|
||||
+ e as u64 * 100000
|
||||
+ d as u64 * 1000000
|
||||
+ c as u64 * 10000000
|
||||
+ b as u64 * 100000000
|
||||
+ a as u64 * 1000000000
|
||||
}
|
||||
|
||||
fn long_f_dynamic(values: &[Value]) -> Result<Vec<Value>, RuntimeError> {
|
||||
Ok(vec![Value::I64(
|
||||
values[9].unwrap_i32() as i64
|
||||
+ values[8].unwrap_i32() as i64 * 10
|
||||
+ values[7].unwrap_i64() * 100
|
||||
+ values[6].unwrap_i64() * 1000
|
||||
+ values[5].unwrap_i32() as i64 * 10000
|
||||
+ values[4].unwrap_i32() as i64 * 100000
|
||||
+ values[3].unwrap_i32() as i64 * 1000000
|
||||
+ values[2].unwrap_i32() as i64 * 10000000
|
||||
+ values[1].unwrap_i32() as i64 * 100000000
|
||||
+ values[0].unwrap_i32() as i64 * 1000000000,
|
||||
)])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_function_works_for_wasm() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let wat = r#"(module
|
||||
(func $multiply (import "env" "multiply") (param i32 i32) (result i32))
|
||||
(func (export "add") (param i32 i32) (result i32)
|
||||
(i32.add (local.get 0)
|
||||
(local.get 1)))
|
||||
(func (export "double_then_add") (param i32 i32) (result i32)
|
||||
(i32.add (call $multiply (local.get 0) (i32.const 2))
|
||||
(call $multiply (local.get 1) (i32.const 2))))
|
||||
)"#;
|
||||
let module = Module::new(&store, wat).unwrap();
|
||||
|
||||
let import_object = imports! {
|
||||
"env" => {
|
||||
"multiply" => Function::new_native(&store, |a: i32, b: i32| a * b),
|
||||
},
|
||||
};
|
||||
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
{
|
||||
let f: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("add")?;
|
||||
let result = f.call(4, 6)?;
|
||||
assert_eq!(result, 10);
|
||||
}
|
||||
|
||||
{
|
||||
let f: &Function = instance.exports.get("double_then_add")?;
|
||||
let result = f.call(&[Val::I32(4), Val::I32(6)])?;
|
||||
assert_eq!(result[0], Val::I32(20));
|
||||
}
|
||||
|
||||
{
|
||||
let dyn_f: &Function = instance.exports.get("double_then_add")?;
|
||||
let f: NativeFunc<(i32, i32), i32> = dyn_f.native().unwrap();
|
||||
let result = f.call(4, 6)?;
|
||||
assert_eq!(result, 20);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(
|
||||
expected = "Closures (functions with captured environments) are currently unsupported with native functions. 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 with native functions. 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 non_native_functions_and_closures_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 captured_by_closure = 20;
|
||||
let import_object = imports! {
|
||||
"env" => {
|
||||
"multiply1" => Function::new(&store, &ty, move |args| {
|
||||
if let (Value::I32(v1), Value::I32(v2)) = (&args[0], &args[1]) {
|
||||
Ok(vec![Value::I32(v1 * v2 * captured_by_closure)])
|
||||
} else {
|
||||
panic!("Invalid arguments");
|
||||
}
|
||||
}),
|
||||
"multiply2" => Function::new_with_env(&store, &ty, env, move |&env, args| {
|
||||
if let (Value::I32(v1), Value::I32(v2)) = (&args[0], &args[1]) {
|
||||
Ok(vec![Value::I32(v1 * v2 * captured_by_closure * 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 * 20) * 10 * 20)) * 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 and Cranelift.
|
||||
// We should enable it for LLVM in Apple Silicon, but now is not trivial.
|
||||
// TODO: Fix once Cranelift has a correct ABI for Apple Silicon.
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
all(
|
||||
// feature = "test-cranelift",
|
||||
target_os = "macos",
|
||||
target_arch = "aarch64",
|
||||
),
|
||||
ignore
|
||||
)]
|
||||
fn native_function_works_for_wasm_function_manyparams() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let wat = r#"(module
|
||||
(func $longf (import "env" "longf") (param i32 i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i64))
|
||||
(func (export "longf_pure") (param i32 i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i64)
|
||||
(call $longf (local.get 0) (local.get 1) (local.get 2) (local.get 3) (local.get 4) (local.get 5) (local.get 6) (local.get 7) (local.get 8) (local.get 9)))
|
||||
(func (export "longf") (result i64)
|
||||
(call $longf (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4) (i32.const 5) (i32.const 6) (i64.const 7) (i64.const 8) (i32.const 9) (i32.const 0)))
|
||||
)"#;
|
||||
let module = Module::new(&store, wat).unwrap();
|
||||
|
||||
let import_object = imports! {
|
||||
"env" => {
|
||||
"longf" => Function::new_native(&store, long_f),
|
||||
},
|
||||
};
|
||||
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
{
|
||||
let dyn_f: &Function = instance.exports.get("longf")?;
|
||||
let f: NativeFunc<(), i64> = dyn_f.native().unwrap();
|
||||
let result = f.call()?;
|
||||
assert_eq!(result, 1234567890);
|
||||
}
|
||||
|
||||
{
|
||||
let dyn_f: &Function = instance.exports.get("longf_pure")?;
|
||||
let f: NativeFunc<(u32, u32, u32, u32, u32, u16, u64, u64, u16, u32), i64> =
|
||||
dyn_f.native().unwrap();
|
||||
let result = f.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)?;
|
||||
assert_eq!(result, 1234567890);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_function_works_for_wasm_function_manyparams_dynamic() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let wat = r#"(module
|
||||
(func $longf (import "env" "longf") (param i32 i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i64))
|
||||
(func (export "longf_pure") (param i32 i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i64)
|
||||
(call $longf (local.get 0) (local.get 1) (local.get 2) (local.get 3) (local.get 4) (local.get 5) (local.get 6) (local.get 7) (local.get 8) (local.get 9)))
|
||||
(func (export "longf") (result i64)
|
||||
(call $longf (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4) (i32.const 5) (i32.const 6) (i64.const 7) (i64.const 8) (i32.const 9) (i32.const 0)))
|
||||
)"#;
|
||||
let module = Module::new(&store, wat).unwrap();
|
||||
|
||||
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),
|
||||
},
|
||||
};
|
||||
|
||||
let instance = Instance::new(&module, &import_object)?;
|
||||
|
||||
{
|
||||
let dyn_f: &Function = instance.exports.get("longf")?;
|
||||
let f: NativeFunc<(), i64> = dyn_f.native().unwrap();
|
||||
let result = f.call()?;
|
||||
assert_eq!(result, 1234567890);
|
||||
}
|
||||
|
||||
{
|
||||
let dyn_f: &Function = instance.exports.get("longf_pure")?;
|
||||
let f: NativeFunc<(u32, u32, u32, u32, u32, u16, u64, u64, u16, u32), i64> =
|
||||
dyn_f.native().unwrap();
|
||||
let result = f.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)?;
|
||||
assert_eq!(result, 1234567890);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_host_function_without_env() -> anyhow::Result<()> {
|
||||
let store = get_store(false);
|
||||
|
||||
fn f(a: i32, b: i64, c: f32, d: f64) -> (f64, f32, i64, i32) {
|
||||
(d * 4.0, c * 3.0, b * 2, a * 1)
|
||||
}
|
||||
|
||||
fn f_ok(a: i32, b: i64, c: f32, d: f64) -> Result<(f64, f32, i64, i32), Infallible> {
|
||||
Ok((d * 4.0, c * 3.0, b * 2, a * 1))
|
||||
}
|
||||
|
||||
fn long_f(
|
||||
a: u32,
|
||||
b: u32,
|
||||
c: u32,
|
||||
d: u32,
|
||||
e: u32,
|
||||
f: u16,
|
||||
g: u64,
|
||||
h: u64,
|
||||
i: u16,
|
||||
j: u32,
|
||||
) -> (u32, u64, u32) {
|
||||
(
|
||||
a + b * 10 + c * 100 + d * 1000 + e * 10000 + f as u32 * 100000,
|
||||
g + h * 10,
|
||||
i as u32 + j * 10,
|
||||
)
|
||||
}
|
||||
|
||||
// Native static host function that returns a tuple.
|
||||
{
|
||||
let f = Function::new_native(&store, f);
|
||||
let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap();
|
||||
let result = f_native.call(1, 3, 5.0, 7.0)?;
|
||||
assert_eq!(result, (28.0, 15.0, 6, 1));
|
||||
}
|
||||
|
||||
// Native static host function that returns a tuple.
|
||||
{
|
||||
let long_f = Function::new_native(&store, long_f);
|
||||
let long_f_native: NativeFunc<
|
||||
(u32, u32, u32, u32, u32, u16, u64, u64, u16, u32),
|
||||
(u32, u64, u32),
|
||||
> = long_f.native().unwrap();
|
||||
let result = long_f_native.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)?;
|
||||
assert_eq!(result, (654321, 87, 09));
|
||||
}
|
||||
|
||||
// Native static host function that returns a result of a tuple.
|
||||
{
|
||||
let f = Function::new_native(&store, f_ok);
|
||||
let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap();
|
||||
let result = f_native.call(1, 3, 5.0, 7.0)?;
|
||||
assert_eq!(result, (28.0, 15.0, 6, 1));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_host_function_with_env() -> anyhow::Result<()> {
|
||||
let store = get_store(false);
|
||||
|
||||
fn f(env: &Env, a: i32, b: i64, c: f32, d: f64) -> (f64, f32, i64, i32) {
|
||||
let mut guard = env.0.lock().unwrap();
|
||||
assert_eq!(*guard, 100);
|
||||
*guard = 101;
|
||||
|
||||
(d * 4.0, c * 3.0, b * 2, a * 1)
|
||||
}
|
||||
|
||||
fn f_ok(env: &Env, a: i32, b: i64, c: f32, d: f64) -> Result<(f64, f32, i64, i32), Infallible> {
|
||||
let mut guard = env.0.lock().unwrap();
|
||||
assert_eq!(*guard, 100);
|
||||
*guard = 101;
|
||||
|
||||
Ok((d * 4.0, c * 3.0, b * 2, a * 1))
|
||||
}
|
||||
|
||||
#[derive(WasmerEnv, Clone)]
|
||||
struct Env(Arc<Mutex<i32>>);
|
||||
|
||||
impl std::ops::Deref for Env {
|
||||
type Target = Arc<Mutex<i32>>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
// Native static host function that returns a tuple.
|
||||
{
|
||||
let env = Env(Arc::new(Mutex::new(100)));
|
||||
|
||||
let f = Function::new_native_with_env(&store, env.clone(), f);
|
||||
let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap();
|
||||
|
||||
assert_eq!(*env.0.lock().unwrap(), 100);
|
||||
|
||||
let result = f_native.call(1, 3, 5.0, 7.0)?;
|
||||
|
||||
assert_eq!(result, (28.0, 15.0, 6, 1));
|
||||
assert_eq!(*env.0.lock().unwrap(), 101);
|
||||
}
|
||||
|
||||
// Native static host function that returns a result of a tuple.
|
||||
{
|
||||
let env = Env(Arc::new(Mutex::new(100)));
|
||||
|
||||
let f = Function::new_native_with_env(&store, env.clone(), f_ok);
|
||||
let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap();
|
||||
|
||||
assert_eq!(*env.0.lock().unwrap(), 100);
|
||||
|
||||
let result = f_native.call(1, 3, 5.0, 7.0)?;
|
||||
|
||||
assert_eq!(result, (28.0, 15.0, 6, 1));
|
||||
assert_eq!(*env.0.lock().unwrap(), 101);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_host_function_without_env() -> anyhow::Result<()> {
|
||||
let store = get_store(false);
|
||||
|
||||
let f = Function::new(
|
||||
&store,
|
||||
FunctionType::new(
|
||||
vec![ValType::I32, ValType::I64, ValType::F32, ValType::F64],
|
||||
vec![ValType::F64, ValType::F32, ValType::I64, ValType::I32],
|
||||
),
|
||||
|values| {
|
||||
Ok(vec![
|
||||
Value::F64(values[3].unwrap_f64() * 4.0),
|
||||
Value::F32(values[2].unwrap_f32() * 3.0),
|
||||
Value::I64(values[1].unwrap_i64() * 2),
|
||||
Value::I32(values[0].unwrap_i32() * 1),
|
||||
])
|
||||
},
|
||||
);
|
||||
let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap();
|
||||
let result = f_native.call(1, 3, 5.0, 7.0)?;
|
||||
|
||||
assert_eq!(result, (28.0, 15.0, 6, 1));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_host_function_with_env() -> anyhow::Result<()> {
|
||||
let store = get_store(false);
|
||||
|
||||
#[derive(WasmerEnv, Clone)]
|
||||
struct Env(Arc<Mutex<i32>>);
|
||||
|
||||
impl std::ops::Deref for Env {
|
||||
type Target = Arc<Mutex<i32>>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
let env = Env(Arc::new(Mutex::new(100)));
|
||||
let f = Function::new_with_env(
|
||||
&store,
|
||||
FunctionType::new(
|
||||
vec![ValType::I32, ValType::I64, ValType::F32, ValType::F64],
|
||||
vec![ValType::F64, ValType::F32, ValType::I64, ValType::I32],
|
||||
),
|
||||
env.clone(),
|
||||
|env, values| {
|
||||
let mut guard = env.0.lock().unwrap();
|
||||
assert_eq!(*guard, 100);
|
||||
|
||||
*guard = 101;
|
||||
|
||||
Ok(vec![
|
||||
Value::F64(values[3].unwrap_f64() * 4.0),
|
||||
Value::F32(values[2].unwrap_f32() * 3.0),
|
||||
Value::I64(values[1].unwrap_i64() * 2),
|
||||
Value::I32(values[0].unwrap_i32() * 1),
|
||||
])
|
||||
},
|
||||
);
|
||||
|
||||
let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap();
|
||||
|
||||
assert_eq!(*env.0.lock().unwrap(), 100);
|
||||
|
||||
let result = f_native.call(1, 3, 5.0, 7.0)?;
|
||||
|
||||
assert_eq!(result, (28.0, 15.0, 6, 1));
|
||||
assert_eq!(*env.0.lock().unwrap(), 101);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
use crate::utils::{get_headless_store, get_store};
|
||||
use anyhow::Result;
|
||||
use wasmer::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialize() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
#[compiler_test(serialize)]
|
||||
fn test_serialize(config: crate::Config) -> Result<()> {
|
||||
let store = config.store();
|
||||
let wat = r#"
|
||||
(module
|
||||
(func $hello (import "" "hello"))
|
||||
@ -18,9 +17,9 @@ fn test_serialize() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
#[compiler_test(serialize)]
|
||||
fn test_deserialize(config: crate::Config) -> Result<()> {
|
||||
let store = config.store();
|
||||
let wat = r#"
|
||||
(module $name
|
||||
(import "host" "sum_part" (func (param i32 i64 i32 f32 f64) (result i64)))
|
||||
@ -38,7 +37,7 @@ fn test_deserialize() -> Result<()> {
|
||||
let module = Module::new(&store, wat)?;
|
||||
let serialized_bytes = module.serialize()?;
|
||||
|
||||
let headless_store = get_headless_store();
|
||||
let headless_store = config.headless_store();
|
||||
let deserialized_module = unsafe { Module::deserialize(&headless_store, &serialized_bytes)? };
|
||||
assert_eq!(deserialized_module.name(), Some("name"));
|
||||
assert_eq!(
|
||||
|
@ -1,526 +0,0 @@
|
||||
use crate::utils::get_store;
|
||||
use anyhow::Result;
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
use wasmer::*;
|
||||
|
||||
#[test]
|
||||
fn test_trap_return() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let wat = r#"
|
||||
(module
|
||||
(func $hello (import "" "hello"))
|
||||
(func (export "run") (call $hello))
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(&store, wat)?;
|
||||
let hello_type = FunctionType::new(vec![], vec![]);
|
||||
let hello_func = Function::new(&store, &hello_type, |_| Err(RuntimeError::new("test 123")));
|
||||
|
||||
let instance = Instance::new(
|
||||
&module,
|
||||
&imports! {
|
||||
"" => {
|
||||
"hello" => hello_func
|
||||
}
|
||||
},
|
||||
)?;
|
||||
let run_func = instance
|
||||
.exports
|
||||
.get_function("run")
|
||||
.expect("expected function export");
|
||||
|
||||
let e = run_func.call(&[]).err().expect("error calling function");
|
||||
|
||||
assert_eq!(e.message(), "test 123");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(
|
||||
feature = "test-singlepass",
|
||||
feature = "test-native",
|
||||
target_arch = "aarch64",
|
||||
target_env = "musl",
|
||||
),
|
||||
ignore
|
||||
)]
|
||||
fn test_trap_trace() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let wat = r#"
|
||||
(module $hello_mod
|
||||
(func (export "run") (call $hello))
|
||||
(func $hello (unreachable))
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(&store, wat)?;
|
||||
let instance = Instance::new(&module, &imports! {})?;
|
||||
let run_func = instance
|
||||
.exports
|
||||
.get_function("run")
|
||||
.expect("expected function export");
|
||||
|
||||
let e = run_func.call(&[]).err().expect("error calling function");
|
||||
|
||||
let trace = e.trace();
|
||||
assert_eq!(trace.len(), 2);
|
||||
assert_eq!(trace[0].module_name(), "hello_mod");
|
||||
assert_eq!(trace[0].func_index(), 1);
|
||||
assert_eq!(trace[0].function_name(), Some("hello"));
|
||||
assert_eq!(trace[1].module_name(), "hello_mod");
|
||||
assert_eq!(trace[1].func_index(), 0);
|
||||
assert_eq!(trace[1].function_name(), None);
|
||||
assert!(
|
||||
e.message().contains("unreachable"),
|
||||
"wrong message: {}",
|
||||
e.message()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trap_trace_cb() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let wat = r#"
|
||||
(module $hello_mod
|
||||
(import "" "throw" (func $throw))
|
||||
(func (export "run") (call $hello))
|
||||
(func $hello (call $throw))
|
||||
)
|
||||
"#;
|
||||
|
||||
let fn_type = FunctionType::new(vec![], vec![]);
|
||||
let fn_func = Function::new(&store, &fn_type, |_| Err(RuntimeError::new("cb throw")));
|
||||
|
||||
let module = Module::new(&store, wat)?;
|
||||
let instance = Instance::new(
|
||||
&module,
|
||||
&imports! {
|
||||
"" => {
|
||||
"throw" => fn_func
|
||||
}
|
||||
},
|
||||
)?;
|
||||
let run_func = instance
|
||||
.exports
|
||||
.get_function("run")
|
||||
.expect("expected function export");
|
||||
|
||||
let e = run_func.call(&[]).err().expect("error calling function");
|
||||
|
||||
let trace = e.trace();
|
||||
println!("Trace {:?}", trace);
|
||||
// TODO: Reenable this (disabled as it was not working with llvm/singlepass)
|
||||
// assert_eq!(trace.len(), 2);
|
||||
// assert_eq!(trace[0].module_name(), "hello_mod");
|
||||
// assert_eq!(trace[0].func_index(), 2);
|
||||
// assert_eq!(trace[1].module_name(), "hello_mod");
|
||||
// assert_eq!(trace[1].func_index(), 1);
|
||||
assert_eq!(e.message(), "cb throw");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(
|
||||
feature = "test-singlepass",
|
||||
feature = "test-native",
|
||||
target_arch = "aarch64",
|
||||
target_env = "musl",
|
||||
),
|
||||
ignore
|
||||
)]
|
||||
fn test_trap_stack_overflow() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let wat = r#"
|
||||
(module $rec_mod
|
||||
(func $run (export "run") (call $run))
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(&store, wat)?;
|
||||
let instance = Instance::new(&module, &imports! {})?;
|
||||
let run_func = instance
|
||||
.exports
|
||||
.get_function("run")
|
||||
.expect("expected function export");
|
||||
|
||||
let e = run_func.call(&[]).err().expect("error calling function");
|
||||
|
||||
let trace = e.trace();
|
||||
assert!(trace.len() >= 32);
|
||||
for i in 0..trace.len() {
|
||||
assert_eq!(trace[i].module_name(), "rec_mod");
|
||||
assert_eq!(trace[i].func_index(), 0);
|
||||
assert_eq!(trace[i].function_name(), Some("run"));
|
||||
}
|
||||
assert!(e.message().contains("call stack exhausted"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(
|
||||
feature = "test-singlepass",
|
||||
feature = "test-llvm",
|
||||
feature = "test-native",
|
||||
target_arch = "aarch64",
|
||||
target_env = "musl",
|
||||
),
|
||||
ignore
|
||||
)]
|
||||
fn trap_display_pretty() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let wat = r#"
|
||||
(module $m
|
||||
(func $die unreachable)
|
||||
(func call $die)
|
||||
(func $foo call 1)
|
||||
(func (export "bar") call $foo)
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(&store, wat)?;
|
||||
let instance = Instance::new(&module, &imports! {})?;
|
||||
let run_func = instance
|
||||
.exports
|
||||
.get_function("bar")
|
||||
.expect("expected function export");
|
||||
|
||||
let e = run_func.call(&[]).err().expect("error calling function");
|
||||
assert_eq!(
|
||||
e.to_string(),
|
||||
"\
|
||||
RuntimeError: unreachable
|
||||
at die (m[0]:0x23)
|
||||
at <unnamed> (m[1]:0x27)
|
||||
at foo (m[2]:0x2c)
|
||||
at <unnamed> (m[3]:0x31)"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(
|
||||
feature = "test-singlepass",
|
||||
feature = "test-llvm",
|
||||
feature = "test-native",
|
||||
target_arch = "aarch64",
|
||||
target_env = "musl",
|
||||
),
|
||||
ignore
|
||||
)]
|
||||
fn trap_display_multi_module() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let wat = r#"
|
||||
(module $a
|
||||
(func $die unreachable)
|
||||
(func call $die)
|
||||
(func $foo call 1)
|
||||
(func (export "bar") call $foo)
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(&store, wat)?;
|
||||
let instance = Instance::new(&module, &imports! {})?;
|
||||
let bar = instance.exports.get_function("bar")?.clone();
|
||||
|
||||
let wat = r#"
|
||||
(module $b
|
||||
(import "" "" (func $bar))
|
||||
(func $middle call $bar)
|
||||
(func (export "bar2") call $middle)
|
||||
)
|
||||
"#;
|
||||
let module = Module::new(&store, wat)?;
|
||||
let instance = Instance::new(
|
||||
&module,
|
||||
&imports! {
|
||||
"" => {
|
||||
"" => bar
|
||||
}
|
||||
},
|
||||
)?;
|
||||
let bar2 = instance
|
||||
.exports
|
||||
.get_function("bar2")
|
||||
.expect("expected function export");
|
||||
|
||||
let e = bar2.call(&[]).err().expect("error calling function");
|
||||
assert_eq!(
|
||||
e.to_string(),
|
||||
"\
|
||||
RuntimeError: unreachable
|
||||
at die (a[0]:0x23)
|
||||
at <unnamed> (a[1]:0x27)
|
||||
at foo (a[2]:0x2c)
|
||||
at <unnamed> (a[3]:0x31)
|
||||
at middle (b[1]:0x29)
|
||||
at <unnamed> (b[2]:0x2e)"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trap_start_function_import() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let binary = r#"
|
||||
(module $a
|
||||
(import "" "" (func $foo))
|
||||
(start $foo)
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let sig = FunctionType::new(vec![], vec![]);
|
||||
let func = Function::new(&store, &sig, |_| Err(RuntimeError::new("user trap")));
|
||||
let err = Instance::new(
|
||||
&module,
|
||||
&imports! {
|
||||
"" => {
|
||||
"" => func
|
||||
}
|
||||
},
|
||||
)
|
||||
.err()
|
||||
.unwrap();
|
||||
match err {
|
||||
InstantiationError::Link(_) | InstantiationError::HostEnvInitialization(_) => {
|
||||
panic!("It should be a start error")
|
||||
}
|
||||
InstantiationError::Start(err) => {
|
||||
assert_eq!(err.message(), "user trap");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rust_panic_import() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let binary = r#"
|
||||
(module $a
|
||||
(import "" "foo" (func $foo))
|
||||
(import "" "bar" (func $bar))
|
||||
(func (export "foo") call $foo)
|
||||
(func (export "bar") call $bar)
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let sig = FunctionType::new(vec![], vec![]);
|
||||
let func = Function::new(&store, &sig, |_| panic!("this is a panic"));
|
||||
let instance = Instance::new(
|
||||
&module,
|
||||
&imports! {
|
||||
"" => {
|
||||
"foo" => func,
|
||||
"bar" => Function::new_native(&store, || panic!("this is another panic"))
|
||||
}
|
||||
},
|
||||
)?;
|
||||
let func = instance.exports.get_function("foo")?.clone();
|
||||
let err = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
drop(func.call(&[]));
|
||||
}))
|
||||
.unwrap_err();
|
||||
assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
|
||||
|
||||
// TODO: Reenable this (disabled as it was not working with llvm/singlepass)
|
||||
// It doesn't work either with cranelift and `--test-threads=1`.
|
||||
// let func = instance.exports.get_function("bar")?.clone();
|
||||
// let err = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
// drop(func.call(&[]));
|
||||
// }))
|
||||
// .unwrap_err();
|
||||
// assert_eq!(
|
||||
// err.downcast_ref::<&'static str>(),
|
||||
// Some(&"this is another panic")
|
||||
// );
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rust_panic_start_function() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let binary = r#"
|
||||
(module $a
|
||||
(import "" "" (func $foo))
|
||||
(start $foo)
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let sig = FunctionType::new(vec![], vec![]);
|
||||
let func = Function::new(&store, &sig, |_| panic!("this is a panic"));
|
||||
let err = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
drop(Instance::new(
|
||||
&module,
|
||||
&imports! {
|
||||
"" => {
|
||||
"" => func
|
||||
}
|
||||
},
|
||||
));
|
||||
}))
|
||||
.unwrap_err();
|
||||
assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
|
||||
|
||||
let func = Function::new_native(&store, || panic!("this is another panic"));
|
||||
let err = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
drop(Instance::new(
|
||||
&module,
|
||||
&imports! {
|
||||
"" => {
|
||||
"" => func
|
||||
}
|
||||
},
|
||||
));
|
||||
}))
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
err.downcast_ref::<&'static str>(),
|
||||
Some(&"this is another panic")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mismatched_arguments() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let binary = r#"
|
||||
(module $a
|
||||
(func (export "foo") (param i32))
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let instance = Instance::new(&module, &imports! {})?;
|
||||
let func: &Function = instance.exports.get("foo")?;
|
||||
assert_eq!(
|
||||
func.call(&[]).unwrap_err().message(),
|
||||
"Parameters of type [] did not match signature [I32] -> []"
|
||||
);
|
||||
assert_eq!(
|
||||
func.call(&[Val::F32(0.0)]).unwrap_err().message(),
|
||||
"Parameters of type [F32] did not match signature [I32] -> []",
|
||||
);
|
||||
assert_eq!(
|
||||
func.call(&[Val::I32(0), Val::I32(1)])
|
||||
.unwrap_err()
|
||||
.message(),
|
||||
"Parameters of type [I32, I32] did not match signature [I32] -> []"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(
|
||||
feature = "test-singlepass",
|
||||
feature = "test-llvm",
|
||||
feature = "test-native",
|
||||
all(target_os = "macos", target_arch = "aarch64"),
|
||||
target_env = "musl",
|
||||
),
|
||||
ignore
|
||||
)]
|
||||
fn call_signature_mismatch() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let binary = r#"
|
||||
(module $a
|
||||
(func $foo
|
||||
i32.const 0
|
||||
call_indirect)
|
||||
(func $bar (param i32))
|
||||
(start $foo)
|
||||
|
||||
(table 1 anyfunc)
|
||||
(elem (i32.const 0) 1)
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let err = Instance::new(&module, &imports! {})
|
||||
.err()
|
||||
.expect("expected error");
|
||||
assert_eq!(
|
||||
format!("{}", err),
|
||||
"\
|
||||
RuntimeError: indirect call type mismatch
|
||||
at foo (a[0]:0x30)\
|
||||
"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(
|
||||
feature = "test-singlepass",
|
||||
feature = "test-llvm",
|
||||
feature = "test-native",
|
||||
target_arch = "aarch64",
|
||||
target_env = "musl",
|
||||
),
|
||||
ignore
|
||||
)]
|
||||
fn start_trap_pretty() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let wat = r#"
|
||||
(module $m
|
||||
(func $die unreachable)
|
||||
(func call $die)
|
||||
(func $foo call 1)
|
||||
(func $start call $foo)
|
||||
(start $start)
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(&store, wat)?;
|
||||
let err = Instance::new(&module, &imports! {})
|
||||
.err()
|
||||
.expect("expected error");
|
||||
|
||||
assert_eq!(
|
||||
format!("{}", err),
|
||||
"\
|
||||
RuntimeError: unreachable
|
||||
at die (m[0]:0x1d)
|
||||
at <unnamed> (m[1]:0x21)
|
||||
at foo (m[2]:0x26)
|
||||
at start (m[3]:0x2b)\
|
||||
"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn present_after_module_drop() -> Result<()> {
|
||||
let store = get_store(false);
|
||||
let module = Module::new(&store, r#"(func (export "foo") unreachable)"#)?;
|
||||
let instance = Instance::new(&module, &imports! {})?;
|
||||
let func: Function = instance.exports.get_function("foo")?.clone();
|
||||
|
||||
println!("asserting before we drop modules");
|
||||
assert_trap(func.call(&[]).unwrap_err());
|
||||
drop((instance, module));
|
||||
|
||||
println!("asserting after drop");
|
||||
assert_trap(func.call(&[]).unwrap_err());
|
||||
return Ok(());
|
||||
|
||||
fn assert_trap(t: RuntimeError) {
|
||||
println!("{}", t);
|
||||
// assert_eq!(t.trace().len(), 1);
|
||||
// assert_eq!(t.trace()[0].func_index(), 0);
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
use wasmer::{ModuleMiddleware, Store};
|
||||
use wasmer_compiler::CompilerConfig;
|
||||
use wasmer_engine::Engine;
|
||||
#[cfg(feature = "test-jit")]
|
||||
use wasmer_engine_jit::JIT;
|
||||
#[cfg(feature = "test-native")]
|
||||
use wasmer_engine_native::Native;
|
||||
|
||||
pub fn get_compiler(canonicalize_nans: bool) -> impl CompilerConfig {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(
|
||||
all(feature = "test-llvm", any(feature = "test-cranelift", feature = "test-singlepass")),
|
||||
all(feature = "test-cranelift", feature = "test-singlepass")
|
||||
))] {
|
||||
compile_error!("Only one compiler can be selected")
|
||||
} else if #[cfg(feature = "test-cranelift")] {
|
||||
let mut compiler = wasmer_compiler_cranelift::Cranelift::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler.enable_verifier();
|
||||
compiler
|
||||
} else if #[cfg(feature = "test-llvm")] {
|
||||
let mut compiler = wasmer_compiler_llvm::LLVM::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler.enable_verifier();
|
||||
compiler
|
||||
} else if #[cfg(feature = "test-singlepass")] {
|
||||
let mut compiler = wasmer_compiler_singlepass::Singlepass::new();
|
||||
compiler.canonicalize_nans(canonicalize_nans);
|
||||
compiler.enable_verifier();
|
||||
compiler
|
||||
} else {
|
||||
compile_error!("No compiler chosen for the tests")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-jit")]
|
||||
pub fn get_engine(canonicalize_nans: bool) -> impl Engine {
|
||||
let compiler_config = get_compiler(canonicalize_nans);
|
||||
JIT::new(compiler_config).engine()
|
||||
}
|
||||
#[cfg(feature = "test-native")]
|
||||
pub fn get_engine(canonicalize_nans: bool) -> impl Engine {
|
||||
let compiler_config = get_compiler(canonicalize_nans);
|
||||
Native::new(compiler_config).engine()
|
||||
}
|
||||
|
||||
pub fn get_store(canonicalize_nans: bool) -> Store {
|
||||
Store::new(&get_engine(canonicalize_nans))
|
||||
}
|
||||
|
||||
pub fn get_store_with_middlewares<I: Iterator<Item = Arc<dyn ModuleMiddleware>>>(
|
||||
middlewares: I,
|
||||
) -> Store {
|
||||
let mut compiler_config = get_compiler(false);
|
||||
for x in middlewares {
|
||||
compiler_config.push_middleware(x);
|
||||
}
|
||||
#[cfg(feature = "test-jit")]
|
||||
let engine = JIT::new(compiler_config).engine();
|
||||
#[cfg(feature = "test-native")]
|
||||
let engine = Native::new(compiler_config).engine();
|
||||
Store::new(&engine)
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-jit")]
|
||||
pub fn get_headless_store() -> Store {
|
||||
Store::new(&JIT::headless().engine())
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-native")]
|
||||
pub fn get_headless_store() -> Store {
|
||||
Store::new(&Native::headless().engine())
|
||||
}
|
@ -1,6 +1,3 @@
|
||||
#![cfg(all(feature = "compiler", feature = "engine"))]
|
||||
|
||||
use crate::utils::get_store;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use wasmer_wast::WasiTest;
|
||||
@ -17,12 +14,9 @@ use wasmer_wast::WasiTest;
|
||||
// }
|
||||
include!(concat!(env!("OUT_DIR"), "/generated_wasitests.rs"));
|
||||
|
||||
pub fn run_wasi(wast_path: &str, base_dir: &str, compiler: &str) -> anyhow::Result<()> {
|
||||
println!(
|
||||
"Running wasi wast `{}` with the {} compiler",
|
||||
wast_path, compiler
|
||||
);
|
||||
let store = get_store(true);
|
||||
pub fn run_wasi(config: crate::Config, wast_path: &str, base_dir: &str) -> anyhow::Result<()> {
|
||||
println!("Running wasi wast `{}`", wast_path);
|
||||
let store = config.store();
|
||||
|
||||
let source = {
|
||||
let mut out = String::new();
|
||||
|
@ -1,12 +1,5 @@
|
||||
#![cfg(all(feature = "compiler", feature = "engine"))]
|
||||
|
||||
use crate::utils::get_compiler;
|
||||
use ::wasmer::Features;
|
||||
use std::path::Path;
|
||||
use wasmer::{Features, Store};
|
||||
#[cfg(feature = "test-jit")]
|
||||
use wasmer_engine_jit::JIT;
|
||||
#[cfg(feature = "test-native")]
|
||||
use wasmer_engine_native::Native;
|
||||
use wasmer_wast::Wast;
|
||||
|
||||
// The generated tests (from build.rs) look like:
|
||||
@ -28,23 +21,8 @@ fn _native_prefixer(bytes: &[u8]) -> String {
|
||||
format!("{}", hash.to_hex())
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-jit")]
|
||||
fn get_store(features: Features, try_nan_canonicalization: bool) -> Store {
|
||||
let compiler_config = get_compiler(try_nan_canonicalization);
|
||||
Store::new(&JIT::new(compiler_config).features(features).engine())
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-native")]
|
||||
fn get_store(features: Features, try_nan_canonicalization: bool) -> Store {
|
||||
let compiler_config = get_compiler(try_nan_canonicalization);
|
||||
Store::new(&Native::new(compiler_config).features(features).engine())
|
||||
}
|
||||
|
||||
pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
|
||||
println!(
|
||||
"Running wast `{}` with the {} compiler",
|
||||
wast_path, compiler
|
||||
);
|
||||
pub fn run_wast(mut config: crate::Config, wast_path: &str) -> anyhow::Result<()> {
|
||||
println!("Running wast `{}`", wast_path);
|
||||
let try_nan_canonicalization = wast_path.contains("nan-canonicalization");
|
||||
let mut features = Features::default();
|
||||
let is_bulkmemory = wast_path.contains("bulk-memory");
|
||||
@ -58,7 +36,10 @@ pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
|
||||
if cfg!(feature = "test-singlepass") {
|
||||
features.multi_value(false);
|
||||
}
|
||||
let store = get_store(features, try_nan_canonicalization);
|
||||
config.set_features(features);
|
||||
config.set_nan_canonicalization(try_nan_canonicalization);
|
||||
|
||||
let store = config.store();
|
||||
let mut wast = Wast::new_with_spectest(store);
|
||||
// `bulk-memory-operations/bulk.wast` checks for a message that
|
||||
// specifies which element is uninitialized, but our traps don't
|
||||
@ -66,7 +47,7 @@ pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
|
||||
wast.allow_trap_message("uninitialized element 2", "uninitialized element");
|
||||
// `liking.wast` has different wording but the same meaning
|
||||
wast.allow_trap_message("out of bounds memory access", "memory out of bounds");
|
||||
if compiler == "cranelift" && cfg!(feature = "test-native") {
|
||||
if config.compiler == crate::Compiler::Cranelift && config.engine == crate::Engine::Native {
|
||||
wast.allow_trap_message("call stack exhausted", "out of bounds memory access");
|
||||
wast.allow_trap_message("indirect call type mismatch", "call stack exhausted");
|
||||
wast.allow_trap_message("integer divide by zero", "call stack exhausted");
|
||||
@ -87,7 +68,7 @@ pub fn run_wast(wast_path: &str, compiler: &str) -> anyhow::Result<()> {
|
||||
"Validation error: Invalid var_u32",
|
||||
]);
|
||||
}
|
||||
if compiler == "singlepass" {
|
||||
if config.compiler == crate::Compiler::Singlepass {
|
||||
// We don't support multivalue yet in singlepass
|
||||
wast.allow_instantiation_failures(&[
|
||||
"Validation error: invalid result arity: func type returns multiple values",
|
||||
|
@ -1,35 +1,38 @@
|
||||
# Compilers
|
||||
singlepass::spec::multi_value
|
||||
singlepass::spec::simd
|
||||
singlepass spec::multi_value
|
||||
singlepass spec::simd
|
||||
|
||||
singlepass on windows # Singlepass is not yet supported on Windows
|
||||
singlepass+windows *
|
||||
|
||||
# LLVM/JIT doesn't work in macOS M1. Skip all tests
|
||||
llvm+jit+macos+aarch64 *
|
||||
|
||||
# TODO: We need to fix this. The issue happens only in Cranelift/LLVM and macOS,
|
||||
# is caused by libunwind overflowing the stack while creating the stacktrace.
|
||||
# https://github.com/rust-lang/backtrace-rs/issues/356
|
||||
cranelift::spec::skip_stack_guard_page on darwin
|
||||
cranelift::spec::skip_stack_guard_page on linux
|
||||
llvm::spec::skip_stack_guard_page on darwin
|
||||
cranelift+macos spec::skip_stack_guard_page
|
||||
cranelift+linux spec::skip_stack_guard_page
|
||||
llvm+macos spec::skip_stack_guard_page
|
||||
|
||||
# https://github.com/wasmerio/wasmer/issues/1722
|
||||
llvm::spec::skip_stack_guard_page on native
|
||||
llvm+native spec::skip_stack_guard_page
|
||||
|
||||
# TODO(https://github.com/wasmerio/wasmer/issues/1727): Traps in native engine
|
||||
cranelift::spec::linking on native
|
||||
cranelift::spec::bulk on native
|
||||
cranelift+native spec::linking
|
||||
cranelift+native spec::bulk
|
||||
|
||||
# Some SIMD opperations are not yet supported by Cranelift
|
||||
cranelift::spec::simd::simd_boolean on aarch64
|
||||
cranelift::spec::simd::simd_conversions
|
||||
cranelift::spec::simd::simd_i16x8_extadd_pairwise_i8x16
|
||||
cranelift::spec::simd::simd_i16x8_extmul_i8x16
|
||||
cranelift::spec::simd::simd_i16x8_q15mulr_sat_s
|
||||
cranelift::spec::simd::simd_i32x4_extadd_pairwise_i16x8
|
||||
cranelift::spec::simd::simd_i32x4_extmul_i16x8
|
||||
cranelift::spec::simd::simd_i32x4_trunc_sat_f64x2
|
||||
cranelift::spec::simd::simd_i64x2_extmul_i32x4
|
||||
cranelift::spec::simd::simd_i8x16_arith2
|
||||
cranelift::spec::simd::simd_int_to_int_extend
|
||||
cranelift+aarch64 spec::simd::simd_boolean
|
||||
cranelift spec::simd::simd_conversions
|
||||
cranelift spec::simd::simd_i16x8_extadd_pairwise_i8x16
|
||||
cranelift spec::simd::simd_i16x8_extmul_i8x16
|
||||
cranelift spec::simd::simd_i16x8_q15mulr_sat_s
|
||||
cranelift spec::simd::simd_i32x4_extadd_pairwise_i16x8
|
||||
cranelift spec::simd::simd_i32x4_extmul_i16x8
|
||||
cranelift spec::simd::simd_i32x4_trunc_sat_f64x2
|
||||
cranelift spec::simd::simd_i64x2_extmul_i32x4
|
||||
cranelift spec::simd::simd_i8x16_arith2
|
||||
cranelift spec::simd::simd_int_to_int_extend
|
||||
|
||||
# Frontends
|
||||
|
||||
@ -66,19 +69,19 @@ wasitests::unstable::poll_oneoff
|
||||
## Failing due to different line endings on Windows
|
||||
## we need a better solution to this problem:
|
||||
|
||||
wasitests::snapshot1::file_metadata on windows
|
||||
wasitests::snapshot1::fseek on windows
|
||||
wasitests::snapshot1::path_link on windows
|
||||
wasitests::snapshot1::path_symlink on windows
|
||||
wasitests::snapshot1::mapdir_with_leading_slash on windows
|
||||
wasitests::unstable::fd_pread on windows
|
||||
wasitests::unstable::fd_read on windows
|
||||
wasitests::unstable::file_metadata on windows
|
||||
wasitests::unstable::fseek on windows
|
||||
wasitests::unstable::path_link on windows
|
||||
wasitests::unstable::path_symlink on windows
|
||||
wasitests::unstable::mapdir_with_leading_slash on windows
|
||||
windows wasitests::snapshot1::file_metadata
|
||||
windows wasitests::snapshot1::fseek
|
||||
windows wasitests::snapshot1::path_link
|
||||
windows wasitests::snapshot1::path_symlink
|
||||
windows wasitests::snapshot1::mapdir_with_leading_slash
|
||||
windows wasitests::unstable::fd_pread
|
||||
windows wasitests::unstable::fd_read
|
||||
windows wasitests::unstable::file_metadata
|
||||
windows wasitests::unstable::fseek
|
||||
windows wasitests::unstable::path_link
|
||||
windows wasitests::unstable::path_symlink
|
||||
windows wasitests::unstable::mapdir_with_leading_slash
|
||||
|
||||
# This test is meant to only run on Unix
|
||||
wasitests::unstable::unix_open_special_files on windows
|
||||
wasitests::snapshot1::unix_open_special_files on windows
|
||||
windows wasitests::unstable::unix_open_special_files
|
||||
windows wasitests::snapshot1::unix_open_special_files
|
||||
|
23
tests/lib/compiler-test-derive/Cargo.toml
Normal file
23
tests/lib/compiler-test-derive/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[package]
|
||||
name = "compiler-test-derive"
|
||||
version = "0.0.1"
|
||||
authors = ["Wasmer Engineering <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
|
||||
description = "A macro to generate easily tests across compilers and engines"
|
||||
keywords = ["unsafe", "body", "fn", "safety", "hygiene"]
|
||||
categories = ["rust-patterns", ]
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.*"
|
||||
quote = "1.*"
|
||||
syn = { version = "1.*", features = ["full"] }
|
||||
|
||||
[features]
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.6.1"
|
||||
trybuild = "1.0.11"
|
9
tests/lib/compiler-test-derive/build.rs
Normal file
9
tests/lib/compiler-test-derive/build.rs
Normal file
@ -0,0 +1,9 @@
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=../../ignores.txt");
|
||||
if let Ok(os) = std::env::var("CARGO_CFG_TARGET_OS") {
|
||||
println!("cargo:rustc-env=CFG_TARGET_OS={}", os);
|
||||
}
|
||||
if let Ok(os) = std::env::var("CARGO_CFG_TARGET_ARCH") {
|
||||
println!("cargo:rustc-env=CFG_TARGET_ARCH={}", os);
|
||||
}
|
||||
}
|
215
tests/lib/compiler-test-derive/src/ignores.rs
Normal file
215
tests/lib/compiler-test-derive/src/ignores.rs
Normal file
@ -0,0 +1,215 @@
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
pub const CFG_TARGET_OS: &'static str = env!("CFG_TARGET_OS");
|
||||
pub const CFG_TARGET_ARCH: &'static str = env!("CFG_TARGET_OS");
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct IgnorePattern {
|
||||
os: Option<String>,
|
||||
arch: Option<String>,
|
||||
engine: Option<String>,
|
||||
compiler: Option<String>,
|
||||
pattern_to_ignore: String,
|
||||
}
|
||||
|
||||
impl IgnorePattern {
|
||||
fn should_ignore(
|
||||
&self,
|
||||
os: &str,
|
||||
arch: &str,
|
||||
engine: &str,
|
||||
compiler: &str,
|
||||
canonical_path: &str,
|
||||
) -> bool {
|
||||
self.os.as_ref().map_or(true, |val| val == os)
|
||||
&& self.arch.as_ref().map_or(true, |val| val == arch)
|
||||
&& self.engine.as_ref().map_or(true, |val| val == engine)
|
||||
&& self.compiler.as_ref().map_or(true, |val| val == compiler)
|
||||
&& (self.pattern_to_ignore == "*" || canonical_path.contains(&*self.pattern_to_ignore))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Ignores {
|
||||
/// The canonical path, and the set of features
|
||||
patterns: Vec<IgnorePattern>,
|
||||
}
|
||||
|
||||
impl Ignores {
|
||||
/// If the path matches any of the paths on the list
|
||||
pub fn should_ignore(
|
||||
&self,
|
||||
os: &str,
|
||||
arch: &str,
|
||||
engine: &str,
|
||||
compiler: &str,
|
||||
canonical_path: &str,
|
||||
) -> bool {
|
||||
self.patterns.iter().any(|p| {
|
||||
// println!(" -> {:?}", p);
|
||||
p.should_ignore(os, arch, engine, compiler, canonical_path)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn should_ignore_host(&self, engine: &str, compiler: &str, canonical_path: &str) -> bool {
|
||||
self.should_ignore(
|
||||
CFG_TARGET_OS,
|
||||
CFG_TARGET_ARCH,
|
||||
engine,
|
||||
compiler,
|
||||
canonical_path,
|
||||
)
|
||||
}
|
||||
|
||||
/// Build a Ignore structure from a file path
|
||||
pub fn build_from_path(path: PathBuf) -> Ignores {
|
||||
let file = File::open(path).unwrap();
|
||||
let reader = BufReader::new(file);
|
||||
let mut patterns = Vec::new();
|
||||
// if cfg!(target_arch = "aarch64") {
|
||||
// host_features.insert("aarch64".to_string());
|
||||
// } else if cfg!(target_arch = "x86-64") {
|
||||
// host_features.insert("aarch64".to_string());
|
||||
// } else {
|
||||
// host_features.insert("unknown-arch".to_string());
|
||||
// };
|
||||
|
||||
// if cfg!(target_os = "macos") {
|
||||
// host_features.insert("macos".to_string());
|
||||
// host_features.insert("unix".to_string());
|
||||
// } else if cfg!(target_os = "linux") {
|
||||
// host_features.insert("linux".to_string());
|
||||
// host_features.insert("unix".to_string());
|
||||
// } else if cfg!(target_os = "windows") {
|
||||
// host_features.insert("windows".to_string());
|
||||
// } else {
|
||||
// host_features.insert("unknown-arch".to_string());
|
||||
// };
|
||||
|
||||
for line in reader.lines() {
|
||||
let line = line.unwrap();
|
||||
// If the line has a `#` we discard all the content that comes after
|
||||
let line = if line.contains('#') {
|
||||
let l: Vec<&str> = line.split('#').collect();
|
||||
l.get(0).unwrap().to_string()
|
||||
} else {
|
||||
line
|
||||
};
|
||||
|
||||
let line = line.trim().to_string();
|
||||
|
||||
// If the lines contains ` ` it means the test should be ignored
|
||||
// on the features exposed
|
||||
if line.contains(" ") {
|
||||
let l: Vec<&str> = line.splitn(2, " ").collect();
|
||||
let mut os: Option<String> = None;
|
||||
let mut arch: Option<String> = None;
|
||||
let mut engine: Option<String> = None;
|
||||
let mut compiler: Option<String> = None;
|
||||
for alias in l.get(0).unwrap().trim().split("+") {
|
||||
match alias {
|
||||
"aarch64" | "x86" | "x64" => {
|
||||
arch = Some(alias.to_string());
|
||||
}
|
||||
"windows" | "macos" | "linux" => {
|
||||
os = Some(alias.to_string());
|
||||
}
|
||||
"jit" | "native" => {
|
||||
engine = Some(alias.to_string());
|
||||
}
|
||||
"cranelift" | "llvm" | "singlepass" => {
|
||||
compiler = Some(alias.to_string());
|
||||
}
|
||||
_ => {
|
||||
panic!("Alias {} not currently supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
let pattern_to_ignore = l.get(1).unwrap().trim().to_string();
|
||||
patterns.push(IgnorePattern {
|
||||
os,
|
||||
arch,
|
||||
engine,
|
||||
compiler,
|
||||
pattern_to_ignore,
|
||||
});
|
||||
} else {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
patterns.push(IgnorePattern {
|
||||
os: None,
|
||||
arch: None,
|
||||
engine: None,
|
||||
compiler: None,
|
||||
pattern_to_ignore: line,
|
||||
});
|
||||
};
|
||||
}
|
||||
Ignores { patterns }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn features_match() -> Result<(), ()> {
|
||||
assert!(IgnorePattern {
|
||||
os: None,
|
||||
arch: None,
|
||||
engine: None,
|
||||
compiler: None,
|
||||
pattern_to_ignore: "*".to_string()
|
||||
}
|
||||
.should_ignore(
|
||||
"unknown",
|
||||
"unknown",
|
||||
"engine",
|
||||
"compiler",
|
||||
"some::random::text"
|
||||
));
|
||||
assert!(IgnorePattern {
|
||||
os: None,
|
||||
arch: None,
|
||||
engine: None,
|
||||
compiler: None,
|
||||
pattern_to_ignore: "some::random".to_string()
|
||||
}
|
||||
.should_ignore(
|
||||
"unknown",
|
||||
"unknown",
|
||||
"engine",
|
||||
"compiler",
|
||||
"some::random::text"
|
||||
));
|
||||
assert!(!IgnorePattern {
|
||||
os: Some("macos".to_string()),
|
||||
arch: None,
|
||||
engine: None,
|
||||
compiler: None,
|
||||
pattern_to_ignore: "other".to_string()
|
||||
}
|
||||
.should_ignore(
|
||||
"unknown",
|
||||
"unknown",
|
||||
"engine",
|
||||
"compiler",
|
||||
"some::random::text"
|
||||
));
|
||||
assert!(!IgnorePattern {
|
||||
os: Some("macos".to_string()),
|
||||
arch: None,
|
||||
engine: Some("jit".to_string()),
|
||||
compiler: None,
|
||||
pattern_to_ignore: "other".to_string()
|
||||
}
|
||||
.should_ignore("macos", "unknown", "jit", "compiler", "some::random::text"));
|
||||
Ok(())
|
||||
}
|
||||
}
|
142
tests/lib/compiler-test-derive/src/lib.rs
Normal file
142
tests/lib/compiler-test-derive/src/lib.rs
Normal file
@ -0,0 +1,142 @@
|
||||
#[cfg(not(test))]
|
||||
extern crate proc_macro;
|
||||
#[cfg(not(test))]
|
||||
use ::proc_macro::TokenStream;
|
||||
#[cfg(test)]
|
||||
use ::proc_macro2::TokenStream;
|
||||
use ::quote::quote;
|
||||
#[cfg(not(test))]
|
||||
use ::syn::parse;
|
||||
#[cfg(test)]
|
||||
use ::syn::parse2 as parse;
|
||||
// use std::iter::FromIterator;
|
||||
use ::syn::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
mod ignores;
|
||||
|
||||
// Reimplement parse_macro_input to use the imported `parse`
|
||||
// function. This way parse_macro_input will parse a TokenStream2 when
|
||||
// unit-testing.
|
||||
macro_rules! parse_macro_input {
|
||||
(
|
||||
$token_stream:ident as $T:ty
|
||||
) => {
|
||||
match parse::<$T>($token_stream) {
|
||||
Ok(data) => data,
|
||||
Err(err) => {
|
||||
return TokenStream::from(err.to_compile_error());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
$token_stream:ident
|
||||
) => {
|
||||
parse_macro_input!($token_stream as _)
|
||||
};
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn compiler_test(attrs: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let path: Option<ExprPath> = parse::<ExprPath>(attrs).ok();
|
||||
let my_fn: ItemFn = parse_macro_input!(input as ItemFn);
|
||||
let fn_name = my_fn.sig.ident.clone();
|
||||
|
||||
// Let's build the ignores to append an `#[ignore]` macro to the
|
||||
// autogenerated tests in case the test appears in the `ignores.txt` path;
|
||||
|
||||
let mut ignores_txt_path = PathBuf::new();
|
||||
ignores_txt_path.push(env!("CARGO_MANIFEST_DIR"));
|
||||
ignores_txt_path.push("../../ignores.txt");
|
||||
|
||||
let ignores = crate::ignores::Ignores::build_from_path(ignores_txt_path);
|
||||
|
||||
let should_ignore = |test_name: &str, compiler_name: &str, engine_name: &str| {
|
||||
let compiler_name = compiler_name.to_lowercase();
|
||||
let engine_name = engine_name.to_lowercase();
|
||||
// We construct the path manually because we can't get the
|
||||
// source_file location from the `Span` (it's only available in nightly)
|
||||
let full_path = format!(
|
||||
"{}::{}::{}::{}",
|
||||
quote! { #path },
|
||||
test_name,
|
||||
compiler_name,
|
||||
engine_name
|
||||
)
|
||||
.replace(" ", "");
|
||||
let should_ignore = ignores.should_ignore_host(&engine_name, &compiler_name, &full_path);
|
||||
// println!("{} -> Should ignore: {}", full_path, should_ignore);
|
||||
return should_ignore;
|
||||
};
|
||||
let construct_engine_test = |sig: &::syn::Signature,
|
||||
compiler_name: &str,
|
||||
engine_name: &str|
|
||||
-> ::proc_macro2::TokenStream {
|
||||
let config_compiler = ::quote::format_ident!("{}", compiler_name);
|
||||
let config_engine = ::quote::format_ident!("{}", engine_name);
|
||||
let engine_name_lowercase = engine_name.to_lowercase();
|
||||
let test_name = ::quote::format_ident!("{}", engine_name_lowercase);
|
||||
let mut new_sig = sig.clone();
|
||||
new_sig.ident = test_name;
|
||||
new_sig.inputs = ::syn::punctuated::Punctuated::new();
|
||||
let func = quote! {
|
||||
#[test]
|
||||
#[cfg(feature = #engine_name_lowercase)]
|
||||
#new_sig {
|
||||
#fn_name(crate::Config::new(crate::Engine::#config_engine, crate::Compiler::#config_compiler))
|
||||
}
|
||||
};
|
||||
if should_ignore(
|
||||
&sig.ident.to_string().replace("r#", ""),
|
||||
compiler_name,
|
||||
engine_name,
|
||||
) {
|
||||
quote! {
|
||||
#[ignore]
|
||||
#func
|
||||
}
|
||||
} else {
|
||||
func
|
||||
}
|
||||
};
|
||||
|
||||
let construct_compiler_test =
|
||||
|sig: &::syn::Signature, compiler_name: &str| -> ::proc_macro2::TokenStream {
|
||||
let mod_name = ::quote::format_ident!("{}", compiler_name.to_lowercase());
|
||||
let jit_engine_test = construct_engine_test(sig, compiler_name, "JIT");
|
||||
let native_engine_test = construct_engine_test(sig, compiler_name, "Native");
|
||||
let compiler_name_lowercase = compiler_name.to_lowercase();
|
||||
|
||||
quote! {
|
||||
#[cfg(feature = #compiler_name_lowercase)]
|
||||
mod #mod_name {
|
||||
use super::*;
|
||||
|
||||
#jit_engine_test
|
||||
#native_engine_test
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let singlepass_compiler_test = construct_compiler_test(&my_fn.sig, "Singlepass");
|
||||
let cranelift_compiler_test = construct_compiler_test(&my_fn.sig, "Cranelift");
|
||||
let llvm_compiler_test = construct_compiler_test(&my_fn.sig, "LLVM");
|
||||
|
||||
let x = quote! {
|
||||
#[cfg(test)]
|
||||
mod #fn_name {
|
||||
use super::*;
|
||||
|
||||
#my_fn
|
||||
|
||||
#singlepass_compiler_test
|
||||
#cranelift_compiler_test
|
||||
#llvm_compiler_test
|
||||
}
|
||||
};
|
||||
x.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
122
tests/lib/compiler-test-derive/src/tests.rs
Normal file
122
tests/lib/compiler-test-derive/src/tests.rs
Normal file
@ -0,0 +1,122 @@
|
||||
use super::*;
|
||||
use ::pretty_assertions::assert_eq;
|
||||
|
||||
macro_rules! gen_tests {(
|
||||
$(
|
||||
$test_name:ident:
|
||||
stringify! {
|
||||
#[$function:ident $(($($attrs:tt)*))?]
|
||||
$($input:tt)*
|
||||
} == $output:expr;
|
||||
)*
|
||||
) => (
|
||||
$(
|
||||
#[test]
|
||||
fn $test_name ()
|
||||
{
|
||||
let input: TokenStream =
|
||||
stringify!($($input)*)
|
||||
.parse()
|
||||
.expect("Syntax error in test")
|
||||
;
|
||||
let output: TokenStream =
|
||||
$output
|
||||
.parse()
|
||||
.expect("Syntax error in test")
|
||||
;
|
||||
let attrs: TokenStream =
|
||||
stringify!($($($attrs)*)?)
|
||||
.parse()
|
||||
.expect("Syntax error in test");
|
||||
let ret = $function(attrs, input).to_string();
|
||||
eprintln!("{}", ret);
|
||||
assert_eq!(
|
||||
ret,
|
||||
output.to_string(),
|
||||
)
|
||||
}
|
||||
)*
|
||||
)}
|
||||
|
||||
gen_tests! {
|
||||
identity_for_no_unsafe:
|
||||
stringify! {
|
||||
#[compiler_test]
|
||||
#[cold]
|
||||
fn add (config: crate::Config)
|
||||
{
|
||||
// Do tests
|
||||
}
|
||||
} == stringify! {
|
||||
#[cfg(test)]
|
||||
mod add {
|
||||
use super::*;
|
||||
|
||||
#[cold]
|
||||
fn add(config: crate::Config)
|
||||
{
|
||||
// Do tests
|
||||
}
|
||||
|
||||
mod singlepass {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn jit() {
|
||||
add(crate::Config::new(
|
||||
crate::Engine::JIT,
|
||||
crate::Compiler::Singlepass
|
||||
))
|
||||
}
|
||||
#[test]
|
||||
fn native() {
|
||||
add(crate::Config::new(
|
||||
crate::Engine::Native,
|
||||
crate::Compiler::Singlepass
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
mod cranelift {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn jit() {
|
||||
add(crate::Config::new(
|
||||
crate::Engine::JIT,
|
||||
crate::Compiler::Cranelift
|
||||
))
|
||||
}
|
||||
#[test]
|
||||
fn native() {
|
||||
add(crate::Config::new(
|
||||
crate::Engine::Native,
|
||||
crate::Compiler::Cranelift
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
mod llvm {
|
||||
use super::add;
|
||||
#[test]
|
||||
fn jit() {
|
||||
add(crate::Config::new(
|
||||
crate::Engine::JIT,
|
||||
crate::Compiler::LLVM
|
||||
))
|
||||
}
|
||||
#[test]
|
||||
fn native() {
|
||||
add(crate::Config::new(
|
||||
crate::Engine::Native,
|
||||
crate::Compiler::LLVM
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// basic_expansion:
|
||||
// stringify! {
|
||||
// ...
|
||||
// };
|
||||
|
||||
}
|
@ -16,21 +16,9 @@ use std::io::{BufRead, BufReader};
|
||||
use std::path::{Path, PathBuf};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
pub type Ignores = HashSet<String>;
|
||||
pub struct Testsuite {
|
||||
pub buffer: String,
|
||||
pub path: Vec<String>,
|
||||
pub ignores: Ignores,
|
||||
}
|
||||
|
||||
impl Testsuite {
|
||||
fn ignore_current(&self) -> bool {
|
||||
let full = self.path.join("::");
|
||||
if self.ignores.contains(&full) {
|
||||
return true;
|
||||
}
|
||||
self.ignores.iter().any(|ignore| full.contains(ignore))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
@ -41,59 +29,6 @@ pub struct Test {
|
||||
|
||||
pub type ProcessorType = fn(&mut Testsuite, PathBuf) -> Option<Test>;
|
||||
|
||||
/// Generates an Ignores struct from a text file
|
||||
pub fn build_ignores_from_textfile(path: PathBuf) -> anyhow::Result<Ignores> {
|
||||
let mut ignores = HashSet::new();
|
||||
let file = File::open(path)?;
|
||||
let reader = BufReader::new(file);
|
||||
let host = Triple::host().to_string();
|
||||
let engine = if cfg!(feature = "test-native") {
|
||||
Some("native")
|
||||
} else if cfg!(feature = "test-jit") {
|
||||
Some("jit")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
for line in reader.lines() {
|
||||
let line = line.unwrap();
|
||||
// If the line has a `#` we discard all the content that comes after
|
||||
let line = if line.contains('#') {
|
||||
let l: Vec<&str> = line.split('#').collect();
|
||||
l.get(0).unwrap().to_string()
|
||||
} else {
|
||||
line
|
||||
};
|
||||
|
||||
let line = line.trim().to_string();
|
||||
|
||||
// If the lines contains ` on ` it means the test should be ignored
|
||||
// on that platform
|
||||
let (line, target) = if line.contains(" on ") {
|
||||
let l: Vec<&str> = line.split(" on ").collect();
|
||||
(
|
||||
l.get(0).unwrap().to_string(),
|
||||
Some(l.get(1).unwrap().to_string()),
|
||||
)
|
||||
} else {
|
||||
(line, None)
|
||||
};
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We skip the ignore if doesn't apply to the current
|
||||
// host target or engine
|
||||
if target.clone().map(|t| !host.contains(&t)).unwrap_or(false)
|
||||
&& target.clone() != engine.map(str::to_string)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ignores.insert(line);
|
||||
}
|
||||
Ok(ignores)
|
||||
}
|
||||
|
||||
pub fn test_directory_module(
|
||||
out: &mut Testsuite,
|
||||
path: impl AsRef<Path>,
|
||||
@ -105,11 +40,16 @@ pub fn test_directory_module(
|
||||
}
|
||||
|
||||
fn write_test(out: &mut Testsuite, testname: &str, body: &str) -> anyhow::Result<()> {
|
||||
writeln!(out.buffer, "#[test]")?;
|
||||
if out.ignore_current() {
|
||||
writeln!(out.buffer, "#[ignore]")?;
|
||||
}
|
||||
writeln!(out.buffer, "fn r#{}() -> anyhow::Result<()> {{", &testname)?;
|
||||
writeln!(
|
||||
out.buffer,
|
||||
"#[compiler_test({})]",
|
||||
out.path[..out.path.len() - 1].join("::")
|
||||
)?;
|
||||
writeln!(
|
||||
out.buffer,
|
||||
"fn r#{}(config: crate::Config) -> anyhow::Result<()> {{",
|
||||
&testname
|
||||
)?;
|
||||
writeln!(out.buffer, "{}", body)?;
|
||||
writeln!(out.buffer, "}}")?;
|
||||
writeln!(out.buffer)?;
|
||||
@ -171,17 +111,3 @@ pub fn with_test_module<T>(
|
||||
out.path.pop().unwrap();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn with_features(
|
||||
mut out: &mut Testsuite,
|
||||
features: &[&str],
|
||||
f: impl Fn(&mut Testsuite) -> anyhow::Result<()> + Copy,
|
||||
) -> anyhow::Result<()> {
|
||||
for compiler in features.iter() {
|
||||
writeln!(out.buffer, "#[cfg(feature=\"test-{}\")]", compiler)?;
|
||||
writeln!(out.buffer, "#[cfg(test)]")?;
|
||||
writeln!(out.buffer, "#[allow(non_snake_case)]")?;
|
||||
with_test_module(&mut out, &compiler, f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -17,10 +17,9 @@ pub fn wast_processor(out: &mut Testsuite, p: PathBuf) -> Option<Test> {
|
||||
}
|
||||
|
||||
let testname = extract_name(&p);
|
||||
let compiler = out.path.get(0).unwrap();
|
||||
|
||||
// The implementation of `run_wast` lives in /tests/spectest.rs
|
||||
let body = format!("crate::run_wast(r#\"{}\"#, \"{}\")", p.display(), compiler);
|
||||
let body = format!("crate::run_wast(config, r#\"{}\"#)", p.display());
|
||||
|
||||
Some(Test {
|
||||
name: testname,
|
||||
@ -48,14 +47,12 @@ pub fn emscripten_processor(out: &mut Testsuite, p: PathBuf) -> Option<Test> {
|
||||
};
|
||||
|
||||
let testname = extract_name(&p);
|
||||
let compiler = out.path.get(0).unwrap();
|
||||
|
||||
// The implementation of `run_emscripten` lives in /tests/emtest.rs
|
||||
let body = format!(
|
||||
"crate::emscripten::run_emscripten(r#\"{}\"#, r#\"{}\"#, \"{}\")",
|
||||
"crate::emscripten::run_emscripten(config, r#\"{}\"#, r#\"{}\"#)",
|
||||
p.display(),
|
||||
outfile.display(),
|
||||
compiler
|
||||
outfile.display()
|
||||
);
|
||||
|
||||
Some(Test {
|
||||
@ -79,14 +76,12 @@ pub fn wasi_processor(out: &mut Testsuite, p: PathBuf) -> Option<Test> {
|
||||
inner
|
||||
};
|
||||
let testname = extract_name(&p);
|
||||
let compiler = out.path.get(0).unwrap();
|
||||
|
||||
// The implementation of `run_wasi` lives in /tests/wasitest.rs
|
||||
let body = format!(
|
||||
"crate::run_wasi(r#\"{}\"#, \"{}\", \"{}\")",
|
||||
"crate::run_wasi(config, r#\"{}\"#, \"{}\")",
|
||||
p.display(),
|
||||
wasm_dir.display(),
|
||||
compiler
|
||||
);
|
||||
|
||||
Some(Test {
|
||||
|
Reference in New Issue
Block a user