diff --git a/crates/it-lilo-utils/src/error.rs b/crates/it-lilo-utils/src/error.rs index 881fb57..fb7660f 100644 --- a/crates/it-lilo-utils/src/error.rs +++ b/crates/it-lilo-utils/src/error.rs @@ -27,3 +27,38 @@ pub enum MemoryAccessError { memory_size: usize, }, } + +#[derive(Debug, ThisError)] +pub enum MemoryWriteError { + /// The memory doesn't exist. + #[error("memory `{memory_index}` does not exist")] + MemoryIsMissing { + /// The memory index. + memory_index: usize, + }, + + /// The local or import function doesn't exist. + #[error("the allocate function with index `{function_index}` doesn't exist in Wasm module")] + AllocateFuncIsMissing { + /// The local or import function index. + function_index: u32, + }, + + /// Failed to call a allocate function. + #[error("call to allocated was failed")] + AllocateCallFailed, + + /// Allocate input types doesn't match with needed. + #[error( + "allocate func doesn't receive two i32 values,\ + probably a Wasm module's built with unsupported sdk version" + )] + AllocateFuncIncompatibleSignature, + + /// Allocate output types doesn't match with needed. + #[error( + "allocate func doesn't return a one value of I32 type,\ + probably a Wasm module's built with unsupported sdk version" + )] + AllocateFuncIncompatibleOutput, +} diff --git a/crates/it-lilo-utils/src/lib.rs b/crates/it-lilo-utils/src/lib.rs index dc1801f..6bb5d47 100644 --- a/crates/it-lilo-utils/src/lib.rs +++ b/crates/it-lilo-utils/src/lib.rs @@ -23,7 +23,8 @@ pub use fluence_it_types::IRecordType; pub use fluence_it_types::IType; pub use fluence_it_types::IValue; -pub type MResult = std::result::Result; +pub type ReadResult = std::result::Result; +pub type WriteResult = std::result::Result; /// Size of a value in a serialized view. pub fn ser_type_size(ty: &IType) -> usize { @@ -42,7 +43,7 @@ pub fn ser_type_size(ty: &IType) -> usize { } /// Size of a value in a serialized view. -pub fn ser_value_size(value: &IValue) -> usize { +pub fn ser_value_size(value: &IValue) -> u32 { match value { IValue::Boolean(_) | IValue::S8(_) | IValue::U8(_) => 1, IValue::S16(_) | IValue::U16(_) => 2, diff --git a/crates/it-lilo-utils/src/macros.rs b/crates/it-lilo-utils/src/macros.rs index 41889db..edd8c0e 100644 --- a/crates/it-lilo-utils/src/macros.rs +++ b/crates/it-lilo-utils/src/macros.rs @@ -101,7 +101,7 @@ macro_rules! read_array_ty { &self, offset: usize, elements_count: usize, - ) -> crate::MResult> { + ) -> crate::ReadResult> { let reader = self.sequential_reader(offset, std::mem::size_of::<$ty>() * elements_count)?; let mut result = Vec::with_capacity(elements_count); diff --git a/crates/it-lilo-utils/src/memory_reader.rs b/crates/it-lilo-utils/src/memory_reader.rs index d567137..3420b8c 100644 --- a/crates/it-lilo-utils/src/memory_reader.rs +++ b/crates/it-lilo-utils/src/memory_reader.rs @@ -18,7 +18,7 @@ use crate::error::MemoryAccessError; use crate::read_array_ty; use crate::read_ty; use crate::IValue; -use crate::MResult; +use crate::ReadResult; use std::cell::Cell; @@ -46,13 +46,13 @@ impl<'m> MemoryReader<'m> { &self, offset: usize, size: usize, - ) -> MResult> { + ) -> ReadResult> { self.check_access(offset, size)?; Ok(SequentialReader::new(&self, offset)) } - pub fn read_raw_u8_array(&self, offset: usize, elements_count: usize) -> MResult> { + pub fn read_raw_u8_array(&self, offset: usize, elements_count: usize) -> ReadResult> { let reader = self.sequential_reader(offset, elements_count)?; let mut result = Vec::with_capacity(elements_count); @@ -64,7 +64,7 @@ impl<'m> MemoryReader<'m> { Ok(result) } - pub fn read_bool_array(&self, offset: usize, elements_count: usize) -> MResult> { + pub fn read_bool_array(&self, offset: usize, elements_count: usize) -> ReadResult> { let reader = self.sequential_reader(offset, elements_count)?; let mut result = Vec::with_capacity(elements_count); @@ -76,7 +76,7 @@ impl<'m> MemoryReader<'m> { Ok(result) } - pub fn check_access(&self, offset: usize, size: usize) -> MResult<()> { + pub fn check_access(&self, offset: usize, size: usize) -> ReadResult<()> { let right = offset + size; // the first condition is a check for overflow diff --git a/crates/it-lilo-utils/src/memory_writer.rs b/crates/it-lilo-utils/src/memory_writer.rs index 7f36978..a3d6ff3 100644 --- a/crates/it-lilo-utils/src/memory_writer.rs +++ b/crates/it-lilo-utils/src/memory_writer.rs @@ -14,106 +14,78 @@ * limitations under the License. */ -use crate::error::MemoryAccessError; -use crate::MResult; - +use crate::WriteResult; use std::cell::Cell; -pub struct MemoryWriter<'m> { - memory: &'m [Cell], +pub type MemSlice<'m> = &'m [Cell]; + +const MEMORY_INDEX: usize = 0; + +pub trait Heapable { + fn allocate(&self, size: u32, type_tag: u32) -> WriteResult; + + fn memory_slice(&self, memory_index: usize) -> WriteResult>; } -/// Writes values of basic types sequentially to the provided writer. -/// It don't check memory limits for the optimization purposes, -/// so it could be created only by the MemoryReader::sequential_reader method. -pub struct SequentialWriter<'w, 'm> { - writer: &'w MemoryWriter<'m>, +pub struct MemoryWriter<'i, T: Heapable> { + heap_manager: &'i T, + pub(self) memory: Cell>, +} + +pub struct SequentialWriter { + start_offset: usize, offset: Cell, } -impl<'m> MemoryWriter<'m> { - pub fn new(memory: &'m [Cell]) -> Self { - Self { memory } +impl<'i, T: Heapable> MemoryWriter<'i, T> { + pub fn new(heap_manager: &'i T) -> WriteResult { + let mem_slice = heap_manager.memory_slice(MEMORY_INDEX)?; + let memory = Cell::new(mem_slice); + + let writer = Self { + heap_manager, + memory, + }; + Ok(writer) } - pub fn write_array(&self, offset: usize, values: [u8; N]) -> MResult<()> { - self.check_access(offset, values.len())?; + pub fn write_bytes(&self, bytes: &[u8]) -> WriteResult { + let byte_type_tag = crate::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); - self.memory[offset..offset + N] - .iter() - .zip(values.iter()) - .for_each(|(cell, &byte)| cell.set(byte)); - - Ok(()) + Ok(seq_writer.start_offset()) } - // specialization of write_array for u8 - pub fn write_u8(&self, offset: usize, value: u8) -> MResult<()> { - self.check_access(offset, 1)?; - self.memory[offset].set(value); + pub fn sequential_writer(&self, size: u32, type_tag: u32) -> WriteResult { + let offset = self.heap_manager.allocate(size, type_tag)?; + let new_mem_slice = self.heap_manager.memory_slice(MEMORY_INDEX)?; + self.memory.set(new_mem_slice); - Ok(()) - } - - // specialization of write_array for u32 - pub fn write_u32(&self, offset: usize, value: u32) -> MResult<()> { - self.check_access(offset, 4)?; - - let value = value.to_le_bytes(); - self.memory[offset].set(value[0]); - self.memory[offset + 1].set(value[1]); - self.memory[offset + 2].set(value[2]); - self.memory[offset + 3].set(value[3]); - - Ok(()) - } - - pub fn write_bytes(&self, offset: usize, bytes: &[u8]) -> MResult<()> { - let writer = self.sequential_writer(offset, bytes.len())?; - writer.write_bytes(bytes); - - Ok(()) - } - - pub fn sequential_writer( - &self, - offset: usize, - size: usize, - ) -> MResult> { - self.check_access(offset, size)?; - - Ok(SequentialWriter::new(&self, offset)) - } - - pub fn check_access(&self, offset: usize, size: usize) -> MResult<()> { - let right = offset + size; - - // the first condition is a check for overflow - if right < offset || right >= self.memory.len() { - return Err(MemoryAccessError::InvalidAccess { - offset, - size, - memory_size: self.memory.len(), - }); - } - - Ok(()) + Ok(SequentialWriter::new(offset)) } } -impl<'w, 'm> SequentialWriter<'w, 'm> { - pub(super) fn new(writer: &'w MemoryWriter<'m>, offset: usize) -> Self { - let offset = Cell::new(offset); - - Self { writer, offset } +impl SequentialWriter { + pub(self) fn new(offset: usize) -> Self { + Self { + offset: Cell::new(offset), + start_offset: offset, + } } - pub fn write_array(&self, values: [u8; N]) { + pub fn start_offset(&self) -> usize { + self.start_offset + } + + pub fn write_array( + &self, + writer: &MemoryWriter, + values: [u8; N], + ) { let offset = self.offset.get(); - log::trace!("write array: offset {} {:?}", offset, values); - - self.writer.memory[offset..offset + N] + writer.memory.get()[offset..offset + N] .iter() .zip(values.iter()) .for_each(|(cell, &byte)| cell.set(byte)); @@ -122,33 +94,35 @@ impl<'w, 'm> SequentialWriter<'w, 'm> { } // specialization of write_array for u8 - pub fn write_u8(&self, value: u8) { + pub fn write_u8(&self, writer: &MemoryWriter, value: u8) { let offset = self.offset.get(); - log::trace!("write_u8: write {} to {}", value, offset); - self.writer.memory[offset].set(value); + writer.memory.get()[offset].set(value); self.offset.set(offset + 1); } // specialization of write_array for u32 - pub fn write_u32(&self, value: u32) { + pub fn write_u32(&self, writer: &MemoryWriter, value: u32) { let offset = self.offset.get(); let value = value.to_le_bytes(); - self.writer.memory[offset].set(value[0]); - self.writer.memory[offset + 1].set(value[1]); - self.writer.memory[offset + 2].set(value[2]); - self.writer.memory[offset + 3].set(value[3]); + 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, bytes: &[u8]) { + pub fn write_bytes(&self, writer: &MemoryWriter, bytes: &[u8]) { let offset = self.offset.get(); - self.writer.memory[offset..offset + bytes.len()] + let memory = writer.memory.get(); + memory[offset..offset + bytes.len()] .iter() .zip(bytes) .for_each(|(cell, &byte)| cell.set(byte)); diff --git a/crates/it-types/src/impls/values.rs b/crates/it-types/src/impls/values.rs index 3d2c12e..6ae6f72 100644 --- a/crates/it-types/src/impls/values.rs +++ b/crates/it-types/src/impls/values.rs @@ -50,4 +50,46 @@ native!(f32, F32); native!(f64, F64); native!(u128, U128); native!(String, String); -native!(Vec, ByteArray); + +impl NativeType for Vec { + const INTERFACE_TYPE: IType = IType::ByteArray; +} + +impl From> for IValue { + fn from(n: Vec) -> Self { + IValue::ByteArray(n) + } +} + +impl TryFrom for Vec { + type Error = WasmValueNativeCastError; + + fn try_from(w: IValue) -> Result { + match w { + IValue::ByteArray(n) => Ok(n), + IValue::Array(ivalues) => try_to_byte_array(ivalues), + _ => Err(WasmValueNativeCastError { + from: w, + to: Vec::::INTERFACE_TYPE, + }), + } + } +} + +fn try_to_byte_array(ivalues: Vec) -> Result, WasmValueNativeCastError> { + let mut result = Vec::with_capacity(ivalues.len()); + + for value in &ivalues { + match value { + IValue::U8(byte) => result.push(*byte), + _ => { + return Err(WasmValueNativeCastError { + from: IValue::Array(ivalues), + to: Vec::::INTERFACE_TYPE, + }) + } + } + } + + Ok(result) +} diff --git a/wasmer-it/src/errors.rs b/wasmer-it/src/errors.rs index a9a894a..1e8601b 100644 --- a/wasmer-it/src/errors.rs +++ b/wasmer-it/src/errors.rs @@ -12,6 +12,7 @@ use std::{ string::{self, ToString}, }; +use it_lilo_utils::error::MemoryWriteError; use thiserror::Error as ThisError; pub use fluence_it_types::WasmValueNativeCastError; @@ -47,6 +48,14 @@ impl InstructionError { let error_kind = InstructionErrorKind::LiLoError(lilo); Self::from_error_kind(instruction, error_kind) } + + pub(crate) fn from_write_error( + instruction: Instruction, + write_error: MemoryWriteError, + ) -> Self { + let error_kind = InstructionErrorKind::MemoryWriteError(write_error); + Self::from_error_kind(instruction, error_kind) + } } impl Error for InstructionError {} @@ -221,6 +230,10 @@ pub enum InstructionErrorKind { /// Errors related to lifting/lowering records. #[error("{0}")] LiLoError(#[from] LiLoError), + + /// Errors related to incorrect writing to memory. + #[error("{0}")] + MemoryWriteError(#[from] MemoryWriteError), } impl From<(TryFromIntError, &'static str)> for InstructionErrorKind { diff --git a/wasmer-it/src/interpreter/instructions/arrays.rs b/wasmer-it/src/interpreter/instructions/arrays.rs index ea885e9..991fe60 100644 --- a/wasmer-it/src/interpreter/instructions/arrays.rs +++ b/wasmer-it/src/interpreter/instructions/arrays.rs @@ -14,6 +14,7 @@ use crate::{ interpreter::Instruction, IType, IValue, }; +use it_lilo_utils::memory_writer::MemoryWriter; use std::convert::TryInto; @@ -119,25 +120,12 @@ where })?; } - log::trace!("array.lower_memory: 1"); - let memory_index = 0; - let memory_view = instance - .memory(memory_index) - .ok_or_else(|| { - InstructionError::from_error_kind( - instruction.clone(), - InstructionErrorKind::MemoryIsMissing { memory_index }, - ) - })? - .view(); - log::trace!("array.lower_memory: 1"); - let memory = memory_view.deref(); + let lo_helper = lilo::LoHelper::new(&**instance); + let memory_writer = MemoryWriter::new(&lo_helper) + .map_err(|e| InstructionError::from_write_error(instruction.clone(), e))?; - let lo_helper = lilo::LoHelper::new(&**instance, memory) - .map_err(|e| InstructionError::from_lilo(instruction.clone(), e))?; - log::trace!("array.lower_memory: 3"); - let (offset, size) = array_lower_memory_impl(&lo_helper, values) - .map_err(|e| InstructionError::from_lilo(instruction.clone(), e))?; + let (offset, size) = array_lower_memory_impl(&memory_writer, values) + .map_err(|e| InstructionError::from_write_error(instruction.clone(), e))?; log::trace!( "array.lower_memory: pushing {}, {} on the stack", diff --git a/wasmer-it/src/interpreter/instructions/arrays/lower_array.rs b/wasmer-it/src/interpreter/instructions/arrays/lower_array.rs index 2f07af4..3ed7584 100644 --- a/wasmer-it/src/interpreter/instructions/arrays/lower_array.rs +++ b/wasmer-it/src/interpreter/instructions/arrays/lower_array.rs @@ -1,75 +1,66 @@ -use super::lilo::*; - use crate::IValue; +use it_lilo_utils::memory_writer::Heapable; +use it_lilo_utils::memory_writer::MemoryWriter; use it_lilo_utils::ser_value_size; use it_lilo_utils::type_tag_form_ivalue; +use it_lilo_utils::WriteResult; -pub(crate) fn array_lower_memory_impl( - lo_helper: &LoHelper, +pub(crate) fn array_lower_memory_impl( + writer: &MemoryWriter, array_values: Vec, -) -> LiLoResult<(usize, usize)> { - log::trace!("array_lower_memory_impl: 1"); +) -> WriteResult<(usize, usize)> { if array_values.is_empty() { return Ok((0, 0)); } - let elements_count = array_values.len(); - let size_to_allocate = ser_value_size(&array_values[0]) * elements_count; - let offset = (lo_helper.allocate)( - size_to_allocate as _, - type_tag_form_ivalue(&array_values[0]) as _, - )?; - log::trace!("array_lower_memory_impl: 2"); - - let seq_writer = lo_helper - .writer - .sequential_writer(offset, size_to_allocate)?; - log::trace!("array_lower_memory_impl: 3"); + let elements_count = array_values.len() as u32; + let size = ser_value_size(&array_values[0]) * elements_count; + let type_tag = type_tag_form_ivalue(&array_values[0]); + let seq_writer = writer.sequential_writer(size, type_tag)?; // here it's known that all interface values have the same type for value in array_values { - log::trace!("array_lower_memory_impl: write {:?}", value); match value { - IValue::Boolean(value) => seq_writer.write_u8(value as _), - IValue::S8(value) => seq_writer.write_u8(value as _), - IValue::S16(value) => seq_writer.write_array(value.to_le_bytes()), - IValue::S32(value) => seq_writer.write_array(value.to_le_bytes()), - IValue::S64(value) => seq_writer.write_array(value.to_le_bytes()), - IValue::U8(value) => seq_writer.write_array(value.to_le_bytes()), - IValue::U16(value) => seq_writer.write_array(value.to_le_bytes()), - IValue::U32(value) => seq_writer.write_array(value.to_le_bytes()), - IValue::U64(value) => seq_writer.write_array(value.to_le_bytes()), - IValue::U128(value) => seq_writer.write_array(value.to_le_bytes()), - IValue::I32(value) => seq_writer.write_array(value.to_le_bytes()), - IValue::I64(value) => seq_writer.write_array(value.to_le_bytes()), - IValue::F32(value) => seq_writer.write_array(value.to_le_bytes()), - IValue::F64(value) => seq_writer.write_array(value.to_le_bytes()), + IValue::Boolean(value) => seq_writer.write_u8(writer, value as _), + IValue::S8(value) => seq_writer.write_u8(writer, value as _), + IValue::S16(value) => seq_writer.write_array(writer, value.to_le_bytes()), + IValue::S32(value) => seq_writer.write_array(writer, value.to_le_bytes()), + IValue::S64(value) => seq_writer.write_array(writer, value.to_le_bytes()), + IValue::U8(value) => seq_writer.write_array(writer, value.to_le_bytes()), + IValue::U16(value) => seq_writer.write_array(writer, value.to_le_bytes()), + IValue::U32(value) => seq_writer.write_array(writer, value.to_le_bytes()), + IValue::U64(value) => seq_writer.write_array(writer, value.to_le_bytes()), + IValue::U128(value) => seq_writer.write_array(writer, value.to_le_bytes()), + IValue::I32(value) => seq_writer.write_array(writer, value.to_le_bytes()), + IValue::I64(value) => seq_writer.write_array(writer, value.to_le_bytes()), + IValue::F32(value) => seq_writer.write_array(writer, value.to_le_bytes()), + IValue::F64(value) => seq_writer.write_array(writer, value.to_le_bytes()), IValue::String(value) => { - let offset = lo_helper.write_to_mem(value.as_bytes())? as u32; + let offset = writer.write_bytes(value.as_bytes())? as u32; - seq_writer.write_array(offset.to_le_bytes()); - seq_writer.write_array((value.len() as u32).to_le_bytes()); + seq_writer.write_array(writer, offset.to_le_bytes()); + seq_writer.write_array(writer, (value.len() as u32).to_le_bytes()); } IValue::ByteArray(values) => { - let offset = lo_helper.write_to_mem(&values)? as u32; + let offset = writer.write_bytes(&values)? as u32; - seq_writer.write_array(offset.to_le_bytes()); - seq_writer.write_array((values.len() as u32).to_le_bytes()); + seq_writer.write_array(writer, offset.to_le_bytes()); + seq_writer.write_array(writer, (values.len() as u32).to_le_bytes()); } IValue::Array(values) => { - let (offset, size) = array_lower_memory_impl(lo_helper, values)?; + let (offset, size) = array_lower_memory_impl(writer, values)?; - seq_writer.write_array((offset as u32).to_le_bytes()); - seq_writer.write_array((size as u32).to_le_bytes()); + seq_writer.write_array(writer, (offset as u32).to_le_bytes()); + seq_writer.write_array(writer, (size as u32).to_le_bytes()); } - IValue::Record(values) => { - let offset = super::record_lower_memory_impl(lo_helper, values)? as u32; - seq_writer.write_array(offset.to_le_bytes()); + let offset = super::record_lower_memory_impl(writer, values)? as u32; + seq_writer.write_array(writer, offset.to_le_bytes()); } } } + let offset = seq_writer.start_offset(); Ok((offset as _, elements_count as _)) } diff --git a/wasmer-it/src/interpreter/instructions/byte_arrays.rs b/wasmer-it/src/interpreter/instructions/byte_arrays.rs index fc0566d..5ca3dff 100644 --- a/wasmer-it/src/interpreter/instructions/byte_arrays.rs +++ b/wasmer-it/src/interpreter/instructions/byte_arrays.rs @@ -129,10 +129,21 @@ executable_instruction!( Ok(()) }, + Some(IValue::Array(array)) => { + let array = check_array_type(array, &instruction)?; + + let length = array.len() as i32; + + log::debug!("byte_array.size: pushing {} on the stack", length); + runtime.stack.push(IValue::I32(length)); + + Ok(()) + }, + Some(value) => instr_error!( instruction.clone(), InstructionErrorKind::InvalidValueOnTheStack { - expected_type: IType::String, + expected_type: IType::ByteArray, received_value: (&value).clone(), } ), @@ -145,3 +156,23 @@ executable_instruction!( } } ); + +fn check_array_type( + ivalues: Vec, + instruction: &Instruction, +) -> Result, InstructionError> { + if ivalues.is_empty() { + return Ok(ivalues); + } + + match &ivalues[0] { + IValue::U8(_) => Ok(ivalues), + _ => instr_error!( + instruction.clone(), + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: IType::ByteArray, + received_value: IValue::Array(ivalues), + } + ), + } +} diff --git a/wasmer-it/src/interpreter/instructions/lilo/li_helper.rs b/wasmer-it/src/interpreter/instructions/lilo/li_helper.rs index 617baba..8dd87ec 100644 --- a/wasmer-it/src/interpreter/instructions/lilo/li_helper.rs +++ b/wasmer-it/src/interpreter/instructions/lilo/li_helper.rs @@ -1,10 +1,12 @@ +use super::LiLoError; use super::LiLoResult; -use super::RecordResolver; use crate::interpreter::wasm; +use crate::IRecordType; use it_lilo_utils::memory_reader::MemoryReader; use std::cell::Cell; +use std::rc::Rc; pub(crate) struct LiHelper<'i> { pub(crate) reader: MemoryReader<'i>, @@ -23,7 +25,7 @@ impl<'instance> LiHelper<'instance> { MemoryView: wasm::structures::MemoryView, Instance: wasm::structures::Instance, { - let record_resolver = super::build_record_resolver(instance)?; + let record_resolver = build_record_resolver(instance)?; let reader = MemoryReader::new(memory); let helper = Self { @@ -34,3 +36,26 @@ impl<'instance> LiHelper<'instance> { Ok(helper) } } + +pub(crate) type RecordResolver<'i> = Box LiLoResult> + 'i>; + +pub(super) fn build_record_resolver<'instance, Instance, Export, LocalImport, Memory, MemoryView>( + instance: &'instance Instance, +) -> LiLoResult> +where + Export: wasm::structures::Export + 'instance, + LocalImport: wasm::structures::LocalImport + 'instance, + Memory: wasm::structures::Memory + 'instance, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, +{ + let resolver = move |record_type_id: u64| { + let record = instance + .wit_record_by_id(record_type_id) + .ok_or(LiLoError::RecordTypeByNameIsMissing { record_type_id })?; + + Ok(record.clone()) + }; + + Ok(Box::new(resolver)) +} diff --git a/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs b/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs index b2e1b5d..2c8289a 100644 --- a/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs +++ b/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs @@ -1,45 +1,95 @@ -use super::AllocateFunc; -use super::LiLoResult; - use crate::interpreter::wasm; -use crate::IType; +use crate::interpreter::wasm::structures::FunctionIndex; +use crate::IValue; -use it_lilo_utils::memory_writer::MemoryWriter; -use it_lilo_utils::type_tag_form_itype; +use it_lilo_utils::error::MemoryWriteError; +use it_lilo_utils::memory_writer::Heapable; +use it_lilo_utils::WriteResult; use std::cell::Cell; +use std::marker::PhantomData; -pub(crate) struct LoHelper<'i> { - pub(crate) writer: MemoryWriter<'i>, - pub(crate) allocate: AllocateFunc<'i>, +pub struct LoHelper<'i, Instance, Export, LocalImport, Memory, MemoryView> +where + Export: wasm::structures::Export + 'i, + LocalImport: wasm::structures::LocalImport + 'i, + Memory: wasm::structures::Memory + 'i, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, +{ + pub(crate) instance: &'i Instance, + _phantom_export: PhantomData, + _phantom_local_import: PhantomData, + _phantom_memory: PhantomData, + _phantom_memory_view: PhantomData, } -impl<'instance> LoHelper<'instance> { - pub(crate) fn new( - instance: &'instance Instance, - memory: &'instance [Cell], - ) -> LiLoResult - where - Export: wasm::structures::Export + 'instance, - LocalImport: wasm::structures::LocalImport + 'instance, - Memory: wasm::structures::Memory + 'instance, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, - { - let allocate = super::build_allocate_func(instance)?; - let writer = MemoryWriter::new(memory); - - let helper = Self { writer, allocate }; - - Ok(helper) - } - - pub(crate) fn write_to_mem(&self, bytes: &[u8]) -> LiLoResult { - let alloc_type_tag = type_tag_form_itype(&IType::U8); - let offset = (self.allocate)(bytes.len() as _, alloc_type_tag as _)?; - - self.writer.write_bytes(offset, bytes)?; - - Ok(offset) +impl<'i, Instance, Export, LocalImport, Memory, MemoryView> + LoHelper<'i, Instance, Export, LocalImport, Memory, MemoryView> +where + Export: wasm::structures::Export + 'i, + LocalImport: wasm::structures::LocalImport + 'i, + Memory: wasm::structures::Memory + 'i, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, +{ + pub(crate) fn new(instance: &'i Instance) -> Self { + Self { + instance, + _phantom_export: PhantomData, + _phantom_local_import: PhantomData, + _phantom_memory: PhantomData, + _phantom_memory_view: PhantomData, + } + } +} + +impl<'i, Instance, Export, LocalImport, Memory, MemoryView> Heapable + for LoHelper<'i, Instance, Export, LocalImport, Memory, MemoryView> +where + Export: wasm::structures::Export + 'i, + LocalImport: wasm::structures::LocalImport + 'i, + Memory: wasm::structures::Memory + 'i, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, +{ + fn allocate(&self, size: u32, type_tag: u32) -> WriteResult { + use crate::interpreter::instructions::ALLOCATE_FUNC_INDEX; + use crate::interpreter::wasm::structures::TypedIndex; + + let index = FunctionIndex::new(ALLOCATE_FUNC_INDEX as usize); + let local_or_import = self.instance.local_or_import(index).ok_or( + MemoryWriteError::AllocateFuncIsMissing { + function_index: ALLOCATE_FUNC_INDEX, + }, + )?; + + let inputs = vec![IValue::I32(size as _), IValue::I32(type_tag as _)]; + // TODO: we could check it only once on the module startup or memorize check result + crate::interpreter::instructions::check_function_signature( + self.instance, + local_or_import, + &inputs, + ) + .map_err(|_| MemoryWriteError::AllocateFuncIncompatibleSignature)?; + + let outcome = local_or_import + .call(&inputs) + .map_err(|_| MemoryWriteError::AllocateCallFailed)?; + + if outcome.len() != 1 { + return Err(MemoryWriteError::AllocateFuncIncompatibleOutput); + } + + match outcome[0] { + IValue::I32(offset) => Ok(offset as _), + _ => Err(MemoryWriteError::AllocateFuncIncompatibleOutput), + } + } + + fn memory_slice(&self, memory_index: usize) -> WriteResult<&[Cell]> { + self.instance + .memory_slice(memory_index) + .ok_or(MemoryWriteError::MemoryIsMissing { memory_index }) } } diff --git a/wasmer-it/src/interpreter/instructions/lilo/mod.rs b/wasmer-it/src/interpreter/instructions/lilo/mod.rs index f14ce7e..b651497 100644 --- a/wasmer-it/src/interpreter/instructions/lilo/mod.rs +++ b/wasmer-it/src/interpreter/instructions/lilo/mod.rs @@ -1,13 +1,8 @@ mod li_helper; mod lo_helper; -mod utils; pub(crate) use crate::errors::LiLoError; pub(crate) use li_helper::LiHelper; pub(crate) use lo_helper::LoHelper; -pub(crate) use utils::AllocateFunc; -pub(crate) use utils::RecordResolver; pub(crate) type LiLoResult = std::result::Result; - -pub(self) use utils::*; diff --git a/wasmer-it/src/interpreter/instructions/lilo/utils.rs b/wasmer-it/src/interpreter/instructions/lilo/utils.rs deleted file mode 100644 index 7ac8fce..0000000 --- a/wasmer-it/src/interpreter/instructions/lilo/utils.rs +++ /dev/null @@ -1,88 +0,0 @@ -use super::LiLoError; -use super::LiLoResult; -use crate::interpreter::instructions::ALLOCATE_FUNC_INDEX; -use crate::interpreter::wasm; -use crate::interpreter::wasm::structures::{FunctionIndex, TypedIndex}; - -use crate::IRecordType; -use crate::IValue; - -use std::rc::Rc; - -pub(crate) type AllocateFunc<'i> = Box LiLoResult + 'i>; -pub(crate) type RecordResolver<'i> = Box LiLoResult> + 'i>; - -pub(super) fn build_allocate_func<'instance, Instance, Export, LocalImport, Memory, MemoryView>( - instance: &'instance Instance, -) -> LiLoResult> -where - Export: wasm::structures::Export + 'instance, - LocalImport: wasm::structures::LocalImport + 'instance, - Memory: wasm::structures::Memory + 'instance, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, -{ - let closure = move |size: usize, ty: usize| { - log::trace!("call allocate closure 1: {} {}", size, ty); - let index = FunctionIndex::new(ALLOCATE_FUNC_INDEX as usize); - let local_or_import = - instance - .local_or_import(index) - .ok_or(LiLoError::AllocateFuncIsMissing { - function_index: ALLOCATE_FUNC_INDEX, - })?; - - log::trace!("call allocate closure 2"); - - let inputs = vec![IValue::I32(size as _), IValue::I32(ty as _)]; - // TODO: we could check it only once on the module startup or memorize check result - crate::interpreter::instructions::check_function_signature( - instance, - local_or_import, - &inputs, - ) - .map_err(|_| LiLoError::AllocateFuncIncompatibleSignature)?; - - log::trace!("call allocate closure 3: {:?}", inputs); - - let outcome = local_or_import - .call(&inputs) - .map_err(|_| LiLoError::AllocateCallFailed)?; - - log::trace!("call allocate closure 4: {:?}", outcome); - - if outcome.len() != 1 { - return Err(LiLoError::AllocateFuncIncompatibleOutput); - } - - log::trace!("call allocate closure 5"); - - match outcome[0] { - IValue::I32(offset) => Ok(offset as _), - _ => Err(LiLoError::AllocateFuncIncompatibleOutput), - } - }; - - Ok(Box::new(closure)) -} - -pub(super) fn build_record_resolver<'instance, Instance, Export, LocalImport, Memory, MemoryView>( - instance: &'instance Instance, -) -> LiLoResult> -where - Export: wasm::structures::Export + 'instance, - LocalImport: wasm::structures::LocalImport + 'instance, - Memory: wasm::structures::Memory + 'instance, - MemoryView: wasm::structures::MemoryView, - Instance: wasm::structures::Instance, -{ - let resolver = move |record_type_id: u64| { - let record = instance - .wit_record_by_id(record_type_id) - .ok_or(LiLoError::RecordTypeByNameIsMissing { record_type_id })?; - - Ok(record.clone()) - }; - - Ok(Box::new(resolver)) -} diff --git a/wasmer-it/src/interpreter/instructions/records.rs b/wasmer-it/src/interpreter/instructions/records.rs index dfc68bb..d40422b 100644 --- a/wasmer-it/src/interpreter/instructions/records.rs +++ b/wasmer-it/src/interpreter/instructions/records.rs @@ -7,13 +7,13 @@ pub(crate) use lower_record::record_lower_memory_impl; use super::array_lift_memory_impl; use super::array_lower_memory_impl; use super::lilo; - use crate::instr_error; use crate::interpreter::instructions::{is_record_fields_compatible_to_type, to_native}; use crate::IType; use crate::IValue; use crate::{errors::InstructionError, errors::InstructionErrorKind, interpreter::Instruction}; +use it_lilo_utils::memory_writer::MemoryWriter; use std::convert::TryInto; pub(crate) fn record_lift_memory( @@ -113,22 +113,11 @@ where log::debug!("record.lower_memory: obtained {:?} values on the stack for record type = {}", record_fields, record_type_id); - let memory_index = 0; - let memory_view = instance - .memory(memory_index) - .ok_or_else(|| { - InstructionError::from_error_kind( - instruction.clone(), - InstructionErrorKind::MemoryIsMissing { memory_index }, - ) - })? - .view(); - let memory = memory_view.deref(); - - let lo_helper = lilo::LoHelper::new(&**instance, memory) - .map_err(|e| InstructionError::from_lilo(instruction.clone(), e))?; - let offset = record_lower_memory_impl(&lo_helper, record_fields) - .map_err(|e| InstructionError::from_lilo(instruction.clone(), e))?; + let lo_helper = lilo::LoHelper::new(&**instance); + let memory_writer = MemoryWriter::new(&lo_helper) + .map_err(|e| InstructionError::from_write_error(instruction.clone(), e))?; + let offset = record_lower_memory_impl(&memory_writer, record_fields) + .map_err(|e| InstructionError::from_write_error(instruction.clone(), e))?; log::debug!("record.lower_memory: pushing {} on the stack", offset); runtime.stack.push(IValue::I32(offset)); diff --git a/wasmer-it/src/interpreter/instructions/records/lower_record.rs b/wasmer-it/src/interpreter/instructions/records/lower_record.rs index 616e777..4604e63 100644 --- a/wasmer-it/src/interpreter/instructions/records/lower_record.rs +++ b/wasmer-it/src/interpreter/instructions/records/lower_record.rs @@ -1,12 +1,13 @@ -use super::lilo::*; - use crate::IValue; use crate::NEVec; +use it_lilo_utils::memory_writer::Heapable; +use it_lilo_utils::memory_writer::MemoryWriter; +use it_lilo_utils::WriteResult; -pub(crate) fn record_lower_memory_impl( - lo_helper: &LoHelper, +pub(crate) fn record_lower_memory_impl( + writer: &MemoryWriter, values: NEVec, -) -> LiLoResult { +) -> WriteResult { let average_field_size = 4; let mut result: Vec = Vec::with_capacity(average_field_size * values.len()); @@ -27,34 +28,34 @@ pub(crate) fn record_lower_memory_impl( IValue::F32(value) => result.extend_from_slice(&value.to_le_bytes()), IValue::F64(value) => result.extend_from_slice(&value.to_le_bytes()), IValue::String(value) => { - let offset = lo_helper.write_to_mem(value.as_bytes())? as u32; + let offset = writer.write_bytes(value.as_bytes())? as u32; result.extend_from_slice(&offset.to_le_bytes()); result.extend_from_slice(&(value.len() as u32).to_le_bytes()); } IValue::ByteArray(value) => { - let offset = lo_helper.write_to_mem(&value)? as u32; + let offset = writer.write_bytes(&value)? as u32; result.extend_from_slice(&offset.to_le_bytes()); result.extend_from_slice(&(value.len() as u32).to_le_bytes()); } IValue::Array(values) => { - let (offset, size) = super::array_lower_memory_impl(lo_helper, values)?; + let (offset, size) = super::array_lower_memory_impl(writer, values)?; result.extend_from_slice(&(offset as u32).to_le_bytes()); result.extend_from_slice(&(size as u32).to_le_bytes()); } IValue::Record(values) => { - let offset = record_lower_memory_impl(lo_helper, values)? as u32; + let offset = record_lower_memory_impl(writer, values)? as u32; result.extend_from_slice(&offset.to_le_bytes()); } } } - let result_pointer = lo_helper.write_to_mem(&result)?; + let result_pointer = writer.write_bytes(&result)?; Ok(result_pointer as _) } diff --git a/wasmer-it/src/interpreter/wasm/structures.rs b/wasmer-it/src/interpreter/wasm/structures.rs index 4666a75..e23144a 100644 --- a/wasmer-it/src/interpreter/wasm/structures.rs +++ b/wasmer-it/src/interpreter/wasm/structures.rs @@ -80,6 +80,7 @@ where 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 wit_record_by_id(&self, index: u64) -> Option<&Rc>; } @@ -168,6 +169,10 @@ where None } + fn memory_slice(&self, _: usize) -> Option<&[Cell]> { + None + } + fn local_or_import(&self, _index: I) -> Option<&LI> { None }