mirror of
https://github.com/fluencelabs/wasmer
synced 2025-03-18 17:10:52 +00:00
feat(interface-types) Add an abstract Wasm instance and a runtime to the interpreter.
This commit is contained in:
parent
c63345029e
commit
62e1f7867b
@ -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(), &[]);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use crate::ast::InterfaceType;
|
||||
|
||||
pub mod interpreter;
|
||||
mod stack;
|
||||
pub mod wasm;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Instruction<'input> {
|
||||
|
@ -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>,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user