diff --git a/Cargo.lock b/Cargo.lock index 7ce4593..e7b112e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,14 +39,22 @@ dependencies = [ [[package]] name = "it-lilo" -version = "0.1.0" +version = "0.2.0" dependencies = [ "fluence-it-types", + "it-memory-traits", "log", "paste", "thiserror", ] +[[package]] +name = "it-memory-traits" +version = "0.1.0" +dependencies = [ + "thiserror", +] + [[package]] name = "it-to-bytes" version = "0.1.0" @@ -272,10 +280,11 @@ checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "wasmer-interface-types-fl" -version = "0.20.1" +version = "0.21.0" dependencies = [ "fluence-it-types", "it-lilo", + "it-memory-traits", "it-to-bytes", "itertools", "log", diff --git a/Cargo.toml b/Cargo.toml index cc38e80..887d905 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "crates/it-lilo", "crates/to-bytes", "crates/it-types", + "crates/it-memory-traits", "wasmer-it", ] diff --git a/crates/it-lilo/Cargo.toml b/crates/it-lilo/Cargo.toml index dd994f7..7722b4f 100644 --- a/crates/it-lilo/Cargo.toml +++ b/crates/it-lilo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "it-lilo" -version = "0.1.0" +version = "0.2.0" authors = ["Fluence Labs"] description = "Defines some helper utils for lifting/lowering IT" edition = "2018" @@ -12,6 +12,7 @@ path = "src/lib.rs" [dependencies] fluence-it-types = { path = "../it-types/", version = "0.3.0" } +it-memory-traits = { path = "../it-memory-traits", version = "0.1.0" } paste = "1.0.5" thiserror = "1.0.24" diff --git a/crates/it-lilo/src/lifter/error.rs b/crates/it-lilo/src/lifter/error.rs index dc8dd76..c5177f3 100644 --- a/crates/it-lilo/src/lifter/error.rs +++ b/crates/it-lilo/src/lifter/error.rs @@ -15,18 +15,15 @@ */ use crate::traits::RecordResolvableError; + +use it_memory_traits::MemoryAccessError; + use thiserror::Error as ThisError; #[derive(Debug, ThisError)] pub enum LiError { - #[error( - "Out-of-bound Wasm memory access: offset {offset}, size {size}, while memory_size {memory_size}" - )] - InvalidAccess { - offset: usize, - size: usize, - memory_size: usize, - }, + #[error("{0}")] + MemoryAccessError(#[from] MemoryAccessError), #[error("{0}")] RecordResolvableError(#[from] RecordResolvableError), diff --git a/crates/it-lilo/src/lifter/lift_array.rs b/crates/it-lilo/src/lifter/lift_array.rs index 01de7cd..5b561c3 100644 --- a/crates/it-lilo/src/lifter/lift_array.rs +++ b/crates/it-lilo/src/lifter/lift_array.rs @@ -22,8 +22,10 @@ use crate::utils::ser_type_size; use crate::IType; use crate::IValue; -pub fn array_lift_memory( - lifter: &ILifter<'_, '_, R>, +use it_memory_traits::{SequentialMemoryView, SequentialReader}; + +pub fn array_lift_memory SequentialMemoryView<'a>>( + lifter: &ILifter<'_, R, MV>, value_type: &IType, offset: usize, elements_count: usize, @@ -59,8 +61,8 @@ pub fn array_lift_memory( Ok(IValue::Array(ivalues)) } -fn read_string_array( - lifter: &ILifter<'_, '_, R>, +fn read_string_array SequentialMemoryView<'a>>( + lifter: &ILifter<'_, R, MV>, offset: usize, elements_count: usize, ) -> LiResult> { @@ -81,8 +83,8 @@ fn read_string_array( Ok(result) } -fn read_array_array( - lifter: &ILifter<'_, '_, R>, +fn read_array_array SequentialMemoryView<'a>>( + lifter: &ILifter<'_, R, MV>, ty: &IType, offset: usize, elements_count: usize, @@ -103,8 +105,8 @@ fn read_array_array( Ok(result) } -fn read_record_array( - lifter: &ILifter<'_, '_, R>, +fn read_record_array SequentialMemoryView<'a>>( + lifter: &ILifter<'_, R, MV>, record_type_id: u64, offset: usize, elements_count: usize, diff --git a/crates/it-lilo/src/lifter/lift_record.rs b/crates/it-lilo/src/lifter/lift_record.rs index 8fe23a9..b5ac803 100644 --- a/crates/it-lilo/src/lifter/lift_record.rs +++ b/crates/it-lilo/src/lifter/lift_record.rs @@ -18,7 +18,6 @@ use super::ILifter; use super::LiError; use super::LiResult; use super::MemoryReader; -use super::SequentialReader; use crate::traits::RecordResolvable; use crate::utils::record_size; use crate::IRecordType; @@ -26,8 +25,10 @@ use crate::IType; use crate::IValue; use crate::NEVec; -pub fn record_lift_memory( - lifter: &ILifter<'_, '_, R>, +use it_memory_traits::{SequentialMemoryView, SequentialReader}; + +pub fn record_lift_memory SequentialMemoryView<'a>>( + lifter: &ILifter<'_, R, MV>, record_type: &IRecordType, offset: usize, ) -> LiResult { @@ -67,9 +68,9 @@ pub fn record_lift_memory( Ok(IValue::Record(record)) } -fn read_string( - reader: &MemoryReader<'_>, - seq_reader: &SequentialReader<'_, '_>, +fn read_string SequentialMemoryView<'a>>( + reader: &MemoryReader, + seq_reader: &>::SR, ) -> LiResult { let offset = seq_reader.read_u32(); let size = seq_reader.read_u32(); @@ -80,9 +81,9 @@ fn read_string( Ok(string) } -fn read_byte_array( - reader: &MemoryReader<'_>, - seq_reader: &SequentialReader<'_, '_>, +fn read_byte_array SequentialMemoryView<'a>>( + reader: &MemoryReader, + seq_reader: &>::SR, ) -> LiResult { let offset = seq_reader.read_u32(); let size = seq_reader.read_u32(); @@ -92,9 +93,9 @@ fn read_byte_array( Ok(IValue::ByteArray(array)) } -fn read_array( - lifter: &ILifter<'_, '_, R>, - seq_reader: &SequentialReader<'_, '_>, +fn read_array SequentialMemoryView<'a>>( + lifter: &ILifter<'_, R, MV>, + seq_reader: &>::SR, value_type: &IType, ) -> LiResult { let offset = seq_reader.read_u32(); @@ -103,9 +104,9 @@ fn read_array( super::array_lift_memory(lifter, value_type, offset as _, size as _) } -fn read_record( - lifter: &ILifter<'_, '_, R>, - seq_reader: &SequentialReader<'_, '_>, +fn read_record SequentialMemoryView<'a>>( + lifter: &ILifter<'_, R, MV>, + seq_reader: &>::SR, record_type_id: u64, ) -> LiResult { let offset = seq_reader.read_u32(); diff --git a/crates/it-lilo/src/lifter/macros.rs b/crates/it-lilo/src/lifter/macros.rs index 0c7e973..3189c10 100644 --- a/crates/it-lilo/src/lifter/macros.rs +++ b/crates/it-lilo/src/lifter/macros.rs @@ -17,7 +17,7 @@ #[macro_export] macro_rules! value_der { ($self:expr, $offset:expr, @seq_start $($ids:tt),* @seq_end) => { - [$($self.reader.memory[$offset + $ids].get()),+] + [$($self.memory.get($offset + $ids)),+] }; ($self:expr, $offset:expr, 1) => { @@ -44,7 +44,7 @@ macro_rules! value_der { #[macro_export] macro_rules! read_ty { ($func_name:ident, $ty:ty, 1) => { - pub fn $func_name(&self) -> $ty { + fn $func_name(&self) -> $ty { let offset = self.offset.get(); let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 1)); @@ -54,7 +54,7 @@ macro_rules! read_ty { }; ($func_name:ident, $ty:ty, 2) => { - pub fn $func_name(&self) -> $ty { + fn $func_name(&self) -> $ty { let offset = self.offset.get(); let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 2)); @@ -64,7 +64,7 @@ macro_rules! read_ty { }; ($func_name:ident, $ty:ty, 4) => { - pub fn $func_name(&self) -> $ty { + fn $func_name(&self) -> $ty { let offset = self.offset.get(); let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 4)); @@ -74,7 +74,7 @@ macro_rules! read_ty { }; ($func_name:ident, $ty:ty, 8) => { - pub fn $func_name(&self) -> $ty { + fn $func_name(&self) -> $ty { let offset = self.offset.get(); let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 8)); @@ -84,7 +84,7 @@ macro_rules! read_ty { }; ($func_name:ident, $ty:ty, 16) => { - pub fn $func_name(&self) -> $ty { + fn $func_name(&self) -> $ty { let offset = self.offset.get(); let result = <$ty>::from_le_bytes(crate::value_der!(self, offset, 16)); @@ -94,6 +94,29 @@ macro_rules! read_ty { }; } +#[macro_export] +macro_rules! read_ty_decl { + ($func_name:ident, $ty:ty, 1) => { + fn $func_name(&self) -> $ty; + }; + + ($func_name:ident, $ty:ty, 2) => { + fn $func_name(&self) -> $ty; + }; + + ($func_name:ident, $ty:ty, 4) => { + fn $func_name(&self) -> $ty; + }; + + ($func_name:ident, $ty:ty, 8) => { + fn $func_name(&self) -> $ty; + }; + + ($func_name:ident, $ty:ty, 16) => { + fn $func_name(&self) -> $ty; + }; +} + #[macro_export] macro_rules! read_array_ty { ($func_name:ident, $ty:ident, $ity:ident) => { diff --git a/crates/it-lilo/src/lifter/memory_reader.rs b/crates/it-lilo/src/lifter/memory_reader.rs index 17b147d..ffc9959 100644 --- a/crates/it-lilo/src/lifter/memory_reader.rs +++ b/crates/it-lilo/src/lifter/memory_reader.rs @@ -14,29 +14,19 @@ * limitations under the License. */ -use super::LiError; use super::LiResult; use crate::read_array_ty; -use crate::read_ty; use crate::IValue; -use std::cell::Cell; +use it_memory_traits::{SequentialMemoryView, SequentialReader}; -pub struct MemoryReader<'m> { - pub(self) memory: &'m [Cell], +pub struct MemoryReader { + pub(self) view: MV, } -/// Reads values of basic types sequentially from the provided reader. -/// It doesn't check memory limits for the optimization purposes, -/// so it could be created only by the MemoryReader::sequential_reader method. -pub struct SequentialReader<'r, 'm> { - reader: &'r MemoryReader<'m>, - offset: Cell, -} - -impl<'m> MemoryReader<'m> { - pub fn new(memory: &'m [Cell]) -> Self { - Self { memory } +impl SequentialMemoryView<'a>> MemoryReader { + pub fn new(view: MV) -> Self { + Self { view } } /// Returns reader that allows read sequentially. It's important that memory limit is checked @@ -46,10 +36,9 @@ impl<'m> MemoryReader<'m> { &self, offset: usize, size: usize, - ) -> LiResult> { - self.check_access(offset, size)?; - - Ok(SequentialReader::new(&self, offset)) + ) -> LiResult<>::SR> { + let seq_reader = self.view.sequential_reader(offset, size)?; + Ok(seq_reader) } pub fn read_raw_u8_array(&self, offset: usize, elements_count: usize) -> LiResult> { @@ -76,21 +65,6 @@ impl<'m> MemoryReader<'m> { Ok(result) } - pub fn check_access(&self, offset: usize, size: usize) -> LiResult<()> { - let right = offset + size; - - // the first condition is a check for overflow - if right < offset || right >= self.memory.len() { - return Err(LiError::InvalidAccess { - offset, - size, - memory_size: self.memory.len(), - }); - } - - Ok(()) - } - read_array_ty!(read_u8_array, u8, U8); read_array_ty!(read_s8_array, i8, S8); read_array_ty!(read_u16_array, u16, U16); @@ -104,29 +78,3 @@ impl<'m> MemoryReader<'m> { read_array_ty!(read_i64_array, i64, I64); read_array_ty!(read_f64_array, f64, F64); } - -impl<'r, 'm> SequentialReader<'r, 'm> { - pub(self) fn new(reader: &'r MemoryReader<'m>, offset: usize) -> Self { - let offset = Cell::new(offset); - Self { reader, offset } - } - - pub fn read_bool(&self) -> bool { - let offset = self.offset.get(); - let result = self.reader.memory[offset].get() != 0; - - self.offset.set(offset + 1); - result - } - - read_ty!(read_u8, u8, 1); - read_ty!(read_i8, i8, 1); - read_ty!(read_u16, u16, 2); - read_ty!(read_i16, i16, 2); - read_ty!(read_u32, u32, 4); - read_ty!(read_i32, i32, 4); - read_ty!(read_f32, f32, 4); - read_ty!(read_u64, u64, 8); - read_ty!(read_i64, i64, 8); - read_ty!(read_f64, f64, 8); -} diff --git a/crates/it-lilo/src/lifter/mod.rs b/crates/it-lilo/src/lifter/mod.rs index 802a531..11093b2 100644 --- a/crates/it-lilo/src/lifter/mod.rs +++ b/crates/it-lilo/src/lifter/mod.rs @@ -24,22 +24,21 @@ pub use error::LiError; pub use lift_array::array_lift_memory; pub use lift_record::record_lift_memory; pub use memory_reader::MemoryReader; -pub use memory_reader::SequentialReader; use super::traits::RecordResolvable; -use std::cell::Cell; +pub use it_memory_traits::SequentialMemoryView; pub type LiResult = std::result::Result; -pub struct ILifter<'m, 'r, R: RecordResolvable> { - pub reader: MemoryReader<'m>, +pub struct ILifter<'r, R: RecordResolvable, MV> { + pub reader: MemoryReader, pub resolver: &'r R, } -impl<'m, 'r, R: RecordResolvable> ILifter<'m, 'r, R> { - pub fn new(memory: &'m [Cell], resolver: &'r R) -> Self { - let reader = MemoryReader::new(memory); +impl<'r, R: RecordResolvable, MV: for<'a> SequentialMemoryView<'a>> ILifter<'r, R, MV> { + pub fn new(view: MV, resolver: &'r R) -> Self { + let reader = MemoryReader::new(view); Self { reader, resolver } } } diff --git a/crates/it-lilo/src/lowerer/error.rs b/crates/it-lilo/src/lowerer/error.rs index 6d1d501..169030f 100644 --- a/crates/it-lilo/src/lowerer/error.rs +++ b/crates/it-lilo/src/lowerer/error.rs @@ -16,6 +16,7 @@ use crate::traits::AllocatableError; use crate::traits::RecordResolvableError; +use it_memory_traits::MemoryAccessError; use thiserror::Error as ThisError; #[derive(Debug, ThisError)] @@ -25,4 +26,7 @@ pub enum LoError { #[error("{0}")] RecordResolvableError(#[from] RecordResolvableError), + + #[error("{0}")] + MemoryAccessError(#[from] MemoryAccessError), } diff --git a/crates/it-lilo/src/lowerer/lower_array.rs b/crates/it-lilo/src/lowerer/lower_array.rs index d4cf737..ad6c99d 100644 --- a/crates/it-lilo/src/lowerer/lower_array.rs +++ b/crates/it-lilo/src/lowerer/lower_array.rs @@ -21,6 +21,8 @@ use crate::utils::ser_value_size; use crate::utils::type_tag_form_ivalue; use crate::IValue; +use it_memory_traits::{SequentialMemoryView, SequentialWriter}; + pub struct LoweredArray { pub offset: usize, pub size: usize, @@ -36,8 +38,8 @@ impl LoweredArray { } } -pub fn array_lower_memory( - lowerer: &ILowerer<'_, A>, +pub fn array_lower_memory SequentialMemoryView<'a>>( + lowerer: &ILowerer<'_, A, MV>, array_values: Vec, ) -> LoResult { if array_values.is_empty() { @@ -52,40 +54,40 @@ pub fn array_lower_memory( // here it's known that all interface values have the same type for value in array_values { match value { - IValue::Boolean(value) => seq_writer.write_u8(&lowerer.writer, value as _), - IValue::S8(value) => seq_writer.write_u8(&lowerer.writer, value as _), - IValue::S16(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()), - IValue::S32(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()), - IValue::S64(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()), - IValue::U8(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()), - IValue::U16(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()), - IValue::U32(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()), - IValue::U64(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()), - IValue::I32(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()), - IValue::I64(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()), - IValue::F32(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()), - IValue::F64(value) => seq_writer.write_array(&lowerer.writer, value.to_le_bytes()), + IValue::Boolean(value) => seq_writer.write_u8(value as _), + IValue::S8(value) => seq_writer.write_u8(value as _), + IValue::S16(value) => seq_writer.write_bytes(&value.to_le_bytes()), + IValue::S32(value) => seq_writer.write_bytes(&value.to_le_bytes()), + IValue::S64(value) => seq_writer.write_bytes(&value.to_le_bytes()), + IValue::U8(value) => seq_writer.write_bytes(&value.to_le_bytes()), + IValue::U16(value) => seq_writer.write_bytes(&value.to_le_bytes()), + IValue::U32(value) => seq_writer.write_bytes(&value.to_le_bytes()), + IValue::U64(value) => seq_writer.write_bytes(&value.to_le_bytes()), + IValue::I32(value) => seq_writer.write_bytes(&value.to_le_bytes()), + IValue::I64(value) => seq_writer.write_bytes(&value.to_le_bytes()), + IValue::F32(value) => seq_writer.write_bytes(&value.to_le_bytes()), + IValue::F64(value) => seq_writer.write_bytes(&value.to_le_bytes()), IValue::String(value) => { - let offset = lowerer.writer.write_bytes(value.as_bytes())? as u32; + let offset = lowerer.writer.write_bytes(&value.as_bytes())? as u32; - seq_writer.write_array(&lowerer.writer, offset.to_le_bytes()); - seq_writer.write_array(&lowerer.writer, (value.len() as u32).to_le_bytes()); + seq_writer.write_bytes(&offset.to_le_bytes()); + seq_writer.write_bytes(&(value.len() as u32).to_le_bytes()); } IValue::ByteArray(values) => { let offset = lowerer.writer.write_bytes(&values)? as u32; - seq_writer.write_array(&lowerer.writer, offset.to_le_bytes()); - seq_writer.write_array(&lowerer.writer, (values.len() as u32).to_le_bytes()); + seq_writer.write_bytes(&offset.to_le_bytes()); + seq_writer.write_bytes(&(values.len() as u32).to_le_bytes()); } IValue::Array(values) => { let LoweredArray { offset, size } = array_lower_memory(lowerer, values)?; - seq_writer.write_array(&lowerer.writer, (offset as u32).to_le_bytes()); - seq_writer.write_array(&lowerer.writer, (size as u32).to_le_bytes()); + seq_writer.write_bytes(&(offset as u32).to_le_bytes()); + seq_writer.write_bytes(&(size as u32).to_le_bytes()); } IValue::Record(values) => { let offset = super::record_lower_memory(lowerer, values)? as u32; - seq_writer.write_array(&lowerer.writer, offset.to_le_bytes()); + seq_writer.write_bytes(&offset.to_le_bytes()); } } } diff --git a/crates/it-lilo/src/lowerer/lower_record.rs b/crates/it-lilo/src/lowerer/lower_record.rs index b1699e1..05a48c2 100644 --- a/crates/it-lilo/src/lowerer/lower_record.rs +++ b/crates/it-lilo/src/lowerer/lower_record.rs @@ -21,8 +21,10 @@ use crate::traits::Allocatable; use crate::IValue; use crate::NEVec; -pub fn record_lower_memory( - lowerer: &ILowerer<'_, A>, +use it_memory_traits::SequentialMemoryView; + +pub fn record_lower_memory SequentialMemoryView<'a>>( + lowerer: &ILowerer<'_, A, MV>, values: NEVec, ) -> LoResult { let average_field_size = 4; diff --git a/crates/it-lilo/src/lowerer/memory_writer.rs b/crates/it-lilo/src/lowerer/memory_writer.rs index 722b52f..895551d 100644 --- a/crates/it-lilo/src/lowerer/memory_writer.rs +++ b/crates/it-lilo/src/lowerer/memory_writer.rs @@ -16,113 +16,36 @@ use super::LoResult; use crate::traits::Allocatable; -use crate::traits::MemSlice; -use crate::traits::DEFAULT_MEMORY_INDEX; use crate::utils::type_tag_form_itype; -use std::cell::Cell; +use it_memory_traits::{SequentialMemoryView, SequentialWriter}; -pub struct MemoryWriter<'i, R: Allocatable> { +pub struct MemoryWriter<'i, R: Allocatable, MV> { heap_manager: &'i R, - pub(self) memory: Cell>, + view: MV, } -pub struct SequentialWriter { - start_offset: usize, - offset: Cell, -} - -impl<'i, A: Allocatable> MemoryWriter<'i, A> { - pub fn new(heap_manager: &'i A) -> LoResult { - let mem_slice = heap_manager.memory_slice(DEFAULT_MEMORY_INDEX)?; - let memory = Cell::new(mem_slice); - - let writer = Self { - heap_manager, - memory, - }; +impl<'i, A: Allocatable, MV: for<'a> SequentialMemoryView<'a>> MemoryWriter<'i, A, MV> { + pub fn new(view: MV, heap_manager: &'i A) -> LoResult { + let writer = Self { heap_manager, view }; Ok(writer) } pub fn write_bytes(&self, bytes: &[u8]) -> LoResult { let byte_type_tag = type_tag_form_itype(&crate::IType::U8); let seq_writer = self.sequential_writer(bytes.len() as _, byte_type_tag)?; - seq_writer.write_bytes(self, bytes); + seq_writer.write_bytes(bytes); Ok(seq_writer.start_offset()) } - pub fn sequential_writer(&self, size: u32, type_tag: u32) -> LoResult { - let offset = self.heap_manager.allocate(size, type_tag)?; - - let new_mem_slice = self.heap_manager.memory_slice(DEFAULT_MEMORY_INDEX)?; - self.memory.set(new_mem_slice); - - Ok(SequentialWriter::new(offset)) - } -} - -impl SequentialWriter { - pub(self) fn new(offset: usize) -> Self { - Self { - offset: Cell::new(offset), - start_offset: offset, - } - } - - pub fn start_offset(&self) -> usize { - self.start_offset - } - - pub fn write_array( + pub fn sequential_writer( &self, - writer: &MemoryWriter<'_, A>, - values: [u8; N], - ) { - let offset = self.offset.get(); - - writer.memory.get()[offset..offset + N] - .iter() - .zip(values.iter()) - .for_each(|(cell, &byte)| cell.set(byte)); - - self.offset.set(offset + N); - } - - // specialization of write_array for u8 - pub fn write_u8(&self, writer: &MemoryWriter<'_, A>, value: u8) { - let offset = self.offset.get(); - - writer.memory.get()[offset].set(value); - - self.offset.set(offset + 1); - } - - // specialization of write_array for u32 - pub fn write_u32(&self, writer: &MemoryWriter<'_, A>, value: u32) { - let offset = self.offset.get(); - - let value = value.to_le_bytes(); - let memory = writer.memory.get(); - - memory[offset].set(value[0]); - memory[offset + 1].set(value[1]); - memory[offset + 2].set(value[2]); - memory[offset + 3].set(value[3]); - - self.offset.set(offset + 4); - } - - #[allow(dead_code)] - pub fn write_bytes(&self, writer: &MemoryWriter<'_, A>, bytes: &[u8]) { - let offset = self.offset.get(); - - let memory = writer.memory.get(); - memory[offset..offset + bytes.len()] - .iter() - .zip(bytes) - .for_each(|(cell, &byte)| cell.set(byte)); - - self.offset.set(offset + bytes.len()); + size: u32, + type_tag: u32, + ) -> LoResult<>::SW> { + let offset = self.heap_manager.allocate(size, type_tag)?; + let seq_writer = self.view.sequential_writer(offset, size as usize)?; + Ok(seq_writer) } } diff --git a/crates/it-lilo/src/lowerer/mod.rs b/crates/it-lilo/src/lowerer/mod.rs index d890a11..68c2974 100644 --- a/crates/it-lilo/src/lowerer/mod.rs +++ b/crates/it-lilo/src/lowerer/mod.rs @@ -27,15 +27,17 @@ pub use lower_array::array_lower_memory; pub use lower_array::LoweredArray; pub use lower_record::record_lower_memory; +pub use it_memory_traits::SequentialMemoryView; + pub type LoResult = std::result::Result; -pub struct ILowerer<'m, A: Allocatable> { - pub writer: MemoryWriter<'m, A>, +pub struct ILowerer<'m, A: Allocatable, MV> { + pub writer: MemoryWriter<'m, A, MV>, } -impl<'m, A: Allocatable> ILowerer<'m, A> { - pub fn new(allocatable: &'m A) -> LoResult { - let writer = MemoryWriter::new(allocatable)?; +impl<'m, A: Allocatable, MV: for<'a> SequentialMemoryView<'a>> ILowerer<'m, A, MV> { + pub fn new(view: MV, allocatable: &'m A) -> LoResult { + let writer = MemoryWriter::new(view, allocatable)?; let lowerer = Self { writer }; Ok(lowerer) diff --git a/crates/it-lilo/src/traits/allocatable.rs b/crates/it-lilo/src/traits/allocatable.rs index a675db3..18394b8 100644 --- a/crates/it-lilo/src/traits/allocatable.rs +++ b/crates/it-lilo/src/traits/allocatable.rs @@ -14,17 +14,12 @@ * limitations under the License. */ -use std::cell::Cell; use thiserror::Error as ThisError; pub const DEFAULT_MEMORY_INDEX: usize = 0; -pub type MemSlice<'m> = &'m [Cell]; - pub trait Allocatable { fn allocate(&self, size: u32, type_tag: u32) -> Result; - - fn memory_slice(&self, memory_index: usize) -> Result, AllocatableError>; } #[derive(Debug, ThisError)] diff --git a/crates/it-lilo/src/traits/record_resolvable.rs b/crates/it-lilo/src/traits/record_resolvable.rs index ad53e0d..6c02d01 100644 --- a/crates/it-lilo/src/traits/record_resolvable.rs +++ b/crates/it-lilo/src/traits/record_resolvable.rs @@ -26,4 +26,7 @@ pub enum RecordResolvableError { /// Record for such type is wasn't found. #[error("Record with type id '{0}' not found")] RecordNotFound(u64), + + #[error("Memory with index '{memory_index}' not found")] + MemoryIsMissing { memory_index: usize }, } diff --git a/crates/it-memory-traits/Cargo.toml b/crates/it-memory-traits/Cargo.toml new file mode 100644 index 0000000..7f14341 --- /dev/null +++ b/crates/it-memory-traits/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "it-memory-traits" +version = "0.1.0" +authors = ["Fluence Labs"] +description = "Defines traits that IT uses for accessing memory" +edition = "2018" +license = "Apache-2.0" + +[lib] +name = "it_memory_traits" +path = "src/lib.rs" + +[dependencies] +thiserror = "1.0.24" diff --git a/crates/it-memory-traits/src/errors.rs b/crates/it-memory-traits/src/errors.rs new file mode 100644 index 0000000..61df3b9 --- /dev/null +++ b/crates/it-memory-traits/src/errors.rs @@ -0,0 +1,27 @@ +/* + * Copyright 2022 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use thiserror::Error as ThisError; + +#[derive(Debug, ThisError)] +pub enum MemoryAccessError { + #[error("Out-of-bound Wasm memory access: offset {offset}, size {size}, while memory_size {memory_size}")] + OutOfBounds { + offset: usize, + size: usize, + memory_size: usize, + }, +} diff --git a/crates/it-memory-traits/src/lib.rs b/crates/it-memory-traits/src/lib.rs new file mode 100644 index 0000000..88c8faf --- /dev/null +++ b/crates/it-memory-traits/src/lib.rs @@ -0,0 +1,86 @@ +/* + * Copyright 2022 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod errors; + +pub use errors::MemoryAccessError; + +macro_rules! read_ty { + ($func_name:ident, $ty:ty, $size:literal) => { + fn $func_name(&self) -> $ty { + <$ty>::from_le_bytes(self.read_bytes::<$size>()) + } + }; +} + +pub trait SequentialReader { + fn read_byte(&self) -> u8; + + fn read_bytes(&self) -> [u8; COUNT]; + + fn read_bool(&self) -> bool { + self.read_byte() != 0 + } + + read_ty!(read_u8, u8, 1); + read_ty!(read_i8, i8, 1); + read_ty!(read_u16, u16, 2); + read_ty!(read_i16, i16, 2); + read_ty!(read_u32, u32, 4); + read_ty!(read_i32, i32, 4); + read_ty!(read_f32, f32, 4); + read_ty!(read_u64, u64, 8); + read_ty!(read_i64, i64, 8); + read_ty!(read_f64, f64, 8); +} + +pub trait SequentialWriter { + fn start_offset(&self) -> usize; + + // specialization of write_array for u8 + fn write_u8(&self, value: u8); + + // specialization of write_array for u32 + fn write_u32(&self, value: u32); + + fn write_bytes(&self, bytes: &[u8]); +} + +// the lifetime is needed because some implementations +// need to bind SR and SW lifetimes to lifetime of &self in methods +pub trait SequentialMemoryView<'s> { + type SR: SequentialReader + 's; + type SW: SequentialWriter + 's; + + fn sequential_writer( + &'s self, + offset: usize, + size: usize, + ) -> Result; + + fn sequential_reader( + &'s self, + offset: usize, + size: usize, + ) -> Result; +} + +pub trait Memory +where + View: for<'a> SequentialMemoryView<'a>, +{ + fn view(&self) -> View; +} diff --git a/wasmer-it/Cargo.toml b/wasmer-it/Cargo.toml index f888311..01272d7 100644 --- a/wasmer-it/Cargo.toml +++ b/wasmer-it/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-interface-types-fl" -version = "0.20.2" +version = "0.21.0" description = "WebAssembly Interface Types library for Wasmer" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -10,7 +10,8 @@ edition = "2018" [dependencies] fluence-it-types = { path = "../crates/it-types", version = "0.3.1", features = ["impls"] } it-to-bytes = { path = "../crates/to-bytes", version = "0.1.0" } -it-lilo = { path = "../crates/it-lilo", version = "0.1.0" } +it-lilo = { path = "../crates/it-lilo", version = "0.2.0" } +it-memory-traits = { path = "../crates/it-memory-traits", version = "0.1.0" } nom = "5.1" wast = "8.0" diff --git a/wasmer-it/src/errors.rs b/wasmer-it/src/errors.rs index 9aa428c..7df7458 100644 --- a/wasmer-it/src/errors.rs +++ b/wasmer-it/src/errors.rs @@ -17,6 +17,7 @@ use it_lilo::lowerer::LoError; use thiserror::Error as ThisError; pub use fluence_it_types::WasmValueNativeCastError; +use it_memory_traits::MemoryAccessError; /// A type alias for instruction's results. pub type InstructionResult = Result; @@ -54,6 +55,25 @@ impl InstructionError { let error_kind = InstructionErrorKind::LoError(lo); Self::from_error_kind(instruction, error_kind) } + + pub(crate) fn from_memory_access( + instruction: Instruction, + memory_access: MemoryAccessError, + ) -> Self { + match memory_access { + MemoryAccessError::OutOfBounds { + offset, + size, + memory_size, + } => Self::from_error_kind( + instruction.clone(), + InstructionErrorKind::MemoryOutOfBoundsAccess { + index: offset + size, + length: memory_size, + }, + ), + } + } } impl Error for InstructionError {} diff --git a/wasmer-it/src/interpreter/instructions/arrays.rs b/wasmer-it/src/interpreter/instructions/arrays.rs index 96cc728..b27d29b 100644 --- a/wasmer-it/src/interpreter/instructions/arrays.rs +++ b/wasmer-it/src/interpreter/instructions/arrays.rs @@ -10,20 +10,21 @@ use crate::{ use it_lilo::lifter::ILifter; use it_lilo::lowerer::ILowerer; use it_lilo::lowerer::LoweredArray; +use it_lilo::traits::DEFAULT_MEMORY_INDEX; use std::convert::TryInto; -pub(crate) fn array_lift_memory( +pub(crate) fn array_lift_memory( instruction: Instruction, value_type: IType, -) -> crate::interpreter::ExecutableInstruction +) -> crate::interpreter::ExecutableInstruction where Export: crate::interpreter::wasm::structures::Export, LocalImport: crate::interpreter::wasm::structures::LocalImport, - Memory: crate::interpreter::wasm::structures::Memory, - MemoryView: crate::interpreter::wasm::structures::MemoryView, + Memory: crate::interpreter::wasm::structures::Memory, + SequentialMemoryView: for<'a> crate::interpreter::wasm::structures::SequentialMemoryView<'a>, Instance: - crate::interpreter::wasm::structures::Instance, + crate::interpreter::wasm::structures::Instance, { #[allow(unused_imports)] use crate::interpreter::stack::Stackable; @@ -55,7 +56,7 @@ where let instance = &mut runtime.wasm_instance; - let memory_index = 0; + let memory_index = DEFAULT_MEMORY_INDEX; let memory_view = instance .memory(memory_index) .ok_or_else(|| { @@ -65,10 +66,9 @@ where ) })? .view(); - let memory = memory_view.deref(); let li_helper = lilo::LiHelper::new(&**instance); - let lifter = ILifter::new(memory, &li_helper); + let lifter = ILifter::new(memory_view, &li_helper); let array = it_lilo::lifter::array_lift_memory(&lifter, &value_type, offset as _, size as _) .map_err(|e| InstructionError::from_li(instruction.clone(), e))?; @@ -81,17 +81,17 @@ where }) } -pub(crate) fn array_lower_memory( +pub(crate) fn array_lower_memory( instruction: Instruction, value_type: IType, -) -> crate::interpreter::ExecutableInstruction +) -> crate::interpreter::ExecutableInstruction where Export: crate::interpreter::wasm::structures::Export, LocalImport: crate::interpreter::wasm::structures::LocalImport, - Memory: crate::interpreter::wasm::structures::Memory, - MemoryView: crate::interpreter::wasm::structures::MemoryView, + Memory: crate::interpreter::wasm::structures::Memory, + SequentialMemoryView: for<'a> crate::interpreter::wasm::structures::SequentialMemoryView<'a>, Instance: - crate::interpreter::wasm::structures::Instance, + crate::interpreter::wasm::structures::Instance, { #[allow(unused_imports)] use crate::interpreter::stack::Stackable; @@ -115,9 +115,19 @@ where InstructionError::from_error_kind(instruction.clone(), e) })?; } + let memory_index = DEFAULT_MEMORY_INDEX; + let memory_view = instance + .memory(memory_index) + .ok_or_else(|| { + InstructionError::from_error_kind( + instruction.clone(), + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })? + .view(); let lo_helper = lilo::LoHelper::new(&**instance); - let lowerer = ILowerer::new(&lo_helper) + let lowerer = ILowerer::new(memory_view, &lo_helper) .map_err(|e| InstructionError::from_lo(instruction.clone(), e))?; let LoweredArray { offset, size } = @@ -136,7 +146,18 @@ where } IValue::ByteArray(bytearray) => { let lo_helper = lilo::LoHelper::new(&**instance); - let lowerer = ILowerer::new(&lo_helper) + let memory_index = DEFAULT_MEMORY_INDEX; + let memory_view = instance + .memory(memory_index) + .ok_or_else(|| { + InstructionError::from_error_kind( + instruction.clone(), + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })? + .view(); + + let lowerer = ILowerer::new(memory_view, &lo_helper) .map_err(|e| InstructionError::from_lo(instruction.clone(), e))?; let offset = lowerer diff --git a/wasmer-it/src/interpreter/instructions/byte_arrays.rs b/wasmer-it/src/interpreter/instructions/byte_arrays.rs index a7aa715..7169398 100644 --- a/wasmer-it/src/interpreter/instructions/byte_arrays.rs +++ b/wasmer-it/src/interpreter/instructions/byte_arrays.rs @@ -7,7 +7,10 @@ use crate::{ interpreter::Instruction, }; -use std::{cell::Cell, convert::TryInto}; +use it_memory_traits::{SequentialReader, SequentialWriter}; +use it_lilo::traits::DEFAULT_MEMORY_INDEX; + +use std::convert::TryInto; executable_instruction!( byte_array_lift_memory(instruction: Instruction) -> _ { @@ -19,7 +22,7 @@ executable_instruction!( ) })?; - let memory_index = 0; + let memory_index = DEFAULT_MEMORY_INDEX; let memory = runtime .wasm_instance .memory(memory_index) @@ -47,20 +50,14 @@ executable_instruction!( return Ok(()) } - if memory_view.len() < pointer + length { - return instr_error!( - instruction.clone(), - InstructionErrorKind::MemoryOutOfBoundsAccess { - index: pointer + length, - length: memory_view.len(), - } - ); - } + let reader = memory_view + .sequential_reader(pointer, length) + .map_err(|e| InstructionError::from_memory_access(instruction.clone(), e))?; - let data: Vec = (&memory_view[pointer..pointer + length]) - .iter() - .map(Cell::get) - .collect(); + let mut data = Vec::::with_capacity(length); + for index in 0..length { + data[index] = reader.read_u8(); + } log::debug!("byte_array.lift_memory: pushing {:?} on the stack", data); runtime.stack.push(IValue::ByteArray(data)); @@ -93,7 +90,7 @@ executable_instruction!( })?; let instance = &mut runtime.wasm_instance; - let memory_index = 0; + let memory_index = DEFAULT_MEMORY_INDEX; let memory_view = instance .memory(memory_index) .ok_or_else(|| { @@ -104,9 +101,11 @@ executable_instruction!( })? .view(); - for (nth, byte) in array.iter().enumerate() { - memory_view[array_pointer as usize + nth].set(*byte); - } + let writer = memory_view + .sequential_writer(array_pointer, array.len()) + .map_err(|e| InstructionError::from_memory_access(instruction.clone(), e))?; + + writer.write_bytes(&array); log::debug!("string.lower_memory: pushing {}, {} on the stack", array_pointer, length); runtime.stack.push(IValue::I32(array_pointer as i32)); diff --git a/wasmer-it/src/interpreter/instructions/lilo/li_helper.rs b/wasmer-it/src/interpreter/instructions/lilo/li_helper.rs index a761a79..8a8c311 100644 --- a/wasmer-it/src/interpreter/instructions/lilo/li_helper.rs +++ b/wasmer-it/src/interpreter/instructions/lilo/li_helper.rs @@ -6,29 +6,29 @@ use it_lilo::traits::RecordResolvableError; use std::marker::PhantomData; -pub struct LiHelper<'i, Instance, Export, LocalImport, Memory, MemoryView> +pub struct LiHelper<'i, Instance, Export, LocalImport, Memory, SequentialMemoryView> where Export: wasm::structures::Export + 'i, LocalImport: wasm::structures::LocalImport + 'i, - Memory: wasm::structures::Memory + 'i, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory + 'i, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>) + 'i, + Instance: wasm::structures::Instance, { pub(crate) instance: &'i Instance, _export: PhantomData, _local_import: PhantomData, _memory: PhantomData, - _memory_view: PhantomData, + _memory_view: PhantomData, } -impl<'i, Instance, Export, LocalImport, Memory, MemoryView> - LiHelper<'i, Instance, Export, LocalImport, Memory, MemoryView> +impl<'i, Instance, Export, LocalImport, Memory, SequentialMemoryView> + LiHelper<'i, Instance, Export, LocalImport, Memory, SequentialMemoryView> where Export: wasm::structures::Export + 'i, LocalImport: wasm::structures::LocalImport + 'i, - Memory: wasm::structures::Memory + 'i, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory + 'i, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance, { pub(crate) fn new(instance: &'i Instance) -> Self { Self { @@ -41,14 +41,14 @@ where } } -impl<'i, Instance, Export, LocalImport, Memory, MemoryView> RecordResolvable - for LiHelper<'i, Instance, Export, LocalImport, Memory, MemoryView> +impl<'i, Instance, Export, LocalImport, Memory, SequentialMemoryView> RecordResolvable + for LiHelper<'i, Instance, Export, LocalImport, Memory, SequentialMemoryView> where Export: wasm::structures::Export + 'i, LocalImport: wasm::structures::LocalImport + 'i, - Memory: wasm::structures::Memory + 'i, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory + 'i, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance, { fn resolve_record(&self, record_type_id: u64) -> Result<&IRecordType, RecordResolvableError> { let record = self diff --git a/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs b/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs index 72e2b66..48d4ef5 100644 --- a/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs +++ b/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs @@ -4,33 +4,32 @@ use crate::IValue; use it_lilo::traits::Allocatable; use it_lilo::traits::AllocatableError; -use it_lilo::traits::MemSlice; use std::marker::PhantomData; -pub struct LoHelper<'i, Instance, Export, LocalImport, Memory, MemoryView> +pub struct LoHelper<'i, Instance, Export, LocalImport, Memory, SequentialMemoryView> where Export: wasm::structures::Export + 'i, LocalImport: wasm::structures::LocalImport + 'i, - Memory: wasm::structures::Memory + 'i, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory + 'i, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance, { pub(crate) instance: &'i Instance, _export: PhantomData, _local_import: PhantomData, _memory: PhantomData, - _memory_view: PhantomData, + _memory_view: PhantomData, } -impl<'i, Instance, Export, LocalImport, Memory, MemoryView> - LoHelper<'i, Instance, Export, LocalImport, Memory, MemoryView> +impl<'i, Instance, Export, LocalImport, Memory, SequentialMemoryView> + LoHelper<'i, Instance, Export, LocalImport, Memory, SequentialMemoryView> where Export: wasm::structures::Export + 'i, LocalImport: wasm::structures::LocalImport + 'i, - Memory: wasm::structures::Memory + 'i, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory + 'i, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance, { pub(crate) fn new(instance: &'i Instance) -> Self { Self { @@ -43,14 +42,14 @@ where } } -impl<'i, Instance, Export, LocalImport, Memory, MemoryView> Allocatable - for LoHelper<'i, Instance, Export, LocalImport, Memory, MemoryView> +impl<'i, Instance, Export, LocalImport, Memory, SequentialMemoryView> Allocatable + for LoHelper<'i, Instance, Export, LocalImport, Memory, SequentialMemoryView> where Export: wasm::structures::Export + 'i, LocalImport: wasm::structures::LocalImport + 'i, - Memory: wasm::structures::Memory + 'i, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory + 'i, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance, { fn allocate(&self, size: u32, type_tag: u32) -> Result { use AllocatableError::*; @@ -88,10 +87,4 @@ where _ => Err(AllocateFuncIncompatibleOutput), } } - - fn memory_slice(&self, memory_index: usize) -> Result, AllocatableError> { - self.instance - .memory_slice(memory_index) - .ok_or(AllocatableError::MemoryIsMissing { memory_index }) - } } diff --git a/wasmer-it/src/interpreter/instructions/mod.rs b/wasmer-it/src/interpreter/instructions/mod.rs index b396f7f..7f55ee5 100644 --- a/wasmer-it/src/interpreter/instructions/mod.rs +++ b/wasmer-it/src/interpreter/instructions/mod.rs @@ -232,7 +232,7 @@ pub(crate) fn check_function_signature< Export, LocalImport, Memory, - MemoryView, + SequentialMemoryView, >( instance: &'instance Instance, local_import: &LocalImport, @@ -241,9 +241,9 @@ pub(crate) fn check_function_signature< where Export: wasm::structures::Export + 'instance, LocalImport: wasm::structures::LocalImport + 'instance, - Memory: wasm::structures::Memory + 'instance, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory + 'instance, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance, { let func_inputs = local_import.arguments(); @@ -261,7 +261,7 @@ pub(crate) fn is_value_compatible_to_type< Export, LocalImport, Memory, - MemoryView, + SequentialMemoryView, >( instance: &'instance Instance, interface_type: &IType, @@ -270,9 +270,9 @@ pub(crate) fn is_value_compatible_to_type< where Export: wasm::structures::Export + 'instance, LocalImport: wasm::structures::LocalImport + 'instance, - Memory: wasm::structures::Memory + 'instance, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory + 'instance, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance, { match (&interface_type, interface_value) { (IType::Boolean, IValue::Boolean(_)) => Ok(()), @@ -332,7 +332,7 @@ pub(crate) fn is_record_fields_compatible_to_type< Export, LocalImport, Memory, - MemoryView, + SequentialMemoryView, >( instance: &'instance Instance, record_type_id: u64, @@ -341,9 +341,9 @@ pub(crate) fn is_record_fields_compatible_to_type< where Export: wasm::structures::Export + 'instance, LocalImport: wasm::structures::LocalImport + 'instance, - Memory: wasm::structures::Memory + 'instance, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory + 'instance, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance, { let record_type = instance .wit_record_by_id(record_type_id) @@ -428,11 +428,11 @@ pub(crate) mod tests { } #[derive(Default, Clone)] - pub(crate) struct MemoryView(Rc>>); + pub(crate) struct SequentialMemoryView(Rc>>); - impl wasm::structures::MemoryView for MemoryView {} + impl wasm::structures::SequentialMemoryView for SequentialMemoryView {} - impl Deref for MemoryView { + impl Deref for SequentialMemoryView { type Target = [Cell]; fn deref(&self) -> &Self::Target { @@ -442,19 +442,19 @@ pub(crate) mod tests { #[derive(Default)] pub(crate) struct Memory { - pub(crate) view: MemoryView, + pub(crate) view: SequentialMemoryView, } impl Memory { pub(crate) fn new(data: Vec>) -> Self { Self { - view: MemoryView(Rc::new(data)), + view: SequentialMemoryView(Rc::new(data)), } } } - impl wasm::structures::Memory for Memory { - fn view(&self) -> MemoryView { + impl wasm::structures::Memory for Memory { + fn view(&self) -> SequentialMemoryView { self.view.clone() } } @@ -554,7 +554,7 @@ pub(crate) mod tests { } } - impl wasm::structures::Instance for Instance { + impl wasm::structures::Instance for Instance { fn export(&self, export_name: &str) -> Option<&Export> { self.exports.get(export_name) } diff --git a/wasmer-it/src/interpreter/instructions/records.rs b/wasmer-it/src/interpreter/instructions/records.rs index 0920b1f..e133ac5 100644 --- a/wasmer-it/src/interpreter/instructions/records.rs +++ b/wasmer-it/src/interpreter/instructions/records.rs @@ -7,20 +7,21 @@ use crate::{errors::InstructionError, errors::InstructionErrorKind, interpreter: use it_lilo::lifter::ILifter; use it_lilo::lowerer::ILowerer; +use it_lilo::traits::DEFAULT_MEMORY_INDEX; use std::convert::TryInto; -pub(crate) fn record_lift_memory( +pub(crate) fn record_lift_memory( record_type_id: u64, instruction: Instruction, -) -> crate::interpreter::ExecutableInstruction +) -> crate::interpreter::ExecutableInstruction where Export: crate::interpreter::wasm::structures::Export, LocalImport: crate::interpreter::wasm::structures::LocalImport, - Memory: crate::interpreter::wasm::structures::Memory, - MemoryView: crate::interpreter::wasm::structures::MemoryView, + Memory: crate::interpreter::wasm::structures::Memory, + SequentialMemoryView: for<'a> crate::interpreter::wasm::structures::SequentialMemoryView<'a>, Instance: - crate::interpreter::wasm::structures::Instance, + crate::interpreter::wasm::structures::Instance, { #[allow(unused_imports)] use crate::interpreter::stack::Stackable; @@ -53,7 +54,7 @@ where record_type_id ); - let memory_index = 0; + let memory_index = DEFAULT_MEMORY_INDEX; let memory_view = instance .memory(memory_index) .ok_or_else(|| { @@ -63,10 +64,9 @@ where ) })? .view(); - let memory = memory_view.deref(); let li_helper = lilo::LiHelper::new(&**instance); - let lifter = ILifter::new(memory, &li_helper); + let lifter = ILifter::new(memory_view, &li_helper); let record = it_lilo::lifter::record_lift_memory(&lifter, record_type, offset) .map_err(|e| InstructionError::from_li(instruction.clone(), e))?; @@ -78,17 +78,17 @@ where }) } -pub(crate) fn record_lower_memory( +pub(crate) fn record_lower_memory( record_type_id: u64, instruction: Instruction, -) -> crate::interpreter::ExecutableInstruction +) -> crate::interpreter::ExecutableInstruction where Export: crate::interpreter::wasm::structures::Export, LocalImport: crate::interpreter::wasm::structures::LocalImport, - Memory: crate::interpreter::wasm::structures::Memory, - MemoryView: crate::interpreter::wasm::structures::MemoryView, + Memory: crate::interpreter::wasm::structures::Memory, + SequentialMemoryView: for<'a> crate::interpreter::wasm::structures::SequentialMemoryView<'a>, Instance: - crate::interpreter::wasm::structures::Instance, + crate::interpreter::wasm::structures::Instance, { #[allow(unused_imports)] use crate::interpreter::stack::Stackable; @@ -107,8 +107,19 @@ where log::debug!("record.lower_memory: obtained {:?} values on the stack for record type = {}", record_fields, record_type_id); + let memory_index = DEFAULT_MEMORY_INDEX; + let memory_view = instance + .memory(memory_index) + .ok_or_else(|| { + InstructionError::from_error_kind( + instruction.clone(), + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })? + .view(); + let lo_helper = lilo::LoHelper::new(&**instance); - let memory_writer = ILowerer::new(&lo_helper) + let memory_writer = ILowerer::new(memory_view, &lo_helper) .map_err(|e| InstructionError::from_lo(instruction.clone(), e))?; let offset = it_lilo::lowerer::record_lower_memory(&memory_writer, record_fields) diff --git a/wasmer-it/src/interpreter/instructions/strings.rs b/wasmer-it/src/interpreter/instructions/strings.rs index 5d395ad..8e0808b 100644 --- a/wasmer-it/src/interpreter/instructions/strings.rs +++ b/wasmer-it/src/interpreter/instructions/strings.rs @@ -7,7 +7,10 @@ use crate::{ interpreter::Instruction, }; -use std::{cell::Cell, convert::TryInto}; +use it_memory_traits::{SequentialReader, SequentialWriter}; +use it_lilo::traits::DEFAULT_MEMORY_INDEX; + +use std::convert::TryInto; executable_instruction!( string_lift_memory(instruction: Instruction) -> _ { @@ -19,7 +22,7 @@ executable_instruction!( ) })?; - let memory_index = 0; + let memory_index = DEFAULT_MEMORY_INDEX; let memory = runtime .wasm_instance .memory(memory_index) @@ -46,20 +49,14 @@ executable_instruction!( return Ok(()) } - if memory_view.len() < pointer + length { - return instr_error!( - instruction.clone(), - InstructionErrorKind::MemoryOutOfBoundsAccess { - index: pointer + length, - length: memory_view.len(), - } - ); - } + let reader = memory_view + .sequential_reader(pointer, length) + .map_err(|e| InstructionError::from_memory_access(instruction.clone(), e))?; - let data: Vec = (&memory_view[pointer..pointer + length]) - .iter() - .map(Cell::get) - .collect(); + let mut data = Vec::::with_capacity(length); + for index in 0..length { + data[index] = reader.read_u8(); +} let string = String::from_utf8(data) .map_err(|error| InstructionError::from_error_kind(instruction.clone(), InstructionErrorKind::String(error)))?; @@ -96,7 +93,7 @@ executable_instruction!( })?; let instance = &mut runtime.wasm_instance; - let memory_index = 0; + let memory_index = DEFAULT_MEMORY_INDEX; let memory_view = instance .memory(memory_index) .ok_or_else(|| { @@ -107,9 +104,11 @@ executable_instruction!( })? .view(); - for (nth, byte) in string_bytes.iter().enumerate() { - memory_view[string_pointer as usize + nth].set(*byte); - } + let seq_writer = memory_view + .sequential_writer(string_pointer, string_length as usize) + .map_err(|e| InstructionError::from_memory_access(instruction.clone(), e))?; + + seq_writer.write_bytes(&string_bytes); log::debug!("string.lower_memory: pushing {}, {} on the stack", string_pointer, string_length); runtime.stack.push(IValue::I32(string_pointer as i32)); diff --git a/wasmer-it/src/interpreter/mod.rs b/wasmer-it/src/interpreter/mod.rs index 4fbcbdb..5f1f0ec 100644 --- a/wasmer-it/src/interpreter/mod.rs +++ b/wasmer-it/src/interpreter/mod.rs @@ -13,13 +13,13 @@ use std::{convert::TryFrom, marker::PhantomData}; /// Represents the `Runtime`, which is used by an adapter to execute /// its instructions. -pub(crate) struct Runtime<'invocation, 'instance, Instance, Export, LocalImport, Memory, MemoryView> +pub(crate) struct Runtime<'invocation, 'instance, Instance, Export, LocalImport, Memory, SequentialMemoryView> where Export: wasm::structures::Export + 'instance, LocalImport: wasm::structures::LocalImport + 'instance, - Memory: wasm::structures::Memory + 'instance, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance + 'instance, + Memory: wasm::structures::Memory + 'instance, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance + 'instance, { /// The invocation inputs are all the arguments received by an /// adapter. @@ -33,13 +33,13 @@ where wasm_instance: &'instance mut Instance, /// Phantom data. - _phantom: PhantomData<(Export, LocalImport, Memory, MemoryView)>, + _phantom: PhantomData<(Export, LocalImport, Memory, SequentialMemoryView)>, } /// Type alias for an executable instruction. It's an implementation /// details, but an instruction is a boxed closure instance. -pub(crate) type ExecutableInstruction = Box< - dyn Fn(&mut Runtime) -> InstructionResult<()> +pub(crate) type ExecutableInstruction = Box< + dyn Fn(&mut Runtime) -> InstructionResult<()> + Send, >; @@ -61,7 +61,7 @@ pub(crate) type ExecutableInstruction = (&vec![ +/// let interpreter: Interpreter = (&vec![ /// Instruction::ArgumentGet { index: 1 }, /// Instruction::ArgumentGet { index: 0 }, /// Instruction::CallCore { function_index: 42 }, @@ -121,31 +121,31 @@ pub(crate) type ExecutableInstruction +pub struct Interpreter where Export: wasm::structures::Export, LocalImport: wasm::structures::LocalImport, - Memory: wasm::structures::Memory, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance, { executable_instructions: - Vec>, + Vec>, } -impl - Interpreter +impl + Interpreter where Export: wasm::structures::Export, LocalImport: wasm::structures::LocalImport, - Memory: wasm::structures::Memory, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance, { fn iter( &self, ) -> impl Iterator< - Item = &ExecutableInstruction, + Item = &ExecutableInstruction, > + '_ { self.executable_instructions.iter() } @@ -176,14 +176,14 @@ where } /// Transforms a `Vec` into an `Interpreter`. -impl TryFrom> - for Interpreter +impl TryFrom> + for Interpreter where Export: wasm::structures::Export, LocalImport: wasm::structures::LocalImport, - Memory: wasm::structures::Memory, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory, + SequentialMemoryView: (for<'a> wasm::structures::SequentialMemoryView<'a>), + Instance: wasm::structures::Instance, { type Error = (); diff --git a/wasmer-it/src/interpreter/wasm/structures.rs b/wasmer-it/src/interpreter/wasm/structures.rs index e23144a..62f2cd9 100644 --- a/wasmer-it/src/interpreter/wasm/structures.rs +++ b/wasmer-it/src/interpreter/wasm/structures.rs @@ -5,7 +5,8 @@ use crate::IRecordType; use crate::IType; use crate::IValue; use std::rc::Rc; -use std::{cell::Cell, ops::Deref}; + +pub use it_memory_traits::{Memory, SequentialMemoryView, SequentialReader, SequentialWriter, MemoryAccessError}; pub trait TypedIndex: Copy + Clone { fn new(index: usize) -> Self; @@ -61,26 +62,17 @@ pub trait LocalImport { fn call(&self, arguments: &[IValue]) -> Result, ()>; } -pub trait MemoryView: Deref]> {} - -pub trait Memory -where - View: MemoryView, -{ - fn view(&self) -> View; -} - pub trait Instance where E: Export, LI: LocalImport, M: Memory, - MV: MemoryView, + MV: for<'a> SequentialMemoryView<'a>, { fn export(&self, export_name: &str) -> Option<&E>; fn local_or_import(&self, index: I) -> Option<&LI>; fn memory(&self, index: usize) -> Option<&M>; - fn memory_slice(&self, index: usize) -> Option<&[Cell]>; + fn memory_view(&self, index: usize) -> Option; fn wit_record_by_id(&self, index: u64) -> Option<&Rc>; } @@ -138,13 +130,49 @@ impl LocalImport for () { pub(crate) struct EmptyMemoryView; -impl MemoryView for EmptyMemoryView {} +pub(crate) struct EmptySequentialReader; +pub(crate) struct EmptySequentialWriter; -impl Deref for EmptyMemoryView { - type Target = [Cell]; +impl SequentialReader for EmptySequentialReader { + fn read_byte(&self) -> u8 { + 0u8 + } - fn deref(&self) -> &Self::Target { - &[] + fn read_bytes(&self) -> [u8; COUNT] { + [0u8; COUNT] + } +} + +impl SequentialWriter for EmptySequentialWriter { + fn start_offset(&self) -> usize { + 0 + } + + fn write_u8(&self, _value: u8) {} + + fn write_u32(&self, _value: u32) {} + + fn write_bytes(&self, _bytes: &[u8]) {} +} + +impl<'a> SequentialMemoryView<'a> for EmptyMemoryView { + type SR = EmptySequentialReader; + type SW = EmptySequentialWriter; + + fn sequential_writer(&self, offset: usize, size: usize) -> Result { + Err(MemoryAccessError::OutOfBounds { + offset, + size, + memory_size: 0, + }) + } + + fn sequential_reader(&self, offset: usize, size: usize) -> Result { + Err(MemoryAccessError::OutOfBounds { + offset, + size, + memory_size: 0, + }) } } @@ -159,7 +187,7 @@ where E: Export, LI: LocalImport, M: Memory, - MV: MemoryView, + MV: for<'a> SequentialMemoryView<'a>, { fn export(&self, _export_name: &str) -> Option<&E> { None @@ -169,7 +197,7 @@ where None } - fn memory_slice(&self, _: usize) -> Option<&[Cell]> { + fn memory_view(&self, _index: usize) -> Option { None } diff --git a/wasmer-it/src/macros.rs b/wasmer-it/src/macros.rs index 897fad7..4dd7e9a 100644 --- a/wasmer-it/src/macros.rs +++ b/wasmer-it/src/macros.rs @@ -67,15 +67,15 @@ macro_rules! consume { /// Check the existing executable instruction to get more examples. macro_rules! executable_instruction { ($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => { - pub(crate) fn $name( + pub(crate) fn $name( $($argument_name: $argument_type),* - ) -> crate::interpreter::ExecutableInstruction + ) -> crate::interpreter::ExecutableInstruction where Export: crate::interpreter::wasm::structures::Export, LocalImport: crate::interpreter::wasm::structures::LocalImport, - Memory: crate::interpreter::wasm::structures::Memory, - MemoryView: crate::interpreter::wasm::structures::MemoryView, - Instance: crate::interpreter::wasm::structures::Instance, + Memory: crate::interpreter::wasm::structures::Memory, + SequentialMemoryView: for<'a> crate::interpreter::wasm::structures::SequentialMemoryView<'a>, + Instance: crate::interpreter::wasm::structures::Instance, { #[allow(unused_imports)] use crate::interpreter::{stack::Stackable}; @@ -100,7 +100,7 @@ macro_rules! test_executable_instruction { fn $test_name() { use crate::{ interpreter::{ - instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView}, + instructions::tests::{Export, Instance, LocalImport, Memory, SequentialMemoryView}, stack::Stackable, Instruction, Interpreter, }, @@ -109,7 +109,7 @@ macro_rules! test_executable_instruction { }; use std::{cell::Cell, collections::HashMap, convert::TryInto}; - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![$($instructions),*]).try_into().unwrap(); let invocation_inputs = vec![$($invocation_inputs),*]; @@ -142,7 +142,7 @@ macro_rules! test_executable_instruction { fn $test_name() { use crate::{ interpreter::{ - instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView}, + instructions::tests::{Export, Instance, LocalImport, Memory, SequentialMemoryView}, stack::Stackable, Instruction, Interpreter, }, @@ -151,7 +151,7 @@ macro_rules! test_executable_instruction { }; use std::{cell::Cell, collections::HashMap, convert::TryInto}; - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![$($instructions),*]).try_into().unwrap(); let invocation_inputs = vec![$($invocation_inputs),*];