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:
bors[bot] 2020-04-06 06:17:45 +00:00 committed by GitHub
commit 4d33020b35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 2665 additions and 341 deletions

1
Cargo.lock generated
View File

@ -2855,6 +2855,7 @@ name = "wasmer-interface-types"
version = "0.16.2"
dependencies = [
"nom",
"serde",
"wast",
]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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"#,
);
}

View File

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

View File

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

View File

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

View 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);
}
}

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

View 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);
}
}

View File

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

View File

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

View File

@ -57,3 +57,4 @@ pub mod decoders;
pub mod encoders;
pub mod errors;
pub mod interpreter;
pub mod vec1;

View File

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

View 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
}
}

View File

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