Merge pull request #2 from fluencelabs/struct_support

Support records and byte arrays
This commit is contained in:
vms 2020-07-28 01:20:57 +03:00 committed by GitHub
commit 458adc2534
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 927 additions and 311 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

@ -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:
///

View File

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

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::{
@ -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>()?;

View File

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

View File

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

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

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

View File

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

View File

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

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

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

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

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

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)]
@ -39,6 +39,9 @@ pub enum InterfaceType {
/// A string.
String,
/// A byte array.
ByteArray,
/// An `any` reference.
Anyref,

View File

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

View File

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