From 75745055ccce57d57318dbb45a8af1d1b6376428 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 20 Feb 2018 18:01:02 +0300 Subject: [PATCH] add testsuite and list all malformed cases --- spec/Cargo.toml | 14 +- spec/build.rs | 8 -- spec/src/fixtures.rs | 132 ----------------- spec/src/lib.rs | 39 ++++- spec/src/run.rs | 314 +++-------------------------------------- spec/src/test.rs | 104 -------------- src/elements/mod.rs | 4 + src/elements/module.rs | 12 +- 8 files changed, 71 insertions(+), 556 deletions(-) delete mode 100644 spec/build.rs delete mode 100644 spec/src/fixtures.rs delete mode 100644 spec/src/test.rs diff --git a/spec/Cargo.toml b/spec/Cargo.toml index f2fceee..5a12f8c 100644 --- a/spec/Cargo.toml +++ b/spec/Cargo.toml @@ -7,19 +7,7 @@ readme = "README.md" repository = "https://github.com/nikvolf/parity-wasm" homepage = "https://github.com/nikvolf/parity-wasm" description = "parity-wasm testsuite" -build = "build.rs" - -[build-dependencies] -cmake = "0.1" [dependencies] +wabt = "0.2" parity-wasm = { path = ".." } -serde_json = "1.0" -serde_derive = "1.0" -serde = "1.0" - -[dev-dependencies] -parity-wasm = { path = ".." } -serde_json = "1.0" -serde_derive = "1.0" -serde = "1.0" \ No newline at end of file diff --git a/spec/build.rs b/spec/build.rs deleted file mode 100644 index 596e8a4..0000000 --- a/spec/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -extern crate cmake; -use cmake::Config; - -fn main() { - let _dst = Config::new("wabt") - .define("BUILD_TESTS", "OFF") - .build(); -} \ No newline at end of file diff --git a/spec/src/fixtures.rs b/spec/src/fixtures.rs deleted file mode 100644 index e7af523..0000000 --- a/spec/src/fixtures.rs +++ /dev/null @@ -1,132 +0,0 @@ -macro_rules! run_test { - ($label: expr, $test_name: ident, fail) => ( - #[test] - fn $test_name() { - ::run::failing_spec($label) - } - ); - ($label: expr, $test_name: ident) => ( - #[test] - fn $test_name() { - ::run::spec($label) - } - ); -} - -run_test!("address-offset-range.fail", wasm_address_offset_range_fail, fail); -run_test!("address", wasm_address); -run_test!("binary", wasm_binary); -run_test!("block-end-label-mismatch.fail", wasm_block_end_label_mismatch_fail, fail); -run_test!("block-end-label-superfluous.fail", wasm_block_end_label_superfluous_fail, fail); -run_test!("block", wasm_block); -run_test!("br_if", wasm_br_if); -run_test!("br_table", wasm_br_table); -run_test!("br", wasm_br); -run_test!("break-drop", wasm_break_drop); -run_test!("call_indirect", wasm_call_indirect); -run_test!("call", wasm_call); -run_test!("comments", wasm_comments); -// TODO: commented out until sNaN issue is resolved: -// https://github.com/NikVolf/parity-wasm/blob/b5aaf103cf28f1e36df832f4883f55043e67894b/src/interpreter/value.rs#L510 -// run_test!("conversions", wasm_conversions); -run_test!("custom_section", wasm_custom_section); -run_test!("endianness", wasm_endianness); -run_test!("f32_exports", wasm_exports); -run_test!("f32_bitwise", wasm_f32_bitwise); -run_test!("f32_cmp", wasm_f32_cmp); -run_test!("f32.load32.fail", wasm_f32_load32_fail, fail); -run_test!("f32.load64.fail", wasm_f32_load64_fail, fail); -run_test!("f32.store32.fail", wasm_f32_store32_fail, fail); -run_test!("f32.store64.fail", wasm_f32_store64_fail, fail); -run_test!("f32", wasm_f32); -run_test!("f64_bitwise", wasm_f64_bitwise); -run_test!("f64_cmp", wasm_f64_cmp); -run_test!("f64.load32.fail", wasm_f64_load32_fail, fail); -run_test!("f64.load64.fail", wasm_f64_load64_fail, fail); -run_test!("f64.store32.fail", wasm_f64_store32_fail, fail); -run_test!("f64.store64.fail", wasm_f64_store64_fail, fail); -run_test!("f64", wasm_f64); -run_test!("fac", wasm_fac); -// TODO: commented out until sNaN issue is resolved: -// https://github.com/NikVolf/parity-wasm/blob/b5aaf103cf28f1e36df832f4883f55043e67894b/src/interpreter/value.rs#L510 -// 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_ptrs", wasm_func_ptrs); -run_test!("func-local-after-body.fail", wasm_func_local_after_body_fail, fail); -run_test!("func-local-before-param.fail", wasm_func_local_before_param_fail, fail); -run_test!("func-local-before-result.fail", wasm_func_local_before_result_fail, fail); -run_test!("func-param-after-body.fail", wasm_func_param_after_body_fail, fail); -run_test!("func-result-after-body.fail", wasm_func_result_after_body_fail, fail); -run_test!("func-result-before-param.fail", wasm_func_result_before_param_fail, fail); -run_test!("func", wasm_func); -run_test!("get_local", wasm_get_local); -run_test!("globals", wasm_globals); -run_test!("i32.load32_s.fail", wasm_i32_load32s_fail, fail); -run_test!("i32.load32_u.fail", wasm_i32_load32u_fail, fail); -run_test!("i32.load64_s.fail", wasm_i32_load64s_fail, fail); -run_test!("i32.load64_u.fail", wasm_i32_load64u_fail, fail); -run_test!("i32.store32.fail", wasm_i32_store32_fail, fail); -run_test!("i32.store64.fail", wasm_i32_store64_fail, fail); -run_test!("i32", wasm_i32); -run_test!("i64.load64_s.fail", wasm_i64_load64s_fail, fail); -run_test!("i64.load64_u.fail", wasm_i64_load64u_fail, fail); -run_test!("i64.store64.fail", wasm_i64_store64_fail, fail); -run_test!("i64", wasm_i64); -run_test!("if-else-end-label-mismatch.fail", wasm_if_else_end_label_mismatch_fail, fail); -run_test!("if-else-end-label-superfluous.fail", wasm_if_else_end_label_superfluous_fail, fail); -run_test!("if-else-label-mismatch.fail", wasm_if_else_label_mismatch_fail, fail); -run_test!("if-else-label-superfluous.fail", wasm_if_else_label_superfluous_fail, fail); -run_test!("if-end-label-mismatch.fail", wasm_if_end_label_mismatch_fail, fail); -run_test!("if-end-label-superfluous.fail", wasm_if_end_label_superfluous_fail, fail); -run_test!("if", wasm_if); -run_test!("import-after-func.fail", wasm_import_after_func_fail, fail); -run_test!("import-after-global.fail", wasm_import_after_global_fail, fail); -run_test!("import-after-memory.fail", wasm_import_after_memory_fail, fail); -run_test!("import-after-table.fail", wasm_import_after_table_fail, fail); -run_test!("imports", wasm_imports); -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!("load-align-0.fail", wasm_load_align_0_fail, fail); -run_test!("load-align-big.fail", wasm_load_align_big_fail, fail); -run_test!("load-align-odd.fail", wasm_load_align_odd_fail, fail); -run_test!("loop-end-label-mismatch.fail", wasm_end_label_mismatch_fail, fail); -run_test!("loop-end-label-superfluous.fail", wasm_end_label_superfluous_fail, fail); -run_test!("loop", wasm_loop); -run_test!("memory_redundancy", wasm_memory_redundancy); -run_test!("memory_trap", wasm_memory_trap); -run_test!("memory", wasm_memory); -run_test!("names", wasm_names); -run_test!("nop", wasm_nop); -run_test!("of_string-overflow-hex-u32.fail", wasm_of_string_overflow_hex_u32_fail, fail); -run_test!("of_string-overflow-hex-u64.fail", wasm_of_string_overflow_hex_u64_fail, fail); -run_test!("of_string-overflow-s32.fail", wasm_of_string_overflow_s32_fail, fail); -run_test!("of_string-overflow-s64.fail", wasm_of_string_overflow_s64_fail, fail); -run_test!("of_string-overflow-u32.fail", wasm_of_string_overflow_u32_fail, fail); -run_test!("of_string-overflow-u64.fail", wasm_of_string_overflow_u64_fail, fail); -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!("store-align-0.fail", wasm_store_align_0_fail, fail); -run_test!("store-align-big.fail", wasm_store_align_big_fail, fail); -run_test!("store-align-odd.fail", wasm_store_align_odd_fail, fail); -run_test!("switch", wasm_switch); -run_test!("tee_local", wasm_tee_local); -run_test!("traps", wasm_traps); -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-selection-id", wasm_utf8_custom_selection_id); -run_test!("utf8-import-field", wasm_utf8_import_field); -run_test!("utf8-import-module", wasm_utf8_import_module); diff --git a/spec/src/lib.rs b/spec/src/lib.rs index 9576ba9..eecf544 100644 --- a/spec/src/lib.rs +++ b/spec/src/lib.rs @@ -1,8 +1,37 @@ -#[cfg_attr(test, macro_use)] #[cfg(test)] extern crate serde_derive; +#![cfg(test)] + +extern crate wabt; extern crate parity_wasm; -extern crate serde; -extern crate serde_json; mod run; -mod test; -mod fixtures; \ No newline at end of file + +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!("call_indirect", wasm_call_indirect); +run_test!("const", wasm_const); +run_test!("custom_section", wasm_custom_section); +run_test!("float_literals", wasm_float_literals); +run_test!("func", wasm_func); +run_test!("globals", wasm_globals); +run_test!("if", wasm_if); +run_test!("imports", wasm_imports); +run_test!("int_literals", wasm_int_literals); +run_test!("loop", wasm_loop); +run_test!("memory", wasm_memory); +run_test!("token", wasm_token); +run_test!("type", wasm_type); +run_test!("utf8-custom-section-id", wasm_utf8_custom_section_id); +run_test!("utf8-import-field", wasm_import_field); +run_test!("utf8-import-module", wasm_import_module); +run_test!("utf8-invalid-encoding", wasm_invalid_encoding); \ No newline at end of file diff --git a/spec/src/run.rs b/spec/src/run.rs index a727e73..8072247 100644 --- a/spec/src/run.rs +++ b/spec/src/run.rs @@ -1,293 +1,23 @@ -#![cfg(test)] +use wabt::script::{ScriptParser, Command, CommandKind}; +use parity_wasm::elements::{Module, deserialize_buffer}; -use std::env; -use std::path::PathBuf; -use std::process::Command; -use std::fs::File; -use std::sync::Arc; - -use serde_json; -use test; -use parity_wasm::{self, elements, builder}; -use parity_wasm::interpreter::{ - RuntimeValue, - ProgramInstance, ModuleInstance, - ItemIndex, ExportEntryType, - Error as InterpreterError, -}; - -fn spec_test_module() -> elements::Module { - builder::module() - .function().signature().build().body().build().build() - .function().signature().param().i32().build().body().build().build() - .function().signature().param().i64().build().body().build().build() - .function().signature().param().f32().build().body().build().build() - .function().signature().param().f64().build().body().build().build() - .function().signature().param().i32().param().f32().build().body().build().build() - .function().signature().param().f64().param().f64().build().body().build().build() - .global().value_type().i32().init_expr(elements::Opcode::I32Const(666)).build() - .with_table(elements::TableType::new(100, None)) - .memory().with_min(1).with_max(Some(2)).build() - .export().field("print").internal().func(0).build() - .export().field("print").internal().func(1).build() - .export().field("print").internal().func(2).build() - .export().field("print").internal().func(3).build() - .export().field("print").internal().func(4).build() - .export().field("print").internal().func(5).build() - .export().field("print").internal().func(6).build() - .export().field("global").internal().global(0).build() - .export().field("table").internal().table(0).build() - .export().field("memory").internal().memory(0).build() - .build() -} - -fn load_module(base_dir: &str, path: &str, name: &Option, program: &ProgramInstance) -> Arc { - let module = try_deserialize(base_dir, path).expect(&format!("Wasm file {} failed to load", path)); - - program.add_module("spectest", spec_test_module(), None).expect("Failed adding 'spectest' module"); - - let module_name = name.as_ref().map(|s| s.as_ref()).unwrap_or("wasm_test").trim_left_matches('$'); - let module_instance = program.add_module(module_name, module, None).expect(&format!("Failed adding {} module", module_name)); - module_instance -} - -fn try_deserialize(base_dir: &str, module_path: &str) -> Result { - let mut wasm_path = PathBuf::from(base_dir.clone()); - wasm_path.push(module_path); - parity_wasm::deserialize_file(&wasm_path) -} - -fn try_load(base_dir: &str, module_path: &str) -> Result<(), InterpreterError> { - let module = try_deserialize(base_dir, module_path).map_err(|e| parity_wasm::interpreter::Error::Program(format!("{:?}", e)))?; - let program = ProgramInstance::new(); - program.add_module("try_load", module, None).map(|_| ()) -} - -fn runtime_value(test_val: &test::RuntimeValue) -> parity_wasm::RuntimeValue { - match test_val.value_type.as_ref() { - "i32" => { - let unsigned: u32 = test_val.value.parse().expect("Literal parse error"); - parity_wasm::RuntimeValue::I32(unsigned as i32) - }, - "i64" => { - let unsigned: u64 = test_val.value.parse().expect("Literal parse error"); - parity_wasm::RuntimeValue::I64(unsigned as i64) - }, - "f32" => { - let unsigned: u32 = test_val.value.parse().expect("Literal parse error"); - parity_wasm::RuntimeValue::decode_f32(unsigned) - }, - "f64" => { - let unsigned: u64 = test_val.value.parse().expect("Literal parse error"); - parity_wasm::RuntimeValue::decode_f64(unsigned) - }, - _ => panic!("Unknwon runtime value type"), - } -} - -fn runtime_values(test_vals: &[test::RuntimeValue]) -> Vec { - test_vals.iter().map(runtime_value).collect::>() -} - -fn run_action(program: &ProgramInstance, action: &test::Action) - -> Result, InterpreterError> -{ - match *action { - test::Action::Invoke { ref module, ref field, ref args } => { - let module = module.clone().unwrap_or("wasm_test".into()); - let module = module.trim_left_matches('$'); - let module = program.module(&module).expect(&format!("Expected program to have loaded module {}", module)); - module.execute_export(&jstring_to_rstring(field), runtime_values(args).into()) - }, - test::Action::Get { ref module, ref field, .. } => { - let module = module.clone().unwrap_or("wasm_test".into()); - let module = module.trim_left_matches('$'); - let module = program.module(&module).expect(&format!("Expected program to have loaded module {}", module)); - let field = jstring_to_rstring(&field); - - module.export_entry(field.as_ref(), &ExportEntryType::Any) - .and_then(|i| match i { - elements::Internal::Global(global_index) => Ok(ItemIndex::IndexSpace(global_index)), - _ => Err(InterpreterError::Global(format!("Expected to have exported global with name {}", field))), - }) - .and_then(|g| module.global(g, None, None).map(|g| Some(g.get()))) - } - } -} - -pub struct FixtureParams { - failing: bool, - json: String, -} - -pub fn run_wast2wasm(name: &str) -> FixtureParams { - let outdir = env::var("OUT_DIR").unwrap(); - - let mut wast2wasm_path = PathBuf::from(outdir.clone()); - wast2wasm_path.push("bin"); - wast2wasm_path.push("wast2wasm"); - - let mut json_spec_path = PathBuf::from(outdir.clone()); - json_spec_path.push(&format!("{}.json", name)); - - let wast2wasm_output = Command::new(wast2wasm_path) - .arg("--spec") - .arg("-o") - .arg(&json_spec_path) - .arg(&format!("./wabt/third_party/testsuite/{}.wast", name)) - .output() - .expect("Failed to execute process"); - - FixtureParams { - json: json_spec_path.to_str().unwrap().to_owned(), - failing: { - if !wast2wasm_output.status.success() { - println!("wasm2wast error code: {}", wast2wasm_output.status); - println!("wasm2wast stdout: {}", String::from_utf8_lossy(&wast2wasm_output.stdout)); - println!("wasm2wast stderr: {}", String::from_utf8_lossy(&wast2wasm_output.stderr)); - true - } else { - false - } - } - } -} - -pub fn failing_spec(name: &str) { - let fixture = run_wast2wasm(name); - if !fixture.failing { - panic!("wasm2wast expected to fail, but terminated normally"); - } -} - -pub fn spec(name: &str) { - let outdir = env::var("OUT_DIR").unwrap(); - - let fixture = run_wast2wasm(name); - if fixture.failing { - panic!("wasm2wast terminated abnormally, expected to success"); - } - - let mut f = File::open(&fixture.json) - .expect(&format!("Failed to load json file {}", &fixture.json)); - let spec: test::Spec = serde_json::from_reader(&mut f).expect("Failed to deserialize JSON file"); - - let program = ProgramInstance::new(); - let mut last_module = None; - for command in &spec.commands { - println!("command {:?}", command); - match command { - &test::Command::Module { ref name, ref filename, .. } => { - last_module = Some(load_module(&outdir, &filename, &name, &program)); - }, - &test::Command::AssertReturn { line, ref action, ref expected } => { - let result = run_action(&program, action); - match result { - Ok(result) => { - let spec_expected = runtime_values(expected); - let actual_result = result.into_iter().collect::>(); - for (actual_result, spec_expected) in actual_result.iter().zip(spec_expected.iter()) { - assert_eq!(actual_result.variable_type(), spec_expected.variable_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), - } - } - println!("assert_return at line {} - success", line); - }, - Err(e) => { - panic!("Expected action to return value, got error: {:?}", e); - } - } - }, - &test::Command::AssertReturnCanonicalNan { line, ref action } | &test::Command::AssertReturnArithmeticNan { line, ref action } => { - let result = run_action(&program, 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), - } - } - println!("assert_return_nan at line {} - success", line); - }, - Err(e) => { - panic!("Expected action to return value, got error: {:?}", e); - } - } - }, - &test::Command::AssertExhaustion { line, ref action, .. } => { - let result = run_action(&program, action); - match result { - Ok(result) => panic!("Expected exhaustion, got result: {:?}", result), - Err(e) => println!("assert_exhaustion at line {} - success ({:?})", line, e), - } - }, - &test::Command::AssertTrap { line, ref action, .. } => { - let result = run_action(&program, action); - match result { - Ok(result) => { - panic!("Expected action to result in a trap, got result: {:?}", result); - }, - Err(e) => { - println!("assert_trap at line {} - success ({:?})", line, e); - } - } - }, - &test::Command::AssertInvalid { line, ref filename, .. } - | &test::Command::AssertMalformed { line, ref filename, .. } - | &test::Command::AssertUnlinkable { line, ref filename, .. } - => { - let module_load = try_load(&outdir, filename); - match module_load { - Ok(_) => { - panic!("Expected invalid module definition, got some module!") - }, - Err(e) => { - println!("assert_invalid at line {} - success ({:?})", line, e) - } - } - }, - &test::Command::AssertUninstantiable { line, ref filename, .. } => { - match try_load(&outdir, &filename) { - Ok(_) => panic!("Expected error running start function at line {}", line), - Err(e) => println!("assert_uninstantiable - success ({:?})", e), - } - }, - &test::Command::Register { ref name, ref as_name, .. } => { - match name { - &Some(ref name) => assert_eq!(name.trim_left_matches('$'), as_name), // we have already registered this module without $ prefix - &None => program.insert_loaded_module(as_name, last_module.take().expect("Last module must be set for this command")).map(|_| ()).unwrap(), - } - }, - &test::Command::Action { line, ref action } => { - match run_action(&program, action) { - Ok(_) => { }, - Err(e) => { - panic!("Failed to invoke action at line {}: {:?}", line, e) - } - } - }, - } - } -} - -// Convert json string to correct rust UTF8 string. -// The reason is that, for example, rust character "\u{FEEF}" (3-byte UTF8 BOM) is represented as "\u00ef\u00bb\u00bf" in spec json. -// It is incorrect. Correct BOM representation in json is "\uFEFF" => we need to do a double utf8-parse here. -// This conversion is incorrect in general case (casting char to u8)!!! -fn jstring_to_rstring(jstring: &str) -> String { - let jstring_chars: Vec = jstring.chars().map(|c| c as u8).collect(); - let rstring = String::from_utf8(jstring_chars).unwrap(); - rstring -} +pub fn spec(path: &str) { + let mut parser = ScriptParser::from_file(&format!("./testsuite/{}.wast", path)).expect("Can't read spec script"); + while let Some(Command { kind, line }) = parser.next().expect("Failed to iterate") { + match kind { + CommandKind::AssertMalformed { module, .. } => + { + let module_load = deserialize_buffer::( + &module.into_vec().expect("Invalid filename provided") + ); + match module_load { + Ok(_) => panic!("Expected invalid module definition, got some module!"), + Err(e) => println!("assert_invalid at line {} - success ({:?})", line, e), + } + } + _ => { + // Skipping interpreted + } + } + } +} \ No newline at end of file diff --git a/spec/src/test.rs b/spec/src/test.rs deleted file mode 100644 index 518cc4a..0000000 --- a/spec/src/test.rs +++ /dev/null @@ -1,104 +0,0 @@ -#![cfg(test)] - -#[derive(Deserialize, Debug)] -pub struct RuntimeValue { - #[serde(rename = "type")] - pub value_type: String, - pub value: String, -} - -#[derive(Deserialize, Debug)] -#[serde(tag = "type")] -pub enum Action { - #[serde(rename = "invoke")] - Invoke { - module: Option, - field: String, - args: Vec, - }, - #[serde(rename = "get")] - Get { - module: Option, - field: String, - } -} - -#[derive(Deserialize, Debug)] -#[serde(tag = "type")] -pub enum Command { - #[serde(rename = "module")] - Module { - line: u64, - name: Option, - filename: String - }, - #[serde(rename = "assert_return")] - AssertReturn { - line: u64, - action: Action, - expected: Vec, - }, - #[serde(rename = "assert_return_canonical_nan")] - AssertReturnCanonicalNan { - line: u64, - action: Action, - }, - #[serde(rename = "assert_return_arithmetic_nan")] - AssertReturnArithmeticNan { - line: u64, - action: Action, - }, - #[serde(rename = "assert_trap")] - AssertTrap { - line: u64, - action: Action, - text: String, - }, - #[serde(rename = "assert_invalid")] - AssertInvalid { - line: u64, - filename: String, - text: String, - }, - #[serde(rename = "assert_malformed")] - AssertMalformed { - line: u64, - filename: String, - text: String, - }, - #[serde(rename = "assert_uninstantiable")] - AssertUninstantiable { - line: u64, - filename: String, - text: String, - }, - #[serde(rename = "assert_exhaustion")] - AssertExhaustion { - line: u64, - action: Action, - }, - #[serde(rename = "assert_unlinkable")] - AssertUnlinkable { - line: u64, - filename: String, - text: String, - }, - #[serde(rename = "register")] - Register { - line: u64, - name: Option, - #[serde(rename = "as")] - as_name: String, - }, - #[serde(rename = "action")] - Action { - line: u64, - action: Action, - }, -} - -#[derive(Deserialize, Debug)] -pub struct Spec { - pub source_filename: String, - pub commands: Vec, -} \ No newline at end of file diff --git a/src/elements/mod.rs b/src/elements/mod.rs index 84cd602..e29b73b 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -132,6 +132,8 @@ pub enum Error { UnknownFunctionForm(u8), /// Invalid varint7 (should be in -64..63 range) InvalidVarInt7(u8), + /// Amount of code and signatures does not match + InconsistentCode, } impl fmt::Display for Error { @@ -164,6 +166,7 @@ impl fmt::Display for Error { Error::InvalidMemoryReference(ref mem_ref) => write!(f, "Invalid memory reference ({})", mem_ref), Error::InvalidTableReference(ref table_ref) => write!(f, "Invalid table reference ({})", table_ref), Error::UnknownFunctionForm(ref form) => write!(f, "Unknown function form ({})", form), + Error::InconsistentCode => write!(f, "Amount of code and signatures does not match"), } } } @@ -196,6 +199,7 @@ impl error::Error for Error { Error::InvalidMemoryReference(_) => "Invalid memory reference", Error::InvalidTableReference(_) => "Invalid table reference", Error::UnknownFunctionForm(_) => "Unknown function form", + Error::InconsistentCode => "Amount of code and signatures does not match", } } } diff --git a/src/elements/module.rs b/src/elements/module.rs index 394b48c..c40727b 100644 --- a/src/elements/module.rs +++ b/src/elements/module.rs @@ -250,11 +250,19 @@ impl Deserialize for Module { } } - Ok(Module { + let module = Module { magic: LittleEndian::read_u32(&magic), version: version, sections: sections, - }) + }; + + if module.code_section().map(|cs| cs.bodies().len()).unwrap_or(0) != + module.function_section().map(|fs| fs.entries().len()).unwrap_or(0) + { + return Err(Error::InconsistentCode); + } + + Ok(module) } }