feat(interface-types) Add an abstract Wasm instance and a runtime to the interpreter.

This commit is contained in:
Ivan Enderlin 2019-09-20 00:06:15 +02:00
parent c63345029e
commit 62e1f7867b
3 changed files with 173 additions and 54 deletions

View File

@ -1,78 +1,117 @@
use crate::instructions::{
stack::{Stack, Stackable},
Instruction,
wasm, Instruction,
};
use std::convert::TryFrom;
type ExecutableInstruction = Box<dyn Fn(&mut Stack<u64>) -> Result<(), &'static str>>;
pub(crate) struct Interpreter {
executable_instructions: Vec<ExecutableInstruction>,
struct Runtime<'invocation, 'instance, Instance>
where
Instance: wasm::Instance,
{
invocation_inputs: &'invocation Vec<u64>,
stack: Stack<u64>,
wasm_instance: &'instance Instance,
}
impl Interpreter {
fn iter(&self) -> impl Iterator<Item = &ExecutableInstruction> + '_ {
pub(crate) struct Interpreter<Instance>
where
Instance: wasm::Instance,
{
executable_instructions: Vec<Box<dyn Fn(&mut Runtime<Instance>) -> Result<(), String>>>,
}
impl<Instance> Interpreter<Instance>
where
Instance: wasm::Instance,
{
fn iter(
&self,
) -> impl Iterator<Item = &Box<dyn Fn(&mut Runtime<Instance>) -> Result<(), String>>> + '_ {
self.executable_instructions.iter()
}
pub(crate) fn run(&self) -> Result<Stack<u64>, &'static str> {
let mut stack = Stack::new();
pub(crate) fn run(
&self,
invocation_inputs: &Vec<u64>,
wasm_instance: &Instance,
) -> Result<Stack<u64>, 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<Instruction<'binary_input>>> for Interpreter {
type Error = &'static str;
impl<'binary_input, Instance> TryFrom<&Vec<Instruction<'binary_input>>> for Interpreter<Instance>
where
Instance: wasm::Instance,
{
type Error = String;
fn try_from(instructions: &Vec<Instruction>) -> Result<Self, Self::Error> {
let executable_instructions = instructions
.iter()
.map(|instruction| -> ExecutableInstruction {
match instruction {
Instruction::ArgumentGet(index) => {
let index = index.to_owned();
.map(
|instruction| -> Box<dyn Fn(&mut Runtime<Instance>) -> Result<(), String>> {
match instruction {
Instruction::ArgumentGet(index) => {
let index = index.to_owned();
let instruction_name: String = instruction.into();
Box::new(move |stack: &mut Stack<u64>| -> Result<(), _> {
println!("argument get {}", index);
stack.push(index);
Box::new(move |runtime: &mut Runtime<Instance>| -> 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<Instance>| -> Result<(), _> {
println!("call export {}", export_name);
Ok(())
})
}
Instruction::ReadUtf8 => {
Box::new(|_runtime: &mut Runtime<Instance>| -> Result<(), _> {
println!("read utf8");
Ok(())
})
}
Instruction::Call(index) => {
let index = index.to_owned();
Box::new(move |_runtime: &mut Runtime<Instance>| -> Result<(), _> {
println!("call {}", index);
Ok(())
})
}
_ => unimplemented!(),
}
Instruction::CallExport(export_name) => {
let export_name = (*export_name).to_owned();
Box::new(move |_stack: &mut Stack<u64>| -> Result<(), _> {
println!("call export {}", export_name);
Ok(())
})
}
Instruction::ReadUtf8 => Box::new(|_stack: &mut Stack<u64>| -> Result<(), _> {
println!("read utf8");
Ok(())
}),
Instruction::Call(index) => {
let index = index.to_owned();
Box::new(move |_stack: &mut Stack<u64>| -> Result<(), _> {
println!("call {}", index);
Ok(())
})
}
_ => unimplemented!(),
}
})
},
)
.collect();
Ok(Interpreter {
@ -84,27 +123,53 @@ impl<'binary_input> TryFrom<&Vec<Instruction<'binary_input>>> 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<String, ()>,
}
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<Instance> =
(&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<Instance> =
(&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<Instance> =
(&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<Instance> =
(&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(), &[]);
}
*/
}

View File

@ -2,6 +2,7 @@ use crate::ast::InterfaceType;
pub mod interpreter;
mod stack;
pub mod wasm;
#[derive(PartialEq, Debug)]
pub enum Instruction<'input> {

View File

@ -8,6 +8,7 @@ pub(crate) trait Stackable {
fn pop(&mut self, n: usize) -> Option<Vec<Self::Item>>;
}
#[derive(Debug)]
pub(crate) struct Stack<T> {
inner: Vec<T>,
}