mirror of
https://github.com/fluencelabs/interface-types
synced 2024-12-04 15:20:20 +00:00
Merge pull request #2 from fluencelabs/struct_support
Support records and byte arrays
This commit is contained in:
commit
458adc2534
25
Cargo.lock
generated
25
Cargo.lock
generated
@ -21,6 +21,12 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||
|
||||
[[package]]
|
||||
name = "leb128"
|
||||
version = "0.2.4"
|
||||
@ -97,6 +103,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1"
|
||||
|
||||
[[package]]
|
||||
name = "safe-transmute"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50b8b2cd387f744f69469aaed197954ba4c0ecdb31e02edf99b023e0df11178a"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
@ -132,6 +144,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "0.3.4"
|
||||
@ -166,7 +189,9 @@ name = "wasmer-interface-types"
|
||||
version = "0.17.0"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"safe-transmute",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wast",
|
||||
]
|
||||
|
||||
|
@ -15,6 +15,8 @@ wast = "8.0"
|
||||
# required by WIT itself, is is used to cross the boundary between the
|
||||
# host and WIT more easily, but it is not used inside Wasm.
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
serde_json = "1.0"
|
||||
safe-transmute = "0.11.0"
|
||||
|
||||
[features]
|
||||
default = ["serde"]
|
||||
default = ["serde"]
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
use std::str;
|
||||
|
||||
/// Represents the kind of type.
|
||||
#[derive(PartialEq, Debug)]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum TypeKind {
|
||||
/// A function type.
|
||||
Function,
|
||||
@ -18,7 +18,7 @@ pub enum TypeKind {
|
||||
}
|
||||
|
||||
/// Represents a type.
|
||||
#[derive(PartialEq, Debug)]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Type {
|
||||
/// A function type, like:
|
||||
///
|
||||
|
@ -92,6 +92,7 @@ fn ty<'input, E: ParseError<&'input [u8]>>(
|
||||
0x08 => InterfaceType::F32,
|
||||
0x09 => InterfaceType::F64,
|
||||
0x0a => InterfaceType::String,
|
||||
0x36 => InterfaceType::ByteArray,
|
||||
0x0b => InterfaceType::Anyref,
|
||||
0x0c => InterfaceType::I32,
|
||||
0x0d => InterfaceType::I64,
|
||||
@ -235,6 +236,10 @@ fn instruction<'input, E: ParseError<&'input [u8]>>(
|
||||
0x23 => (input, Instruction::StringLowerMemory),
|
||||
0x24 => (input, Instruction::StringSize),
|
||||
|
||||
0x37 => (input, Instruction::ByteArrayLiftMemory),
|
||||
0x38 => (input, Instruction::ByteArrayLowerMemory),
|
||||
0x39 => (input, Instruction::ByteArraySize),
|
||||
|
||||
0x25 => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
|
||||
@ -256,6 +261,27 @@ fn instruction<'input, E: ParseError<&'input [u8]>>(
|
||||
)
|
||||
}
|
||||
|
||||
0x3A => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
|
||||
(
|
||||
input,
|
||||
Instruction::RecordLiftMemory {
|
||||
type_index: argument_0 as u32,
|
||||
},
|
||||
)
|
||||
}
|
||||
0x3B => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
|
||||
(
|
||||
input,
|
||||
Instruction::RecordLowerMemory {
|
||||
type_index: argument_0 as u32,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
0x34 => (input, Instruction::Dup),
|
||||
|
||||
0x35 => (input, Instruction::Swap2),
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
use crate::{ast::*, interpreter::Instruction, types::*, vec1::Vec1};
|
||||
pub use wast::parser::ParseBuffer as Buffer;
|
||||
pub use wast::Error;
|
||||
use wast::parser::{self, Cursor, Parse, Parser, Peek, Result};
|
||||
pub use wast::Error;
|
||||
|
||||
mod keyword {
|
||||
pub use wast::{
|
||||
@ -27,6 +27,7 @@ mod keyword {
|
||||
custom_keyword!(u32);
|
||||
custom_keyword!(u64);
|
||||
custom_keyword!(string);
|
||||
custom_keyword!(byte_array);
|
||||
|
||||
// Instructions.
|
||||
custom_keyword!(argument_get = "arg.get");
|
||||
@ -66,8 +67,13 @@ mod keyword {
|
||||
custom_keyword!(string_lift_memory = "string.lift_memory");
|
||||
custom_keyword!(string_lower_memory = "string.lower_memory");
|
||||
custom_keyword!(string_size = "string.size");
|
||||
custom_keyword!(byte_array_lift_memory = "byte_array.lift_memory");
|
||||
custom_keyword!(byte_array_lower_memory = "byte_array.lower_memory");
|
||||
custom_keyword!(byte_array_size = "byte_array.size");
|
||||
custom_keyword!(record_lift = "record.lift");
|
||||
custom_keyword!(record_lower = "record.lower");
|
||||
custom_keyword!(record_lift_memory = "record.lift_memory");
|
||||
custom_keyword!(record_lower_memory = "record.lower_memory");
|
||||
custom_keyword!(dup = "dup");
|
||||
custom_keyword!(swap2 = "swap2");
|
||||
}
|
||||
@ -120,6 +126,10 @@ impl Parse<'_> for InterfaceType {
|
||||
parser.parse::<keyword::string>()?;
|
||||
|
||||
Ok(InterfaceType::String)
|
||||
} else if lookahead.peek::<keyword::byte_array>() {
|
||||
parser.parse::<keyword::byte_array>()?;
|
||||
|
||||
Ok(InterfaceType::ByteArray)
|
||||
} else if lookahead.peek::<keyword::anyref>() {
|
||||
parser.parse::<keyword::anyref>()?;
|
||||
|
||||
@ -317,6 +327,18 @@ impl<'a> Parse<'a> for Instruction {
|
||||
parser.parse::<keyword::string_size>()?;
|
||||
|
||||
Ok(Instruction::StringSize)
|
||||
} else if lookahead.peek::<keyword::byte_array_lift_memory>() {
|
||||
parser.parse::<keyword::byte_array_lift_memory>()?;
|
||||
|
||||
Ok(Instruction::ByteArrayLiftMemory)
|
||||
} else if lookahead.peek::<keyword::byte_array_lower_memory>() {
|
||||
parser.parse::<keyword::byte_array_lower_memory>()?;
|
||||
|
||||
Ok(Instruction::ByteArrayLowerMemory)
|
||||
} else if lookahead.peek::<keyword::byte_array_size>() {
|
||||
parser.parse::<keyword::byte_array_size>()?;
|
||||
|
||||
Ok(Instruction::ByteArraySize)
|
||||
} else if lookahead.peek::<keyword::record_lift>() {
|
||||
parser.parse::<keyword::record_lift>()?;
|
||||
|
||||
@ -329,6 +351,18 @@ impl<'a> Parse<'a> for Instruction {
|
||||
Ok(Instruction::RecordLower {
|
||||
type_index: parser.parse()?,
|
||||
})
|
||||
} else if lookahead.peek::<keyword::record_lift_memory>() {
|
||||
parser.parse::<keyword::record_lift_memory>()?;
|
||||
|
||||
Ok(Instruction::RecordLiftMemory {
|
||||
type_index: parser.parse()?,
|
||||
})
|
||||
} else if lookahead.peek::<keyword::record_lower_memory>() {
|
||||
parser.parse::<keyword::record_lower_memory>()?;
|
||||
|
||||
Ok(Instruction::RecordLowerMemory {
|
||||
type_index: parser.parse()?,
|
||||
})
|
||||
} else if lookahead.peek::<keyword::dup>() {
|
||||
parser.parse::<keyword::dup>()?;
|
||||
|
||||
|
@ -105,6 +105,7 @@ where
|
||||
InterfaceType::F32 => 0x08_u8.to_bytes(writer),
|
||||
InterfaceType::F64 => 0x09_u8.to_bytes(writer),
|
||||
InterfaceType::String => 0x0a_u8.to_bytes(writer),
|
||||
InterfaceType::ByteArray => 0x36_u8.to_bytes(writer),
|
||||
InterfaceType::Anyref => 0x0b_u8.to_bytes(writer),
|
||||
InterfaceType::I32 => 0x0c_u8.to_bytes(writer),
|
||||
InterfaceType::I64 => 0x0d_u8.to_bytes(writer),
|
||||
@ -334,6 +335,10 @@ where
|
||||
Instruction::StringLowerMemory => 0x23_u8.to_bytes(writer)?,
|
||||
Instruction::StringSize => 0x24_u8.to_bytes(writer)?,
|
||||
|
||||
Instruction::ByteArrayLiftMemory => 0x37_u8.to_bytes(writer)?,
|
||||
Instruction::ByteArrayLowerMemory => 0x38_u8.to_bytes(writer)?,
|
||||
Instruction::ByteArraySize => 0x39_u8.to_bytes(writer)?,
|
||||
|
||||
Instruction::RecordLift { type_index } => {
|
||||
0x25_u8.to_bytes(writer)?;
|
||||
(*type_index as u64).to_bytes(writer)?
|
||||
@ -342,6 +347,14 @@ where
|
||||
0x26_u8.to_bytes(writer)?;
|
||||
(*type_index as u64).to_bytes(writer)?
|
||||
}
|
||||
Instruction::RecordLiftMemory { type_index } => {
|
||||
0x3A_u8.to_bytes(writer)?;
|
||||
(*type_index as u64).to_bytes(writer)?
|
||||
}
|
||||
Instruction::RecordLowerMemory { type_index } => {
|
||||
0x3B_u8.to_bytes(writer)?;
|
||||
(*type_index as u64).to_bytes(writer)?
|
||||
}
|
||||
Instruction::Dup => 0x34_u8.to_bytes(writer)?,
|
||||
Instruction::Swap2 => 0x35_u8.to_bytes(writer)?,
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ impl ToString for &InterfaceType {
|
||||
InterfaceType::F32 => "f32".to_string(),
|
||||
InterfaceType::F64 => "f64".to_string(),
|
||||
InterfaceType::String => "string".to_string(),
|
||||
InterfaceType::ByteArray => "byte_array".to_string(),
|
||||
InterfaceType::Anyref => "anyref".to_string(),
|
||||
InterfaceType::I32 => "i32".to_string(),
|
||||
InterfaceType::I64 => "i64".to_string(),
|
||||
@ -138,8 +139,17 @@ impl ToString for &Instruction {
|
||||
Instruction::StringLiftMemory => "string.lift_memory".into(),
|
||||
Instruction::StringLowerMemory => "string.lower_memory".into(),
|
||||
Instruction::StringSize => "string.size".into(),
|
||||
Instruction::ByteArrayLiftMemory => "byte_array.lift_memory".into(),
|
||||
Instruction::ByteArrayLowerMemory => "byte_array.lower_memory".into(),
|
||||
Instruction::ByteArraySize => "byte_array.size".into(),
|
||||
Instruction::RecordLift { type_index } => format!("record.lift {}", type_index),
|
||||
Instruction::RecordLower { type_index } => format!("record.lower {}", type_index),
|
||||
Instruction::RecordLiftMemory { type_index } => {
|
||||
format!("record.lift_memory {}", type_index)
|
||||
}
|
||||
Instruction::RecordLowerMemory { type_index } => {
|
||||
format!("record.lower_memory {}", type_index)
|
||||
}
|
||||
Instruction::Dup => "dup".into(),
|
||||
Instruction::Swap2 => "swap2".into(),
|
||||
}
|
||||
|
@ -171,6 +171,9 @@ pub enum InstructionErrorKind {
|
||||
/// The received kind.
|
||||
received_kind: TypeKind,
|
||||
},
|
||||
|
||||
/// Errors related to Serialization/deserialization of record.
|
||||
SerdeError(String),
|
||||
}
|
||||
|
||||
impl Error for InstructionErrorKind {}
|
||||
@ -258,6 +261,10 @@ impl Display for InstructionErrorKind {
|
||||
"read a type of kind `{:?}`, but the kind `{:?}` was expected",
|
||||
received_kind, expected_kind
|
||||
),
|
||||
Self::SerdeError(err) => write!(
|
||||
formatter,
|
||||
"serde error: {}", err,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
141
src/interpreter/instructions/byte_arrays.rs
Normal file
141
src/interpreter/instructions/byte_arrays.rs
Normal file
@ -0,0 +1,141 @@
|
||||
use super::to_native;
|
||||
use crate::{
|
||||
errors::{InstructionError, InstructionErrorKind},
|
||||
interpreter::Instruction,
|
||||
types::InterfaceType,
|
||||
values::InterfaceValue,
|
||||
};
|
||||
use std::{cell::Cell, convert::TryInto};
|
||||
|
||||
executable_instruction!(
|
||||
byte_array_lift_memory(instruction: Instruction) -> _ {
|
||||
move |runtime| -> _ {
|
||||
let inputs = runtime.stack.pop(2).ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::StackIsTooSmall { needed: 2 },
|
||||
)
|
||||
})?;
|
||||
|
||||
let memory_index: u32 = 0;
|
||||
let memory = runtime
|
||||
.wasm_instance
|
||||
.memory(memory_index as usize)
|
||||
.ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::MemoryIsMissing { memory_index },
|
||||
)
|
||||
})?;
|
||||
|
||||
let pointer: usize = to_native::<i32>(&inputs[0], instruction)?
|
||||
.try_into()
|
||||
.map_err(|e| (e, "pointer").into())
|
||||
.map_err(|k| InstructionError::new(instruction, k))?;
|
||||
let length: usize = to_native::<i32>(&inputs[1], instruction)?
|
||||
.try_into()
|
||||
.map_err(|e| (e, "length").into())
|
||||
.map_err(|k| InstructionError::new(instruction, k))?;
|
||||
let memory_view = memory.view();
|
||||
|
||||
if length == 0 {
|
||||
runtime.stack.push(InterfaceValue::ByteArray(vec![]));
|
||||
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
if memory_view.len() < pointer + length {
|
||||
return Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::MemoryOutOfBoundsAccess {
|
||||
index: pointer + length,
|
||||
length: memory_view.len(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
let data: Vec<u8> = (&memory_view[pointer..pointer + length])
|
||||
.iter()
|
||||
.map(Cell::get)
|
||||
.collect();
|
||||
|
||||
runtime.stack.push(InterfaceValue::ByteArray(data));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
executable_instruction!(
|
||||
byte_array_lower_memory(instruction: Instruction) -> _ {
|
||||
move |runtime| -> _ {
|
||||
let inputs = runtime.stack.pop(2).ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::StackIsTooSmall { needed: 2 },
|
||||
)
|
||||
})?;
|
||||
|
||||
let byte_array_pointer: usize = to_native::<i32>(&inputs[0], instruction)?
|
||||
.try_into()
|
||||
.map_err(|e| (e, "pointer").into())
|
||||
.map_err(|k| InstructionError::new(instruction, k))?;
|
||||
let byte_array: Vec<u8> = to_native(&inputs[1], instruction)?;
|
||||
let byte_array_length: i32 = byte_array.len().try_into().map_err(|_| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::NegativeValue { subject: "string_length" },
|
||||
)
|
||||
})?;
|
||||
|
||||
let instance = &mut runtime.wasm_instance;
|
||||
let memory_index: u32 = 0;
|
||||
let memory_view = instance
|
||||
.memory(memory_index as usize)
|
||||
.ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::MemoryIsMissing { memory_index },
|
||||
)
|
||||
})?
|
||||
.view();
|
||||
|
||||
for (nth, byte) in byte_array.iter().enumerate() {
|
||||
memory_view[byte_array_pointer as usize + nth].set(*byte);
|
||||
}
|
||||
|
||||
runtime.stack.push(InterfaceValue::I32(byte_array_pointer as i32));
|
||||
runtime.stack.push(InterfaceValue::I32(byte_array_length));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
executable_instruction!(
|
||||
byte_array_size(instruction: Instruction) -> _ {
|
||||
move |runtime| -> _ {
|
||||
match runtime.stack.pop1() {
|
||||
Some(InterfaceValue::ByteArray(byte_array)) => {
|
||||
let length = byte_array.len() as i32;
|
||||
runtime.stack.push(InterfaceValue::I32(length));
|
||||
|
||||
Ok(())
|
||||
},
|
||||
|
||||
Some(value) => Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::InvalidValueOnTheStack {
|
||||
expected_type: InterfaceType::ByteArray,
|
||||
received_type: (&value).into(),
|
||||
},
|
||||
)),
|
||||
|
||||
None => Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::StackIsTooSmall { needed: 1 },
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
@ -15,7 +15,7 @@ executable_instruction!(
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::LocalOrImportIsMissing {
|
||||
function_index: function_index,
|
||||
function_index,
|
||||
},
|
||||
)
|
||||
})?;
|
||||
@ -38,7 +38,7 @@ executable_instruction!(
|
||||
return Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::LocalOrImportSignatureMismatch {
|
||||
function_index: function_index,
|
||||
function_index,
|
||||
expected: (local_or_import.inputs().to_vec(), vec![]),
|
||||
received: (input_types, vec![]),
|
||||
},
|
||||
@ -49,7 +49,7 @@ executable_instruction!(
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::LocalOrImportCall {
|
||||
function_index: function_index,
|
||||
function_index,
|
||||
},
|
||||
)
|
||||
})?;
|
||||
|
@ -1,9 +1,10 @@
|
||||
mod argument_get;
|
||||
mod byte_arrays;
|
||||
mod call_core;
|
||||
mod dup;
|
||||
mod numbers;
|
||||
mod records;
|
||||
mod strings;
|
||||
mod dup;
|
||||
mod swap2;
|
||||
|
||||
use crate::{
|
||||
@ -11,14 +12,18 @@ use crate::{
|
||||
values::{InterfaceValue, NativeType},
|
||||
};
|
||||
pub(crate) use argument_get::argument_get;
|
||||
pub(crate) use byte_arrays::*;
|
||||
pub(crate) use call_core::call_core;
|
||||
pub(crate) use dup::dup;
|
||||
pub(crate) use numbers::*;
|
||||
pub(crate) use records::*;
|
||||
use std::convert::TryFrom;
|
||||
pub(crate) use strings::*;
|
||||
pub(crate) use dup::dup;
|
||||
pub(crate) use swap2::swap2;
|
||||
|
||||
pub(self) const ALLOCATE_FUNC_INDEX: u32 = 0;
|
||||
pub(self) const DEALLOCATE_FUNC_INDEX: u32 = 1;
|
||||
|
||||
/// Represents all the possible WIT instructions.
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum Instruction {
|
||||
@ -139,6 +144,15 @@ pub enum Instruction {
|
||||
/// The `string.size` instruction.
|
||||
StringSize,
|
||||
|
||||
/// The `byte_array.lift_memory` instruction.
|
||||
ByteArrayLiftMemory,
|
||||
|
||||
/// The `byte_array.lower_memory` instruction.
|
||||
ByteArrayLowerMemory,
|
||||
|
||||
/// The `string.size` instruction.
|
||||
ByteArraySize,
|
||||
|
||||
/// The `record.lift` instruction.
|
||||
RecordLift {
|
||||
/// The type index of the record.
|
||||
@ -151,6 +165,18 @@ pub enum Instruction {
|
||||
type_index: u32,
|
||||
},
|
||||
|
||||
/// The `record.lift_memory` instruction.
|
||||
RecordLiftMemory {
|
||||
/// The type index of the record.
|
||||
type_index: u32,
|
||||
},
|
||||
|
||||
/// The `record.lower_memory` instruction.
|
||||
RecordLowerMemory {
|
||||
/// The type index of the record.
|
||||
type_index: u32,
|
||||
},
|
||||
|
||||
/// The `dup` instructions.
|
||||
Dup,
|
||||
|
||||
|
@ -1,3 +1,11 @@
|
||||
mod utils;
|
||||
|
||||
use utils::read_from_instance_mem;
|
||||
use utils::write_to_instance_mem;
|
||||
|
||||
// use crate::interpreter::wasm;
|
||||
|
||||
use crate::interpreter::instructions::to_native;
|
||||
use crate::{
|
||||
ast::{Type, TypeKind},
|
||||
errors::{InstructionError, InstructionErrorKind},
|
||||
@ -9,7 +17,9 @@ use crate::{
|
||||
values::{FlattenInterfaceValueIterator, InterfaceValue},
|
||||
vec1::Vec1,
|
||||
};
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::TryInto;
|
||||
|
||||
/// Build an `InterfaceValue::Record` based on values on the stack.
|
||||
///
|
||||
@ -30,41 +40,45 @@ fn record_lift_(
|
||||
) -> Result<InterfaceValue, InstructionErrorKind> {
|
||||
let length = record_type.fields.len();
|
||||
let mut values = VecDeque::with_capacity(length);
|
||||
|
||||
// Iterate over fields in reverse order to match the stack `pop`
|
||||
// order.
|
||||
for field in record_type.fields.iter().rev() {
|
||||
match field {
|
||||
// The record type tells a record is expected.
|
||||
InterfaceType::Record(record_type) => {
|
||||
// Build it recursively.
|
||||
values.push_front(record_lift_(stack, &record_type)?)
|
||||
}
|
||||
// Any other type.
|
||||
ty => {
|
||||
let value = stack.pop1().unwrap();
|
||||
let value_type = (&value).into();
|
||||
|
||||
if ty != &value_type {
|
||||
return Err(InstructionErrorKind::InvalidValueOnTheStack {
|
||||
expected_type: ty.clone(),
|
||||
received_type: value_type,
|
||||
});
|
||||
}
|
||||
|
||||
values.push_front(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(InterfaceValue::Record(
|
||||
Vec1::new(values.into_iter().collect())
|
||||
.expect("Record must have at least one field, zero given"), // normally unreachable because of the type-checking
|
||||
.expect("Record must have at least one field, zero given"),
|
||||
))
|
||||
}
|
||||
|
||||
executable_instruction!(
|
||||
record_lift(type_index: u32, instruction: Instruction) -> _ {
|
||||
pub(crate) fn record_lift<Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
type_index: u32,
|
||||
instruction: Instruction,
|
||||
) -> crate::interpreter::ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>
|
||||
where
|
||||
Export: crate::interpreter::wasm::structures::Export,
|
||||
LocalImport: crate::interpreter::wasm::structures::LocalImport,
|
||||
Memory: crate::interpreter::wasm::structures::Memory<MemoryView>,
|
||||
MemoryView: crate::interpreter::wasm::structures::MemoryView,
|
||||
Instance:
|
||||
crate::interpreter::wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
||||
{
|
||||
#[allow(unused_imports)]
|
||||
use crate::interpreter::stack::Stackable;
|
||||
Box::new({
|
||||
move |runtime| -> _ {
|
||||
let instance = &runtime.wasm_instance;
|
||||
let record_type = match instance.wit_type(type_index).ok_or_else(|| {
|
||||
@ -74,29 +88,302 @@ executable_instruction!(
|
||||
)
|
||||
})? {
|
||||
Type::Record(record_type) => record_type,
|
||||
Type::Function { .. } => return Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::InvalidTypeKind {
|
||||
expected_kind: TypeKind::Record,
|
||||
received_kind: TypeKind::Function
|
||||
}
|
||||
)),
|
||||
Type::Function { .. } => {
|
||||
return Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::InvalidTypeKind {
|
||||
expected_kind: TypeKind::Record,
|
||||
received_kind: TypeKind::Function,
|
||||
},
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let record = record_lift_(&mut runtime.stack, &record_type)
|
||||
.map_err(|k| InstructionError::new(instruction, k))?;
|
||||
runtime.stack.push(record);
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn record_lift_memory_<'instance, Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
instance: &'instance mut Instance,
|
||||
record_type: RecordType,
|
||||
offset: usize,
|
||||
instruction: Instruction,
|
||||
) -> Result<InterfaceValue, InstructionError>
|
||||
where
|
||||
Export: crate::interpreter::wasm::structures::Export,
|
||||
LocalImport: crate::interpreter::wasm::structures::LocalImport,
|
||||
Memory: crate::interpreter::wasm::structures::Memory<MemoryView>,
|
||||
MemoryView: crate::interpreter::wasm::structures::MemoryView,
|
||||
Instance: crate::interpreter::wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>
|
||||
+ 'instance,
|
||||
{
|
||||
fn record_size(record_type: &RecordType) -> usize {
|
||||
let mut record_size = 0;
|
||||
|
||||
for ty in record_type.fields.iter() {
|
||||
let params_count = match ty {
|
||||
InterfaceType::String | InterfaceType::ByteArray => 2,
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
record_size += std::mem::size_of::<u64>() * params_count;
|
||||
}
|
||||
|
||||
record_size
|
||||
}
|
||||
|
||||
let length = record_type.fields.len();
|
||||
let mut values = VecDeque::with_capacity(length);
|
||||
let size = record_size(&record_type);
|
||||
let data = read_from_instance_mem(instance, instruction, offset, size)?;
|
||||
// TODO: add error handling
|
||||
let data =
|
||||
safe_transmute::transmute_many::<u64, safe_transmute::SingleManyGuard>(&data).unwrap();
|
||||
|
||||
let mut field_id = 0;
|
||||
for field in record_type.fields.into_vec() {
|
||||
let value = data[field_id];
|
||||
match field {
|
||||
InterfaceType::S8 => {
|
||||
values.push_back(InterfaceValue::S8(value as _));
|
||||
}
|
||||
InterfaceType::S16 => {
|
||||
values.push_back(InterfaceValue::S16(value as _));
|
||||
}
|
||||
InterfaceType::S32 => {
|
||||
values.push_back(InterfaceValue::S32(value as _));
|
||||
}
|
||||
InterfaceType::S64 => {
|
||||
values.push_back(InterfaceValue::S64(value as _));
|
||||
}
|
||||
InterfaceType::I32 => {
|
||||
values.push_back(InterfaceValue::I32(value as _));
|
||||
}
|
||||
InterfaceType::I64 => {
|
||||
values.push_back(InterfaceValue::I64(value as _));
|
||||
}
|
||||
InterfaceType::U8 => {
|
||||
values.push_back(InterfaceValue::U8(value as _));
|
||||
}
|
||||
InterfaceType::U16 => {
|
||||
values.push_back(InterfaceValue::U16(value as _));
|
||||
}
|
||||
InterfaceType::U32 => {
|
||||
values.push_back(InterfaceValue::U32(value as _));
|
||||
}
|
||||
InterfaceType::U64 => {
|
||||
values.push_back(InterfaceValue::U64(value as _));
|
||||
}
|
||||
InterfaceType::F32 => {
|
||||
values.push_back(InterfaceValue::F32(value as _));
|
||||
}
|
||||
InterfaceType::F64 => values.push_back(InterfaceValue::F64(f64::from_bits(value))),
|
||||
InterfaceType::Anyref => {}
|
||||
InterfaceType::String => {
|
||||
let string_offset = value;
|
||||
field_id += 1;
|
||||
let string_size = data[field_id];
|
||||
|
||||
if string_size != 0 {
|
||||
let string_mem = read_from_instance_mem(
|
||||
instance,
|
||||
instruction,
|
||||
string_offset as _,
|
||||
string_size as _,
|
||||
)?;
|
||||
|
||||
// TODO: check
|
||||
let string = String::from_utf8(string_mem).unwrap();
|
||||
values.push_back(InterfaceValue::String(string));
|
||||
|
||||
utils::deallocate(instance, instruction, string_offset as _, string_size as _)?;
|
||||
} else {
|
||||
values.push_back(InterfaceValue::String("".to_string()));
|
||||
}
|
||||
}
|
||||
InterfaceType::ByteArray => {
|
||||
let array_offset = value;
|
||||
field_id += 1;
|
||||
let array_size = data[field_id];
|
||||
|
||||
if array_size != 0 {
|
||||
let byte_array = read_from_instance_mem(
|
||||
instance,
|
||||
instruction,
|
||||
array_offset as _,
|
||||
array_size as _,
|
||||
)?;
|
||||
|
||||
values.push_back(InterfaceValue::ByteArray(byte_array));
|
||||
|
||||
utils::deallocate(instance, instruction, array_offset as _, array_size as _)?;
|
||||
} else {
|
||||
values.push_back(InterfaceValue::ByteArray(vec![]));
|
||||
}
|
||||
}
|
||||
InterfaceType::Record(record_type) => {
|
||||
let offset = value;
|
||||
|
||||
values.push_back(record_lift_memory_(
|
||||
instance,
|
||||
record_type,
|
||||
offset as _,
|
||||
instruction,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
field_id += 1;
|
||||
}
|
||||
|
||||
utils::deallocate(instance, instruction, offset as _, size as _)?;
|
||||
|
||||
Ok(InterfaceValue::Record(
|
||||
Vec1::new(values.into_iter().collect())
|
||||
.expect("Record must have at least one field, zero given"),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn record_lift_memory<Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
type_index: u32,
|
||||
instruction: Instruction,
|
||||
) -> crate::interpreter::ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>
|
||||
where
|
||||
Export: crate::interpreter::wasm::structures::Export,
|
||||
LocalImport: crate::interpreter::wasm::structures::LocalImport,
|
||||
Memory: crate::interpreter::wasm::structures::Memory<MemoryView>,
|
||||
MemoryView: crate::interpreter::wasm::structures::MemoryView,
|
||||
Instance:
|
||||
crate::interpreter::wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
||||
{
|
||||
#[allow(unused_imports)]
|
||||
use crate::interpreter::stack::Stackable;
|
||||
Box::new({
|
||||
move |runtime| -> _ {
|
||||
let inputs = runtime.stack.pop(1).ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::StackIsTooSmall { needed: 1 },
|
||||
)
|
||||
})?;
|
||||
|
||||
let offset: usize = to_native::<i32>(&inputs[0], instruction)?
|
||||
.try_into()
|
||||
.map_err(|e| (e, "offset").into())
|
||||
.map_err(|k| InstructionError::new(instruction, k))?;
|
||||
|
||||
// TODO: size = 0
|
||||
let instance = &mut runtime.wasm_instance;
|
||||
let record_type = match instance.wit_type(type_index).ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::TypeIsMissing { type_index },
|
||||
)
|
||||
})? {
|
||||
Type::Record(record_type) => record_type.clone(),
|
||||
Type::Function { .. } => {
|
||||
return Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::InvalidTypeKind {
|
||||
expected_kind: TypeKind::Record,
|
||||
received_kind: TypeKind::Function,
|
||||
},
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let record = record_lift_memory_(*instance, record_type, offset, instruction)?;
|
||||
runtime.stack.push(record);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
executable_instruction!(
|
||||
record_lower(type_index: u32, instruction: Instruction) -> _ {
|
||||
fn record_lower_memory_<Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
instance: &mut Instance,
|
||||
instruction: Instruction,
|
||||
values: Vec1<InterfaceValue>,
|
||||
) -> Result<i32, InstructionError>
|
||||
where
|
||||
Export: crate::interpreter::wasm::structures::Export,
|
||||
LocalImport: crate::interpreter::wasm::structures::LocalImport,
|
||||
Memory: crate::interpreter::wasm::structures::Memory<MemoryView>,
|
||||
MemoryView: crate::interpreter::wasm::structures::MemoryView,
|
||||
Instance:
|
||||
crate::interpreter::wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
||||
{
|
||||
let mut result: Vec<u64> = Vec::with_capacity(values.len());
|
||||
|
||||
for value in values.into_vec() {
|
||||
match value {
|
||||
InterfaceValue::S8(value) => result.push(value as _),
|
||||
InterfaceValue::S16(value) => result.push(value as _),
|
||||
InterfaceValue::S32(value) => result.push(value as _),
|
||||
InterfaceValue::S64(value) => result.push(value as _),
|
||||
InterfaceValue::U8(value) => result.push(value as _),
|
||||
InterfaceValue::U16(value) => result.push(value as _),
|
||||
InterfaceValue::U32(value) => result.push(value as _),
|
||||
InterfaceValue::U64(value) => result.push(value as _),
|
||||
InterfaceValue::I32(value) => result.push(value as _),
|
||||
InterfaceValue::I64(value) => result.push(value as _),
|
||||
InterfaceValue::F32(value) => result.push(value as _),
|
||||
InterfaceValue::F64(value) => result.push(value.to_bits()),
|
||||
InterfaceValue::String(value) => {
|
||||
let string_pointer = if !value.is_empty() {
|
||||
write_to_instance_mem(instance, instruction, value.as_bytes())?
|
||||
} else {
|
||||
0i32
|
||||
};
|
||||
|
||||
result.push(string_pointer as _);
|
||||
result.push(value.len() as _);
|
||||
}
|
||||
|
||||
InterfaceValue::ByteArray(value) => {
|
||||
let byte_array_pointer = if !value.is_empty() {
|
||||
write_to_instance_mem(instance, instruction, &value)?
|
||||
} else {
|
||||
0i32
|
||||
};
|
||||
|
||||
result.push(byte_array_pointer as _);
|
||||
result.push(value.len() as _);
|
||||
}
|
||||
|
||||
InterfaceValue::Record(record) => {
|
||||
let record_ptr = record_lower_memory_(instance, instruction, record)?;
|
||||
|
||||
result.push(record_ptr as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = safe_transmute::transmute_to_bytes::<u64>(&result);
|
||||
let result_pointer = write_to_instance_mem(instance, instruction, &result)?;
|
||||
|
||||
Ok(result_pointer)
|
||||
}
|
||||
|
||||
pub(crate) fn record_lower_memory<Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
type_index: u32,
|
||||
instruction: Instruction,
|
||||
) -> crate::interpreter::ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>
|
||||
where
|
||||
Export: crate::interpreter::wasm::structures::Export,
|
||||
LocalImport: crate::interpreter::wasm::structures::LocalImport,
|
||||
Memory: crate::interpreter::wasm::structures::Memory<MemoryView>,
|
||||
MemoryView: crate::interpreter::wasm::structures::MemoryView,
|
||||
Instance:
|
||||
crate::interpreter::wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
||||
{
|
||||
#[allow(unused_imports)]
|
||||
use crate::interpreter::stack::Stackable;
|
||||
Box::new({
|
||||
move |runtime| -> _ {
|
||||
let instance = &runtime.wasm_instance;
|
||||
let instance = &mut runtime.wasm_instance;
|
||||
let record_type = match instance.wit_type(type_index).ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
@ -104,281 +391,110 @@ executable_instruction!(
|
||||
)
|
||||
})? {
|
||||
Type::Record(record_type) => record_type,
|
||||
Type::Function { .. } => return Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::InvalidTypeKind {
|
||||
expected_kind: TypeKind::Record,
|
||||
received_kind: TypeKind::Function
|
||||
}
|
||||
)),
|
||||
Type::Function { .. } => {
|
||||
return Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::InvalidTypeKind {
|
||||
expected_kind: TypeKind::Record,
|
||||
received_kind: TypeKind::Function,
|
||||
},
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
match runtime.stack.pop1() {
|
||||
Some(InterfaceValue::Record(record_values)) if record_type == &(&*record_values).into() => {
|
||||
let values = FlattenInterfaceValueIterator::new(&record_values);
|
||||
Some(InterfaceValue::Record(record_values))
|
||||
if record_type == &(&*record_values).into() =>
|
||||
{
|
||||
/*
|
||||
let value: Vec<u8> = crate::serde::de::from_interface_values(&record_values)
|
||||
.map_err(|e| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::SerdeError(e.to_string()),
|
||||
)
|
||||
})?;
|
||||
|
||||
for value in values {
|
||||
runtime.stack.push(value.clone());
|
||||
}
|
||||
let value_pointer = write_to_instance_mem(*instance, instruction, &value)?;
|
||||
runtime.stack.push(InterfaceValue::I32(value_pointer));
|
||||
runtime.stack.push(InterfaceValue::I32(value.len() as _));
|
||||
*/
|
||||
let offset = record_lower_memory_(*instance, instruction, record_values)?;
|
||||
runtime.stack.push(InterfaceValue::I32(offset));
|
||||
|
||||
Ok(())
|
||||
},
|
||||
|
||||
}
|
||||
Some(value) => Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::InvalidValueOnTheStack {
|
||||
expected_type: InterfaceType::Record(record_type.clone()),
|
||||
received_type: (&value).into(),
|
||||
}
|
||||
},
|
||||
)),
|
||||
|
||||
None => Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::StackIsTooSmall { needed: 1 },
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
test_executable_instruction!(
|
||||
test_record_lift =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 2 },
|
||||
Instruction::ArgumentGet { index: 3 },
|
||||
Instruction::RecordLift { type_index: 0 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::String("Hello".to_string()),
|
||||
InterfaceValue::F32(2.),
|
||||
InterfaceValue::I64(3),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
stack: [InterfaceValue::Record(vec1![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::Record(vec1![
|
||||
InterfaceValue::String("Hello".to_string()),
|
||||
InterfaceValue::F32(2.),
|
||||
]),
|
||||
InterfaceValue::I64(3),
|
||||
])],
|
||||
);
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
#[allow(non_snake_case, unused)]
|
||||
fn test_record_lift__to_rust_struct() {
|
||||
use crate::{
|
||||
interpreter::{
|
||||
instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView},
|
||||
stack::Stackable,
|
||||
Instruction, Interpreter,
|
||||
},
|
||||
types::InterfaceType,
|
||||
values::{from_interface_values, InterfaceValue},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::{cell::Cell, collections::HashMap, convert::TryInto};
|
||||
|
||||
let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> = (&vec![
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 2 },
|
||||
Instruction::ArgumentGet { index: 3 },
|
||||
Instruction::RecordLift { type_index: 0 },
|
||||
])
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
let invocation_inputs = vec![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::String("Hello".to_string()),
|
||||
InterfaceValue::F32(2.),
|
||||
InterfaceValue::I64(3),
|
||||
];
|
||||
let mut instance = Instance::new();
|
||||
let run = interpreter.run(&invocation_inputs, &mut instance);
|
||||
|
||||
assert!(run.is_ok());
|
||||
|
||||
let stack = run.unwrap();
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
struct S {
|
||||
a: String,
|
||||
b: f32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
struct T {
|
||||
x: i32,
|
||||
s: S,
|
||||
y: i64,
|
||||
}
|
||||
|
||||
let record: T = from_interface_values(stack.as_slice()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
record,
|
||||
T {
|
||||
x: 1,
|
||||
s: S {
|
||||
a: "Hello".to_string(),
|
||||
b: 2.,
|
||||
},
|
||||
y: 3,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
test_executable_instruction!(
|
||||
test_record_lift__one_dimension =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::RecordLift { type_index: 1 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::I32(2),
|
||||
],
|
||||
instance: {
|
||||
let mut instance = Instance::new();
|
||||
instance.wit_types.push(
|
||||
Type::Record(RecordType {
|
||||
fields: vec1![InterfaceType::I32, InterfaceType::I32],
|
||||
})
|
||||
);
|
||||
|
||||
instance
|
||||
},
|
||||
stack: [InterfaceValue::Record(vec1![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::I32(2),
|
||||
])],
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_record_lift__type_is_missing =
|
||||
instructions: [
|
||||
Instruction::RecordLift { type_index: 0 },
|
||||
],
|
||||
invocation_inputs: [],
|
||||
instance: Default::default(),
|
||||
error: r#"`record.lift 0` the type `0` doesn't exist"#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_record_lift__invalid_value_on_the_stack =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 2 },
|
||||
Instruction::ArgumentGet { index: 3 },
|
||||
Instruction::RecordLift { type_index: 0 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::String("Hello".to_string()),
|
||||
InterfaceValue::F64(2.),
|
||||
// ^^^ F32 is expected
|
||||
InterfaceValue::I64(3),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`record.lift 0` read a value of type `F64` from the stack, but the type `F32` was expected"#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_record_lower =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::RecordLower { type_index: 0 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::Record(vec1![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::Record(vec1![
|
||||
InterfaceValue::String("Hello".to_string()),
|
||||
InterfaceValue::F32(2.),
|
||||
]),
|
||||
InterfaceValue::I64(3),
|
||||
])
|
||||
],
|
||||
instance: Instance::new(),
|
||||
stack: [
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::String("Hello".to_string()),
|
||||
InterfaceValue::F32(2.),
|
||||
InterfaceValue::I64(3),
|
||||
],
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_record__roundtrip =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::RecordLower { type_index: 0 },
|
||||
Instruction::RecordLift { type_index: 0 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::Record(vec1![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::Record(vec1![
|
||||
InterfaceValue::String("Hello".to_string()),
|
||||
InterfaceValue::F32(2.),
|
||||
]),
|
||||
InterfaceValue::I64(3),
|
||||
])
|
||||
],
|
||||
instance: Instance::new(),
|
||||
stack: [
|
||||
InterfaceValue::Record(vec1![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::Record(vec1![
|
||||
InterfaceValue::String("Hello".to_string()),
|
||||
InterfaceValue::F32(2.),
|
||||
]),
|
||||
InterfaceValue::I64(3),
|
||||
])
|
||||
],
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_record_lower__invalid_value_on_the_stack =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::RecordLower { type_index: 0 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(1),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`record.lower 0` read a value of type `I32` from the stack, but the type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String, F32] }), I64] })` was expected"#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_record_lower__invalid_value_on_the_stack__different_record_type =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::RecordLower { type_index: 0 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::Record(vec1![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::Record(vec1![
|
||||
InterfaceValue::String("Hello".to_string()),
|
||||
]),
|
||||
InterfaceValue::I64(3),
|
||||
])
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`record.lower 0` read a value of type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String] }), I64] })` from the stack, but the type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String, F32] }), I64] })` was expected"#,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn record_lower<Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
type_index: u32,
|
||||
instruction: Instruction,
|
||||
) -> crate::interpreter::ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>
|
||||
where
|
||||
Export: crate::interpreter::wasm::structures::Export,
|
||||
LocalImport: crate::interpreter::wasm::structures::LocalImport,
|
||||
Memory: crate::interpreter::wasm::structures::Memory<MemoryView>,
|
||||
MemoryView: crate::interpreter::wasm::structures::MemoryView,
|
||||
Instance:
|
||||
crate::interpreter::wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
|
||||
{
|
||||
#[allow(unused_imports)]
|
||||
use crate::interpreter::stack::Stackable;
|
||||
Box::new({
|
||||
move |runtime| -> _ {
|
||||
let instance = &runtime.wasm_instance;
|
||||
let record_type = match instance.wit_type(type_index).ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::TypeIsMissing { type_index },
|
||||
)
|
||||
})? {
|
||||
Type::Record(record_type) => record_type,
|
||||
Type::Function { .. } => {
|
||||
return Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::InvalidTypeKind {
|
||||
expected_kind: TypeKind::Record,
|
||||
received_kind: TypeKind::Function,
|
||||
},
|
||||
))
|
||||
}
|
||||
};
|
||||
match runtime.stack.pop1() {
|
||||
Some(InterfaceValue::Record(record_values))
|
||||
if record_type == &(&*record_values).into() =>
|
||||
{
|
||||
let values = FlattenInterfaceValueIterator::new(&record_values);
|
||||
for value in values {
|
||||
runtime.stack.push(value.clone());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Some(value) => Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::InvalidValueOnTheStack {
|
||||
expected_type: InterfaceType::Record(record_type.clone()),
|
||||
received_type: (&value).into(),
|
||||
},
|
||||
)),
|
||||
None => Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::StackIsTooSmall { needed: 1 },
|
||||
)),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
172
src/interpreter/instructions/records/utils.rs
Normal file
172
src/interpreter/instructions/records/utils.rs
Normal file
@ -0,0 +1,172 @@
|
||||
use crate::interpreter::instructions::ALLOCATE_FUNC_INDEX;
|
||||
use crate::interpreter::instructions::DEALLOCATE_FUNC_INDEX;
|
||||
use crate::interpreter::wasm;
|
||||
use crate::interpreter::wasm::structures::{FunctionIndex, TypedIndex};
|
||||
|
||||
use crate::interpreter::instructions::to_native;
|
||||
use crate::{
|
||||
errors::{InstructionError, InstructionErrorKind},
|
||||
interpreter::Instruction,
|
||||
types::InterfaceType,
|
||||
values::InterfaceValue,
|
||||
};
|
||||
|
||||
pub(super) fn read_from_instance_mem<'instance, Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
instance: &'instance Instance,
|
||||
instruction: Instruction,
|
||||
offset: usize,
|
||||
size: usize,
|
||||
) -> Result<Vec<u8>, InstructionError>
|
||||
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 memory_index: u32 = 0;
|
||||
let memory_view = instance
|
||||
.memory(memory_index as usize)
|
||||
.ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::MemoryIsMissing { memory_index },
|
||||
)
|
||||
})?
|
||||
.view();
|
||||
|
||||
Ok((&memory_view[offset..offset + size])
|
||||
.iter()
|
||||
.map(std::cell::Cell::get)
|
||||
.collect::<Vec<u8>>())
|
||||
}
|
||||
|
||||
pub(super) fn write_to_instance_mem<'instance, Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
instance: &'instance mut Instance,
|
||||
instruction: Instruction,
|
||||
bytes: &[u8],
|
||||
) -> Result<i32, InstructionError>
|
||||
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 mem_pointer = allocate(instance, instruction, bytes.len() as _)?;
|
||||
|
||||
let memory_index: u32 = 0;
|
||||
let memory_view = instance
|
||||
.memory(memory_index as usize)
|
||||
.ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::MemoryIsMissing { memory_index },
|
||||
)
|
||||
})?
|
||||
.view();
|
||||
|
||||
for (byte_id, byte) in bytes.iter().enumerate() {
|
||||
memory_view[mem_pointer as usize + byte_id].set(*byte);
|
||||
}
|
||||
|
||||
Ok(mem_pointer)
|
||||
}
|
||||
|
||||
pub(super) fn allocate<'instance, Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
instance: &'instance mut Instance,
|
||||
instruction: Instruction,
|
||||
size: i32,
|
||||
) -> Result<i32, InstructionError>
|
||||
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 values = call_core(
|
||||
instance,
|
||||
ALLOCATE_FUNC_INDEX,
|
||||
instruction,
|
||||
vec![InterfaceValue::I32(size)],
|
||||
)?;
|
||||
if values.len() != 1 {
|
||||
return Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::LocalOrImportSignatureMismatch {
|
||||
function_index: ALLOCATE_FUNC_INDEX,
|
||||
expected: (vec![InterfaceType::I32], vec![]),
|
||||
received: (vec![], vec![]),
|
||||
},
|
||||
));
|
||||
}
|
||||
to_native::<i32>(&values[0], instruction)
|
||||
}
|
||||
|
||||
pub(super) fn deallocate<'instance, Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
instance: &'instance mut Instance,
|
||||
instruction: Instruction,
|
||||
mem_ptr: i32,
|
||||
size: i32,
|
||||
) -> Result<(), InstructionError>
|
||||
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 _ = call_core(
|
||||
instance,
|
||||
DEALLOCATE_FUNC_INDEX,
|
||||
instruction,
|
||||
vec![InterfaceValue::I32(mem_ptr), InterfaceValue::I32(size)],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn call_core<'instance, Instance, Export, LocalImport, Memory, MemoryView>(
|
||||
instance: &'instance mut Instance,
|
||||
function_index: u32,
|
||||
instruction: Instruction,
|
||||
inputs: Vec<InterfaceValue>,
|
||||
) -> Result<Vec<InterfaceValue>, InstructionError>
|
||||
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 index = FunctionIndex::new(function_index as usize);
|
||||
let local_or_import = instance.local_or_import(index).ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::LocalOrImportIsMissing { function_index },
|
||||
)
|
||||
})?;
|
||||
let input_types = inputs
|
||||
.iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<InterfaceType>>();
|
||||
if input_types != local_or_import.inputs() {
|
||||
return Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::LocalOrImportSignatureMismatch {
|
||||
function_index,
|
||||
expected: (local_or_import.inputs().to_vec(), vec![]),
|
||||
received: (input_types, vec![]),
|
||||
},
|
||||
));
|
||||
}
|
||||
let outputs = local_or_import.call(&inputs).map_err(|_| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::LocalOrImportCall { function_index },
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(outputs)
|
||||
}
|
@ -40,9 +40,8 @@ where
|
||||
/// Type alias for an executable instruction. It's an implementation
|
||||
/// details, but an instruction is a boxed closure instance.
|
||||
pub(crate) type ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView> = Box<
|
||||
dyn Fn(
|
||||
&mut Runtime<Instance, Export, LocalImport, Memory, MemoryView>,
|
||||
) -> InstructionResult<()> + Send,
|
||||
dyn Fn(&mut Runtime<Instance, Export, LocalImport, Memory, MemoryView>) -> InstructionResult<()>
|
||||
+ Send,
|
||||
>;
|
||||
|
||||
/// An interpreter is the central piece of this crate. It is a set of
|
||||
@ -238,12 +237,27 @@ where
|
||||
Instruction::StringLowerMemory => instructions::string_lower_memory(*instruction),
|
||||
Instruction::StringSize => instructions::string_size(*instruction),
|
||||
|
||||
Instruction::ByteArrayLiftMemory => {
|
||||
instructions::byte_array_lift_memory(*instruction)
|
||||
}
|
||||
Instruction::ByteArrayLowerMemory => {
|
||||
instructions::byte_array_lower_memory(*instruction)
|
||||
}
|
||||
Instruction::ByteArraySize => instructions::byte_array_size(*instruction),
|
||||
|
||||
Instruction::RecordLift { type_index } => {
|
||||
instructions::record_lift(*type_index, *instruction)
|
||||
}
|
||||
Instruction::RecordLower { type_index } => {
|
||||
instructions::record_lower(*type_index, *instruction)
|
||||
},
|
||||
}
|
||||
|
||||
Instruction::RecordLiftMemory { type_index } => {
|
||||
instructions::record_lift_memory(*type_index, *instruction)
|
||||
}
|
||||
Instruction::RecordLowerMemory { type_index } => {
|
||||
instructions::record_lower_memory(*type_index, *instruction)
|
||||
}
|
||||
Instruction::Dup => instructions::dup(*instruction),
|
||||
Instruction::Swap2 => instructions::swap2(*instruction),
|
||||
})
|
||||
|
@ -39,7 +39,7 @@
|
||||
//! [instructions]: interpreter::Instruction
|
||||
|
||||
#![deny(
|
||||
dead_code,
|
||||
// dead_code,
|
||||
intra_doc_link_resolution_failure,
|
||||
missing_docs,
|
||||
nonstandard_style,
|
||||
@ -49,7 +49,7 @@
|
||||
unused_unsafe,
|
||||
unused_variables
|
||||
)]
|
||||
#![forbid(unsafe_code)]
|
||||
// #![forbid(unsafe_code)]
|
||||
#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
|
||||
#![doc(html_logo_url = "https://github.com/wasmerio.png")]
|
||||
|
||||
|
@ -97,7 +97,7 @@ macro_rules! next {
|
||||
None => Err(DeserializeError::InputEmpty),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<'de> Deserializer<'de> {
|
||||
@ -129,6 +129,23 @@ impl<'de> Deserializer<'de> {
|
||||
}
|
||||
}
|
||||
|
||||
fn next_byte_array(&mut self) -> Result<&'de [u8], DeserializeError> {
|
||||
match self.iterator.peek() {
|
||||
Some(InterfaceValue::ByteArray(v)) => {
|
||||
self.iterator.next();
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
Some(wrong_value) => Err(DeserializeError::TypeMismatch {
|
||||
expected_type: InterfaceType::ByteArray,
|
||||
received_type: (*wrong_value).into(),
|
||||
}),
|
||||
|
||||
None => Err(DeserializeError::InputEmpty),
|
||||
}
|
||||
}
|
||||
|
||||
next!(next_i32, I32, i32);
|
||||
next!(next_i64, I64, i64);
|
||||
}
|
||||
@ -200,6 +217,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
||||
Some(InterfaceValue::F32(_)) => self.deserialize_f32(visitor),
|
||||
Some(InterfaceValue::F64(_)) => self.deserialize_f64(visitor),
|
||||
Some(InterfaceValue::String(_)) => self.deserialize_string(visitor),
|
||||
Some(InterfaceValue::ByteArray(_)) => self.deserialize_bytes(visitor),
|
||||
Some(InterfaceValue::I32(_)) => self.deserialize_i32(visitor),
|
||||
Some(InterfaceValue::I64(_)) => self.deserialize_i64(visitor),
|
||||
Some(InterfaceValue::Record(_)) => unreachable!("Records should have been flattened."), // already flattened
|
||||
@ -309,11 +327,11 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
||||
self.deserialize_str(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`bytes` is not supported by WIT for the moment.")
|
||||
visitor.visit_bytes(self.next_byte_array()?)
|
||||
}
|
||||
|
||||
fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
|
@ -423,17 +423,21 @@ impl<'a> ser::SerializeTupleVariant for &'a mut Serializer {
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a> ser::SerializeMap for &'a mut Serializer {
|
||||
impl<'a> ser::SerializeMap for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = SerializeError;
|
||||
|
||||
fn serialize_key<T: ?Sized>(&mut self, _key: &T) -> Result<(), Self::Error> where
|
||||
T: Serialize {
|
||||
fn serialize_key<T: ?Sized>(&mut self, _key: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error> where
|
||||
T: Serialize {
|
||||
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! This module defines the WIT types.
|
||||
|
||||
use crate::vec1::Vec1;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents the types supported by WIT.
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
@ -39,6 +39,9 @@ pub enum InterfaceType {
|
||||
/// A string.
|
||||
String,
|
||||
|
||||
/// A byte array.
|
||||
ByteArray,
|
||||
|
||||
/// An `any` reference.
|
||||
Anyref,
|
||||
|
||||
|
@ -46,6 +46,9 @@ pub enum InterfaceValue {
|
||||
/// A string.
|
||||
String(String),
|
||||
|
||||
/// A byte array.
|
||||
ByteArray(Vec<u8>),
|
||||
|
||||
//Anyref(?),
|
||||
/// A 32-bits integer (as defined in WebAssembly core).
|
||||
I32(i32),
|
||||
@ -71,6 +74,7 @@ impl From<&InterfaceValue> for InterfaceType {
|
||||
InterfaceValue::F32(_) => Self::F32,
|
||||
InterfaceValue::F64(_) => Self::F64,
|
||||
InterfaceValue::String(_) => Self::String,
|
||||
InterfaceValue::ByteArray(_) => Self::ByteArray,
|
||||
//InterfaceValue::Anyref(_) => Self::Anyref,
|
||||
InterfaceValue::I32(_) => Self::I32,
|
||||
InterfaceValue::I64(_) => Self::I64,
|
||||
@ -139,6 +143,7 @@ native!(u64, U64);
|
||||
native!(f32, F32);
|
||||
native!(f64, F64);
|
||||
native!(String, String);
|
||||
native!(Vec<u8>, ByteArray);
|
||||
|
||||
/// Iterates over a vector of `InterfaceValues` but flatten all the
|
||||
/// values. So `I32(1), Record([I32(2), I32(3)]), I32(4)` will be
|
||||
|
@ -1,15 +1,15 @@
|
||||
//! `Vec1<T>` represents a non-empty `Vec<T>`.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
error,
|
||||
fmt::{self, Debug},
|
||||
ops,
|
||||
};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// `Vec1<T>` represents a non-empty `Vec<T>`. It derefs to `Vec<T>`
|
||||
/// directly.
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize, Default)]
|
||||
pub struct Vec1<T>(Vec<T>)
|
||||
where
|
||||
T: Debug;
|
||||
|
Loading…
Reference in New Issue
Block a user