mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-22 03:02:08 +00:00
another tests added
This commit is contained in:
@ -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()));
|
||||
}
|
||||
|
@ -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);
|
||||
|
101
spec/src/run.rs
101
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<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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>,
|
||||
|
@ -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())),
|
||||
|
@ -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> {
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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, ¶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<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)),
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
@ -856,7 +858,7 @@ impl Interpreter {
|
||||
loop {
|
||||
let instruction = &body[context.position];
|
||||
|
||||
//println!("=== RUNNING {:?}", instruction); // TODO: trace
|
||||
// println!("=== RUNNING {:?}", instruction); // TODO: trace
|
||||
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<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(),
|
||||
|
@ -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;
|
||||
|
@ -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(¶ms.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]);
|
||||
|
@ -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));
|
||||
|
@ -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()
|
||||
})
|
||||
@ -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<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> {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
Reference in New Issue
Block a user