Merge pull request #57 from NikVolf/plain_instruction_set

Plain instruction set
This commit is contained in:
Nikolay Volf 2017-06-23 14:24:07 +03:00 committed by GitHub
commit 0398726cd7
12 changed files with 582 additions and 793 deletions

View File

@ -7,16 +7,22 @@ use parity_wasm::builder;
pub fn inject_nop(opcodes: &mut elements::Opcodes) {
use parity_wasm::elements::Opcode::*;
for opcode in opcodes.elements_mut().iter_mut() {
match opcode {
&mut Block(_, ref mut block) | &mut If(_, ref mut block) => {
inject_nop(block)
},
_ => { }
let opcodes = opcodes.elements_mut();
let mut position = 0;
loop {
let need_inject = match &opcodes[position] {
&Block(_) | &If(_) => true,
_ => false,
};
if need_inject {
opcodes.insert(position + 1, Nop);
}
position += 1;
if position >= opcodes.len() {
break;
}
}
opcodes.elements_mut().insert(0, Nop);
}
fn main() {

View File

@ -33,12 +33,18 @@ impl Deserialize for Opcodes {
fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, Self::Error> {
let mut opcodes = Vec::new();
let mut block_count = 1usize;
loop {
let opcode = Opcode::deserialize(reader)?;
let is_terminal = opcode.is_terminal();
if opcode.is_terminal() {
block_count -= 1;
} else if opcode.is_block() {
block_count = block_count.checked_add(1).ok_or(Error::Other("too many opcodes"))?;
}
opcodes.push(opcode);
if is_terminal {
if block_count == 0 {
break;
}
}
@ -100,9 +106,9 @@ impl Deserialize for InitExpr {
pub enum Opcode {
Unreachable,
Nop,
Block(BlockType, Opcodes),
Loop(BlockType, Opcodes),
If(BlockType, Opcodes),
Block(BlockType),
Loop(BlockType),
If(BlockType),
Else,
End,
Br(u32),
@ -289,6 +295,14 @@ pub enum Opcode {
}
impl Opcode {
/// Is this opcode starts the new block (which should end with terminal opcode).
pub fn is_block(&self) -> bool {
match self {
&Opcode::Block(_) | &Opcode::Loop(_) | &Opcode::If(_) => true,
_ => false,
}
}
/// Is this opcode determines the termination of opcode sequence
/// `true` for `Opcode::End`
pub fn is_terminal(&self) -> bool {
@ -311,9 +325,9 @@ impl Deserialize for Opcode {
match val {
0x00 => Unreachable,
0x01 => Nop,
0x02 => Block(BlockType::deserialize(reader)?, Opcodes::deserialize(reader)?),
0x03 => Loop(BlockType::deserialize(reader)?, Opcodes::deserialize(reader)?),
0x04 => If(BlockType::deserialize(reader)?, Opcodes::deserialize(reader)?),
0x02 => Block(BlockType::deserialize(reader)?),
0x03 => Loop(BlockType::deserialize(reader)?),
0x04 => If(BlockType::deserialize(reader)?),
0x05 => Else,
0x0b => End,
@ -599,17 +613,14 @@ impl Serialize for Opcode {
match self {
Unreachable => op!(writer, 0x00),
Nop => op!(writer, 0x01),
Block(block_type, ops) => op!(writer, 0x02, {
Block(block_type) => op!(writer, 0x02, {
block_type.serialize(writer)?;
ops.serialize(writer)?;
}),
Loop(block_type, ops) => op!(writer, 0x03, {
Loop(block_type) => op!(writer, 0x03, {
block_type.serialize(writer)?;
ops.serialize(writer)?;
}),
If(block_type, ops) => op!(writer, 0x04, {
If(block_type) => op!(writer, 0x04, {
block_type.serialize(writer)?;
ops.serialize(writer)?;
}),
Else => op!(writer, 0x05),
End => op!(writer, 0x0b),
@ -926,20 +937,19 @@ impl Serialize for InitExpr {
#[test]
fn ifelse() {
// see if-else.wast/if-else.wasm
let opcode = super::deserialize_buffer::<Opcode>(vec![0x04, 0x7F, 0x41, 0x05, 0x05, 0x41, 0x07, 0x0B])
let opcode = super::deserialize_buffer::<Opcodes>(vec![0x04, 0x7F, 0x41, 0x05, 0x05, 0x41, 0x07, 0x0B, 0x0B])
.expect("valid hex of if instruction");
match opcode {
Opcode::If(_, ops) => {
let before_else = ops.elements().iter()
.take_while(|op| match **op { Opcode::Else => false, _ => true }).count();
let after_else = ops.elements().iter()
.skip_while(|op| match **op { Opcode::Else => false, _ => true })
.take_while(|op| match **op { Opcode::End => false, _ => true })
.count()
- 1; // minus Opcode::Else itself
assert_eq!(before_else, after_else);
},
_ => { panic!("Should be deserialized as if opcode"); }
let opcodes = opcode.elements();
match &opcodes[0] {
&Opcode::If(_) => (),
_ => panic!("Should be deserialized as if opcode"),
}
}
let before_else = opcodes.iter().skip(1)
.take_while(|op| match **op { Opcode::Else => false, _ => true }).count();
let after_else = opcodes.iter().skip(1)
.skip_while(|op| match **op { Opcode::Else => false, _ => true })
.take_while(|op| match **op { Opcode::End => false, _ => true })
.count()
- 1; // minus Opcode::Else itself
assert_eq!(before_else, after_else);
}

View File

@ -1016,13 +1016,9 @@ mod tests {
FuncBody::new(
vec![Local::new(1, ValueType::I32)],
Opcodes::new(vec![
Block(
BlockType::Value(ValueType::I32),
Opcodes::new(vec![
GetGlobal(0),
End
])
),
Block(BlockType::Value(ValueType::I32)),
GetGlobal(0),
End,
End,
])
)

View File

@ -92,10 +92,6 @@ impl EnvModuleInstance {
}
impl ModuleInstanceInterface for EnvModuleInstance {
fn instantiate<'a>(&self, is_user_module: bool, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error> {
self.instance.instantiate(is_user_module, externals)
}
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
self.instance.execute_index(index, params)
}

View File

@ -110,10 +110,6 @@ impl<'a> NativeModuleInstance<'a> {
}
impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> {
fn instantiate<'b>(&self, is_user_module: bool, externals: Option<&'b HashMap<String, Arc<ModuleInstanceInterface + 'b>>>) -> Result<(), Error> {
self.env.instantiate(is_user_module, externals)
}
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
self.env.execute_index(index, params)
}

View File

@ -41,8 +41,6 @@ pub enum ExportEntryType {
/// Module instance API.
pub trait ModuleInstanceInterface {
/// Run instantiation-time procedures (validation and start function [if any] call). Module is not completely validated until this call.
fn instantiate<'a>(&self, is_user_module: bool, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error>;
/// Execute function with the given index.
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error>;
/// Execute function with the given export name.
@ -86,6 +84,8 @@ pub struct ModuleInstance {
name: String,
/// Module.
module: Module,
/// Function labels.
functions_labels: HashMap<u32, HashMap<usize, usize>>,
/// Module imports.
imports: ModuleImports,
/// Tables.
@ -129,6 +129,8 @@ pub struct InternalFunction<'a> {
pub locals: &'a [Local],
/// Function body.
pub body: &'a [Opcode],
/// Function labels.
pub labels: &'a HashMap<usize, usize>,
}
impl<'a> ExecutionParams<'a> {
@ -199,38 +201,15 @@ impl ModuleInstance {
name: name,
module: module,
imports: imports,
functions_labels: HashMap::new(),
memory: memory,
tables: tables,
globals: globals,
})
}
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"),
ItemIndex::Internal(index) => self.module.function_section()
.ok_or(Error::Function(format!("missing internal function {}", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Function(format!("missing internal function {}", index))))
.map(|f| f.type_ref()),
ItemIndex::External(index) => self.module.import_section()
.ok_or(Error::Function(format!("missing external function {}", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Function(format!("missing external function {}", index))))
.and_then(|import| match import.external() {
&External::Function(type_idx) => Ok(type_idx),
_ => Err(Error::Function(format!("external function {} is pointing to non-function import", index))),
}),
}
}
}
impl ModuleInstanceInterface for ModuleInstance {
fn instantiate<'a>(&self, is_user_module: bool, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error> {
/// Run instantiation-time procedures (validation). Module is not completely validated until this call.
pub fn instantiate<'a>(&mut self, is_user_module: bool, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error> {
// validate start section
if let Some(start_function) = self.module.start_section() {
let func_type_index = self.require_function(ItemIndex::IndexSpace(start_function))?;
@ -335,23 +314,27 @@ impl ModuleInstanceInterface for ModuleInstance {
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)));
let mut context = FunctionValidationContext::new(
&self.module,
&self.imports,
&locals,
DEFAULT_VALUE_STACK_LIMIT,
DEFAULT_FRAME_STACK_LIMIT,
&function_type);
let block_type = function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult);
Validator::validate_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))
} else {
e
}
})?;
let function_labels = {
let mut context = FunctionValidationContext::new(
self,
&locals,
DEFAULT_VALUE_STACK_LIMIT,
DEFAULT_FRAME_STACK_LIMIT,
&function_type);
let block_type = function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult);
Validator::validate_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))
} else {
e
}
})?;
context.function_labels()
};
self.functions_labels.insert(index as u32, function_labels);
}
}
@ -381,14 +364,43 @@ impl ModuleInstanceInterface for ModuleInstance {
}
}
Ok(())
}
/// Run start function [if any].
pub fn run_start_function(&self) -> Result<(), Error> {
// execute start function (if any)
if let Some(start_function) = self.module.start_section() {
self.execute_index(start_function, ExecutionParams::default())?;
}
Ok(())
}
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"),
ItemIndex::Internal(index) => self.module.function_section()
.ok_or(Error::Function(format!("missing internal function {}", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Function(format!("missing internal function {}", index))))
.map(|f| f.type_ref()),
ItemIndex::External(index) => self.module.import_section()
.ok_or(Error::Function(format!("missing external function {}", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Function(format!("missing external function {}", index))))
.and_then(|import| match import.external() {
&External::Function(type_idx) => Ok(type_idx),
_ => Err(Error::Function(format!("external function {} is pointing to non-function import", index))),
}),
}
}
}
impl ModuleInstanceInterface for ModuleInstance {
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
let ExecutionParams { args, externals } = params;
let mut args = StackWithLimit::with_data(args, DEFAULT_VALUE_STACK_LIMIT);
@ -549,18 +561,22 @@ impl ModuleInstanceInterface for ModuleInstance {
.and_then(|s| s.bodies()
.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()))))?;
let function_labels = self.functions_labels.get(&internal_index)
.ok_or(Error::Function(format!("trying to call non-validated internal function {}", internal_index)))?;
Ok(Some(InternalFunction {
locals: function_body.locals(),
body: function_body.code().elements(),
labels: function_labels,
}))
}
fn call_internal_function(&self, mut outer: CallerContext, index: u32) -> Result<Option<RuntimeValue>, Error> {
let function_labels = self.functions_labels.get(&index).ok_or(Error::Function(format!("trying to call non-validated internal function {}", index)))?;
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);
let inner = FunctionContext::new(function_ref, outer.externals, function_labels.clone(), outer.value_stack_limit, outer.frame_stack_limit, &function_type, args);
Interpreter::run_function(inner)
}
}

