mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-05 02:20:19 +00:00
Merge #1331
1331: feat(interface-types) Implement the `record` instructions r=Hywan a=Hywan ### Description This PR implements the `record` WIT type, along with the `record.lift` and `record.lower` instructions. With my current understanding of the draft/specification, here is how it works. Let's say we want to represent a Rust struct like the following: ```rust struct S { x: String, y: i32 } ``` First declare a WIT type, such as: ```wat (@interface type (record (field string) (field i32))) ``` The `record` type is supported by the binary encoder, the WAT encoder, the binary decoder, and the WAT decoder. A new `TypeKind` node has been introduced in the AST to differentiate a function type (`(@interface type (func (param …) (result …)))`) of a record type (see above). Second, the `record.lower` transforms a host value (here Rust value, `S`) into a WIT value. In our implementation, a record value is defined as: ```rust InterfaceValue::Record(Vec<InterfaceValue>) ``` Multiple mechanisms are used to type check a record value based on a record type. The code of the `record.lower` is pretty straightforward. Because transforming a host value into a WIT value isn't obvious, a `Serializer` has been implemented, based on [`serde`](https://serde.rs/). This feature is behind the `serde` flag, which is turned on by default. Serde is only used to cross the host/Wasm boundary, but it's not used to represent the value in memory or anything. It's only a shortcut to transform a host value into a WIT value, and vice versa. Use the following code to transform `S` into a WIT value: ```rust #[derive(Serialize, Deserialize, Debug, PartialEq)] struct S { x: String, y: i32, } let host_value = S { x: "hello".to_string(), y: 42 }; let wit_value = to_interface_value(&host_value).unwrap(); assert_eq!( wit_value, InterfaceValue::Record(vec![ InterfaceValue::String("hello".to_string()), InterfaceValue::I32(42), ]) ); ``` Third, the `record.lift` instruction does the opposite of `record.lower`: It transforms WIT values into a host value. To also facilitate the user experience, this PR contains a `Deserializer` implementation, still based on `serde`, with the `from_interface_values` function. It looks like this: ```rust let wit_values = vec![ InterfaceValue::Record(vec![ InterfaceValue::String("hello".to_string()), InterfaceValue::I32(42), ]) ]; let host_value = from_interface_values::<S>(&wit_values).unwrap(); assert_eq!( host_value, S { x: "hello".to_string(), y: 42 }, ); ``` With the `Serializer` and `Deserializer`, it's super easy for the user to send or receive values from WIT. The `record.lift` and `record.lower` instructions are kind of basic. The `record.lift` instruction has a little trick to reduce vector allocations, but there is a documentation for that. #### Opened questions Records of dimension 1 do not raise any issue. With `record.lift`, all values on the stack (the WIT interpreter stack) are popped, and are used as record field values. Something like: ``` [stack] i32(1) i64(2), string("hello") record.lift <record_type> ``` generates ``` [stack] record { i32(1), i64(2), string("hello") } ``` But it's not clear what happens with record of dimension > 1, for instance for a type like `record (field i32) (record (field i32) (field i32)) (field string)`, it is assumed (in this PR) that the stack must be like this: ``` [stack] i32(1) i32(2) i32(3) string("hello") record.lift <record_type> ``` to generate: ``` [stack] record { i32(1), record { i32(2), i32(3) }, string("hello") } ``` If we want the stack to contain an intermediate record, we should have something like this: ``` [stack] i32(1) i32(2) i32(3) record.lift <record_type_2> string("hello") record.lift <record_type_1> ``` But it would imply that `record_type_1` is defined as `record (field i32) (record (type record_type_2)) (field i32)`. A sub-record defined by another record type isn't support, as it is not specified in the draft. I believe my assumption is fine enough for a first implementation of records in WIT. ### To do - [x] Encode and decode record type (`(@interface type (record string i32))`): - [x] Binary encoder/decoder - [x] WAT encoder/decoder - [x] Implement the `record.lift` instruction - [x] Implement the `record.lower` instruction - [x] Test - [x] Documentation - [x] Surprise! - [x] Serialize a Rust value to WIT values (useful for `record`s) - [x] Deserialize WIT values to a Rust value (useful for `record`s) Co-authored-by: Ivan Enderlin <ivan.enderlin@hoa-project.net>
This commit is contained in:
commit
4d33020b35
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2855,6 +2855,7 @@ name = "wasmer-interface-types"
|
||||
version = "0.16.2"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"serde",
|
||||
"wast",
|
||||
]
|
||||
|
||||
|
@ -10,3 +10,11 @@ edition = "2018"
|
||||
[dependencies]
|
||||
nom = "5.1"
|
||||
wast = "8.0"
|
||||
|
||||
# `serde` is useful only to simplify the users' life. It is not
|
||||
# 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 }
|
||||
|
||||
[features]
|
||||
default = ["serde"]
|
@ -1,7 +1,7 @@
|
||||
//! Represents the WIT language as a tree. This is the central
|
||||
//! representation of the language.
|
||||
|
||||
use crate::interpreter::Instruction;
|
||||
use crate::{interpreter::Instruction, vec1::Vec1};
|
||||
use std::str;
|
||||
|
||||
/// Represents the types supported by WIT.
|
||||
@ -48,20 +48,50 @@ pub enum InterfaceType {
|
||||
|
||||
/// A 64-bits integer (as defiend in WebAssembly core).
|
||||
I64,
|
||||
|
||||
/// A record.
|
||||
Record(RecordType),
|
||||
}
|
||||
|
||||
/// Represents a type signature.
|
||||
///
|
||||
/// ```wasm,ignore
|
||||
/// (@interface type (param i32 i32) (result string))
|
||||
/// ```
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Type {
|
||||
/// Types for the parameters (`(param …)`).
|
||||
pub inputs: Vec<InterfaceType>,
|
||||
/// Represents a record type.
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct RecordType {
|
||||
/// Types representing the fields.
|
||||
pub fields: Vec1<InterfaceType>,
|
||||
}
|
||||
|
||||
/// Types for the results (`(result …)`).
|
||||
pub outputs: Vec<InterfaceType>,
|
||||
/// Represents the kind of type.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum TypeKind {
|
||||
/// A function type.
|
||||
Function,
|
||||
|
||||
/// A record type.
|
||||
Record,
|
||||
}
|
||||
|
||||
/// Represents a type.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Type {
|
||||
/// A function type, like:
|
||||
///
|
||||
/// ```wasm,ignore
|
||||
/// (@interface type (func (param i32 i32) (result string)))
|
||||
/// ```
|
||||
Function {
|
||||
/// Types for the parameters (`(param …)`).
|
||||
inputs: Vec<InterfaceType>,
|
||||
|
||||
/// Types for the results (`(result …)`).
|
||||
outputs: Vec<InterfaceType>,
|
||||
},
|
||||
|
||||
/// A record type, like:
|
||||
///
|
||||
/// ```wasm,ignore
|
||||
/// (@interface type (record string i32))
|
||||
/// ```
|
||||
Record(RecordType),
|
||||
}
|
||||
|
||||
/// Represents an imported function.
|
||||
|
@ -1,33 +1,21 @@
|
||||
//! Parse the WIT binary representation into an [AST](crate::ast).
|
||||
|
||||
use crate::{ast::*, interpreter::Instruction};
|
||||
use crate::{ast::*, interpreter::Instruction, vec1::Vec1};
|
||||
use nom::{
|
||||
error::{make_error, ErrorKind, ParseError},
|
||||
Err, IResult,
|
||||
};
|
||||
use std::{convert::TryFrom, str};
|
||||
|
||||
/// Parse an `InterfaceType`.
|
||||
impl TryFrom<u8> for InterfaceType {
|
||||
/// Parse a type kind.
|
||||
impl TryFrom<u8> for TypeKind {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(code: u8) -> Result<Self, Self::Error> {
|
||||
Ok(match code {
|
||||
0 => Self::S8,
|
||||
1 => Self::S16,
|
||||
2 => Self::S32,
|
||||
3 => Self::S64,
|
||||
4 => Self::U8,
|
||||
5 => Self::U16,
|
||||
6 => Self::U32,
|
||||
7 => Self::U64,
|
||||
8 => Self::F32,
|
||||
9 => Self::F64,
|
||||
10 => Self::String,
|
||||
11 => Self::Anyref,
|
||||
12 => Self::I32,
|
||||
13 => Self::I64,
|
||||
_ => return Err("Unknown interface type code."),
|
||||
0x00 => Self::Function,
|
||||
0x01 => Self::Record,
|
||||
_ => return Err("Unknown type kind code."),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -82,6 +70,56 @@ fn uleb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'i
|
||||
))
|
||||
}
|
||||
|
||||
/// Parse an interface type.
|
||||
fn ty<'input, E: ParseError<&'input [u8]>>(
|
||||
mut input: &'input [u8],
|
||||
) -> IResult<&'input [u8], InterfaceType, E> {
|
||||
if input.is_empty() {
|
||||
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
|
||||
}
|
||||
|
||||
consume!((input, opcode) = byte(input)?);
|
||||
|
||||
let ty = match opcode {
|
||||
0x00 => InterfaceType::S8,
|
||||
0x01 => InterfaceType::S16,
|
||||
0x02 => InterfaceType::S32,
|
||||
0x03 => InterfaceType::S64,
|
||||
0x04 => InterfaceType::U8,
|
||||
0x05 => InterfaceType::U16,
|
||||
0x06 => InterfaceType::U32,
|
||||
0x07 => InterfaceType::U64,
|
||||
0x08 => InterfaceType::F32,
|
||||
0x09 => InterfaceType::F64,
|
||||
0x0a => InterfaceType::String,
|
||||
0x0b => InterfaceType::Anyref,
|
||||
0x0c => InterfaceType::I32,
|
||||
0x0d => InterfaceType::I64,
|
||||
0x0e => {
|
||||
consume!((input, record_type) = record_type(input)?);
|
||||
|
||||
InterfaceType::Record(record_type)
|
||||
}
|
||||
_ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))),
|
||||
};
|
||||
|
||||
Ok((input, ty))
|
||||
}
|
||||
|
||||
/// Parse a record type.
|
||||
fn record_type<'input, E: ParseError<&'input [u8]>>(
|
||||
input: &'input [u8],
|
||||
) -> IResult<&'input [u8], RecordType, E> {
|
||||
let (output, fields) = list(input, ty)?;
|
||||
|
||||
Ok((
|
||||
output,
|
||||
RecordType {
|
||||
fields: Vec1::new(fields).expect("Record must have at least one field, zero given."),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Parse a UTF-8 string.
|
||||
fn string<'input, E: ParseError<&'input [u8]>>(
|
||||
input: &'input [u8],
|
||||
@ -131,22 +169,6 @@ fn list<'input, I, E: ParseError<&'input [u8]>>(
|
||||
Ok((input, items))
|
||||
}
|
||||
|
||||
/// Parse a type.
|
||||
fn ty<'input, E: ParseError<&'input [u8]>>(
|
||||
input: &'input [u8],
|
||||
) -> IResult<&'input [u8], InterfaceType, E> {
|
||||
if input.is_empty() {
|
||||
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
|
||||
}
|
||||
|
||||
let (output, ty) = byte(input)?;
|
||||
|
||||
match InterfaceType::try_from(ty) {
|
||||
Ok(ty) => Ok((output, ty)),
|
||||
Err(_) => Err(Err::Error(make_error(input, ErrorKind::ParseTo))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an instruction with its arguments.
|
||||
fn instruction<'input, E: ParseError<&'input [u8]>>(
|
||||
input: &'input [u8],
|
||||
@ -156,6 +178,7 @@ fn instruction<'input, E: ParseError<&'input [u8]>>(
|
||||
Ok(match opcode {
|
||||
0x00 => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
|
||||
(
|
||||
input,
|
||||
Instruction::ArgumentGet {
|
||||
@ -166,6 +189,7 @@ fn instruction<'input, E: ParseError<&'input [u8]>>(
|
||||
|
||||
0x01 => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
|
||||
(
|
||||
input,
|
||||
Instruction::CallCore {
|
||||
@ -208,9 +232,9 @@ fn instruction<'input, E: ParseError<&'input [u8]>>(
|
||||
0x21 => (input, Instruction::I64FromU64),
|
||||
|
||||
0x22 => (input, Instruction::StringLiftMemory),
|
||||
|
||||
0x23 => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
|
||||
(
|
||||
input,
|
||||
Instruction::StringLowerMemory {
|
||||
@ -218,9 +242,29 @@ fn instruction<'input, E: ParseError<&'input [u8]>>(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
0x24 => (input, Instruction::StringSize),
|
||||
|
||||
0x25 => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
|
||||
(
|
||||
input,
|
||||
Instruction::RecordLift {
|
||||
type_index: argument_0 as u32,
|
||||
},
|
||||
)
|
||||
}
|
||||
0x26 => {
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
|
||||
(
|
||||
input,
|
||||
Instruction::RecordLower {
|
||||
type_index: argument_0 as u32,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
_ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))),
|
||||
})
|
||||
}
|
||||
@ -234,10 +278,25 @@ fn types<'input, E: ParseError<&'input [u8]>>(
|
||||
let mut types = Vec::with_capacity(number_of_types as usize);
|
||||
|
||||
for _ in 0..number_of_types {
|
||||
consume!((input, inputs) = list(input, ty)?);
|
||||
consume!((input, outputs) = list(input, ty)?);
|
||||
consume!((input, type_kind) = byte(input)?);
|
||||
|
||||
types.push(Type { inputs, outputs });
|
||||
let type_kind = TypeKind::try_from(type_kind)
|
||||
.map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?;
|
||||
|
||||
match type_kind {
|
||||
TypeKind::Function => {
|
||||
consume!((input, inputs) = list(input, ty)?);
|
||||
consume!((input, outputs) = list(input, ty)?);
|
||||
|
||||
types.push(Type::Function { inputs, outputs });
|
||||
}
|
||||
|
||||
TypeKind::Record => {
|
||||
consume!((input, record_type) = record_type(input)?);
|
||||
|
||||
types.push(Type::Record(record_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((input, types))
|
||||
@ -403,6 +462,7 @@ fn interfaces<'input, E: ParseError<&'input [u8]>>(
|
||||
/// let input = &[
|
||||
/// 0x00, // type section
|
||||
/// 0x01, // 1 type
|
||||
/// 0x00, // function type
|
||||
/// 0x01, // list of 1 item
|
||||
/// 0x00, // S8
|
||||
/// 0x01, // list of 1 item
|
||||
@ -436,7 +496,7 @@ fn interfaces<'input, E: ParseError<&'input [u8]>>(
|
||||
/// let output = Ok((
|
||||
/// &[] as &[u8],
|
||||
/// Interfaces {
|
||||
/// types: vec![Type {
|
||||
/// types: vec![Type::Function {
|
||||
/// inputs: vec![InterfaceType::S8],
|
||||
/// outputs: vec![InterfaceType::S16],
|
||||
/// }],
|
||||
@ -546,6 +606,95 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ty() {
|
||||
let input = &[
|
||||
0x0f, // list of 15 items
|
||||
0x00, // S8
|
||||
0x01, // S16
|
||||
0x02, // S32
|
||||
0x03, // S64
|
||||
0x04, // U8
|
||||
0x05, // U16
|
||||
0x06, // U32
|
||||
0x07, // U64
|
||||
0x08, // F32
|
||||
0x09, // F64
|
||||
0x0a, // String
|
||||
0x0b, // Anyref
|
||||
0x0c, // I32
|
||||
0x0d, // I64
|
||||
0x0e, 0x01, 0x02, // Record
|
||||
0x01,
|
||||
];
|
||||
let output = Ok((
|
||||
&[0x01][..],
|
||||
vec![
|
||||
InterfaceType::S8,
|
||||
InterfaceType::S16,
|
||||
InterfaceType::S32,
|
||||
InterfaceType::S64,
|
||||
InterfaceType::U8,
|
||||
InterfaceType::U16,
|
||||
InterfaceType::U32,
|
||||
InterfaceType::U64,
|
||||
InterfaceType::F32,
|
||||
InterfaceType::F64,
|
||||
InterfaceType::String,
|
||||
InterfaceType::Anyref,
|
||||
InterfaceType::I32,
|
||||
InterfaceType::I64,
|
||||
InterfaceType::Record(RecordType {
|
||||
fields: vec1![InterfaceType::S32],
|
||||
}),
|
||||
],
|
||||
));
|
||||
|
||||
assert_eq!(list::<_, ()>(input, ty), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_record_type() {
|
||||
let input = &[
|
||||
0x03, // list of 3 items
|
||||
0x01, // 1 field
|
||||
0x0a, // String
|
||||
0x02, // 2 fields
|
||||
0x0a, // String
|
||||
0x0c, // I32
|
||||
0x03, // 3 fields
|
||||
0x0a, // String
|
||||
0x0e, // Record
|
||||
0x02, // 2 fields
|
||||
0x0c, // I32
|
||||
0x0c, // I32
|
||||
0x09, // F64
|
||||
0x01,
|
||||
];
|
||||
let output = Ok((
|
||||
&[0x01][..],
|
||||
vec![
|
||||
RecordType {
|
||||
fields: vec1![InterfaceType::String],
|
||||
},
|
||||
RecordType {
|
||||
fields: vec1![InterfaceType::String, InterfaceType::I32],
|
||||
},
|
||||
RecordType {
|
||||
fields: vec1![
|
||||
InterfaceType::String,
|
||||
InterfaceType::Record(RecordType {
|
||||
fields: vec1![InterfaceType::I32, InterfaceType::I32],
|
||||
}),
|
||||
InterfaceType::F64,
|
||||
],
|
||||
},
|
||||
],
|
||||
));
|
||||
|
||||
assert_eq!(list::<_, ()>(input, record_type), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string() {
|
||||
let input = &[
|
||||
@ -573,56 +722,13 @@ mod tests {
|
||||
];
|
||||
let output = Ok((&[0x07][..], vec!["a", "bc"]));
|
||||
|
||||
assert_eq!(list::<&str, ()>(input, string), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ty() {
|
||||
let input = &[
|
||||
0x0e, // list of 14 items
|
||||
0x00, // S8
|
||||
0x01, // S16
|
||||
0x02, // S32
|
||||
0x03, // S64
|
||||
0x04, // U8
|
||||
0x05, // U16
|
||||
0x06, // U32
|
||||
0x07, // U64
|
||||
0x08, // F32
|
||||
0x09, // F64
|
||||
0x0a, // String
|
||||
0x0b, // Anyref
|
||||
0x0c, // I32
|
||||
0x0d, // I64
|
||||
0x01,
|
||||
];
|
||||
let output = Ok((
|
||||
&[0x01][..],
|
||||
vec![
|
||||
InterfaceType::S8,
|
||||
InterfaceType::S16,
|
||||
InterfaceType::S32,
|
||||
InterfaceType::S64,
|
||||
InterfaceType::U8,
|
||||
InterfaceType::U16,
|
||||
InterfaceType::U32,
|
||||
InterfaceType::U64,
|
||||
InterfaceType::F32,
|
||||
InterfaceType::F64,
|
||||
InterfaceType::String,
|
||||
InterfaceType::Anyref,
|
||||
InterfaceType::I32,
|
||||
InterfaceType::I64,
|
||||
],
|
||||
));
|
||||
|
||||
assert_eq!(list::<InterfaceType, ()>(input, ty), output);
|
||||
assert_eq!(list::<_, ()>(input, string), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instructions() {
|
||||
let input = &[
|
||||
0x25, // list of 37 items
|
||||
0x27, // list of 39 items
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
0x01, 0x01, // CallCore { function_index: 1 }
|
||||
0x02, // S8FromI32
|
||||
@ -660,6 +766,8 @@ mod tests {
|
||||
0x22, // StringLiftMemory
|
||||
0x23, 0x01, // StringLowerMemory { allocator_index: 1 }
|
||||
0x24, // StringSize
|
||||
0x25, 0x01, // RecordLift { type_index: 1 },
|
||||
0x26, 0x01, // RecordLower { type_index: 1 },
|
||||
0x0a,
|
||||
];
|
||||
let output = Ok((
|
||||
@ -702,10 +810,12 @@ mod tests {
|
||||
Instruction::StringLiftMemory,
|
||||
Instruction::StringLowerMemory { allocator_index: 1 },
|
||||
Instruction::StringSize,
|
||||
Instruction::RecordLift { type_index: 1 },
|
||||
Instruction::RecordLower { type_index: 1 },
|
||||
],
|
||||
));
|
||||
|
||||
assert_eq!(list::<Instruction, ()>(input, instruction), output);
|
||||
assert_eq!(list::<_, ()>(input, instruction), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -739,19 +849,29 @@ mod tests {
|
||||
#[test]
|
||||
fn test_types() {
|
||||
let input = &[
|
||||
0x01, // 1 type
|
||||
0x02, // 2 type
|
||||
0x00, // function type
|
||||
0x02, // list of 2 items
|
||||
0x02, // S32
|
||||
0x02, // S32
|
||||
0x01, // list of 2 items
|
||||
0x02, // S32
|
||||
0x01, // record type
|
||||
0x02, // list of 2 items
|
||||
0x02, // S32
|
||||
0x02, // S32
|
||||
];
|
||||
let output = Ok((
|
||||
&[] as &[u8],
|
||||
vec![Type {
|
||||
inputs: vec![InterfaceType::S32, InterfaceType::S32],
|
||||
outputs: vec![InterfaceType::S32],
|
||||
}],
|
||||
vec![
|
||||
Type::Function {
|
||||
inputs: vec![InterfaceType::S32, InterfaceType::S32],
|
||||
outputs: vec![InterfaceType::S32],
|
||||
},
|
||||
Type::Record(RecordType {
|
||||
fields: vec1![InterfaceType::S32, InterfaceType::S32],
|
||||
}),
|
||||
],
|
||||
));
|
||||
|
||||
assert_eq!(types::<()>(input), output);
|
||||
@ -815,6 +935,7 @@ mod tests {
|
||||
let input = &[
|
||||
0x00, // type section
|
||||
0x01, // 1 type
|
||||
0x00, // function type
|
||||
0x01, // list of 1 item
|
||||
0x00, // S8
|
||||
0x01, // list of 1 item
|
||||
@ -848,7 +969,7 @@ mod tests {
|
||||
let output = Ok((
|
||||
&[] as &[u8],
|
||||
Interfaces {
|
||||
types: vec![Type {
|
||||
types: vec![Type::Function {
|
||||
inputs: vec![InterfaceType::S8],
|
||||
outputs: vec![InterfaceType::S16],
|
||||
}],
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Parse the WIT textual representation into an [AST](crate::ast).
|
||||
|
||||
use crate::{ast::*, interpreter::Instruction};
|
||||
use crate::{ast::*, interpreter::Instruction, vec1::Vec1};
|
||||
pub use wast::parser::ParseBuffer as Buffer;
|
||||
use wast::parser::{self, Cursor, Parse, Parser, Peek, Result};
|
||||
|
||||
@ -13,6 +13,8 @@ mod keyword {
|
||||
// New keywords.
|
||||
custom_keyword!(implement);
|
||||
custom_keyword!(r#type = "type");
|
||||
custom_keyword!(record);
|
||||
custom_keyword!(field);
|
||||
|
||||
// New types.
|
||||
custom_keyword!(s8);
|
||||
@ -63,6 +65,8 @@ 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!(record_lift = "record.lift");
|
||||
custom_keyword!(record_lower = "record.lower");
|
||||
}
|
||||
|
||||
impl Parse<'_> for InterfaceType {
|
||||
@ -125,12 +129,34 @@ impl Parse<'_> for InterfaceType {
|
||||
parser.parse::<keyword::i64>()?;
|
||||
|
||||
Ok(InterfaceType::I64)
|
||||
} else if lookahead.peek::<keyword::record>() {
|
||||
Ok(InterfaceType::Record(parser.parse()?))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse<'_> for RecordType {
|
||||
fn parse(parser: Parser<'_>) -> Result<Self> {
|
||||
parser.parse::<keyword::record>()?;
|
||||
|
||||
let mut fields = vec![];
|
||||
|
||||
while !parser.is_empty() {
|
||||
fields.push(parser.parens(|parser| {
|
||||
parser.parse::<keyword::field>()?;
|
||||
|
||||
parser.parse()
|
||||
})?);
|
||||
}
|
||||
|
||||
Ok(RecordType {
|
||||
fields: Vec1::new(fields).expect("Record must have at least one field, zero given."),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> for Instruction {
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
@ -290,6 +316,18 @@ impl<'a> Parse<'a> for Instruction {
|
||||
parser.parse::<keyword::string_size>()?;
|
||||
|
||||
Ok(Instruction::StringSize)
|
||||
} else if lookahead.peek::<keyword::record_lift>() {
|
||||
parser.parse::<keyword::record_lift>()?;
|
||||
|
||||
Ok(Instruction::RecordLift {
|
||||
type_index: parser.parse()?,
|
||||
})
|
||||
} else if lookahead.peek::<keyword::record_lower>() {
|
||||
parser.parse::<keyword::record_lower>()?;
|
||||
|
||||
Ok(Instruction::RecordLower {
|
||||
type_index: parser.parse()?,
|
||||
})
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
@ -401,25 +439,36 @@ impl<'a> Parse<'a> for Type {
|
||||
fn parse(parser: Parser<'a>) -> Result<Self> {
|
||||
parser.parse::<keyword::r#type>()?;
|
||||
|
||||
let (inputs, outputs) = parser.parens(|parser| {
|
||||
parser.parse::<keyword::func>()?;
|
||||
let ty = parser.parens(|parser| {
|
||||
let mut lookahead = parser.lookahead1();
|
||||
|
||||
let mut input_types = vec![];
|
||||
let mut output_types = vec![];
|
||||
if lookahead.peek::<keyword::func>() {
|
||||
parser.parse::<keyword::func>()?;
|
||||
|
||||
while !parser.is_empty() {
|
||||
let function_type = parser.parse::<FunctionType>()?;
|
||||
let mut input_types = vec![];
|
||||
let mut output_types = vec![];
|
||||
|
||||
match function_type {
|
||||
FunctionType::Input(mut inputs) => input_types.append(&mut inputs),
|
||||
FunctionType::Output(mut outputs) => output_types.append(&mut outputs),
|
||||
while !parser.is_empty() {
|
||||
let function_type = parser.parse::<FunctionType>()?;
|
||||
|
||||
match function_type {
|
||||
FunctionType::Input(mut inputs) => input_types.append(&mut inputs),
|
||||
FunctionType::Output(mut outputs) => output_types.append(&mut outputs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((input_types, output_types))
|
||||
Ok(Type::Function {
|
||||
inputs: input_types,
|
||||
outputs: output_types,
|
||||
})
|
||||
} else if lookahead.peek::<keyword::record>() {
|
||||
Ok(Type::Record(parser.parse()?))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(Type { inputs, outputs })
|
||||
Ok(ty)
|
||||
}
|
||||
}
|
||||
|
||||
@ -561,7 +610,7 @@ impl<'a> Parse<'a> for Interfaces<'a> {
|
||||
/// )
|
||||
/// .unwrap();
|
||||
/// let output = Interfaces {
|
||||
/// types: vec![Type {
|
||||
/// types: vec![Type::Function {
|
||||
/// inputs: vec![InterfaceType::I32],
|
||||
/// outputs: vec![InterfaceType::S8],
|
||||
/// }],
|
||||
@ -602,8 +651,21 @@ mod tests {
|
||||
#[test]
|
||||
fn test_interface_type() {
|
||||
let inputs = vec![
|
||||
"s8", "s16", "s32", "s64", "u8", "u16", "u32", "u64", "f32", "f64", "string", "anyref",
|
||||
"i32", "i64",
|
||||
"s8",
|
||||
"s16",
|
||||
"s32",
|
||||
"s64",
|
||||
"u8",
|
||||
"u16",
|
||||
"u32",
|
||||
"u64",
|
||||
"f32",
|
||||
"f64",
|
||||
"string",
|
||||
"anyref",
|
||||
"i32",
|
||||
"i64",
|
||||
"record (field string)",
|
||||
];
|
||||
let outputs = vec![
|
||||
InterfaceType::S8,
|
||||
@ -620,6 +682,9 @@ mod tests {
|
||||
InterfaceType::Anyref,
|
||||
InterfaceType::I32,
|
||||
InterfaceType::I64,
|
||||
InterfaceType::Record(RecordType {
|
||||
fields: vec1![InterfaceType::String],
|
||||
}),
|
||||
];
|
||||
|
||||
assert_eq!(inputs.len(), outputs.len());
|
||||
@ -632,6 +697,41 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_record_type() {
|
||||
let inputs = vec![
|
||||
"record (field string)",
|
||||
"record (field string) (field i32)",
|
||||
"record (field string) (field record (field i32) (field i32)) (field f64)",
|
||||
];
|
||||
let outputs = vec![
|
||||
RecordType {
|
||||
fields: vec1![InterfaceType::String],
|
||||
},
|
||||
RecordType {
|
||||
fields: vec1![InterfaceType::String, InterfaceType::I32],
|
||||
},
|
||||
RecordType {
|
||||
fields: vec1![
|
||||
InterfaceType::String,
|
||||
InterfaceType::Record(RecordType {
|
||||
fields: vec1![InterfaceType::I32, InterfaceType::I32],
|
||||
}),
|
||||
InterfaceType::F64,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
assert_eq!(inputs.len(), outputs.len());
|
||||
|
||||
for (input, output) in inputs.iter().zip(outputs.iter()) {
|
||||
assert_eq!(
|
||||
&parser::parse::<RecordType>(&buffer(input)).unwrap(),
|
||||
output
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instructions() {
|
||||
let inputs = vec![
|
||||
@ -672,6 +772,8 @@ mod tests {
|
||||
"string.lift_memory",
|
||||
"string.lower_memory 42",
|
||||
"string.size",
|
||||
"record.lift 42",
|
||||
"record.lower 42",
|
||||
];
|
||||
let outputs = vec![
|
||||
Instruction::ArgumentGet { index: 7 },
|
||||
@ -713,6 +815,8 @@ mod tests {
|
||||
allocator_index: 42,
|
||||
},
|
||||
Instruction::StringSize,
|
||||
Instruction::RecordLift { type_index: 42 },
|
||||
Instruction::RecordLower { type_index: 42 },
|
||||
];
|
||||
|
||||
assert_eq!(inputs.len(), outputs.len());
|
||||
@ -758,9 +862,9 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type() {
|
||||
fn test_type_function() {
|
||||
let input = buffer(r#"(@interface type (func (param i32 i32) (result i32)))"#);
|
||||
let output = Interface::Type(Type {
|
||||
let output = Interface::Type(Type::Function {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
});
|
||||
@ -768,6 +872,16 @@ mod tests {
|
||||
assert_eq!(parser::parse::<Interface>(&input).unwrap(), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_record() {
|
||||
let input = buffer(r#"(@interface type (record (field string) (field i32)))"#);
|
||||
let output = Interface::Type(Type::Record(RecordType {
|
||||
fields: vec1![InterfaceType::String, InterfaceType::I32],
|
||||
}));
|
||||
|
||||
assert_eq!(parser::parse::<Interface>(&input).unwrap(), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_export() {
|
||||
let input = buffer(r#"(@interface export "foo" (func 0))"#);
|
||||
@ -838,7 +952,7 @@ mod tests {
|
||||
(@interface implement (func 0) (func 1))"#,
|
||||
);
|
||||
let output = Interfaces {
|
||||
types: vec![Type {
|
||||
types: vec![Type::Function {
|
||||
inputs: vec![InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::S8],
|
||||
}],
|
||||
|
@ -108,6 +108,33 @@ where
|
||||
InterfaceType::Anyref => 0x0b_u8.to_bytes(writer),
|
||||
InterfaceType::I32 => 0x0c_u8.to_bytes(writer),
|
||||
InterfaceType::I64 => 0x0d_u8.to_bytes(writer),
|
||||
InterfaceType::Record(record_type) => {
|
||||
0x0e_u8.to_bytes(writer)?;
|
||||
record_type.to_bytes(writer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a `RecordType` into bytes.
|
||||
impl<W> ToBytes<W> for RecordType
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
self.fields.to_bytes(writer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a `TypeKind` into bytes.
|
||||
impl<W> ToBytes<W> for TypeKind
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
TypeKind::Function => 0x00_u8.to_bytes(writer),
|
||||
TypeKind::Record => 0x01_u8.to_bytes(writer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,8 +163,18 @@ where
|
||||
W: Write,
|
||||
{
|
||||
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
|
||||
self.inputs.to_bytes(writer)?;
|
||||
self.outputs.to_bytes(writer)?;
|
||||
match self {
|
||||
Type::Function { inputs, outputs } => {
|
||||
TypeKind::Function.to_bytes(writer)?;
|
||||
inputs.to_bytes(writer)?;
|
||||
outputs.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Type::Record(RecordType { fields }) => {
|
||||
TypeKind::Record.to_bytes(writer)?;
|
||||
fields.to_bytes(writer)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -294,13 +331,20 @@ where
|
||||
Instruction::I64FromU64 => 0x21_u8.to_bytes(writer)?,
|
||||
|
||||
Instruction::StringLiftMemory => 0x22_u8.to_bytes(writer)?,
|
||||
|
||||
Instruction::StringLowerMemory { allocator_index } => {
|
||||
0x23_u8.to_bytes(writer)?;
|
||||
(*allocator_index as u64).to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::StringSize => 0x24_u8.to_bytes(writer)?,
|
||||
|
||||
Instruction::RecordLift { type_index } => {
|
||||
0x25_u8.to_bytes(writer)?;
|
||||
(*type_index as u64).to_bytes(writer)?
|
||||
}
|
||||
Instruction::RecordLower { type_index } => {
|
||||
0x26_u8.to_bytes(writer)?;
|
||||
(*type_index as u64).to_bytes(writer)?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -399,6 +443,55 @@ mod tests {
|
||||
assert_to_bytes!(InterfaceType::Anyref, &[0x0b]);
|
||||
assert_to_bytes!(InterfaceType::I32, &[0x0c]);
|
||||
assert_to_bytes!(InterfaceType::I64, &[0x0d]);
|
||||
assert_to_bytes!(
|
||||
InterfaceType::Record(RecordType {
|
||||
fields: vec1![InterfaceType::String]
|
||||
}),
|
||||
&[0x0e, 0x01, 0x0a]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_record_type() {
|
||||
assert_to_bytes!(
|
||||
RecordType {
|
||||
fields: vec1![InterfaceType::String]
|
||||
},
|
||||
&[
|
||||
0x01, // 1 field
|
||||
0x0a, // String
|
||||
]
|
||||
);
|
||||
assert_to_bytes!(
|
||||
RecordType {
|
||||
fields: vec1![InterfaceType::String, InterfaceType::I32]
|
||||
},
|
||||
&[
|
||||
0x02, // 2 fields
|
||||
0x0a, // String
|
||||
0x0c, // I32
|
||||
]
|
||||
);
|
||||
assert_to_bytes!(
|
||||
RecordType {
|
||||
fields: vec1![
|
||||
InterfaceType::String,
|
||||
InterfaceType::Record(RecordType {
|
||||
fields: vec1![InterfaceType::I32, InterfaceType::I32],
|
||||
}),
|
||||
InterfaceType::F64,
|
||||
],
|
||||
},
|
||||
&[
|
||||
0x03, // 3 fields
|
||||
0x0a, // String
|
||||
0x0e, // Record
|
||||
0x02, // 2 fields
|
||||
0x0c, // I32
|
||||
0x0c, // I32
|
||||
0x09, // F64
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -428,13 +521,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type() {
|
||||
fn test_type_function() {
|
||||
assert_to_bytes!(
|
||||
Type {
|
||||
Type::Function {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I64],
|
||||
outputs: vec![InterfaceType::S32],
|
||||
},
|
||||
&[
|
||||
0x00, // function type
|
||||
0x02, // list of 2 items
|
||||
0x0c, // I32
|
||||
0x0d, // I64
|
||||
@ -444,6 +538,21 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_record() {
|
||||
assert_to_bytes!(
|
||||
Type::Record(RecordType {
|
||||
fields: vec1![InterfaceType::I32, InterfaceType::I64],
|
||||
}),
|
||||
&[
|
||||
0x01, // record type
|
||||
0x02, // list of 2 items
|
||||
0x0c, // I32
|
||||
0x0d, // I64
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import() {
|
||||
assert_to_bytes!(
|
||||
@ -481,7 +590,7 @@ mod tests {
|
||||
fn test_interfaces() {
|
||||
assert_to_bytes!(
|
||||
Interfaces {
|
||||
types: vec![Type {
|
||||
types: vec![Type::Function {
|
||||
inputs: vec![InterfaceType::S8],
|
||||
outputs: vec![InterfaceType::S16],
|
||||
}],
|
||||
@ -506,6 +615,7 @@ mod tests {
|
||||
&[
|
||||
0x00, // type section
|
||||
0x01, // 1 type
|
||||
0x00, // function type
|
||||
0x01, // list of 1 item
|
||||
0x00, // S8
|
||||
0x01, // list of 1 item
|
||||
@ -580,9 +690,11 @@ mod tests {
|
||||
Instruction::StringLiftMemory,
|
||||
Instruction::StringLowerMemory { allocator_index: 1 },
|
||||
Instruction::StringSize,
|
||||
Instruction::RecordLift { type_index: 1 },
|
||||
Instruction::RecordLower { type_index: 1 },
|
||||
],
|
||||
&[
|
||||
0x25, // list of 37 items
|
||||
0x27, // list of 39 items
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
0x01, 0x01, // CallCore { function_index: 1 }
|
||||
0x02, // S8FromI32
|
||||
@ -620,6 +732,8 @@ mod tests {
|
||||
0x22, // StringLiftMemory
|
||||
0x23, 0x01, // StringLowerMemory { allocator_index: 1 }
|
||||
0x24, // StringSize
|
||||
0x025, 0x01, // RecordLift { type_index: 1 }
|
||||
0x026, 0x01, // RecordLower { type_index: 1 }
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
//! };
|
||||
//!
|
||||
//! let input: String = (&Interfaces {
|
||||
//! types: vec![Type {
|
||||
//! types: vec![Type::Function {
|
||||
//! inputs: vec![InterfaceType::I32],
|
||||
//! outputs: vec![InterfaceType::S8],
|
||||
//! }],
|
||||
@ -61,24 +61,41 @@ use std::string::ToString;
|
||||
impl ToString for &InterfaceType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
InterfaceType::S8 => "s8".into(),
|
||||
InterfaceType::S16 => "s16".into(),
|
||||
InterfaceType::S32 => "s32".into(),
|
||||
InterfaceType::S64 => "s64".into(),
|
||||
InterfaceType::U8 => "u8".into(),
|
||||
InterfaceType::U16 => "u16".into(),
|
||||
InterfaceType::U32 => "u32".into(),
|
||||
InterfaceType::U64 => "u64".into(),
|
||||
InterfaceType::F32 => "f32".into(),
|
||||
InterfaceType::F64 => "f64".into(),
|
||||
InterfaceType::String => "string".into(),
|
||||
InterfaceType::Anyref => "anyref".into(),
|
||||
InterfaceType::I32 => "i32".into(),
|
||||
InterfaceType::I64 => "i64".into(),
|
||||
InterfaceType::S8 => "s8".to_string(),
|
||||
InterfaceType::S16 => "s16".to_string(),
|
||||
InterfaceType::S32 => "s32".to_string(),
|
||||
InterfaceType::S64 => "s64".to_string(),
|
||||
InterfaceType::U8 => "u8".to_string(),
|
||||
InterfaceType::U16 => "u16".to_string(),
|
||||
InterfaceType::U32 => "u32".to_string(),
|
||||
InterfaceType::U64 => "u64".to_string(),
|
||||
InterfaceType::F32 => "f32".to_string(),
|
||||
InterfaceType::F64 => "f64".to_string(),
|
||||
InterfaceType::String => "string".to_string(),
|
||||
InterfaceType::Anyref => "anyref".to_string(),
|
||||
InterfaceType::I32 => "i32".to_string(),
|
||||
InterfaceType::I64 => "i64".to_string(),
|
||||
InterfaceType::Record(record_type) => record_type.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for &RecordType {
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
"record{fields}",
|
||||
fields = self
|
||||
.fields
|
||||
.iter()
|
||||
.fold(String::new(), |mut accumulator, interface_type| {
|
||||
accumulator.push(' ');
|
||||
accumulator.push_str(&format!("(field {})", &interface_type.to_string()));
|
||||
accumulator
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an `Instruction` into a string.
|
||||
impl ToString for &Instruction {
|
||||
fn to_string(&self) -> String {
|
||||
@ -119,9 +136,11 @@ impl ToString for &Instruction {
|
||||
Instruction::I64FromU64 => "i64.from_u64".into(),
|
||||
Instruction::StringLiftMemory => "string.lift_memory".into(),
|
||||
Instruction::StringLowerMemory { allocator_index } => {
|
||||
format!(r#"string.lower_memory {}"#, allocator_index)
|
||||
format!("string.lower_memory {}", allocator_index)
|
||||
}
|
||||
Instruction::StringSize => "string.size".into(),
|
||||
Instruction::RecordLift { type_index } => format!("record.lift {}", type_index),
|
||||
Instruction::RecordLower { type_index } => format!("record.lower {}", type_index),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,11 +186,18 @@ fn output_types_to_result(output_types: &[InterfaceType]) -> String {
|
||||
/// Encode a `Type` into a string.
|
||||
impl<'input> ToString for &Type {
|
||||
fn to_string(&self) -> String {
|
||||
format!(
|
||||
r#"(@interface type (func{inputs}{outputs}))"#,
|
||||
inputs = input_types_to_param(&self.inputs),
|
||||
outputs = output_types_to_result(&self.outputs),
|
||||
)
|
||||
match self {
|
||||
Type::Function { inputs, outputs } => format!(
|
||||
r#"(@interface type (func{inputs}{outputs}))"#,
|
||||
inputs = input_types_to_param(&inputs),
|
||||
outputs = output_types_to_result(&outputs),
|
||||
),
|
||||
|
||||
Type::Record(record_type) => format!(
|
||||
r#"(@interface type ({record_type}))"#,
|
||||
record_type = record_type.to_string(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -341,10 +367,58 @@ mod tests {
|
||||
(&InterfaceType::Anyref).to_string(),
|
||||
(&InterfaceType::I32).to_string(),
|
||||
(&InterfaceType::I64).to_string(),
|
||||
(&InterfaceType::Record(RecordType {
|
||||
fields: vec1![InterfaceType::String],
|
||||
}))
|
||||
.to_string(),
|
||||
];
|
||||
let outputs = vec![
|
||||
"s8", "s16", "s32", "s64", "u8", "u16", "u32", "u64", "f32", "f64", "string", "anyref",
|
||||
"i32", "i64",
|
||||
"s8",
|
||||
"s16",
|
||||
"s32",
|
||||
"s64",
|
||||
"u8",
|
||||
"u16",
|
||||
"u32",
|
||||
"u64",
|
||||
"f32",
|
||||
"f64",
|
||||
"string",
|
||||
"anyref",
|
||||
"i32",
|
||||
"i64",
|
||||
"record (field string)",
|
||||
];
|
||||
|
||||
assert_eq!(inputs, outputs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_record_type() {
|
||||
let inputs = vec![
|
||||
(&RecordType {
|
||||
fields: vec1![InterfaceType::String],
|
||||
})
|
||||
.to_string(),
|
||||
(&RecordType {
|
||||
fields: vec1![InterfaceType::String, InterfaceType::I32],
|
||||
})
|
||||
.to_string(),
|
||||
(&RecordType {
|
||||
fields: vec1![
|
||||
InterfaceType::String,
|
||||
InterfaceType::Record(RecordType {
|
||||
fields: vec1![InterfaceType::I32, InterfaceType::I32],
|
||||
}),
|
||||
InterfaceType::F64,
|
||||
],
|
||||
})
|
||||
.to_string(),
|
||||
];
|
||||
let outputs = vec![
|
||||
"record (field string)",
|
||||
"record (field string) (field i32)",
|
||||
"record (field string) (field record (field i32) (field i32)) (field f64)",
|
||||
];
|
||||
|
||||
assert_eq!(inputs, outputs);
|
||||
@ -393,6 +467,8 @@ mod tests {
|
||||
})
|
||||
.to_string(),
|
||||
(&Instruction::StringSize).to_string(),
|
||||
(&Instruction::RecordLift { type_index: 42 }).to_string(),
|
||||
(&Instruction::RecordLower { type_index: 42 }).to_string(),
|
||||
];
|
||||
let outputs = vec![
|
||||
"arg.get 7",
|
||||
@ -432,6 +508,8 @@ mod tests {
|
||||
"string.lift_memory",
|
||||
"string.lower_memory 42",
|
||||
"string.size",
|
||||
"record.lift 42",
|
||||
"record.lower 42",
|
||||
];
|
||||
|
||||
assert_eq!(inputs, outputs);
|
||||
@ -440,26 +518,30 @@ mod tests {
|
||||
#[test]
|
||||
fn test_types() {
|
||||
let inputs: Vec<String> = vec![
|
||||
(&Type {
|
||||
(&Type::Function {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::F32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
})
|
||||
.to_string(),
|
||||
(&Type {
|
||||
(&Type::Function {
|
||||
inputs: vec![InterfaceType::I32],
|
||||
outputs: vec![],
|
||||
})
|
||||
.to_string(),
|
||||
(&Type {
|
||||
(&Type::Function {
|
||||
inputs: vec![],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
})
|
||||
.to_string(),
|
||||
(&Type {
|
||||
(&Type::Function {
|
||||
inputs: vec![],
|
||||
outputs: vec![],
|
||||
})
|
||||
.to_string(),
|
||||
(&Type::Record(RecordType {
|
||||
fields: vec1![InterfaceType::String, InterfaceType::I32],
|
||||
}))
|
||||
.to_string(),
|
||||
];
|
||||
let outputs = vec![
|
||||
r#"(@interface type (func
|
||||
@ -470,6 +552,7 @@ mod tests {
|
||||
r#"(@interface type (func
|
||||
(result i32)))"#,
|
||||
r#"(@interface type (func))"#,
|
||||
r#"(@interface type (record (field string) (field i32)))"#,
|
||||
];
|
||||
|
||||
assert_eq!(inputs, outputs);
|
||||
@ -516,7 +599,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_interfaces() {
|
||||
let input: String = (&Interfaces {
|
||||
types: vec![Type {
|
||||
types: vec![Type::Function {
|
||||
inputs: vec![InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::S8],
|
||||
}],
|
||||
|
@ -1,7 +1,10 @@
|
||||
//! The error module contains all the data structures that represent
|
||||
//! an error.
|
||||
|
||||
use crate::{ast::InterfaceType, interpreter::Instruction};
|
||||
use crate::{
|
||||
ast::{InterfaceType, TypeKind},
|
||||
interpreter::Instruction,
|
||||
};
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::{self, Display, Formatter},
|
||||
@ -156,6 +159,21 @@ pub enum InstructionErrorKind {
|
||||
/// The variable name that triggered the error.
|
||||
subject: &'static str,
|
||||
},
|
||||
|
||||
/// The type doesn't exist.
|
||||
TypeIsMissing {
|
||||
/// The type index.
|
||||
type_index: u32,
|
||||
},
|
||||
|
||||
/// Read a type that has an unexpected type.
|
||||
InvalidTypeKind {
|
||||
/// The expected kind.
|
||||
expected_kind: TypeKind,
|
||||
|
||||
/// The received kind.
|
||||
received_kind: TypeKind,
|
||||
},
|
||||
}
|
||||
|
||||
impl Error for InstructionErrorKind {}
|
||||
@ -203,11 +221,7 @@ impl Display for InstructionErrorKind {
|
||||
Self::LocalOrImportSignatureMismatch { function_index, expected, received } => write!(
|
||||
formatter,
|
||||
"the local or import function `{}` has the signature `{:?} -> {:?}` but it received values of kind `{:?} -> {:?}`",
|
||||
function_index,
|
||||
expected.0,
|
||||
expected.1,
|
||||
received.0,
|
||||
received.1,
|
||||
function_index, expected.0, expected.1, received.0, received.1,
|
||||
),
|
||||
|
||||
Self::LocalOrImportCall { function_index } => write!(
|
||||
@ -225,21 +239,28 @@ impl Display for InstructionErrorKind {
|
||||
Self::MemoryOutOfBoundsAccess { index, length } => write!(
|
||||
formatter,
|
||||
"read out of the memory bounds (index {} > memory length {})",
|
||||
index,
|
||||
length,
|
||||
index, length,
|
||||
),
|
||||
|
||||
Self::String(error) => write!(
|
||||
formatter,
|
||||
"{}",
|
||||
error
|
||||
),
|
||||
Self::String(error) => write!(formatter, "{}", error),
|
||||
|
||||
Self::NegativeValue { subject } => write!(
|
||||
formatter,
|
||||
"attempted to convert `{}` but it appears to be a negative value",
|
||||
subject
|
||||
),
|
||||
|
||||
Self::TypeIsMissing { type_index } => write!(
|
||||
formatter,
|
||||
"the type `{}` doesn't exist",
|
||||
type_index
|
||||
),
|
||||
|
||||
Self::InvalidTypeKind { expected_kind, received_kind } => write!(
|
||||
formatter,
|
||||
"read a type of kind `{:?}`, but the kind `{:?}` was expected",
|
||||
received_kind, expected_kind
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,125 +0,0 @@
|
||||
//use crate::ast::InterfaceType;
|
||||
|
||||
/// Represents all the possible WIT instructions.
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum Instruction {
|
||||
/// The `arg.get` instruction.
|
||||
ArgumentGet {
|
||||
/// The argument index.
|
||||
index: u32,
|
||||
},
|
||||
|
||||
/// The `call-core` instruction.
|
||||
CallCore {
|
||||
/// The function index.
|
||||
function_index: u32,
|
||||
},
|
||||
|
||||
/// The `s8.from_i32` instruction.
|
||||
S8FromI32,
|
||||
|
||||
/// The `s8.from_i64` instruction.
|
||||
S8FromI64,
|
||||
|
||||
/// The `s16.from_i32` instruction.
|
||||
S16FromI32,
|
||||
|
||||
/// The `s16.from_i64` instruction.
|
||||
S16FromI64,
|
||||
|
||||
/// The `s32.from_i32` instruction.
|
||||
S32FromI32,
|
||||
|
||||
/// The `s32.from_i64` instruction.
|
||||
S32FromI64,
|
||||
|
||||
/// The `s64.from_i32` instruction.
|
||||
S64FromI32,
|
||||
|
||||
/// The `s64.from_i64` instruction.
|
||||
S64FromI64,
|
||||
|
||||
/// The `i32.from_s8` instruction.
|
||||
I32FromS8,
|
||||
|
||||
/// The `i32.from_s16` instruction.
|
||||
I32FromS16,
|
||||
|
||||
/// The `i32.from_s32` instruction.
|
||||
I32FromS32,
|
||||
|
||||
/// The `i32.from_s64` instruction.
|
||||
I32FromS64,
|
||||
|
||||
/// The `i64.from_s8` instruction.
|
||||
I64FromS8,
|
||||
|
||||
/// The `i64.from_s16` instruction.
|
||||
I64FromS16,
|
||||
|
||||
/// The `i64.from_s32` instruction.
|
||||
I64FromS32,
|
||||
|
||||
/// The `i64.from_s64` instruction.
|
||||
I64FromS64,
|
||||
|
||||
/// The `u8.from_i32` instruction.
|
||||
U8FromI32,
|
||||
|
||||
/// The `u8.from_i64` instruction.
|
||||
U8FromI64,
|
||||
|
||||
/// The `u16.from_i32` instruction.
|
||||
U16FromI32,
|
||||
|
||||
/// The `u16.from_i64` instruction.
|
||||
U16FromI64,
|
||||
|
||||
/// The `u32.from_i32` instruction.
|
||||
U32FromI32,
|
||||
|
||||
/// The `u32.from_i64` instruction.
|
||||
U32FromI64,
|
||||
|
||||
/// The `u64.from_i32` instruction.
|
||||
U64FromI32,
|
||||
|
||||
/// The `u64.from_i64` instruction.
|
||||
U64FromI64,
|
||||
|
||||
/// The `i32.from_u8` instruction.
|
||||
I32FromU8,
|
||||
|
||||
/// The `i32.from_u16` instruction.
|
||||
I32FromU16,
|
||||
|
||||
/// The `i32.from_u32` instruction.
|
||||
I32FromU32,
|
||||
|
||||
/// The `i32.from_u64` instruction.
|
||||
I32FromU64,
|
||||
|
||||
/// The `i64.from_u8` instruction.
|
||||
I64FromU8,
|
||||
|
||||
/// The `i64.from_u16` instruction.
|
||||
I64FromU16,
|
||||
|
||||
/// The `i64.from_u32` instruction.
|
||||
I64FromU32,
|
||||
|
||||
/// The `i64.from_u64` instruction.
|
||||
I64FromU64,
|
||||
|
||||
/// The `string.lift_memory` instruction.
|
||||
StringLiftMemory,
|
||||
|
||||
/// The `string.lower_memory` instruction.
|
||||
StringLowerMemory {
|
||||
/// The allocator function index.
|
||||
allocator_index: u32,
|
||||
},
|
||||
|
||||
/// The `string.size` instruction.
|
||||
StringSize,
|
||||
}
|
@ -56,8 +56,8 @@ executable_instruction!(
|
||||
)
|
||||
})?;
|
||||
|
||||
for output in outputs.iter() {
|
||||
runtime.stack.push(output.clone());
|
||||
for output in outputs.into_iter() {
|
||||
runtime.stack.push(output)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1,21 +1,156 @@
|
||||
mod argument_get;
|
||||
mod call_core;
|
||||
mod numbers;
|
||||
mod records;
|
||||
mod strings;
|
||||
|
||||
use crate::{
|
||||
errors::{InstructionError, InstructionErrorKind, InstructionResult, WasmValueNativeCastError},
|
||||
interpreter::{
|
||||
wasm::values::{InterfaceValue, NativeType},
|
||||
Instruction,
|
||||
},
|
||||
interpreter::wasm::values::{InterfaceValue, NativeType},
|
||||
};
|
||||
pub(crate) use argument_get::argument_get;
|
||||
pub(crate) use call_core::call_core;
|
||||
pub(crate) use numbers::*;
|
||||
pub(crate) use records::*;
|
||||
use std::convert::TryFrom;
|
||||
pub(crate) use strings::*;
|
||||
|
||||
/// Represents all the possible WIT instructions.
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum Instruction {
|
||||
/// The `arg.get` instruction.
|
||||
ArgumentGet {
|
||||
/// The argument index.
|
||||
index: u32,
|
||||
},
|
||||
|
||||
/// The `call-core` instruction.
|
||||
CallCore {
|
||||
/// The function index.
|
||||
function_index: u32,
|
||||
},
|
||||
|
||||
/// The `s8.from_i32` instruction.
|
||||
S8FromI32,
|
||||
|
||||
/// The `s8.from_i64` instruction.
|
||||
S8FromI64,
|
||||
|
||||
/// The `s16.from_i32` instruction.
|
||||
S16FromI32,
|
||||
|
||||
/// The `s16.from_i64` instruction.
|
||||
S16FromI64,
|
||||
|
||||
/// The `s32.from_i32` instruction.
|
||||
S32FromI32,
|
||||
|
||||
/// The `s32.from_i64` instruction.
|
||||
S32FromI64,
|
||||
|
||||
/// The `s64.from_i32` instruction.
|
||||
S64FromI32,
|
||||
|
||||
/// The `s64.from_i64` instruction.
|
||||
S64FromI64,
|
||||
|
||||
/// The `i32.from_s8` instruction.
|
||||
I32FromS8,
|
||||
|
||||
/// The `i32.from_s16` instruction.
|
||||
I32FromS16,
|
||||
|
||||
/// The `i32.from_s32` instruction.
|
||||
I32FromS32,
|
||||
|
||||
/// The `i32.from_s64` instruction.
|
||||
I32FromS64,
|
||||
|
||||
/// The `i64.from_s8` instruction.
|
||||
I64FromS8,
|
||||
|
||||
/// The `i64.from_s16` instruction.
|
||||
I64FromS16,
|
||||
|
||||
/// The `i64.from_s32` instruction.
|
||||
I64FromS32,
|
||||
|
||||
/// The `i64.from_s64` instruction.
|
||||
I64FromS64,
|
||||
|
||||
/// The `u8.from_i32` instruction.
|
||||
U8FromI32,
|
||||
|
||||
/// The `u8.from_i64` instruction.
|
||||
U8FromI64,
|
||||
|
||||
/// The `u16.from_i32` instruction.
|
||||
U16FromI32,
|
||||
|
||||
/// The `u16.from_i64` instruction.
|
||||
U16FromI64,
|
||||
|
||||
/// The `u32.from_i32` instruction.
|
||||
U32FromI32,
|
||||
|
||||
/// The `u32.from_i64` instruction.
|
||||
U32FromI64,
|
||||
|
||||
/// The `u64.from_i32` instruction.
|
||||
U64FromI32,
|
||||
|
||||
/// The `u64.from_i64` instruction.
|
||||
U64FromI64,
|
||||
|
||||
/// The `i32.from_u8` instruction.
|
||||
I32FromU8,
|
||||
|
||||
/// The `i32.from_u16` instruction.
|
||||
I32FromU16,
|
||||
|
||||
/// The `i32.from_u32` instruction.
|
||||
I32FromU32,
|
||||
|
||||
/// The `i32.from_u64` instruction.
|
||||
I32FromU64,
|
||||
|
||||
/// The `i64.from_u8` instruction.
|
||||
I64FromU8,
|
||||
|
||||
/// The `i64.from_u16` instruction.
|
||||
I64FromU16,
|
||||
|
||||
/// The `i64.from_u32` instruction.
|
||||
I64FromU32,
|
||||
|
||||
/// The `i64.from_u64` instruction.
|
||||
I64FromU64,
|
||||
|
||||
/// The `string.lift_memory` instruction.
|
||||
StringLiftMemory,
|
||||
|
||||
/// The `string.lower_memory` instruction.
|
||||
StringLowerMemory {
|
||||
/// The allocator function index.
|
||||
allocator_index: u32,
|
||||
},
|
||||
|
||||
/// The `string.size` instruction.
|
||||
StringSize,
|
||||
|
||||
/// The `record.lift` instruction.
|
||||
RecordLift {
|
||||
/// The type index of the record.
|
||||
type_index: u32,
|
||||
},
|
||||
|
||||
/// The `record.lower` instruction.
|
||||
RecordLower {
|
||||
/// The type index of the record.
|
||||
type_index: u32,
|
||||
},
|
||||
}
|
||||
|
||||
/// Just a short helper to map the error of a cast from an
|
||||
/// `InterfaceValue` to a native value.
|
||||
pub(crate) fn to_native<'a, T>(
|
||||
@ -31,9 +166,12 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use crate::interpreter::wasm::{
|
||||
self,
|
||||
values::{InterfaceType, InterfaceValue},
|
||||
use crate::{
|
||||
ast,
|
||||
interpreter::wasm::{
|
||||
self,
|
||||
values::{InterfaceType, InterfaceValue},
|
||||
},
|
||||
};
|
||||
use std::{cell::Cell, collections::HashMap, convert::TryInto, ops::Deref, rc::Rc};
|
||||
|
||||
@ -130,6 +268,7 @@ pub(crate) mod tests {
|
||||
pub(crate) exports: HashMap<String, Export>,
|
||||
pub(crate) locals_or_imports: HashMap<usize, LocalImport>,
|
||||
pub(crate) memory: Memory,
|
||||
pub(crate) wit_types: Vec<ast::Type>,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
@ -186,6 +325,15 @@ pub(crate) mod tests {
|
||||
hashmap
|
||||
},
|
||||
memory: Memory::new(vec![Cell::new(0); 128]),
|
||||
wit_types: vec![ast::Type::Record(ast::RecordType {
|
||||
fields: vec1![
|
||||
InterfaceType::I32,
|
||||
InterfaceType::Record(ast::RecordType {
|
||||
fields: vec1![InterfaceType::String, InterfaceType::F32],
|
||||
}),
|
||||
InterfaceType::I64,
|
||||
],
|
||||
})],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,5 +353,9 @@ pub(crate) mod tests {
|
||||
fn memory(&self, _index: usize) -> Option<&Memory> {
|
||||
Some(&self.memory)
|
||||
}
|
||||
|
||||
fn wit_type(&self, index: u32) -> Option<&ast::Type> {
|
||||
self.wit_types.get(index as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
377
lib/interface-types/src/interpreter/instructions/records.rs
Normal file
377
lib/interface-types/src/interpreter/instructions/records.rs
Normal file
@ -0,0 +1,377 @@
|
||||
use crate::{
|
||||
ast::{InterfaceType, RecordType, Type, TypeKind},
|
||||
errors::{InstructionError, InstructionErrorKind},
|
||||
interpreter::wasm::values::FlattenInterfaceValueIterator,
|
||||
interpreter::{
|
||||
stack::{Stack, Stackable},
|
||||
wasm::values::InterfaceValue,
|
||||
Instruction,
|
||||
},
|
||||
};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// Build an `InterfaceValue::Record` based on values on the stack.
|
||||
///
|
||||
/// To fill a record, every field `field_1` to `field_n` must get its
|
||||
/// value from the stack with `value_1` to `value_n`. It is not
|
||||
/// possible to use `Stack::pop` because the one-pass algorithm does
|
||||
/// not know exactly the number of values to read from the stack
|
||||
/// ahead-of-time, so `Stack::pop1` is used. It implies that values
|
||||
/// are read one after the other from the stack, in a natural reverse
|
||||
/// order, from `value_n` to `value_1`. Thus, the `values` vector must
|
||||
/// be filled from the end to the beginning. It is not safely possible
|
||||
/// to fill the `values` vector with empty values though (so that it
|
||||
/// is possible to access to last positions). So a `VecDeque` type is
|
||||
/// used: it is a double-ended queue.
|
||||
fn record_lift_(
|
||||
stack: &mut Stack<InterfaceValue>,
|
||||
record_type: &RecordType,
|
||||
) -> 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(values.into_iter().collect()))
|
||||
}
|
||||
|
||||
executable_instruction!(
|
||||
record_lift(type_index: u32, instruction: Instruction) -> _ {
|
||||
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
|
||||
}
|
||||
)),
|
||||
};
|
||||
|
||||
let record = record_lift_(&mut runtime.stack, &record_type)
|
||||
.map_err(|k| InstructionError::new(instruction, k))?;
|
||||
|
||||
runtime.stack.push(record);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
executable_instruction!(
|
||||
record_lower(type_index: u32, instruction: Instruction) -> _ {
|
||||
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 },
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ast::{RecordType, Type};
|
||||
|
||||
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(vec![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::Record(vec![
|
||||
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,
|
||||
wasm::values::{from_interface_values, InterfaceType, InterfaceValue},
|
||||
Instruction, Interpreter,
|
||||
};
|
||||
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(vec![
|
||||
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(vec![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::Record(vec![
|
||||
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(vec![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::Record(vec![
|
||||
InterfaceValue::String("Hello".to_string()),
|
||||
InterfaceValue::F32(2.),
|
||||
]),
|
||||
InterfaceValue::I64(3),
|
||||
])
|
||||
],
|
||||
instance: Instance::new(),
|
||||
stack: [
|
||||
InterfaceValue::Record(vec![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::Record(vec![
|
||||
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(vec![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::Record(vec![
|
||||
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"#,
|
||||
);
|
||||
}
|
@ -93,15 +93,15 @@ executable_instruction!(
|
||||
InstructionErrorKind::LocalOrImportSignatureMismatch {
|
||||
function_index: allocator_index,
|
||||
expected: (vec![InterfaceType::I32], vec![InterfaceType::I32]),
|
||||
received: (allocator.inputs().to_vec(), allocator.outputs().to_vec())
|
||||
}
|
||||
received: (allocator.inputs().to_vec(), allocator.outputs().to_vec()),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
let string = runtime.stack.pop1().ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::StackIsTooSmall { needed: 1 }
|
||||
InstructionErrorKind::StackIsTooSmall { needed: 1 },
|
||||
)
|
||||
})?;
|
||||
|
||||
@ -133,7 +133,7 @@ executable_instruction!(
|
||||
.ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::MemoryIsMissing { memory_index }
|
||||
InstructionErrorKind::MemoryIsMissing { memory_index },
|
||||
)
|
||||
})?
|
||||
.view();
|
||||
@ -153,26 +153,26 @@ executable_instruction!(
|
||||
executable_instruction!(
|
||||
string_size(instruction: Instruction) -> _ {
|
||||
move |runtime| -> _ {
|
||||
let value = runtime.stack.peek1().ok_or_else(|| {
|
||||
InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::StackIsTooSmall { needed: 1 },
|
||||
)
|
||||
})?;
|
||||
match runtime.stack.peek1() {
|
||||
Some(InterfaceValue::String(string)) => {
|
||||
let length = string.len() as i32;
|
||||
runtime.stack.push(InterfaceValue::I32(length));
|
||||
|
||||
if let InterfaceValue::String(string) = value {
|
||||
let length = string.len() as i32;
|
||||
runtime.stack.push(InterfaceValue::I32(length));
|
||||
Ok(())
|
||||
},
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InstructionError::new(
|
||||
Some(value) => Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::InvalidValueOnTheStack {
|
||||
expected_type: InterfaceType::String,
|
||||
received_type: value.into(),
|
||||
}
|
||||
))
|
||||
},
|
||||
)),
|
||||
|
||||
None => Err(InstructionError::new(
|
||||
instruction,
|
||||
InstructionErrorKind::StackIsTooSmall { needed: 1 },
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -310,7 +310,7 @@ mod tests {
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_string_memory =
|
||||
test_string_lower_memory =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::StringLowerMemory { allocator_index: 43 },
|
||||
@ -326,7 +326,7 @@ mod tests {
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_string_memory__roundtrip_with_memory_to_string =
|
||||
test_string__roundtrip =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::StringLowerMemory { allocator_index: 43 },
|
||||
@ -338,7 +338,7 @@ mod tests {
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_string_memory__allocator_does_not_exist =
|
||||
test_string_lower_memory__allocator_does_not_exist =
|
||||
instructions: [Instruction::StringLowerMemory { allocator_index: 43 }],
|
||||
invocation_inputs: [],
|
||||
instance: Instance { ..Default::default() },
|
||||
@ -346,7 +346,7 @@ mod tests {
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_string_memory__stack_is_too_small =
|
||||
test_string_lower_memory__stack_is_too_small =
|
||||
instructions: [
|
||||
Instruction::StringLowerMemory { allocator_index: 43 }
|
||||
// ^^ `43` expects 1 value on the stack, none is present
|
||||
@ -357,7 +357,7 @@ mod tests {
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_string_memory__failure_when_calling_the_allocator =
|
||||
test_string_lower_memory__failure_when_calling_the_allocator =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::StringLowerMemory { allocator_index: 153 }
|
||||
@ -381,7 +381,7 @@ mod tests {
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_string_memory__invalid_allocator_signature =
|
||||
test_string_lower_memory__invalid_allocator_signature =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::StringLowerMemory { allocator_index: 153 }
|
||||
|
@ -1,12 +1,11 @@
|
||||
//! A stack-based interpreter to execute instructions of WIT adapters.
|
||||
|
||||
mod instruction;
|
||||
mod instructions;
|
||||
pub mod stack;
|
||||
pub mod wasm;
|
||||
|
||||
use crate::errors::{InstructionResult, InterpreterResult};
|
||||
pub use instruction::Instruction;
|
||||
pub use instructions::Instruction;
|
||||
use stack::Stack;
|
||||
use std::{convert::TryFrom, marker::PhantomData};
|
||||
use wasm::values::InterfaceValue;
|
||||
@ -235,6 +234,13 @@ where
|
||||
instructions::string_lower_memory(*allocator_index, *instruction)
|
||||
}
|
||||
Instruction::StringSize => instructions::string_size(*instruction),
|
||||
|
||||
Instruction::RecordLift { type_index } => {
|
||||
instructions::record_lift(*type_index, *instruction)
|
||||
}
|
||||
Instruction::RecordLower { type_index } => {
|
||||
instructions::record_lower(*type_index, *instruction)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -2,5 +2,8 @@
|
||||
//! types, and traits —basically this is the part a runtime should
|
||||
//! take a look to use the `wasmer-interface-types` crate—.
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde;
|
||||
|
||||
pub mod structures;
|
||||
pub mod values;
|
||||
|
590
lib/interface-types/src/interpreter/wasm/serde/de.rs
Normal file
590
lib/interface-types/src/interpreter/wasm/serde/de.rs
Normal file
@ -0,0 +1,590 @@
|
||||
//! Provides a deserializer from WIT values to Rust value.
|
||||
|
||||
use crate::{
|
||||
ast::InterfaceType,
|
||||
interpreter::wasm::values::{FlattenInterfaceValueIterator, InterfaceValue},
|
||||
};
|
||||
use serde::{de, Deserialize};
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
iter::Peekable,
|
||||
};
|
||||
|
||||
/// Deserialize a set of `InterfaceValue`s to a type `T` that
|
||||
/// implements the `Deserialize` trait.
|
||||
///
|
||||
/// This is not a requirement to use WIT, but Serde provides an even
|
||||
/// nicer API to the user to rebuild its complex types from WIT
|
||||
/// values.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use wasmer_interface_types::interpreter::wasm::values::{
|
||||
/// InterfaceValue,
|
||||
/// from_interface_values,
|
||||
/// };
|
||||
/// use serde::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize, Debug, PartialEq)]
|
||||
/// struct S(i32, i64);
|
||||
///
|
||||
/// #[derive(Deserialize, Debug, PartialEq)]
|
||||
/// struct T<'a> {
|
||||
/// x: &'a str,
|
||||
/// s: S,
|
||||
/// y: f32,
|
||||
/// };
|
||||
///
|
||||
/// let values = vec![InterfaceValue::Record(vec![
|
||||
/// InterfaceValue::String("abc".to_string()),
|
||||
/// InterfaceValue::Record(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]),
|
||||
/// InterfaceValue::F32(3.),
|
||||
/// ])];
|
||||
/// let t = from_interface_values::<T>(&values).unwrap();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// t,
|
||||
/// T {
|
||||
/// x: "abc",
|
||||
/// s: S(1, 2),
|
||||
/// y: 3.,
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
pub fn from_interface_values<'a, T>(values: &'a [InterfaceValue]) -> Result<T, DeserializeError>
|
||||
where
|
||||
T: Deserialize<'a>,
|
||||
{
|
||||
let mut deserializer = Deserializer::new(values);
|
||||
let result = T::deserialize(&mut deserializer)?;
|
||||
|
||||
match deserializer.iterator.peek() {
|
||||
None => Ok(result),
|
||||
_ => Err(DeserializeError::InputNotEmpty),
|
||||
}
|
||||
}
|
||||
|
||||
/// The deserializer. The iterator iterates over `InterfaceValue`s,
|
||||
/// all flatten, see `FlattenInterfaceValueIterator`.
|
||||
struct Deserializer<'de> {
|
||||
iterator: Peekable<FlattenInterfaceValueIterator<'de>>,
|
||||
}
|
||||
|
||||
impl<'de> Deserializer<'de> {
|
||||
pub fn new(input: &'de [InterfaceValue]) -> Deserializer<'de> {
|
||||
Deserializer {
|
||||
iterator: FlattenInterfaceValueIterator::new(input).peekable(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! next {
|
||||
($method_name:ident, $variant:ident, $type:ty) => {
|
||||
fn $method_name(&mut self) -> Result<$type, DeserializeError> {
|
||||
match self.iterator.peek() {
|
||||
Some(InterfaceValue::$variant(value)) => {
|
||||
self.iterator.next();
|
||||
|
||||
Ok(*value)
|
||||
}
|
||||
|
||||
Some(wrong_value) => Err(DeserializeError::TypeMismatch {
|
||||
expected_type: InterfaceType::$variant,
|
||||
received_type: (*wrong_value).into(),
|
||||
}),
|
||||
|
||||
None => Err(DeserializeError::InputEmpty),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserializer<'de> {
|
||||
next!(next_s8, S8, i8);
|
||||
next!(next_s16, S16, i16);
|
||||
next!(next_s32, S32, i32);
|
||||
next!(next_s64, S64, i64);
|
||||
next!(next_u8, U8, u8);
|
||||
next!(next_u16, U16, u16);
|
||||
next!(next_u32, U32, u32);
|
||||
next!(next_u64, U64, u64);
|
||||
next!(next_f32, F32, f32);
|
||||
next!(next_f64, F64, f64);
|
||||
|
||||
fn next_string(&mut self) -> Result<&'de str, DeserializeError> {
|
||||
match self.iterator.peek() {
|
||||
Some(InterfaceValue::String(v)) => {
|
||||
self.iterator.next();
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
Some(wrong_value) => Err(DeserializeError::TypeMismatch {
|
||||
expected_type: InterfaceType::String,
|
||||
received_type: (*wrong_value).into(),
|
||||
}),
|
||||
|
||||
None => Err(DeserializeError::InputEmpty),
|
||||
}
|
||||
}
|
||||
|
||||
next!(next_i32, I32, i32);
|
||||
next!(next_i64, I64, i64);
|
||||
}
|
||||
|
||||
/// Represents an error while deserializing.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum DeserializeError {
|
||||
/// The input isn't empty, i.e. some values aren't deserialized.
|
||||
InputNotEmpty,
|
||||
|
||||
/// The input is too short!
|
||||
InputEmpty,
|
||||
|
||||
/// The current value hasn't the expected type.
|
||||
TypeMismatch {
|
||||
/// The expected type.
|
||||
expected_type: InterfaceType,
|
||||
|
||||
/// The received type.
|
||||
received_type: InterfaceType,
|
||||
},
|
||||
|
||||
/// Arbitrary message.
|
||||
Message(String),
|
||||
}
|
||||
|
||||
impl de::Error for DeserializeError {
|
||||
fn custom<T: Display>(msg: T) -> Self {
|
||||
Self::Message(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DeserializeError {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::InputNotEmpty => write!(formatter, "Unexpected input remaining"),
|
||||
Self::Message(ref msg) => write!(formatter, "{}", msg),
|
||||
Self::InputEmpty => write!(formatter, "Unexpected end of input"),
|
||||
Self::TypeMismatch {
|
||||
ref expected_type,
|
||||
ref received_type,
|
||||
} => write!(
|
||||
formatter,
|
||||
"Type mismatch detected, expected `{:?}` but received `{:?}`",
|
||||
expected_type, received_type
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for DeserializeError {}
|
||||
|
||||
impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
||||
type Error = DeserializeError;
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.iterator.peek() {
|
||||
Some(InterfaceValue::S8(_)) => self.deserialize_i8(visitor),
|
||||
Some(InterfaceValue::S16(_)) => self.deserialize_i16(visitor),
|
||||
Some(InterfaceValue::S32(_)) => self.deserialize_i32(visitor),
|
||||
Some(InterfaceValue::S64(_)) => self.deserialize_i64(visitor),
|
||||
Some(InterfaceValue::U8(_)) => self.deserialize_u8(visitor),
|
||||
Some(InterfaceValue::U16(_)) => self.deserialize_u16(visitor),
|
||||
Some(InterfaceValue::U32(_)) => self.deserialize_u32(visitor),
|
||||
Some(InterfaceValue::U64(_)) => self.deserialize_u64(visitor),
|
||||
Some(InterfaceValue::F32(_)) => self.deserialize_f32(visitor),
|
||||
Some(InterfaceValue::F64(_)) => self.deserialize_f64(visitor),
|
||||
Some(InterfaceValue::String(_)) => self.deserialize_string(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
|
||||
None => Err(DeserializeError::InputEmpty),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_bool<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
unimplemented!("`bool` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i8(self.next_s8()?)
|
||||
}
|
||||
|
||||
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i16(self.next_s16()?)
|
||||
}
|
||||
|
||||
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
// Both `InterfaceValue::S32` and `InterfaceValue::I32`
|
||||
// represent `i32`.
|
||||
visitor.visit_i32(self.next_s32().or_else(|_| self.next_i32())?)
|
||||
}
|
||||
|
||||
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
// Both `InterfaceValue::S64` and `InterfaceValue::I64`
|
||||
// represent `i64`.
|
||||
visitor.visit_i64(self.next_s64().or_else(|_| self.next_i64())?)
|
||||
}
|
||||
|
||||
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u8(self.next_u8()?)
|
||||
}
|
||||
|
||||
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u16(self.next_u16()?)
|
||||
}
|
||||
|
||||
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u32(self.next_u32()?)
|
||||
}
|
||||
|
||||
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u64(self.next_u64()?)
|
||||
}
|
||||
|
||||
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_f32(self.next_f32()?)
|
||||
}
|
||||
|
||||
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_f64(self.next_f64()?)
|
||||
}
|
||||
|
||||
fn deserialize_char<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`char` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_borrowed_str(self.next_string()?)
|
||||
}
|
||||
|
||||
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
self.deserialize_str(visitor)
|
||||
}
|
||||
|
||||
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.")
|
||||
}
|
||||
|
||||
fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`bytes` buffer is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn deserialize_option<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`option` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn deserialize_unit<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`unit` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn deserialize_unit_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`unit_struct` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_newtype_struct(self)
|
||||
}
|
||||
|
||||
fn deserialize_seq<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`seq` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn deserialize_tuple<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`tuple` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct<V>(
|
||||
mut self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_seq(Sequence::new(&mut self))
|
||||
}
|
||||
|
||||
fn deserialize_map<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`map` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
mut self,
|
||||
_name: &'static str,
|
||||
_fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_seq(Sequence::new(&mut self))
|
||||
}
|
||||
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variants: &'static [&'static str],
|
||||
_visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`enum` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn deserialize_identifier<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`identifier` is not supported by WIT for the moment.");
|
||||
}
|
||||
|
||||
fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
todo!("`ignored_any` is not implemented for the moment.")
|
||||
}
|
||||
}
|
||||
|
||||
struct Sequence<'a, 'de>
|
||||
where
|
||||
'de: 'a,
|
||||
{
|
||||
de: &'a mut Deserializer<'de>,
|
||||
}
|
||||
|
||||
impl<'a, 'de> Sequence<'a, 'de> {
|
||||
fn new(de: &'a mut Deserializer<'de>) -> Self {
|
||||
Sequence { de }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> de::SeqAccess<'de> for Sequence<'a, 'de> {
|
||||
type Error = DeserializeError;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
if self.de.iterator.peek().is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
seed.deserialize(&mut *self.de).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! deserialize_value {
|
||||
($test_name:ident, $variant:ident, $ty:ident, $value:expr) => {
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn $test_name() {
|
||||
let input = vec![InterfaceValue::$variant($value)];
|
||||
let output: $ty = $value;
|
||||
|
||||
assert_eq!(from_interface_values::<$ty>(&input).unwrap(), output);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
deserialize_value!(test_deserialize_value__s8, S8, i8, 42);
|
||||
deserialize_value!(test_deserialize_value__s16, S16, i16, 42);
|
||||
deserialize_value!(test_deserialize_value__s32, S32, i32, 42);
|
||||
deserialize_value!(test_deserialize_value__s64, S64, i64, 42);
|
||||
deserialize_value!(test_deserialize_value__u8, U8, u8, 42);
|
||||
deserialize_value!(test_deserialize_value__u16, U16, u16, 42);
|
||||
deserialize_value!(test_deserialize_value__u32, U32, u32, 42);
|
||||
deserialize_value!(test_deserialize_value__u64, U64, u64, 42);
|
||||
deserialize_value!(test_deserialize_value__f32, F32, f32, 42.);
|
||||
deserialize_value!(test_deserialize_value__f64, F32, f32, 42.);
|
||||
deserialize_value!(
|
||||
test_deserialize_value__string,
|
||||
String,
|
||||
String,
|
||||
"foo".to_string()
|
||||
);
|
||||
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn test_deserialize_value__str() {
|
||||
let foo = "foo".to_string();
|
||||
let values = vec![InterfaceValue::String(foo)];
|
||||
let input: &str = from_interface_values(&values).unwrap();
|
||||
let output: &str = "foo";
|
||||
|
||||
assert_eq!(input, output);
|
||||
}
|
||||
|
||||
deserialize_value!(test_deserialize_value__i32, I32, i32, 42);
|
||||
deserialize_value!(test_deserialize_value__i64, I64, i64, 42);
|
||||
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn test_deserialize_value__newtype_struct() {
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
struct S(i8);
|
||||
|
||||
let input = vec![InterfaceValue::Record(vec![InterfaceValue::S8(42)])];
|
||||
let output = S(42);
|
||||
|
||||
assert_eq!(from_interface_values::<S>(&input).unwrap(), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn test_deserialize_value__tuple_struct() {
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
struct S(i8, f32);
|
||||
|
||||
let input = vec![InterfaceValue::Record(vec![
|
||||
InterfaceValue::S8(7),
|
||||
InterfaceValue::F32(42.),
|
||||
])];
|
||||
let output = S(7, 42.);
|
||||
|
||||
assert_eq!(from_interface_values::<S>(&input).unwrap(), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn test_deserialize_value__struct() {
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
struct S {
|
||||
x: i8,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
let input = vec![InterfaceValue::Record(vec![
|
||||
InterfaceValue::S8(7),
|
||||
InterfaceValue::F32(42.),
|
||||
])];
|
||||
let output = S { x: 7, y: 42. };
|
||||
|
||||
assert_eq!(from_interface_values::<S>(&input).unwrap(), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn test_deserialize_value__struct_nested() {
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
struct Line {
|
||||
p1: Point,
|
||||
p2: Point,
|
||||
}
|
||||
|
||||
let input = vec![InterfaceValue::Record(vec![
|
||||
InterfaceValue::Record(vec![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::I32(2),
|
||||
InterfaceValue::I32(3),
|
||||
]),
|
||||
InterfaceValue::Record(vec![
|
||||
InterfaceValue::I32(4),
|
||||
InterfaceValue::I32(5),
|
||||
InterfaceValue::I32(6),
|
||||
]),
|
||||
])];
|
||||
let output = Line {
|
||||
p1: Point { x: 1, y: 2, z: 3 },
|
||||
p2: Point { x: 4, y: 5, z: 6 },
|
||||
};
|
||||
|
||||
assert_eq!(from_interface_values::<Line>(&input).unwrap(), output);
|
||||
}
|
||||
}
|
6
lib/interface-types/src/interpreter/wasm/serde/mod.rs
Normal file
6
lib/interface-types/src/interpreter/wasm/serde/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
//! Serde is not necessary to use WIT. It only provides a nicer API
|
||||
//! for the end-user to send or receive its complex types to/from WIT
|
||||
//! values, like `record` for instance.
|
||||
|
||||
pub(crate) mod de;
|
||||
pub(crate) mod ser;
|
570
lib/interface-types/src/interpreter/wasm/serde/ser.rs
Normal file
570
lib/interface-types/src/interpreter/wasm/serde/ser.rs
Normal file
@ -0,0 +1,570 @@
|
||||
//! Provides a serializer from Rust value to WIT values.
|
||||
|
||||
use crate::interpreter::wasm::values::InterfaceValue;
|
||||
use serde::{ser, Serialize};
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
/// Serialize a type `T` that implements the `Serialize` trait to an
|
||||
/// `InterfaceValue`.
|
||||
///
|
||||
/// This is not a requirement to use WIT, but Serde provides an even
|
||||
/// nicer API to the user to send its complex types to WIT.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use wasmer_interface_types::interpreter::wasm::values::{
|
||||
/// InterfaceValue,
|
||||
/// to_interface_value,
|
||||
/// };
|
||||
/// use serde::Serialize;
|
||||
///
|
||||
/// #[derive(Serialize)]
|
||||
/// struct S(i32, i64);
|
||||
///
|
||||
/// #[derive(Serialize)]
|
||||
/// struct T {
|
||||
/// x: String,
|
||||
/// s: S,
|
||||
/// y: f32,
|
||||
/// };
|
||||
///
|
||||
/// let input = T {
|
||||
/// x: "abc".to_string(),
|
||||
/// s: S(1, 2),
|
||||
/// y: 3.,
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// to_interface_value(&input).unwrap(),
|
||||
/// InterfaceValue::Record(vec![
|
||||
/// InterfaceValue::String("abc".to_string()),
|
||||
/// InterfaceValue::Record(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]),
|
||||
/// InterfaceValue::F32(3.),
|
||||
/// ]),
|
||||
/// );
|
||||
/// ```
|
||||
pub fn to_interface_value<T>(value: &T) -> Result<InterfaceValue, SerializeError>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let mut serializer = Serializer::new();
|
||||
value.serialize(&mut serializer)?;
|
||||
|
||||
if serializer.values.len() != 1 {
|
||||
Err(SerializeError::TransformationNotFinished)
|
||||
} else {
|
||||
let mut first_values = serializer.values.pop().unwrap(); // this `unwrap` is safe because we are sure the length is 1.
|
||||
|
||||
if first_values.len() != 1 {
|
||||
Err(SerializeError::TransformationNotFinished)
|
||||
} else {
|
||||
let first_value = first_values.pop().unwrap(); // this `unwrap` is safe because we are sure the length is 1.
|
||||
|
||||
Ok(first_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The serializer.
|
||||
struct Serializer {
|
||||
values: Vec<Vec<InterfaceValue>>,
|
||||
}
|
||||
|
||||
impl Serializer {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
values: vec![vec![]],
|
||||
}
|
||||
}
|
||||
|
||||
fn last(&mut self) -> &mut Vec<InterfaceValue> {
|
||||
self.values.last_mut().unwrap()
|
||||
}
|
||||
|
||||
fn push_with_capacity(&mut self, capacity: usize) {
|
||||
self.values.push(Vec::with_capacity(capacity));
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Result<Vec<InterfaceValue>, SerializeError> {
|
||||
// The first `vec` contains the final result. It is forbidden
|
||||
// to `pop` it as is.
|
||||
if self.values.len() < 2 {
|
||||
Err(SerializeError::InternalValuesCorrupted)
|
||||
} else {
|
||||
Ok(self.values.pop().unwrap()) // this `unwrap` is safe before `self.values` contains at least 2 items
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an error while serializing.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum SerializeError {
|
||||
/// The serialization still has pending values internally.
|
||||
TransformationNotFinished,
|
||||
|
||||
/// The internal values have been corrupted during the
|
||||
/// serialization.
|
||||
InternalValuesCorrupted,
|
||||
|
||||
/// Arbitrary message.
|
||||
Message(String),
|
||||
}
|
||||
|
||||
impl ser::Error for SerializeError {
|
||||
fn custom<T: Display>(msg: T) -> Self {
|
||||
Self::Message(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SerializeError {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::TransformationNotFinished => write!(
|
||||
formatter,
|
||||
"serialization still has pending values internally, something incorrect happened"
|
||||
),
|
||||
Self::InternalValuesCorrupted => write!(
|
||||
formatter,
|
||||
"the internal values have been corrutped during the serialization"
|
||||
),
|
||||
Self::Message(ref msg) => write!(formatter, "{}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for SerializeError {}
|
||||
|
||||
impl<'a> ser::Serializer for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = SerializeError;
|
||||
|
||||
type SerializeSeq = Self;
|
||||
type SerializeTuple = Self;
|
||||
type SerializeTupleStruct = Self;
|
||||
type SerializeTupleVariant = Self;
|
||||
type SerializeMap = Self;
|
||||
type SerializeStruct = Self;
|
||||
type SerializeStructVariant = Self;
|
||||
|
||||
fn serialize_bool(self, _value: bool) -> Result<Self::Ok, Self::Error> {
|
||||
unimplemented!("`bool` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_i8(self, value: i8) -> Result<Self::Ok, Self::Error> {
|
||||
self.last().push(value.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_i16(self, value: i16) -> Result<Self::Ok, Self::Error> {
|
||||
self.last().push(value.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_i32(self, value: i32) -> Result<Self::Ok, Self::Error> {
|
||||
self.last().push(value.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_i64(self, value: i64) -> Result<Self::Ok, Self::Error> {
|
||||
self.last().push(value.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_u8(self, value: u8) -> Result<Self::Ok, Self::Error> {
|
||||
self.last().push(value.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_u16(self, value: u16) -> Result<Self::Ok, Self::Error> {
|
||||
self.last().push(value.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_u32(self, value: u32) -> Result<Self::Ok, Self::Error> {
|
||||
self.last().push(value.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_u64(self, value: u64) -> Result<Self::Ok, Self::Error> {
|
||||
self.last().push(value.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_f32(self, value: f32) -> Result<Self::Ok, Self::Error> {
|
||||
self.last().push(value.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_f64(self, value: f64) -> Result<Self::Ok, Self::Error> {
|
||||
self.last().push(value.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_char(self, _value: char) -> Result<Self::Ok, Self::Error> {
|
||||
todo!("`char` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
|
||||
self.last().push(value.to_owned().into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
todo!("`bytes` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
self.serialize_unit()
|
||||
}
|
||||
|
||||
fn serialize_some<T>(self, _value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
todo!("`some` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
||||
todo!("`unit` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
|
||||
todo!("`unit_struct` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
) -> Result<Self::Ok, Self::Error> {
|
||||
todo!("`unit_variant` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
todo!("`newtype_variant` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||
todo!("`seq` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
||||
todo!("`tuple` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
||||
self.push_with_capacity(len);
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||
todo!("`tuple_variant` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
||||
todo!("`map` is not supported by WIT for the moment.")
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStruct, Self::Error> {
|
||||
self.push_with_capacity(len);
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStructVariant, Self::Error> {
|
||||
todo!("`struct_variant` is not supported by WIT for the moment.")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeSeq for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = SerializeError;
|
||||
|
||||
fn serialize_element<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeTuple for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = SerializeError;
|
||||
|
||||
fn serialize_element<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeTupleStruct for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = SerializeError;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
let record = InterfaceValue::Record(self.pop()?);
|
||||
self.last().push(record);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeTupleVariant for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = SerializeError;
|
||||
|
||||
fn serialize_field<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeMap for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = SerializeError;
|
||||
|
||||
fn serialize_key<T>(&mut self, _key: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_value<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeStruct for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = SerializeError;
|
||||
|
||||
fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
let record = InterfaceValue::Record(self.pop()?);
|
||||
self.last().push(record);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeStructVariant for &'a mut Serializer {
|
||||
type Ok = ();
|
||||
type Error = SerializeError;
|
||||
|
||||
fn serialize_field<T>(
|
||||
&mut self,
|
||||
_key: &'static str,
|
||||
_value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! serialize_value {
|
||||
($test_name:ident, $ty:ident, $variant:ident, $value:expr) => {
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn $test_name() {
|
||||
let input: $ty = $value;
|
||||
let output = InterfaceValue::$variant($value);
|
||||
|
||||
assert_eq!(to_interface_value(&input).unwrap(), output);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
serialize_value!(test_serialize_value__s8, i8, S8, 42);
|
||||
serialize_value!(test_serialize_value__s16, i16, S16, 42);
|
||||
serialize_value!(test_serialize_value__i32, i32, I32, 42);
|
||||
serialize_value!(test_serialize_value__i64, i64, I64, 42);
|
||||
serialize_value!(test_serialize_value__u8, u8, U8, 42);
|
||||
serialize_value!(test_serialize_value__u16, u16, U16, 42);
|
||||
serialize_value!(test_serialize_value__u32, u32, U32, 42);
|
||||
serialize_value!(test_serialize_value__u64, u64, U64, 42);
|
||||
serialize_value!(test_serialize_value__f32, f32, F32, 42.);
|
||||
serialize_value!(test_serialize_value__f64, f32, F32, 42.);
|
||||
serialize_value!(
|
||||
test_serialize_value__string,
|
||||
String,
|
||||
String,
|
||||
"foo".to_string()
|
||||
);
|
||||
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn test_serialize_value__newtype_struct() {
|
||||
#[derive(Serialize)]
|
||||
struct S(i8);
|
||||
|
||||
let input = S(42);
|
||||
let output = InterfaceValue::S8(42);
|
||||
|
||||
assert_eq!(to_interface_value(&input).unwrap(), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn test_serialize_value__tuple_struct() {
|
||||
#[derive(Serialize)]
|
||||
struct S(i8, f32);
|
||||
|
||||
let input = S(7, 42.);
|
||||
let output = InterfaceValue::Record(vec![InterfaceValue::S8(7), InterfaceValue::F32(42.)]);
|
||||
|
||||
assert_eq!(to_interface_value(&input).unwrap(), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn test_serialize_value__struct() {
|
||||
#[derive(Serialize)]
|
||||
struct S {
|
||||
x: i8,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
let input = S { x: 7, y: 42. };
|
||||
let output = InterfaceValue::Record(vec![InterfaceValue::S8(7), InterfaceValue::F32(42.)]);
|
||||
|
||||
assert_eq!(to_interface_value(&input).unwrap(), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn test_serialize_value__struct_nested() {
|
||||
#[derive(Serialize)]
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Line {
|
||||
p1: Point,
|
||||
p2: Point,
|
||||
}
|
||||
|
||||
let input = Line {
|
||||
p1: Point { x: 1, y: 2, z: 3 },
|
||||
p2: Point { x: 4, y: 5, z: 6 },
|
||||
};
|
||||
let output = InterfaceValue::Record(vec![
|
||||
InterfaceValue::Record(vec![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::I32(2),
|
||||
InterfaceValue::I32(3),
|
||||
]),
|
||||
InterfaceValue::Record(vec![
|
||||
InterfaceValue::I32(4),
|
||||
InterfaceValue::I32(5),
|
||||
InterfaceValue::I32(6),
|
||||
]),
|
||||
]);
|
||||
|
||||
assert_eq!(to_interface_value(&input).unwrap(), output);
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use super::values::{InterfaceType, InterfaceValue};
|
||||
use crate::{
|
||||
ast,
|
||||
interpreter::wasm::values::{InterfaceType, InterfaceValue},
|
||||
};
|
||||
use std::{cell::Cell, ops::Deref};
|
||||
|
||||
pub trait TypedIndex: Copy + Clone {
|
||||
@ -74,6 +77,7 @@ where
|
||||
fn export(&self, export_name: &str) -> Option<&E>;
|
||||
fn local_or_import<I: TypedIndex + LocalImportIndex>(&mut self, index: I) -> Option<&LI>;
|
||||
fn memory(&self, index: usize) -> Option<&M>;
|
||||
fn wit_type(&self, index: u32) -> Option<&ast::Type>;
|
||||
}
|
||||
|
||||
impl Export for () {
|
||||
@ -156,4 +160,8 @@ where
|
||||
fn local_or_import<I: TypedIndex + LocalImportIndex>(&mut self, _index: I) -> Option<&LI> {
|
||||
None
|
||||
}
|
||||
|
||||
fn wit_type(&self, _index: u32) -> Option<&ast::Type> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,57 @@
|
||||
#![allow(missing_docs)]
|
||||
//! Defines WIT values and associated operations.
|
||||
|
||||
pub use crate::ast::InterfaceType;
|
||||
use crate::errors::WasmValueNativeCastError;
|
||||
use std::convert::TryFrom;
|
||||
pub use crate::ast::{InterfaceType, RecordType};
|
||||
use crate::{errors::WasmValueNativeCastError, vec1::Vec1};
|
||||
use std::{convert::TryFrom, slice::Iter};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub use crate::interpreter::wasm::serde::{de::*, ser::*};
|
||||
|
||||
/// A WIT value.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum InterfaceValue {
|
||||
/// A 8-bits signed integer.
|
||||
S8(i8),
|
||||
|
||||
/// A 16-bits signed integer.
|
||||
S16(i16),
|
||||
|
||||
/// A 32-bits signed integer.
|
||||
S32(i32),
|
||||
|
||||
/// A 64-bits signed integer.
|
||||
S64(i64),
|
||||
|
||||
/// A 8-bits unsigned integer.
|
||||
U8(u8),
|
||||
|
||||
/// A 16-bits unsigned integer.
|
||||
U16(u16),
|
||||
|
||||
/// A 32-bits unsigned integer.
|
||||
U32(u32),
|
||||
|
||||
/// A 64-bits unsigned integer.
|
||||
U64(u64),
|
||||
|
||||
/// A 32-bits float.
|
||||
F32(f32),
|
||||
|
||||
/// A 64-bits float.
|
||||
F64(f64),
|
||||
|
||||
/// A string.
|
||||
String(String),
|
||||
|
||||
//Anyref(?),
|
||||
/// A 32-bits integer (as defined in WebAssembly core).
|
||||
I32(i32),
|
||||
|
||||
/// A 64-bits integer (as defiend in WebAssembly core).
|
||||
I64(i64),
|
||||
|
||||
/// A record.
|
||||
Record(Vec<InterfaceValue>),
|
||||
}
|
||||
|
||||
impl From<&InterfaceValue> for InterfaceType {
|
||||
@ -39,6 +71,7 @@ impl From<&InterfaceValue> for InterfaceType {
|
||||
//InterfaceValue::Anyref(_) => Self::Anyref,
|
||||
InterfaceValue::I32(_) => Self::I32,
|
||||
InterfaceValue::I64(_) => Self::I64,
|
||||
InterfaceValue::Record(values) => Self::Record(values.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,7 +82,18 @@ impl Default for InterfaceValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Vec<InterfaceValue>> for RecordType {
|
||||
fn from(values: &Vec<InterfaceValue>) -> Self {
|
||||
RecordType {
|
||||
fields: Vec1::new(values.iter().map(Into::into).collect())
|
||||
.expect("Record must have at least one field, zero given."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a native type supported by WIT.
|
||||
pub trait NativeType {
|
||||
/// The associated interface type that maps to the native type.
|
||||
const INTERFACE_TYPE: InterfaceType;
|
||||
}
|
||||
|
||||
@ -83,6 +127,8 @@ macro_rules! native {
|
||||
|
||||
native!(i8, S8);
|
||||
native!(i16, S16);
|
||||
native!(i32, I32);
|
||||
native!(i64, I64);
|
||||
native!(u8, U8);
|
||||
native!(u16, U16);
|
||||
native!(u32, U32);
|
||||
@ -90,5 +136,112 @@ native!(u64, U64);
|
||||
native!(f32, F32);
|
||||
native!(f64, F64);
|
||||
native!(String, String);
|
||||
native!(i32, I32);
|
||||
native!(i64, I64);
|
||||
|
||||
/// Iterates over a vector of `InterfaceValues` but flatten all the
|
||||
/// values. So `I32(1), Record([I32(2), I32(3)]), I32(4)` will be
|
||||
/// iterated like `I32(1), I32(2), I32(3), I32(4)`.
|
||||
pub(crate) struct FlattenInterfaceValueIterator<'a> {
|
||||
iterators: Vec<Iter<'a, InterfaceValue>>,
|
||||
}
|
||||
|
||||
impl<'a> FlattenInterfaceValueIterator<'a> {
|
||||
pub(crate) fn new(values: &'a [InterfaceValue]) -> Self {
|
||||
Self {
|
||||
iterators: vec![values.iter()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FlattenInterfaceValueIterator<'a> {
|
||||
type Item = &'a InterfaceValue;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.iterators.last_mut()?.next() {
|
||||
// End of the current iterator, go back to the previous
|
||||
// one.
|
||||
None => {
|
||||
self.iterators.pop();
|
||||
self.next()
|
||||
}
|
||||
|
||||
// Recursively iterate over the record.
|
||||
Some(InterfaceValue::Record(values)) => {
|
||||
self.iterators.push(values.iter());
|
||||
self.next()
|
||||
}
|
||||
|
||||
// A regular item.
|
||||
e @ Some(_) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! value_to_type {
|
||||
($test_name:ident, $ty:ident, $value:expr) => {
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn $test_name() {
|
||||
assert_eq!(
|
||||
InterfaceType::from(&InterfaceValue::$ty($value)),
|
||||
InterfaceType::$ty
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
value_to_type!(interface_type_from_interface_value__s8, S8, 42);
|
||||
value_to_type!(interface_type_from_interface_value__s16, S16, 42);
|
||||
value_to_type!(interface_type_from_interface_value__s32, S32, 42);
|
||||
value_to_type!(interface_type_from_interface_value__s64, S64, 42);
|
||||
value_to_type!(interface_type_from_interface_value__u8, U8, 42);
|
||||
value_to_type!(interface_type_from_interface_value__u16, U16, 42);
|
||||
value_to_type!(interface_type_from_interface_value__u32, U32, 42);
|
||||
value_to_type!(interface_type_from_interface_value__u64, U64, 42);
|
||||
value_to_type!(interface_type_from_interface_value__f32, F32, 42.);
|
||||
value_to_type!(interface_type_from_interface_value__f64, F64, 42.);
|
||||
value_to_type!(
|
||||
interface_type_from_interface_value__string,
|
||||
String,
|
||||
"foo".to_string()
|
||||
);
|
||||
value_to_type!(interface_type_from_interface_value__i32, I32, 42);
|
||||
value_to_type!(interface_type_from_interface_value__i64, I64, 42);
|
||||
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn interface_type_from_interface_value__record() {
|
||||
assert_eq!(
|
||||
InterfaceType::from(&InterfaceValue::Record(vec![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::S8(2)
|
||||
])),
|
||||
InterfaceType::Record(RecordType {
|
||||
fields: vec1![InterfaceType::I32, InterfaceType::S8]
|
||||
})
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
InterfaceType::from(&InterfaceValue::Record(vec![
|
||||
InterfaceValue::I32(1),
|
||||
InterfaceValue::Record(vec![
|
||||
InterfaceValue::String("a".to_string()),
|
||||
InterfaceValue::F64(42.)
|
||||
]),
|
||||
InterfaceValue::S8(2)
|
||||
])),
|
||||
InterfaceType::Record(RecordType {
|
||||
fields: vec1![
|
||||
InterfaceType::I32,
|
||||
InterfaceType::Record(RecordType {
|
||||
fields: vec1![InterfaceType::String, InterfaceType::F64]
|
||||
}),
|
||||
InterfaceType::S8
|
||||
]
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -57,3 +57,4 @@ pub mod decoders;
|
||||
pub mod encoders;
|
||||
pub mod errors;
|
||||
pub mod interpreter;
|
||||
pub mod vec1;
|
||||
|
@ -1,3 +1,28 @@
|
||||
/// This macro creates a `Vec1` by checking at compile-time that its
|
||||
/// invariant holds.
|
||||
#[allow(unused)]
|
||||
macro_rules! vec1 {
|
||||
($item:expr; 0) => {
|
||||
compile_error!("Cannot create an empty `Vec1`, it violates its invariant.")
|
||||
};
|
||||
|
||||
() => {
|
||||
compile_error!("Cannot create an empty `Vec1`, it violates its invariant.")
|
||||
};
|
||||
|
||||
($item:expr; $length:expr) => {
|
||||
{
|
||||
crate::vec1::Vec1::new(vec![$item; $length]).unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
($($item:expr),+ $(,)?) => {
|
||||
{
|
||||
crate::vec1::Vec1::new(vec![$($item),*]).unwrap()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This macro runs a parser, extracts the next input and the parser
|
||||
/// output, and positions the next input on `$input`.
|
||||
macro_rules! consume {
|
||||
|
62
lib/interface-types/src/vec1.rs
Normal file
62
lib/interface-types/src/vec1.rs
Normal file
@ -0,0 +1,62 @@
|
||||
//! `Vec1<T>` represents a non-empty `Vec<T>`.
|
||||
|
||||
use std::{
|
||||
error,
|
||||
fmt::{self, Debug},
|
||||
ops,
|
||||
};
|
||||
|
||||
/// `Vec1<T>` represents a non-empty `Vec<T>`. It derefs to `Vec<T>`
|
||||
/// directly.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Vec1<T>(Vec<T>)
|
||||
where
|
||||
T: Debug;
|
||||
|
||||
/// Represents the only error that can be emitted by `Vec1`, i.e. when
|
||||
/// the number of items is zero.
|
||||
#[derive(Debug)]
|
||||
pub struct EmptyVec;
|
||||
|
||||
impl error::Error for EmptyVec {}
|
||||
|
||||
impl fmt::Display for EmptyVec {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(formatter, "Vec1 must as least contain one item, zero given")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Vec1<T>
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
/// Creates a new non-empty vector, based on an inner `Vec<T>`. If
|
||||
/// the inner vector is empty, a `EmptyVec` error is returned.
|
||||
pub fn new(items: Vec<T>) -> Result<Self, EmptyVec> {
|
||||
if items.len() == 0 {
|
||||
Err(EmptyVec)
|
||||
} else {
|
||||
Ok(Self(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Vec1<T>
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(formatter, "{:?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ops::Deref for Vec1<T>
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
use wasmer_interface_types::{
|
||||
ast::*, decoders::binary::parse, encoders::binary::ToBytes, interpreter::Instruction,
|
||||
vec1::Vec1,
|
||||
};
|
||||
|
||||
/// Tests an AST to binary, then binary to AST roundtrip.
|
||||
@ -7,14 +8,17 @@ use wasmer_interface_types::{
|
||||
fn test_binary_encoding_decoding_roundtrip() {
|
||||
let original_ast = Interfaces {
|
||||
types: vec![
|
||||
Type {
|
||||
Type::Function {
|
||||
inputs: vec![],
|
||||
outputs: vec![],
|
||||
},
|
||||
Type {
|
||||
Type::Function {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::S32],
|
||||
},
|
||||
Type::Record(RecordType {
|
||||
fields: Vec1::new(vec![InterfaceType::String, InterfaceType::I32]).unwrap(),
|
||||
}),
|
||||
],
|
||||
imports: vec![Import {
|
||||
namespace: "a",
|
||||
|
Loading…
Reference in New Issue
Block a user