diff --git a/Cargo.toml b/Cargo.toml index e9205e8..8d22e52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,5 +12,6 @@ keywords = ["wasm", "webassembly", "bytecode", "serde", "interpreter"] exclude = [ "res/*", "spec/*" ] [dependencies] +log = "0.3" byteorder = "1.0" parking_lot = "0.4" \ No newline at end of file diff --git a/examples/interpret.rs b/examples/interpret.rs index 7a5abf4..fad4c51 100644 --- a/examples/interpret.rs +++ b/examples/interpret.rs @@ -20,7 +20,7 @@ fn main() { } ).expect("Failed to load program"); let module = parity_wasm::deserialize_file(&args[1]).expect("Failed to load module"); - let module = program.add_module("main", module).expect("Failed to initialize module"); + let module = program.add_module("main", module, None).expect("Failed to initialize module"); let argument: i32 = args[2].parse().expect("Integer argument required"); println!("Result: {:?}", module.execute_export("_call", vec![parity_wasm::RuntimeValue::I32(argument)].into())); } diff --git a/spec/src/fixtures.rs b/spec/src/fixtures.rs index b6a3e70..72be5e1 100644 --- a/spec/src/fixtures.rs +++ b/spec/src/fixtures.rs @@ -13,17 +13,91 @@ macro_rules! run_test { ); } -run_test!("address", wasm_address); 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); +// TODO: run_test!("br_table", wasm_br_table); +run_test!("br", wasm_br); +run_test!("break-drop", wasm_break_drop); +// TODO: run_test!("call_indirect", wasm_call_indirect); +// TODO: run_test!("call", wasm_call); +run_test!("comments", wasm_comments); +// TODO: run_test!("conversions", wasm_conversions); +// TODO: run_test!("custom_section", wasm_custom_section); run_test!("endianness", wasm_endianness); -run_test!("f32", wasm_f32); +run_test!("f32_exports", wasm_exports); run_test!("f32_bitwise", wasm_f32_bitwise); -run_test!("f64", wasm_f64); +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); // TODO: fix comparison??? run_test!("names", wasm_names); run_test!("nop", wasm_nop); diff --git a/spec/src/run.rs b/spec/src/run.rs index 8693111..142bbc1 100644 --- a/spec/src/run.rs +++ b/spec/src/run.rs @@ -11,42 +11,56 @@ use test; use parity_wasm::{self, elements, builder}; use parity_wasm::interpreter::{ RuntimeValue, - ProgramInstance, ModuleInstance, ModuleInstanceInterface, + ProgramInstance, ModuleInstance, + ItemIndex, ExportEntryType, Error as InterpreterError, }; fn spec_test_module() -> elements::Module { builder::module() - .function() - .signature().build() - .body().build() - .build() - .global().value_type().i32().init_expr(elements::Opcode::I32Const(0)).build() + .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 setup_program(base_dir: &str, test_module_path: &str) -> (ProgramInstance, Arc) { +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(test_module_path); - let module = parity_wasm::deserialize_file(&wasm_path) - .expect(&format!("Wasm file {} failed to load", wasm_path.to_string_lossy())); - - let program = ProgramInstance::new().expect("Failed creating program"); - program.add_module("spectest", spec_test_module()).expect("Failed adding 'spectest' module"); - - let module_instance = program.add_module("test", module).expect("Failed adding module"); - (program, module_instance) + wasm_path.push(module_path); + parity_wasm::deserialize_file(&wasm_path) } fn try_load(base_dir: &str, module_path: &str) -> Result<(), parity_wasm::interpreter::Error> { - let mut wasm_path = PathBuf::from(base_dir.clone()); - wasm_path.push(module_path); - let module = parity_wasm::deserialize_file(&wasm_path).map_err(|e| parity_wasm::interpreter::Error::Program(format!("{:?}", e)))?; - + let module = try_deserialize(base_dir, module_path).map_err(|e| parity_wasm::interpreter::Error::Program(format!("{:?}", e)))?; let program = ProgramInstance::new().expect("Failed creating program"); - program.add_module("try_load", module).map(|_| ()) + program.add_module("try_load", module, None).map(|_| ()) } fn runtime_value(test_val: &test::RuntimeValue) -> parity_wasm::RuntimeValue { @@ -75,12 +89,27 @@ fn runtime_values(test_vals: &[test::RuntimeValue]) -> Vec>() } -fn run_action(module: &ModuleInstance, action: &test::Action) +fn run_action(program: &ProgramInstance, action: &test::Action) -> Result, InterpreterError> { match *action { - test::Action::Invoke { ref field, ref args} => { + 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(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)); + + module.export_entry(field.as_ref(), None, &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).map(|g| Some(g.get()))) } } } @@ -142,18 +171,16 @@ pub fn spec(name: &str) { .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 mut _program = None; - let mut module = None; + let program = ProgramInstance::new().expect("Failed creating program"); + let mut last_module = None; for command in &spec.commands { println!("command {:?}", command); match command { - &test::Command::Module { ref filename, .. } => { - let (new_program, new_module) = setup_program(&outdir, &filename); - _program = Some(new_program); - module = Some(new_module); + &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(&*module.as_ref().unwrap(), action); + let result = run_action(&program, action); match result { Ok(result) => { let spec_expected = runtime_values(expected); @@ -181,7 +208,7 @@ pub fn spec(name: &str) { } }, &test::Command::AssertReturnCanonicalNan { line, ref action } | &test::Command::AssertReturnArithmeticNan { line, ref action } => { - let result = run_action(&*module.as_ref().unwrap(), action); + let result = run_action(&program, action); match result { Ok(result) => { for actual_result in result.into_iter().collect::>() { @@ -199,14 +226,14 @@ pub fn spec(name: &str) { } }, &test::Command::AssertExhaustion { line, ref action, .. } => { - let result = run_action(&*module.as_ref().unwrap(), 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(&*module.as_ref().unwrap(), action); + let result = run_action(&program, action); match result { Ok(result) => { panic!("Expected action to result in a trap, got result: {:?}", result); @@ -236,14 +263,20 @@ pub fn spec(name: &str) { 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(&*module.as_ref().unwrap(), action) { + match run_action(&program, action) { Ok(_) => { }, Err(e) => { panic!("Failed to invoke action at line {}: {:?}", line, e) } } - }, + }, } } } diff --git a/spec/src/test.rs b/spec/src/test.rs index 866ad41..8c4a2b3 100644 --- a/spec/src/test.rs +++ b/spec/src/test.rs @@ -11,14 +11,27 @@ pub struct RuntimeValue { #[serde(tag = "type")] pub enum Action { #[serde(rename = "invoke")] - Invoke { field: String, args: Vec } + 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, filename: String }, + Module { + line: u64, + name: Option, + filename: String + }, #[serde(rename = "assert_return")] AssertReturn { line: u64, @@ -70,6 +83,13 @@ pub enum Command { filename: String, text: String, }, + #[serde(rename = "register")] + Register { + line: u64, + name: Option, + #[serde(rename = "as")] + as_name: String, + }, #[serde(rename = "action")] Action { line: u64, diff --git a/src/builder/module.rs b/src/builder/module.rs index 584b2d9..c63b5a1 100644 --- a/src/builder/module.rs +++ b/src/builder/module.rs @@ -311,6 +311,12 @@ impl ModuleBuilder where F: Invoke { self } + /// With table + pub fn with_table(mut self, table: elements::TableType) -> Self { + self.module.table.entries_mut().push(table); + self + } + /// Export entry builder pub fn export(self) -> export::ExportBuilder { export::ExportBuilder::with_callback(self) diff --git a/src/elements/mod.rs b/src/elements/mod.rs index b065155..3a66a1f 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -77,6 +77,8 @@ pub enum Error { UnknownInternalKind(u8), /// Unknown opcode encountered UnknownOpcode(u8), + /// Invalid VarUint1 value + InvalidVarUint1(u8), } impl From for Error { diff --git a/src/elements/primitives.rs b/src/elements/primitives.rs index c59821e..305f597 100644 --- a/src/elements/primitives.rs +++ b/src/elements/primitives.rs @@ -427,8 +427,11 @@ impl Deserialize for VarUint1 { fn deserialize(reader: &mut R) -> Result { let mut u8buf = [0u8; 1]; reader.read_exact(&mut u8buf)?; - // todo check range - Ok(VarUint1(u8buf[0] == 1)) + match u8buf[0] { + 0 => Ok(VarUint1(false)), + 1 => Ok(VarUint1(true)), + v @ _ => Err(Error::InvalidVarUint1(v)), + } } } diff --git a/src/elements/types.rs b/src/elements/types.rs index 24248f1..686b895 100644 --- a/src/elements/types.rs +++ b/src/elements/types.rs @@ -116,7 +116,7 @@ impl Serialize for BlockType { } /// Function signature type. -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct FunctionType { form: u8, params: Vec, diff --git a/src/interpreter/env.rs b/src/interpreter/env.rs index 1aa9670..d9c65d2 100644 --- a/src/interpreter/env.rs +++ b/src/interpreter/env.rs @@ -1,4 +1,5 @@ use std::sync::{Arc, Weak}; +use std::collections::HashMap; use builder::module; use elements::{Module, FunctionType, ExportEntry, Internal, GlobalEntry, GlobalType, @@ -6,11 +7,11 @@ use elements::{Module, FunctionType, ExportEntry, Internal, GlobalEntry, GlobalT use interpreter::Error; use interpreter::env_native::NATIVE_INDEX_FUNC_MIN; use interpreter::module::{ModuleInstanceInterface, ModuleInstance, ExecutionParams, - ItemIndex, CallerContext}; + ItemIndex, CallerContext, ExportEntryType}; use interpreter::memory::{MemoryInstance, LINEAR_MEMORY_PAGE_SIZE}; use interpreter::table::TableInstance; use interpreter::value::RuntimeValue; -use interpreter::variable::VariableInstance; +use interpreter::variable::{VariableInstance, VariableType}; /// Memory address, at which stack begins. const DEFAULT_STACK_BASE: u32 = 0; @@ -81,7 +82,7 @@ pub struct EnvModuleInstance { impl EnvModuleInstance { pub fn new(params: EnvParams, module: Module) -> Result { - let instance = ModuleInstance::new_with_validation_flag(Weak::default(), module, false)?; + let instance = ModuleInstance::new(Weak::default(), "env".into(), module)?; Ok(EnvModuleInstance { _params: params, @@ -91,6 +92,10 @@ impl EnvModuleInstance { } impl ModuleInstanceInterface for EnvModuleInstance { + fn instantiate<'a>(&self, is_user_module: bool, externals: Option<&'a HashMap>>) -> Result<(), Error> { + self.instance.instantiate(is_user_module, externals) + } + fn execute_index(&self, index: u32, params: ExecutionParams) -> Result, Error> { self.instance.execute_index(index, params) } @@ -99,8 +104,12 @@ impl ModuleInstanceInterface for EnvModuleInstance { self.instance.execute_export(name, params) } - fn export_entry(&self, name: &str) -> Result { - self.instance.export_entry(name) + fn export_entry<'a>(&self, name: &str, externals: Option<&'a HashMap>>, required_type: &ExportEntryType) -> Result { + self.instance.export_entry(name, externals, required_type) + } + + fn function_type<'a>(&self, function_index: ItemIndex, externals: Option<&'a HashMap>>) -> Result { + self.instance.function_type(function_index, externals) } fn table(&self, index: ItemIndex) -> Result, Error> { @@ -111,12 +120,12 @@ impl ModuleInstanceInterface for EnvModuleInstance { self.instance.memory(index) } - fn global(&self, index: ItemIndex) -> Result, Error> { - self.instance.global(index) + fn global(&self, index: ItemIndex, variable_type: Option) -> Result, Error> { + self.instance.global(index, variable_type) } - fn call_function(&self, outer: CallerContext, index: ItemIndex) -> Result, Error> { - self.instance.call_function(outer, index) + fn call_function(&self, outer: CallerContext, index: ItemIndex, function_type: Option<&FunctionType>) -> Result, Error> { + self.instance.call_function(outer, index, function_type) } fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, type_index: u32, func_index: u32) -> Result, Error> { @@ -127,19 +136,19 @@ impl ModuleInstanceInterface for EnvModuleInstance { // TODO: check function type // to make interpreter independent of *SCRIPTEN runtime, just make abort/assert = interpreter Error match index { - INDEX_FUNC_ABORT => self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_ABORT)) + INDEX_FUNC_ABORT => self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_ABORT), Some(VariableType::I32)) .and_then(|g| g.set(RuntimeValue::I32(1))) .and_then(|_| Err(Error::Trap("abort".into()))), INDEX_FUNC_ASSERT => outer.value_stack.pop_as::() .and_then(|condition| if condition == 0 { - self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_ABORT)) + self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_ABORT), Some(VariableType::I32)) .and_then(|g| g.set(RuntimeValue::I32(1))) .and_then(|_| Err(Error::Trap("assertion failed".into()))) } else { Ok(None) }), INDEX_FUNC_ENLARGE_MEMORY => Ok(Some(RuntimeValue::I32(0))), // TODO: support memory enlarge - INDEX_FUNC_GET_TOTAL_MEMORY => self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_TOTAL_MEMORY)) + INDEX_FUNC_GET_TOTAL_MEMORY => self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_TOTAL_MEMORY), Some(VariableType::I32)) .map(|g| g.get()) .map(Some), INDEX_FUNC_MIN_NONUSED ... INDEX_FUNC_MAX => Err(Error::Trap("unimplemented".into())), diff --git a/src/interpreter/env_native.rs b/src/interpreter/env_native.rs index 7e92304..19216af 100644 --- a/src/interpreter/env_native.rs +++ b/src/interpreter/env_native.rs @@ -4,11 +4,11 @@ use parking_lot::RwLock; use elements::{FunctionType, Internal, ValueType}; use interpreter::Error; use interpreter::module::{ModuleInstanceInterface, ExecutionParams, ItemIndex, - CallerContext}; + CallerContext, ExportEntryType}; use interpreter::memory::MemoryInstance; use interpreter::table::TableInstance; use interpreter::value::RuntimeValue; -use interpreter::variable::VariableInstance; +use interpreter::variable::{VariableInstance, VariableType}; /// Min index of native function. pub const NATIVE_INDEX_FUNC_MIN: u32 = 10001; @@ -65,6 +65,10 @@ impl<'a> NativeModuleInstance<'a> { } impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> { + fn instantiate<'b>(&self, is_user_module: bool, externals: Option<&'b HashMap>>) -> Result<(), Error> { + self.env.instantiate(is_user_module, externals) + } + fn execute_index(&self, index: u32, params: ExecutionParams) -> Result, Error> { self.env.execute_index(index, params) } @@ -73,12 +77,28 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> { self.env.execute_export(name, params) } - fn export_entry(&self, name: &str) -> Result { + fn export_entry<'b>(&self, name: &str, externals: Option<&'b HashMap>>, required_type: &ExportEntryType) -> Result { if let Some(index) = self.by_name.get(name) { return Ok(Internal::Function(NATIVE_INDEX_FUNC_MIN + *index)); } - self.env.export_entry(name) + self.env.export_entry(name, externals, required_type) + } + + fn function_type<'b>(&self, function_index: ItemIndex, externals: Option<&'b HashMap>>) -> Result { + let index = match function_index { + ItemIndex::IndexSpace(index) | ItemIndex::Internal(index) => index, + ItemIndex::External(_) => unreachable!("trying to call function, exported by native env module"), + }; + + if index < NATIVE_INDEX_FUNC_MIN { + return self.env.function_type(function_index, externals); + } + + self.functions + .get((index - NATIVE_INDEX_FUNC_MIN) as usize) + .ok_or(Error::Native(format!("missing native env function with index {}", index))) + .map(|f| FunctionType::new(f.params.clone(), f.result.clone())) } fn table(&self, index: ItemIndex) -> Result, Error> { @@ -89,12 +109,12 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> { self.env.memory(index) } - fn global(&self, index: ItemIndex) -> Result, Error> { - self.env.global(index) + fn global(&self, index: ItemIndex, variable_type: Option) -> Result, Error> { + self.env.global(index, variable_type) } - fn call_function(&self, outer: CallerContext, index: ItemIndex) -> Result, Error> { - self.env.call_function(outer, index) + fn call_function(&self, outer: CallerContext, index: ItemIndex, function_type: Option<&FunctionType>) -> Result, Error> { + self.env.call_function(outer, index, function_type) } fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, type_index: u32, func_index: u32) -> Result, Error> { diff --git a/src/interpreter/imports.rs b/src/interpreter/imports.rs index c5adedf..8703333 100644 --- a/src/interpreter/imports.rs +++ b/src/interpreter/imports.rs @@ -1,12 +1,12 @@ use std::sync::{Arc, Weak}; use std::collections::HashMap; -use elements::{ImportSection, ImportEntry, External, Internal}; +use elements::{ImportSection, ImportEntry, External, Internal, FunctionType}; use interpreter::Error; use interpreter::memory::MemoryInstance; -use interpreter::module::{ModuleInstanceInterface, ItemIndex}; +use interpreter::module::{ModuleInstanceInterface, ItemIndex, ExportEntryType}; use interpreter::program::ProgramInstanceEssence; use interpreter::table::TableInstance; -use interpreter::variable::VariableInstance; +use interpreter::variable::{VariableInstance, VariableType}; // TODO: cache Internal-s to fasten access /// Module imports. @@ -119,8 +119,8 @@ impl ModuleImports { } /// Get function index. - pub fn function<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result { - let (_, export) = self.external_export(externals, import)?; + pub fn function<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry, required_type: Option<&FunctionType>) -> Result { + let (_, export) = self.external_export(externals, import, &required_type.map(|ft| ExportEntryType::Function(ft.clone())).unwrap_or(ExportEntryType::Any))?; if let Internal::Function(external_index) = export { return Ok(external_index); } @@ -130,7 +130,7 @@ impl ModuleImports { /// Get table reference. pub fn table<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result, Error> { - let (module, export) = self.external_export(externals, import)?; + let (module, export) = self.external_export(externals, import, &ExportEntryType::Any)?; if let Internal::Table(external_index) = export { return module.table(ItemIndex::Internal(external_index)); } @@ -140,7 +140,7 @@ impl ModuleImports { /// Get memory reference. pub fn memory<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result, Error> { - let (module, export) = self.external_export(externals, import)?; + let (module, export) = self.external_export(externals, import, &ExportEntryType::Any)?; if let Internal::Memory(external_index) = export { return module.memory(ItemIndex::Internal(external_index)); } @@ -149,19 +149,19 @@ impl ModuleImports { } /// Get global reference. - pub fn global<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result, Error> { - let (module, export) = self.external_export(externals, import)?; + pub fn global<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry, required_type: Option) -> Result, Error> { + let (module, export) = self.external_export(externals, import, &required_type.clone().map(|rt| ExportEntryType::Global(rt)).unwrap_or(ExportEntryType::Any))?; if let Internal::Global(external_index) = export { - return module.global(ItemIndex::Internal(external_index)); + return module.global(ItemIndex::Internal(external_index), required_type); } Err(Error::Program(format!("wrong import {} from module {} (expecting global)", import.field(), import.module()))) } - fn external_export<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result<(Arc, Internal), Error> { + fn external_export<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry, required_type: &ExportEntryType) -> Result<(Arc, Internal), Error> { self.module(externals, import.module()) .and_then(|m| - m.export_entry(import.field()) + m.export_entry(import.field(), externals, required_type) .map(|e| (m, e))) } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 049d7e7..7d3331a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -74,7 +74,7 @@ mod variable; mod tests; pub use self::memory::MemoryInstance; -pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, CallerContext, ExecutionParams}; +pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, ExportEntryType, CallerContext, ExecutionParams}; pub use self::table::TableInstance; pub use self::program::ProgramInstance; pub use self::value::RuntimeValue; diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index dd1ecb9..91c06af 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeSet, HashMap}; +use std::collections::HashMap; use std::iter::repeat; use std::sync::{Arc, Weak}; use elements::{Module, InitExpr, Opcode, Type, FunctionType, FuncBody, Internal, External, BlockType, ResizableLimits}; @@ -18,8 +18,8 @@ const DEFAULT_VALUE_STACK_LIMIT: usize = 16384; /// Maximum number of entries in frame stack. const DEFAULT_FRAME_STACK_LIMIT: usize = 128; // TODO: fix runner to support bigger depth -#[derive(Default, Clone)] /// Execution context. +#[derive(Default, Clone)] pub struct ExecutionParams<'a> { /// Arguments. pub args: Vec, @@ -27,22 +27,37 @@ pub struct ExecutionParams<'a> { pub externals: HashMap>, } +/// Export type. +#[derive(Debug, Clone)] +pub enum ExportEntryType { + /// Any type. + Any, + /// Type of function. + Function(FunctionType), + /// Type of global. + Global(VariableType), +} + /// Module instance API. pub trait ModuleInstanceInterface { + /// Run instantiation-time procedures (validation and start function [if any] call). Module is not completely validated until this call. + fn instantiate<'a>(&self, is_user_module: bool, externals: Option<&'a HashMap>>) -> Result<(), Error>; /// Execute function with the given index. fn execute_index(&self, index: u32, params: ExecutionParams) -> Result, Error>; /// Execute function with the given export name. fn execute_export(&self, name: &str, params: ExecutionParams) -> Result, Error>; /// Get export entry. - fn export_entry(&self, name: &str) -> Result; + fn export_entry<'a>(&self, name: &str, externals: Option<&'a HashMap>>, required_type: &ExportEntryType) -> Result; + /// Get function type. + fn function_type<'a>(&self, function_index: ItemIndex, externals: Option<&'a HashMap>>) -> Result; /// Get table reference. fn table(&self, index: ItemIndex) -> Result, Error>; /// Get memory reference. fn memory(&self, index: ItemIndex) -> Result, Error>; /// Get global reference. - fn global(&self, index: ItemIndex) -> Result, Error>; + fn global(&self, index: ItemIndex, variable_type: Option) -> Result, Error>; /// Call function with given index in functions index space. - fn call_function(&self, outer: CallerContext, index: ItemIndex) -> Result, Error>; + fn call_function(&self, outer: CallerContext, index: ItemIndex, function_type: Option<&FunctionType>) -> Result, Error>; /// Call function with given index in the given table. fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, type_index: u32, func_index: u32) -> Result, Error>; /// Call function with internal index. @@ -62,6 +77,8 @@ pub enum ItemIndex { /// Module instance. pub struct ModuleInstance { + /// Module name. + name: String, /// Module. module: Module, /// Module imports. @@ -115,12 +132,7 @@ impl<'a> From> for ExecutionParams<'a> { impl ModuleInstance { /// Instantiate given module within program context. - pub fn new(program: Weak, module: Module) -> Result { - Self::new_with_validation_flag(program, module, true) - } - - /// Instantiate given module within program context. - pub fn new_with_validation_flag(program: Weak, module: Module, is_user_module: bool) -> Result { + pub fn new<'a>(program: Weak, name: String, module: Module) -> Result { // load entries from import section let imports = ModuleImports::new(program, module.import_section()); @@ -147,7 +159,7 @@ impl ModuleInstance { Some(global_section) => global_section.entries() .iter() .map(|g| { - get_initializer(g.init_expr(), &module, &imports) + get_initializer(g.init_expr(), &module, &imports, g.global_type().content_type().into()) .map_err(|e| Error::Initialization(e.into())) .and_then(|v| VariableInstance::new_global(g.global_type(), v).map(Arc::new)) }) @@ -155,19 +167,47 @@ impl ModuleInstance { None => Vec::new(), }; - let mut module = ModuleInstance { + Ok(ModuleInstance { + name: name, module: module, imports: imports, memory: memory, tables: tables, globals: globals, - }; - module.complete_initialization(is_user_module)?; - Ok(module) + }) } - /// Complete module initialization. - fn complete_initialization(&mut self, is_user_module: bool) -> Result<(), Error> { + fn require_function(&self, index: ItemIndex) -> Result { + match self.imports.parse_function_index(index) { + ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), + ItemIndex::Internal(index) => self.module.function_section() + .ok_or(Error::Function(format!("missing internal function {}", index))) + .and_then(|s| s.entries().get(index as usize) + .ok_or(Error::Function(format!("missing internal function {}", index)))) + .map(|f| f.type_ref()), + ItemIndex::External(index) => self.module.import_section() + .ok_or(Error::Function(format!("missing external function {}", index))) + .and_then(|s| s.entries().get(index as usize) + .ok_or(Error::Function(format!("missing external function {}", index)))) + .and_then(|import| match import.external() { + &External::Function(type_idx) => Ok(type_idx), + _ => Err(Error::Function(format!("external function {} is pointing to non-function import", index))), + }), + } + } + + fn require_function_type(&self, type_index: u32) -> Result<&FunctionType, Error> { + self.module.type_section() + .ok_or(Error::Validation(format!("type reference {} exists in module without type section", type_index))) + .and_then(|s| match s.types().get(type_index as usize) { + Some(&Type::Function(ref function_type)) => Ok(function_type), + _ => Err(Error::Validation(format!("missing function type with index {}", type_index))), + }) + } +} + +impl ModuleInstanceInterface for ModuleInstance { + fn instantiate<'a>(&self, is_user_module: bool, externals: Option<&'a HashMap>>) -> Result<(), Error> { // validate start section if let Some(start_function) = self.module.start_section() { let func_type_index = self.require_function(ItemIndex::IndexSpace(start_function))?; @@ -182,19 +222,12 @@ impl ModuleInstance { // validate export section if is_user_module { // TODO: env module exports STACKTOP global, which is mutable => check is failed if let Some(export_section) = self.module.export_section() { - // duplicate name check is not on specification, but it seems logical - let mut names = BTreeSet::new(); for export in export_section.entries() { - if !names.insert(export.field()) { - return Err(Error::Validation(format!("duplicate export with name {}", export.field()))); - } - - // this allows reexporting match export.internal() { &Internal::Function(function_index) => self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?, &Internal::Global(global_index) => - self.global(ItemIndex::IndexSpace(global_index)) + self.global(ItemIndex::IndexSpace(global_index), None) .and_then(|g| if g.is_mutable() { Err(Error::Validation(format!("trying to export mutable global {}", export.field()))) } else { @@ -211,15 +244,43 @@ impl ModuleInstance { // validate import section if let Some(import_section) = self.module.import_section() { - // external module + its export existance is checked on runtime for import in import_section.entries() { match import.external() { - &External::Function(ref function_index) => self.require_function_type(*function_index).map(|_| ())?, + // for functions we need to check if function type matches in both modules + &External::Function(ref function_type_index) => { + // External::Function points to function type in type section in this module + let import_function_type = self.require_function_type(*function_type_index)?; + + // get export entry in external module + let external_module = self.imports.module(externals, import.module())?; + let export_entry = external_module.export_entry(import.field(), externals, &ExportEntryType::Function(import_function_type.clone()))?; + + // export entry points to function in function index space + // and Internal::Function points to type in type section + let export_function_type = match export_entry { + Internal::Function(function_index) => external_module.function_type(ItemIndex::IndexSpace(function_index), externals)?, + _ => return Err(Error::Validation(format!("Export with name {} from module {} is not a function", import.field(), import.module()))), + }; + + if import_function_type != &export_function_type { + return Err(Error::Validation(format!("Export function type {} mismatch. Expected function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}", + function_type_index, import_function_type.params(), import_function_type.return_type(), + export_function_type.params(), export_function_type.return_type()))); + } + }, &External::Global(ref global_type) => if global_type.is_mutable() { - return Err(Error::Validation(format!("trying to import mutable global {}", import.field()))) + return Err(Error::Validation(format!("trying to import mutable global {}", import.field()))); + } else { + self.imports.global(externals, import, Some(global_type.content_type().into()))?; + }, + &External::Memory(ref memory_type) => { + check_limits(memory_type.limits())?; + self.imports.memory(externals, import)?; + }, + &External::Table(ref table_type) => { + check_limits(table_type.limits())?; + self.imports.table(externals, import)?; }, - &External::Memory(ref memory_type) => check_limits(memory_type.limits())?, - &External::Table(ref table_type) => check_limits(table_type.limits())?, } } } @@ -253,14 +314,14 @@ impl ModuleInstance { locals.extend(function_body.locals().iter().flat_map(|l| repeat(l.value_type()).take(l.count() as usize))); let mut context = FunctionValidationContext::new(&self.module, &self.imports, &locals, DEFAULT_VALUE_STACK_LIMIT, DEFAULT_FRAME_STACK_LIMIT, &function_type); let block_type = function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult); - Validator::validate_block(&mut context, block_type, function_body.code().elements(), Opcode::End)?; + Validator::validate_block(&mut context, false, block_type, function_body.code().elements(), Opcode::End)?; } } // use data section to initialize linear memory regions if let Some(data_section) = self.module.data_section() { for (data_segment_index, data_segment) in data_section.entries().iter().enumerate() { - let offset: u32 = get_initializer(data_segment.offset(), &self.module, &self.imports)?.try_into()?; + let offset: u32 = get_initializer(data_segment.offset(), &self.module, &self.imports, VariableType::I32)?.try_into()?; self.memory(ItemIndex::IndexSpace(data_segment.index())) .map_err(|e| Error::Initialization(format!("DataSegment {} initializes non-existant MemoryInstance {}: {:?}", data_segment_index, data_segment.index(), e))) .and_then(|m| m.set(offset, data_segment.value())) @@ -271,14 +332,14 @@ impl ModuleInstance { // use element section to fill tables if let Some(element_section) = self.module.elements_section() { for (element_segment_index, element_segment) in element_section.entries().iter().enumerate() { - let offset: u32 = get_initializer(element_segment.offset(), &self.module, &self.imports)?.try_into()?; + let offset: u32 = get_initializer(element_segment.offset(), &self.module, &self.imports, VariableType::I32)?.try_into()?; for function_index in element_segment.members() { self.require_function(ItemIndex::IndexSpace(*function_index))?; } self.table(ItemIndex::IndexSpace(element_segment.index())) .map_err(|e| Error::Initialization(format!("ElementSegment {} initializes non-existant Table {}: {:?}", element_segment_index, element_segment.index(), e))) - .and_then(|m| m.set_raw(offset, element_segment.members())) + .and_then(|m| m.set_raw(offset, self.name.clone(), element_segment.members())) .map_err(|e| Error::Initialization(e.into()))?; } } @@ -291,41 +352,11 @@ impl ModuleInstance { Ok(()) } - fn require_function(&self, index: ItemIndex) -> Result { - match self.imports.parse_function_index(index) { - ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), - ItemIndex::Internal(index) => self.module.function_section() - .ok_or(Error::Function(format!("missing internal function {}", index))) - .and_then(|s| s.entries().get(index as usize) - .ok_or(Error::Function(format!("missing internal function {}", index)))) - .map(|f| f.type_ref()), - ItemIndex::External(index) => self.module.import_section() - .ok_or(Error::Function(format!("missing external function {}", index))) - .and_then(|s| s.entries().get(index as usize) - .ok_or(Error::Function(format!("missing external function {}", index)))) - .and_then(|import| match import.external() { - &External::Function(type_idx) => Ok(type_idx), - _ => Err(Error::Function(format!("external function {} is pointing to non-function import", index))), - }), - } - } - - fn require_function_type(&self, type_index: u32) -> Result<&FunctionType, Error> { - self.module.type_section() - .ok_or(Error::Validation(format!("type reference {} exists in module without type section", type_index))) - .and_then(|s| match s.types().get(type_index as usize) { - Some(&Type::Function(ref function_type)) => Ok(function_type), - _ => Err(Error::Validation(format!("missing function type with index {}", type_index))), - }) - } -} - -impl ModuleInstanceInterface for ModuleInstance { fn execute_index(&self, index: u32, params: ExecutionParams) -> Result, Error> { let args_len = params.args.len(); let mut args = StackWithLimit::with_data(params.args, args_len); let caller_context = CallerContext::topmost(&mut args, ¶ms.externals); - self.call_function(caller_context, ItemIndex::IndexSpace(index)) + self.call_function(caller_context, ItemIndex::IndexSpace(index), None) } fn execute_export(&self, name: &str, params: ExecutionParams) -> Result, Error> { @@ -345,15 +376,42 @@ impl ModuleInstanceInterface for ModuleInstance { self.execute_index(index, params) } - fn export_entry(&self, name: &str) -> Result { + fn export_entry<'a>(&self, name: &str, externals: Option<&'a HashMap>>, required_type: &ExportEntryType) -> Result { self.module.export_section() .ok_or(Error::Program(format!("trying to import {} from module without export section", name))) .and_then(|s| s.entries().iter() - .find(|e| e.field() == name) + .find(|e| e.field() == name && match required_type { + &ExportEntryType::Any => true, + &ExportEntryType::Global(global_type) => match e.internal() { + &Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type)).map(|_| true).unwrap_or(false), + _ => false, + }, + &ExportEntryType::Function(ref required_type) => match e.internal() { + &Internal::Function(function_index) => + self.function_type(ItemIndex::IndexSpace(function_index), externals) + .map(|ft| &ft == required_type) + .unwrap_or(false), + _ => false, + }, + }) .map(|e| *e.internal()) .ok_or(Error::Program(format!("unresolved import {}", name)))) } + fn function_type<'a>(&self, function_index: ItemIndex, externals: Option<&'a HashMap>>) -> Result { + match self.imports.parse_function_index(function_index) { + ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), + ItemIndex::Internal(index) => self.require_function(ItemIndex::Internal(index)) + .and_then(|ft| self.require_function_type(ft).map(Clone::clone)), + ItemIndex::External(index) => self.module.import_section() + .ok_or(Error::Function(format!("trying to access external function with index {} in module without import section", index))) + .and_then(|s| s.entries().get(index as usize) + .ok_or(Error::Function(format!("trying to access external function with index {} in module with {}-entries import section", index, s.entries().len())))) + .and_then(|e| self.imports.module(externals, e.module())) + .and_then(|m| m.function_type(ItemIndex::IndexSpace(index), externals)), + } + } + fn table(&self, index: ItemIndex) -> Result, Error> { match self.imports.parse_table_index(index) { ItemIndex::IndexSpace(_) => unreachable!("parse_table_index resolves IndexSpace option"), @@ -380,7 +438,7 @@ impl ModuleInstanceInterface for ModuleInstance { } } - fn global(&self, index: ItemIndex) -> Result, Error> { + fn global(&self, index: ItemIndex, variable_type: Option) -> Result, Error> { match self.imports.parse_global_index(index) { ItemIndex::IndexSpace(_) => unreachable!("parse_global_index resolves IndexSpace option"), ItemIndex::Internal(index) => self.globals.get(index as usize).cloned() @@ -389,21 +447,21 @@ impl ModuleInstanceInterface for ModuleInstance { .ok_or(Error::Global(format!("trying to access external global with index {} in module without import section", index))) .and_then(|s| s.entries().get(index as usize) .ok_or(Error::Global(format!("trying to access external global with index {} in module with {}-entries import section", index, s.entries().len())))) - .and_then(|e| self.imports.global(None, e)), + .and_then(|e| self.imports.global(None, e, variable_type)), } } - fn call_function(&self, outer: CallerContext, index: ItemIndex) -> Result, Error> { + fn call_function(&self, outer: CallerContext, index: ItemIndex, function_type: Option<&FunctionType>) -> Result, Error> { match self.imports.parse_function_index(index) { ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), - ItemIndex::Internal(index) => self.call_internal_function(outer, index, None), - ItemIndex::External(index) => - self.module.import_section() + ItemIndex::Internal(index) => self.call_internal_function(outer, index, function_type), + ItemIndex::External(index) => self.module.import_section() .ok_or(Error::Function(format!("trying to access external function with index {} in module without import section", index))) .and_then(|s| s.entries().get(index as usize) .ok_or(Error::Function(format!("trying to access external function with index {} in module with {}-entries import section", index, s.entries().len())))) - .and_then(|e| Ok((self.imports.module(Some(outer.externals), e.module())?, self.imports.function(Some(outer.externals), e)?))) - .and_then(|(m, index)| m.call_internal_function(outer, index, None)), + .and_then(|e| Ok((self.imports.module(Some(outer.externals), e.module())?, + self.imports.function(Some(outer.externals), e, function_type)?))) + .and_then(|(m, index)| m.call_internal_function(outer, index, function_type)), } } @@ -415,30 +473,14 @@ impl ModuleInstanceInterface for ModuleInstance { &Type::Function(ref function_type) => function_type, }; - match self.imports.parse_table_index(table_index) { - ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), - ItemIndex::Internal(table_index) => { - let table = self.table(ItemIndex::Internal(table_index))?; - let index = match table.get(func_index)? { - RuntimeValue::AnyFunc(index) => index, - _ => return Err(Error::Function(format!("trying to indirect call function {} via non-anyfunc table {}", func_index, table_index))), - }; - self.call_internal_function(outer, index, Some(function_type)) - }, - ItemIndex::External(table_index) => { - let table = self.table(ItemIndex::External(table_index))?; - let index = match table.get(func_index)? { - RuntimeValue::AnyFunc(index) => index, - _ => return Err(Error::Function(format!("trying to indirect call function {} via non-anyfunc table {}", func_index, table_index))), - }; - let module = self.module.import_section() - .ok_or(Error::Function(format!("trying to access external table with index {} in module without import section", table_index))) - .and_then(|s| s.entries().get(table_index as usize) - .ok_or(Error::Function(format!("trying to access external table with index {} in module with {}-entries import section", table_index, s.entries().len())))) - .and_then(|e| self.imports.module(Some(outer.externals), e.module()))?; - module.call_internal_function(outer, index, Some(function_type)) - } - } + let table = self.table(table_index)?; + let (module, index) = match table.get(func_index)? { + RuntimeValue::AnyFunc(module, index) => (module.clone(), index), + _ => return Err(Error::Function(format!("trying to indirect call function {} via non-anyfunc table {:?}", func_index, table_index))), + }; + + let module = self.imports.module(Some(outer.externals), &module)?; + module.call_function(outer, ItemIndex::IndexSpace(index), Some(function_type)) } fn call_internal_function(&self, mut outer: CallerContext, index: u32, function_type: Option<&FunctionType>) -> Result, Error> { @@ -459,7 +501,7 @@ impl ModuleInstanceInterface for ModuleInstance { .ok_or(Error::Function(format!("trying to call function with index {} in module without types section", index))) .and_then(|s| s.types() .get(function_type_index as usize) - .ok_or(Error::Function(format!("trying to call function with type index {} in module with {} types", index, s.types().len()))))?; + .ok_or(Error::Function(format!("trying to call function with type index {} in module with {} types", function_type_index, s.types().len()))))?; let actual_function_type = match item_type { &Type::Function(ref function_type) => function_type, }; @@ -542,7 +584,7 @@ fn prepare_function_locals(function_type: &FunctionType, function_body: &FuncBod .collect::, _>>() } -fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports) -> Result { +fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports, expected_type: VariableType) -> Result { let first_opcode = match expr.code().len() { 1 => &expr.code()[0], 2 if expr.code().len() == 2 && expr.code()[1] == Opcode::End => &expr.code()[0], @@ -559,7 +601,7 @@ fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports) -> .ok_or(Error::Global(format!("trying to initialize with external global with index {} in module without import section", index))) .and_then(|s| s.entries().get(index as usize) .ok_or(Error::Global(format!("trying to initialize with external global with index {} in module with {}-entries import section", index, s.entries().len())))) - .and_then(|e| imports.global(None, e)) + .and_then(|e| imports.global(None, e, Some(expected_type))) .map(|g| g.get()) }, &Opcode::I32Const(val) => Ok(RuntimeValue::I32(val)), diff --git a/src/interpreter/program.rs b/src/interpreter/program.rs index 0bcbe8c..cc272ee 100644 --- a/src/interpreter/program.rs +++ b/src/interpreter/program.rs @@ -32,16 +32,25 @@ impl ProgramInstance { } /// Instantiate module with validation. - pub fn add_module(&self, name: &str, module: Module) -> Result, Error> { - let module_instance = Arc::new(ModuleInstance::new(Arc::downgrade(&self.essence), module)?); + pub fn add_module<'a>(&self, name: &str, module: Module, externals: Option<&'a HashMap>>) -> Result, Error> { + let module_instance = Arc::new(ModuleInstance::new(Arc::downgrade(&self.essence), name.into(), module)?); + module_instance.instantiate(true, externals)?; // replace existing module with the same name with new one self.essence.modules.write().insert(name.into(), module_instance.clone()); Ok(module_instance) } /// Instantiate module without validation. - pub fn add_module_without_validation(&self, name: &str, module: Module) -> Result, Error> { - let module_instance = Arc::new(ModuleInstance::new_with_validation_flag(Arc::downgrade(&self.essence), module, false)?); + pub fn add_module_without_validation<'a>(&self, name: &str, module: Module, externals: Option<&'a HashMap>>) -> Result, Error> { + let module_instance = Arc::new(ModuleInstance::new(Arc::downgrade(&self.essence), name.into(), module)?); + module_instance.instantiate(false, externals)?; + // replace existing module with the same name with new one + self.essence.modules.write().insert(name.into(), module_instance.clone()); + Ok(module_instance) + } + + /// Insert instantiated module. + pub fn insert_loaded_module(&self, name: &str, module_instance: Arc) -> Result, Error> { // replace existing module with the same name with new one self.essence.modules.write().insert(name.into(), module_instance.clone()); Ok(module_instance) diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index 6e2b8da..da73e78 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -56,6 +56,8 @@ pub enum InstructionOutcome { #[derive(Debug, Clone)] pub struct BlockFrame { + /// Is loop frame? + is_loop: bool, // A label for reference from branch instructions. branch_position: usize, // A label for reference from end instructions. @@ -69,7 +71,7 @@ pub struct BlockFrame { impl Interpreter { pub fn run_function(context: &mut FunctionContext, body: &[Opcode]) -> Result, Error> { let return_type = context.return_type; - context.push_frame(body.len() - 1, body.len() - 1, return_type)?; + context.push_frame(false, body.len() - 1, body.len() - 1, return_type)?; Interpreter::execute_block(context, body)?; match context.return_type { @@ -282,13 +284,13 @@ impl Interpreter { fn run_block(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result { let frame_position = context.position + 1; - context.push_frame(frame_position, frame_position, block_type.clone())?; + context.push_frame(false, frame_position, frame_position, block_type.clone())?; Interpreter::execute_block(context, body) } fn run_loop(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result { let frame_position = context.position; - context.push_frame(frame_position, frame_position + 1, block_type.clone())?; + context.push_frame(true, frame_position, frame_position + 1, block_type.clone())?; Interpreter::execute_block(context, body) } @@ -303,7 +305,7 @@ impl Interpreter { if begin_index != end_index { let frame_position = context.position + 1; - context.push_frame(frame_position, frame_position, block_type.clone())?; + context.push_frame(false, frame_position, frame_position, block_type.clone())?; Interpreter::execute_block(context, &body[begin_index..end_index]) } else { Ok(InstructionOutcome::RunNextInstruction) @@ -394,7 +396,7 @@ impl Interpreter { fn run_get_global(context: &mut FunctionContext, index: u32) -> Result { context.module() - .global(ItemIndex::IndexSpace(index)) + .global(ItemIndex::IndexSpace(index), None) .and_then(|g| context.value_stack_mut().push(g.get())) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -403,7 +405,7 @@ impl Interpreter { context .value_stack_mut() .pop() - .and_then(|v| context.module().global(ItemIndex::IndexSpace(index)).and_then(|g| g.set(v))) + .and_then(|v| context.module().global(ItemIndex::IndexSpace(index), None).and_then(|g| g.set(v))) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -856,7 +858,7 @@ impl Interpreter { loop { let instruction = &body[context.position]; - //println!("=== RUNNING {:?}", instruction); // TODO: trace + debug!(target: "interpreter", "running {:?}", instruction); match Interpreter::run_instruction(context, instruction)? { InstructionOutcome::RunInstruction => (), InstructionOutcome::RunNextInstruction => context.position += 1, @@ -901,7 +903,7 @@ impl<'a> FunctionContext<'a> { } pub fn call_function(&mut self, index: u32) -> Result, Error> { - self.module.call_function(CallerContext::nested(self), ItemIndex::IndexSpace(index)) + self.module.call_function(CallerContext::nested(self), ItemIndex::IndexSpace(index), None) } pub fn call_function_indirect(&mut self, table_index: u32, type_index: u32, func_index: u32) -> Result, Error> { @@ -933,8 +935,9 @@ impl<'a> FunctionContext<'a> { &self.frame_stack } - pub fn push_frame(&mut self, branch_position: usize, end_position: usize, signature: BlockType) -> Result<(), Error> { + pub fn push_frame(&mut self, is_loop: bool, branch_position: usize, end_position: usize, signature: BlockType) -> Result<(), Error> { self.frame_stack.push(BlockFrame { + is_loop: is_loop, branch_position: branch_position, end_position: end_position, value_limit: self.value_stack.len(), @@ -952,10 +955,10 @@ impl<'a> FunctionContext<'a> { if frame.value_limit > self.value_stack.len() { return Err(Error::Stack("invalid stack len".into())); } - + let frame_value = match frame.signature { - BlockType::Value(_) => Some(self.value_stack.pop()?), - BlockType::NoResult => None, + BlockType::Value(_) if !frame.is_loop || !is_branch => Some(self.value_stack.pop()?), + _ => None, }; self.value_stack.resize(frame.value_limit, RuntimeValue::I32(0)); self.position = if is_branch { frame.branch_position } else { frame.end_position }; @@ -970,6 +973,7 @@ impl<'a> FunctionContext<'a> { impl BlockFrame { pub fn invalid() -> Self { BlockFrame { + is_loop: false, branch_position: usize::max_value(), end_position: usize::max_value(), value_limit: usize::max_value(), diff --git a/src/interpreter/table.rs b/src/interpreter/table.rs index 52d6d23..930b022 100644 --- a/src/interpreter/table.rs +++ b/src/interpreter/table.rs @@ -38,10 +38,10 @@ impl TableInstance { } /// Set the table value from raw slice - pub fn set_raw(&self, mut offset: u32, value: &[u32]) -> Result<(), Error> { + pub fn set_raw(&self, mut offset: u32, module_name: String, value: &[u32]) -> Result<(), Error> { for val in value { match self.variable_type { - VariableType::AnyFunc => self.set(offset, RuntimeValue::AnyFunc(*val))?, + VariableType::AnyFunc => self.set(offset, RuntimeValue::AnyFunc(module_name.clone(), *val))?, _ => return Err(Error::Table(format!("table of type {:?} is not supported", self.variable_type))), } offset += 1; diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index a4e5294..e8e9f35 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -38,8 +38,8 @@ fn import_function() { .build(); let program = ProgramInstance::new().unwrap(); - let external_module = program.add_module("external_module", module1).unwrap(); - let main_module = program.add_module("main", module2).unwrap(); + let external_module = program.add_module("external_module", module1, None).unwrap(); + let main_module = program.add_module("main", module2, None).unwrap(); assert_eq!(external_module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(3)); assert_eq!(main_module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(10)); @@ -72,10 +72,8 @@ fn wrong_import() { .build(); let program = ProgramInstance::new().unwrap(); - let _side_module_instance = program.add_module("side_module", side_module).unwrap(); - let module_instance = program.add_module("main", module).unwrap(); - - assert!(module_instance.execute_index(1, vec![].into()).is_err()); + let _side_module_instance = program.add_module("side_module", side_module, None).unwrap(); + assert!(program.add_module("main", module, None).is_err()); } #[test] @@ -117,7 +115,7 @@ fn global_get_set() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module_without_validation("main", module).unwrap(); // validation is failing (accessing immutable global) + let module = program.add_module_without_validation("main", module, None).unwrap(); // validation is failing (accessing immutable global) assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(50)); assert_eq!(module.execute_index(1, vec![].into()).unwrap_err(), Error::Variable("trying to update immutable variable".into())); assert_eq!(module.execute_index(2, vec![].into()).unwrap_err(), Error::Variable("trying to update variable of type I32 with value of type Some(I64)".into())); @@ -162,36 +160,12 @@ fn single_program_different_modules() { // => linear memory is created let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap(); - let module = module() - .with_import(ImportEntry::new("env".into(), "add".into(), External::Function(0))) - .with_import(ImportEntry::new("env".into(), "sub".into(), External::Function(0))) - .function() - .signature().param().i32().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::Call(0), - Opcode::End, - ])).build() - .build() - .function() - .signature().param().i32().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::Call(1), - Opcode::End, - ])).build() - .build() - .build(); - - // load module - let module_instance = program.add_module("main", module).unwrap(); - + // create native env module executor let mut executor = FunctionExecutor { memory: env_memory.clone(), values: Vec::new(), }; { - // create native env module with native add && sub implementations let functions: UserFunctions = UserFunctions { executor: &mut executor, functions: vec![UserFunction { @@ -205,13 +179,37 @@ fn single_program_different_modules() { }], }; let native_env_instance = Arc::new(env_native_module(env_instance, functions).unwrap()); - - // execute functions let params = ExecutionParams::with_external("env".into(), native_env_instance); - assert_eq!(module_instance.execute_index(2, params.clone().add_argument(RuntimeValue::I32(7))).unwrap().unwrap(), RuntimeValue::I32(7)); - assert_eq!(module_instance.execute_index(2, params.clone().add_argument(RuntimeValue::I32(50))).unwrap().unwrap(), RuntimeValue::I32(57)); - assert_eq!(module_instance.execute_index(3, params.clone().add_argument(RuntimeValue::I32(15))).unwrap().unwrap(), RuntimeValue::I32(42)); + let module = module() + .with_import(ImportEntry::new("env".into(), "add".into(), External::Function(0))) + .with_import(ImportEntry::new("env".into(), "sub".into(), External::Function(0))) + .function() + .signature().param().i32().return_type().i32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::GetLocal(0), + Opcode::Call(0), + Opcode::End, + ])).build() + .build() + .function() + .signature().param().i32().return_type().i32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::GetLocal(0), + Opcode::Call(1), + Opcode::End, + ])).build() + .build() + .build(); + + // load module + let module_instance = program.add_module("main", module, Some(¶ms.externals)).unwrap(); + + { + assert_eq!(module_instance.execute_index(2, params.clone().add_argument(RuntimeValue::I32(7))).unwrap().unwrap(), RuntimeValue::I32(7)); + assert_eq!(module_instance.execute_index(2, params.clone().add_argument(RuntimeValue::I32(50))).unwrap().unwrap(), RuntimeValue::I32(57)); + assert_eq!(module_instance.execute_index(3, params.clone().add_argument(RuntimeValue::I32(15))).unwrap().unwrap(), RuntimeValue::I32(42)); + } } assert_eq!(executor.memory.get(0, 1).unwrap()[0], 42); diff --git a/src/interpreter/tests/wabt.rs b/src/interpreter/tests/wabt.rs index e2d511f..0897212 100644 --- a/src/interpreter/tests/wabt.rs +++ b/src/interpreter/tests/wabt.rs @@ -13,7 +13,7 @@ use interpreter::variable::{VariableInstance, VariableType}; fn run_function_i32(body: &Opcodes, arg: i32) -> Result { let ftype = FunctionType::new(vec![ValueType::I32], Some(ValueType::I32)); - let module = ModuleInstance::new(Weak::default(), Module::default()).unwrap(); + let module = ModuleInstance::new(Weak::default(), "test".into(), Module::default()).unwrap(); let externals = HashMap::new(); let mut context = FunctionContext::new(&module, &externals, 1024, 1024, &ftype, vec![ VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(arg)).unwrap(), // arg @@ -522,7 +522,7 @@ fn return_void() { .build() .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); module.execute_index(0, vec![RuntimeValue::I32(0)].into()).unwrap(); let memory = module.memory(ItemIndex::IndexSpace(0)).unwrap(); @@ -581,7 +581,7 @@ fn call_1() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(10)); } @@ -630,7 +630,7 @@ fn call_2() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(3628800)); } @@ -675,7 +675,7 @@ fn call_zero_args() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(43)); } @@ -721,7 +721,7 @@ fn callindirect_1() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(2, vec![RuntimeValue::I32(0)].into()).unwrap().unwrap(), RuntimeValue::I32(0)); assert_eq!(module.execute_index(2, vec![RuntimeValue::I32(1)].into()).unwrap().unwrap(), RuntimeValue::I32(1)); } @@ -794,7 +794,7 @@ fn callindirect_2() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(0)].into()).unwrap().unwrap(), RuntimeValue::I32(14)); assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(1)].into()).unwrap().unwrap(), RuntimeValue::I32(6)); assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(2)].into()).unwrap_err(), @@ -862,7 +862,7 @@ fn select() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![RuntimeValue::I32(0)].into()).unwrap().unwrap(), RuntimeValue::I32(2)); assert_eq!(module.execute_index(0, vec![RuntimeValue::I32(1)].into()).unwrap().unwrap(), RuntimeValue::I32(1)); assert_eq!(module.execute_index(1, vec![RuntimeValue::I32(0)].into()).unwrap().unwrap(), RuntimeValue::I64(2)); @@ -1015,7 +1015,7 @@ fn binary_i32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(3)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(16)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(21)); @@ -1175,7 +1175,7 @@ fn binary_i64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(3)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(16)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(21)); @@ -1265,7 +1265,7 @@ fn binary_f32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(5.000000)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-9995.500000)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-8487.187500)); @@ -1347,7 +1347,7 @@ fn binary_f64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(1111111110.000000)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(123400000000000007812762268812638756607430593436581896388608.000000)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(-15179717820000.000000)); @@ -1400,7 +1400,7 @@ fn cast() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(4.5)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1067450368)); // 3227516928 assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(125.125000)); @@ -1666,7 +1666,7 @@ fn compare_i32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); @@ -1956,7 +1956,7 @@ fn compare_i64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); @@ -2140,7 +2140,7 @@ fn compare_f32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); @@ -2312,7 +2312,7 @@ fn compare_f64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); @@ -2380,7 +2380,7 @@ fn convert_i32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1)); // 4294967295 assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-100)); // 4294967196 assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1294967296)); // 3000000000 @@ -2453,7 +2453,7 @@ fn convert_i64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(4294967295)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-1)); // 18446744073709551615 assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); @@ -2511,7 +2511,7 @@ fn convert_f32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-1.000000)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(4294967296.000000)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(12345679.000000)); @@ -2568,7 +2568,7 @@ fn convert_f64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(-1.000000)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(4294967295.000000)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(12345679.000000)); @@ -2628,7 +2628,7 @@ fn load_i32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1)); @@ -2704,7 +2704,7 @@ fn load_i64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-1)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-1)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-1)); @@ -2734,7 +2734,7 @@ fn load_f32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(25.750000)); } @@ -2758,7 +2758,7 @@ fn load_f64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(1023.875000)); } @@ -2815,7 +2815,7 @@ fn store_i32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-16909061)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-859059511)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-123456)); @@ -2885,7 +2885,7 @@ fn store_i64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(4278058235)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(3435907785)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(4294843840)); @@ -2913,7 +2913,7 @@ fn store_f32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1069547520)); } @@ -2938,7 +2938,7 @@ fn store_f64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1064352256)); } @@ -2989,7 +2989,7 @@ fn unary_i32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(24)); @@ -3044,7 +3044,7 @@ fn unary_i64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(56)); @@ -3143,7 +3143,7 @@ fn unary_f32() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-100.000000)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(100.000000)); assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); @@ -3246,7 +3246,7 @@ fn unary_f64() { .build(); let program = ProgramInstance::new().unwrap(); - let module = program.add_module("main", module).unwrap(); + let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(-100.000000)); assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(100.000000)); assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); diff --git a/src/interpreter/validator.rs b/src/interpreter/validator.rs index df6698a..7d995a0 100644 --- a/src/interpreter/validator.rs +++ b/src/interpreter/validator.rs @@ -39,7 +39,9 @@ pub enum StackValueType { /// Function validation frame. #[derive(Debug, Clone)] -struct ValidationFrame { +pub struct ValidationFrame { + /// Is loop frame? + pub is_loop: bool, /// Return type. pub block_type: BlockType, /// Value stack len. @@ -59,12 +61,12 @@ pub enum InstructionOutcome { } impl Validator { - pub fn validate_block(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode], end_instr: Opcode) -> Result { + pub fn validate_block(context: &mut FunctionValidationContext, is_loop: bool, block_type: BlockType, body: &[Opcode], end_instr: Opcode) -> Result { if body.is_empty() || body[body.len() - 1] != end_instr { return Err(Error::Validation("Every block must end with end/else instruction".into())); } - context.push_label(block_type)?; + context.push_label(is_loop, block_type)?; for opcode in body { match Validator::validate_instruction(context, opcode)? { InstructionOutcome::RunNextInstruction => (), @@ -75,10 +77,11 @@ impl Validator { } pub fn validate_instruction(context: &mut FunctionValidationContext, opcode: &Opcode) -> Result { + debug!(target: "validator", "validating {:?}", opcode); match opcode { &Opcode::Unreachable => Ok(InstructionOutcome::Unreachable), &Opcode::Nop => Ok(InstructionOutcome::RunNextInstruction), - &Opcode::Block(block_type, ref ops) => Validator::validate_block(context, block_type, ops.elements(), Opcode::End), + &Opcode::Block(block_type, ref ops) => Validator::validate_block(context, false, block_type, ops.elements(), Opcode::End), &Opcode::Loop(block_type, ref ops) => Validator::validate_loop(context, block_type, ops.elements()), &Opcode::If(block_type, ref ops) => Validator::validate_if(context, block_type, ops.elements()), &Opcode::Else => Ok(InstructionOutcome::RunNextInstruction), @@ -384,7 +387,7 @@ impl Validator { } fn validate_loop(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode]) -> Result { - Validator::validate_block(context, block_type, body, Opcode::End) + Validator::validate_block(context, true, block_type, body, Opcode::End) } fn validate_if(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode]) -> Result { @@ -395,32 +398,43 @@ impl Validator { .position(|op| *op == Opcode::Else) .unwrap_or(body_len - 1); if separator_index != body_len - 1 { - Validator::validate_block(context, block_type, &body[..separator_index + 1], Opcode::Else)?; - Validator::validate_block(context, block_type, &body[separator_index+1..], Opcode::End) + Validator::validate_block(context, false, block_type, &body[..separator_index + 1], Opcode::Else)?; + Validator::validate_block(context, false, block_type, &body[separator_index+1..], Opcode::End) } else { - Validator::validate_block(context, block_type, body, Opcode::End) + if block_type != BlockType::NoResult { + return Err(Error::Validation(format!("If block without else required to have NoResult block type. But it have {:?} type", block_type))); + } + + Validator::validate_block(context, false, block_type, body, Opcode::End) } } fn validate_br(context: &mut FunctionValidationContext, idx: u32) -> Result { - if let BlockType::Value(value_type) = context.require_label(idx)? { - context.tee_value(value_type.into())?; + let (frame_is_loop, frame_block_type) = { + let frame = context.require_label(idx)?; + (frame.is_loop, frame.block_type) + }; + + if !frame_is_loop { + if let BlockType::Value(value_type) = frame_block_type { + context.tee_value(value_type.into())?; + } } Ok(InstructionOutcome::Unreachable) } fn validate_br_if(context: &mut FunctionValidationContext, idx: u32) -> Result { context.pop_value(ValueType::I32.into())?; - if let BlockType::Value(value_type) = context.require_label(idx)? { + if let BlockType::Value(value_type) = context.require_label(idx)?.block_type { context.tee_value(value_type.into())?; } Ok(InstructionOutcome::RunNextInstruction) } fn validate_br_table(context: &mut FunctionValidationContext, table: &Vec, default: u32) -> Result { - let default_block_type = context.require_label(default)?; + let default_block_type = context.require_label(default)?.block_type; for label in table { - let label_block_type = context.require_label(*label)?; + let label_block_type = context.require_label(*label)?.block_type; if default_block_type != label_block_type { return Err(Error::Validation(format!("Default label in br_table points to block of type {:?}, while other points to {:?}", default_block_type, label_block_type))); } @@ -454,6 +468,8 @@ impl Validator { fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result { context.require_table(DEFAULT_TABLE_INDEX, VariableType::AnyFunc)?; + + context.pop_value(ValueType::I32.into())?; let (argument_types, return_type) = context.require_function_type(idx)?; for argument_type in argument_types.iter().rev() { context.pop_value((*argument_type).into())?; @@ -537,8 +553,9 @@ impl<'a> FunctionValidationContext<'a> { self.value_stack.push(StackValueType::AnyUnlimited) } - pub fn push_label(&mut self, block_type: BlockType) -> Result<(), Error> { + pub fn push_label(&mut self, is_loop: bool, block_type: BlockType) -> Result<(), Error> { self.frame_stack.push(ValidationFrame { + is_loop: is_loop, block_type: block_type, value_stack_len: self.value_stack.len() }) @@ -555,7 +572,7 @@ impl<'a> FunctionValidationContext<'a> { match frame.block_type { BlockType::NoResult if actual_value_type.map(|vt| vt.is_any_unlimited()).unwrap_or(true) => (), - BlockType::Value(required_value_type) if actual_value_type.map(|vt| vt == required_value_type).unwrap_or(false) =>(), + BlockType::Value(required_value_type) if actual_value_type.map(|vt| vt == required_value_type).unwrap_or(false) => (), _ => return Err(Error::Validation(format!("Expected block to return {:?} while it has returned {:?}", frame.block_type, actual_value_type))), } if let BlockType::Value(value_type) = frame.block_type { @@ -565,8 +582,8 @@ impl<'a> FunctionValidationContext<'a> { Ok(InstructionOutcome::RunNextInstruction) } - pub fn require_label(&self, idx: u32) -> Result { - self.frame_stack.get(idx as usize).map(|ref frame| frame.block_type) + pub fn require_label(&self, idx: u32) -> Result<&ValidationFrame, Error> { + self.frame_stack.get(idx as usize) } pub fn return_type(&self) -> Result { diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 87c8340..736407f 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -9,8 +9,8 @@ use interpreter::variable::VariableType; pub enum RuntimeValue { /// Null value. Null, - /// Reference to the function in the same module. - AnyFunc(u32), + /// Reference to the function in the given module' function index space. + AnyFunc(String, u32), /// 32b-length signed/unsigned int. I32(i32), /// 64b-length signed/unsigned int. @@ -115,7 +115,7 @@ impl RuntimeValue { /// Creates new default value of given type. pub fn default(variable_type: VariableType) -> Self { match variable_type { - VariableType::AnyFunc => RuntimeValue::AnyFunc(0), + VariableType::AnyFunc => RuntimeValue::AnyFunc("".into(), 0), VariableType::I32 => RuntimeValue::I32(0), VariableType::I64 => RuntimeValue::I64(0), VariableType::F32 => RuntimeValue::F32(0f32), @@ -141,19 +141,11 @@ impl RuntimeValue { } } - /// Gets function index, if type of value is AnyFunc. - pub fn as_any_func_index(&self) -> Option { - match *self { - RuntimeValue::AnyFunc(idx) => Some(idx), - _ => None, - } - } - /// Get variable type for this value. pub fn variable_type(&self) -> Option { match *self { RuntimeValue::Null => None, - RuntimeValue::AnyFunc(_) => Some(VariableType::AnyFunc), + RuntimeValue::AnyFunc(_, _) => Some(VariableType::AnyFunc), RuntimeValue::I32(_) => Some(VariableType::I32), RuntimeValue::I64(_) => Some(VariableType::I64), RuntimeValue::F32(_) => Some(VariableType::F32), @@ -275,16 +267,18 @@ macro_rules! impl_try_truncate_into { ($from: ident, $into: ident) => { impl TryTruncateInto<$into, Error> for $from { fn try_truncate_into(self) -> Result<$into, Error> { - if !self.is_normal() { + // Casting from a float to an integer will round the float towards zero + // NOTE: currently this will cause Undefined Behavior if the rounded value cannot be represented by the + // target integer type. This includes Inf and NaN. This is a bug and will be fixed. + if self.is_nan() || self.is_infinite() { return Err(Error::Value("invalid float value for this operation".into())); } - let truncated = self.trunc(); - if truncated < $into::MIN as $from || truncated > $into::MAX as $from { + if self < $into::MIN as $from || self > $into::MAX as $from { return Err(Error::Value("invalid float value for this operation".into())); } - Ok(truncated as $into) + Ok(self as $into) } } } diff --git a/src/interpreter/variable.rs b/src/interpreter/variable.rs index 74ebde3..d0b1583 100644 --- a/src/interpreter/variable.rs +++ b/src/interpreter/variable.rs @@ -54,6 +54,11 @@ impl VariableInstance { self.is_mutable } + /// Get variable type. + pub fn variable_type(&self) -> VariableType { + self.variable_type + } + /// Get the value of the variable instance pub fn get(&self) -> RuntimeValue { self.value.read().clone() diff --git a/src/lib.rs b/src/lib.rs index d0443f7..680de78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ #![warn(missing_docs)] +#[macro_use] +extern crate log; extern crate byteorder; extern crate parking_lot;