From 9410d17cb6516d9b8f4ef1d877afc2aeedee0073 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 25 Jan 2019 11:52:12 +0100 Subject: [PATCH] Rejig the repo structure --- .gitmodules | 2 +- Cargo.toml | 40 +- wasmi/Cargo.toml | 34 ++ .../res}/fixtures/accumulate_u8.wast | 0 {res => wasmi/res}/fixtures/inc_i32.wast | 0 {src => wasmi/src}/bin/instantiate.rs | 0 {src => wasmi/src}/common/mod.rs | 0 {src => wasmi/src}/common/stack.rs | 0 {src => wasmi/src}/derive_support.rs | 0 {src => wasmi/src}/func.rs | 0 {src => wasmi/src}/global.rs | 0 {src => wasmi/src}/host.rs | 0 {src => wasmi/src}/imports.rs | 0 {src => wasmi/src}/isa.rs | 0 {src => wasmi/src}/lib.rs | 0 {src => wasmi/src}/memory.rs | 0 {src => wasmi/src}/module.rs | 0 {src => wasmi/src}/nan_preserving_float.rs | 0 {src => wasmi/src}/runner.rs | 0 {src => wasmi/src}/table.rs | 0 {src => wasmi/src}/tests/host.rs | 0 {src => wasmi/src}/tests/mod.rs | 0 {src => wasmi/src}/tests/wasm.rs | 0 {src => wasmi/src}/types.rs | 0 {src => wasmi/src}/validation/context.rs | 0 {src => wasmi/src}/validation/func.rs | 0 {src => wasmi/src}/validation/mod.rs | 0 {src => wasmi/src}/validation/tests.rs | 0 {src => wasmi/src}/validation/util.rs | 0 {src => wasmi/src}/value.rs | 0 wasmi/tests/derives.rs | 36 ++ wasmi/tests/spec/mod.rs | 83 +++ wasmi/tests/spec/run.rs | 503 ++++++++++++++++++ {tests => wasmi/tests}/spec/testsuite | 0 wasmi/tests/spec_shim.rs | 6 + 35 files changed, 667 insertions(+), 37 deletions(-) create mode 100644 wasmi/Cargo.toml rename {res => wasmi/res}/fixtures/accumulate_u8.wast (100%) rename {res => wasmi/res}/fixtures/inc_i32.wast (100%) rename {src => wasmi/src}/bin/instantiate.rs (100%) rename {src => wasmi/src}/common/mod.rs (100%) rename {src => wasmi/src}/common/stack.rs (100%) rename {src => wasmi/src}/derive_support.rs (100%) rename {src => wasmi/src}/func.rs (100%) rename {src => wasmi/src}/global.rs (100%) rename {src => wasmi/src}/host.rs (100%) rename {src => wasmi/src}/imports.rs (100%) rename {src => wasmi/src}/isa.rs (100%) rename {src => wasmi/src}/lib.rs (100%) rename {src => wasmi/src}/memory.rs (100%) rename {src => wasmi/src}/module.rs (100%) rename {src => wasmi/src}/nan_preserving_float.rs (100%) rename {src => wasmi/src}/runner.rs (100%) rename {src => wasmi/src}/table.rs (100%) rename {src => wasmi/src}/tests/host.rs (100%) rename {src => wasmi/src}/tests/mod.rs (100%) rename {src => wasmi/src}/tests/wasm.rs (100%) rename {src => wasmi/src}/types.rs (100%) rename {src => wasmi/src}/validation/context.rs (100%) rename {src => wasmi/src}/validation/func.rs (100%) rename {src => wasmi/src}/validation/mod.rs (100%) rename {src => wasmi/src}/validation/tests.rs (100%) rename {src => wasmi/src}/validation/util.rs (100%) rename {src => wasmi/src}/value.rs (100%) create mode 100644 wasmi/tests/derives.rs create mode 100644 wasmi/tests/spec/mod.rs create mode 100644 wasmi/tests/spec/run.rs rename {tests => wasmi/tests}/spec/testsuite (100%) create mode 100644 wasmi/tests/spec_shim.rs diff --git a/.gitmodules b/.gitmodules index 4bceed0..322870c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "tests/spec/testsuite"] - path = tests/spec/testsuite + path = wasmi/tests/spec/testsuite url = https://github.com/WebAssembly/testsuite.git diff --git a/Cargo.toml b/Cargo.toml index 128f039..990cc57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,37 +1,5 @@ -[package] -name = "wasmi" -version = "0.4.3" -authors = ["Nikolay Volf ", "Svyatoslav Nikolsky ", "Sergey Pepyakin "] -license = "MIT/Apache-2.0" -readme = "README.md" -repository = "https://github.com/paritytech/wasmi" -documentation = "https://paritytech.github.io/wasmi/" -description = "WebAssembly interpreter" -keywords = ["wasm", "webassembly", "bytecode", "interpreter"] -exclude = [ "/res/*", "/tests/*", "/fuzz/*", "/benches/*" ] - -[features] -default = ["std"] -# Disable for no_std support -std = ["parity-wasm/std", "byteorder/std"] -# Enable for no_std support -# hashmap_core only works on no_std -core = ["hashmap_core", "libm"] -derive = ["wasmi-derive"] - -[dependencies] -parity-wasm = { version = "0.31", default-features = false } -byteorder = { version = "1.0", default-features = false } -hashmap_core = { version = "0.1.9", optional = true } -memory_units = "0.3.0" -libm = { version = "0.1.2", optional = true } -wasmi-derive = { version = "0.1", path = "derive", optional = true } - -[dev-dependencies] -assert_matches = "1.1" -rand = "0.4.2" -wabt = "0.6" -wasmi-derive = { version = "0.1", path = "derive" } - [workspace] -members = ["derive"] +members = [ + "wasmi", + "derive", +] diff --git a/wasmi/Cargo.toml b/wasmi/Cargo.toml new file mode 100644 index 0000000..0271c8b --- /dev/null +++ b/wasmi/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "wasmi" +version = "0.4.3" +authors = ["Nikolay Volf ", "Svyatoslav Nikolsky ", "Sergey Pepyakin "] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/paritytech/wasmi" +documentation = "https://paritytech.github.io/wasmi/" +description = "WebAssembly interpreter" +keywords = ["wasm", "webassembly", "bytecode", "interpreter"] +exclude = [ "/res/*", "/tests/*", "/fuzz/*", "/benches/*" ] + +[features] +default = ["std"] +# Disable for no_std support +std = ["parity-wasm/std", "byteorder/std"] +# Enable for no_std support +# hashmap_core only works on no_std +core = ["hashmap_core", "libm"] +derive = ["wasmi-derive"] + +[dependencies] +parity-wasm = { version = "0.31", default-features = false } +byteorder = { version = "1.0", default-features = false } +hashmap_core = { version = "0.1.9", optional = true } +memory_units = "0.3.0" +libm = { version = "0.1.2", optional = true } +wasmi-derive = { version = "0.1", path = "../derive", optional = true } + +[dev-dependencies] +assert_matches = "1.1" +rand = "0.4.2" +wabt = "0.6" +wasmi-derive = { version = "0.1", path = "../derive" } diff --git a/res/fixtures/accumulate_u8.wast b/wasmi/res/fixtures/accumulate_u8.wast similarity index 100% rename from res/fixtures/accumulate_u8.wast rename to wasmi/res/fixtures/accumulate_u8.wast diff --git a/res/fixtures/inc_i32.wast b/wasmi/res/fixtures/inc_i32.wast similarity index 100% rename from res/fixtures/inc_i32.wast rename to wasmi/res/fixtures/inc_i32.wast diff --git a/src/bin/instantiate.rs b/wasmi/src/bin/instantiate.rs similarity index 100% rename from src/bin/instantiate.rs rename to wasmi/src/bin/instantiate.rs diff --git a/src/common/mod.rs b/wasmi/src/common/mod.rs similarity index 100% rename from src/common/mod.rs rename to wasmi/src/common/mod.rs diff --git a/src/common/stack.rs b/wasmi/src/common/stack.rs similarity index 100% rename from src/common/stack.rs rename to wasmi/src/common/stack.rs diff --git a/src/derive_support.rs b/wasmi/src/derive_support.rs similarity index 100% rename from src/derive_support.rs rename to wasmi/src/derive_support.rs diff --git a/src/func.rs b/wasmi/src/func.rs similarity index 100% rename from src/func.rs rename to wasmi/src/func.rs diff --git a/src/global.rs b/wasmi/src/global.rs similarity index 100% rename from src/global.rs rename to wasmi/src/global.rs diff --git a/src/host.rs b/wasmi/src/host.rs similarity index 100% rename from src/host.rs rename to wasmi/src/host.rs diff --git a/src/imports.rs b/wasmi/src/imports.rs similarity index 100% rename from src/imports.rs rename to wasmi/src/imports.rs diff --git a/src/isa.rs b/wasmi/src/isa.rs similarity index 100% rename from src/isa.rs rename to wasmi/src/isa.rs diff --git a/src/lib.rs b/wasmi/src/lib.rs similarity index 100% rename from src/lib.rs rename to wasmi/src/lib.rs diff --git a/src/memory.rs b/wasmi/src/memory.rs similarity index 100% rename from src/memory.rs rename to wasmi/src/memory.rs diff --git a/src/module.rs b/wasmi/src/module.rs similarity index 100% rename from src/module.rs rename to wasmi/src/module.rs diff --git a/src/nan_preserving_float.rs b/wasmi/src/nan_preserving_float.rs similarity index 100% rename from src/nan_preserving_float.rs rename to wasmi/src/nan_preserving_float.rs diff --git a/src/runner.rs b/wasmi/src/runner.rs similarity index 100% rename from src/runner.rs rename to wasmi/src/runner.rs diff --git a/src/table.rs b/wasmi/src/table.rs similarity index 100% rename from src/table.rs rename to wasmi/src/table.rs diff --git a/src/tests/host.rs b/wasmi/src/tests/host.rs similarity index 100% rename from src/tests/host.rs rename to wasmi/src/tests/host.rs diff --git a/src/tests/mod.rs b/wasmi/src/tests/mod.rs similarity index 100% rename from src/tests/mod.rs rename to wasmi/src/tests/mod.rs diff --git a/src/tests/wasm.rs b/wasmi/src/tests/wasm.rs similarity index 100% rename from src/tests/wasm.rs rename to wasmi/src/tests/wasm.rs diff --git a/src/types.rs b/wasmi/src/types.rs similarity index 100% rename from src/types.rs rename to wasmi/src/types.rs diff --git a/src/validation/context.rs b/wasmi/src/validation/context.rs similarity index 100% rename from src/validation/context.rs rename to wasmi/src/validation/context.rs diff --git a/src/validation/func.rs b/wasmi/src/validation/func.rs similarity index 100% rename from src/validation/func.rs rename to wasmi/src/validation/func.rs diff --git a/src/validation/mod.rs b/wasmi/src/validation/mod.rs similarity index 100% rename from src/validation/mod.rs rename to wasmi/src/validation/mod.rs diff --git a/src/validation/tests.rs b/wasmi/src/validation/tests.rs similarity index 100% rename from src/validation/tests.rs rename to wasmi/src/validation/tests.rs diff --git a/src/validation/util.rs b/wasmi/src/validation/util.rs similarity index 100% rename from src/validation/util.rs rename to wasmi/src/validation/util.rs diff --git a/src/value.rs b/wasmi/src/value.rs similarity index 100% rename from src/value.rs rename to wasmi/src/value.rs diff --git a/wasmi/tests/derives.rs b/wasmi/tests/derives.rs new file mode 100644 index 0000000..2f05f24 --- /dev/null +++ b/wasmi/tests/derives.rs @@ -0,0 +1,36 @@ +// If you are to update this code, make sure you update the example in `wasmi-derive`. + +extern crate wasmi; +extern crate wasmi_derive; + +use std::fmt; +use wasmi::HostError; +use wasmi_derive::derive_externals; + +#[derive(Debug)] +struct NoInfoError; +impl HostError for NoInfoError {} +impl fmt::Display for NoInfoError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "NoInfoError") + } +} + +struct NonStaticExternals<'a> { + state: &'a mut usize, +} + +#[derive_externals] +impl<'a> NonStaticExternals<'a> { + pub fn hello(&self, a: u32, b: u32) -> u32 { + a + b + } + + pub fn increment(&mut self) { + *self.state += 1; + } + + pub fn traps(&self) -> Result<(), NoInfoError> { + Err(NoInfoError) + } +} diff --git a/wasmi/tests/spec/mod.rs b/wasmi/tests/spec/mod.rs new file mode 100644 index 0000000..3cd1f97 --- /dev/null +++ b/wasmi/tests/spec/mod.rs @@ -0,0 +1,83 @@ +mod run; + +macro_rules! run_test { + ($label: expr, $test_name: ident) => { + #[test] + fn $test_name() { + self::run::spec($label) + } + }; +} + +run_test!("address", wasm_address); +run_test!("align", wasm_align); +run_test!("binary", wasm_binary); +run_test!("block", wasm_block); +run_test!("br", wasm_br); +run_test!("br_if", wasm_br_if); +run_test!("br_table", wasm_br_table); +run_test!("break-drop", wasm_break_drop); +run_test!("call", wasm_call); +run_test!("call_indirect", wasm_call_indirect); +run_test!("comments", wasm_comments); +run_test!("const", wasm_const); +run_test!("conversions", wasm_conversions); +run_test!("custom", wasm_custom); +run_test!("custom_section", wasm_custom_section); +run_test!("data", wasm_data); +run_test!("elem", wasm_elem); +run_test!("endianness", wasm_endianness); +run_test!("exports", wasm_exports); +run_test!("f32", wasm_f32); +run_test!("f32_bitwise", wasm_f32_bitwise); +run_test!("f32_cmp", wasm_f32_cmp); +run_test!("f64", wasm_f64); +run_test!("f64_bitwise", wasm_f64_bitwise); +run_test!("f64_cmp", wasm_f64_cmp); +run_test!("fac", wasm_fac); +run_test!("float_exprs", wasm_float_exprs); +run_test!("float_literals", wasm_float_literals); +run_test!("float_memory", wasm_float_memory); +run_test!("float_misc", wasm_float_misc); +run_test!("forward", wasm_forward); +run_test!("func", wasm_func); +run_test!("func_ptrs", wasm_func_ptrs); +run_test!("get_local", wasm_get_local); +run_test!("globals", wasm_globals); +run_test!("i32", wasm_i32); +run_test!("i64", wasm_i64); +run_test!("if", wasm_if); +run_test!("imports", wasm_imports); +run_test!("inline-module", inline_module); +run_test!("int_exprs", wasm_int_exprs); +run_test!("int_literals", wasm_int_literals); +run_test!("labels", wasm_labels); +run_test!("left-to-right", wasm_left_to_right); +run_test!("linking", wasm_linking); +run_test!("loop", wasm_loop); +run_test!("memory", wasm_memory); +run_test!("memory_redundancy", wasm_memory_redundancy); +run_test!("memory_trap", wasm_memory_trap); +run_test!("names", wasm_names); +run_test!("nop", wasm_nop); +run_test!("resizing", wasm_resizing); +run_test!("return", wasm_return); +run_test!("select", wasm_select); +run_test!("set_local", wasm_set_local); +run_test!("skip-stack-guard-page", wasm_skip_stack_guard_page); +run_test!("stack", wasm_stack); +run_test!("start", wasm_start); +run_test!("store_retval", wasm_store_retval); +run_test!("switch", wasm_switch); +run_test!("tee_local", wasm_tee_local); +run_test!("token", wasm_token); +run_test!("traps", wasm_traps); +run_test!("type", wasm_type); +run_test!("typecheck", wasm_typecheck); +run_test!("unreachable", wasm_unreachable); +run_test!("unreached-invalid", wasm_unreached_invalid); +run_test!("unwind", wasm_unwind); +run_test!("utf8-custom-section-id", wasm_utf8_custom_section_id); +run_test!("utf8-import-field", wasm_utf8_import_field); +run_test!("utf8-import-module", wasm_utf8_import_module); +run_test!("utf8-invalid-encoding", wasm_utf8_invalid_encoding); diff --git a/wasmi/tests/spec/run.rs b/wasmi/tests/spec/run.rs new file mode 100644 index 0000000..59db57b --- /dev/null +++ b/wasmi/tests/spec/run.rs @@ -0,0 +1,503 @@ +#![cfg(test)] + +use std::collections::HashMap; +use std::fs::File; + +use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value}; +use wasmi::memory_units::Pages; +use wasmi::{ + Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, + GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module, + ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature, + TableDescriptor, TableInstance, TableRef, Trap, +}; + +fn spec_to_runtime_value(val: Value) -> RuntimeValue { + match val { + Value::I32(v) => RuntimeValue::I32(v), + Value::I64(v) => RuntimeValue::I64(v), + Value::F32(v) => RuntimeValue::F32(v.into()), + Value::F64(v) => RuntimeValue::F64(v.into()), + } +} + +#[derive(Debug)] +enum Error { + Load(String), + Start(Trap), + Script(script::Error), + Interpreter(InterpreterError), +} + +impl From for Error { + fn from(e: InterpreterError) -> Error { + Error::Interpreter(e) + } +} + +impl From for Error { + fn from(e: script::Error) -> Error { + Error::Script(e) + } +} + +struct SpecModule { + table: TableRef, + memory: MemoryRef, + global_i32: GlobalRef, + global_f32: GlobalRef, + global_f64: GlobalRef, +} + +impl SpecModule { + fn new() -> Self { + SpecModule { + table: TableInstance::alloc(10, Some(20)).unwrap(), + memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(), + global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false), + global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0.into()), false), + global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0.into()), false), + } + } +} + +const PRINT_FUNC_INDEX: usize = 0; + +impl Externals for SpecModule { + fn invoke_index( + &mut self, + index: usize, + args: RuntimeArgs, + ) -> Result, Trap> { + match index { + PRINT_FUNC_INDEX => { + println!("print: {:?}", args); + Ok(None) + } + _ => panic!("SpecModule doesn't provide function at index {}", index), + } + } +} + +impl ModuleImportResolver for SpecModule { + fn resolve_func( + &self, + field_name: &str, + func_type: &Signature, + ) -> Result { + let index = match field_name { + "print" => PRINT_FUNC_INDEX, + "print_i32" => PRINT_FUNC_INDEX, + "print_i32_f32" => PRINT_FUNC_INDEX, + "print_f64_f64" => PRINT_FUNC_INDEX, + "print_f32" => PRINT_FUNC_INDEX, + "print_f64" => PRINT_FUNC_INDEX, + _ => { + return Err(InterpreterError::Instantiation(format!( + "Unknown host func import {}", + field_name + ))); + } + }; + + if func_type.return_type().is_some() { + return Err(InterpreterError::Instantiation( + "Function `print_` have unit return type".into(), + )); + } + + let func = FuncInstance::alloc_host(func_type.clone(), index); + return Ok(func); + } + + fn resolve_global( + &self, + field_name: &str, + _global_type: &GlobalDescriptor, + ) -> Result { + match field_name { + "global_i32" => Ok(self.global_i32.clone()), + "global_f32" => Ok(self.global_f32.clone()), + "global_f64" => Ok(self.global_f64.clone()), + _ => Err(InterpreterError::Instantiation(format!( + "Unknown host global import {}", + field_name + ))), + } + } + + fn resolve_memory( + &self, + field_name: &str, + _memory_type: &MemoryDescriptor, + ) -> Result { + if field_name == "memory" { + return Ok(self.memory.clone()); + } + + Err(InterpreterError::Instantiation(format!( + "Unknown host memory import {}", + field_name + ))) + } + + fn resolve_table( + &self, + field_name: &str, + _table_type: &TableDescriptor, + ) -> Result { + if field_name == "table" { + return Ok(self.table.clone()); + } + + Err(InterpreterError::Instantiation(format!( + "Unknown host table import {}", + field_name + ))) + } +} + +struct SpecDriver { + spec_module: SpecModule, + instances: HashMap, + last_module: Option, +} + +impl SpecDriver { + fn new() -> SpecDriver { + SpecDriver { + spec_module: SpecModule::new(), + instances: HashMap::new(), + last_module: None, + } + } + + fn spec_module(&mut self) -> &mut SpecModule { + &mut self.spec_module + } + + fn add_module(&mut self, name: Option, module: ModuleRef) { + self.last_module = Some(module.clone()); + if let Some(name) = name { + self.instances.insert(name, module); + } + } + + fn module(&self, name: &str) -> Result { + self.instances.get(name).cloned().ok_or_else(|| { + InterpreterError::Instantiation(format!("Module not registered {}", name)) + }) + } + + fn module_or_last(&self, name: Option<&str>) -> Result { + match name { + Some(name) => self.module(name), + None => self + .last_module + .clone() + .ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())), + } + } +} + +impl ImportResolver for SpecDriver { + fn resolve_func( + &self, + module_name: &str, + field_name: &str, + func_type: &Signature, + ) -> Result { + if module_name == "spectest" { + self.spec_module.resolve_func(field_name, func_type) + } else { + self.module(module_name)? + .resolve_func(field_name, func_type) + } + } + + fn resolve_global( + &self, + module_name: &str, + field_name: &str, + global_type: &GlobalDescriptor, + ) -> Result { + if module_name == "spectest" { + self.spec_module.resolve_global(field_name, global_type) + } else { + self.module(module_name)? + .resolve_global(field_name, global_type) + } + } + + fn resolve_memory( + &self, + module_name: &str, + field_name: &str, + memory_type: &MemoryDescriptor, + ) -> Result { + if module_name == "spectest" { + self.spec_module.resolve_memory(field_name, memory_type) + } else { + self.module(module_name)? + .resolve_memory(field_name, memory_type) + } + } + + fn resolve_table( + &self, + module_name: &str, + field_name: &str, + table_type: &TableDescriptor, + ) -> Result { + if module_name == "spectest" { + self.spec_module.resolve_table(field_name, table_type) + } else { + self.module(module_name)? + .resolve_table(field_name, table_type) + } + } +} + +fn try_load_module(wasm: &[u8]) -> Result { + Module::from_buffer(wasm).map_err(|e| Error::Load(e.to_string())) +} + +fn try_load(wasm: &[u8], spec_driver: &mut SpecDriver) -> Result<(), Error> { + let module = try_load_module(wasm)?; + let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?; + instance + .run_start(spec_driver.spec_module()) + .map_err(|trap| Error::Start(trap))?; + Ok(()) +} + +fn load_module( + wasm: &[u8], + name: &Option, + spec_driver: &mut SpecDriver, +) -> Result { + let module = try_load_module(wasm)?; + let instance = ModuleInstance::new(&module, spec_driver) + .map_err(|e| Error::Load(e.to_string()))? + .run_start(spec_driver.spec_module()) + .map_err(|trap| Error::Start(trap))?; + + let module_name = name.clone(); + spec_driver.add_module(module_name, instance.clone()); + + Ok(instance) +} + +fn run_action( + program: &mut SpecDriver, + action: &Action, +) -> Result, InterpreterError> { + match *action { + Action::Invoke { + ref module, + ref field, + ref args, + } => { + let module = program + .module_or_last(module.as_ref().map(|x| x.as_ref())) + .expect(&format!( + "Expected program to have loaded module {:?}", + module + )); + let vec_args = args + .iter() + .cloned() + .map(spec_to_runtime_value) + .collect::>(); + module.invoke_export(field, &vec_args, program.spec_module()) + } + Action::Get { + ref module, + ref field, + .. + } => { + let module = program + .module_or_last(module.as_ref().map(|x| x.as_ref())) + .expect(&format!( + "Expected program to have loaded module {:?}", + module + )); + let global = module + .export_by_name(&field) + .ok_or_else(|| { + InterpreterError::Global(format!("Expected to have export with name {}", field)) + })? + .as_global() + .cloned() + .ok_or_else(|| { + InterpreterError::Global(format!("Expected export {} to be a global", field)) + })?; + Ok(Some(global.get())) + } + } +} + +pub fn spec(name: &str) { + println!("running test: {}", name); + try_spec(name).expect("Failed to run spec"); +} + +fn try_spec(name: &str) -> Result<(), Error> { + let mut spec_driver = SpecDriver::new(); + let spec_script_path = format!("tests/spec/testsuite/{}.wast", name); + + use std::io::Read; + let mut spec_source = Vec::new(); + let mut spec_file = File::open(&spec_script_path).expect("Can't open file"); + spec_file + .read_to_end(&mut spec_source) + .expect("Can't read file"); + + let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name)) + .expect("Can't read spec script"); + let mut errors = vec![]; + + while let Some(Command { kind, line }) = parser.next()? { + macro_rules! assert_eq { + ($a:expr, $b:expr) => {{ + let (a, b) = ($a, $b); + + if a != b { + errors.push(format!( + r#"ERROR (line {}): + expected: {:?} + got: {:?} +"#, + line, b, a, + )); + } + }}; + } + + println!("Running spec cmd {}: {:?}", line, kind); + + match kind { + CommandKind::Module { name, module, .. } => { + load_module(&module.into_vec(), &name, &mut spec_driver) + .expect("Failed to load module"); + } + CommandKind::AssertReturn { action, expected } => { + let result = run_action(&mut spec_driver, &action); + match result { + Ok(result) => { + let spec_expected = expected + .iter() + .cloned() + .map(spec_to_runtime_value) + .collect::>(); + let actual_result = result.into_iter().collect::>(); + for (actual_result, spec_expected) in + actual_result.iter().zip(spec_expected.iter()) + { + assert_eq!(actual_result.value_type(), spec_expected.value_type()); + // f32::NAN != f32::NAN + match spec_expected { + &RuntimeValue::F32(val) if val.is_nan() => match actual_result { + &RuntimeValue::F32(val) => assert!(val.is_nan()), + _ => unreachable!(), // checked above that types are same + }, + &RuntimeValue::F64(val) if val.is_nan() => match actual_result { + &RuntimeValue::F64(val) => assert!(val.is_nan()), + _ => unreachable!(), // checked above that types are same + }, + spec_expected @ _ => assert_eq!(actual_result, spec_expected), + } + } + } + Err(e) => { + panic!("Expected action to return value, got error: {:?}", e); + } + } + } + CommandKind::AssertReturnCanonicalNan { action } + | CommandKind::AssertReturnArithmeticNan { action } => { + let result = run_action(&mut spec_driver, &action); + match result { + Ok(result) => { + for actual_result in result.into_iter().collect::>() { + match actual_result { + RuntimeValue::F32(val) => { + if !val.is_nan() { + panic!("Expected nan value, got {:?}", val) + } + } + RuntimeValue::F64(val) => { + if !val.is_nan() { + panic!("Expected nan value, got {:?}", val) + } + } + val @ _ => { + panic!("Expected action to return float value, got {:?}", val) + } + } + } + } + Err(e) => { + panic!("Expected action to return value, got error: {:?}", e); + } + } + } + CommandKind::AssertExhaustion { action, .. } => { + let result = run_action(&mut spec_driver, &action); + match result { + Ok(result) => panic!("Expected exhaustion, got result: {:?}", result), + Err(_e) => {} + } + } + CommandKind::AssertTrap { action, .. } => { + let result = run_action(&mut spec_driver, &action); + match result { + Ok(result) => { + panic!( + "Expected action to result in a trap, got result: {:?}", + result + ); + } + Err(_e) => {} + } + } + CommandKind::AssertInvalid { module, .. } + | CommandKind::AssertMalformed { module, .. } + | CommandKind::AssertUnlinkable { module, .. } => { + let module_load = try_load(&module.into_vec(), &mut spec_driver); + match module_load { + Ok(_) => panic!("Expected invalid module definition, got some module!"), + Err(_e) => {} + } + } + CommandKind::AssertUninstantiable { module, .. } => { + match try_load(&module.into_vec(), &mut spec_driver) { + Ok(_) => panic!("Expected error running start function at line {}", line), + Err(_e) => {} + } + } + CommandKind::Register { name, as_name, .. } => { + let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) { + Ok(module) => module, + Err(e) => panic!("No such module, at line {} - ({:?})", e, line), + }; + spec_driver.add_module(Some(as_name.clone()), module); + } + CommandKind::PerformAction(action) => match run_action(&mut spec_driver, &action) { + Ok(_) => {} + Err(e) => panic!("Failed to invoke action at line {}: {:?}", line, e), + }, + } + } + + if !errors.is_empty() { + use std::fmt::Write; + let mut out = "\n".to_owned(); + for err in errors { + write!(out, "{}", err).expect("Error formatting errors"); + } + panic!(out); + } + + Ok(()) +} diff --git a/tests/spec/testsuite b/wasmi/tests/spec/testsuite similarity index 100% rename from tests/spec/testsuite rename to wasmi/tests/spec/testsuite diff --git a/wasmi/tests/spec_shim.rs b/wasmi/tests/spec_shim.rs new file mode 100644 index 0000000..e254b66 --- /dev/null +++ b/wasmi/tests/spec_shim.rs @@ -0,0 +1,6 @@ +//! Official spec testsuite. + +extern crate wabt; +extern crate wasmi; + +mod spec;