mirror of
https://github.com/fluencelabs/interface-types
synced 2024-12-04 07:10:21 +00:00
first attempt to fix
This commit is contained in:
parent
34bf8a196a
commit
6bf0eedec7
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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",
|
||||
|
@ -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 _))
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
@ -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::*;
|
||||
|
@ -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))
|
||||
}
|
@ -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));
|
||||
|
@ -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 _)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user