first attempt to fix

This commit is contained in:
vms 2021-04-23 23:56:41 +03:00
parent 34bf8a196a
commit 6bf0eedec7
17 changed files with 373 additions and 321 deletions

View File

@ -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,
}

View File

@ -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<T> = std::result::Result<T, error::MemoryAccessError>;
pub type ReadResult<T> = std::result::Result<T, error::MemoryAccessError>;
pub type WriteResult<T> = std::result::Result<T, error::MemoryWriteError>;
/// 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,

View File

@ -101,7 +101,7 @@ macro_rules! read_array_ty {
&self,
offset: usize,
elements_count: usize,
) -> crate::MResult<Vec<crate::IValue>> {
) -> crate::ReadResult<Vec<crate::IValue>> {
let reader =
self.sequential_reader(offset, std::mem::size_of::<$ty>() * elements_count)?;
let mut result = Vec::with_capacity(elements_count);

View File

@ -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<SequentialReader<'_, '_>> {
) -> ReadResult<SequentialReader<'_, '_>> {
self.check_access(offset, size)?;
Ok(SequentialReader::new(&self, offset))
}
pub fn read_raw_u8_array(&self, offset: usize, elements_count: usize) -> MResult<Vec<u8>> {
pub fn read_raw_u8_array(&self, offset: usize, elements_count: usize) -> ReadResult<Vec<u8>> {
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<Vec<IValue>> {
pub fn read_bool_array(&self, offset: usize, elements_count: usize) -> ReadResult<Vec<IValue>> {
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

View File

@ -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<u8>],
pub type MemSlice<'m> = &'m [Cell<u8>];
const MEMORY_INDEX: usize = 0;
pub trait Heapable {
fn allocate(&self, size: u32, type_tag: u32) -> WriteResult<usize>;
fn memory_slice(&self, memory_index: usize) -> WriteResult<MemSlice<'_>>;
}
/// 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<MemSlice<'i>>,
}
pub struct SequentialWriter {
start_offset: usize,
offset: Cell<usize>,
}
impl<'m> MemoryWriter<'m> {
pub fn new(memory: &'m [Cell<u8>]) -> Self {
Self { memory }
impl<'i, T: Heapable> MemoryWriter<'i, T> {
pub fn new(heap_manager: &'i T) -> WriteResult<Self> {
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<const N: usize>(&self, offset: usize, values: [u8; N]) -> MResult<()> {
self.check_access(offset, values.len())?;
pub fn write_bytes(&self, bytes: &[u8]) -> WriteResult<usize> {
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<SequentialWriter> {
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<SequentialWriter<'_, '_>> {
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<const N: usize>(&self, values: [u8; N]) {
pub fn start_offset(&self) -> usize {
self.start_offset
}
pub fn write_array<T: Heapable, const N: usize>(
&self,
writer: &MemoryWriter<T>,
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<T: Heapable>(&self, writer: &MemoryWriter<T>, 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<T: Heapable>(&self, writer: &MemoryWriter<T>, 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<T: Heapable>(&self, writer: &MemoryWriter<T>, 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));

View File

@ -50,4 +50,46 @@ native!(f32, F32);
native!(f64, F64);
native!(u128, U128);
native!(String, String);
native!(Vec<u8>, ByteArray);
impl NativeType for Vec<u8> {
const INTERFACE_TYPE: IType = IType::ByteArray;
}
impl From<Vec<u8>> for IValue {
fn from(n: Vec<u8>) -> Self {
IValue::ByteArray(n)
}
}
impl TryFrom<IValue> for Vec<u8> {
type Error = WasmValueNativeCastError;
fn try_from(w: IValue) -> Result<Self, Self::Error> {
match w {
IValue::ByteArray(n) => Ok(n),
IValue::Array(ivalues) => try_to_byte_array(ivalues),
_ => Err(WasmValueNativeCastError {
from: w,
to: Vec::<u8>::INTERFACE_TYPE,
}),
}
}
}
fn try_to_byte_array(ivalues: Vec<IValue>) -> Result<Vec<u8>, 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::<u8>::INTERFACE_TYPE,
})
}
}
}
Ok(result)
}

View File

@ -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 {

View File

@ -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",

View File

@ -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<T: Heapable>(
writer: &MemoryWriter<T>,
array_values: Vec<IValue>,
) -> 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 _))
}

View File

@ -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<IValue>,
instruction: &Instruction,
) -> Result<Vec<IValue>, 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),
}
),
}
}

