diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 11d6ddae6..2859f8069 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -1,78 +1,117 @@ use crate::instructions::{ stack::{Stack, Stackable}, - Instruction, + wasm, Instruction, }; use std::convert::TryFrom; -type ExecutableInstruction = Box) -> Result<(), &'static str>>; - -pub(crate) struct Interpreter { - executable_instructions: Vec, +struct Runtime<'invocation, 'instance, Instance> +where + Instance: wasm::Instance, +{ + invocation_inputs: &'invocation Vec, + stack: Stack, + wasm_instance: &'instance Instance, } -impl Interpreter { - fn iter(&self) -> impl Iterator + '_ { +pub(crate) struct Interpreter +where + Instance: wasm::Instance, +{ + executable_instructions: Vec) -> Result<(), String>>>, +} + +impl Interpreter +where + Instance: wasm::Instance, +{ + fn iter( + &self, + ) -> impl Iterator) -> Result<(), String>>> + '_ { self.executable_instructions.iter() } - pub(crate) fn run(&self) -> Result, &'static str> { - let mut stack = Stack::new(); + pub(crate) fn run( + &self, + invocation_inputs: &Vec, + wasm_instance: &Instance, + ) -> Result, String> { + let mut runtime = Runtime { + invocation_inputs, + stack: Stack::new(), + wasm_instance, + }; for executable_instruction in self.iter() { - match executable_instruction(&mut stack) { + match executable_instruction(&mut runtime) { Ok(_) => continue, Err(message) => return Err(message), } } - Ok(stack) + Ok(runtime.stack) } } -impl<'binary_input> TryFrom<&Vec>> for Interpreter { - type Error = &'static str; +impl<'binary_input, Instance> TryFrom<&Vec>> for Interpreter +where + Instance: wasm::Instance, +{ + type Error = String; fn try_from(instructions: &Vec) -> Result { let executable_instructions = instructions .iter() - .map(|instruction| -> ExecutableInstruction { - match instruction { - Instruction::ArgumentGet(index) => { - let index = index.to_owned(); + .map( + |instruction| -> Box) -> Result<(), String>> { + match instruction { + Instruction::ArgumentGet(index) => { + let index = index.to_owned(); + let instruction_name: String = instruction.into(); - Box::new(move |stack: &mut Stack| -> Result<(), _> { - println!("argument get {}", index); - stack.push(index); + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + let invocation_inputs = runtime.invocation_inputs; - Ok(()) - }) + if index >= (invocation_inputs.len() as u64) { + return Err(format!( + "`{}` cannot access argument #{} because it does't exist.", + instruction_name, index + )); + } + + runtime.stack.push(invocation_inputs[index as usize]); + + Ok(()) + }) + } + Instruction::CallExport(export_name) => { + let export_name = (*export_name).to_owned(); + + Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { + println!("call export {}", export_name); + + Ok(()) + }) + } + Instruction::ReadUtf8 => { + Box::new(|_runtime: &mut Runtime| -> Result<(), _> { + println!("read utf8"); + + Ok(()) + }) + } + Instruction::Call(index) => { + let index = index.to_owned(); + + Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { + println!("call {}", index); + + Ok(()) + }) + } + _ => unimplemented!(), } - Instruction::CallExport(export_name) => { - let export_name = (*export_name).to_owned(); - - Box::new(move |_stack: &mut Stack| -> Result<(), _> { - println!("call export {}", export_name); - - Ok(()) - }) - } - Instruction::ReadUtf8 => Box::new(|_stack: &mut Stack| -> Result<(), _> { - println!("read utf8"); - - Ok(()) - }), - Instruction::Call(index) => { - let index = index.to_owned(); - - Box::new(move |_stack: &mut Stack| -> Result<(), _> { - println!("call {}", index); - - Ok(()) - }) - } - _ => unimplemented!(), - } - }) + }, + ) .collect(); Ok(Interpreter { @@ -84,27 +123,53 @@ impl<'binary_input> TryFrom<&Vec>> for Interpreter { #[cfg(test)] mod tests { use super::Interpreter; - use crate::instructions::{stack::Stackable, Instruction}; - use std::convert::TryInto; + use crate::instructions::{stack::Stackable, wasm, Instruction}; + use std::{collections::HashMap, convert::TryInto}; + + struct Instance { + exports: HashMap, + } + + impl Instance { + fn new() -> Self { + Self { + exports: { + let mut hashmap = HashMap::new(); + hashmap.insert("foo".into(), ()); + + hashmap + }, + } + } + } + + impl wasm::Instance for Instance { + fn export_exists(&self, export_name: &str) -> bool { + self.exports.contains_key(export_name) + } + } #[test] fn test_interpreter_from_instructions() { let instructions = vec![ Instruction::ArgumentGet(0), Instruction::ArgumentGet(0), - Instruction::CallExport("strlen"), + Instruction::CallExport("foo"), Instruction::ReadUtf8, Instruction::Call(7), ]; - let interpreter: Interpreter = (&instructions).try_into().unwrap(); + let interpreter: Interpreter<()> = (&instructions).try_into().unwrap(); assert_eq!(interpreter.executable_instructions.len(), 5); } #[test] fn test_interpreter_argument_get() { - let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(42)]).try_into().unwrap(); - let run = interpreter.run(); + let interpreter: Interpreter = + (&vec![Instruction::ArgumentGet(0)]).try_into().unwrap(); + let invocation_inputs = vec![42]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); assert!(run.is_ok()); @@ -112,4 +177,56 @@ mod tests { assert_eq!(stack.as_slice(), &[42]); } + + #[test] + fn test_interpreter_argument_get_invalid_index() { + let interpreter: Interpreter = + (&vec![Instruction::ArgumentGet(1)]).try_into().unwrap(); + let invocation_inputs = vec![42]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from("`arg.get 1` cannot access argument #1 because it does't exist.") + ); + } + + #[test] + fn test_interpreter_argument_get_argument_get() { + let interpreter: Interpreter = + (&vec![Instruction::ArgumentGet(0), Instruction::ArgumentGet(1)]) + .try_into() + .unwrap(); + let invocation_inputs = vec![7, 42]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_ok()); + + let stack = run.unwrap(); + + assert_eq!(stack.as_slice(), &[7, 42]); + } + + /* + #[test] + fn test_interpreter_call_export() { + let interpreter: Interpreter = + (&vec![Instruction::ArgumentGet(7), Instruction::ArgumentGet(42)]) + .try_into() + .unwrap(); + let run = interpreter.run(&Instance::new()); + + assert!(run.is_ok()); + + let stack = run.unwrap(); + + assert_eq!(stack.as_slice(), &[]); + } + */ } diff --git a/lib/interface-types/src/instructions/mod.rs b/lib/interface-types/src/instructions/mod.rs index eb62f43d2..43e536c4b 100644 --- a/lib/interface-types/src/instructions/mod.rs +++ b/lib/interface-types/src/instructions/mod.rs @@ -2,6 +2,7 @@ use crate::ast::InterfaceType; pub mod interpreter; mod stack; +pub mod wasm; #[derive(PartialEq, Debug)] pub enum Instruction<'input> { diff --git a/lib/interface-types/src/instructions/stack.rs b/lib/interface-types/src/instructions/stack.rs index ff68d9380..c9b42d6ad 100644 --- a/lib/interface-types/src/instructions/stack.rs +++ b/lib/interface-types/src/instructions/stack.rs @@ -8,6 +8,7 @@ pub(crate) trait Stackable { fn pop(&mut self, n: usize) -> Option>; } +#[derive(Debug)] pub(crate) struct Stack { inner: Vec, }