mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-05-28 23:21:35 +00:00
Merge pull request #53 from NikVolf/tests_finish
Latest (almost :) ) bunch of tests
This commit is contained in:
commit
ca6e41b66b
@ -20,13 +20,15 @@ run_test!("block-end-label-mismatch.fail", wasm_block_end_label_mismatch_fail, f
|
||||
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_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!("call_indirect", wasm_call_indirect);
|
||||
run_test!("call", wasm_call);
|
||||
run_test!("comments", wasm_comments);
|
||||
// TODO: run_test!("conversions", wasm_conversions);
|
||||
// TODO: commented out until sNaN issue is resolved:
|
||||
// https://github.com/NikVolf/parity-wasm/blob/b5aaf103cf28f1e36df832f4883f55043e67894b/src/interpreter/value.rs#L510
|
||||
// run_test!("conversions", wasm_conversions);
|
||||
// TODO: run_test!("custom_section", wasm_custom_section);
|
||||
run_test!("endianness", wasm_endianness);
|
||||
run_test!("f32_exports", wasm_exports);
|
||||
|
@ -104,7 +104,7 @@ fn run_action(program: &ProgramInstance, action: &test::Action)
|
||||
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)
|
||||
module.export_entry(field.as_ref(), &ExportEntryType::Any)
|
||||
.and_then(|i| match i {
|
||||
elements::Internal::Global(global_index) => Ok(ItemIndex::IndexSpace(global_index)),
|
||||
_ => Err(InterpreterError::Global(format!("Expected to have exported global with name {}", field))),
|
||||
|
@ -7,7 +7,7 @@ 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, ExportEntryType};
|
||||
ItemIndex, CallerContext, ExportEntryType, InternalFunctionReference, InternalFunction};
|
||||
use interpreter::memory::{MemoryInstance, LINEAR_MEMORY_PAGE_SIZE};
|
||||
use interpreter::table::TableInstance;
|
||||
use interpreter::value::RuntimeValue;
|
||||
@ -104,12 +104,8 @@ impl ModuleInstanceInterface for EnvModuleInstance {
|
||||
self.instance.execute_export(name, params)
|
||||
}
|
||||
|
||||
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 export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result<Internal, Error> {
|
||||
self.instance.export_entry(name, required_type)
|
||||
}
|
||||
|
||||
fn table(&self, index: ItemIndex) -> Result<Arc<TableInstance>, Error> {
|
||||
@ -124,16 +120,27 @@ impl ModuleInstanceInterface for EnvModuleInstance {
|
||||
self.instance.global(index, variable_type)
|
||||
}
|
||||
|
||||
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 function_type(&self, function_index: ItemIndex) -> Result<FunctionType, Error> {
|
||||
self.instance.function_type(function_index)
|
||||
}
|
||||
|
||||
fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, type_index: u32, func_index: u32) -> Result<Option<RuntimeValue>, Error> {
|
||||
self.instance.call_function_indirect(outer, table_index, type_index, func_index)
|
||||
fn function_type_by_index(&self, type_index: u32) -> Result<FunctionType, Error> {
|
||||
self.instance.function_type_by_index(type_index)
|
||||
}
|
||||
|
||||
fn call_internal_function(&self, outer: CallerContext, index: u32, _function_type: Option<&FunctionType>) -> Result<Option<RuntimeValue>, Error> {
|
||||
// TODO: check function type
|
||||
fn function_reference<'a>(&self, index: ItemIndex, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<InternalFunctionReference<'a>, Error> {
|
||||
self.instance.function_reference(index, externals)
|
||||
}
|
||||
|
||||
fn function_reference_indirect<'a>(&self, table_idx: u32, type_idx: u32, func_idx: u32, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<InternalFunctionReference<'a>, Error> {
|
||||
self.instance.function_reference_indirect(table_idx, type_idx, func_idx, externals)
|
||||
}
|
||||
|
||||
fn function_body<'a>(&'a self, _internal_index: u32) -> Result<Option<InternalFunction<'a>>, Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result<Option<RuntimeValue>, Error> {
|
||||
// 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), Some(VariableType::I32))
|
||||
|
@ -5,7 +5,7 @@ use parking_lot::RwLock;
|
||||
use elements::{FunctionType, Internal, ValueType};
|
||||
use interpreter::Error;
|
||||
use interpreter::module::{ModuleInstanceInterface, ExecutionParams, ItemIndex,
|
||||
CallerContext, ExportEntryType};
|
||||
CallerContext, ExportEntryType, InternalFunctionReference, InternalFunction};
|
||||
use interpreter::memory::MemoryInstance;
|
||||
use interpreter::table::TableInstance;
|
||||
use interpreter::value::RuntimeValue;
|
||||
@ -14,9 +14,6 @@ use interpreter::variable::{VariableInstance, VariableType};
|
||||
/// Min index of native function.
|
||||
pub const NATIVE_INDEX_FUNC_MIN: u32 = 10001;
|
||||
|
||||
/// User function closure type.
|
||||
// pub type UserFunctionClosure<'a> = &'a mut FnMut(context: CallerContext) -> Result<Option<RuntimeValue>, Error>;
|
||||
|
||||
/// User functions executor.
|
||||
pub trait UserFunctionExecutor {
|
||||
/// Execute function with given name.
|
||||
@ -125,28 +122,13 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> {
|
||||
self.env.execute_export(name, params)
|
||||
}
|
||||
|
||||
fn export_entry<'b>(&self, name: &str, externals: Option<&'b HashMap<String, Arc<ModuleInstanceInterface + 'b>>>, required_type: &ExportEntryType) -> Result<Internal, Error> {
|
||||
fn export_entry<'b>(&self, name: &str, required_type: &ExportEntryType) -> Result<Internal, Error> {
|
||||
if let Some(index) = self.by_name.get(name) {
|
||||
// TODO: check type
|
||||
return Ok(Internal::Function(NATIVE_INDEX_FUNC_MIN + *index));
|
||||
}
|
||||
|
||||
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().to_vec(), f.result().clone()))
|
||||
self.env.export_entry(name, required_type)
|
||||
}
|
||||
|
||||
fn table(&self, index: ItemIndex) -> Result<Arc<TableInstance>, Error> {
|
||||
@ -161,20 +143,43 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> {
|
||||
self.env.global(index, variable_type)
|
||||
}
|
||||
|
||||
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 function_type(&self, function_index: ItemIndex) -> 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"),
|
||||
};
|
||||
|
||||
fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, type_index: u32, func_index: u32) -> Result<Option<RuntimeValue>, Error> {
|
||||
self.env.call_function_indirect(outer, table_index, type_index, func_index)
|
||||
}
|
||||
|
||||
fn call_internal_function(&self, outer: CallerContext, index: u32, function_type: Option<&FunctionType>) -> Result<Option<RuntimeValue>, Error> {
|
||||
if index < NATIVE_INDEX_FUNC_MIN {
|
||||
return self.env.call_internal_function(outer, index, function_type);
|
||||
return self.env.function_type(function_index);
|
||||
}
|
||||
|
||||
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().to_vec(), f.result().clone()))
|
||||
}
|
||||
|
||||
fn function_type_by_index(&self, type_index: u32) -> Result<FunctionType, Error> {
|
||||
self.function_type(ItemIndex::Internal(type_index))
|
||||
}
|
||||
|
||||
fn function_reference<'b>(&self, index: ItemIndex, externals: Option<&'b HashMap<String, Arc<ModuleInstanceInterface + 'b>>>) -> Result<InternalFunctionReference<'b>, Error> {
|
||||
self.env.function_reference(index, externals)
|
||||
}
|
||||
|
||||
fn function_reference_indirect<'b>(&self, table_idx: u32, type_idx: u32, func_idx: u32, externals: Option<&'b HashMap<String, Arc<ModuleInstanceInterface + 'b>>>) -> Result<InternalFunctionReference<'b>, Error> {
|
||||
self.env.function_reference_indirect(table_idx, type_idx, func_idx, externals)
|
||||
}
|
||||
|
||||
fn function_body<'b>(&'b self, _internal_index: u32) -> Result<Option<InternalFunction<'b>>, Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result<Option<RuntimeValue>, Error> {
|
||||
if index < NATIVE_INDEX_FUNC_MIN {
|
||||
return self.env.call_internal_function(outer, index);
|
||||
}
|
||||
|
||||
// TODO: check type
|
||||
self.functions
|
||||
.get((index - NATIVE_INDEX_FUNC_MIN) as usize)
|
||||
.ok_or(Error::Native(format!("trying to call native function with index {}", index)))
|
||||
@ -183,6 +188,6 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> {
|
||||
}
|
||||
|
||||
/// Create wrapper for env module with given native user functions.
|
||||
pub fn env_native_module(env: Arc<ModuleInstanceInterface>, user_functions: UserFunctions) -> Result<NativeModuleInstance, Error> {
|
||||
pub fn env_native_module<'a>(env: Arc<ModuleInstanceInterface>, user_functions: UserFunctions<'a>) -> Result<NativeModuleInstance, Error> {
|
||||
NativeModuleInstance::new(env, user_functions)
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ impl ModuleImports {
|
||||
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(), externals, required_type)
|
||||
m.export_entry(import.field(), required_type)
|
||||
.map(|e| (m, e)))
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
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};
|
||||
use std::fmt;
|
||||
use elements::{Module, InitExpr, Opcode, Type, FunctionType, Internal, External, BlockType, ResizableLimits, Local};
|
||||
use interpreter::Error;
|
||||
use interpreter::imports::ModuleImports;
|
||||
use interpreter::memory::MemoryInstance;
|
||||
use interpreter::program::ProgramInstanceEssence;
|
||||
use interpreter::runner::{Interpreter, FunctionContext};
|
||||
use interpreter::runner::{Interpreter, FunctionContext, prepare_function_args};
|
||||
use interpreter::stack::StackWithLimit;
|
||||
use interpreter::table::TableInstance;
|
||||
use interpreter::validator::{Validator, FunctionValidationContext};
|
||||
@ -16,7 +17,7 @@ use interpreter::variable::{VariableInstance, VariableType};
|
||||
/// Maximum number of entries in value stack.
|
||||
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
|
||||
const DEFAULT_FRAME_STACK_LIMIT: usize = 1024;
|
||||
|
||||
/// Execution context.
|
||||
#[derive(Default, Clone)]
|
||||
@ -47,21 +48,25 @@ pub trait ModuleInstanceInterface {
|
||||
/// 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<'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>;
|
||||
fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result<Internal, 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, 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, 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.
|
||||
fn call_internal_function(&self, outer: CallerContext, index: u32, function_type: Option<&FunctionType>) -> Result<Option<RuntimeValue>, Error>;
|
||||
/// Get function type for given function index.
|
||||
fn function_type(&self, function_index: ItemIndex) -> Result<FunctionType, Error>;
|
||||
/// Get function type for given function index.
|
||||
fn function_type_by_index(&self, type_index: u32) -> Result<FunctionType, Error>;
|
||||
/// Get function reference.
|
||||
fn function_reference<'a>(&self, index: ItemIndex, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<InternalFunctionReference<'a>, Error>;
|
||||
/// Get function indirect reference.
|
||||
fn function_reference_indirect<'a>(&self, table_idx: u32, type_idx: u32, func_idx: u32, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<InternalFunctionReference<'a>, Error>;
|
||||
/// Get internal function for interpretation.
|
||||
fn function_body<'a>(&'a self, internal_index: u32) -> Result<Option<InternalFunction<'a>>, Error>;
|
||||
/// Call function with given internal index.
|
||||
fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result<Option<RuntimeValue>, Error>;
|
||||
}
|
||||
|
||||
/// Item index in items index space.
|
||||
@ -103,6 +108,29 @@ pub struct CallerContext<'a> {
|
||||
pub externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>,
|
||||
}
|
||||
|
||||
/// Internal function reference.
|
||||
#[derive(Clone)]
|
||||
pub struct InternalFunctionReference<'a> {
|
||||
/// Module reference.
|
||||
pub module: Arc<ModuleInstanceInterface + 'a>,
|
||||
/// Internal function index.
|
||||
pub internal_index: u32,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for InternalFunctionReference<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "InternalFunctionReference")
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function ready for interpretation.
|
||||
pub struct InternalFunction<'a> {
|
||||
/// Function locals.
|
||||
pub locals: &'a [Local],
|
||||
/// Function body.
|
||||
pub body: &'a [Opcode],
|
||||
}
|
||||
|
||||
impl<'a> ExecutionParams<'a> {
|
||||
/// Create new execution params with given externa; module override.
|
||||
pub fn with_external(name: String, module: Arc<ModuleInstanceInterface + 'a>) -> Self {
|
||||
@ -177,6 +205,10 @@ impl ModuleInstance {
|
||||
})
|
||||
}
|
||||
|
||||
fn self_ref<'a>(&self, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<Arc<ModuleInstanceInterface + 'a>, Error> {
|
||||
self.imports.module(externals, &self.name)
|
||||
}
|
||||
|
||||
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"),
|
||||
@ -195,15 +227,6 @@ impl ModuleInstance {
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -212,7 +235,7 @@ impl ModuleInstanceInterface for ModuleInstance {
|
||||
if let Some(start_function) = self.module.start_section() {
|
||||
let func_type_index = self.require_function(ItemIndex::IndexSpace(start_function))?;
|
||||
if is_user_module { // tests use non-empty main functions
|
||||
let func_type = self.require_function_type(func_type_index)?;
|
||||
let func_type = self.function_type_by_index(func_type_index)?;
|
||||
if func_type.return_type() != None || func_type.params().len() != 0 {
|
||||
return Err(Error::Validation("start function expected to have type [] -> []".into()));
|
||||
}
|
||||
@ -249,20 +272,20 @@ impl ModuleInstanceInterface for ModuleInstance {
|
||||
// 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)?;
|
||||
let import_function_type = self.function_type_by_index(*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()))?;
|
||||
let export_entry = external_module.export_entry(import.field(), &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)?,
|
||||
Internal::Function(function_index) => external_module.function_type(ItemIndex::IndexSpace(function_index))?,
|
||||
_ => 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 {
|
||||
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())));
|
||||
@ -308,7 +331,7 @@ impl ModuleInstanceInterface for ModuleInstance {
|
||||
let code_section = self.module.code_section().expect("function_section_len != 0; function_section_len == code_section_len; qed");
|
||||
// check every function body
|
||||
for (index, function) in function_section.entries().iter().enumerate() {
|
||||
let function_type = self.require_function_type(function.type_ref())?;
|
||||
let function_type = self.function_type_by_index(function.type_ref())?;
|
||||
let function_body = code_section.bodies().get(index as usize).ok_or(Error::Validation(format!("Missing body for function {}", index)))?;
|
||||
let mut locals = function_type.params().to_vec();
|
||||
locals.extend(function_body.locals().iter().flat_map(|l| repeat(l.value_type()).take(l.count() as usize)));
|
||||
@ -321,7 +344,7 @@ impl ModuleInstanceInterface for ModuleInstance {
|
||||
&function_type);
|
||||
|
||||
let block_type = function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult);
|
||||
Validator::validate_block(&mut context, false, block_type, function_body.code().elements(), Opcode::End)
|
||||
Validator::validate_function(&mut context, block_type, function_body.code().elements())
|
||||
.map_err(|e| {
|
||||
if let Error::Validation(msg) = e {
|
||||
Error::Validation(format!("Function #{} validation error: {}", index, msg))
|
||||
@ -367,10 +390,11 @@ 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), None)
|
||||
let ExecutionParams { args, externals } = params;
|
||||
let mut args = StackWithLimit::with_data(args, DEFAULT_VALUE_STACK_LIMIT);
|
||||
let function_reference = self.function_reference(ItemIndex::IndexSpace(index), Some(&externals))?;
|
||||
let function_context = CallerContext::topmost(&mut args, &externals);
|
||||
function_reference.module.call_internal_function(function_context, function_reference.internal_index)
|
||||
}
|
||||
|
||||
fn execute_export(&self, name: &str, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
|
||||
@ -390,7 +414,7 @@ impl ModuleInstanceInterface for ModuleInstance {
|
||||
self.execute_index(index, params)
|
||||
}
|
||||
|
||||
fn export_entry<'a>(&self, name: &str, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>, required_type: &ExportEntryType) -> Result<Internal, Error> {
|
||||
fn export_entry<'a>(&self, name: &str, 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()
|
||||
@ -402,7 +426,7 @@ impl ModuleInstanceInterface for ModuleInstance {
|
||||
},
|
||||
&ExportEntryType::Function(ref required_type) => match e.internal() {
|
||||
&Internal::Function(function_index) =>
|
||||
self.function_type(ItemIndex::IndexSpace(function_index), externals)
|
||||
self.function_type(ItemIndex::IndexSpace(function_index))
|
||||
.map(|ft| &ft == required_type)
|
||||
.unwrap_or(false),
|
||||
_ => false,
|
||||
@ -412,20 +436,6 @@ impl ModuleInstanceInterface for ModuleInstance {
|
||||
.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"),
|
||||
@ -465,83 +475,93 @@ impl ModuleInstanceInterface for ModuleInstance {
|
||||
}
|
||||
}
|
||||
|
||||
fn call_function(&self, outer: CallerContext, index: ItemIndex, function_type: Option<&FunctionType>) -> Result<Option<RuntimeValue>, Error> {
|
||||
match self.imports.parse_function_index(index) {
|
||||
fn function_type(&self, function_index: ItemIndex) -> Result<FunctionType, Error> {
|
||||
match self.imports.parse_function_index(function_index) {
|
||||
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"),
|
||||
ItemIndex::Internal(index) => self.call_internal_function(outer, index, function_type),
|
||||
ItemIndex::Internal(index) => self.require_function(ItemIndex::Internal(index))
|
||||
.and_then(|ft| self.function_type_by_index(ft)),
|
||||
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, function_type)?)))
|
||||
.and_then(|(m, index)| m.call_internal_function(outer, index, function_type)),
|
||||
.and_then(|e| match e.external() {
|
||||
&External::Function(type_index) => self.function_type_by_index(type_index),
|
||||
_ => Err(Error::Function(format!("exported function {} is not a function", index))),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, type_index: u32, func_index: u32) -> Result<Option<RuntimeValue>, Error> {
|
||||
let function_type = match self.module.type_section()
|
||||
.ok_or(Error::Function(format!("trying to indirect call function {} with non-existent function section", func_index)))
|
||||
.and_then(|s| s.types().get(type_index as usize)
|
||||
.ok_or(Error::Function(format!("trying to indirect call function {} with non-existent type index {}", func_index, type_index))))? {
|
||||
&Type::Function(ref function_type) => function_type,
|
||||
};
|
||||
fn function_type_by_index(&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))),
|
||||
})
|
||||
.map(Clone::clone)
|
||||
}
|
||||
|
||||
let table = self.table(table_index)?;
|
||||
let (module, index) = match table.get(func_index)? {
|
||||
fn function_reference<'a>(&self, index: ItemIndex, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<InternalFunctionReference<'a>, Error> {
|
||||
match self.imports.parse_function_index(index) {
|
||||
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"),
|
||||
ItemIndex::Internal(index) => Ok(InternalFunctionReference {
|
||||
module: self.self_ref(externals)?,
|
||||
internal_index: index,
|
||||
}),
|
||||
ItemIndex::External(index) => {
|
||||
let import_entry = self.module.import_section()
|
||||
.expect("parse_function_index has returned External(index); it is only returned when import section exists; qed")
|
||||
.entries().get(index as usize)
|
||||
.expect("parse_function_index has returned External(index); it is only returned when entry with index exists in import section exists; qed");
|
||||
let required_function_type = self.function_type(ItemIndex::External(index))?;
|
||||
let internal_function_index = self.imports.function(externals, import_entry, Some(&required_function_type))?;
|
||||
Ok(InternalFunctionReference {
|
||||
module: self.imports.module(externals, import_entry.module())?,
|
||||
internal_index: internal_function_index,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn function_reference_indirect<'a>(&self, table_idx: u32, type_idx: u32, func_idx: u32, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<InternalFunctionReference<'a>, Error> {
|
||||
let table = self.table(ItemIndex::IndexSpace(table_idx))?;
|
||||
let (module, index) = match table.get(func_idx)? {
|
||||
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))),
|
||||
_ => return Err(Error::Function(format!("trying to indirect call function {} via non-anyfunc table {:?}", func_idx, table_idx))),
|
||||
};
|
||||
|
||||
let module = self.imports.module(Some(outer.externals), &module)?;
|
||||
module.call_function(outer, ItemIndex::IndexSpace(index), Some(function_type))
|
||||
let module = self.imports.module(externals, &module)?;
|
||||
let required_function_type = self.function_type_by_index(type_idx)?;
|
||||
let actual_function_type = module.function_type(ItemIndex::IndexSpace(index))?;
|
||||
if required_function_type != actual_function_type {
|
||||
return Err(Error::Function(format!("expected indirect function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}",
|
||||
required_function_type.params(), required_function_type.return_type(),
|
||||
actual_function_type.params(), actual_function_type.return_type())));
|
||||
}
|
||||
|
||||
module.function_reference(ItemIndex::IndexSpace(index), externals)
|
||||
}
|
||||
|
||||
fn call_internal_function(&self, mut outer: CallerContext, index: u32, function_type: Option<&FunctionType>) -> Result<Option<RuntimeValue>, Error> {
|
||||
// TODO: cache
|
||||
// internal index = index of function in functions section && index of code in code section
|
||||
// get function type index
|
||||
let function_type_index = self.module
|
||||
.function_section()
|
||||
.ok_or(Error::Function(format!("trying to call function with index {} in module without function section", index)))
|
||||
.and_then(|s| s.entries()
|
||||
.get(index as usize)
|
||||
.ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions", index, s.entries().len()))))?
|
||||
.type_ref();
|
||||
// function type index = index of function type in types index
|
||||
// get function type
|
||||
let item_type = self.module
|
||||
.type_section()
|
||||
.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", function_type_index, s.types().len()))))?;
|
||||
let actual_function_type = match item_type {
|
||||
&Type::Function(ref function_type) => function_type,
|
||||
};
|
||||
if let Some(ref function_type) = function_type {
|
||||
if function_type != &actual_function_type {
|
||||
return Err(Error::Function(format!("expected function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}",
|
||||
function_type.params(), function_type.return_type(), actual_function_type.params(), actual_function_type.return_type())));
|
||||
}
|
||||
}
|
||||
// get function body
|
||||
fn function_body<'a>(&'a self, internal_index: u32) -> Result<Option<InternalFunction<'a>>, Error> {
|
||||
let function_body = self.module
|
||||
.code_section()
|
||||
.ok_or(Error::Function(format!("trying to call function with index {} in module without code section", index)))
|
||||
.ok_or(Error::Function(format!("trying to call function with index {} in module without code section", internal_index)))
|
||||
.and_then(|s| s.bodies()
|
||||
.get(index as usize)
|
||||
.ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions codes", index, s.bodies().len()))))?;
|
||||
.get(internal_index as usize)
|
||||
.ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions codes", internal_index, s.bodies().len()))))?;
|
||||
|
||||
// each functions has its own value stack
|
||||
// but there's global stack limit
|
||||
// args, locals
|
||||
let function_code = function_body.code().elements();
|
||||
let value_stack_limit = outer.value_stack_limit;
|
||||
let frame_stack_limit = outer.frame_stack_limit;
|
||||
let locals = prepare_function_locals(actual_function_type, function_body, &mut outer)?;
|
||||
let mut innner = FunctionContext::new(self, outer.externals, value_stack_limit, frame_stack_limit, actual_function_type, locals);
|
||||
Interpreter::run_function(&mut innner, function_code)
|
||||
Ok(Some(InternalFunction {
|
||||
locals: function_body.locals(),
|
||||
body: function_body.code().elements(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn call_internal_function(&self, mut outer: CallerContext, index: u32) -> Result<Option<RuntimeValue>, Error> {
|
||||
let function_type = self.function_type(ItemIndex::Internal(index))?;
|
||||
let args = prepare_function_args(&function_type, outer.value_stack)?;
|
||||
let function_ref = InternalFunctionReference { module: self.self_ref(Some(outer.externals))?, internal_index: index };
|
||||
let inner = FunctionContext::new(function_ref, outer.externals, outer.value_stack_limit, outer.frame_stack_limit, &function_type, args);
|
||||
Interpreter::run_function(inner)
|
||||
}
|
||||
}
|
||||
|
||||
@ -577,27 +597,6 @@ pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_function_locals(function_type: &FunctionType, function_body: &FuncBody, outer: &mut CallerContext) -> Result<Vec<VariableInstance>, Error> {
|
||||
// locals = function arguments + defined locals
|
||||
function_type.params().iter().rev()
|
||||
.map(|param_type| {
|
||||
let param_value = outer.value_stack.pop()?;
|
||||
let actual_type = param_value.variable_type();
|
||||
let expected_type = (*param_type).into();
|
||||
if actual_type != Some(expected_type) {
|
||||
return Err(Error::Function(format!("invalid parameter type {:?} when expected {:?}", actual_type, expected_type)));
|
||||
}
|
||||
|
||||
VariableInstance::new(true, expected_type, param_value)
|
||||
})
|
||||
.collect::<Vec<_>>().into_iter().rev()
|
||||
.chain(function_body.locals()
|
||||
.iter()
|
||||
.flat_map(|l| repeat(l.value_type().into()).take(l.count() as usize))
|
||||
.map(|vt| VariableInstance::new(true, vt, RuntimeValue::default(vt))))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
}
|
||||
|
||||
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],
|
||||
|
@ -34,19 +34,29 @@ impl ProgramInstance {
|
||||
/// Instantiate module with validation.
|
||||
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)
|
||||
// replace existing module with the same name with new one
|
||||
match module_instance.instantiate(true, externals) {
|
||||
Ok(()) => Ok(module_instance),
|
||||
Err(err) => {
|
||||
self.essence.modules.write().remove(name.into());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Instantiate module without validation.
|
||||
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)
|
||||
// replace existing module with the same name with new one
|
||||
match module_instance.instantiate(false, externals) {
|
||||
Ok(()) => Ok(module_instance),
|
||||
Err(err) => {
|
||||
self.essence.modules.write().remove(name.into());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert instantiated module.
|
||||
|
@ -2,11 +2,12 @@ use std::mem;
|
||||
use std::ops;
|
||||
use std::u32;
|
||||
use std::sync::Arc;
|
||||
use std::fmt::Display;
|
||||
use std::collections::HashMap;
|
||||
use elements::{Opcode, BlockType, FunctionType};
|
||||
use std::fmt::{self, Display};
|
||||
use std::iter::repeat;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use elements::{Opcode, BlockType, FunctionType, Local};
|
||||
use interpreter::Error;
|
||||
use interpreter::module::{ModuleInstance, ModuleInstanceInterface, CallerContext, ItemIndex};
|
||||
use interpreter::module::{ModuleInstanceInterface, CallerContext, ItemIndex, InternalFunctionReference};
|
||||
use interpreter::stack::StackWithLimit;
|
||||
use interpreter::value::{
|
||||
RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto,
|
||||
@ -24,8 +25,10 @@ pub struct Interpreter;
|
||||
|
||||
/// Function execution context.
|
||||
pub struct FunctionContext<'a> {
|
||||
/// Module instance.
|
||||
pub module: &'a ModuleInstance,
|
||||
/// Is context initialized.
|
||||
pub is_initialized: bool,
|
||||
/// Internal function reference.
|
||||
pub function: InternalFunctionReference<'a>,
|
||||
/// Execution-local external modules.
|
||||
pub externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>,
|
||||
/// Function return type.
|
||||
@ -40,52 +43,192 @@ pub struct FunctionContext<'a> {
|
||||
pub position: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InstructionOutcome {
|
||||
/// Interpreter action to execute after executing instruction.
|
||||
#[derive(Debug)]
|
||||
pub enum InstructionOutcome<'a> {
|
||||
/// Continue with current instruction.
|
||||
RunInstruction,
|
||||
/// Continue with next instruction.
|
||||
RunNextInstruction,
|
||||
/// Branch to given frame.
|
||||
Branch(usize),
|
||||
/// Execute block.
|
||||
ExecuteBlock,
|
||||
/// Execute function call.
|
||||
ExecuteCall(InternalFunctionReference<'a>),
|
||||
/// End current frame.
|
||||
End,
|
||||
/// Return from current function block.
|
||||
Return,
|
||||
}
|
||||
|
||||
/// Control stack frame.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlockFrame {
|
||||
/// Is loop frame?
|
||||
is_loop: bool,
|
||||
// A label for reference from branch instructions.
|
||||
/// Frame type.
|
||||
frame_type: BlockFrameType,
|
||||
/// A label for reference to block instruction.
|
||||
begin_position: usize,
|
||||
/// A label for reference from branch instructions.
|
||||
branch_position: usize,
|
||||
// A label for reference from end instructions.
|
||||
/// A label for reference from end instructions.
|
||||
end_position: usize,
|
||||
// A limit integer value, which is an index into the value stack indicating where to reset it to on a branch to that label.
|
||||
/// A limit integer value, which is an index into the value stack indicating where to reset it to on a branch to that label.
|
||||
value_limit: usize,
|
||||
// A signature, which is a block signature type indicating the number and types of result values of the region.
|
||||
/// A signature, which is a block signature type indicating the number and types of result values of the region.
|
||||
signature: BlockType,
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
pub fn run_function(context: &mut FunctionContext, body: &[Opcode]) -> Result<Option<RuntimeValue>, Error> {
|
||||
let return_type = context.return_type;
|
||||
context.push_frame(false, body.len() - 1, body.len() - 1, return_type)?;
|
||||
/// Type of block frame.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum BlockFrameType {
|
||||
/// Usual block frame.
|
||||
Block,
|
||||
/// Loop frame (branching to the beginning of block).
|
||||
Loop,
|
||||
/// True-subblock of if expression.
|
||||
IfTrue,
|
||||
/// False-subblock of if expression.
|
||||
IfElse,
|
||||
}
|
||||
|
||||
Interpreter::execute_block(context, body)?;
|
||||
match context.return_type {
|
||||
BlockType::Value(_) => Ok(Some(context.value_stack_mut().pop()?)),
|
||||
BlockType::NoResult => Ok(None),
|
||||
/// Function run result.
|
||||
enum RunResult<'a> {
|
||||
/// Function has returned (optional) value.
|
||||
Return(Option<RuntimeValue>),
|
||||
/// Function is calling other function.
|
||||
NestedCall(FunctionContext<'a>),
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
pub fn run_function(function_context: FunctionContext) -> Result<Option<RuntimeValue>, Error> {
|
||||
let mut function_stack = VecDeque::new();
|
||||
function_stack.push_back(function_context);
|
||||
|
||||
loop {
|
||||
let mut function_context = function_stack.pop_back().expect("on loop entry - not empty; on loop continue - checking for emptiness; qed");
|
||||
let function_ref = function_context.function.clone();
|
||||
let function_body = function_ref.module.function_body(function_ref.internal_index)?;
|
||||
|
||||
let function_return = match function_body {
|
||||
Some(function_body) => {
|
||||
if !function_context.is_initialized() {
|
||||
let return_type = function_context.return_type;
|
||||
function_context.initialize(function_body.locals)?;
|
||||
function_context.push_frame(BlockFrameType::Block, 0, function_body.body.len() - 1, function_body.body.len() - 1, return_type)?;
|
||||
}
|
||||
|
||||
Interpreter::do_run_function(&mut function_context, function_body.body)?
|
||||
},
|
||||
None => {
|
||||
// move locals back to the stack
|
||||
let locals_to_move: Vec<_> = function_context.locals.drain(..).rev().collect();
|
||||
for local in locals_to_move {
|
||||
function_context.value_stack_mut().push(local.get())?;
|
||||
}
|
||||
|
||||
let nested_context = CallerContext::nested(&mut function_context);
|
||||
RunResult::Return(function_ref.module.call_internal_function(nested_context, function_ref.internal_index)?)
|
||||
},
|
||||
};
|
||||
|
||||
match function_return {
|
||||
RunResult::Return(return_value) => {
|
||||
match function_stack.back_mut() {
|
||||
Some(caller_context) => if let Some(return_value) = return_value {
|
||||
caller_context.value_stack_mut().push(return_value)?;
|
||||
},
|
||||
None => return Ok(return_value),
|
||||
}
|
||||
},
|
||||
RunResult::NestedCall(nested_context) => {
|
||||
function_stack.push_back(function_context);
|
||||
function_stack.push_back(nested_context);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_instruction(context: &mut FunctionContext, opcode: &Opcode) -> Result<InstructionOutcome, Error> {
|
||||
fn do_run_function<'a>(function_context: &mut FunctionContext<'a>, function_body: &[Opcode]) -> Result<RunResult<'a>, Error> {
|
||||
// TODO: remove this after 'plaining' function instructions
|
||||
let mut body_stack = VecDeque::with_capacity(function_context.frame_stack().values().len());
|
||||
body_stack.push_back(function_body);
|
||||
for frame in function_context.frame_stack().values().iter().skip(1) {
|
||||
let instruction = &body_stack.back().unwrap()[frame.begin_position];
|
||||
let instruction_body = Interpreter::into_block(instruction, frame.frame_type)?;
|
||||
body_stack.push_back(instruction_body);
|
||||
}
|
||||
|
||||
loop {
|
||||
let block_result = Interpreter::run_block_instructions(function_context, body_stack.back().expect("TODO"))?;
|
||||
|
||||
match block_result {
|
||||
InstructionOutcome::RunInstruction | InstructionOutcome::RunNextInstruction => unreachable!("managed by run_block_instructions"),
|
||||
InstructionOutcome::Branch(mut index) => {
|
||||
// discard index - 1 blocks
|
||||
while index >= 1 {
|
||||
function_context.discard_frame()?;
|
||||
assert!(body_stack.pop_back().is_some());
|
||||
index -= 1;
|
||||
}
|
||||
|
||||
function_context.pop_frame(true)?;
|
||||
assert!(body_stack.pop_back().is_some());
|
||||
if function_context.frame_stack().is_empty() {
|
||||
return Ok(RunResult::Return(match function_context.return_type {
|
||||
BlockType::Value(_) => Some(function_context.value_stack_mut().pop()?),
|
||||
BlockType::NoResult => None,
|
||||
}));
|
||||
}
|
||||
},
|
||||
InstructionOutcome::ExecuteBlock => {
|
||||
function_context.position = 0;
|
||||
let top_frame = function_context.frame_stack().top().expect("TODO");
|
||||
let instruction = &body_stack.back().expect("TODO")[top_frame.begin_position];
|
||||
let block_body = Interpreter::into_block(instruction, top_frame.frame_type)?;
|
||||
body_stack.push_back(block_body);
|
||||
},
|
||||
InstructionOutcome::ExecuteCall(func_ref) => return Ok(RunResult::NestedCall(function_context.nested(func_ref)?)),
|
||||
InstructionOutcome::End if !function_context.frame_stack().is_empty() => {
|
||||
assert!(body_stack.pop_back().is_some());
|
||||
},
|
||||
InstructionOutcome::End | InstructionOutcome::Return => return Ok(RunResult::Return(match function_context.return_type {
|
||||
BlockType::Value(_) => Some(function_context.value_stack_mut().pop()?),
|
||||
BlockType::NoResult => None,
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_block_instructions<'a, 'b>(context: &'b mut FunctionContext<'a>, body: &[Opcode]) -> Result<InstructionOutcome<'a>, Error> {
|
||||
loop {
|
||||
let instruction = &body[context.position];
|
||||
|
||||
debug!(target: "interpreter", "running {:?}", instruction);
|
||||
match Interpreter::run_instruction(context, instruction)? {
|
||||
InstructionOutcome::RunInstruction => (),
|
||||
InstructionOutcome::RunNextInstruction => context.position += 1,
|
||||
InstructionOutcome::Branch(index) => return Ok(InstructionOutcome::Branch(index)),
|
||||
InstructionOutcome::ExecuteBlock => return Ok(InstructionOutcome::ExecuteBlock),
|
||||
InstructionOutcome::ExecuteCall(func_ref) => {
|
||||
context.position += 1;
|
||||
return Ok(InstructionOutcome::ExecuteCall(func_ref));
|
||||
},
|
||||
InstructionOutcome::End => {
|
||||
context.pop_frame(false)?;
|
||||
return Ok(InstructionOutcome::End);
|
||||
},
|
||||
InstructionOutcome::Return => return Ok(InstructionOutcome::Return),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_instruction<'a, 'b>(context: &'b mut FunctionContext<'a>, opcode: &Opcode) -> Result<InstructionOutcome<'a>, Error> {
|
||||
match opcode {
|
||||
&Opcode::Unreachable => Interpreter::run_unreachable(context),
|
||||
&Opcode::Nop => Interpreter::run_nop(context),
|
||||
&Opcode::Block(block_type, ref ops) => Interpreter::run_block(context, block_type, ops.elements()),
|
||||
&Opcode::Loop(block_type, ref ops) => Interpreter::run_loop(context, block_type, ops.elements()),
|
||||
&Opcode::Block(block_type, _) => Interpreter::run_block(context, block_type),
|
||||
&Opcode::Loop(block_type, _) => Interpreter::run_loop(context, block_type),
|
||||
&Opcode::If(block_type, ref ops) => Interpreter::run_if(context, block_type, ops.elements()),
|
||||
&Opcode::Else => Interpreter::run_else(context),
|
||||
&Opcode::End => Interpreter::run_end(context),
|
||||
@ -190,9 +333,9 @@ impl Interpreter {
|
||||
&Opcode::I32And => Interpreter::run_and::<i32>(context),
|
||||
&Opcode::I32Or => Interpreter::run_or::<i32>(context),
|
||||
&Opcode::I32Xor => Interpreter::run_xor::<i32>(context),
|
||||
&Opcode::I32Shl => Interpreter::run_shl::<i32>(context),
|
||||
&Opcode::I32ShrS => Interpreter::run_shr::<i32, i32>(context),
|
||||
&Opcode::I32ShrU => Interpreter::run_shr::<i32, u32>(context),
|
||||
&Opcode::I32Shl => Interpreter::run_shl::<i32>(context, 0x1F),
|
||||
&Opcode::I32ShrS => Interpreter::run_shr::<i32, i32>(context, 0x1F),
|
||||
&Opcode::I32ShrU => Interpreter::run_shr::<i32, u32>(context, 0x1F),
|
||||
&Opcode::I32Rotl => Interpreter::run_rotl::<i32>(context),
|
||||
&Opcode::I32Rotr => Interpreter::run_rotr::<i32>(context),
|
||||
|
||||
@ -209,9 +352,9 @@ impl Interpreter {
|
||||
&Opcode::I64And => Interpreter::run_and::<i64>(context),
|
||||
&Opcode::I64Or => Interpreter::run_or::<i64>(context),
|
||||
&Opcode::I64Xor => Interpreter::run_xor::<i64>(context),
|
||||
&Opcode::I64Shl => Interpreter::run_shl::<i64>(context),
|
||||
&Opcode::I64ShrS => Interpreter::run_shr::<i64, i64>(context),
|
||||
&Opcode::I64ShrU => Interpreter::run_shr::<i64, u64>(context),
|
||||
&Opcode::I64Shl => Interpreter::run_shl::<i64>(context, 0x3F),
|
||||
&Opcode::I64ShrS => Interpreter::run_shr::<i64, i64>(context, 0x3F),
|
||||
&Opcode::I64ShrU => Interpreter::run_shr::<i64, u64>(context, 0x3F),
|
||||
&Opcode::I64Rotl => Interpreter::run_rotl::<i64>(context),
|
||||
&Opcode::I64Rotr => Interpreter::run_rotr::<i64>(context),
|
||||
|
||||
@ -274,57 +417,73 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_unreachable(_context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
Err(Error::Trap("programmatic".into()))
|
||||
fn into_block(opcode: &Opcode, frame_type: BlockFrameType) -> Result<&[Opcode], Error> {
|
||||
match opcode {
|
||||
&Opcode::Block(_, ref ops) if frame_type != BlockFrameType::IfElse => Ok(ops.elements()),
|
||||
&Opcode::Loop(_, ref ops) if frame_type != BlockFrameType::IfElse => Ok(ops.elements()),
|
||||
&Opcode::If(_, ref ops) => Ok(Interpreter::separate_if(ops.elements(), frame_type)),
|
||||
_ => Err(Error::Interpreter("trying to read block from non-bock instruction".into()))
|
||||
}
|
||||
}
|
||||
|
||||
fn run_nop(_context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_block(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
||||
let frame_position = context.position + 1;
|
||||
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(true, frame_position, frame_position + 1, block_type.clone())?;
|
||||
Interpreter::execute_block(context, body)
|
||||
}
|
||||
|
||||
fn run_if(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
||||
fn separate_if(body: &[Opcode], frame_type: BlockFrameType) -> &[Opcode] {
|
||||
let body_len = body.len();
|
||||
let else_index = body.iter().position(|op| *op == Opcode::Else).unwrap_or(body_len - 1);
|
||||
let (begin_index, end_index) = if context.value_stack_mut().pop_as()? {
|
||||
let (begin_index, end_index) = if frame_type == BlockFrameType::IfTrue {
|
||||
(0, else_index + 1)
|
||||
} else {
|
||||
(else_index + 1, body_len)
|
||||
};
|
||||
&body[begin_index..end_index]
|
||||
}
|
||||
|
||||
if begin_index != end_index {
|
||||
let frame_position = context.position + 1;
|
||||
context.push_frame(false, frame_position, frame_position, block_type.clone())?;
|
||||
Interpreter::execute_block(context, &body[begin_index..end_index])
|
||||
fn run_unreachable<'a>(_context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
Err(Error::Trap("programmatic".into()))
|
||||
}
|
||||
|
||||
fn run_nop<'a>(_context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_block<'a, 'b>(context: &'b mut FunctionContext<'a>, block_type: BlockType) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let frame_position = context.position;
|
||||
context.push_frame(BlockFrameType::Block, frame_position, frame_position + 1, frame_position + 1, block_type)?;
|
||||
Ok(InstructionOutcome::ExecuteBlock)
|
||||
}
|
||||
|
||||
fn run_loop<'a, 'b>(context: &'b mut FunctionContext<'a>, block_type: BlockType) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let frame_position = context.position;
|
||||
context.push_frame(BlockFrameType::Loop, frame_position, frame_position, frame_position + 1, block_type)?;
|
||||
Ok(InstructionOutcome::ExecuteBlock)
|
||||
}
|
||||
|
||||
fn run_if<'a, 'b>(context: &'b mut FunctionContext<'a>, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let branch = context.value_stack_mut().pop_as()?;
|
||||
let block_frame_type = if branch { BlockFrameType::IfTrue } else { BlockFrameType::IfElse };
|
||||
let branch_body = Interpreter::separate_if(body, block_frame_type);
|
||||
|
||||
if branch_body.len() != 0 {
|
||||
let frame_position = context.position;
|
||||
context.push_frame(block_frame_type, frame_position, frame_position + 1, frame_position + 1, block_type)?;
|
||||
Ok(InstructionOutcome::ExecuteBlock)
|
||||
} else {
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_else(_context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
fn run_else<'a>(_context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
Ok(InstructionOutcome::End)
|
||||
}
|
||||
|
||||
fn run_end(_context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
fn run_end<'a>(_context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
Ok(InstructionOutcome::End)
|
||||
}
|
||||
|
||||
fn run_br(_context: &mut FunctionContext, label_idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn run_br<'a>(_context: &mut FunctionContext, label_idx: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
Ok(InstructionOutcome::Branch(label_idx as usize))
|
||||
}
|
||||
|
||||
fn run_br_if(context: &mut FunctionContext, label_idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn run_br_if<'a>(context: &mut FunctionContext, label_idx: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
if context.value_stack_mut().pop_as()? {
|
||||
Ok(InstructionOutcome::Branch(label_idx as usize))
|
||||
} else {
|
||||
@ -332,36 +491,40 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_br_table(context: &mut FunctionContext, table: &Vec<u32>, default: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn run_br_table<'a>(context: &mut FunctionContext, table: &Vec<u32>, default: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let index: u32 = context.value_stack_mut().pop_as()?;
|
||||
Ok(InstructionOutcome::Branch(table.get(index as usize).cloned().unwrap_or(default) as usize))
|
||||
}
|
||||
|
||||
fn run_return(_context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
fn run_return<'a>(_context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
Ok(InstructionOutcome::Return)
|
||||
}
|
||||
|
||||
fn run_call(context: &mut FunctionContext, func_idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
context.call_function(func_idx)
|
||||
.and_then(|r| r.map(|r| context.value_stack_mut().push(r)).unwrap_or(Ok(())))
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
fn run_call<'a, 'b>(context: &'b mut FunctionContext<'a>, func_idx: u32) -> Result<InstructionOutcome<'a>, Error> where 'a: 'b {
|
||||
Ok(InstructionOutcome::ExecuteCall(context.module().function_reference(ItemIndex::IndexSpace(func_idx), Some(context.externals))?))
|
||||
}
|
||||
|
||||
fn run_call_indirect(context: &mut FunctionContext, type_idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn run_call_indirect<'a, 'b>(context: &'b mut FunctionContext<'a>, type_idx: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let table_func_idx: u32 = context.value_stack_mut().pop_as()?;
|
||||
context.call_function_indirect(DEFAULT_TABLE_INDEX, type_idx, table_func_idx)
|
||||
.and_then(|r| r.map(|r| context.value_stack_mut().push(r)).unwrap_or(Ok(())))
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
let function_reference = context.module().function_reference_indirect(DEFAULT_TABLE_INDEX, type_idx, table_func_idx, Some(context.externals))?;
|
||||
let required_function_type = context.module().function_type_by_index(type_idx)?;
|
||||
let actual_function_type = function_reference.module.function_type(ItemIndex::Internal(function_reference.internal_index))?;
|
||||
if required_function_type != actual_function_type {
|
||||
return Err(Error::Function(format!("expected function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}",
|
||||
required_function_type.params(), required_function_type.return_type(),
|
||||
actual_function_type.params(), actual_function_type.return_type())));
|
||||
}
|
||||
Ok(InstructionOutcome::ExecuteCall(function_reference))
|
||||
}
|
||||
|
||||
fn run_drop(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
fn run_drop<'a>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
.pop()
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_select(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
fn run_select<'a>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
.pop_triple()
|
||||
@ -376,32 +539,32 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_get_local(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn run_get_local<'a>(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.get_local(index as usize)
|
||||
.map(|value| context.value_stack_mut().push(value))
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_set_local(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn run_set_local<'a>(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let arg = context.value_stack_mut().pop()?;
|
||||
context.set_local(index as usize, arg)
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_tee_local(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn run_tee_local<'a>(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let arg = context.value_stack().top()?.clone();
|
||||
context.set_local(index as usize, arg)
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_get_global(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn run_get_global<'a>(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.module()
|
||||
.global(ItemIndex::IndexSpace(index), None)
|
||||
.and_then(|g| context.value_stack_mut().push(g.get()))
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_set_global(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn run_set_global<'a>(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
.pop()
|
||||
@ -409,7 +572,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_load<T>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result<InstructionOutcome, Error>
|
||||
fn run_load<'a, T>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T>, T: LittleEndianConvert {
|
||||
let address = effective_address(offset, context.value_stack_mut().pop_as()?)?;
|
||||
context.module()
|
||||
@ -420,7 +583,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_load_extend<T, U>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result<InstructionOutcome, Error>
|
||||
fn run_load_extend<'a, T, U>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result<InstructionOutcome<'a>, Error>
|
||||
where T: ExtendInto<U>, RuntimeValue: From<U>, T: LittleEndianConvert {
|
||||
let address = effective_address(offset, context.value_stack_mut().pop_as()?)?;
|
||||
let stack_value: U = context.module()
|
||||
@ -434,7 +597,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_store<T>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result<InstructionOutcome, Error>
|
||||
fn run_store<'a, T>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: TryInto<T, Error>, T: LittleEndianConvert {
|
||||
let stack_value = context
|
||||
.value_stack_mut()
|
||||
@ -447,7 +610,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_store_wrap<T, U>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result<InstructionOutcome, Error>
|
||||
fn run_store_wrap<'a, T, U>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: TryInto<T, Error>, T: WrapInto<U>, U: LittleEndianConvert {
|
||||
let stack_value: T = context.value_stack_mut().pop().and_then(|v| v.try_into())?;
|
||||
let stack_value = stack_value.wrap_into().into_little_endian();
|
||||
@ -458,7 +621,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_current_memory(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
fn run_current_memory<'a>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.module()
|
||||
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
||||
.map(|m| m.size())
|
||||
@ -466,7 +629,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_grow_memory(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
fn run_grow_memory<'a>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let pages: u32 = context.value_stack_mut().pop_as()?;
|
||||
context.module()
|
||||
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
||||
@ -475,14 +638,14 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_const(context: &mut FunctionContext, val: RuntimeValue) -> Result<InstructionOutcome, Error> {
|
||||
fn run_const<'a>(context: &mut FunctionContext, val: RuntimeValue) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
.push(val)
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_eqz<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_eqz<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: TryInto<T, Error>, T: PartialEq<T> + Default {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -492,7 +655,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_eq<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_eq<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: TryInto<T, Error>, T: PartialEq<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -502,7 +665,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_ne<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_ne<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: TryInto<T, Error>, T: PartialEq<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -512,7 +675,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_lt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_lt<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> + Display {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -522,7 +685,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_gt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_gt<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -532,7 +695,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_lte<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_lte<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -542,7 +705,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_gte<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_gte<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -552,7 +715,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_clz<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_clz<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -562,7 +725,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_ctz<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_ctz<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -572,7 +735,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_popcnt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_popcnt<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -582,7 +745,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_add<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_add<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: ArithmeticOps<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -592,7 +755,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_sub<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_sub<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: ArithmeticOps<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -602,7 +765,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_mul<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_mul<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: ArithmeticOps<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -612,7 +775,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_div<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_div<'a, T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: TransmuteInto<U> + Display, U: ArithmeticOps<U> + TransmuteInto<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -624,7 +787,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_rem<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_rem<'a, T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: TransmuteInto<U>, U: Integer<U> + TransmuteInto<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -636,7 +799,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_and<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_and<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<<T as ops::BitAnd>::Output> + TryInto<T, Error>, T: ops::BitAnd<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -646,7 +809,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_or<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_or<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<<T as ops::BitOr>::Output> + TryInto<T, Error>, T: ops::BitOr<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -656,7 +819,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_xor<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_xor<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<<T as ops::BitXor>::Output> + TryInto<T, Error>, T: ops::BitXor<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -666,29 +829,29 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_shl<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
where RuntimeValue: From<<T as ops::Shl<T>>::Output> + TryInto<T, Error>, T: ops::Shl<T> {
|
||||
fn run_shl<'a, T>(context: &mut FunctionContext, mask: T) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<<T as ops::Shl<T>>::Output> + TryInto<T, Error>, T: ops::Shl<T> + ops::BitAnd<T, Output=T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
.pop_pair_as::<T>()
|
||||
.map(|(left, right)| left.shl(right))
|
||||
.map(|(left, right)| left.shl(right & mask))
|
||||
.map(|v| context.value_stack_mut().push(v.into()))
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_shr<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: TransmuteInto<U>, U: ops::Shr<U>, <U as ops::Shr<U>>::Output: TransmuteInto<T> {
|
||||
fn run_shr<'a, T, U>(context: &mut FunctionContext, mask: U) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: TransmuteInto<U>, U: ops::Shr<U> + ops::BitAnd<U, Output=U>, <U as ops::Shr<U>>::Output: TransmuteInto<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
.pop_pair_as::<T>()
|
||||
.map(|(left, right)| (left.transmute_into(), right.transmute_into()))
|
||||
.map(|(left, right)| left.shr(right))
|
||||
.map(|(left, right)| left.shr(right & mask))
|
||||
.map(|v| v.transmute_into())
|
||||
.map(|v| context.value_stack_mut().push(v.into()))
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_rotl<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_rotl<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -698,7 +861,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_rotr<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_rotr<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -708,7 +871,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_abs<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_abs<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -718,7 +881,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_neg<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_neg<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<<T as ops::Neg>::Output> + TryInto<T, Error>, T: ops::Neg {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -728,7 +891,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_ceil<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_ceil<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -738,7 +901,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_floor<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_floor<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -748,7 +911,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_trunc<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_trunc<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -758,7 +921,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_nearest<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_nearest<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -768,7 +931,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_sqrt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_sqrt<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -778,7 +941,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_min<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_min<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -788,7 +951,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_max<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_max<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -798,7 +961,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_copysign<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_copysign<'a, T>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -808,7 +971,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_wrap<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_wrap<'a, T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<U> + TryInto<T, Error>, T: WrapInto<U> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -818,7 +981,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_trunc_to_int<T, U, V>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_trunc_to_int<'a, T, U, V>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<V> + TryInto<T, Error>, T: TryTruncateInto<U, Error>, U: TransmuteInto<V>, {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -829,7 +992,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_extend<T, U, V>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_extend<'a, T, U, V>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<V> + TryInto<T, Error>, T: ExtendInto<U>, U: TransmuteInto<V> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -840,7 +1003,7 @@ impl Interpreter {
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn run_reinterpret<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
fn run_reinterpret<'a, T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error>
|
||||
where RuntimeValue: From<U>, RuntimeValue: TryInto<T, Error>, T: TransmuteInto<U> {
|
||||
context
|
||||
.value_stack_mut()
|
||||
@ -849,44 +1012,15 @@ impl Interpreter {
|
||||
.and_then(|val| context.value_stack_mut().push(val.into()))
|
||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
|
||||
fn execute_block(context: &mut FunctionContext, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
||||
debug_assert!(!context.frame_stack.is_empty());
|
||||
|
||||
// run instructions
|
||||
context.position = 0;
|
||||
loop {
|
||||
let instruction = &body[context.position];
|
||||
|
||||
debug!(target: "interpreter", "running {:?}", instruction);
|
||||
match Interpreter::run_instruction(context, instruction)? {
|
||||
InstructionOutcome::RunInstruction => (),
|
||||
InstructionOutcome::RunNextInstruction => context.position += 1,
|
||||
InstructionOutcome::Branch(index) => {
|
||||
if index != 0 {
|
||||
context.discard_frame()?;
|
||||
return Ok(InstructionOutcome::Branch(index - 1));
|
||||
} else {
|
||||
context.pop_frame(true)?;
|
||||
return Ok(InstructionOutcome::RunInstruction);
|
||||
}
|
||||
},
|
||||
InstructionOutcome::End => {
|
||||
context.pop_frame(false)?;
|
||||
return Ok(InstructionOutcome::RunInstruction);
|
||||
},
|
||||
InstructionOutcome::Return => return Ok(InstructionOutcome::Return),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FunctionContext<'a> {
|
||||
pub fn new(module: &'a ModuleInstance, externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>, value_stack_limit: usize, frame_stack_limit: usize, function: &FunctionType, args: Vec<VariableInstance>) -> Self {
|
||||
pub fn new(function: InternalFunctionReference<'a>, externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>, value_stack_limit: usize, frame_stack_limit: usize, function_type: &FunctionType, args: Vec<VariableInstance>) -> Self {
|
||||
FunctionContext {
|
||||
module: module,
|
||||
is_initialized: false,
|
||||
function: function,
|
||||
externals: externals,
|
||||
return_type: function.return_type().map(|vt| BlockType::Value(vt)).unwrap_or(BlockType::NoResult),
|
||||
return_type: function_type.return_type().map(|vt| BlockType::Value(vt)).unwrap_or(BlockType::NoResult),
|
||||
value_stack: StackWithLimit::with_limit(value_stack_limit),
|
||||
frame_stack: StackWithLimit::with_limit(frame_stack_limit),
|
||||
locals: args,
|
||||
@ -894,23 +1028,48 @@ impl<'a> FunctionContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn module(&self) -> &ModuleInstance {
|
||||
self.module
|
||||
pub fn nested(&mut self, function: InternalFunctionReference<'a>) -> Result<Self, Error> {
|
||||
let function_type = function.module.function_type(ItemIndex::Internal(function.internal_index))?;
|
||||
let function_return_type = function_type.return_type().map(|vt| BlockType::Value(vt)).unwrap_or(BlockType::NoResult);
|
||||
let function_locals = prepare_function_args(&function_type, &mut self.value_stack)?;
|
||||
|
||||
Ok(FunctionContext {
|
||||
is_initialized: false,
|
||||
function: function,
|
||||
externals: self.externals,
|
||||
return_type: function_return_type,
|
||||
value_stack: StackWithLimit::with_limit(self.value_stack.limit() - self.value_stack.len()),
|
||||
frame_stack: StackWithLimit::with_limit(self.frame_stack.limit() - self.frame_stack.len()),
|
||||
locals: function_locals,
|
||||
position: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_initialized(&self) -> bool {
|
||||
self.is_initialized
|
||||
}
|
||||
|
||||
pub fn initialize(&mut self, locals: &[Local]) -> Result<(), Error> {
|
||||
debug_assert!(!self.is_initialized);
|
||||
self.is_initialized = true;
|
||||
|
||||
let locals = locals.iter()
|
||||
.flat_map(|l| repeat(l.value_type().into()).take(l.count() as usize))
|
||||
.map(|vt| VariableInstance::new(true, vt, RuntimeValue::default(vt)))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
self.locals.extend(locals);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn module(&self) -> &Arc<ModuleInstanceInterface + 'a> {
|
||||
&self.function.module
|
||||
}
|
||||
|
||||
pub fn externals(&self) -> &HashMap<String, Arc<ModuleInstanceInterface + 'a>> {
|
||||
&self.externals
|
||||
}
|
||||
|
||||
pub fn call_function(&mut self, index: u32) -> Result<Option<RuntimeValue>, Error> {
|
||||
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> {
|
||||
self.module.call_function_indirect(CallerContext::nested(self), ItemIndex::IndexSpace(table_index), type_index, func_index)
|
||||
}
|
||||
|
||||
pub fn set_local(&mut self, index: usize, value: RuntimeValue) -> Result<InstructionOutcome, Error> {
|
||||
pub fn set_local(&mut self, index: usize, value: RuntimeValue) -> Result<InstructionOutcome<'a>, Error> {
|
||||
self.locals.get_mut(index)
|
||||
.ok_or(Error::Local(format!("expected to have local with index {}", index)))
|
||||
.and_then(|l| l.set(value))
|
||||
@ -935,9 +1094,14 @@ impl<'a> FunctionContext<'a> {
|
||||
&self.frame_stack
|
||||
}
|
||||
|
||||
pub fn push_frame(&mut self, is_loop: bool, branch_position: usize, end_position: usize, signature: BlockType) -> Result<(), Error> {
|
||||
pub fn frame_stack_mut(&mut self) -> &mut StackWithLimit<BlockFrame> {
|
||||
&mut self.frame_stack
|
||||
}
|
||||
|
||||
pub fn push_frame(&mut self, frame_type: BlockFrameType, begin_position: usize, branch_position: usize, end_position: usize, signature: BlockType) -> Result<(), Error> {
|
||||
self.frame_stack.push(BlockFrame {
|
||||
is_loop: is_loop,
|
||||
frame_type: frame_type,
|
||||
begin_position: begin_position,
|
||||
branch_position: branch_position,
|
||||
end_position: end_position,
|
||||
value_limit: self.value_stack.len(),
|
||||
@ -957,7 +1121,7 @@ impl<'a> FunctionContext<'a> {
|
||||
}
|
||||
|
||||
let frame_value = match frame.signature {
|
||||
BlockType::Value(_) if !frame.is_loop || !is_branch => Some(self.value_stack.pop()?),
|
||||
BlockType::Value(_) if frame.frame_type != BlockFrameType::Loop || !is_branch => Some(self.value_stack.pop()?),
|
||||
_ => None,
|
||||
};
|
||||
self.value_stack.resize(frame.value_limit, RuntimeValue::I32(0));
|
||||
@ -970,15 +1134,9 @@ 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(),
|
||||
signature: BlockType::NoResult,
|
||||
}
|
||||
impl<'a> fmt::Debug for FunctionContext<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "FunctionContext")
|
||||
}
|
||||
}
|
||||
|
||||
@ -988,3 +1146,18 @@ fn effective_address(address: u32, offset: u32) -> Result<u32, Error> {
|
||||
Some(address) => Ok(address),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_function_args(function_type: &FunctionType, caller_stack: &mut StackWithLimit<RuntimeValue>) -> Result<Vec<VariableInstance>, Error> {
|
||||
let mut args = function_type.params().iter().rev().map(|param_type| {
|
||||
let param_value = caller_stack.pop()?;
|
||||
let actual_type = param_value.variable_type();
|
||||
let expected_type = (*param_type).into();
|
||||
if actual_type != Some(expected_type) {
|
||||
return Err(Error::Function(format!("invalid parameter type {:?} when expected {:?}", actual_type, expected_type)));
|
||||
}
|
||||
|
||||
VariableInstance::new(true, expected_type, param_value)
|
||||
}).collect::<Result<Vec<_>, _>>()?;
|
||||
args.reverse();
|
||||
Ok(args)
|
||||
}
|
||||
|
@ -38,6 +38,10 @@ impl<T> StackWithLimit<T> where T: Clone {
|
||||
self.limit
|
||||
}
|
||||
|
||||
pub fn values(&self) -> &VecDeque<T> {
|
||||
&self.values
|
||||
}
|
||||
|
||||
pub fn top(&self) -> Result<&T, Error> {
|
||||
self.values
|
||||
.back()
|
||||
@ -67,6 +71,19 @@ impl<T> StackWithLimit<T> where T: Clone {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn push_penultimate(&mut self, value: T) -> Result<(), Error> {
|
||||
if self.values.is_empty() {
|
||||
return Err(Error::Stack("trying to insert penultimate element into empty stack".into()));
|
||||
}
|
||||
self.push(value)?;
|
||||
|
||||
let last_index = self.values.len() - 1;
|
||||
let penultimate_index = last_index - 1;
|
||||
self.values.swap(last_index, penultimate_index);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Result<T, Error> {
|
||||
self.values
|
||||
.pop_back()
|
||||
|
@ -235,7 +235,7 @@ fn if_else_with_return_type_validation() {
|
||||
let imports = ModuleImports::new(Weak::default(), None);
|
||||
let mut context = FunctionValidationContext::new(&module, &imports, &[], 1024, 1024, &FunctionType::default());
|
||||
|
||||
Validator::validate_block(&mut context, false, BlockType::NoResult, &[
|
||||
Validator::validate_function(&mut context, BlockType::NoResult, &[
|
||||
Opcode::I32Const(1),
|
||||
Opcode::If(BlockType::NoResult, Opcodes::new(vec![
|
||||
Opcode::I32Const(1),
|
||||
@ -249,5 +249,5 @@ fn if_else_with_return_type_validation() {
|
||||
Opcode::End,
|
||||
])),
|
||||
Opcode::End,
|
||||
], Opcode::End).unwrap();
|
||||
]).unwrap();
|
||||
}
|
||||
|
@ -1,54 +1,60 @@
|
||||
///! Tests from https://github.com/WebAssembly/wabt/tree/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp
|
||||
|
||||
use std::sync::Weak;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use builder::module;
|
||||
use elements::{Module, ValueType, Opcodes, Opcode, BlockType, FunctionType};
|
||||
use elements::{ValueType, Opcodes, Opcode, BlockType, Local};
|
||||
use interpreter::Error;
|
||||
use interpreter::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex};
|
||||
use interpreter::module::{ModuleInstanceInterface, ItemIndex};
|
||||
use interpreter::program::ProgramInstance;
|
||||
use interpreter::runner::{Interpreter, FunctionContext};
|
||||
use interpreter::value::{RuntimeValue, TryInto};
|
||||
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(), "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
|
||||
VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(0)).unwrap(), // local1
|
||||
VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(0)).unwrap(), // local2
|
||||
]);
|
||||
Interpreter::run_function(&mut context, body.elements())
|
||||
.map(|v| v.unwrap().try_into().unwrap())
|
||||
fn make_function_i32(body: Opcodes) -> (ProgramInstance, Arc<ModuleInstanceInterface>) {
|
||||
let module = module()
|
||||
.function()
|
||||
.signature().param().i32().return_type().i32().build()
|
||||
.body()
|
||||
.with_locals(vec![Local::new(2, ValueType::I32)])
|
||||
.with_opcodes(body)
|
||||
.build()
|
||||
.build()
|
||||
.build();
|
||||
|
||||
let program = ProgramInstance::new().unwrap();
|
||||
let module = program.add_module("main", module, None).unwrap();
|
||||
(program, module)
|
||||
}
|
||||
|
||||
fn run_function_i32(module: &Arc<ModuleInstanceInterface>, arg: i32) -> Result<i32, Error> {
|
||||
module
|
||||
.execute_index(0, vec![RuntimeValue::I32(arg)].into())
|
||||
.map(|r| r.unwrap().try_into().unwrap())
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/unreachable.txt
|
||||
#[test]
|
||||
fn unreachable() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Unreachable, // trap
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap_err(), Error::Trap("programmatic".into()));
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap_err(), Error::Trap("programmatic".into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nop() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Nop, // nop
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::Nop, // nop
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 1);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-block.txt
|
||||
#[test]
|
||||
fn expr_block() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::Value(ValueType::I32), // mark block
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(10), // [10]
|
||||
@ -56,15 +62,15 @@ fn expr_block() {
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::End,
|
||||
])),
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 1);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/loop.txt
|
||||
#[test]
|
||||
fn loop_test() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Loop(BlockType::NoResult, // loop
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
@ -85,15 +91,15 @@ fn loop_test() {
|
||||
])),
|
||||
Opcode::End])), // end loop
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 10);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 10);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/if.txt#L3
|
||||
#[test]
|
||||
fn if_1() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::I32Const(0), // [0]
|
||||
Opcode::SetLocal(0), // [] + arg = 0
|
||||
Opcode::I32Const(1), // [1]
|
||||
@ -115,15 +121,15 @@ fn if_1() {
|
||||
Opcode::End, // end if
|
||||
])),
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 1);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/if.txt#L23
|
||||
#[test]
|
||||
fn if_2() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::If(BlockType::NoResult, // if 1
|
||||
Opcodes::new(vec![
|
||||
@ -147,15 +153,15 @@ fn if_2() {
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::GetLocal(1), // [arg, local1]
|
||||
Opcode::I32Add, // [arg + local1]
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 9);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 9);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-if.txt
|
||||
#[test]
|
||||
fn expr_if() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(0), // [arg, 0]
|
||||
Opcode::I32Eq, // [arg == 0]
|
||||
@ -166,16 +172,16 @@ fn expr_if() {
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::End, // end if
|
||||
])),
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&module, 1).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/nested-if.txt
|
||||
#[test]
|
||||
fn nested_if() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult,
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(1),
|
||||
@ -194,15 +200,15 @@ fn nested_if() {
|
||||
Opcode::End,
|
||||
])),
|
||||
Opcode::I32Const(4),
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 4);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 4);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L4
|
||||
#[test]
|
||||
fn br_0() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // mark block
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(1), // [1]
|
||||
@ -224,15 +230,15 @@ fn br_0() {
|
||||
Opcode::I32Const(1), // [arg == 0, local1, 1]
|
||||
Opcode::I32Eq, // [arg == 0, local1 == 1]
|
||||
Opcode::I32Add, // [arg == 0 + local1 == 1]
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L26
|
||||
#[test]
|
||||
fn br_1() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block1
|
||||
Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block2
|
||||
@ -264,15 +270,15 @@ fn br_1() {
|
||||
Opcode::I32Const(1), // [arg == 0 + local1 == 0, local2, 1]
|
||||
Opcode::I32Eq, // [arg == 0 + local1 == 0, local2 == 1]
|
||||
Opcode::I32Add, // [arg == 0 + local1 == 0 + local2 == 1]
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 3);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 3);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L56
|
||||
#[test]
|
||||
fn br_2() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block1
|
||||
Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block2
|
||||
@ -291,15 +297,15 @@ fn br_2() {
|
||||
])),
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::Return, // return 2
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L71
|
||||
#[test]
|
||||
fn br_3() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block1
|
||||
Opcodes::new(vec![
|
||||
Opcode::Loop(BlockType::NoResult, // loop
|
||||
@ -333,15 +339,15 @@ fn br_3() {
|
||||
])),
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::Return, // return local1
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 3);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 3);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-br.txt
|
||||
#[test]
|
||||
fn expr_br() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::Value(ValueType::I32), // block1
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
@ -356,16 +362,16 @@ fn expr_br() {
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::End, // end (block1)
|
||||
])),
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&module, 1).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brif.txt
|
||||
#[test]
|
||||
fn brif() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block1
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
@ -376,16 +382,16 @@ fn brif() {
|
||||
])),
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::Return, // return 2
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&module, 1).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brif-loop.txt
|
||||
#[test]
|
||||
fn brif_loop() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Loop(BlockType::NoResult, // loop
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
@ -400,16 +406,16 @@ fn brif_loop() {
|
||||
])),
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::Return, // return
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 3).unwrap(), 3);
|
||||
assert_eq!(run_function_i32(&body, 10).unwrap(), 10);
|
||||
assert_eq!(run_function_i32(&module, 3).unwrap(), 3);
|
||||
assert_eq!(run_function_i32(&module, 10).unwrap(), 10);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-brif.txt
|
||||
#[test]
|
||||
fn expr_brif() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Loop(BlockType::NoResult, // loop
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
@ -423,16 +429,16 @@ fn expr_brif() {
|
||||
Opcode::End, // end (loop)
|
||||
])),
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 3).unwrap(), 3);
|
||||
assert_eq!(run_function_i32(&body, 10).unwrap(), 10);
|
||||
assert_eq!(run_function_i32(&module, 3).unwrap(), 3);
|
||||
assert_eq!(run_function_i32(&module, 10).unwrap(), 10);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brtable.txt
|
||||
#[test]
|
||||
fn brtable() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block3
|
||||
Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block2
|
||||
@ -457,18 +463,18 @@ fn brtable() {
|
||||
])),
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::Return, // return 2
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 0);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&body, 2).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&body, 3).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 0);
|
||||
assert_eq!(run_function_i32(&module, 1).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&module, 2).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&module, 3).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/return.txt
|
||||
#[test]
|
||||
fn return_test() {
|
||||
let body = Opcodes::new(vec![
|
||||
let (_program, module) = make_function_i32(Opcodes::new(vec![
|
||||
Opcode::GetLocal(0),
|
||||
Opcode::I32Const(0),
|
||||
Opcode::I32Eq,
|
||||
@ -489,11 +495,11 @@ fn return_test() {
|
||||
])),
|
||||
Opcode::I32Const(3),
|
||||
Opcode::Return,
|
||||
Opcode::End]);
|
||||
Opcode::End]));
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&body, 5).unwrap(), 3);
|
||||
assert_eq!(run_function_i32(&module, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&module, 1).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&module, 5).unwrap(), 3);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/return-void.txt
|
||||
@ -798,7 +804,7 @@ fn callindirect_2() {
|
||||
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(),
|
||||
Error::Function("expected function with signature ([I32, I32]) -> Some(I32) when got with ([I32]) -> Some(I32)".into()));
|
||||
Error::Function("expected indirect function with signature ([I32, I32]) -> Some(I32) when got with ([I32]) -> Some(I32)".into()));
|
||||
assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(3)].into()).unwrap_err(),
|
||||
Error::Table("trying to read table item with index 3 when there are only 3 items".into()));
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::u32;
|
||||
use std::collections::VecDeque;
|
||||
use elements::{Module, Opcode, BlockType, FunctionType, ValueType, External, Type};
|
||||
use interpreter::Error;
|
||||
use interpreter::runner::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||
@ -53,39 +54,91 @@ pub struct Validator;
|
||||
|
||||
/// Instruction outcome.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InstructionOutcome {
|
||||
pub enum InstructionOutcome<'a> {
|
||||
/// Continue with next instruction.
|
||||
RunNextInstruction,
|
||||
ValidateNextInstruction,
|
||||
/// Unreachable instruction reached.
|
||||
Unreachable,
|
||||
/// Validate block.
|
||||
ValidateBlock(bool, BlockType, &'a [Opcode]),
|
||||
/// Validate 2 blocks.
|
||||
ValidateBlock2(bool, BlockType, &'a [Opcode], &'a [Opcode]),
|
||||
}
|
||||
|
||||
impl Validator {
|
||||
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()));
|
||||
pub fn validate_function(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode]) -> Result<(), Error> {
|
||||
context.push_label(false, block_type)?;
|
||||
Validator::validate_block(context, body)?;
|
||||
while !context.frame_stack.is_empty() {
|
||||
context.pop_label()?;
|
||||
}
|
||||
|
||||
context.push_label(is_loop, block_type)?;
|
||||
for opcode in body {
|
||||
match Validator::validate_instruction(context, opcode)? {
|
||||
InstructionOutcome::RunNextInstruction => (),
|
||||
InstructionOutcome::Unreachable => context.unreachable()?,
|
||||
}
|
||||
}
|
||||
context.pop_label()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_instruction(context: &mut FunctionValidationContext, opcode: &Opcode) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_block<'a>(context: &mut FunctionValidationContext, mut body: &[Opcode]) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let mut block_stack = VecDeque::new();
|
||||
let mut position = 0;
|
||||
loop {
|
||||
let opcode = if position < body.len() {
|
||||
&body[position]
|
||||
} else {
|
||||
let body_and_position = match block_stack.pop_back() {
|
||||
Some((new_body, new_position, need_pop_value)) => (new_body, new_position, need_pop_value),
|
||||
None => return Ok(InstructionOutcome::ValidateNextInstruction),
|
||||
};
|
||||
context.pop_label()?;
|
||||
|
||||
if body_and_position.2 {
|
||||
context.pop_any_value()?;
|
||||
}
|
||||
|
||||
body = body_and_position.0;
|
||||
position = body_and_position.1;
|
||||
|
||||
continue;
|
||||
};
|
||||
|
||||
match Validator::validate_instruction(context, opcode)? {
|
||||
InstructionOutcome::ValidateNextInstruction => position += 1,
|
||||
InstructionOutcome::Unreachable => {
|
||||
context.unreachable()?;
|
||||
position += 1;
|
||||
},
|
||||
InstructionOutcome::ValidateBlock(is_loop, block_type, new_body) => {
|
||||
context.push_label(is_loop, block_type)?;
|
||||
block_stack.push_back((body, position + 1, false));
|
||||
|
||||
body = new_body;
|
||||
position = 0;
|
||||
},
|
||||
InstructionOutcome::ValidateBlock2(is_loop, block_type, new_body, new_body2) => {
|
||||
let need_pop_value = match block_type {
|
||||
BlockType::NoResult => false,
|
||||
BlockType::Value(_) => true,
|
||||
};
|
||||
context.push_label(is_loop, block_type)?;
|
||||
context.push_label(is_loop, block_type)?;
|
||||
block_stack.push_back((body, position + 1, false));
|
||||
block_stack.push_back((new_body2, 0, need_pop_value));
|
||||
|
||||
body = new_body;
|
||||
position = 0;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_instruction<'a>(context: &mut FunctionValidationContext, opcode: &'a Opcode) -> Result<InstructionOutcome<'a>, Error> {
|
||||
debug!(target: "validator", "validating {:?}", opcode);
|
||||
match opcode {
|
||||
&Opcode::Unreachable => Ok(InstructionOutcome::Unreachable),
|
||||
&Opcode::Nop => Ok(InstructionOutcome::RunNextInstruction),
|
||||
&Opcode::Block(block_type, ref ops) => Validator::validate_block(context, false, block_type, ops.elements(), Opcode::End),
|
||||
&Opcode::Nop => Ok(InstructionOutcome::ValidateNextInstruction),
|
||||
&Opcode::Block(block_type, ref ops) => Validator::schedule_validate_block(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),
|
||||
&Opcode::End => Ok(InstructionOutcome::RunNextInstruction),
|
||||
&Opcode::Else => Ok(InstructionOutcome::ValidateNextInstruction),
|
||||
&Opcode::End => Ok(InstructionOutcome::ValidateNextInstruction),
|
||||
&Opcode::Br(idx) => Validator::validate_br(context, idx),
|
||||
&Opcode::BrIf(idx) => Validator::validate_br_if(context, idx),
|
||||
&Opcode::BrTable(ref table, default) => Validator::validate_br_table(context, table, default),
|
||||
@ -271,96 +324,96 @@ impl Validator {
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_const(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_const<'a>(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.push_value(value_type)?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_unop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_unop<'a>(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.pop_value(value_type)?;
|
||||
context.push_value(value_type)?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_binop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_binop<'a>(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.pop_value(value_type)?;
|
||||
context.pop_value(value_type)?;
|
||||
context.push_value(value_type)?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_testop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_testop<'a>(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.pop_value(value_type)?;
|
||||
context.push_value(ValueType::I32.into())?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_relop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_relop<'a>(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.pop_value(value_type)?;
|
||||
context.pop_value(value_type)?;
|
||||
context.push_value(ValueType::I32.into())?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: StackValueType, value_type2: StackValueType) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_cvtop<'a>(context: &mut FunctionValidationContext, value_type1: StackValueType, value_type2: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.pop_value(value_type1)?;
|
||||
context.push_value(value_type2)?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_drop(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_drop<'a>(context: &mut FunctionValidationContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.pop_any_value().map(|_| ())?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_select(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_select<'a>(context: &mut FunctionValidationContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
let select_type = context.pop_any_value()?;
|
||||
context.pop_value(select_type)?;
|
||||
context.push_value(select_type)?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_get_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_get_local<'a>(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let local_type = context.require_local(index)?;
|
||||
context.push_value(local_type)?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_set_local<'a>(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let local_type = context.require_local(index)?;
|
||||
let value_type = context.pop_any_value()?;
|
||||
if local_type != value_type {
|
||||
return Err(Error::Validation(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type)));
|
||||
}
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_tee_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_tee_local<'a>(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let local_type = context.require_local(index)?;
|
||||
let value_type = context.tee_any_value()?;
|
||||
if local_type != value_type {
|
||||
return Err(Error::Validation(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type)));
|
||||
}
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_get_global(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_get_global<'a>(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let global_type = context.require_global(index, None)?;
|
||||
context.push_value(global_type)?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_set_global(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_set_global<'a>(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let global_type = context.require_global(index, Some(true))?;
|
||||
let value_type = context.pop_any_value()?;
|
||||
if global_type != value_type {
|
||||
return Err(Error::Validation(format!("Trying to update global {} of type {:?} with value of type {:?}", index, global_type, value_type)));
|
||||
}
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_load<'a>(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
|
||||
if align != NATURAL_ALIGNMENT {
|
||||
if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align {
|
||||
return Err(Error::Validation(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align)));
|
||||
@ -370,10 +423,10 @@ impl Validator {
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
context.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.push_value(value_type)?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_store<'a>(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
|
||||
if align != NATURAL_ALIGNMENT {
|
||||
if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align {
|
||||
return Err(Error::Validation(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align)));
|
||||
@ -383,14 +436,14 @@ impl Validator {
|
||||
context.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.pop_value(value_type)?;
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_loop(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
||||
Validator::validate_block(context, true, block_type, body, Opcode::End)
|
||||
fn validate_loop<'a>(_context: &mut FunctionValidationContext, block_type: BlockType, body: &'a [Opcode]) -> Result<InstructionOutcome<'a>, Error> {
|
||||
Validator::schedule_validate_block(true, block_type, body, Opcode::End)
|
||||
}
|
||||
|
||||
fn validate_if(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_if<'a>(context: &mut FunctionValidationContext, block_type: BlockType, body: &'a [Opcode]) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
|
||||
let body_len = body.len();
|
||||
@ -398,22 +451,17 @@ impl Validator {
|
||||
.position(|op| *op == Opcode::Else)
|
||||
.unwrap_or(body_len - 1);
|
||||
if separator_index != body_len - 1 {
|
||||
Validator::validate_block(context, false, block_type, &body[..separator_index + 1], Opcode::Else)?;
|
||||
if let BlockType::Value(value_type) = block_type {
|
||||
context.pop_value(value_type.into())?;
|
||||
}
|
||||
|
||||
Validator::validate_block(context, false, block_type, &body[separator_index+1..], Opcode::End)
|
||||
Validator::schedule_validate_block2(false, block_type, &body[..separator_index + 1], Opcode::Else, &body[separator_index+1..], Opcode::End)
|
||||
} else {
|
||||
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)
|
||||
Validator::schedule_validate_block(false, block_type, body, Opcode::End)
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_br(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_br<'a>(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let (frame_is_loop, frame_block_type) = {
|
||||
let frame = context.require_label(idx)?;
|
||||
(frame.is_loop, frame.block_type)
|
||||
@ -427,39 +475,54 @@ impl Validator {
|
||||
Ok(InstructionOutcome::Unreachable)
|
||||
}
|
||||
|
||||
fn validate_br_if(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_br_if<'a>(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
if let BlockType::Value(value_type) = context.require_label(idx)?.block_type {
|
||||
context.tee_value(value_type.into())?;
|
||||
}
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_br_table(context: &mut FunctionValidationContext, table: &Vec<u32>, default: u32) -> Result<InstructionOutcome, Error> {
|
||||
let default_block_type = context.require_label(default)?.block_type;
|
||||
for label in table {
|
||||
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)));
|
||||
fn validate_br_table<'a>(context: &mut FunctionValidationContext, table: &Vec<u32>, default: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let mut required_block_type = None;
|
||||
|
||||
{
|
||||
let default_block = context.require_label(default)?;
|
||||
if !default_block.is_loop {
|
||||
required_block_type = Some(default_block.block_type);
|
||||
}
|
||||
|
||||
for label in table {
|
||||
let label_block = context.require_label(*label)?;
|
||||
if !label_block.is_loop {
|
||||
if let Some(required_block_type) = required_block_type {
|
||||
if required_block_type != label_block.block_type {
|
||||
return Err(Error::Validation(format!("Labels in br_table points to block of different types: {:?} and {:?}", required_block_type, label_block.block_type)));
|
||||
}
|
||||
}
|
||||
required_block_type = Some(label_block.block_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
if let BlockType::Value(value_type) = default_block_type {
|
||||
context.tee_value(value_type.into())?;
|
||||
if let Some(required_block_type) = required_block_type {
|
||||
if let BlockType::Value(value_type) = required_block_type {
|
||||
context.tee_value(value_type.into())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(InstructionOutcome::Unreachable)
|
||||
}
|
||||
|
||||
fn validate_return(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_return<'a>(context: &mut FunctionValidationContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
if let BlockType::Value(value_type) = context.return_type()? {
|
||||
context.tee_value(value_type.into())?;
|
||||
}
|
||||
Ok(InstructionOutcome::Unreachable)
|
||||
}
|
||||
|
||||
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_call<'a>(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
let (argument_types, return_type) = context.require_function(idx)?;
|
||||
for argument_type in argument_types.iter().rev() {
|
||||
context.pop_value((*argument_type).into())?;
|
||||
@ -467,10 +530,10 @@ impl Validator {
|
||||
if let BlockType::Value(value_type) = return_type {
|
||||
context.push_value(value_type.into())?;
|
||||
}
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_call_indirect<'a>(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.require_table(DEFAULT_TABLE_INDEX, VariableType::AnyFunc)?;
|
||||
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
@ -481,20 +544,39 @@ impl Validator {
|
||||
if let BlockType::Value(value_type) = return_type {
|
||||
context.push_value(value_type.into())?;
|
||||
}
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_current_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_current_memory<'a>(context: &mut FunctionValidationContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.push_value(ValueType::I32.into())?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
|
||||
fn validate_grow_memory<'a>(context: &mut FunctionValidationContext) -> Result<InstructionOutcome<'a>, Error> {
|
||||
context.require_memory(DEFAULT_MEMORY_INDEX)?;
|
||||
context.pop_value(ValueType::I32.into())?;
|
||||
context.push_value(ValueType::I32.into())?;
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
fn schedule_validate_block<'a>(is_loop: bool, block_type: BlockType, body: &'a [Opcode], end_instr: Opcode) -> Result<InstructionOutcome<'a>, Error> {
|
||||
if body.is_empty() || body[body.len() - 1] != end_instr {
|
||||
return Err(Error::Validation("Every block must end with end/else instruction".into()));
|
||||
}
|
||||
|
||||
Ok(InstructionOutcome::ValidateBlock(is_loop, block_type, body))
|
||||
}
|
||||
|
||||
fn schedule_validate_block2<'a>(is_loop: bool, block_type: BlockType, body: &'a [Opcode], end_instr: Opcode, body2: &'a [Opcode], end_instr2: Opcode) -> Result<InstructionOutcome<'a>, Error> {
|
||||
if body.is_empty() || body[body.len() - 1] != end_instr {
|
||||
return Err(Error::Validation("Every block must end with end/else instruction".into()));
|
||||
}
|
||||
if body2.is_empty() || body2[body2.len() - 1] != end_instr2 {
|
||||
return Err(Error::Validation("Every block must end with end/else instruction".into()));
|
||||
}
|
||||
|
||||
Ok(InstructionOutcome::ValidateBlock2(is_loop, block_type, body, body2))
|
||||
}
|
||||
}
|
||||
|
||||
@ -590,7 +672,7 @@ impl<'a> FunctionValidationContext<'a> {
|
||||
self.push_value(value_type.into())?;
|
||||
}
|
||||
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
Ok(InstructionOutcome::ValidateNextInstruction)
|
||||
}
|
||||
|
||||
pub fn require_label(&self, idx: u32) -> Result<&ValidationFrame, Error> {
|
||||
|
@ -274,7 +274,9 @@ macro_rules! impl_try_truncate_into {
|
||||
return Err(Error::Value("invalid float value for this operation".into()));
|
||||
}
|
||||
|
||||
if self < $into::MIN as $from || self > $into::MAX as $from {
|
||||
// range check
|
||||
let result = self as $into;
|
||||
if result as $from != self.trunc() {
|
||||
return Err(Error::Value("invalid float value for this operation".into()));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user