mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-23 03:31:56 +00:00
function calls
This commit is contained in:
@ -10,8 +10,7 @@ pub enum Error {
|
|||||||
Variable(String),
|
Variable(String),
|
||||||
Global(String),
|
Global(String),
|
||||||
Local(String),
|
Local(String),
|
||||||
ValueStack(String),
|
Stack(String),
|
||||||
FrameStack(String),
|
|
||||||
Value(String),
|
Value(String),
|
||||||
Interpreter(String),
|
Interpreter(String),
|
||||||
Trap,
|
Trap,
|
||||||
@ -29,8 +28,7 @@ impl Into<String> for Error {
|
|||||||
Error::Variable(s) => s,
|
Error::Variable(s) => s,
|
||||||
Error::Global(s) => s,
|
Error::Global(s) => s,
|
||||||
Error::Local(s) => s,
|
Error::Local(s) => s,
|
||||||
Error::ValueStack(s) => s,
|
Error::Stack(s) => s,
|
||||||
Error::FrameStack(s) => s,
|
|
||||||
Error::Interpreter(s) => s,
|
Error::Interpreter(s) => s,
|
||||||
Error::Value(s) => s,
|
Error::Value(s) => s,
|
||||||
Error::Trap => "trap".into(),
|
Error::Trap => "trap".into(),
|
||||||
@ -44,6 +42,7 @@ mod memory;
|
|||||||
mod module;
|
mod module;
|
||||||
mod program;
|
mod program;
|
||||||
mod runner;
|
mod runner;
|
||||||
|
mod stack;
|
||||||
mod table;
|
mod table;
|
||||||
mod value;
|
mod value;
|
||||||
mod variable;
|
mod variable;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use elements::{Module, InitExpr, Opcode, Type};
|
use elements::{Module, InitExpr, Opcode, Type, FunctionType, FuncBody};
|
||||||
use interpreter::Error;
|
use interpreter::Error;
|
||||||
use interpreter::imports::ModuleImports;
|
use interpreter::imports::ModuleImports;
|
||||||
use interpreter::memory::MemoryInstance;
|
use interpreter::memory::MemoryInstance;
|
||||||
use interpreter::program::ProgramInstanceEssence;
|
use interpreter::program::ProgramInstanceEssence;
|
||||||
use interpreter::runner::Interpreter;
|
use interpreter::runner::{Interpreter, FunctionContext};
|
||||||
use interpreter::table::TableInstance;
|
use interpreter::table::TableInstance;
|
||||||
use interpreter::value::{RuntimeValue, TryInto};
|
use interpreter::value::{RuntimeValue, TryInto};
|
||||||
use interpreter::variable::VariableInstance;
|
use interpreter::variable::VariableInstance;
|
||||||
@ -67,7 +67,7 @@ impl ModuleInstance {
|
|||||||
.map(|g| {
|
.map(|g| {
|
||||||
get_initializer(g.init_expr())
|
get_initializer(g.init_expr())
|
||||||
.map_err(|e| Error::Initialization(e.into()))
|
.map_err(|e| Error::Initialization(e.into()))
|
||||||
.and_then(|v| VariableInstance::new(g.global_type(), v))
|
.and_then(|v| VariableInstance::new_global(g.global_type(), v).map(Arc::new))
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
None => Vec::new(),
|
None => Vec::new(),
|
||||||
@ -152,9 +152,7 @@ impl ModuleInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call function with given index in functions index space.
|
/// Call function with given index in functions index space.
|
||||||
pub fn call_function(&self, index: ItemIndex) -> Result<Option<RuntimeValue>, Error> {
|
pub fn call_function<'a>(&self, outer: &mut FunctionContext<'a>, index: ItemIndex) -> Result<Option<RuntimeValue>, Error> {
|
||||||
// each functions has its own value stack
|
|
||||||
// but there's global stack limit
|
|
||||||
match self.imports.parse_function_index(index) {
|
match self.imports.parse_function_index(index) {
|
||||||
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"),
|
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"),
|
||||||
ItemIndex::Internal(index) => {
|
ItemIndex::Internal(index) => {
|
||||||
@ -187,19 +185,44 @@ impl ModuleInstance {
|
|||||||
.get(index as usize)
|
.get(index as usize)
|
||||||
.ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions codes", index, s.bodies().len()))))?;
|
.ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions codes", index, s.bodies().len()))))?;
|
||||||
|
|
||||||
// TODO: args, locals
|
// TODO:
|
||||||
Interpreter::run_function(function_type, function_body.code().elements(), &vec![])
|
// 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() - outer.value_stack().len();
|
||||||
|
let frame_stack_limit = outer.frame_stack().limit() - outer.frame_stack().len();
|
||||||
|
let locals = prepare_function_locals(function_type, function_body, outer)?;
|
||||||
|
let mut innner = FunctionContext::new(self, value_stack_limit, frame_stack_limit, function_type, function_code, locals)?;
|
||||||
|
Interpreter::run_function(&mut innner, function_code)
|
||||||
},
|
},
|
||||||
ItemIndex::External(index) => self.module.import_section()
|
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)))
|
.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)
|
.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()))))
|
.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(e.module()))
|
.and_then(|e| self.imports.module(e.module()))
|
||||||
.and_then(|m| m.call_function(ItemIndex::Internal(index))),
|
.and_then(|m| m.call_function(outer, ItemIndex::Internal(index))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepare_function_locals(function_type: &FunctionType, function_body: &FuncBody, outer: &mut FunctionContext) -> Result<Vec<VariableInstance>, Error> {
|
||||||
|
// locals = function arguments + defined locals
|
||||||
|
function_type.params().iter().rev()
|
||||||
|
.map(|param_type| {
|
||||||
|
let param_value = outer.value_stack_mut().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)
|
||||||
|
})
|
||||||
|
.chain(function_body.locals().iter().map(|l| VariableInstance::new(true, l.value_type().into(), RuntimeValue::Null)))
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_initializer(expr: &InitExpr) -> Result<RuntimeValue, Error> {
|
fn get_initializer(expr: &InitExpr) -> Result<RuntimeValue, Error> {
|
||||||
let first_opcode = expr.code().get(0).ok_or(Error::Initialization(format!("empty instantiation-time initializer")))?;
|
let first_opcode = expr.code().get(0).ok_or(Error::Initialization(format!("empty instantiation-time initializer")))?;
|
||||||
match first_opcode {
|
match first_opcode {
|
||||||
|
@ -1,45 +1,36 @@
|
|||||||
// TODO: WebAssembly code must be validated before it can be instantiated and executed.
|
|
||||||
// WebAssembly is designed to allow decoding and validation to be performed in a single linear pass through a WebAssembly module,
|
|
||||||
// and to enable many parts of decoding and validation to be performed concurrently.
|
|
||||||
// => Interpreter is written in assumption that code has been validated
|
|
||||||
// (important https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#code-section)
|
|
||||||
|
|
||||||
// Externals:
|
|
||||||
// to call function: list of imported functions + list of functions
|
|
||||||
// to access globals: list of imported globals + list of globals
|
|
||||||
// to access linear memory: list of imported regions + list of regions
|
|
||||||
|
|
||||||
use std::sync::Weak;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
use std::collections::VecDeque;
|
use elements::{Opcode, BlockType, FunctionType};
|
||||||
use super::super::elements::{Module, Opcode, BlockType, FunctionType};
|
|
||||||
use interpreter::Error;
|
use interpreter::Error;
|
||||||
use interpreter::module::{ModuleInstance, ItemIndex};
|
use interpreter::module::{ModuleInstance, ItemIndex};
|
||||||
|
use interpreter::stack::StackWithLimit;
|
||||||
use interpreter::value::{RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, TransmuteInto,
|
use interpreter::value::{RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, TransmuteInto,
|
||||||
ArithmeticOps, Integer, Float};
|
ArithmeticOps, Integer, Float};
|
||||||
|
use interpreter::variable::VariableInstance;
|
||||||
|
|
||||||
const DEFAULT_MEMORY_INDEX: u32 = 0;
|
const DEFAULT_MEMORY_INDEX: u32 = 0;
|
||||||
|
|
||||||
pub struct Interpreter;
|
pub struct Interpreter;
|
||||||
|
|
||||||
/// Function execution context.
|
/// Function execution context.
|
||||||
struct FunctionContext<'a> {
|
pub struct FunctionContext<'a> {
|
||||||
/// Module instance.
|
/// Module instance.
|
||||||
module: &'a mut ModuleInstance,
|
module: &'a ModuleInstance,
|
||||||
|
/// Function return type.
|
||||||
|
return_type: BlockType,
|
||||||
|
/// Local variables.
|
||||||
|
locals: Vec<VariableInstance>,
|
||||||
/// Values stack.
|
/// Values stack.
|
||||||
value_stack: &'a mut VecDeque<RuntimeValue>,
|
value_stack: StackWithLimit<RuntimeValue>,
|
||||||
/// Blocks frames stack.
|
/// Blocks frames stack.
|
||||||
frame_stack: &'a mut VecDeque<BlockFrame>,
|
frame_stack: StackWithLimit<BlockFrame>,
|
||||||
/// Local function variables.
|
|
||||||
locals: Vec<RuntimeValue>,
|
|
||||||
/// Current instruction position.
|
/// Current instruction position.
|
||||||
position: usize,
|
position: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum InstructionOutcome {
|
pub enum InstructionOutcome {
|
||||||
/// Continue with current instruction.
|
/// Continue with current instruction.
|
||||||
RunInstruction,
|
RunInstruction,
|
||||||
/// Continue with next instruction.
|
/// Continue with next instruction.
|
||||||
@ -51,7 +42,7 @@ enum InstructionOutcome {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct BlockFrame {
|
pub struct BlockFrame {
|
||||||
// A label for reference from branch instructions.
|
// A label for reference from branch instructions.
|
||||||
position: usize,
|
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.
|
||||||
@ -61,20 +52,10 @@ struct BlockFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Interpreter {
|
impl Interpreter {
|
||||||
pub fn run_function(function: &FunctionType, body: &[Opcode], args: &[RuntimeValue]) -> Result<Option<RuntimeValue>, Error> {
|
pub fn run_function(context: &mut FunctionContext, body: &[Opcode]) -> Result<Option<RuntimeValue>, Error> {
|
||||||
// prepare execution context
|
Interpreter::execute_block(context, body)?;
|
||||||
let mut module = ModuleInstance::new(Weak::default(), Module::default()).unwrap();
|
match context.return_type {
|
||||||
let mut value_stack = VecDeque::new();
|
BlockType::Value(_) => Ok(Some(context.value_stack_mut().pop()?)),
|
||||||
let mut frame_stack = VecDeque::new();
|
|
||||||
let mut context = FunctionContext::new(&mut module, &mut value_stack, &mut frame_stack, function, body, args)?;
|
|
||||||
|
|
||||||
let block_type = match function.return_type() {
|
|
||||||
Some(value_type) => BlockType::Value(value_type),
|
|
||||||
None => BlockType::NoResult,
|
|
||||||
};
|
|
||||||
Interpreter::execute_block(&mut context, block_type.clone(), body)?;
|
|
||||||
match block_type {
|
|
||||||
BlockType::Value(_) => Ok(Some(context.pop_value()?)),
|
|
||||||
BlockType::NoResult => Ok(None),
|
BlockType::NoResult => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,19 +265,19 @@ impl Interpreter {
|
|||||||
fn run_block(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
fn run_block(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
||||||
let frame_position = context.position + 1;
|
let frame_position = context.position + 1;
|
||||||
context.push_frame(frame_position, block_type.clone())?;
|
context.push_frame(frame_position, block_type.clone())?;
|
||||||
Interpreter::execute_block(context, block_type, body)
|
Interpreter::execute_block(context, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_loop(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
fn run_loop(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
||||||
let frame_position = context.position;
|
let frame_position = context.position;
|
||||||
context.push_frame(frame_position, block_type.clone())?;
|
context.push_frame(frame_position, block_type.clone())?;
|
||||||
Interpreter::execute_block(context, block_type, body)
|
Interpreter::execute_block(context, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_if(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
fn run_if(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
||||||
let body_len = body.len();
|
let body_len = body.len();
|
||||||
let else_index = body.iter().position(|op| *op == Opcode::Else).unwrap_or(body_len - 1);
|
let else_index = body.iter().position(|op| *op == Opcode::Else).unwrap_or(body_len - 1);
|
||||||
let (begin_index, end_index) = if context.pop_value_as()? {
|
let (begin_index, end_index) = if context.value_stack_mut().pop_as()? {
|
||||||
(0, else_index + 1)
|
(0, else_index + 1)
|
||||||
} else {
|
} else {
|
||||||
(else_index + 1, body_len)
|
(else_index + 1, body_len)
|
||||||
@ -305,7 +286,7 @@ impl Interpreter {
|
|||||||
if begin_index != end_index {
|
if begin_index != end_index {
|
||||||
let frame_position = context.position + 1;
|
let frame_position = context.position + 1;
|
||||||
context.push_frame(frame_position, block_type.clone())?;
|
context.push_frame(frame_position, block_type.clone())?;
|
||||||
Interpreter::execute_block(context, block_type, &body[begin_index..end_index])
|
Interpreter::execute_block(context, &body[begin_index..end_index])
|
||||||
} else {
|
} else {
|
||||||
Ok(InstructionOutcome::RunNextInstruction)
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
@ -324,7 +305,7 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run_br_if(context: &mut FunctionContext, label_idx: u32) -> Result<InstructionOutcome, Error> {
|
fn run_br_if(context: &mut FunctionContext, label_idx: u32) -> Result<InstructionOutcome, Error> {
|
||||||
if context.pop_value_as()? {
|
if context.value_stack_mut().pop_as()? {
|
||||||
Ok(InstructionOutcome::PopFrame(label_idx as usize))
|
Ok(InstructionOutcome::PopFrame(label_idx as usize))
|
||||||
} else {
|
} else {
|
||||||
Ok(InstructionOutcome::RunNextInstruction)
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
@ -332,7 +313,7 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run_br_table(context: &mut FunctionContext, table: &Vec<u32>, default: u32) -> Result<InstructionOutcome, Error> {
|
fn run_br_table(context: &mut FunctionContext, table: &Vec<u32>, default: u32) -> Result<InstructionOutcome, Error> {
|
||||||
let index: u32 = context.pop_value_as()?;
|
let index: u32 = context.value_stack_mut().pop_as()?;
|
||||||
Ok(InstructionOutcome::PopFrame(table.get(index as usize).cloned().unwrap_or(default) as usize))
|
Ok(InstructionOutcome::PopFrame(table.get(index as usize).cloned().unwrap_or(default) as usize))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,39 +331,40 @@ impl Interpreter {
|
|||||||
|
|
||||||
fn run_drop(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
fn run_drop(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||||
context
|
context
|
||||||
.pop_value()
|
.value_stack_mut()
|
||||||
|
.pop()
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_select(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
fn run_select(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||||
context
|
context
|
||||||
.pop_value_triple()
|
.value_stack_mut()
|
||||||
|
.pop_triple()
|
||||||
.and_then(|(left, mid, right)|
|
.and_then(|(left, mid, right)|
|
||||||
match (left, mid, right.try_into()) {
|
match (left, mid, right.try_into()) {
|
||||||
(left, mid, Ok(condition)) => Ok((left, mid, condition)),
|
(left, mid, Ok(condition)) => Ok((left, mid, condition)),
|
||||||
_ => Err(Error::ValueStack("expected to get int value from stack".into()))
|
_ => Err(Error::Stack("expected to get int value from stack".into()))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.map(|(left, mid, condition)| if condition { left } else { mid })
|
.map(|(left, mid, condition)| if condition { left } else { mid })
|
||||||
.map(|val| context.push_value(val))
|
.map(|val| context.value_stack_mut().push(val))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_get_local(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
fn run_get_local(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||||
context.get_local(index as usize)
|
context.get_local(index as usize)
|
||||||
.map(|value| value.clone())
|
.map(|value| context.value_stack_mut().push(value))
|
||||||
.map(|value| context.push_value(value))
|
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_set_local(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
fn run_set_local(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||||
let arg = context.pop_value()?;
|
let arg = context.value_stack_mut().pop()?;
|
||||||
context.set_local(index as usize, arg)
|
context.set_local(index as usize, arg)
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_tee_local(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
fn run_tee_local(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||||
let arg = context.top_value()?.clone();
|
let arg = context.value_stack().top()?.clone();
|
||||||
context.set_local(index as usize, arg)
|
context.set_local(index as usize, arg)
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
@ -390,13 +372,14 @@ impl Interpreter {
|
|||||||
fn run_get_global(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
fn run_get_global(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||||
context.module()
|
context.module()
|
||||||
.global(ItemIndex::IndexSpace(index))
|
.global(ItemIndex::IndexSpace(index))
|
||||||
.and_then(|g| context.push_value(g.get()))
|
.and_then(|g| context.value_stack_mut().push(g.get()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_set_global(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
fn run_set_global(context: &mut FunctionContext, index: u32) -> Result<InstructionOutcome, Error> {
|
||||||
context
|
context
|
||||||
.pop_value()
|
.value_stack_mut()
|
||||||
|
.pop()
|
||||||
.and_then(|v| context.module().global(ItemIndex::IndexSpace(index)).and_then(|g| g.set(v)))
|
.and_then(|v| context.module().global(ItemIndex::IndexSpace(index)).and_then(|g| g.set(v)))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
@ -407,7 +390,7 @@ impl Interpreter {
|
|||||||
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
||||||
.and_then(|m| m.get(effective_address(offset, align)?, 4))
|
.and_then(|m| m.get(effective_address(offset, align)?, 4))
|
||||||
.map(|b| from_little_endian_bytes::<T>(&b))
|
.map(|b| from_little_endian_bytes::<T>(&b))
|
||||||
.and_then(|n| context.push_value(n.into()))
|
.and_then(|n| context.value_stack_mut().push(n.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,14 +402,16 @@ impl Interpreter {
|
|||||||
.map(|b| from_little_endian_bytes::<T>(&b))
|
.map(|b| from_little_endian_bytes::<T>(&b))
|
||||||
.map(|v| v.extend_into())?;
|
.map(|v| v.extend_into())?;
|
||||||
context
|
context
|
||||||
.push_value(stack_value.into())
|
.value_stack_mut()
|
||||||
|
.push(stack_value.into())
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_store<T>(context: &mut FunctionContext, offset: u32, align: u32) -> Result<InstructionOutcome, Error>
|
fn run_store<T>(context: &mut FunctionContext, offset: u32, align: u32) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: TryInto<T, Error> {
|
where RuntimeValue: TryInto<T, Error> {
|
||||||
let stack_value = context
|
let stack_value = context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|n| to_little_endian_bytes::<T>(n))?;
|
.map(|n| to_little_endian_bytes::<T>(n))?;
|
||||||
context.module()
|
context.module()
|
||||||
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
||||||
@ -436,7 +421,7 @@ impl Interpreter {
|
|||||||
|
|
||||||
fn run_store_wrap<T, U>(context: &mut FunctionContext, offset: u32, align: u32) -> Result<InstructionOutcome, Error>
|
fn run_store_wrap<T, U>(context: &mut FunctionContext, offset: u32, align: u32) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: TryInto<T, Error>, T: WrapInto<U> {
|
where RuntimeValue: TryInto<T, Error>, T: WrapInto<U> {
|
||||||
let stack_value: T = context.pop_value().and_then(|v| v.try_into())?;
|
let stack_value: T = context.value_stack_mut().pop().and_then(|v| v.try_into())?;
|
||||||
let stack_value = to_little_endian_bytes::<U>(stack_value.wrap_into());
|
let stack_value = to_little_endian_bytes::<U>(stack_value.wrap_into());
|
||||||
context.module()
|
context.module()
|
||||||
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
||||||
@ -448,307 +433,339 @@ impl Interpreter {
|
|||||||
context.module()
|
context.module()
|
||||||
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
||||||
.map(|m| m.size())
|
.map(|m| m.size())
|
||||||
.and_then(|s| context.push_value(RuntimeValue::I64(s as i64)))
|
.and_then(|s| context.value_stack_mut().push(RuntimeValue::I64(s as i64)))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_grow_memory(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
fn run_grow_memory(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||||
let pages: u32 = context.pop_value_as()?;
|
let pages: u32 = context.value_stack_mut().pop_as()?;
|
||||||
context.module()
|
context.module()
|
||||||
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
.memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX))
|
||||||
.and_then(|m| m.grow(pages))
|
.and_then(|m| m.grow(pages))
|
||||||
.and_then(|m| context.push_value(RuntimeValue::I32(m as i32)))
|
.and_then(|m| context.value_stack_mut().push(RuntimeValue::I32(m as i32)))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_const(context: &mut FunctionContext, val: RuntimeValue) -> Result<InstructionOutcome, Error> {
|
fn run_const(context: &mut FunctionContext, val: RuntimeValue) -> Result<InstructionOutcome, Error> {
|
||||||
context
|
context
|
||||||
.push_value(val)
|
.value_stack_mut()
|
||||||
|
.push(val)
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_eqz<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_eqz<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: TryInto<T, Error>, T: PartialEq<T> + Default {
|
where RuntimeValue: TryInto<T, Error>, T: PartialEq<T> + Default {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| RuntimeValue::I32(if v == Default::default() { 1 } else { 0 }))
|
.map(|v| RuntimeValue::I32(if v == Default::default() { 1 } else { 0 }))
|
||||||
.and_then(|v| context.push_value(v))
|
.and_then(|v| context.value_stack_mut().push(v))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_eq<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_eq<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: TryInto<T, Error>, T: PartialEq<T> {
|
where RuntimeValue: TryInto<T, Error>, T: PartialEq<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| RuntimeValue::I32(if left == right { 1 } else { 0 }))
|
.map(|(left, right)| RuntimeValue::I32(if left == right { 1 } else { 0 }))
|
||||||
.and_then(|v| context.push_value(v))
|
.and_then(|v| context.value_stack_mut().push(v))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_ne<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_ne<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: TryInto<T, Error>, T: PartialEq<T> {
|
where RuntimeValue: TryInto<T, Error>, T: PartialEq<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| RuntimeValue::I32(if left != right { 1 } else { 0 }))
|
.map(|(left, right)| RuntimeValue::I32(if left != right { 1 } else { 0 }))
|
||||||
.and_then(|v| context.push_value(v))
|
.and_then(|v| context.value_stack_mut().push(v))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_lt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_lt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| RuntimeValue::I32(if left < right { 1 } else { 0 }))
|
.map(|(left, right)| RuntimeValue::I32(if left < right { 1 } else { 0 }))
|
||||||
.and_then(|v| context.push_value(v))
|
.and_then(|v| context.value_stack_mut().push(v))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_gt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_gt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| RuntimeValue::I32(if left > right { 1 } else { 0 }))
|
.map(|(left, right)| RuntimeValue::I32(if left > right { 1 } else { 0 }))
|
||||||
.and_then(|v| context.push_value(v))
|
.and_then(|v| context.value_stack_mut().push(v))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_lte<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_lte<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| RuntimeValue::I32(if left <= right { 1 } else { 0 }))
|
.map(|(left, right)| RuntimeValue::I32(if left <= right { 1 } else { 0 }))
|
||||||
.and_then(|v| context.push_value(v))
|
.and_then(|v| context.value_stack_mut().push(v))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_gte<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_gte<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| RuntimeValue::I32(if left >= right { 1 } else { 0 }))
|
.map(|(left, right)| RuntimeValue::I32(if left >= right { 1 } else { 0 }))
|
||||||
.and_then(|v| context.push_value(v))
|
.and_then(|v| context.value_stack_mut().push(v))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_clz<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_clz<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.leading_zeros())
|
.map(|v| v.leading_zeros())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_ctz<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_ctz<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.trailing_zeros())
|
.map(|v| v.trailing_zeros())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_popcnt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_popcnt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.count_ones())
|
.map(|v| v.count_ones())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_add<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_add<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: ArithmeticOps<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: ArithmeticOps<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| left.add(right))
|
.map(|(left, right)| left.add(right))
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sub<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_sub<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: ArithmeticOps<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: ArithmeticOps<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| left.sub(right))
|
.map(|(left, right)| left.sub(right))
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_mul<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_mul<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: ArithmeticOps<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: ArithmeticOps<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| left.mul(right))
|
.map(|(left, right)| left.mul(right))
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_div<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_div<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: TransmuteInto<U>, U: ArithmeticOps<U> + TransmuteInto<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: TransmuteInto<U>, U: ArithmeticOps<U> + TransmuteInto<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| (left.transmute_into(), right.transmute_into()))
|
.map(|(left, right)| (left.transmute_into(), right.transmute_into()))
|
||||||
.map(|(left, right)| left.div(right))
|
.map(|(left, right)| left.div(right))
|
||||||
.map(|v| v.transmute_into())
|
.map(|v| v.transmute_into())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_rem<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_rem<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: TransmuteInto<U>, U: Integer<U> + TransmuteInto<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: TransmuteInto<U>, U: Integer<U> + TransmuteInto<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| (left.transmute_into(), right.transmute_into()))
|
.map(|(left, right)| (left.transmute_into(), right.transmute_into()))
|
||||||
.map(|(left, right)| left.rem(right))
|
.map(|(left, right)| left.rem(right))
|
||||||
.map(|v| v.transmute_into())
|
.map(|v| v.transmute_into())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_and<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_and<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<<T as ops::BitAnd>::Output> + TryInto<T, Error>, T: ops::BitAnd<T> {
|
where RuntimeValue: From<<T as ops::BitAnd>::Output> + TryInto<T, Error>, T: ops::BitAnd<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| left.bitand(right))
|
.map(|(left, right)| left.bitand(right))
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_or<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_or<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<<T as ops::BitOr>::Output> + TryInto<T, Error>, T: ops::BitOr<T> {
|
where RuntimeValue: From<<T as ops::BitOr>::Output> + TryInto<T, Error>, T: ops::BitOr<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| left.bitor(right))
|
.map(|(left, right)| left.bitor(right))
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_xor<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_xor<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<<T as ops::BitXor>::Output> + TryInto<T, Error>, T: ops::BitXor<T> {
|
where RuntimeValue: From<<T as ops::BitXor>::Output> + TryInto<T, Error>, T: ops::BitXor<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| left.bitxor(right))
|
.map(|(left, right)| left.bitxor(right))
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_shl<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
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> {
|
where RuntimeValue: From<<T as ops::Shl<T>>::Output> + TryInto<T, Error>, T: ops::Shl<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| left.shl(right))
|
.map(|(left, right)| left.shl(right))
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_shr<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
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> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: TransmuteInto<U>, U: ops::Shr<U>, <U as ops::Shr<U>>::Output: TransmuteInto<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| (left.transmute_into(), right.transmute_into()))
|
.map(|(left, right)| (left.transmute_into(), right.transmute_into()))
|
||||||
.map(|(left, right)| left.shr(right))
|
.map(|(left, right)| left.shr(right))
|
||||||
.map(|v| v.transmute_into())
|
.map(|v| v.transmute_into())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_rotl<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_rotl<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| left.rotl(right))
|
.map(|(left, right)| left.rotl(right))
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_rotr<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_rotr<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Integer<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| left.rotr(right))
|
.map(|(left, right)| left.rotr(right))
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_abs<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_abs<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.abs())
|
.map(|v| v.abs())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_neg<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_neg<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<<T as ops::Neg>::Output> + TryInto<T, Error>, T: ops::Neg {
|
where RuntimeValue: From<<T as ops::Neg>::Output> + TryInto<T, Error>, T: ops::Neg {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.neg())
|
.map(|v| v.neg())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_ceil<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_ceil<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.ceil())
|
.map(|v| v.ceil())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_floor<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_floor<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.floor())
|
.map(|v| v.floor())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_trunc<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_trunc<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.trunc())
|
.map(|v| v.trunc())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_nearest<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_nearest<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.round())
|
.map(|v| v.round())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sqrt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_sqrt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.sqrt())
|
.map(|v| v.sqrt())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_min<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_min<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| left.min(right))
|
.map(|(left, right)| left.min(right))
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_max<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_max<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
where RuntimeValue: From<T> + TryInto<T, Error>, T: Float<T> {
|
||||||
context
|
context
|
||||||
.pop_value_pair_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_pair_as::<T>()
|
||||||
.map(|(left, right)| left.max(right))
|
.map(|(left, right)| left.max(right))
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,55 +777,54 @@ impl Interpreter {
|
|||||||
fn run_wrap<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_wrap<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<U> + TryInto<T, Error>, T: WrapInto<U> {
|
where RuntimeValue: From<U> + TryInto<T, Error>, T: WrapInto<U> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.wrap_into())
|
.map(|v| v.wrap_into())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_trunc_to_int<T, U, V>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_trunc_to_int<T, U, V>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<V> + TryInto<T, Error>, T: TryTruncateInto<U, Error>, U: TransmuteInto<V>, {
|
where RuntimeValue: From<V> + TryInto<T, Error>, T: TryTruncateInto<U, Error>, U: TransmuteInto<V>, {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.and_then(|v| v.try_truncate_into())
|
.and_then(|v| v.try_truncate_into())
|
||||||
.map(|v| v.transmute_into())
|
.map(|v| v.transmute_into())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_extend<T, U, V>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_extend<T, U, V>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<V> + TryInto<T, Error>, T: ExtendInto<U>, U: TransmuteInto<V> {
|
where RuntimeValue: From<V> + TryInto<T, Error>, T: ExtendInto<U>, U: TransmuteInto<V> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(|v| v.extend_into())
|
.map(|v| v.extend_into())
|
||||||
.map(|v| v.transmute_into())
|
.map(|v| v.transmute_into())
|
||||||
.map(|v| context.push_value(v.into()))
|
.map(|v| context.value_stack_mut().push(v.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_reinterpret<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
fn run_reinterpret<T, U>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||||
where RuntimeValue: From<U>, RuntimeValue: TryInto<T, Error>, T: TransmuteInto<U> {
|
where RuntimeValue: From<U>, RuntimeValue: TryInto<T, Error>, T: TransmuteInto<U> {
|
||||||
context
|
context
|
||||||
.pop_value_as::<T>()
|
.value_stack_mut()
|
||||||
|
.pop_as::<T>()
|
||||||
.map(TransmuteInto::transmute_into)
|
.map(TransmuteInto::transmute_into)
|
||||||
.and_then(|val| context.push_value(val.into()))
|
.and_then(|val| context.value_stack_mut().push(val.into()))
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_block(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
fn execute_block(context: &mut FunctionContext, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
||||||
debug_assert!(!context.frame_stack.is_empty());
|
debug_assert!(!context.frame_stack.is_empty());
|
||||||
|
|
||||||
// run instructions
|
// run instructions
|
||||||
context.position = 0;
|
context.position = 0;
|
||||||
loop {
|
loop {
|
||||||
// TODO: blocks ends with end => it should work with
|
|
||||||
// If the current position is now past the end of the sequence, function return
|
|
||||||
// execution is initiated and execution of the function is thereafter complete.
|
|
||||||
// if context.position == body_len {
|
|
||||||
// return Ok(InstructionOutcome::Next);
|
|
||||||
// }
|
|
||||||
let instruction = &body[context.position];
|
let instruction = &body[context.position];
|
||||||
println!("=== RUNNING {:?}", instruction);
|
|
||||||
|
println!("=== RUNNING {:?}", instruction); // TODO: trace
|
||||||
match Interpreter::run_instruction(context, instruction)? {
|
match Interpreter::run_instruction(context, instruction)? {
|
||||||
InstructionOutcome::RunInstruction => (),
|
InstructionOutcome::RunInstruction => (),
|
||||||
InstructionOutcome::RunNextInstruction => context.position += 1,
|
InstructionOutcome::RunNextInstruction => context.position += 1,
|
||||||
@ -827,20 +843,13 @@ println!("=== RUNNING {:?}", instruction);
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FunctionContext<'a> {
|
impl<'a> FunctionContext<'a> {
|
||||||
pub fn new(module: &'a mut ModuleInstance, value_stack: &'a mut VecDeque<RuntimeValue>, frame_stack: &'a mut VecDeque<BlockFrame>, function: &FunctionType, body: &[Opcode], args: &[RuntimeValue]) -> Result<Self, Error> {
|
pub fn new(module: &'a ModuleInstance, value_stack_limit: usize, frame_stack_limit: usize, function: &FunctionType, body: &[Opcode], args: Vec<VariableInstance>) -> Result<Self, Error> {
|
||||||
let mut context = FunctionContext {
|
let mut context = FunctionContext {
|
||||||
module: module,
|
module: module,
|
||||||
// The value stack begins empty.
|
return_type: function.return_type().map(|vt| BlockType::Value(vt)).unwrap_or(BlockType::NoResult),
|
||||||
value_stack: value_stack,
|
value_stack: StackWithLimit::with_limit(value_stack_limit),
|
||||||
// The control-flow stack begins with an entry holding a label bound to the last instruction in
|
frame_stack: StackWithLimit::with_limit(frame_stack_limit),
|
||||||
// the instruction sequence, a limit value of zero, and a signature corresponding to the function's
|
locals: args,
|
||||||
// return types:
|
|
||||||
// - If the function's return type sequence is empty, its signature is void.
|
|
||||||
// - If the function's return type sequence has exactly one element, the signature is that element.
|
|
||||||
frame_stack: frame_stack,
|
|
||||||
// The value of each incoming argument is copied to the local with the corresponding index, and the rest of the locals are initialized to all-zeros bit-pattern values.
|
|
||||||
locals: Vec::from(args),
|
|
||||||
// The current position starts at the first instruction in the function body.
|
|
||||||
position: 0,
|
position: 0,
|
||||||
};
|
};
|
||||||
context.push_frame(body.len() - 1, match function.return_type() {
|
context.push_frame(body.len() - 1, match function.return_type() {
|
||||||
@ -850,92 +859,57 @@ impl<'a> FunctionContext<'a> {
|
|||||||
Ok(context)
|
Ok(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn module(&mut self) -> &mut ModuleInstance {
|
pub fn module(&self) -> &ModuleInstance {
|
||||||
self.module
|
self.module
|
||||||
}
|
}
|
||||||
|
|
||||||
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, Error> {
|
||||||
self.locals.get_mut(index)
|
self.locals.get_mut(index)
|
||||||
.map(|local| *local = value)
|
|
||||||
.map(|_| InstructionOutcome::RunNextInstruction)
|
|
||||||
.ok_or(Error::Local(format!("expected to have local with index {}", index)))
|
.ok_or(Error::Local(format!("expected to have local with index {}", index)))
|
||||||
|
.and_then(|l| l.set(value))
|
||||||
|
.map(|_| InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(&mut self, index: usize) -> Result<&RuntimeValue, Error> {
|
pub fn get_local(&mut self, index: usize) -> Result<RuntimeValue, Error> {
|
||||||
self.locals.get(index)
|
self.locals.get(index)
|
||||||
.ok_or(Error::Local(format!("expected to have local with index {}", index)))
|
.ok_or(Error::Local(format!("expected to have local with index {}", index)))
|
||||||
|
.map(|l| l.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_value(&mut self, value: RuntimeValue) -> Result<(), Error> {
|
pub fn value_stack(&self) -> &StackWithLimit<RuntimeValue> {
|
||||||
self.value_stack.push_back(value);
|
&self.value_stack
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn top_value(&mut self) -> Result<RuntimeValue, Error> {
|
pub fn value_stack_mut(&mut self) -> &mut StackWithLimit<RuntimeValue> {
|
||||||
self.value_stack
|
&mut self.value_stack
|
||||||
.back()
|
|
||||||
.cloned()
|
|
||||||
.ok_or(Error::ValueStack("non-empty value stack expected".into() ))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_value(&mut self) -> Result<RuntimeValue, Error> {
|
pub fn frame_stack(&self) -> &StackWithLimit<BlockFrame> {
|
||||||
self.value_stack
|
&self.frame_stack
|
||||||
.pop_back()
|
|
||||||
.ok_or(Error::ValueStack("non-empty value stack expected".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop_value_as<T>(&mut self) -> Result<T, Error>
|
|
||||||
where RuntimeValue: TryInto<T, Error> {
|
|
||||||
self.pop_value()
|
|
||||||
.and_then(TryInto::try_into)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop_value_pair(&mut self) -> Result<(RuntimeValue, RuntimeValue), Error> {
|
|
||||||
let right = self.pop_value()?;
|
|
||||||
let left = self.pop_value()?;
|
|
||||||
Ok((left, right))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop_value_pair_as<T>(&mut self) -> Result<(T, T), Error>
|
|
||||||
where RuntimeValue: TryInto<T, Error> {
|
|
||||||
let right = self.pop_value_as()?;
|
|
||||||
let left = self.pop_value_as()?;
|
|
||||||
Ok((left, right))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop_value_triple(&mut self) -> Result<(RuntimeValue, RuntimeValue, RuntimeValue), Error> {
|
|
||||||
let right = self.pop_value()?;
|
|
||||||
let mid = self.pop_value()?;
|
|
||||||
let left = self.pop_value()?;
|
|
||||||
Ok((left, mid, right))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_frame(&mut self, position: usize, signature: BlockType) -> Result<(), Error> {
|
pub fn push_frame(&mut self, position: usize, signature: BlockType) -> Result<(), Error> {
|
||||||
self.frame_stack.push_back(BlockFrame {
|
self.frame_stack.push(BlockFrame {
|
||||||
position: position,
|
position: position,
|
||||||
value_limit: self.value_stack.len(),
|
value_limit: self.value_stack.len(),
|
||||||
signature: signature,
|
signature: signature,
|
||||||
});
|
})
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_frame(&mut self) -> Result<(), Error> {
|
pub fn pop_frame(&mut self) -> Result<(), Error> {
|
||||||
let frame = match self.frame_stack.pop_back() {
|
let frame = self.frame_stack.pop()?;
|
||||||
Some(frame) => frame,
|
|
||||||
None => return Err(Error::FrameStack("non-empty frame stack expected".into())),
|
|
||||||
};
|
|
||||||
|
|
||||||
if frame.value_limit > self.value_stack.len() {
|
if frame.value_limit > self.value_stack.len() {
|
||||||
return Err(Error::FrameStack("non-empty frame stack expected".into()));
|
return Err(Error::Stack("invalid stack len".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let frame_value = match frame.signature {
|
let frame_value = match frame.signature {
|
||||||
BlockType::Value(_) => Some(self.pop_value()?),
|
BlockType::Value(_) => Some(self.value_stack.pop()?),
|
||||||
BlockType::NoResult => None,
|
BlockType::NoResult => None,
|
||||||
};
|
};
|
||||||
self.value_stack.resize(frame.value_limit, RuntimeValue::I32(0));
|
self.value_stack.resize(frame.value_limit, RuntimeValue::I32(0));
|
||||||
self.position = frame.position;
|
self.position = frame.position;
|
||||||
if let Some(frame_value) = frame_value {
|
if let Some(frame_value) = frame_value {
|
||||||
self.push_value(frame_value)?;
|
self.value_stack.push(frame_value)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -972,14 +946,21 @@ fn from_little_endian_bytes<T>(buffer: &[u8]) -> T {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::super::super::elements::{ValueType, Opcodes, Opcode, BlockType, FunctionType};
|
use std::sync::Weak;
|
||||||
|
use elements::{Module, ValueType, Opcodes, Opcode, BlockType, FunctionType};
|
||||||
use interpreter::Error;
|
use interpreter::Error;
|
||||||
use interpreter::runner::Interpreter;
|
use interpreter::module::ModuleInstance;
|
||||||
|
use interpreter::runner::{Interpreter, FunctionContext};
|
||||||
use interpreter::value::{RuntimeValue, TryInto};
|
use interpreter::value::{RuntimeValue, TryInto};
|
||||||
|
use interpreter::variable::{VariableInstance, VariableType};
|
||||||
|
|
||||||
fn run_function_i32(body: &Opcodes, arg: i32) -> Result<i32, Error> {
|
fn run_function_i32(body: &Opcodes, arg: i32) -> Result<i32, Error> {
|
||||||
let function_type = FunctionType::new(vec![ValueType::I32], Some(ValueType::I32));
|
let ftype = FunctionType::new(vec![ValueType::I32], Some(ValueType::I32));
|
||||||
Interpreter::run_function(&function_type, body.elements(), &[RuntimeValue::I32(arg)])
|
let module = ModuleInstance::new(Weak::default(), Module::default()).unwrap();
|
||||||
|
let mut context = FunctionContext::new(&module, 1024, 1024, &ftype, body.elements(), vec![
|
||||||
|
VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(arg)).unwrap()
|
||||||
|
])?;
|
||||||
|
Interpreter::run_function(&mut context, body.elements())
|
||||||
.map(|v| v.unwrap().try_into().unwrap())
|
.map(|v| v.unwrap().try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
85
src/interpreter/stack.rs
Normal file
85
src/interpreter/stack.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
use interpreter::Error;
|
||||||
|
use interpreter::value::{RuntimeValue, TryInto};
|
||||||
|
|
||||||
|
/// Stack with limit.
|
||||||
|
pub struct StackWithLimit<T> where T: Clone {
|
||||||
|
/// Stack values.
|
||||||
|
values: VecDeque<T>,
|
||||||
|
/// Stack limit (maximal stack len).
|
||||||
|
limit: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> StackWithLimit<T> where T: Clone {
|
||||||
|
pub fn with_limit(limit: usize) -> Self {
|
||||||
|
StackWithLimit {
|
||||||
|
values: VecDeque::new(),
|
||||||
|
limit: limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.values.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.values.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn limit(&self) -> usize {
|
||||||
|
self.limit
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn top(&self) -> Result<&T, Error> {
|
||||||
|
self.values
|
||||||
|
.back()
|
||||||
|
.ok_or(Error::Stack("non-empty stack expected".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, value: T) -> Result<(), Error> {
|
||||||
|
if self.values.len() >= self.limit {
|
||||||
|
return Err(Error::Stack(format!("exceeded stack limit {}", self.limit)));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.values.push_back(value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Result<T, Error> {
|
||||||
|
self.values
|
||||||
|
.pop_back()
|
||||||
|
.ok_or(Error::Stack("non-empty stack expected".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, new_size: usize, dummy: T) {
|
||||||
|
debug_assert!(new_size <= self.values.len());
|
||||||
|
self.values.resize(new_size, dummy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackWithLimit<RuntimeValue> {
|
||||||
|
pub fn pop_as<T>(&mut self) -> Result<T, Error>
|
||||||
|
where RuntimeValue: TryInto<T, Error> {
|
||||||
|
self.pop().and_then(TryInto::try_into)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_pair(&mut self) -> Result<(RuntimeValue, RuntimeValue), Error> {
|
||||||
|
let right = self.pop()?;
|
||||||
|
let left = self.pop()?;
|
||||||
|
Ok((left, right))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_pair_as<T>(&mut self) -> Result<(T, T), Error>
|
||||||
|
where RuntimeValue: TryInto<T, Error> {
|
||||||
|
let right = self.pop_as()?;
|
||||||
|
let left = self.pop_as()?;
|
||||||
|
Ok((left, right))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_triple(&mut self) -> Result<(RuntimeValue, RuntimeValue, RuntimeValue), Error> {
|
||||||
|
let right = self.pop()?;
|
||||||
|
let mid = self.pop()?;
|
||||||
|
let left = self.pop()?;
|
||||||
|
Ok((left, mid, right))
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use elements::{GlobalType, ValueType};
|
use elements::{GlobalType, ValueType};
|
||||||
use interpreter::Error;
|
use interpreter::Error;
|
||||||
@ -20,6 +19,7 @@ pub enum VariableType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Variable instance.
|
/// Variable instance.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct VariableInstance {
|
pub struct VariableInstance {
|
||||||
/// Is mutable?
|
/// Is mutable?
|
||||||
is_mutable: bool,
|
is_mutable: bool,
|
||||||
@ -30,17 +30,20 @@ pub struct VariableInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VariableInstance {
|
impl VariableInstance {
|
||||||
pub fn new(global_type: &GlobalType, value: RuntimeValue) -> Result<Arc<Self>, Error> {
|
pub fn new(is_mutable: bool, variable_type: VariableType, value: RuntimeValue) -> Result<Self, Error> {
|
||||||
let variable_type: VariableType = global_type.content_type().into();
|
|
||||||
if !value.is_null() && value.variable_type() != Some(variable_type) {
|
if !value.is_null() && value.variable_type() != Some(variable_type) {
|
||||||
return Err(Error::Variable(format!("trying to initialize variable of type {:?} with value of type {:?}", variable_type, value.variable_type())));
|
return Err(Error::Variable(format!("trying to initialize variable of type {:?} with value of type {:?}", variable_type, value.variable_type())));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Arc::new(VariableInstance {
|
Ok(VariableInstance {
|
||||||
is_mutable: global_type.is_mutable(),
|
is_mutable: is_mutable,
|
||||||
variable_type: variable_type,
|
variable_type: variable_type,
|
||||||
value: RwLock::new(value),
|
value: RwLock::new(value),
|
||||||
}))
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_global(global_type: &GlobalType, value: RuntimeValue) -> Result<Self, Error> {
|
||||||
|
Self::new(global_type.is_mutable(), global_type.content_type().into(), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self) -> RuntimeValue {
|
pub fn get(&self) -> RuntimeValue {
|
||||||
|
Reference in New Issue
Block a user