View File

@ -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<Export, LocalImport, Memory, MemoryView>,
{
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<dyn Fn(u64) -> LiLoResult<Rc<IRecordType>> + 'i>;
pub(super) fn build_record_resolver<'instance, Instance, Export, LocalImport, Memory, MemoryView>(
instance: &'instance Instance,
) -> LiLoResult<RecordResolver<'instance>>
where
Export: wasm::structures::Export + 'instance,
LocalImport: wasm::structures::LocalImport + 'instance,
Memory: wasm::structures::Memory<MemoryView> + 'instance,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
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))
}

View File

@ -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<MemoryView> + 'i,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
pub(crate) instance: &'i Instance,
_phantom_export: PhantomData<Export>,
_phantom_local_import: PhantomData<LocalImport>,
_phantom_memory: PhantomData<Memory>,
_phantom_memory_view: PhantomData<MemoryView>,
}
impl<'instance> LoHelper<'instance> {
pub(crate) fn new<Instance, Export, LocalImport, Memory, MemoryView>(
instance: &'instance Instance,
memory: &'instance [Cell<u8>],
) -> LiLoResult<Self>
where
Export: wasm::structures::Export + 'instance,
LocalImport: wasm::structures::LocalImport + 'instance,
Memory: wasm::structures::Memory<MemoryView> + 'instance,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
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<usize> {
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<MemoryView> + 'i,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
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<MemoryView> + 'i,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
fn allocate(&self, size: u32, type_tag: u32) -> WriteResult<usize> {
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<u8>]> {
self.instance
.memory_slice(memory_index)
.ok_or(MemoryWriteError::MemoryIsMissing { memory_index })
}
}

View File

@ -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<T> = std::result::Result<T, LiLoError>;
pub(self) use utils::*;

View File

@ -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<dyn Fn(usize, usize) -> LiLoResult<usize> + 'i>;
pub(crate) type RecordResolver<'i> = Box<dyn Fn(u64) -> LiLoResult<Rc<IRecordType>> + 'i>;
pub(super) fn build_allocate_func<'instance, Instance, Export, LocalImport, Memory, MemoryView>(
instance: &'instance Instance,
) -> LiLoResult<AllocateFunc<'instance>>
where
Export: wasm::structures::Export + 'instance,
LocalImport: wasm::structures::LocalImport + 'instance,
Memory: wasm::structures::Memory<MemoryView> + 'instance,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
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<RecordResolver<'instance>>
where
Export: wasm::structures::Export + 'instance,
LocalImport: wasm::structures::LocalImport + 'instance,
Memory: wasm::structures::Memory<MemoryView> + 'instance,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
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))
}

View File

@ -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<Instance, Export, LocalImport, Memory, MemoryView>(
@ -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));

View File

@ -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<T: Heapable>(
writer: &MemoryWriter<T>,
values: NEVec<IValue>,
) -> LiLoResult<i32> {
) -> WriteResult<i32> {
let average_field_size = 4;
let mut result: Vec<u8> = 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 _)
}

View File

@ -80,6 +80,7 @@ where
fn export(&self, export_name: &str) -> Option<&E>;
fn local_or_import<I: TypedIndex + LocalImportIndex>(&self, index: I) -> Option<&LI>;
fn memory(&self, index: usize) -> Option<&M>;
fn memory_slice(&self, index: usize) -> Option<&[Cell<u8>]>;
fn wit_record_by_id(&self, index: u64) -> Option<&Rc<IRecordType>>;
}
@ -168,6 +169,10 @@ where
None
}
fn memory_slice(&self, _: usize) -> Option<&[Cell<u8>]> {
None
}
fn local_or_import<I: TypedIndex + LocalImportIndex>(&self, _index: I) -> Option<&LI> {
None
}