feat(interface-types) Implement the read-utf8 instruction.

It implies to create the `wasm::Memory` trait.

Also, the patch updates `wasm::Type` and `wasm::Value` to
`wasm::InterfaceType` and `wasm::InterfaceValue`. It enforces a new
rule that is: All values in the stack must be owned by the stack. Any
value going in or out must be cloned.
This commit is contained in:
Ivan Enderlin 2019-09-23 16:29:01 +02:00
parent aea18f6250
commit be5624e28b
3 changed files with 351 additions and 114 deletions

View File

@ -1,51 +1,56 @@
use crate::instructions::{ use crate::instructions::{
stack::{Stack, Stackable}, stack::{Stack, Stackable},
wasm::{self, Type, Value}, wasm::{self, InterfaceType, InterfaceValue},
Instruction, Instruction,
}; };
use std::{convert::TryFrom, marker::PhantomData}; use std::{cell::Cell, convert::TryFrom, marker::PhantomData};
struct Runtime<'invocation, 'instance, Instance, Export> struct Runtime<'invocation, 'instance, Instance, Export, Memory>
where where
Export: wasm::Export + 'instance, Export: wasm::Export + 'instance,
Instance: wasm::Instance<Export> + 'instance, Memory: wasm::Memory + 'instance,
Instance: wasm::Instance<Export, Memory> + 'instance,
{ {
invocation_inputs: &'invocation [Value], invocation_inputs: &'invocation [InterfaceValue],
stack: Stack<Value>, stack: Stack<InterfaceValue>,
wasm_instance: &'instance Instance, wasm_instance: &'instance Instance,
wasm_exports: PhantomData<Export>, wasm_exports: PhantomData<Export>,
wasm_memory: PhantomData<Memory>,
} }
type ExecutableInstruction<Instance, Export> = type ExecutableInstruction<Instance, Export, Memory> =
Box<dyn Fn(&mut Runtime<Instance, Export>) -> Result<(), String>>; Box<dyn Fn(&mut Runtime<Instance, Export, Memory>) -> Result<(), String>>;
pub struct Interpreter<Instance, Export> pub struct Interpreter<Instance, Export, Memory>
where where
Export: wasm::Export, Export: wasm::Export,
Instance: wasm::Instance<Export>, Memory: wasm::Memory,
Instance: wasm::Instance<Export, Memory>,
{ {
executable_instructions: Vec<ExecutableInstruction<Instance, Export>>, executable_instructions: Vec<ExecutableInstruction<Instance, Export, Memory>>,
} }
impl<Instance, Export> Interpreter<Instance, Export> impl<Instance, Export, Memory> Interpreter<Instance, Export, Memory>
where where
Export: wasm::Export, Export: wasm::Export,
Instance: wasm::Instance<Export>, Memory: wasm::Memory,
Instance: wasm::Instance<Export, Memory>,
{ {
fn iter(&self) -> impl Iterator<Item = &ExecutableInstruction<Instance, Export>> + '_ { fn iter(&self) -> impl Iterator<Item = &ExecutableInstruction<Instance, Export, Memory>> + '_ {
self.executable_instructions.iter() self.executable_instructions.iter()
} }
pub fn run( pub fn run(
&self, &self,
invocation_inputs: &[Value], invocation_inputs: &[InterfaceValue],
wasm_instance: &Instance, wasm_instance: &Instance,
) -> Result<Stack<Value>, String> { ) -> Result<Stack<InterfaceValue>, String> {
let mut runtime = Runtime { let mut runtime = Runtime {
invocation_inputs, invocation_inputs,
stack: Stack::new(), stack: Stack::new(),
wasm_instance, wasm_instance,
wasm_exports: PhantomData, wasm_exports: PhantomData,
wasm_memory: PhantomData,
}; };
for executable_instruction in self.iter() { for executable_instruction in self.iter() {
@ -59,11 +64,12 @@ where
} }
} }
impl<'binary_input, Instance, Export> TryFrom<&Vec<Instruction<'binary_input>>> impl<'binary_input, Instance, Export, Memory> TryFrom<&Vec<Instruction<'binary_input>>>
for Interpreter<Instance, Export> for Interpreter<Instance, Export, Memory>
where where
Export: wasm::Export, Export: wasm::Export,
Instance: wasm::Instance<Export>, Memory: wasm::Memory,
Instance: wasm::Instance<Export, Memory>,
{ {
type Error = String; type Error = String;
@ -71,13 +77,13 @@ where
let executable_instructions = instructions let executable_instructions = instructions
.iter() .iter()
.map( .map(
|instruction| -> ExecutableInstruction<Instance, Export> { |instruction| -> ExecutableInstruction<Instance, Export, Memory> {
match instruction { match instruction {
Instruction::ArgumentGet(index) => { Instruction::ArgumentGet(index) => {
let index = index.to_owned(); let index = index.to_owned();
let instruction_name: String = instruction.into(); let instruction_name: String = instruction.into();
Box::new(move |runtime: &mut Runtime<Instance, Export>| -> Result<(), _> { Box::new(move |runtime: &mut Runtime<Instance, Export, Memory>| -> Result<(), _> {
let invocation_inputs = runtime.invocation_inputs; let invocation_inputs = runtime.invocation_inputs;
if index >= (invocation_inputs.len() as u64) { if index >= (invocation_inputs.len() as u64) {
@ -87,7 +93,7 @@ where
)); ));
} }
runtime.stack.push(invocation_inputs[index as usize]); runtime.stack.push(invocation_inputs[index as usize].clone());
Ok(()) Ok(())
}) })
@ -96,7 +102,7 @@ where
let export_name = (*export_name).to_owned(); let export_name = (*export_name).to_owned();
let instruction_name: String = instruction.into(); let instruction_name: String = instruction.into();
Box::new(move |runtime: &mut Runtime<Instance, Export>| -> Result<(), _> { Box::new(move |runtime: &mut Runtime<Instance, Export, Memory>| -> Result<(), _> {
let instance = runtime.wasm_instance; let instance = runtime.wasm_instance;
match instance.export(&export_name) { match instance.export(&export_name) {
@ -108,11 +114,11 @@ where
let input_types = inputs let input_types = inputs
.iter() .iter()
.map(|input| input.into()) .map(|input| input.into())
.collect::<Vec<Type>>(); .collect::<Vec<InterfaceType>>();
if input_types != export.inputs() { if input_types != export.inputs() {
return Err(format!( return Err(format!(
"`{}` cannot call the exported function `{}` because the value types in the stack mismatch the function signature (expects {:?}).", "`{}` cannot call the exported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).",
instruction_name, instruction_name,
export_name, export_name,
export.inputs(), export.inputs(),
@ -122,7 +128,7 @@ where
match export.call(&inputs) { match export.call(&inputs) {
Ok(outputs) => { Ok(outputs) => {
for output in outputs.iter() { for output in outputs.iter() {
runtime.stack.push(*output); runtime.stack.push(output.clone());
} }
Ok(()) Ok(())
@ -135,7 +141,7 @@ where
} }
} }
None => Err(format!( None => Err(format!(
"`{}` cannot call the exported function `{}` because there is no enought data in the stack for the arguments (needs {}).", "`{}` cannot call the exported function `{}` because there is no enough data on the stack for the arguments (needs {}).",
instruction_name, instruction_name,
export_name, export_name,
inputs_cardinality, inputs_cardinality,
@ -151,16 +157,59 @@ where
}) })
} }
Instruction::ReadUtf8 => { Instruction::ReadUtf8 => {
Box::new(|_runtime: &mut Runtime<Instance, Export>| -> Result<(), _> { let instruction_name: String = instruction.into();
println!("read utf8");
Ok(()) Box::new(move |runtime: &mut Runtime<Instance, Export, Memory>| -> Result<(), _> {
match runtime.stack.pop(2) {
Some(inputs) => match runtime.wasm_instance.memory(0) {
Some(memory) => {
let length = i32::try_from(&inputs[0])? as usize;
let pointer = i32::try_from(&inputs[1])? as usize;
let memory_view = memory.view::<u8>();
if memory_view.len() < pointer + length {
return Err(format!(
"`{}` failed because it has to read out of the memory bounds (index {} > memory length {}).",
instruction_name,
pointer + length,
memory_view.len()
));
}
let data: Vec<u8> = (&memory_view[pointer..pointer + length])
.iter()
.map(Cell::get)
.collect();
match String::from_utf8(data) {
Ok(string) => {
runtime.stack.push(InterfaceValue::String(string));
Ok(())
}
Err(utf8_error) => Err(format!(
"`{}` failed because the read string isn't UTF-8 valid ({}).",
instruction_name,
utf8_error,
))
}
}
None => Err(format!(
"`{}` failed because there is no memory to read.",
instruction_name
))
}
None => Err(format!(
"`{}` failed because there is no enough data on the stack (needs 2).",
instruction_name,
))
}
}) })
} }
Instruction::Call(index) => { Instruction::Call(index) => {
let index = index.to_owned(); let index = index.to_owned();
Box::new(move |_runtime: &mut Runtime<Instance, Export>| -> Result<(), _> { Box::new(move |_runtime: &mut Runtime<Instance, Export, Memory>| -> Result<(), _> {
println!("call {}", index); println!("call {}", index);
Ok(()) Ok(())
@ -183,15 +232,15 @@ mod tests {
use super::Interpreter; use super::Interpreter;
use crate::instructions::{ use crate::instructions::{
stack::Stackable, stack::Stackable,
wasm::{self, Type, Value}, wasm::{self, InterfaceType, InterfaceValue},
Instruction, Instruction,
}; };
use std::{collections::HashMap, convert::TryInto}; use std::{cell::Cell, collections::HashMap, convert::TryInto};
struct Export { struct Export {
inputs: Vec<Type>, inputs: Vec<InterfaceType>,
outputs: Vec<Type>, outputs: Vec<InterfaceType>,
function: fn(arguments: &[Value]) -> Result<Vec<Value>, ()>, function: fn(arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>,
} }
impl wasm::Export for Export { impl wasm::Export for Export {
@ -203,21 +252,42 @@ mod tests {
self.outputs.len() self.outputs.len()
} }
fn inputs(&self) -> &[Type] { fn inputs(&self) -> &[InterfaceType] {
&self.inputs &self.inputs
} }
fn outputs(&self) -> &[Type] { fn outputs(&self) -> &[InterfaceType] {
&self.outputs &self.outputs
} }
fn call(&self, arguments: &[Value]) -> Result<Vec<Value>, ()> { fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
(self.function)(arguments) (self.function)(arguments)
} }
} }
#[derive(Default)]
struct Memory {
data: Vec<Cell<u8>>,
}
impl Memory {
fn new(data: Vec<Cell<u8>>) -> Self {
Self { data }
}
}
impl wasm::Memory for Memory {
fn view<V: wasm::ValueType>(&self) -> &[Cell<V>] {
let slice = self.data.as_slice();
unsafe { ::std::slice::from_raw_parts(slice.as_ptr() as *const Cell<V>, slice.len()) }
}
}
#[derive(Default)]
struct Instance { struct Instance {
exports: HashMap<String, Export>, exports: HashMap<String, Export>,
memory: Memory,
} }
impl Instance { impl Instance {
@ -228,27 +298,32 @@ mod tests {
hashmap.insert( hashmap.insert(
"sum".into(), "sum".into(),
Export { Export {
inputs: vec![Type::I32, Type::I32], inputs: vec![InterfaceType::I32, InterfaceType::I32],
outputs: vec![Type::I32], outputs: vec![InterfaceType::I32],
function: |arguments: &[Value]| { function: |arguments: &[InterfaceValue]| {
let a: i32 = (&arguments[0]).try_into().unwrap(); let a: i32 = (&arguments[0]).try_into().unwrap();
let b: i32 = (&arguments[1]).try_into().unwrap(); let b: i32 = (&arguments[1]).try_into().unwrap();
Ok(vec![Value::I32(a + b)]) Ok(vec![InterfaceValue::I32(a + b)])
}, },
}, },
); );
hashmap hashmap
}, },
memory: Memory::new(vec![]),
} }
} }
} }
impl wasm::Instance<Export> for Instance { impl wasm::Instance<Export, Memory> for Instance {
fn export(&self, export_name: &str) -> Option<&Export> { fn export(&self, export_name: &str) -> Option<&Export> {
self.exports.get(export_name) self.exports.get(export_name)
} }
fn memory(&self, _index: usize) -> Option<&Memory> {
Some(&self.memory)
}
} }
#[test] #[test]
@ -260,17 +335,17 @@ mod tests {
Instruction::ReadUtf8, Instruction::ReadUtf8,
Instruction::Call(7), Instruction::Call(7),
]; ];
let interpreter: Interpreter<(), ()> = (&instructions).try_into().unwrap(); let interpreter: Interpreter<(), (), ()> = (&instructions).try_into().unwrap();
assert_eq!(interpreter.executable_instructions.len(), 5); assert_eq!(interpreter.executable_instructions.len(), 5);
} }
#[test] #[test]
fn test_interpreter_argument_get() { fn test_interpreter_argument_get() {
let interpreter: Interpreter<Instance, Export> = let interpreter: Interpreter<Instance, Export, Memory> =
(&vec![Instruction::ArgumentGet(0)]).try_into().unwrap(); (&vec![Instruction::ArgumentGet(0)]).try_into().unwrap();
let invocation_inputs = vec![Value::I32(42)]; let invocation_inputs = vec![InterfaceValue::I32(42)];
let instance = Instance::new(); let instance = Instance::new();
let run = interpreter.run(&invocation_inputs, &instance); let run = interpreter.run(&invocation_inputs, &instance);
@ -278,15 +353,15 @@ mod tests {
let stack = run.unwrap(); let stack = run.unwrap();
assert_eq!(stack.as_slice(), &[Value::I32(42)]); assert_eq!(stack.as_slice(), &[InterfaceValue::I32(42)]);
} }
#[test] #[test]
fn test_interpreter_argument_get_invalid_index() { fn test_interpreter_argument_get_invalid_index() {
let interpreter: Interpreter<Instance, Export> = let interpreter: Interpreter<Instance, Export, Memory> =
(&vec![Instruction::ArgumentGet(1)]).try_into().unwrap(); (&vec![Instruction::ArgumentGet(1)]).try_into().unwrap();
let invocation_inputs = vec![Value::I32(42)]; let invocation_inputs = vec![InterfaceValue::I32(42)];
let instance = Instance::new(); let instance = Instance::new();
let run = interpreter.run(&invocation_inputs, &instance); let run = interpreter.run(&invocation_inputs, &instance);
@ -302,12 +377,12 @@ mod tests {
#[test] #[test]
fn test_interpreter_argument_get_argument_get() { fn test_interpreter_argument_get_argument_get() {
let interpreter: Interpreter<Instance, Export> = let interpreter: Interpreter<Instance, Export, Memory> =
(&vec![Instruction::ArgumentGet(0), Instruction::ArgumentGet(1)]) (&vec![Instruction::ArgumentGet(0), Instruction::ArgumentGet(1)])
.try_into() .try_into()
.unwrap(); .unwrap();
let invocation_inputs = vec![Value::I32(7), Value::I32(42)]; let invocation_inputs = vec![InterfaceValue::I32(7), InterfaceValue::I32(42)];
let instance = Instance::new(); let instance = Instance::new();
let run = interpreter.run(&invocation_inputs, &instance); let run = interpreter.run(&invocation_inputs, &instance);
@ -315,12 +390,15 @@ mod tests {
let stack = run.unwrap(); let stack = run.unwrap();
assert_eq!(stack.as_slice(), &[Value::I32(7), Value::I32(42)]); assert_eq!(
stack.as_slice(),
&[InterfaceValue::I32(7), InterfaceValue::I32(42)]
);
} }
#[test] #[test]
fn test_interpreter_call_export() { fn test_interpreter_call_export() {
let interpreter: Interpreter<Instance, Export> = (&vec![ let interpreter: Interpreter<Instance, Export, Memory> = (&vec![
Instruction::ArgumentGet(1), Instruction::ArgumentGet(1),
Instruction::ArgumentGet(0), Instruction::ArgumentGet(0),
Instruction::CallExport("sum"), Instruction::CallExport("sum"),
@ -328,7 +406,7 @@ mod tests {
.try_into() .try_into()
.unwrap(); .unwrap();
let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)];
let instance = Instance::new(); let instance = Instance::new();
let run = interpreter.run(&invocation_inputs, &instance); let run = interpreter.run(&invocation_inputs, &instance);
@ -336,12 +414,12 @@ mod tests {
let stack = run.unwrap(); let stack = run.unwrap();
assert_eq!(stack.as_slice(), &[Value::I32(7)]); assert_eq!(stack.as_slice(), &[InterfaceValue::I32(7)]);
} }
#[test] #[test]
fn test_interpreter_call_export_invalid_export_name() { fn test_interpreter_call_export_invalid_export_name() {
let interpreter: Interpreter<Instance, Export> = let interpreter: Interpreter<Instance, Export, Memory> =
(&vec![Instruction::CallExport("bar")]).try_into().unwrap(); (&vec![Instruction::CallExport("bar")]).try_into().unwrap();
let invocation_inputs = vec![]; let invocation_inputs = vec![];
@ -359,16 +437,16 @@ mod tests {
} }
#[test] #[test]
fn test_interpreter_call_export_too_small_stack() { fn test_interpreter_call_export_stack_is_too_small() {
let interpreter: Interpreter<Instance, Export> = (&vec![ let interpreter: Interpreter<Instance, Export, Memory> = (&vec![
Instruction::ArgumentGet(0), Instruction::ArgumentGet(0),
Instruction::CallExport("sum"), Instruction::CallExport("sum"),
// ^^^ `sum` expects 2 values in the stack, only one is present // ^^^ `sum` expects 2 values on the stack, only one is present
]) ])
.try_into() .try_into()
.unwrap(); .unwrap();
let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)];
let instance = Instance::new(); let instance = Instance::new();
let run = interpreter.run(&invocation_inputs, &instance); let run = interpreter.run(&invocation_inputs, &instance);
@ -378,13 +456,13 @@ mod tests {
assert_eq!( assert_eq!(
error, error,
String::from(r#"`call-export "sum"` cannot call the exported function `sum` because there is no enought data in the stack for the arguments (needs 2)."#) String::from(r#"`call-export "sum"` cannot call the exported function `sum` because there is no enough data on the stack for the arguments (needs 2)."#)
); );
} }
#[test] #[test]
fn test_interpreter_call_export_invalid_types_in_the_stack() { fn test_interpreter_call_export_invalid_types_in_the_stack() {
let interpreter: Interpreter<Instance, Export> = (&vec![ let interpreter: Interpreter<Instance, Export, Memory> = (&vec![
Instruction::ArgumentGet(1), Instruction::ArgumentGet(1),
Instruction::ArgumentGet(0), Instruction::ArgumentGet(0),
Instruction::CallExport("sum"), Instruction::CallExport("sum"),
@ -392,7 +470,7 @@ mod tests {
.try_into() .try_into()
.unwrap(); .unwrap();
let invocation_inputs = vec![Value::I32(3), Value::I64(4)]; let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I64(4)];
// ^^^ mismatch with `sum` signature // ^^^ mismatch with `sum` signature
let instance = Instance::new(); let instance = Instance::new();
let run = interpreter.run(&invocation_inputs, &instance); let run = interpreter.run(&invocation_inputs, &instance);
@ -403,13 +481,13 @@ mod tests {
assert_eq!( assert_eq!(
error, error,
String::from(r#"`call-export "sum"` cannot call the exported function `sum` because the value types in the stack mismatch the function signature (expects [I32, I32])."#) String::from(r#"`call-export "sum"` cannot call the exported function `sum` because the value types on the stack mismatch the function signature (expects [I32, I32])."#)
); );
} }
#[test] #[test]
fn test_interpreter_call_export_failed_when_calling() { fn test_interpreter_call_export_failed_when_calling() {
let interpreter: Interpreter<Instance, Export> = (&vec![ let interpreter: Interpreter<Instance, Export, Memory> = (&vec![
Instruction::ArgumentGet(1), Instruction::ArgumentGet(1),
Instruction::ArgumentGet(0), Instruction::ArgumentGet(0),
Instruction::CallExport("sum"), Instruction::CallExport("sum"),
@ -417,15 +495,15 @@ mod tests {
.try_into() .try_into()
.unwrap(); .unwrap();
let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)];
let instance = Instance { let instance = Instance {
exports: { exports: {
let mut hashmap = HashMap::new(); let mut hashmap = HashMap::new();
hashmap.insert( hashmap.insert(
"sum".into(), "sum".into(),
Export { Export {
inputs: vec![Type::I32, Type::I32], inputs: vec![InterfaceType::I32, InterfaceType::I32],
outputs: vec![Type::I32], outputs: vec![InterfaceType::I32],
function: |_| Err(()), function: |_| Err(()),
// ^^^^^^^ function fails // ^^^^^^^ function fails
}, },
@ -433,6 +511,7 @@ mod tests {
hashmap hashmap
}, },
..Default::default()
}; };
let run = interpreter.run(&invocation_inputs, &instance); let run = interpreter.run(&invocation_inputs, &instance);
@ -448,7 +527,7 @@ mod tests {
#[test] #[test]
fn test_interpreter_call_export_that_returns_nothing() { fn test_interpreter_call_export_that_returns_nothing() {
let interpreter: Interpreter<Instance, Export> = (&vec![ let interpreter: Interpreter<Instance, Export, Memory> = (&vec![
Instruction::ArgumentGet(1), Instruction::ArgumentGet(1),
Instruction::ArgumentGet(0), Instruction::ArgumentGet(0),
Instruction::CallExport("sum"), Instruction::CallExport("sum"),
@ -456,15 +535,15 @@ mod tests {
.try_into() .try_into()
.unwrap(); .unwrap();
let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)];
let instance = Instance { let instance = Instance {
exports: { exports: {
let mut hashmap = HashMap::new(); let mut hashmap = HashMap::new();
hashmap.insert( hashmap.insert(
"sum".into(), "sum".into(),
Export { Export {
inputs: vec![Type::I32, Type::I32], inputs: vec![InterfaceType::I32, InterfaceType::I32],
outputs: vec![Type::I32], outputs: vec![InterfaceType::I32],
function: |_| Ok(vec![]), function: |_| Ok(vec![]),
// ^^^^^^^^^^ void function // ^^^^^^^^^^ void function
}, },
@ -472,6 +551,7 @@ mod tests {
hashmap hashmap
}, },
..Default::default()
}; };
let run = interpreter.run(&invocation_inputs, &instance); let run = interpreter.run(&invocation_inputs, &instance);
@ -481,4 +561,128 @@ mod tests {
assert!(stack.is_empty()); assert!(stack.is_empty());
} }
#[test]
fn test_interpreter_read_utf8() {
let interpreter: Interpreter<Instance, Export, Memory> = (&vec![
Instruction::ArgumentGet(1),
Instruction::ArgumentGet(0),
Instruction::ReadUtf8,
])
.try_into()
.unwrap();
let invocation_inputs = vec![InterfaceValue::I32(13), InterfaceValue::I32(0)];
// ^^^^^^^ length ^^^^^^ pointer
let instance = Instance {
memory: Memory::new(
"Hello, World!"
.as_bytes()
.iter()
.map(|u| Cell::new(*u))
.collect(),
),
..Default::default()
};
let run = interpreter.run(&invocation_inputs, &instance);
assert!(run.is_ok());
let stack = run.unwrap();
assert_eq!(
stack.as_slice(),
&[InterfaceValue::String("Hello, World!".into())]
);
}
#[test]
fn test_interpreter_read_utf8_out_of_memory() {
let interpreter: Interpreter<Instance, Export, Memory> = (&vec![
Instruction::ArgumentGet(1),
Instruction::ArgumentGet(0),
Instruction::ReadUtf8,
])
.try_into()
.unwrap();
let invocation_inputs = vec![InterfaceValue::I32(13), InterfaceValue::I32(0)];
// ^^^^^^^ length ^^^^^^ pointer
// is too long
let instance = Instance {
memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
..Default::default()
};
let run = interpreter.run(&invocation_inputs, &instance);
assert!(run.is_err());
let error = run.unwrap_err();
assert_eq!(
error,
String::from(
r#"`read-utf8` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#
)
);
}
#[test]
fn test_interpreter_read_utf8_invalid_encoding() {
let interpreter: Interpreter<Instance, Export, Memory> = (&vec![
Instruction::ArgumentGet(1),
Instruction::ArgumentGet(0),
Instruction::ReadUtf8,
])
.try_into()
.unwrap();
let invocation_inputs = vec![InterfaceValue::I32(4), InterfaceValue::I32(0)];
// ^^^^^^ length ^^^^^^ pointer
let instance = Instance {
memory: Memory::new(
vec![0, 159, 146, 150]
.iter()
.map(|b| Cell::new(*b))
.collect::<Vec<Cell<u8>>>(),
),
..Default::default()
};
let run = interpreter.run(&invocation_inputs, &instance);
assert!(run.is_err());
let error = run.unwrap_err();
assert_eq!(
error,
String::from(r#"`read-utf8` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#)
);
}
#[test]
fn test_interpreter_read_utf8_stack_is_too_small() {
let interpreter: Interpreter<Instance, Export, Memory> = (&vec![
Instruction::ArgumentGet(0),
Instruction::ReadUtf8,
// ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present.
])
.try_into()
.unwrap();
let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)];
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(
r#"`read-utf8` failed because there is no enough data on the stack (needs 2)."#
)
);
}
} }

View File

@ -11,14 +11,14 @@ pub trait Stackable {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Stack<T> pub struct Stack<T>
where where
T: Default, T: Default + Clone,
{ {
inner: Vec<T>, inner: Vec<T>,
} }
impl<T> Stack<T> impl<T> Stack<T>
where where
T: Default, T: Default + Clone,
{ {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@ -29,7 +29,7 @@ where
impl<T> Stackable for Stack<T> impl<T> Stackable for Stack<T>
where where
T: Default, T: Default + Clone,
{ {
type Item = T; type Item = T;

View File

@ -1,55 +1,56 @@
use std::convert::TryFrom; use std::{cell::Cell, convert::TryFrom};
#[derive(Debug, PartialEq)] pub use crate::ast::InterfaceType;
pub enum Type {
I32,
I64,
F32,
F64,
V128,
}
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Value { pub enum InterfaceValue {
Int(isize),
Float(f64),
Any(isize),
String(String),
// Seq(…),
I32(i32), I32(i32),
I64(i64), I64(i64),
F32(f32), F32(f32),
F64(f64), F64(f64),
V128(u128), // AnyRef(…),
} }
impl From<&Value> for Type { impl From<&InterfaceValue> for InterfaceType {
fn from(value: &Value) -> Self { fn from(value: &InterfaceValue) -> Self {
match value { match value {
Value::I32(_) => Type::I32, InterfaceValue::Int(_) => Self::Int,
Value::I64(_) => Type::I64, InterfaceValue::Float(_) => Self::Float,
Value::F32(_) => Type::F32, InterfaceValue::Any(_) => Self::Any,
Value::F64(_) => Type::F64, InterfaceValue::String(_) => Self::String,
Value::V128(_) => Type::V128, InterfaceValue::I32(_) => Self::I32,
InterfaceValue::I64(_) => Self::I64,
InterfaceValue::F32(_) => Self::F32,
InterfaceValue::F64(_) => Self::F64,
} }
} }
} }
impl Default for Value { impl Default for InterfaceValue {
fn default() -> Self { fn default() -> Self {
Self::I32(0) Self::I32(0)
} }
} }
macro_rules! from_x_for_value { macro_rules! from_x_for_interface_value {
($native_type:ty, $value_variant:ident) => { ($native_type:ty, $value_variant:ident) => {
impl From<$native_type> for Value { impl From<$native_type> for InterfaceValue {
fn from(n: $native_type) -> Self { fn from(n: $native_type) -> Self {
Self::$value_variant(n) Self::$value_variant(n)
} }
} }
impl TryFrom<&Value> for $native_type { impl TryFrom<&InterfaceValue> for $native_type {
type Error = &'static str; type Error = &'static str;
fn try_from(w: &Value) -> Result<Self, Self::Error> { fn try_from(w: &InterfaceValue) -> Result<Self, Self::Error> {
match *w { match *w {
Value::$value_variant(n) => Ok(n), InterfaceValue::$value_variant(n) => Ok(n),
_ => Err("Invalid cast."), _ => Err("Invalid cast."),
} }
} }
@ -57,25 +58,46 @@ macro_rules! from_x_for_value {
}; };
} }
from_x_for_value!(i32, I32); from_x_for_interface_value!(i32, I32);
from_x_for_value!(i64, I64); from_x_for_interface_value!(i64, I64);
from_x_for_value!(f32, F32); from_x_for_interface_value!(f32, F32);
from_x_for_value!(f64, F64); from_x_for_interface_value!(f64, F64);
from_x_for_value!(u128, V128);
pub trait ValueType: Copy + Sized {}
macro_rules! value_type {
($native_type:ty) => {
impl ValueType for $native_type {}
};
($($native_type:ty),*) => {
$(
value_type!($native_type);
)*
};
}
value_type!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64);
pub trait Export { pub trait Export {
fn inputs_cardinality(&self) -> usize; fn inputs_cardinality(&self) -> usize;
fn outputs_cardinality(&self) -> usize; fn outputs_cardinality(&self) -> usize;
fn inputs(&self) -> &[Type]; fn inputs(&self) -> &[InterfaceType];
fn outputs(&self) -> &[Type]; fn outputs(&self) -> &[InterfaceType];
fn call(&self, arguments: &[Value]) -> Result<Vec<Value>, ()>; fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>;
} }
pub trait Instance<E> pub trait Memory {
fn view<V: ValueType>(&self) -> &[Cell<V>];
}
pub trait Instance<E, M>
where where
E: Export, E: Export,
M: Memory,
{ {
fn export(&self, export_name: &str) -> Option<&E>; fn export(&self, export_name: &str) -> Option<&E>;
fn memory(&self, index: usize) -> Option<&M>;
} }
impl Export for () { impl Export for () {
@ -87,24 +109,35 @@ impl Export for () {
0 0
} }
fn inputs(&self) -> &[Type] { fn inputs(&self) -> &[InterfaceType] {
&[] &[]
} }
fn outputs(&self) -> &[Type] { fn outputs(&self) -> &[InterfaceType] {
&[] &[]
} }
fn call(&self, _arguments: &[Value]) -> Result<Vec<Value>, ()> { fn call(&self, _arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
Err(()) Err(())
} }
} }
impl<E> Instance<E> for () impl Memory for () {
fn view<V: ValueType>(&self) -> &[Cell<V>] {
&[]
}
}
impl<E, M> Instance<E, M> for ()
where where
E: Export, E: Export,
M: Memory,
{ {
fn export(&self, _export_name: &str) -> Option<&E> { fn export(&self, _export_name: &str) -> Option<&E> {
None None
} }
fn memory(&self, _: usize) -> Option<&M> {
None
}
} }