View File

@ -33,30 +33,13 @@ 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)?);
self.essence.modules.write().insert(name.into(), module_instance.clone());
// 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)
}
}
}
let mut module_instance = ModuleInstance::new(Arc::downgrade(&self.essence), name.into(), module)?;
module_instance.instantiate(true, externals)?;
/// 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)?);
let module_instance = Arc::new(module_instance);
self.essence.modules.write().insert(name.into(), module_instance.clone());
// 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)
}
}
module_instance.run_start_function()?;
Ok(module_instance)
}
/// Insert instantiated module.

View File

@ -1,6 +1,6 @@
use std::mem;
use std::ops;
use std::u32;
use std::{u32, usize};
use std::sync::Arc;
use std::fmt::{self, Display};
use std::iter::repeat;
@ -13,6 +13,7 @@ use interpreter::value::{
RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto,
ArithmeticOps, Integer, Float, LittleEndianConvert, TransmuteInto,
};
use interpreter::validator::{BlockFrame, BlockFrameType};
use interpreter::variable::VariableInstance;
/// Index of default linear memory.
@ -31,6 +32,8 @@ pub struct FunctionContext<'a> {
pub function: InternalFunctionReference<'a>,
/// Execution-local external modules.
pub externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>,
/// Function labels.
pub function_labels: HashMap<usize, usize>,
/// Function return type.
pub return_type: BlockType,
/// Local variables.
@ -46,14 +49,10 @@ pub struct FunctionContext<'a> {
/// 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.
@ -62,36 +61,6 @@ pub enum InstructionOutcome<'a> {
Return,
}
/// Control stack frame.
#[derive(Debug, Clone)]
pub struct BlockFrame {
/// 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.
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.
value_limit: usize,
/// A signature, which is a block signature type indicating the number and types of result values of the region.
signature: BlockType,
}
/// 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,
}
/// Function run result.
enum RunResult<'a> {
/// Function has returned (optional) value.
@ -114,8 +83,8 @@ impl Interpreter {
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)?;
function_context.initialize(function_body.locals, function_body.labels.clone())?;
function_context.push_frame(BlockFrameType::Function, return_type)?;
}
Interpreter::do_run_function(&mut function_context, function_body.body)?
@ -150,88 +119,50 @@ impl Interpreter {
}
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);
}
let non_empty_stack_reason = "body_stack contains entry for every frame_stack; frame_stack is not empty; qed";
loop {
let block_result = Interpreter::run_block_instructions(function_context, body_stack.back().expect(non_empty_stack_reason))?;
let instruction = &function_body[function_context.position];
match block_result {
InstructionOutcome::RunInstruction | InstructionOutcome::RunNextInstruction => unreachable!("managed by run_block_instructions"),
debug!(target: "interpreter", "running {:?}", instruction);
match Interpreter::run_instruction(function_context, instruction)? {
InstructionOutcome::RunNextInstruction => function_context.position += 1,
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,
}));
break;
}
},
InstructionOutcome::ExecuteBlock => {
function_context.position = 0;
let top_frame = function_context.frame_stack().top().expect(non_empty_stack_reason);
let instruction = &body_stack.back().expect(non_empty_stack_reason)[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));
function_context.position += 1;
return Ok(RunResult::NestedCall(function_context.nested(func_ref)?));
},
InstructionOutcome::End => {
context.pop_frame(false)?;
return Ok(InstructionOutcome::End);
if function_context.frame_stack().is_empty() {
break;
}
},
InstructionOutcome::Return => return Ok(InstructionOutcome::Return),
InstructionOutcome::Return => break,
}
}
Ok(RunResult::Return(match function_context.return_type {
BlockType::Value(_) => Some(function_context.value_stack_mut().pop()?),
BlockType::NoResult => None,
}))
}
fn run_instruction<'a, 'b>(context: &'b mut FunctionContext<'a>, opcode: &Opcode) -> Result<InstructionOutcome<'a>, Error> {
fn run_instruction<'a>(context: &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, _) => 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::Block(block_type) => Interpreter::run_block(context, block_type),
&Opcode::Loop(block_type) => Interpreter::run_loop(context, block_type),
&Opcode::If(block_type) => Interpreter::run_if(context, block_type),
&Opcode::Else => Interpreter::run_else(context),
&Opcode::End => Interpreter::run_end(context),
&Opcode::Br(idx) => Interpreter::run_br(context, idx),
@ -419,26 +350,6 @@ impl Interpreter {
}
}
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 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 frame_type == BlockFrameType::IfTrue {
(0, else_index + 1)
} else {
(else_index + 1, body_len)
};
&body[begin_index..end_index]
}
fn run_unreachable<'a>(_context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
Err(Error::Trap("programmatic".into()))
}
@ -447,37 +358,40 @@ impl Interpreter {
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_block<'a>(context: &mut FunctionContext<'a>, block_type: BlockType) -> Result<InstructionOutcome<'a>, Error> {
context.push_frame(BlockFrameType::Block, block_type)?;
Ok(InstructionOutcome::RunNextInstruction)
}
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_loop<'a>(context: &mut FunctionContext<'a>, block_type: BlockType) -> Result<InstructionOutcome<'a>, Error> {
context.push_frame(BlockFrameType::Loop, block_type)?;
Ok(InstructionOutcome::RunNextInstruction)
}
fn run_if<'a, 'b>(context: &'b mut FunctionContext<'a>, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome<'a>, Error> {
fn run_if<'a>(context: &mut FunctionContext<'a>, block_type: BlockType) -> 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);
let block_frame_type = if branch { BlockFrameType::IfTrue } else {
let else_pos = context.function_labels[&context.position];
if !context.function_labels.contains_key(&else_pos) {
context.position = else_pos;
return Ok(InstructionOutcome::RunNextInstruction);
}
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)
}
context.position = else_pos;
BlockFrameType::IfFalse
};
context.push_frame(block_frame_type, block_type).map(|_| InstructionOutcome::RunNextInstruction)
}
fn run_else<'a>(_context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
Ok(InstructionOutcome::End)
fn run_else<'a>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
let end_pos = context.function_labels[&context.position];
context.pop_frame(false)?;
context.position = end_pos;
Ok(InstructionOutcome::RunNextInstruction)
}
fn run_end<'a>(_context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
fn run_end<'a>(context: &mut FunctionContext) -> Result<InstructionOutcome<'a>, Error> {
context.pop_frame(false)?;
Ok(InstructionOutcome::End)
}
@ -502,11 +416,11 @@ impl Interpreter {
Ok(InstructionOutcome::Return)
}
fn run_call<'a, 'b>(context: &'b mut FunctionContext<'a>, func_idx: u32) -> Result<InstructionOutcome<'a>, Error> where 'a: 'b {
fn run_call<'a>(context: &mut FunctionContext<'a>, func_idx: u32) -> Result<InstructionOutcome<'a>, Error> {
Ok(InstructionOutcome::ExecuteCall(context.module().function_reference(ItemIndex::IndexSpace(func_idx), Some(context.externals))?))
}
fn run_call_indirect<'a, 'b>(context: &'b mut FunctionContext<'a>, type_idx: u32) -> Result<InstructionOutcome<'a>, Error> {
fn run_call_indirect<'a>(context: &mut FunctionContext<'a>, type_idx: u32) -> Result<InstructionOutcome<'a>, Error> {
let table_func_idx: u32 = context.value_stack_mut().pop_as()?;
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)?;
@ -1017,11 +931,12 @@ impl Interpreter {
}
impl<'a> FunctionContext<'a> {
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 {
pub fn new(function: InternalFunctionReference<'a>, externals: &'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>, function_labels: HashMap<usize, usize>, value_stack_limit: usize, frame_stack_limit: usize, function_type: &FunctionType, args: Vec<VariableInstance>) -> Self {
FunctionContext {
is_initialized: false,
function: function,
externals: externals,
function_labels: function_labels,
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),
@ -1039,6 +954,7 @@ impl<'a> FunctionContext<'a> {
is_initialized: false,
function: function,
externals: self.externals,
function_labels: HashMap::new(),
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()),
@ -1051,7 +967,7 @@ impl<'a> FunctionContext<'a> {
self.is_initialized
}
pub fn initialize(&mut self, locals: &[Local]) -> Result<(), Error> {
pub fn initialize(&mut self, locals: &[Local], labels: HashMap<usize, usize>) -> Result<(), Error> {
debug_assert!(!self.is_initialized);
self.is_initialized = true;
@ -1060,6 +976,7 @@ impl<'a> FunctionContext<'a> {
.map(|vt| VariableInstance::new(true, vt, RuntimeValue::default(vt)))
.collect::<Result<Vec<_>, _>>()?;
self.locals.extend(locals);
self.function_labels = labels;
Ok(())
}
@ -1100,14 +1017,31 @@ impl<'a> FunctionContext<'a> {
&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> {
pub fn push_frame(&mut self, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> {
let begin_position = self.position;
let branch_position = match frame_type {
BlockFrameType::Function => usize::MAX,
BlockFrameType::Loop => begin_position,
BlockFrameType::IfTrue => {
let else_pos = self.function_labels[&begin_position];
1usize + match self.function_labels.get(&else_pos) {
Some(end_pos) => *end_pos,
None => else_pos,
}
},
_ => self.function_labels[&begin_position] + 1,
};
let end_position = match frame_type {
BlockFrameType::Function => usize::MAX,
_ => self.function_labels[&begin_position] + 1,
};
self.frame_stack.push(BlockFrame {
frame_type: frame_type,
block_type: block_type,
begin_position: begin_position,
branch_position: branch_position,
end_position: end_position,
value_limit: self.value_stack.len(),
signature: signature,
value_stack_len: self.value_stack.len(),
})
}
@ -1118,15 +1052,15 @@ impl<'a> FunctionContext<'a> {
pub fn pop_frame(&mut self, is_branch: bool) -> Result<(), Error> {
let frame = self.frame_stack.pop()?;
if frame.value_limit > self.value_stack.len() {
if frame.value_stack_len > self.value_stack.len() {
return Err(Error::Stack("invalid stack len".into()));
}
let frame_value = match frame.signature {
let frame_value = match frame.block_type {
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));
self.value_stack.resize(frame.value_stack_len, RuntimeValue::I32(0));
self.position = if is_branch { frame.branch_position } else { frame.end_position };
if let Some(frame_value) = frame_value {
self.value_stack.push(frame_value)?;

View File

@ -29,6 +29,11 @@ impl TableInstance {
}))
}
/// Get variable type for this table.
pub fn variable_type(&self) -> VariableType {
self.variable_type
}
/// Get the specific value in the table
pub fn get(&self, offset: u32) -> Result<RuntimeValue, Error> {
let buffer = self.buffer.read();

View File

@ -6,9 +6,8 @@ use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, Global
InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType};
use interpreter::Error;
use interpreter::env_native::{env_native_module, UserFunction, UserFunctions, UserFunctionExecutor, UserFunctionDescriptor};
use interpreter::imports::ModuleImports;
use interpreter::memory::MemoryInstance;
use interpreter::module::{ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType};
use interpreter::module::{ModuleInstance, ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType};
use interpreter::program::ProgramInstance;
use interpreter::validator::{FunctionValidationContext, Validator};
use interpreter::value::RuntimeValue;
@ -82,7 +81,6 @@ fn wrong_import() {
fn global_get_set() {
let module = module()
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(42)])))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(777)])))
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
@ -94,33 +92,11 @@ fn global_get_set() {
Opcode::End,
])).build()
.build()
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::GetGlobal(1),
Opcode::I32Const(8),
Opcode::I32Add,
Opcode::SetGlobal(1),
Opcode::GetGlobal(1),
Opcode::End,
])).build()
.build()
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::I64Const(8),
Opcode::SetGlobal(0),
Opcode::GetGlobal(0),
Opcode::End,
])).build()
.build()
.build();
let program = ProgramInstance::new().unwrap();
let module = program.add_module_without_validation("main", module, None).unwrap(); // validation is failing (accessing immutable global)
let module = program.add_module("main", module, None).unwrap();
assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(50));
assert_eq!(module.execute_index(1, vec![].into()).unwrap_err(), Error::Variable("trying to update immutable variable".into()));
assert_eq!(module.execute_index(2, vec![].into()).unwrap_err(), Error::Variable("trying to update variable of type I32 with value of type Some(I64)".into()));
}
const SIGNATURE_I32: &'static [ValueType] = &[ValueType::I32];
@ -249,23 +225,20 @@ fn env_native_export_entry_type_check() {
#[test]
fn if_else_with_return_type_validation() {
let module = module().build();
let imports = ModuleImports::new(Weak::default(), None);
let mut context = FunctionValidationContext::new(&module, &imports, &[], 1024, 1024, &FunctionType::default());
let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap();
let mut context = FunctionValidationContext::new(&module_instance, &[], 1024, 1024, &FunctionType::default());
Validator::validate_function(&mut context, BlockType::NoResult, &[
Opcode::I32Const(1),
Opcode::If(BlockType::NoResult, Opcodes::new(vec![
Opcode::If(BlockType::NoResult),
Opcode::I32Const(1),
Opcode::If(BlockType::Value(ValueType::I32), Opcodes::new(vec![
Opcode::If(BlockType::Value(ValueType::I32)),
Opcode::I32Const(1),
Opcode::Else,
Opcode::Else,
Opcode::I32Const(2),
Opcode::End,
])),
Opcode::Drop,
Opcode::End,
])),
Opcode::Drop,
Opcode::End,
Opcode::End,
]).unwrap();
}

View File

@ -55,13 +55,11 @@ fn nop() {
#[test]
fn expr_block() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Block(BlockType::Value(ValueType::I32), // mark block
Opcodes::new(vec![
Opcode::I32Const(10), // [10]
Opcode::Drop,
Opcode::I32Const(1), // [1]
Opcode::End,
])),
Opcode::Block(BlockType::Value(ValueType::I32)),
Opcode::I32Const(10), // [10]
Opcode::Drop,
Opcode::I32Const(1), // [1]
Opcode::End,
Opcode::End]));
assert_eq!(run_function_i32(&module, 0).unwrap(), 1);
@ -71,26 +69,23 @@ fn expr_block() {
#[test]
fn loop_test() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Loop(BlockType::NoResult, // loop
Opcodes::new(vec![
Opcode::GetLocal(1), // [local1]
Opcode::GetLocal(0), // [local1, arg]
Opcode::I32Add, // [arg + local1]
Opcode::SetLocal(1), // [] + local1 = arg + local1
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(1), // [arg, 1]
Opcode::I32Add, // [arg + 1]
Opcode::SetLocal(0), // [] + arg = arg + 1
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(5), // [arg, 5]
Opcode::I32LtS, // [arg < 5]
Opcode::If(BlockType::NoResult,
Opcodes::new(vec![
Opcode::Br(1), // break loop
Opcode::End,
])),
Opcode::End])), // end loop
Opcode::GetLocal(1), // [local1]
Opcode::Loop(BlockType::NoResult), // loop
Opcode::GetLocal(1), // [local1]
Opcode::GetLocal(0), // [local1, arg]
Opcode::I32Add, // [arg + local1]
Opcode::SetLocal(1), // [] + local1 = arg + local1
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(1), // [arg, 1]
Opcode::I32Add, // [arg + 1]
Opcode::SetLocal(0), // [] + arg = arg + 1
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(5), // [arg, 5]
Opcode::I32LtS, // [arg < 5]
Opcode::If(BlockType::NoResult),
Opcode::Br(1), // break loop
Opcode::End,
Opcode::End, // end loop
Opcode::GetLocal(1), // [local1]
Opcode::End]));
assert_eq!(run_function_i32(&module, 0).unwrap(), 10);
@ -103,23 +98,19 @@ fn if_1() {
Opcode::I32Const(0), // [0]
Opcode::SetLocal(0), // [] + arg = 0
Opcode::I32Const(1), // [1]
Opcode::If(BlockType::NoResult, // if 1
Opcodes::new(vec![
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(1), // [arg, 1]
Opcode::I32Add, // [arg + 1]
Opcode::SetLocal(0), // [] + arg = arg + 1
Opcode::End, // end if
])),
Opcode::If(BlockType::NoResult), // if 1
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(1), // [arg, 1]
Opcode::I32Add, // [arg + 1]
Opcode::SetLocal(0), // [] + arg = arg + 1
Opcode::End, // end if
Opcode::I32Const(0), // [0]
Opcode::If(BlockType::NoResult, // if 0
Opcodes::new(vec![
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(1), // [arg, 1]
Opcode::I32Add, // [arg + 1]
Opcode::SetLocal(0), // [] + arg = arg + 1
Opcode::End, // end if
])),
Opcode::If(BlockType::NoResult), // if 0
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(1), // [arg, 1]
Opcode::I32Add, // [arg + 1]
Opcode::SetLocal(0), // [] + arg = arg + 1
Opcode::End, // end if
Opcode::GetLocal(0), // [arg]
Opcode::End]));
@ -131,25 +122,21 @@ fn if_1() {
fn if_2() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::I32Const(1), // [1]
Opcode::If(BlockType::NoResult, // if 1
Opcodes::new(vec![
Opcode::I32Const(1), // [1]
Opcode::SetLocal(0), // [] + arg = 1
Opcode::Else, // else
Opcode::I32Const(2), // [2]
Opcode::SetLocal(0), // [] + arg = 2
Opcode::End, // end if
])),
Opcode::If(BlockType::NoResult), // if 1
Opcode::I32Const(1), // [1]
Opcode::SetLocal(0), // [] + arg = 1
Opcode::Else, // else
Opcode::I32Const(2), // [2]
Opcode::SetLocal(0), // [] + arg = 2
Opcode::End, // end if
Opcode::I32Const(0), // [0]
Opcode::If(BlockType::NoResult, // if 0
Opcodes::new(vec![
Opcode::I32Const(4), // [4]
Opcode::SetLocal(1), // [] + local1 = 4
Opcode::Else, // else
Opcode::I32Const(8), // [8]
Opcode::SetLocal(1), // [] + local1 = 8
Opcode::End, // end if
])),
Opcode::If(BlockType::NoResult), // if 0
Opcode::I32Const(4), // [4]
Opcode::SetLocal(1), // [] + local1 = 4
Opcode::Else, // else
Opcode::I32Const(8), // [8]
Opcode::SetLocal(1), // [] + local1 = 8
Opcode::End, // end if
Opcode::GetLocal(0), // [arg]
Opcode::GetLocal(1), // [arg, local1]
Opcode::I32Add, // [arg + local1]
@ -165,13 +152,11 @@ fn expr_if() {
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(0), // [arg, 0]
Opcode::I32Eq, // [arg == 0]
Opcode::If(BlockType::Value(ValueType::I32), // if arg == 0
Opcodes::new(vec![
Opcode::I32Const(1), // [1]
Opcode::Else, // else
Opcode::I32Const(2), // [2]
Opcode::End, // end if
])),
Opcode::If(BlockType::Value(ValueType::I32)), // if arg == 0
Opcode::I32Const(1), // [1]
Opcode::Else, // else
Opcode::I32Const(2), // [2]
Opcode::End, // end if
Opcode::End]));
assert_eq!(run_function_i32(&module, 0).unwrap(), 1);
@ -182,23 +167,17 @@ fn expr_if() {
#[test]
fn nested_if() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Block(BlockType::NoResult,
Opcodes::new(vec![
Opcode::I32Const(1),
Opcode::If(BlockType::NoResult,
Opcodes::new(vec![
Opcode::I32Const(2),
Opcode::Drop,
Opcode::I32Const(3),
Opcode::If(BlockType::NoResult,
Opcodes::new(vec![
Opcode::Br(2),
Opcode::End,
])),
Opcode::End,
])),
Opcode::Block(BlockType::NoResult),
Opcode::I32Const(1),
Opcode::If(BlockType::NoResult),
Opcode::I32Const(2),
Opcode::Drop,
Opcode::I32Const(3),
Opcode::If(BlockType::NoResult),
Opcode::Br(2),
Opcode::End,
])),
Opcode::End,
Opcode::End,
Opcode::I32Const(4),
Opcode::End]));
@ -209,18 +188,14 @@ fn nested_if() {
#[test]
fn br_0() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Block(BlockType::NoResult, // mark block
Opcodes::new(vec![
Opcode::I32Const(1), // [1]
Opcode::If(BlockType::NoResult, // if 1
Opcodes::new(vec![
Opcode::Br(1), // break from block
Opcode::End, // end if
])),
Opcode::I32Const(1), // [1]
Opcode::SetLocal(0), // [] + arg = 1
Opcode::End, // end block
])),
Opcode::Block(BlockType::NoResult), // mark block
Opcode::I32Const(1), // [1]
Opcode::If(BlockType::NoResult), // if 1
Opcode::Br(1), // break from block
Opcode::End, // end if
Opcode::I32Const(1), // [1]
Opcode::SetLocal(0), // [] + arg = 1
Opcode::End, // end block
Opcode::I32Const(1), // [1]
Opcode::SetLocal(1), // [] + local1 = 1
Opcode::GetLocal(0), // [arg]
@ -239,24 +214,18 @@ fn br_0() {
#[test]
fn br_1() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Block(BlockType::NoResult, // block1
Opcodes::new(vec![
Opcode::Block(BlockType::NoResult, // block2
Opcodes::new(vec![
Opcode::I32Const(1), // [1]
Opcode::If(BlockType::NoResult, // if 1
Opcodes::new(vec![
Opcode::Br(2), // break from block2
Opcode::End, // end if
])),
Opcode::I32Const(1), // [1]
Opcode::SetLocal(0), // [] + arg = 1
Opcode::End, // end (block2)
])),
Opcode::I32Const(1), // [1]
Opcode::SetLocal(1), // [] + local1 = 1
Opcode::End, // end (block1)
])),
Opcode::Block(BlockType::NoResult), // block1
Opcode::Block(BlockType::NoResult), // block2
Opcode::I32Const(1), // [1]
Opcode::If(BlockType::NoResult), // if 1
Opcode::Br(2), // break from block2
Opcode::End, // end if
Opcode::I32Const(1), // [1]
Opcode::SetLocal(0), // [] + arg = 1
Opcode::End, // end (block2)
Opcode::I32Const(1), // [1]
Opcode::SetLocal(1), // [] + local1 = 1
Opcode::End, // end (block1)
Opcode::I32Const(1), // [1]
Opcode::SetLocal(2), // [] + local2 = 1
Opcode::GetLocal(0), // [arg]
@ -279,22 +248,16 @@ fn br_1() {
#[test]
fn br_2() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Block(BlockType::NoResult, // block1
Opcodes::new(vec![
Opcode::Block(BlockType::NoResult, // block2
Opcodes::new(vec![
Opcode::I32Const(1), // [1]
Opcode::If(BlockType::NoResult, // if 1
Opcodes::new(vec![
Opcode::Br(2), // break from block2
Opcode::End, // end if
])),
Opcode::I32Const(1), // [1]
Opcode::Return, // return 1
Opcode::End, // end (block2)
])),
Opcode::End, // end (block1)
])),
Opcode::Block(BlockType::NoResult), // block1
Opcode::Block(BlockType::NoResult), // block2
Opcode::I32Const(1), // [1]
Opcode::If(BlockType::NoResult), // if 1
Opcode::Br(2), // break from block2
Opcode::End, // end if
Opcode::I32Const(1), // [1]
Opcode::Return, // return 1
Opcode::End, // end (block2)
Opcode::End, // end (block1)
Opcode::I32Const(2), // [2]
Opcode::Return, // return 2
Opcode::End]));
@ -306,37 +269,29 @@ fn br_2() {
#[test]
fn br_3() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Block(BlockType::NoResult, // block1
Opcodes::new(vec![
Opcode::Loop(BlockType::NoResult, // loop
Opcodes::new(vec![
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(1), // [arg, 1]
Opcode::I32Add, // [arg + 1]
Opcode::SetLocal(0), // [] + arg = arg + 1
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(5), // [arg, 5]
Opcode::I32GeS, // [5 >= arg]
Opcode::If(BlockType::NoResult, // if 5 >= arg
Opcodes::new(vec![
Opcode::Br(2), // break from block1
Opcode::End, // end
])),
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(4), // [arg, 4]
Opcode::I32Eq, // [arg == 4]
Opcode::If(BlockType::NoResult, // if arg == 4
Opcodes::new(vec![
Opcode::Br(1), // break from loop
Opcode::End, // end
])),
Opcode::GetLocal(0), // [arg]
Opcode::SetLocal(1), // [] + local1 = arg
Opcode::Br(0), // continue loop
Opcode::End, // end (loop)
])),
Opcode::End, // end (block1)
])),
Opcode::Block(BlockType::NoResult), // block1
Opcode::Loop(BlockType::NoResult), // loop
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(1), // [arg, 1]
Opcode::I32Add, // [arg + 1]
Opcode::SetLocal(0), // [] + arg = arg + 1
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(5), // [arg, 5]
Opcode::I32GeS, // [5 >= arg]
Opcode::If(BlockType::NoResult), // if 5 >= arg
Opcode::Br(2), // break from block1
Opcode::End, // end
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(4), // [arg, 4]
Opcode::I32Eq, // [arg == 4]
Opcode::If(BlockType::NoResult), // if arg == 4
Opcode::Br(1), // break from loop
Opcode::End, // end
Opcode::GetLocal(0), // [arg]
Opcode::SetLocal(1), // [] + local1 = arg
Opcode::Br(0), // continue loop
Opcode::End, // end (loop)
Opcode::End, // end (block1)
Opcode::GetLocal(1), // [local1]
Opcode::Return, // return local1
Opcode::End]));
@ -348,20 +303,16 @@ fn br_3() {
#[test]
fn expr_br() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Block(BlockType::Value(ValueType::I32), // block1
Opcodes::new(vec![
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(0), // [arg, 0]
Opcode::I32Eq, // [arg == 0]
Opcode::If(BlockType::NoResult, // if arg == 0
Opcodes::new(vec![
Opcode::I32Const(1), // [1]
Opcode::Br(1), // break from block1
Opcode::End, // end (if)
])),
Opcode::I32Const(2), // [2]
Opcode::End, // end (block1)
])),
Opcode::Block(BlockType::Value(ValueType::I32)), // block1
Opcode::GetLocal(0), // [arg]
Opcode::I32Const(0), // [arg, 0]
Opcode::I32Eq, // [arg == 0]
Opcode::If(BlockType::NoResult), // if arg == 0
Opcode::I32Const(1), // [1]
Opcode::Br(1), // break from block1
Opcode::End, // end (if)
Opcode::I32Const(2), // [2]
Opcode::End, // end (block1)
Opcode::End]));
assert_eq!(run_function_i32(&module, 0).unwrap(), 1);
@ -372,14 +323,12 @@ fn expr_br() {
#[test]
fn brif() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Block(BlockType::NoResult, // block1
Opcodes::new(vec![
Opcode::GetLocal(0), // [arg]
Opcode::BrIf(0), // if arg != 0: break from block1
Opcode::I32Const(1), // [1]
Opcode::Return, // return 1
Opcode::End, // end (block1)
])),
Opcode::Block(BlockType::NoResult), // block1
Opcode::GetLocal(0), // [arg]
Opcode::BrIf(0), // if arg != 0: break from block1
Opcode::I32Const(1), // [1]
Opcode::Return, // return 1
Opcode::End, // end (block1)
Opcode::I32Const(2), // [2]
Opcode::Return, // return 2
Opcode::End]));
@ -392,18 +341,16 @@ fn brif() {
#[test]
fn brif_loop() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Loop(BlockType::NoResult, // loop
Opcodes::new(vec![
Opcode::GetLocal(1), // [local1]
Opcode::I32Const(1), // [local1, 1]
Opcode::I32Add, // [local1 + 1]
Opcode::SetLocal(1), // [] + local1 = local1 + 1
Opcode::GetLocal(1), // [local1]
Opcode::GetLocal(0), // [local1, arg]
Opcode::I32LtS, // [local1 < arg]
Opcode::BrIf(0), // break loop if local1 < arg
Opcode::End, // end (loop)
])),
Opcode::Loop(BlockType::NoResult), // loop
Opcode::GetLocal(1), // [local1]
Opcode::I32Const(1), // [local1, 1]
Opcode::I32Add, // [local1 + 1]
Opcode::SetLocal(1), // [] + local1 = local1 + 1
Opcode::GetLocal(1), // [local1]
Opcode::GetLocal(0), // [local1, arg]
Opcode::I32LtS, // [local1 < arg]
Opcode::BrIf(0), // break loop if local1 < arg
Opcode::End, // end (loop)
Opcode::GetLocal(1), // [local1]
Opcode::Return, // return
Opcode::End]));
@ -416,18 +363,16 @@ fn brif_loop() {
#[test]
fn expr_brif() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Loop(BlockType::NoResult, // loop
Opcodes::new(vec![
Opcode::GetLocal(1), // [local1]
Opcode::I32Const(1), // [local1, 1]
Opcode::I32Add, // [local1 + 1]
Opcode::SetLocal(1), // [] + local1 = local1 + 1
Opcode::GetLocal(1), // [local1]
Opcode::GetLocal(0), // [local1, local0]
Opcode::I32LtS, // [local1 < local0]
Opcode::BrIf(0), // if local1 < local0: break from loop
Opcode::End, // end (loop)
])),
Opcode::Loop(BlockType::NoResult), // loop
Opcode::GetLocal(1), // [local1]
Opcode::I32Const(1), // [local1, 1]
Opcode::I32Add, // [local1 + 1]
Opcode::SetLocal(1), // [] + local1 = local1 + 1
Opcode::GetLocal(1), // [local1]
Opcode::GetLocal(0), // [local1, local0]
Opcode::I32LtS, // [local1 < local0]
Opcode::BrIf(0), // if local1 < local0: break from loop
Opcode::End, // end (loop)
Opcode::GetLocal(1), // [local1]
Opcode::End]));
@ -439,30 +384,22 @@ fn expr_brif() {
#[test]
fn brtable() {
let (_program, module) = make_function_i32(Opcodes::new(vec![
Opcode::Block(BlockType::NoResult, // block3
Opcodes::new(vec![
Opcode::Block(BlockType::NoResult, // block2
Opcodes::new(vec![
Opcode::Block(BlockType::NoResult, // block1
Opcodes::new(vec![
Opcode::Block(BlockType::NoResult, // block0
Opcodes::new(vec![
Opcode::GetLocal(0), // [arg]
Opcode::BrTable(vec![0, 1, 2], 3), // br_table
Opcode::End, // end (block0)
])),
Opcode::I32Const(0), // [0]
Opcode::Return, // return 0
Opcode::End, // end (block1)
])),
Opcode::I32Const(1), // [1]
Opcode::Return, // return 1
Opcode::End, // end (block2)
])),
Opcode::End, // end (block3)
])),
Opcode::I32Const(2), // [2]
Opcode::Return, // return 2
Opcode::Block(BlockType::NoResult), // block3
Opcode::Block(BlockType::NoResult), // block2
Opcode::Block(BlockType::NoResult), // block1
Opcode::Block(BlockType::NoResult), // block0
Opcode::GetLocal(0), // [arg]
Opcode::BrTable(vec![0, 1, 2], 3), // br_table
Opcode::End, // end (block0)
Opcode::I32Const(0), // [0]
Opcode::Return, // return 0
Opcode::End, // end (block1)
Opcode::I32Const(1), // [1]
Opcode::Return, // return 1
Opcode::End, // end (block2)
Opcode::End, // end (block3)
Opcode::I32Const(2), // [2]
Opcode::Return, // return 2
Opcode::End]));
assert_eq!(run_function_i32(&module, 0).unwrap(), 0);
@ -478,21 +415,17 @@ fn return_test() {
Opcode::GetLocal(0),
Opcode::I32Const(0),
Opcode::I32Eq,
Opcode::If(BlockType::NoResult,
Opcodes::new(vec![
Opcode::I32Const(1),
Opcode::Return,
Opcode::End,
])),
Opcode::If(BlockType::NoResult),
Opcode::I32Const(1),
Opcode::Return,
Opcode::End,
Opcode::GetLocal(0),
Opcode::I32Const(1),
Opcode::I32Eq,
Opcode::If(BlockType::NoResult,
Opcodes::new(vec![
Opcode::I32Const(2),
Opcode::Return,
Opcode::End,
])),
Opcode::If(BlockType::NoResult),
Opcode::I32Const(2),
Opcode::Return,
Opcode::End,
Opcode::I32Const(3),
Opcode::Return,
Opcode::End]));
@ -509,11 +442,9 @@ fn return_void() {
Opcode::GetLocal(0),
Opcode::I32Const(0),
Opcode::I32Eq,
Opcode::If(BlockType::NoResult,
Opcodes::new(vec![
Opcode::Return,
Opcode::End,
])),
Opcode::If(BlockType::NoResult),
Opcode::Return,
Opcode::End,
Opcode::I32Const(0),
Opcode::I32Const(1),
Opcode::I32Store(2, 0),
@ -604,20 +535,18 @@ fn call_2() {
Opcode::GetLocal(0),
Opcode::I32Const(0),
Opcode::I32GtS,
Opcode::If(BlockType::Value(ValueType::I32),
Opcodes::new(vec![
Opcode::GetLocal(0),
Opcode::GetLocal(0),
Opcode::I32Const(1),
Opcode::I32Sub,
Opcode::Call(1),
Opcode::I32Mul,
Opcode::Return,
Opcode::Else,
Opcode::I32Const(1),
Opcode::Return,
Opcode::End,
])),
Opcode::If(BlockType::Value(ValueType::I32)),
Opcode::GetLocal(0),
Opcode::GetLocal(0),
Opcode::I32Const(1),
Opcode::I32Sub,
Opcode::Call(1),
Opcode::I32Mul,
Opcode::Return,
Opcode::Else,
Opcode::I32Const(1),
Opcode::Return,
Opcode::End,
Opcode::End,
]);

