add support of structs

This commit is contained in:
vms 2020-07-25 11:01:41 +03:00
parent b65b34b0f6
commit 5446f3e108
16 changed files with 681 additions and 308 deletions

25
Cargo.lock generated
View File

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

View File

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

View File

@ -261,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),

View File

@ -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::{
@ -72,6 +72,8 @@ mod keyword {
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");
}
@ -349,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>()?;

View File

@ -347,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)?,
}

View File

@ -144,6 +144,12 @@ impl ToString for &Instruction {
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(),
}

View File

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

View File

@ -1,10 +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::{
@ -14,13 +14,16 @@ use crate::{
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 {
@ -141,10 +144,10 @@ pub enum Instruction {
/// The `string.size` instruction.
StringSize,
/// The `string.lift_memory` instruction.
/// The `byte_array.lift_memory` instruction.
ByteArrayLiftMemory,
/// The `string.lower_memory` instruction.
/// The `byte_array.lower_memory` instruction.
ByteArrayLowerMemory,
/// The `string.size` instruction.
@ -162,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,

View File

@ -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,273 @@ 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,
size: 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,
{
let length = record_type.fields.len();
let mut values = VecDeque::with_capacity(length);
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.0 {
let value = data[field_id];
match field {
InterfaceType::S8 => {
values.push_front(InterfaceValue::S8(value as _));
}
InterfaceType::S16 => {
values.push_front(InterfaceValue::S16(value as _));
}
InterfaceType::S32 => {
values.push_front(InterfaceValue::S32(value as _));
}
InterfaceType::S64 => {
values.push_front(InterfaceValue::S64(value as _));
}
InterfaceType::I32 => {
values.push_front(InterfaceValue::I32(value as _));
}
InterfaceType::I64 => {
values.push_front(InterfaceValue::I64(value as _));
}
InterfaceType::U8 => {
values.push_front(InterfaceValue::U8(value as _));
}
InterfaceType::U16 => {
values.push_front(InterfaceValue::U16(value as _));
}
InterfaceType::U32 => {
values.push_front(InterfaceValue::U32(value as _));
}
InterfaceType::U64 => {
values.push_front(InterfaceValue::U64(value as _));
}
InterfaceType::F32 => {
values.push_front(InterfaceValue::F32(value as _));
}
InterfaceType::F64 => {
unsafe {
values.push_front(InterfaceValue::F64(std::mem::transmute::<u64, f64>(value)))
};
}
InterfaceType::Anyref => {}
InterfaceType::String => {
let offset = value;
field_id += 1;
let size = data[field_id];
let string_mem =
read_from_instance_mem(instance, instruction, offset as _, size as _)?;
// TODO: check
let string = String::from_utf8(string_mem).unwrap();
utils::deallocate(instance, instruction, offset as _, size as _)?;
values.push_front(InterfaceValue::String(string));
}
InterfaceType::ByteArray => {
let offset = value;
field_id += 1;
let size = data[field_id];
let byte_array =
read_from_instance_mem(instance, instruction, offset as _, size as _)?;
values.push_front(InterfaceValue::ByteArray(byte_array));
}
InterfaceType::Record(record_type) => {
let offset = value;
field_id += 1;
let size = data[field_id];
values.push_front(record_lift_memory_(
instance,
record_type,
offset as _,
size as _,
instruction,
)?)
}
}
field_id += 1;
}
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(2).ok_or_else(|| {
InstructionError::new(
instruction,
InstructionErrorKind::StackIsTooSmall { needed: 2 },
)
})?;
let offset: usize = to_native::<i32>(&inputs[0], instruction)?
.try_into()
.map_err(|e| (e, "offset").into())
.map_err(|k| InstructionError::new(instruction, k))?;
let size: usize = to_native::<i32>(&inputs[1], instruction)?
.try_into()
.map_err(|e| (e, "size").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, size, 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, 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.0 {
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(unsafe { std::mem::transmute::<f64, u64>(value) })
}
InterfaceValue::String(value) => {
let string_pointer =
write_to_instance_mem(instance, instruction.clone(), value.as_bytes())?;
result.push(string_pointer as _);
result.push(value.len() as _);
}
InterfaceValue::ByteArray(value) => {
let byte_array_pointer =
write_to_instance_mem(instance, instruction.clone(), &value)?;
result.push(byte_array_pointer as _);
result.push(value.len() as _);
}
InterfaceValue::Record(record) => {
let (record_ptr, record_size) =
record_lower_memory_(instance, instruction, record)?;
result.push(record_ptr as _);
result.push(record_size as _);
}
}
}
let result = safe_transmute::transmute_to_bytes::<u64>(&result);
let result_pointer = write_to_instance_mem(instance, instruction.clone(), &result)?;
Ok((result_pointer, result.len() as _))
}
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 +362,112 @@ 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, size) =
record_lower_memory_(*instance, instruction, record_values)?;
runtime.stack.push(InterfaceValue::I32(offset));
runtime.stack.push(InterfaceValue::I32(size));
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 },
)),
}
}
})
}

View 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.clone(), 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.clone(),
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.clone())
}
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.clone(),
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)
}

View File

@ -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,8 +237,12 @@ 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::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 } => {
@ -247,7 +250,14 @@ where
}
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),
})

View File

@ -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")]

View File

@ -97,7 +97,7 @@ macro_rules! next {
None => Err(DeserializeError::InputEmpty),
}
}
}
};
}
impl<'de> Deserializer<'de> {

View File

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

View File

@ -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)]

View File

@ -1,16 +1,16 @@
//! `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, Default)]
pub struct Vec1<T>(Vec<T>)
pub struct Vec1<T>(pub(crate) Vec<T>)
where
T: Debug;