diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 7f7d75aa0..4028cd7ad 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -1,51 +1,56 @@ use crate::instructions::{ stack::{Stack, Stackable}, - wasm::{self, Type, Value}, + wasm::{self, InterfaceType, InterfaceValue}, 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 Export: wasm::Export + 'instance, - Instance: wasm::Instance + 'instance, + Memory: wasm::Memory + 'instance, + Instance: wasm::Instance + 'instance, { - invocation_inputs: &'invocation [Value], - stack: Stack, + invocation_inputs: &'invocation [InterfaceValue], + stack: Stack, wasm_instance: &'instance Instance, wasm_exports: PhantomData, + wasm_memory: PhantomData, } -type ExecutableInstruction = - Box) -> Result<(), String>>; +type ExecutableInstruction = + Box) -> Result<(), String>>; -pub struct Interpreter +pub struct Interpreter where Export: wasm::Export, - Instance: wasm::Instance, + Memory: wasm::Memory, + Instance: wasm::Instance, { - executable_instructions: Vec>, + executable_instructions: Vec>, } -impl Interpreter +impl Interpreter where Export: wasm::Export, - Instance: wasm::Instance, + Memory: wasm::Memory, + Instance: wasm::Instance, { - fn iter(&self) -> impl Iterator> + '_ { + fn iter(&self) -> impl Iterator> + '_ { self.executable_instructions.iter() } pub fn run( &self, - invocation_inputs: &[Value], + invocation_inputs: &[InterfaceValue], wasm_instance: &Instance, - ) -> Result, String> { + ) -> Result, String> { let mut runtime = Runtime { invocation_inputs, stack: Stack::new(), wasm_instance, wasm_exports: PhantomData, + wasm_memory: PhantomData, }; for executable_instruction in self.iter() { @@ -59,11 +64,12 @@ where } } -impl<'binary_input, Instance, Export> TryFrom<&Vec>> - for Interpreter +impl<'binary_input, Instance, Export, Memory> TryFrom<&Vec>> + for Interpreter where Export: wasm::Export, - Instance: wasm::Instance, + Memory: wasm::Memory, + Instance: wasm::Instance, { type Error = String; @@ -71,13 +77,13 @@ where let executable_instructions = instructions .iter() .map( - |instruction| -> ExecutableInstruction { + |instruction| -> ExecutableInstruction { match instruction { Instruction::ArgumentGet(index) => { let index = index.to_owned(); let instruction_name: String = instruction.into(); - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { let invocation_inputs = runtime.invocation_inputs; 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(()) }) @@ -96,7 +102,7 @@ where let export_name = (*export_name).to_owned(); let instruction_name: String = instruction.into(); - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { let instance = runtime.wasm_instance; match instance.export(&export_name) { @@ -108,11 +114,11 @@ where let input_types = inputs .iter() .map(|input| input.into()) - .collect::>(); + .collect::>(); if input_types != export.inputs() { 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, export_name, export.inputs(), @@ -122,7 +128,7 @@ where match export.call(&inputs) { Ok(outputs) => { for output in outputs.iter() { - runtime.stack.push(*output); + runtime.stack.push(output.clone()); } Ok(()) @@ -135,7 +141,7 @@ where } } 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, export_name, inputs_cardinality, @@ -151,16 +157,59 @@ where }) } Instruction::ReadUtf8 => { - Box::new(|_runtime: &mut Runtime| -> Result<(), _> { - println!("read utf8"); + let instruction_name: String = instruction.into(); - Ok(()) + Box::new(move |runtime: &mut Runtime| -> 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::(); + + 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 = (&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) => { let index = index.to_owned(); - Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { + Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { println!("call {}", index); Ok(()) @@ -183,15 +232,15 @@ mod tests { use super::Interpreter; use crate::instructions::{ stack::Stackable, - wasm::{self, Type, Value}, + wasm::{self, InterfaceType, InterfaceValue}, Instruction, }; - use std::{collections::HashMap, convert::TryInto}; + use std::{cell::Cell, collections::HashMap, convert::TryInto}; struct Export { - inputs: Vec, - outputs: Vec, - function: fn(arguments: &[Value]) -> Result, ()>, + inputs: Vec, + outputs: Vec, + function: fn(arguments: &[InterfaceValue]) -> Result, ()>, } impl wasm::Export for Export { @@ -203,21 +252,42 @@ mod tests { self.outputs.len() } - fn inputs(&self) -> &[Type] { + fn inputs(&self) -> &[InterfaceType] { &self.inputs } - fn outputs(&self) -> &[Type] { + fn outputs(&self) -> &[InterfaceType] { &self.outputs } - fn call(&self, arguments: &[Value]) -> Result, ()> { + fn call(&self, arguments: &[InterfaceValue]) -> Result, ()> { (self.function)(arguments) } } + #[derive(Default)] + struct Memory { + data: Vec>, + } + + impl Memory { + fn new(data: Vec>) -> Self { + Self { data } + } + } + + impl wasm::Memory for Memory { + fn view(&self) -> &[Cell] { + let slice = self.data.as_slice(); + + unsafe { ::std::slice::from_raw_parts(slice.as_ptr() as *const Cell, slice.len()) } + } + } + + #[derive(Default)] struct Instance { exports: HashMap, + memory: Memory, } impl Instance { @@ -228,27 +298,32 @@ mod tests { hashmap.insert( "sum".into(), Export { - inputs: vec![Type::I32, Type::I32], - outputs: vec![Type::I32], - function: |arguments: &[Value]| { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |arguments: &[InterfaceValue]| { let a: i32 = (&arguments[0]).try_into().unwrap(); let b: i32 = (&arguments[1]).try_into().unwrap(); - Ok(vec![Value::I32(a + b)]) + Ok(vec![InterfaceValue::I32(a + b)]) }, }, ); hashmap }, + memory: Memory::new(vec![]), } } } - impl wasm::Instance for Instance { + impl wasm::Instance for Instance { fn export(&self, export_name: &str) -> Option<&Export> { self.exports.get(export_name) } + + fn memory(&self, _index: usize) -> Option<&Memory> { + Some(&self.memory) + } } #[test] @@ -260,17 +335,17 @@ mod tests { 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 = + let interpreter: Interpreter = (&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 run = interpreter.run(&invocation_inputs, &instance); @@ -278,15 +353,15 @@ mod tests { let stack = run.unwrap(); - assert_eq!(stack.as_slice(), &[Value::I32(42)]); + assert_eq!(stack.as_slice(), &[InterfaceValue::I32(42)]); } #[test] fn test_interpreter_argument_get_invalid_index() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&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 run = interpreter.run(&invocation_inputs, &instance); @@ -302,12 +377,12 @@ mod tests { #[test] fn test_interpreter_argument_get_argument_get() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(0), Instruction::ArgumentGet(1)]) .try_into() .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 run = interpreter.run(&invocation_inputs, &instance); @@ -315,12 +390,15 @@ mod tests { 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] fn test_interpreter_call_export() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -328,7 +406,7 @@ mod tests { .try_into() .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 run = interpreter.run(&invocation_inputs, &instance); @@ -336,12 +414,12 @@ mod tests { let stack = run.unwrap(); - assert_eq!(stack.as_slice(), &[Value::I32(7)]); + assert_eq!(stack.as_slice(), &[InterfaceValue::I32(7)]); } #[test] fn test_interpreter_call_export_invalid_export_name() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::CallExport("bar")]).try_into().unwrap(); let invocation_inputs = vec![]; @@ -359,16 +437,16 @@ mod tests { } #[test] - fn test_interpreter_call_export_too_small_stack() { - let interpreter: Interpreter = (&vec![ + fn test_interpreter_call_export_stack_is_too_small() { + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(0), 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() .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 run = interpreter.run(&invocation_inputs, &instance); @@ -378,13 +456,13 @@ mod tests { assert_eq!( 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] fn test_interpreter_call_export_invalid_types_in_the_stack() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -392,7 +470,7 @@ mod tests { .try_into() .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 let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -403,13 +481,13 @@ mod tests { assert_eq!( 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] fn test_interpreter_call_export_failed_when_calling() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -417,15 +495,15 @@ mod tests { .try_into() .unwrap(); - let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; let instance = Instance { exports: { let mut hashmap = HashMap::new(); hashmap.insert( "sum".into(), Export { - inputs: vec![Type::I32, Type::I32], - outputs: vec![Type::I32], + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], function: |_| Err(()), // ^^^^^^^ function fails }, @@ -433,6 +511,7 @@ mod tests { hashmap }, + ..Default::default() }; let run = interpreter.run(&invocation_inputs, &instance); @@ -448,7 +527,7 @@ mod tests { #[test] fn test_interpreter_call_export_that_returns_nothing() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -456,15 +535,15 @@ mod tests { .try_into() .unwrap(); - let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; let instance = Instance { exports: { let mut hashmap = HashMap::new(); hashmap.insert( "sum".into(), Export { - inputs: vec![Type::I32, Type::I32], - outputs: vec![Type::I32], + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], function: |_| Ok(vec![]), // ^^^^^^^^^^ void function }, @@ -472,6 +551,7 @@ mod tests { hashmap }, + ..Default::default() }; let run = interpreter.run(&invocation_inputs, &instance); @@ -481,4 +561,128 @@ mod tests { assert!(stack.is_empty()); } + + #[test] + fn test_interpreter_read_utf8() { + let interpreter: Interpreter = (&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 = (&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 = (&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::>>(), + ), + ..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 = (&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)."# + ) + ); + } } diff --git a/lib/interface-types/src/instructions/stack.rs b/lib/interface-types/src/instructions/stack.rs index d62d40d7c..57b999df2 100644 --- a/lib/interface-types/src/instructions/stack.rs +++ b/lib/interface-types/src/instructions/stack.rs @@ -11,14 +11,14 @@ pub trait Stackable { #[derive(Debug, Default)] pub struct Stack where - T: Default, + T: Default + Clone, { inner: Vec, } impl Stack where - T: Default, + T: Default + Clone, { pub fn new() -> Self { Self { @@ -29,7 +29,7 @@ where impl Stackable for Stack where - T: Default, + T: Default + Clone, { type Item = T; diff --git a/lib/interface-types/src/instructions/wasm.rs b/lib/interface-types/src/instructions/wasm.rs index 8857b6db5..33a25ed0a 100644 --- a/lib/interface-types/src/instructions/wasm.rs +++ b/lib/interface-types/src/instructions/wasm.rs @@ -1,55 +1,56 @@ -use std::convert::TryFrom; +use std::{cell::Cell, convert::TryFrom}; -#[derive(Debug, PartialEq)] -pub enum Type { - I32, - I64, - F32, - F64, - V128, -} +pub use crate::ast::InterfaceType; -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Value { +#[derive(Debug, Clone, PartialEq)] +pub enum InterfaceValue { + Int(isize), + Float(f64), + Any(isize), + String(String), + // Seq(…), I32(i32), I64(i64), F32(f32), F64(f64), - V128(u128), + // AnyRef(…), } -impl From<&Value> for Type { - fn from(value: &Value) -> Self { +impl From<&InterfaceValue> for InterfaceType { + fn from(value: &InterfaceValue) -> Self { match value { - Value::I32(_) => Type::I32, - Value::I64(_) => Type::I64, - Value::F32(_) => Type::F32, - Value::F64(_) => Type::F64, - Value::V128(_) => Type::V128, + InterfaceValue::Int(_) => Self::Int, + InterfaceValue::Float(_) => Self::Float, + InterfaceValue::Any(_) => Self::Any, + InterfaceValue::String(_) => Self::String, + 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 { Self::I32(0) } } -macro_rules! from_x_for_value { +macro_rules! from_x_for_interface_value { ($native_type:ty, $value_variant:ident) => { - impl From<$native_type> for Value { + impl From<$native_type> for InterfaceValue { fn from(n: $native_type) -> Self { Self::$value_variant(n) } } - impl TryFrom<&Value> for $native_type { + impl TryFrom<&InterfaceValue> for $native_type { type Error = &'static str; - fn try_from(w: &Value) -> Result { + fn try_from(w: &InterfaceValue) -> Result { match *w { - Value::$value_variant(n) => Ok(n), + InterfaceValue::$value_variant(n) => Ok(n), _ => Err("Invalid cast."), } } @@ -57,25 +58,46 @@ macro_rules! from_x_for_value { }; } -from_x_for_value!(i32, I32); -from_x_for_value!(i64, I64); -from_x_for_value!(f32, F32); -from_x_for_value!(f64, F64); -from_x_for_value!(u128, V128); +from_x_for_interface_value!(i32, I32); +from_x_for_interface_value!(i64, I64); +from_x_for_interface_value!(f32, F32); +from_x_for_interface_value!(f64, F64); + +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 { fn inputs_cardinality(&self) -> usize; fn outputs_cardinality(&self) -> usize; - fn inputs(&self) -> &[Type]; - fn outputs(&self) -> &[Type]; - fn call(&self, arguments: &[Value]) -> Result, ()>; + fn inputs(&self) -> &[InterfaceType]; + fn outputs(&self) -> &[InterfaceType]; + fn call(&self, arguments: &[InterfaceValue]) -> Result, ()>; } -pub trait Instance +pub trait Memory { + fn view(&self) -> &[Cell]; +} + +pub trait Instance where E: Export, + M: Memory, { fn export(&self, export_name: &str) -> Option<&E>; + fn memory(&self, index: usize) -> Option<&M>; } impl Export for () { @@ -87,24 +109,35 @@ impl Export for () { 0 } - fn inputs(&self) -> &[Type] { + fn inputs(&self) -> &[InterfaceType] { &[] } - fn outputs(&self) -> &[Type] { + fn outputs(&self) -> &[InterfaceType] { &[] } - fn call(&self, _arguments: &[Value]) -> Result, ()> { + fn call(&self, _arguments: &[InterfaceValue]) -> Result, ()> { Err(()) } } -impl Instance for () +impl Memory for () { + fn view(&self) -> &[Cell] { + &[] + } +} + +impl Instance for () where E: Export, + M: Memory, { fn export(&self, _export_name: &str) -> Option<&E> { None } + + fn memory(&self, _: usize) -> Option<&M> { + None + } }