View File

@ -1,10 +1,9 @@
use std::u32;
use std::collections::VecDeque;
use elements::{Module, Opcode, BlockType, FunctionType, ValueType, External, Type};
use std::collections::HashMap;
use elements::{Opcode, BlockType, FunctionType, ValueType};
use interpreter::Error;
use interpreter::runner::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use interpreter::imports::ModuleImports;
use interpreter::module::ItemIndex;
use interpreter::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex};
use interpreter::stack::StackWithLimit;
use interpreter::variable::VariableType;
@ -13,18 +12,20 @@ const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF;
/// Function validation context.
pub struct FunctionValidationContext<'a> {
/// Wasm module.
module: &'a Module,
/// Module imports.
imports: &'a ModuleImports,
/// Wasm module instance (in process of instantiation).
module_instance: &'a ModuleInstance,
/// Current instruction position.
position: usize,
/// Local variables.
locals: &'a [ValueType],
/// Value stack.
value_stack: StackWithLimit<StackValueType>,
/// Frame stack.
frame_stack: StackWithLimit<ValidationFrame>,
frame_stack: StackWithLimit<BlockFrame>,
/// Function return type. None if validating expression.
return_type: Option<BlockType>,
/// Labels positions.
labels: HashMap<usize, usize>,
}
/// Value type on the stack.
@ -38,37 +39,54 @@ pub enum StackValueType {
Specific(ValueType),
}
/// Function validation frame.
/// Control stack frame.
#[derive(Debug, Clone)]
pub struct ValidationFrame {
/// Is loop frame?
pub is_loop: bool,
/// Return type.
pub struct BlockFrame {
/// Frame type.
pub frame_type: BlockFrameType,
/// A signature, which is a block signature type indicating the number and types of result values of the region.
pub block_type: BlockType,
/// Value stack len.
/// A label for reference to block instruction.
pub begin_position: usize,
/// A label for reference from branch instructions.
pub branch_position: usize,
/// A label for reference from end instructions.
pub 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.
pub value_stack_len: usize,
}
/// Type of block frame.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BlockFrameType {
/// Function frame.
Function,
/// Usual block frame.
Block,
/// Loop frame (branching to the beginning of block).
Loop,
/// True-subblock of if expression.
IfTrue,
/// False-subblock of if expression.
IfFalse,
}
/// Function validator.
pub struct Validator;
/// Instruction outcome.
#[derive(Debug, Clone)]
pub enum InstructionOutcome<'a> {
pub enum InstructionOutcome {
/// Continue with next instruction.
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_function(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode]) -> Result<(), Error> {
context.push_label(false, block_type)?;
Validator::validate_block(context, body)?;
context.push_label(BlockFrameType::Function, block_type)?;
Validator::validate_function_block(context, body)?;
while !context.frame_stack.is_empty() {
context.pop_label()?;
}
@ -76,69 +94,36 @@ impl Validator {
Ok(())
}
fn validate_block<'a>(context: &mut FunctionValidationContext, mut body: &[Opcode]) -> Result<InstructionOutcome<'a>, Error> {
let mut block_stack = VecDeque::new();
let mut position = 0;
fn validate_function_block(context: &mut FunctionValidationContext, body: &[Opcode]) -> Result<(), Error> {
let body_len = body.len();
if body_len == 0 {
return Err(Error::Validation("Non-empty function body expected".into()));
}
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;
};
let opcode = &body[context.position];
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));
InstructionOutcome::ValidateNextInstruction => (),
InstructionOutcome::Unreachable => context.unreachable()?,
}
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;
},
context.position += 1;
if context.position >= body_len {
return Ok(());
}
}
}
pub fn validate_instruction<'a>(context: &mut FunctionValidationContext, opcode: &'a Opcode) -> Result<InstructionOutcome<'a>, Error> {
fn validate_instruction(context: &mut FunctionValidationContext, opcode: &Opcode) -> Result<InstructionOutcome, Error> {
debug!(target: "validator", "validating {:?}", opcode);
match opcode {
&Opcode::Unreachable => Ok(InstructionOutcome::Unreachable),
&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::ValidateNextInstruction),
&Opcode::End => Ok(InstructionOutcome::ValidateNextInstruction),
&Opcode::Block(block_type) => Validator::validate_block(context, block_type),
&Opcode::Loop(block_type) => Validator::validate_loop(context, block_type),
&Opcode::If(block_type) => Validator::validate_if(context, block_type),
&Opcode::Else => Validator::validate_else(context),
&Opcode::End => Validator::validate_end(context),
&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),
@ -324,49 +309,49 @@ impl Validator {
}
}
fn validate_const<'a>(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
fn validate_const(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
context.push_value(value_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_unop<'a>(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
fn validate_unop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type)?;
context.push_value(value_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_binop<'a>(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
fn validate_binop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type)?;
context.pop_value(value_type)?;
context.push_value(value_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_testop<'a>(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
fn validate_testop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type)?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_relop<'a>(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
fn validate_relop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type)?;
context.pop_value(value_type)?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_cvtop<'a>(context: &mut FunctionValidationContext, value_type1: StackValueType, value_type2: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: StackValueType, value_type2: StackValueType) -> Result<InstructionOutcome, Error> {
context.pop_value(value_type1)?;
context.push_value(value_type2)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_drop<'a>(context: &mut FunctionValidationContext) -> Result<InstructionOutcome<'a>, Error> {
fn validate_drop(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.pop_any_value().map(|_| ())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_select<'a>(context: &mut FunctionValidationContext) -> Result<InstructionOutcome<'a>, Error> {
fn validate_select(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.pop_value(ValueType::I32.into())?;
let select_type = context.pop_any_value()?;
context.pop_value(select_type)?;
@ -374,13 +359,13 @@ impl Validator {
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_get_local<'a>(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
fn validate_get_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let local_type = context.require_local(index)?;
context.push_value(local_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_set_local<'a>(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let local_type = context.require_local(index)?;
let value_type = context.pop_any_value()?;
if local_type != value_type {
@ -389,7 +374,7 @@ impl Validator {
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_tee_local<'a>(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
fn validate_tee_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let local_type = context.require_local(index)?;
let value_type = context.tee_any_value()?;
if local_type != value_type {
@ -398,13 +383,13 @@ impl Validator {
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_get_global<'a>(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
fn validate_get_global(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let global_type = context.require_global(index, None)?;
context.push_value(global_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_set_global<'a>(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome<'a>, Error> {
fn validate_set_global(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let global_type = context.require_global(index, Some(true))?;
let value_type = context.pop_any_value()?;
if global_type != value_type {
@ -413,7 +398,7 @@ impl Validator {
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_load<'a>(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result<InstructionOutcome, 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)));
@ -426,7 +411,7 @@ impl Validator {
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_store<'a>(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result<InstructionOutcome<'a>, Error> {
fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result<InstructionOutcome, 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)));
@ -439,35 +424,55 @@ impl Validator {
Ok(InstructionOutcome::ValidateNextInstruction)
}
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_block(context: &mut FunctionValidationContext, block_type: BlockType) -> Result<InstructionOutcome, Error> {
context.push_label(BlockFrameType::Block, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_if<'a>(context: &mut FunctionValidationContext, block_type: BlockType, body: &'a [Opcode]) -> Result<InstructionOutcome<'a>, Error> {
fn validate_loop(context: &mut FunctionValidationContext, block_type: BlockType) -> Result<InstructionOutcome, Error> {
context.push_label(BlockFrameType::Loop, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_if(context: &mut FunctionValidationContext, block_type: BlockType) -> Result<InstructionOutcome, Error> {
context.pop_value(ValueType::I32.into())?;
let body_len = body.len();
let separator_index = body.iter()
.position(|op| *op == Opcode::Else)
.unwrap_or(body_len - 1);
if separator_index != body_len - 1 {
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::schedule_validate_block(false, block_type, body, Opcode::End)
}
context.push_label(BlockFrameType::IfTrue, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_br<'a>(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome<'a>, Error> {
let (frame_is_loop, frame_block_type) = {
fn validate_else(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
let block_type = {
let top_frame = context.top_label()?;
if top_frame.frame_type != BlockFrameType::IfTrue {
return Err(Error::Validation("Misplaced else instruction".into()));
}
top_frame.block_type
};
context.pop_label()?;
if let BlockType::Value(value_type) = block_type {
context.pop_value(value_type.into())?;
}
context.push_label(BlockFrameType::IfFalse, block_type).map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_end(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
{
let top_frame = context.top_label()?;
if top_frame.frame_type == BlockFrameType::IfTrue {
if top_frame.block_type != BlockType::NoResult {
return Err(Error::Validation(format!("If block without else required to have NoResult block type. But it have {:?} type", top_frame.block_type)));
}
}
}
context.pop_label().map(|_| InstructionOutcome::ValidateNextInstruction)
}
fn validate_br(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
let (frame_type, frame_block_type) = {
let frame = context.require_label(idx)?;
(frame.is_loop, frame.block_type)
(frame.frame_type, frame.block_type)
};
if !frame_is_loop {
if frame_type != BlockFrameType::Loop {
if let BlockType::Value(value_type) = frame_block_type {
context.tee_value(value_type.into())?;
}
@ -475,7 +480,7 @@ impl Validator {
Ok(InstructionOutcome::Unreachable)
}
fn validate_br_if<'a>(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome<'a>, Error> {
fn validate_br_if(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
context.pop_value(ValueType::I32.into())?;
if let BlockType::Value(value_type) = context.require_label(idx)?.block_type {
context.tee_value(value_type.into())?;
@ -483,18 +488,18 @@ impl Validator {
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_br_table<'a>(context: &mut FunctionValidationContext, table: &Vec<u32>, default: u32) -> Result<InstructionOutcome<'a>, Error> {
fn validate_br_table(context: &mut FunctionValidationContext, table: &Vec<u32>, default: u32) -> Result<InstructionOutcome, Error> {
let mut required_block_type = None;
{
let default_block = context.require_label(default)?;
if !default_block.is_loop {
if default_block.frame_type != BlockFrameType::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 label_block.frame_type != BlockFrameType::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)));
@ -515,14 +520,14 @@ impl Validator {
Ok(InstructionOutcome::Unreachable)
}
fn validate_return<'a>(context: &mut FunctionValidationContext) -> Result<InstructionOutcome<'a>, Error> {
fn validate_return(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
if let BlockType::Value(value_type) = context.return_type()? {
context.tee_value(value_type.into())?;
}
Ok(InstructionOutcome::Unreachable)
}
fn validate_call<'a>(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome<'a>, Error> {
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
let (argument_types, return_type) = context.require_function(idx)?;
for argument_type in argument_types.iter().rev() {
context.pop_value((*argument_type).into())?;
@ -533,7 +538,7 @@ impl Validator {
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_call_indirect<'a>(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome<'a>, Error> {
fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
context.require_table(DEFAULT_TABLE_INDEX, VariableType::AnyFunc)?;
context.pop_value(ValueType::I32.into())?;
@ -547,55 +552,36 @@ impl Validator {
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_current_memory<'a>(context: &mut FunctionValidationContext) -> Result<InstructionOutcome<'a>, Error> {
fn validate_current_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.require_memory(DEFAULT_MEMORY_INDEX)?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_grow_memory<'a>(context: &mut FunctionValidationContext) -> Result<InstructionOutcome<'a>, Error> {
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.require_memory(DEFAULT_MEMORY_INDEX)?;
context.pop_value(ValueType::I32.into())?;
context.push_value(ValueType::I32.into())?;
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))
}
}
impl<'a> FunctionValidationContext<'a> {
pub fn new(
module: &'a Module,
imports: &'a ModuleImports,
module_instance: &'a ModuleInstance,
locals: &'a [ValueType],
value_stack_limit: usize,
frame_stack_limit: usize,
function: &FunctionType,
) -> Self {
FunctionValidationContext {
module: module,
imports: imports,
module_instance: module_instance,
position: 0,
locals: locals,
value_stack: StackWithLimit::with_limit(value_stack_limit),
frame_stack: StackWithLimit::with_limit(frame_stack_limit),
return_type: Some(function.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)),
labels: HashMap::new(),
}
}
@ -646,11 +632,18 @@ impl<'a> FunctionValidationContext<'a> {
self.value_stack.push(StackValueType::AnyUnlimited)
}
pub fn push_label(&mut self, is_loop: bool, block_type: BlockType) -> Result<(), Error> {
self.frame_stack.push(ValidationFrame {
is_loop: is_loop,
pub fn top_label(&self) -> Result<&BlockFrame, Error> {
self.frame_stack.top()
}
pub fn push_label(&mut self, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> {
self.frame_stack.push(BlockFrame {
frame_type: frame_type,
block_type: block_type,
value_stack_len: self.value_stack.len()
begin_position: self.position,
branch_position: self.position,
end_position: self.position,
value_stack_len: self.value_stack.len(),
})
}
@ -668,6 +661,9 @@ impl<'a> FunctionValidationContext<'a> {
BlockType::Value(required_value_type) if actual_value_type.map(|vt| vt == required_value_type).unwrap_or(false) => (),
_ => return Err(Error::Validation(format!("Expected block to return {:?} while it has returned {:?}", frame.block_type, actual_value_type))),
}
if !self.frame_stack.is_empty() {
self.labels.insert(frame.begin_position, self.position);
}
if let BlockType::Value(value_type) = frame.block_type {
self.push_value(value_type.into())?;
}
@ -675,7 +671,7 @@ impl<'a> FunctionValidationContext<'a> {
Ok(InstructionOutcome::ValidateNextInstruction)
}
pub fn require_label(&self, idx: u32) -> Result<&ValidationFrame, Error> {
pub fn require_label(&self, idx: u32) -> Result<&BlockFrame, Error> {
self.frame_stack.get(idx as usize)
}
@ -691,100 +687,49 @@ impl<'a> FunctionValidationContext<'a> {
}
pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<StackValueType, Error> {
match self.imports.parse_global_index(ItemIndex::IndexSpace(idx)) {
ItemIndex::IndexSpace(_) => unreachable!("parse_global_index is intended to resolve this"),
ItemIndex::Internal(internal_idx) => self.module
.global_section().ok_or(Error::Validation(format!("Trying to access internal global {} in module without global section", internal_idx)))
.and_then(|s| s.entries().get(internal_idx as usize).ok_or(Error::Validation(format!("Trying to access internal global {} in module with {} globals", internal_idx, s.entries().len()))))
.and_then(|g| match mutability {
Some(true) if !g.global_type().is_mutable() => Err(Error::Validation(format!("Expected internal global {} to be mutable", internal_idx))),
Some(false) if g.global_type().is_mutable() => Err(Error::Validation(format!("Expected internal global {} to be immutable", internal_idx))),
_ => Ok(g),
})
.map(|g| g.global_type().content_type().into()),
ItemIndex::External(external_idx) => self.module
.import_section().ok_or(Error::Validation(format!("Trying to access external global {} in module without import section", external_idx)))
.and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external global with index {} in module with {}-entries import section", external_idx, s.entries().len()))))
.and_then(|e| match e.external() {
&External::Global(ref g) => {
match mutability {
Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected external global {} to be mutable", external_idx))),
Some(false) if g.is_mutable() => Err(Error::Validation(format!("Expected external global {} to be immutable", external_idx))),
_ => Ok(g.content_type().into()),
}
},
_ => Err(Error::Validation(format!("Import entry {} expected to import global", external_idx)))
}),
}
self.module_instance
.global(ItemIndex::IndexSpace(idx), None)
.and_then(|g| match mutability {
Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be mutable", idx))),
Some(false) if g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be immutable", idx))),
_ => match g.variable_type() {
VariableType::AnyFunc => Err(Error::Validation(format!("Expected global {} to have non-AnyFunc type", idx))),
VariableType::I32 => Ok(StackValueType::Specific(ValueType::I32)),
VariableType::I64 => Ok(StackValueType::Specific(ValueType::I64)),
VariableType::F32 => Ok(StackValueType::Specific(ValueType::F32)),
VariableType::F64 => Ok(StackValueType::Specific(ValueType::F64)),
}
})
}
pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
match self.imports.parse_memory_index(ItemIndex::IndexSpace(idx)) {
ItemIndex::IndexSpace(_) => unreachable!("parse_memory_index is intended to resolve this"),
ItemIndex::Internal(internal_idx) => self.module
.memory_section().ok_or(Error::Validation(format!("Trying to access internal memory {} in module without memory section", internal_idx)))
.and_then(|s| s.entries().get(internal_idx as usize).ok_or(Error::Validation(format!("Trying to access internal memory {} in module with {} memory regions", internal_idx, s.entries().len()))))
.map(|_| ()),
ItemIndex::External(external_idx) => self.module
.import_section().ok_or(Error::Validation(format!("Trying to access external memory {} in module without import section", external_idx)))
.and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external memory with index {} in module with {}-entries import section", external_idx, s.entries().len()))))
.and_then(|e| match e.external() {
&External::Memory(_) => Ok(()),
_ => Err(Error::Validation(format!("Import entry {} expected to import memory", external_idx)))
}),
}
self.module_instance
.memory(ItemIndex::IndexSpace(idx))
.map(|_| ())
}
pub fn require_table(&self, idx: u32, variable_type: VariableType) -> Result<(), Error> {
match self.imports.parse_table_index(ItemIndex::IndexSpace(idx)) {
ItemIndex::IndexSpace(_) => unreachable!("parse_table_index is intended to resolve this"),
ItemIndex::Internal(internal_idx) => self.module
.table_section().ok_or(Error::Validation(format!("Trying to access internal table {} in module without table section", internal_idx)))
.and_then(|s| s.entries().get(internal_idx as usize).ok_or(Error::Validation(format!("Trying to access internal table {} in module with {} tables", internal_idx, s.entries().len()))))
.and_then(|t| if variable_type == t.elem_type().into() {
Ok(())
} else {
Err(Error::Validation(format!("Internal table {} has element type {:?} while {:?} expected", internal_idx, t.elem_type(), variable_type)))
}),
ItemIndex::External(external_idx) => self.module
.import_section().ok_or(Error::Validation(format!("Trying to access external table {} in module without import section", external_idx)))
.and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external table with index {} in module with {}-entries import section", external_idx, s.entries().len()))))
.and_then(|e| match e.external() {
&External::Table(ref t) => if variable_type == t.elem_type().into() {
Ok(())
} else {
Err(Error::Validation(format!("External table {} has element type {:?} while {:?} expected", external_idx, t.elem_type(), variable_type)))
},
_ => Err(Error::Validation(format!("Import entry {} expected to import table", external_idx)))
}),
}
self.module_instance
.table(ItemIndex::IndexSpace(idx))
.and_then(|t| if t.variable_type() == variable_type {
Ok(())
} else {
Err(Error::Validation(format!("Table {} has element type {:?} while {:?} expected", idx, t.variable_type(), variable_type)))
})
}
pub fn require_function(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
match self.imports.parse_function_index(ItemIndex::IndexSpace(idx)) {
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index is intended to resolve this"),
ItemIndex::Internal(internal_idx) => self.module
.function_section().ok_or(Error::Validation(format!("Trying to access internal function {} in module without function section", internal_idx)))
.and_then(|s| s.entries().get(internal_idx as usize).map(|f| f.type_ref()).ok_or(Error::Validation(format!("Trying to access internal function {} in module with {} functions", internal_idx, s.entries().len()))))
.and_then(|tidx| self.require_function_type(tidx)),
ItemIndex::External(external_idx) => self.module
.import_section().ok_or(Error::Validation(format!("Trying to access external function {} in module without import section", external_idx)))
.and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external function with index {} in module with {}-entries import section", external_idx, s.entries().len()))))
.and_then(|e| match e.external() {
&External::Function(tidx) => Ok(tidx),
_ => Err(Error::Validation(format!("Import entry {} expected to import function", external_idx)))
})
.and_then(|tidx| self.require_function_type(tidx)),
}
self.module_instance.function_type(ItemIndex::IndexSpace(idx))
.map(|ft| (ft.params().to_vec(), ft.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)))
}
pub fn require_function_type(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
self.module
.type_section().ok_or(Error::Validation(format!("Trying to access internal function {} in module without type section", idx)))
.and_then(|ts| match ts.types().get(idx as usize) {
Some(&Type::Function(ref function_type)) => Ok((function_type.params().to_vec(), function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult))),
_ => Err(Error::Validation(format!("Trying to access internal function {} with wrong type", idx))),
})
self.module_instance.function_type_by_index(idx)
.map(|ft| (ft.params().to_vec(), ft.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)))
}
pub fn function_labels(self) -> HashMap<usize, usize> {
self.labels
}
fn check_stack_access(&self) -> Result<(), Error> {