mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-21 02:31:51 +00:00
wabt interpreter tests
This commit is contained in:
@ -47,3 +47,6 @@ mod table;
|
|||||||
mod utils;
|
mod utils;
|
||||||
mod value;
|
mod value;
|
||||||
mod variable;
|
mod variable;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
|
use std::fmt::Display;
|
||||||
use elements::{Opcode, BlockType, FunctionType};
|
use elements::{Opcode, BlockType, FunctionType};
|
||||||
use interpreter::Error;
|
use interpreter::Error;
|
||||||
use interpreter::module::{ModuleInstance, ItemIndex};
|
use interpreter::module::{ModuleInstance, ItemIndex};
|
||||||
@ -37,8 +38,10 @@ pub enum InstructionOutcome {
|
|||||||
RunInstruction,
|
RunInstruction,
|
||||||
/// Continue with next instruction.
|
/// Continue with next instruction.
|
||||||
RunNextInstruction,
|
RunNextInstruction,
|
||||||
/// Pop given number of stack frames.
|
/// Branch to given frame.
|
||||||
PopFrame(usize),
|
Branch(usize),
|
||||||
|
/// End current frame.
|
||||||
|
End,
|
||||||
/// Return from current function block.
|
/// Return from current function block.
|
||||||
Return,
|
Return,
|
||||||
}
|
}
|
||||||
@ -46,7 +49,9 @@ pub enum InstructionOutcome {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BlockFrame {
|
pub struct BlockFrame {
|
||||||
// A label for reference from branch instructions.
|
// A label for reference from branch instructions.
|
||||||
position: usize,
|
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.
|
// 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,
|
value_limit: usize,
|
||||||
// A signature, which is a block signature type indicating the number and types of result values of the region.
|
// A signature, which is a block signature type indicating the number and types of result values of the region.
|
||||||
@ -266,13 +271,13 @@ 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, frame_position, block_type.clone())?;
|
||||||
Interpreter::execute_block(context, 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, frame_position + 1, block_type.clone())?;
|
||||||
Interpreter::execute_block(context, body)
|
Interpreter::execute_block(context, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +292,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, frame_position, block_type.clone())?;
|
||||||
Interpreter::execute_block(context, &body[begin_index..end_index])
|
Interpreter::execute_block(context, &body[begin_index..end_index])
|
||||||
} else {
|
} else {
|
||||||
Ok(InstructionOutcome::RunNextInstruction)
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
@ -295,20 +300,20 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run_else(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
fn run_else(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||||
Ok(InstructionOutcome::PopFrame(0))
|
Ok(InstructionOutcome::End)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_end(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
fn run_end(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||||
Ok(InstructionOutcome::PopFrame(0))
|
Ok(InstructionOutcome::End)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_br(context: &mut FunctionContext, label_idx: u32) -> Result<InstructionOutcome, Error> {
|
fn run_br(context: &mut FunctionContext, label_idx: u32) -> Result<InstructionOutcome, Error> {
|
||||||
Ok(InstructionOutcome::PopFrame(label_idx as usize))
|
Ok(InstructionOutcome::Branch(label_idx as usize))
|
||||||
}
|
}
|
||||||
|
|
||||||
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.value_stack_mut().pop_as()? {
|
if context.value_stack_mut().pop_as()? {
|
||||||
Ok(InstructionOutcome::PopFrame(label_idx as usize))
|
Ok(InstructionOutcome::Branch(label_idx as usize))
|
||||||
} else {
|
} else {
|
||||||
Ok(InstructionOutcome::RunNextInstruction)
|
Ok(InstructionOutcome::RunNextInstruction)
|
||||||
}
|
}
|
||||||
@ -316,7 +321,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.value_stack_mut().pop_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::Branch(table.get(index as usize).cloned().unwrap_or(default) as usize))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_return(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
fn run_return(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||||
@ -491,7 +496,7 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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> + Display {
|
||||||
context
|
context
|
||||||
.value_stack_mut()
|
.value_stack_mut()
|
||||||
.pop_pair_as::<T>()
|
.pop_pair_as::<T>()
|
||||||
@ -835,14 +840,19 @@ impl Interpreter {
|
|||||||
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,
|
||||||
InstructionOutcome::PopFrame(index) => {
|
InstructionOutcome::Branch(index) => {
|
||||||
context.pop_frame()?;
|
|
||||||
if index != 0 {
|
if index != 0 {
|
||||||
return Ok(InstructionOutcome::PopFrame(index - 1));
|
context.discard_frame()?;
|
||||||
|
return Ok(InstructionOutcome::Branch(index - 1));
|
||||||
} else {
|
} else {
|
||||||
|
context.pop_frame(true)?;
|
||||||
return Ok(InstructionOutcome::RunInstruction);
|
return Ok(InstructionOutcome::RunInstruction);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
InstructionOutcome::End => {
|
||||||
|
context.pop_frame(false)?;
|
||||||
|
return Ok(InstructionOutcome::RunInstruction);
|
||||||
|
},
|
||||||
InstructionOutcome::Return => return Ok(InstructionOutcome::Return),
|
InstructionOutcome::Return => return Ok(InstructionOutcome::Return),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -859,7 +869,7 @@ impl<'a> FunctionContext<'a> {
|
|||||||
locals: args,
|
locals: args,
|
||||||
position: 0,
|
position: 0,
|
||||||
};
|
};
|
||||||
context.push_frame(body.len() - 1, match function.return_type() {
|
context.push_frame(body.len() - 1, body.len() - 1, match function.return_type() {
|
||||||
Some(value_type) => BlockType::Value(value_type),
|
Some(value_type) => BlockType::Value(value_type),
|
||||||
None => BlockType::NoResult,
|
None => BlockType::NoResult,
|
||||||
})?;
|
})?;
|
||||||
@ -903,15 +913,21 @@ impl<'a> FunctionContext<'a> {
|
|||||||
&self.frame_stack
|
&self.frame_stack
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_frame(&mut self, position: usize, signature: BlockType) -> Result<(), Error> {
|
pub fn push_frame(&mut self, branch_position: usize, end_position: usize, signature: BlockType) -> Result<(), Error> {
|
||||||
self.frame_stack.push(BlockFrame {
|
self.frame_stack.push(BlockFrame {
|
||||||
position: position,
|
branch_position: branch_position,
|
||||||
|
end_position: end_position,
|
||||||
value_limit: self.value_stack.len(),
|
value_limit: self.value_stack.len(),
|
||||||
signature: signature,
|
signature: signature,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_frame(&mut self) -> Result<(), Error> {
|
pub fn discard_frame(&mut self) -> Result<(), Error> {
|
||||||
|
self.frame_stack.pop()
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_frame(&mut self, is_branch: bool) -> Result<(), Error> {
|
||||||
let frame = self.frame_stack.pop()?;
|
let frame = self.frame_stack.pop()?;
|
||||||
if frame.value_limit > self.value_stack.len() {
|
if frame.value_limit > self.value_stack.len() {
|
||||||
return Err(Error::Stack("invalid stack len".into()));
|
return Err(Error::Stack("invalid stack len".into()));
|
||||||
@ -922,7 +938,7 @@ impl<'a> FunctionContext<'a> {
|
|||||||
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 = if is_branch { frame.branch_position } else { frame.end_position };
|
||||||
if let Some(frame_value) = frame_value {
|
if let Some(frame_value) = frame_value {
|
||||||
self.value_stack.push(frame_value)?;
|
self.value_stack.push(frame_value)?;
|
||||||
}
|
}
|
||||||
@ -934,7 +950,8 @@ impl<'a> FunctionContext<'a> {
|
|||||||
impl BlockFrame {
|
impl BlockFrame {
|
||||||
pub fn invalid() -> Self {
|
pub fn invalid() -> Self {
|
||||||
BlockFrame {
|
BlockFrame {
|
||||||
position: usize::max_value(),
|
branch_position: usize::max_value(),
|
||||||
|
end_position: usize::max_value(),
|
||||||
value_limit: usize::max_value(),
|
value_limit: usize::max_value(),
|
||||||
signature: BlockType::NoResult,
|
signature: BlockType::NoResult,
|
||||||
}
|
}
|
||||||
@ -950,158 +967,3 @@ fn effective_address(offset: u32, align: u32) -> Result<u32, Error> {
|
|||||||
.ok_or(Error::Interpreter("invalid memory alignment".into()))
|
.ok_or(Error::Interpreter("invalid memory alignment".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::sync::Weak;
|
|
||||||
use elements::{Module, ValueType, Opcodes, Opcode, BlockType, FunctionType};
|
|
||||||
use interpreter::Error;
|
|
||||||
use interpreter::module::ModuleInstance;
|
|
||||||
use interpreter::runner::{Interpreter, FunctionContext};
|
|
||||||
use interpreter::value::{RuntimeValue, TryInto};
|
|
||||||
use interpreter::variable::{VariableInstance, VariableType};
|
|
||||||
|
|
||||||
fn run_function_i32(body: &Opcodes, arg: i32) -> Result<i32, Error> {
|
|
||||||
let ftype = FunctionType::new(vec![ValueType::I32], Some(ValueType::I32));
|
|
||||||
let module = ModuleInstance::new(Weak::default(), 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())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn trap() {
|
|
||||||
let body = Opcodes::new(vec![
|
|
||||||
Opcode::Unreachable, // trap
|
|
||||||
Opcode::End]);
|
|
||||||
|
|
||||||
assert_eq!(run_function_i32(&body, 0).unwrap_err(), Error::Trap("programmatic".into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn nop() {
|
|
||||||
let body = Opcodes::new(vec![
|
|
||||||
Opcode::Nop, // nop
|
|
||||||
Opcode::I32Const(20), // 20
|
|
||||||
Opcode::Nop, // nop
|
|
||||||
Opcode::End]);
|
|
||||||
|
|
||||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_then() {
|
|
||||||
let body = Opcodes::new(vec![
|
|
||||||
Opcode::I32Const(20), // 20
|
|
||||||
Opcode::GetLocal(0), // read argument
|
|
||||||
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
|
||||||
Opcodes::new(vec![
|
|
||||||
Opcode::I32Const(10), // 10
|
|
||||||
Opcode::End, // end
|
|
||||||
])),
|
|
||||||
Opcode::End]);
|
|
||||||
|
|
||||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 20);
|
|
||||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_then_else() {
|
|
||||||
let body = Opcodes::new(vec![
|
|
||||||
Opcode::GetLocal(0), // read argument
|
|
||||||
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
|
||||||
Opcodes::new(vec![
|
|
||||||
Opcode::I32Const(10), // 10
|
|
||||||
Opcode::Else, // else
|
|
||||||
Opcode::I32Const(20), // 20
|
|
||||||
Opcode::End, // end
|
|
||||||
])),
|
|
||||||
Opcode::End]);
|
|
||||||
|
|
||||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 20);
|
|
||||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_from_if() {
|
|
||||||
let body = Opcodes::new(vec![
|
|
||||||
Opcode::GetLocal(0), // read argument
|
|
||||||
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
|
||||||
Opcodes::new(vec![
|
|
||||||
Opcode::I32Const(20), // 20
|
|
||||||
Opcode::Return, // return
|
|
||||||
Opcode::End,
|
|
||||||
])),
|
|
||||||
Opcode::I32Const(10), // 10
|
|
||||||
Opcode::End]);
|
|
||||||
|
|
||||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 10);
|
|
||||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn block() {
|
|
||||||
let body = Opcodes::new(vec![
|
|
||||||
Opcode::Block(BlockType::Value(ValueType::I32), // mark block
|
|
||||||
Opcodes::new(vec![
|
|
||||||
Opcode::I32Const(10), // 10
|
|
||||||
Opcode::End,
|
|
||||||
])),
|
|
||||||
Opcode::End]);
|
|
||||||
|
|
||||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn loop_block() {
|
|
||||||
// TODO: test
|
|
||||||
/*
|
|
||||||
let body = Opcodes::new(vec![
|
|
||||||
Opcode::I32Const(2), // 2
|
|
||||||
Opcode::Loop(BlockType::Value(ValueType::I32), // start loop
|
|
||||||
Opcodes::new(vec![
|
|
||||||
Opcode::GetLocal(0), // read argument
|
|
||||||
Opcode::I32Const(1), // 1
|
|
||||||
Opcode::I32Sub, // argument--
|
|
||||||
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
|
||||||
Opcodes::new(vec![
|
|
||||||
Opcode::I32Const(2), // 2
|
|
||||||
Opcode::I32Mul, // prev_val * 2
|
|
||||||
Opcode::Br(1), // branch to loop
|
|
||||||
Opcode::End, // end (if)
|
|
||||||
])),
|
|
||||||
Opcode::End, // end (loop)
|
|
||||||
])),
|
|
||||||
Opcode::End]); // end (fun)
|
|
||||||
|
|
||||||
assert_eq!(run_function_i32(&body, 2).unwrap(), 10);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn branch() {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn branch_if() {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn branch_table() {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn drop() {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn select() {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@ use interpreter::Error;
|
|||||||
use interpreter::value::{RuntimeValue, TryInto};
|
use interpreter::value::{RuntimeValue, TryInto};
|
||||||
|
|
||||||
/// Stack with limit.
|
/// Stack with limit.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct StackWithLimit<T> where T: Clone {
|
pub struct StackWithLimit<T> where T: Clone {
|
||||||
/// Stack values.
|
/// Stack values.
|
||||||
values: VecDeque<T>,
|
values: VecDeque<T>,
|
||||||
|
1
src/interpreter/tests/mod.rs
Normal file
1
src/interpreter/tests/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod wabt;
|
579
src/interpreter/tests/wabt.rs
Normal file
579
src/interpreter/tests/wabt.rs
Normal file
@ -0,0 +1,579 @@
|
|||||||
|
///! Tests from https://github.com/WebAssembly/wabt/tree/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp
|
||||||
|
|
||||||
|
use std::sync::Weak;
|
||||||
|
use elements::{Module, ValueType, Opcodes, Opcode, BlockType, FunctionType};
|
||||||
|
use interpreter::Error;
|
||||||
|
use interpreter::module::ModuleInstance;
|
||||||
|
use interpreter::runner::{Interpreter, FunctionContext};
|
||||||
|
use interpreter::value::{RuntimeValue, TryInto};
|
||||||
|
use interpreter::variable::{VariableInstance, VariableType};
|
||||||
|
|
||||||
|
fn run_function_i32(body: &Opcodes, arg: i32) -> Result<i32, Error> {
|
||||||
|
let ftype = FunctionType::new(vec![ValueType::I32], Some(ValueType::I32));
|
||||||
|
let module = ModuleInstance::new(Weak::default(), Module::default()).unwrap();
|
||||||
|
let mut context = FunctionContext::new(&module, 1024, 1024, &ftype, body.elements(), vec![
|
||||||
|
VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(arg)).unwrap(), // arg
|
||||||
|
VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(0)).unwrap(), // local1
|
||||||
|
VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(0)).unwrap(), // local2
|
||||||
|
])?;
|
||||||
|
Interpreter::run_function(&mut context, body.elements())
|
||||||
|
.map(|v| v.unwrap().try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/unreachable.txt
|
||||||
|
#[test]
|
||||||
|
fn unreachable() {
|
||||||
|
let body = Opcodes::new(vec![
|
||||||
|
Opcode::Unreachable, // trap
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap_err(), Error::Trap("programmatic".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nop() {
|
||||||
|
let body = Opcodes::new(vec![
|
||||||
|
Opcode::Nop, // nop
|
||||||
|
Opcode::I32Const(1), // [1]
|
||||||
|
Opcode::Nop, // nop
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-block.txt
|
||||||
|
#[test]
|
||||||
|
fn expr_block() {
|
||||||
|
let body = 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::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/loop.txt
|
||||||
|
#[test]
|
||||||
|
fn loop_test() {
|
||||||
|
let body = 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::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/if.txt#L3
|
||||||
|
#[test]
|
||||||
|
fn if_1() {
|
||||||
|
let body = Opcodes::new(vec![
|
||||||
|
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::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::GetLocal(0), // [arg]
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/if.txt#L23
|
||||||
|
#[test]
|
||||||
|
fn if_2() {
|
||||||
|
let body = 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::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::GetLocal(0), // [arg]
|
||||||
|
Opcode::GetLocal(1), // [arg, local1]
|
||||||
|
Opcode::I32Add, // [arg + local1]
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-if.txt
|
||||||
|
#[test]
|
||||||
|
fn expr_if() {
|
||||||
|
let body = Opcodes::new(vec![
|
||||||
|
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::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||||
|
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L4
|
||||||
|
#[test]
|
||||||
|
fn br_0() {
|
||||||
|
let body = 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::I32Const(1), // [1]
|
||||||
|
Opcode::SetLocal(1), // [] + local1 = 1
|
||||||
|
Opcode::GetLocal(0), // [arg]
|
||||||
|
Opcode::I32Const(0), // [arg, 0]
|
||||||
|
Opcode::I32Eq, // [arg == 0]
|
||||||
|
Opcode::GetLocal(1), // [arg == 0, local1]
|
||||||
|
Opcode::I32Const(1), // [arg == 0, local1, 1]
|
||||||
|
Opcode::I32Eq, // [arg == 0, local1 == 1]
|
||||||
|
Opcode::I32Add, // [arg == 0 + local1 == 1]
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L26
|
||||||
|
#[test]
|
||||||
|
fn br_1() {
|
||||||
|
let body = 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::I32Const(1), // [1]
|
||||||
|
Opcode::SetLocal(2), // [] + local2 = 1
|
||||||
|
Opcode::GetLocal(0), // [arg]
|
||||||
|
Opcode::I32Const(0), // [arg, 0]
|
||||||
|
Opcode::I32Eq, // [arg == 0]
|
||||||
|
Opcode::GetLocal(1), // [arg == 0, local1]
|
||||||
|
Opcode::I32Const(0), // [arg == 0, local1, 0]
|
||||||
|
Opcode::I32Eq, // [arg == 0, local1 == 0]
|
||||||
|
Opcode::I32Add, // [arg == 0 + local1 == 0]
|
||||||
|
Opcode::GetLocal(2), // [arg == 0 + local1 == 0, local2]
|
||||||
|
Opcode::I32Const(1), // [arg == 0 + local1 == 0, local2, 1]
|
||||||
|
Opcode::I32Eq, // [arg == 0 + local1 == 0, local2 == 1]
|
||||||
|
Opcode::I32Add, // [arg == 0 + local1 == 0 + local2 == 1]
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L56
|
||||||
|
#[test]
|
||||||
|
fn br_2() {
|
||||||
|
let body = 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::I32Const(2), // [2]
|
||||||
|
Opcode::Return, // return 2
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L71
|
||||||
|
#[test]
|
||||||
|
fn br_3() {
|
||||||
|
let body = 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::GetLocal(1), // [local1]
|
||||||
|
Opcode::Return, // return local1
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-br.txt
|
||||||
|
#[test]
|
||||||
|
fn expr_br() {
|
||||||
|
let body = 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::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||||
|
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brif.txt
|
||||||
|
#[test]
|
||||||
|
fn brif() {
|
||||||
|
let body = 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::I32Const(2), // [2]
|
||||||
|
Opcode::Return, // return 2
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||||
|
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brif-loop.txt
|
||||||
|
#[test]
|
||||||
|
fn brif_loop() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-brif.txt
|
||||||
|
#[test]
|
||||||
|
fn expr_brif() {
|
||||||
|
let body = 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::GetLocal(1), // [local1]
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 3).unwrap(), 3);
|
||||||
|
assert_eq!(run_function_i32(&body, 10).unwrap(), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brtable.txt
|
||||||
|
#[test]
|
||||||
|
fn brtable() {
|
||||||
|
let body = 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::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 0);
|
||||||
|
assert_eq!(run_function_i32(&body, 1).unwrap(), 1);
|
||||||
|
assert_eq!(run_function_i32(&body, 2).unwrap(), 2);
|
||||||
|
assert_eq!(run_function_i32(&body, 3).unwrap(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/return.txt
|
||||||
|
#[test]
|
||||||
|
fn return_test() {
|
||||||
|
let body = Opcodes::new(vec![
|
||||||
|
Opcode::GetLocal(0),
|
||||||
|
Opcode::I32Const(0),
|
||||||
|
Opcode::I32Eq,
|
||||||
|
Opcode::If(BlockType::NoResult,
|
||||||
|
Opcodes::new(vec![
|
||||||
|
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::I32Const(3),
|
||||||
|
Opcode::Return,
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||||
|
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||||
|
assert_eq!(run_function_i32(&body, 5).unwrap(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/return-void.txt
|
||||||
|
#[test]
|
||||||
|
fn return_void() {
|
||||||
|
// TODO: linear memory required
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basics_loop() {
|
||||||
|
let body = Opcodes::new(vec![
|
||||||
|
Opcode::I32Const(2), // [2]
|
||||||
|
Opcode::SetLocal(1), // [] + local1 = 2
|
||||||
|
Opcode::Block(BlockType::NoResult, // add block label to exit from loop. TODO: is it the correct pattern?
|
||||||
|
Opcodes::new(vec![
|
||||||
|
Opcode::Loop(BlockType::NoResult, // start loop
|
||||||
|
Opcodes::new(vec![
|
||||||
|
Opcode::GetLocal(0), // [local0]
|
||||||
|
Opcode::I32Const(1), // [local0, 1]
|
||||||
|
Opcode::I32Sub, // [local0 - 1]
|
||||||
|
Opcode::SetLocal(0), // [] + local0 = local0 - 1
|
||||||
|
Opcode::GetLocal(0), // [local0]
|
||||||
|
Opcode::If(BlockType::NoResult, // if local0 != 0
|
||||||
|
Opcodes::new(vec![
|
||||||
|
Opcode::GetLocal(1), // [local1]
|
||||||
|
Opcode::I32Const(2), // [local1, 2]
|
||||||
|
Opcode::I32Mul, // [local1 * 2]
|
||||||
|
Opcode::SetLocal(1), // [] + local1 = local1 * 2
|
||||||
|
Opcode::Else, // else
|
||||||
|
Opcode::Br(2), // exit from loop (2 = if + loop)
|
||||||
|
Opcode::End, // end (if)
|
||||||
|
])),
|
||||||
|
Opcode::End, // end (loop)
|
||||||
|
])),
|
||||||
|
Opcode::End, // end (block)
|
||||||
|
])),
|
||||||
|
Opcode::GetLocal(1), // [local1]
|
||||||
|
Opcode::End]); // end (fun)
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 2).unwrap(), 4);
|
||||||
|
assert_eq!(run_function_i32(&body, 8).unwrap(), 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basics_if_then() {
|
||||||
|
let body = Opcodes::new(vec![
|
||||||
|
Opcode::I32Const(20), // 20
|
||||||
|
Opcode::GetLocal(0), // read argument
|
||||||
|
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
||||||
|
Opcodes::new(vec![
|
||||||
|
Opcode::I32Const(10), // 10
|
||||||
|
Opcode::End, // end
|
||||||
|
])),
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 20);
|
||||||
|
assert_eq!(run_function_i32(&body, 1).unwrap(), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basics_if_then_else() {
|
||||||
|
let body = Opcodes::new(vec![
|
||||||
|
Opcode::GetLocal(0), // read argument
|
||||||
|
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
||||||
|
Opcodes::new(vec![
|
||||||
|
Opcode::I32Const(10), // 10
|
||||||
|
Opcode::Else, // else
|
||||||
|
Opcode::I32Const(20), // 20
|
||||||
|
Opcode::End, // end
|
||||||
|
])),
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 20);
|
||||||
|
assert_eq!(run_function_i32(&body, 1).unwrap(), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basics_return() {
|
||||||
|
let body = Opcodes::new(vec![
|
||||||
|
Opcode::GetLocal(0), // read argument
|
||||||
|
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
||||||
|
Opcodes::new(vec![
|
||||||
|
Opcode::I32Const(20), // 20
|
||||||
|
Opcode::Return, // return
|
||||||
|
Opcode::End,
|
||||||
|
])),
|
||||||
|
Opcode::I32Const(10), // 10
|
||||||
|
Opcode::End]);
|
||||||
|
|
||||||
|
assert_eq!(run_function_i32(&body, 0).unwrap(), 10);
|
||||||
|
assert_eq!(run_function_i32(&body, 1).unwrap(), 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn branch_if() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn branch_table() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn drop() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn select() {
|
||||||
|
// TODO
|
||||||
|
}*/
|
Reference in New Issue
Block a user