another tests added

This commit is contained in:
Svyatoslav Nikolsky
2017-06-13 12:01:59 +03:00
parent c8614bf6fe
commit 0ee2826943
21 changed files with 537 additions and 300 deletions

View File

@ -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()));
}

View File

@ -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);

View File

@ -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<ModuleInstance>) {
fn load_module(base_dir: &str, path: &str, name: &Option<String>, program: &ProgramInstance) -> Arc<ModuleInstance> {
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<elements::Module, elements::Error> {
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<parity_wasm::RuntimeV
test_vals.iter().map(runtime_value).collect::<Vec<parity_wasm::RuntimeValue>>()
}
fn run_action(module: &ModuleInstance, action: &test::Action)
fn run_action(program: &ProgramInstance, action: &test::Action)
-> Result<Option<parity_wasm::RuntimeValue>, 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::<Vec<parity_wasm::RuntimeValue>>() {
@ -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,8 +263,14 @@ 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)

View File

@ -11,14 +11,27 @@ pub struct RuntimeValue {
#[serde(tag = "type")]
pub enum Action {
#[serde(rename = "invoke")]
Invoke { field: String, args: Vec<RuntimeValue> }
Invoke {
module: Option<String>,
field: String,
args: Vec<RuntimeValue>,
},
#[serde(rename = "get")]
Get {
module: Option<String>,
field: String,
}
}
#[derive(Deserialize, Debug)]
#[serde(tag = "type")]
pub enum Command {
#[serde(rename = "module")]
Module { line: u64, filename: String },
Module {
line: u64,
name: Option<String>,
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<String>,
#[serde(rename = "as")]
as_name: String,
},
#[serde(rename = "action")]
Action {
line: u64,

View File

@ -311,6 +311,12 @@ impl<F> ModuleBuilder<F> where F: Invoke<elements::Module> {
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<Self> {
export::ExportBuilder::with_callback(self)

View File

@ -77,6 +77,8 @@ pub enum Error {
UnknownInternalKind(u8),
/// Unknown opcode encountered
UnknownOpcode(u8),
/// Invalid VarUint1 value
InvalidVarUint1(u8),
}
impl From<io::Error> for Error {

View File

@ -428,7 +428,11 @@ impl Deserialize for VarUint1 {
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)),
}
}
}

View File

@ -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<ValueType>,

View File

@ -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<Self, Error> {
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<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error> {
self.instance.instantiate(is_user_module, externals)
}
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, 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<Internal, Error> {
self.instance.export_entry(name)
fn export_entry<'a>(&self, name: &str, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, required_type: &ExportEntryType) -> Result<Internal, Error> {
self.instance.export_entry(name, externals, required_type)
}
fn function_type<'a>(&self, function_index: ItemIndex, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<FunctionType, Error> {
self.instance.function_type(function_index, externals)
}
fn table(&self, index: ItemIndex) -> Result<Arc<TableInstance>, Error> {
@ -111,12 +120,12 @@ impl ModuleInstanceInterface for EnvModuleInstance {
self.instance.memory(index)
}
fn global(&self, index: ItemIndex) -> Result<Arc<VariableInstance>, Error> {
self.instance.global(index)
fn global(&self, index: ItemIndex, variable_type: Option<VariableType>) -> Result<Arc<VariableInstance>, Error> {
self.instance.global(index, variable_type)
}
fn call_function(&self, outer: CallerContext, index: ItemIndex) -> Result<Option<RuntimeValue>, Error> {
self.instance.call_function(outer, index)
fn call_function(&self, outer: CallerContext, index: ItemIndex, function_type: Option<&FunctionType>) -> Result<Option<RuntimeValue>, 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<Option<RuntimeValue>, 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::<i32>()
.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())),

View File

@ -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<String, Arc<ModuleInstanceInterface + 'b>>>) -> Result<(), Error> {
self.env.instantiate(is_user_module, externals)
}
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, 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<Internal, Error> {
fn export_entry<'b>(&self, name: &str, externals: Option<&'b HashMap<String, Arc<ModuleInstanceInterface + 'b>>>, required_type: &ExportEntryType) -> Result<Internal, Error> {
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<String, Arc<ModuleInstanceInterface + 'b>>>) -> Result<FunctionType, Error> {
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<Arc<TableInstance>, Error> {
@ -89,12 +109,12 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> {
self.env.memory(index)
}
fn global(&self, index: ItemIndex) -> Result<Arc<VariableInstance>, Error> {
self.env.global(index)
fn global(&self, index: ItemIndex, variable_type: Option<VariableType>) -> Result<Arc<VariableInstance>, Error> {
self.env.global(index, variable_type)
}
fn call_function(&self, outer: CallerContext, index: ItemIndex) -> Result<Option<RuntimeValue>, Error> {
self.env.call_function(outer, index)
fn call_function(&self, outer: CallerContext, index: ItemIndex, function_type: Option<&FunctionType>) -> Result<Option<RuntimeValue>, 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<Option<RuntimeValue>, Error> {

View File

@ -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<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<u32, Error> {
let (_, export) = self.external_export(externals, import)?;
pub fn function<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry, required_type: Option<&FunctionType>) -> Result<u32, Error> {
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<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<Arc<TableInstance>, 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<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<Arc<MemoryInstance>, 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<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<Arc<VariableInstance>, Error> {
let (module, export) = self.external_export(externals, import)?;
pub fn global<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry, required_type: Option<VariableType>) -> Result<Arc<VariableInstance>, 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<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry) -> Result<(Arc<ModuleInstanceInterface + 'a>, Internal), Error> {
fn external_export<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, import: &ImportEntry, required_type: &ExportEntryType) -> Result<(Arc<ModuleInstanceInterface + 'a>, 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)))
}
}

View File

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

View File

@ -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<RuntimeValue>,
@ -27,22 +27,37 @@ pub struct ExecutionParams<'a> {
pub externals: HashMap<String, Arc<ModuleInstanceInterface + 'a>>,
}
/// 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 call). Module is not completely validated until this call.
fn instantiate<'a>(&self, is_user_module: bool, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error>;
/// Execute function with the given index.
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error>;
/// Execute function with the given export name.
fn execute_export(&self, name: &str, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error>;
/// Get export entry.
fn export_entry(&self, name: &str) -> Result<Internal, Error>;
fn export_entry<'a>(&self, name: &str, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, required_type: &ExportEntryType) -> Result<Internal, Error>;
/// Get function type.
fn function_type<'a>(&self, function_index: ItemIndex, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<FunctionType, Error>;
/// Get table reference.
fn table(&self, index: ItemIndex) -> Result<Arc<TableInstance>, Error>;
/// Get memory reference.
fn memory(&self, index: ItemIndex) -> Result<Arc<MemoryInstance>, Error>;
/// Get global reference.
fn global(&self, index: ItemIndex) -> Result<Arc<VariableInstance>, Error>;
fn global(&self, index: ItemIndex, variable_type: Option<VariableType>) -> Result<Arc<VariableInstance>, Error>;
/// Call function with given index in functions index space.
fn call_function(&self, outer: CallerContext, index: ItemIndex) -> Result<Option<RuntimeValue>, Error>;
fn call_function(&self, outer: CallerContext, index: ItemIndex, function_type: Option<&FunctionType>) -> Result<Option<RuntimeValue>, 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<Option<RuntimeValue>, 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<Vec<RuntimeValue>> for ExecutionParams<'a> {
impl ModuleInstance {
/// Instantiate given module within program context.
pub fn new(program: Weak<ProgramInstanceEssence>, module: Module) -> Result<Self, Error> {
Self::new_with_validation_flag(program, module, true)
}
/// Instantiate given module within program context.
pub fn new_with_validation_flag(program: Weak<ProgramInstanceEssence>, module: Module, is_user_module: bool) -> Result<Self, Error> {
pub fn new<'a>(program: Weak<ProgramInstanceEssence>, name: String, module: Module) -> Result<Self, Error> {
// 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<u32, Error> {
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<String, Arc<ModuleInstanceInterface + 'a>>>) -> 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(|_| ())?,
&External::Global(ref global_type) => if global_type.is_mutable() {
return Err(Error::Validation(format!("trying to import mutable global {}", import.field())))
// 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())));
} 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<u32, Error> {
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<Option<RuntimeValue>, Error> {
let args_len = params.args.len();
let mut args = StackWithLimit::with_data(params.args, args_len);
let caller_context = CallerContext::topmost(&mut args, &params.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<Option<RuntimeValue>, Error> {
@ -345,15 +376,42 @@ impl ModuleInstanceInterface for ModuleInstance {
self.execute_index(index, params)
}
fn export_entry(&self, name: &str) -> Result<Internal, Error> {
fn export_entry<'a>(&self, name: &str, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, required_type: &ExportEntryType) -> Result<Internal, Error> {
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<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<FunctionType, Error> {
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<Arc<TableInstance>, 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<Arc<VariableInstance>, Error> {
fn global(&self, index: ItemIndex, variable_type: Option<VariableType>) -> Result<Arc<VariableInstance>, 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<Option<RuntimeValue>, Error> {
fn call_function(&self, outer: CallerContext, index: ItemIndex, function_type: Option<&FunctionType>) -> Result<Option<RuntimeValue>, 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))),
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))),
};
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 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<Option<RuntimeValue>, 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::<Result<Vec<_>, _>>()
}
fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports) -> Result<RuntimeValue, Error> {
fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports, expected_type: VariableType) -> Result<RuntimeValue, Error> {
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)),

View File

@ -32,16 +32,25 @@ impl ProgramInstance {
}
/// Instantiate module with validation.
pub fn add_module(&self, name: &str, module: Module) -> Result<Arc<ModuleInstance>, 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<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<ModuleInstance>, 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<Arc<ModuleInstance>, 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<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<ModuleInstance>, 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<ModuleInstance>) -> Result<Arc<ModuleInstance>, Error> {
// replace existing module with the same name with new one
self.essence.modules.write().insert(name.into(), module_instance.clone());
Ok(module_instance)

View File

@ -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<Option<RuntimeValue>, 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<InstructionOutcome, Error> {
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<InstructionOutcome, Error> {
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<InstructionOutcome, Error> {
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)
}
@ -901,7 +903,7 @@ impl<'a> FunctionContext<'a> {
}
pub fn call_function(&mut self, index: u32) -> Result<Option<RuntimeValue>, 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<Option<RuntimeValue>, 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(),
@ -954,8 +957,8 @@ impl<'a> FunctionContext<'a> {
}
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(),

View File

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

View File

@ -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,6 +160,27 @@ fn single_program_different_modules() {
// => linear memory is created
let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap();
// create native env module executor
let mut executor = FunctionExecutor {
memory: env_memory.clone(),
values: Vec::new(),
};
{
let functions: UserFunctions = UserFunctions {
executor: &mut executor,
functions: vec![UserFunction {
name: "add".into(),
params: vec![ValueType::I32],
result: Some(ValueType::I32),
}, UserFunction {
name: "sub".into(),
params: vec![ValueType::I32],
result: Some(ValueType::I32),
}],
};
let native_env_instance = Arc::new(env_native_module(env_instance, functions).unwrap());
let params = ExecutionParams::with_external("env".into(), native_env_instance);
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)))
@ -184,35 +203,14 @@ fn single_program_different_modules() {
.build();
// load module
let module_instance = program.add_module("main", module).unwrap();
let module_instance = program.add_module("main", module, Some(&params.externals)).unwrap();
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 {
name: "add".into(),
params: vec![ValueType::I32],
result: Some(ValueType::I32),
}, UserFunction {
name: "sub".into(),
params: vec![ValueType::I32],
result: Some(ValueType::I32),
}],
};
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));
}
}
assert_eq!(executor.memory.get(0, 1).unwrap()[0], 42);
assert_eq!(executor.values, vec![7, 57, 42]);

View File

@ -13,7 +13,7 @@ use interpreter::variable::{VariableInstance, VariableType};
fn run_function_i32(body: &Opcodes, arg: i32) -> Result<i32, Error> {
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));

View File

@ -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<InstructionOutcome, Error> {
pub fn validate_block(context: &mut FunctionValidationContext, is_loop: bool, block_type: BlockType, body: &[Opcode], end_instr: Opcode) -> Result<InstructionOutcome, Error> {
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<InstructionOutcome, Error> {
// println!("=== VALIDATING {:?}: {:?}", opcode, context.value_stack);
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<InstructionOutcome, Error> {
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<InstructionOutcome, Error> {
@ -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<InstructionOutcome, Error> {
if let BlockType::Value(value_type) = context.require_label(idx)? {
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<InstructionOutcome, Error> {
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<u32>, default: u32) -> Result<InstructionOutcome, Error> {
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<InstructionOutcome, Error> {
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()
})
@ -565,8 +582,8 @@ impl<'a> FunctionValidationContext<'a> {
Ok(InstructionOutcome::RunNextInstruction)
}
pub fn require_label(&self, idx: u32) -> Result<BlockType, Error> {
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<BlockType, Error> {

View File

@ -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<u32> {
match *self {
RuntimeValue::AnyFunc(idx) => Some(idx),
_ => None,
}
}
/// Get variable type for this value.
pub fn variable_type(&self) -> Option<VariableType> {
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)
}
}
}

View File

@ -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()