From bbb4f1fc0f11677e2011c872d9c16dc0728477a7 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 24 Mar 2020 16:29:29 +0100 Subject: [PATCH 01/43] feat(interface-types) Introduce the record type. This patch updates the `Type` type to be an enum with 2 variants: `Function` and `Record`, resp. to represent: 1. `(@interface type (func (param i32 i32) (result string)))` 2. `(@interface type (record string i32))` This patch updates the binary encoder and decoder, along with the WAT encoder and decoder. --- lib/interface-types/src/ast.rs | 43 ++++++++++++++----- lib/interface-types/src/decoders/binary.rs | 34 +++++++++++++-- lib/interface-types/src/decoders/wat.rs | 48 ++++++++++++++++------ lib/interface-types/src/encoders/binary.rs | 27 +++++++++++- lib/interface-types/src/encoders/wat.rs | 23 ++++++++--- 5 files changed, 143 insertions(+), 32 deletions(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 9828043db0d..431a3b4410a 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -50,18 +50,41 @@ pub enum InterfaceType { I64, } -/// Represents a type signature. -/// -/// ```wasm,ignore -/// (@interface type (param i32 i32) (result string)) -/// ``` +/// Represents the kind of type. #[derive(PartialEq, Debug)] -pub struct Type { - /// Types for the parameters (`(param …)`). - pub inputs: Vec, +pub enum TypeKind { + /// A function type. + Function, - /// Types for the results (`(result …)`). - pub outputs: Vec, + /// 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, + + /// Types for the results (`(result …)`). + outputs: Vec, + }, + + /// A record type, like: + /// + /// ```wasm,ignore + /// (@interface type (record string i32)) + /// ``` + Record { + /// Types representing the fields. + fields: Vec, + }, } /// Represents an imported function. diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index f1f7f771272..3d53d1ff291 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -32,6 +32,19 @@ impl TryFrom for InterfaceType { } } +/// Parse a type kind. +impl TryFrom for TypeKind { + type Error = &'static str; + + fn try_from(code: u8) -> Result { + Ok(match code { + 0x00 => Self::Function, + 0x01 => Self::Record, + _ => return Err("Unknown type kind code."), + }) + } +} + /// Parse an interface kind. impl TryFrom for InterfaceKind { type Error = &'static str; @@ -234,10 +247,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, fields) = list(input, ty)?); + + types.push(Type::Record { fields }); + } + } } Ok((input, types)) diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 3e41d513bdd..3d1b149203c 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -13,6 +13,7 @@ mod keyword { // New keywords. custom_keyword!(implement); custom_keyword!(r#type = "type"); + custom_keyword!(record); // New types. custom_keyword!(s8); @@ -401,25 +402,48 @@ impl<'a> Parse<'a> for Type { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; - let (inputs, outputs) = parser.parens(|parser| { - parser.parse::()?; + let ty = parser.parens(|parser| { + let mut lookahead = parser.lookahead1(); + + if lookahead.peek::() { + parser.parse::()?; - let mut input_types = vec![]; - let mut output_types = vec![]; + let mut input_types = vec![]; + let mut output_types = vec![]; - while !parser.is_empty() { - let function_type = parser.parse::()?; + while !parser.is_empty() { + let function_type = parser.parse::()?; - match function_type { - FunctionType::Input(mut inputs) => input_types.append(&mut inputs), - FunctionType::Output(mut outputs) => output_types.append(&mut outputs), + 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::() { + parser.parse::()?; + + let fields = parser.parens(|parser| { + let mut fields = vec![]; + + while !parser.is_empty() { + fields.push(parser.parse()?); + } + + Ok(fields) + })?; + + Ok(Type::Record { fields }) + } else { + Err(lookahead.error()) + } })?; - Ok(Type { inputs, outputs }) + Ok(ty) } } diff --git a/lib/interface-types/src/encoders/binary.rs b/lib/interface-types/src/encoders/binary.rs index 7a2466952b8..e016ac012b6 100644 --- a/lib/interface-types/src/encoders/binary.rs +++ b/lib/interface-types/src/encoders/binary.rs @@ -112,6 +112,19 @@ where } } +/// Encode a `TypeKind` into bytes. +impl ToBytes 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), + } + } +} + /// Encode an `InterfaceKind` into bytes. impl ToBytes for InterfaceKind where @@ -136,8 +149,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 { fields } => { + TypeKind::Record.to_bytes(writer)?; + fields.to_bytes(writer)?; + } + } Ok(()) } diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 7856e59b26a..a67ce5be57c 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -167,11 +167,24 @@ 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 { fields } => format!( + r#"(@interface type (record {fields}))"#, + fields = fields + .iter() + .fold(String::new(), |mut accumulator, interface_type| { + accumulator.push(' '); + accumulator.push_str(&interface_type.to_string()); + accumulator + }), + ), + } } } From 734795c1f4e8b43cf9d1d4d969176dd5312847a8 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Mar 2020 11:18:24 +0100 Subject: [PATCH 02/43] test(interface-types) Test `Type::Record`. --- lib/interface-types/src/decoders/binary.rs | 26 ++++++++++++++----- lib/interface-types/src/decoders/wat.rs | 30 +++++++++++++--------- lib/interface-types/src/encoders/binary.rs | 23 ++++++++++++++--- lib/interface-types/src/encoders/wat.rs | 19 +++++++++----- lib/interface-types/tests/binary.rs | 7 +++-- 5 files changed, 74 insertions(+), 31 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 3d53d1ff291..df2db2ffcac 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -431,6 +431,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 @@ -464,7 +465,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], /// }], @@ -767,19 +768,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 { + fields: vec![InterfaceType::S32, InterfaceType::S32], + }, + ], )); assert_eq!(types::<()>(input), output); @@ -843,6 +854,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 @@ -876,7 +888,7 @@ mod tests { let output = Ok(( &[] as &[u8], Interfaces { - types: vec![Type { + types: vec![Type::Function { inputs: vec![InterfaceType::S8], outputs: vec![InterfaceType::S16], }], diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 3d1b149203c..7cbcfd5bbc5 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -427,15 +427,11 @@ impl<'a> Parse<'a> for Type { } else if lookahead.peek::() { parser.parse::()?; - let fields = parser.parens(|parser| { - let mut fields = vec![]; + let mut fields = vec![]; - while !parser.is_empty() { - fields.push(parser.parse()?); - } - - Ok(fields) - })?; + while !parser.is_empty() { + fields.push(parser.parse()?); + } Ok(Type::Record { fields }) } else { @@ -585,7 +581,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], /// }], @@ -782,9 +778,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], }); @@ -792,6 +788,16 @@ mod tests { assert_eq!(parser::parse::(&input).unwrap(), output); } + #[test] + fn test_type_record() { + let input = buffer(r#"(@interface type (record string i32))"#); + let output = Interface::Type(Type::Record { + fields: vec![InterfaceType::String, InterfaceType::I32], + }); + + assert_eq!(parser::parse::(&input).unwrap(), output); + } + #[test] fn test_export() { let input = buffer(r#"(@interface export "foo" (func 0))"#); @@ -862,7 +868,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], }], diff --git a/lib/interface-types/src/encoders/binary.rs b/lib/interface-types/src/encoders/binary.rs index e016ac012b6..e012fa13d69 100644 --- a/lib/interface-types/src/encoders/binary.rs +++ b/lib/interface-types/src/encoders/binary.rs @@ -451,13 +451,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 @@ -467,6 +468,21 @@ mod tests { ); } + #[test] + fn test_type_record() { + assert_to_bytes!( + Type::Record { + fields: vec![InterfaceType::I32, InterfaceType::I64], + }, + &[ + 0x01, // record type + 0x02, // list of 2 items + 0x0c, // I32 + 0x0d, // I64 + ] + ); + } + #[test] fn test_import() { assert_to_bytes!( @@ -504,7 +520,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], }], @@ -529,6 +545,7 @@ mod tests { &[ 0x00, // type section 0x01, // 1 type + 0x00, // function type 0x01, // list of 1 item 0x00, // S8 0x01, // list of 1 item diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index a67ce5be57c..8cc6679cdd1 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -10,7 +10,7 @@ //! }; //! //! let input: String = (&Interfaces { -//! types: vec![Type { +//! types: vec![Type::Function { //! inputs: vec![InterfaceType::I32], //! outputs: vec![InterfaceType::S8], //! }], @@ -175,7 +175,7 @@ impl<'input> ToString for &Type { ), Type::Record { fields } => format!( - r#"(@interface type (record {fields}))"#, + r#"(@interface type (record{fields}))"#, fields = fields .iter() .fold(String::new(), |mut accumulator, interface_type| { @@ -453,26 +453,30 @@ mod tests { #[test] fn test_types() { let inputs: Vec = 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 { + fields: vec![InterfaceType::String, InterfaceType::I32], + }) + .to_string(), ]; let outputs = vec![ r#"(@interface type (func @@ -483,6 +487,7 @@ mod tests { r#"(@interface type (func (result i32)))"#, r#"(@interface type (func))"#, + r#"(@interface type (record string i32))"#, ]; assert_eq!(inputs, outputs); @@ -529,7 +534,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], }], diff --git a/lib/interface-types/tests/binary.rs b/lib/interface-types/tests/binary.rs index 0e89173924f..96d1c774401 100644 --- a/lib/interface-types/tests/binary.rs +++ b/lib/interface-types/tests/binary.rs @@ -7,14 +7,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 { + fields: vec![InterfaceType::String, InterfaceType::I32], + }, ], imports: vec![Import { namespace: "a", From 3c02c501edd3fa24f4d1ba6527ab12d109627ca1 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Mar 2020 11:24:10 +0100 Subject: [PATCH 03/43] chore(interface-types) Move the `instruction.rs` module in `instructions/mod.rs`. --- .../src/interpreter/instruction.rs | 125 ----------------- .../src/interpreter/instructions/mod.rs | 129 +++++++++++++++++- lib/interface-types/src/interpreter/mod.rs | 3 +- 3 files changed, 126 insertions(+), 131 deletions(-) delete mode 100644 lib/interface-types/src/interpreter/instruction.rs diff --git a/lib/interface-types/src/interpreter/instruction.rs b/lib/interface-types/src/interpreter/instruction.rs deleted file mode 100644 index 712a6ea5882..00000000000 --- a/lib/interface-types/src/interpreter/instruction.rs +++ /dev/null @@ -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: usize, - }, - - /// 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, -} diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 9d7f0722768..42e2dad4d9b 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -5,10 +5,7 @@ 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; @@ -16,6 +13,130 @@ pub(crate) use numbers::*; 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: usize, + }, + + /// 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, +} + /// 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>( diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 22252aea71b..408d06c0811 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -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; From bd9226eb68f9fb45a32d21e1ead618382267b1aa Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Mar 2020 13:17:36 +0100 Subject: [PATCH 04/43] feat(interface-types) Introduce `RecordType` for `InterfaceType` and `Type`. The `Type::Record` variant now is defined by `RecordType`. In addition, `InterfaceType` has a new variant: `Record`, that is also defined by `RecordType`. Encoders and decoders are updated to consider `RecordType`, which removes code duplication and simplify code. --- lib/interface-types/src/ast.rs | 15 +- lib/interface-types/src/decoders/binary.rs | 206 +++++++++++++-------- lib/interface-types/src/decoders/wat.rs | 92 +++++++-- lib/interface-types/src/encoders/binary.rs | 69 ++++++- lib/interface-types/src/encoders/wat.rs | 115 +++++++++--- lib/interface-types/tests/binary.rs | 4 +- 6 files changed, 372 insertions(+), 129 deletions(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 431a3b4410a..f0e03086ee8 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -48,6 +48,16 @@ pub enum InterfaceType { /// A 64-bits integer (as defiend in WebAssembly core). I64, + + /// A record. + Record(RecordType), +} + +/// Representing a record type. +#[derive(PartialEq, Debug, Clone)] +pub struct RecordType { + /// Types representing the fields. + pub fields: Vec, } /// Represents the kind of type. @@ -81,10 +91,7 @@ pub enum Type { /// ```wasm,ignore /// (@interface type (record string i32)) /// ``` - Record { - /// Types representing the fields. - fields: Vec, - }, + Record(RecordType), } /// Represents an imported function. diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index df2db2ffcac..2ae47a9680a 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -7,31 +7,6 @@ use nom::{ }; use std::{convert::TryFrom, str}; -/// Parse an `InterfaceType`. -impl TryFrom for InterfaceType { - type Error = &'static str; - - fn try_from(code: u8) -> Result { - 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."), - }) - } -} - /// Parse a type kind. impl TryFrom for TypeKind { type Error = &'static str; @@ -95,6 +70,51 @@ 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 an 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 })) +} + /// Parse a UTF-8 string. fn string<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], @@ -144,22 +164,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], @@ -261,9 +265,9 @@ fn types<'input, E: ParseError<&'input [u8]>>( } TypeKind::Record => { - consume!((input, fields) = list(input, ty)?); + consume!((input, record_type) = record_type(input)?); - types.push(Type::Record { fields }); + types.push(Type::Record(record_type)); } } } @@ -575,40 +579,10 @@ mod tests { ); } - #[test] - fn test_string() { - let input = &[ - 0x03, // string of 3 bytes - 0x61, // "a" - 0x62, // "b" - 0x63, // "c" - 0x64, 0x65, - ]; - let output = Ok((&[0x64, 0x65][..], "abc")); - - assert_eq!(string::<()>(input), output); - } - - #[test] - fn test_list() { - let input = &[ - 0x02, // list of 2 items - 0x01, // string of 1 byte - 0x61, // "a" - 0x02, // string of 2 bytes - 0x62, // "b" - 0x63, // "c" - 0x07, - ]; - 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 + 0x0f, // list of 15 items 0x00, // S8 0x01, // S16 0x02, // S32 @@ -623,6 +597,7 @@ mod tests { 0x0b, // Anyref 0x0c, // I32 0x0d, // I64 + 0x0e, 0x01, 0x02, // Record 0x01, ]; let output = Ok(( @@ -642,10 +617,85 @@ mod tests { InterfaceType::Anyref, InterfaceType::I32, InterfaceType::I64, + InterfaceType::Record(RecordType { + fields: vec![InterfaceType::S32], + }), ], )); - assert_eq!(list::(input, ty), output); + 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: vec![InterfaceType::String], + }, + RecordType { + fields: vec![InterfaceType::String, InterfaceType::I32], + }, + RecordType { + fields: vec![ + InterfaceType::String, + InterfaceType::Record(RecordType { + fields: vec![InterfaceType::I32, InterfaceType::I32], + }), + InterfaceType::F64, + ], + }, + ], + )); + + assert_eq!(list::<_, ()>(input, record_type), output); + } + + #[test] + fn test_string() { + let input = &[ + 0x03, // string of 3 bytes + 0x61, // "a" + 0x62, // "b" + 0x63, // "c" + 0x64, 0x65, + ]; + let output = Ok((&[0x64, 0x65][..], "abc")); + + assert_eq!(string::<()>(input), output); + } + + #[test] + fn test_list() { + let input = &[ + 0x02, // list of 2 items + 0x01, // string of 1 byte + 0x61, // "a" + 0x02, // string of 2 bytes + 0x62, // "b" + 0x63, // "c" + 0x07, + ]; + let output = Ok((&[0x07][..], vec!["a", "bc"])); + + assert_eq!(list::<_, ()>(input, string), output); } #[test] @@ -734,7 +784,7 @@ mod tests { ], )); - assert_eq!(list::(input, instruction), output); + assert_eq!(list::<_, ()>(input, instruction), output); } #[test] @@ -787,9 +837,9 @@ mod tests { inputs: vec![InterfaceType::S32, InterfaceType::S32], outputs: vec![InterfaceType::S32], }, - Type::Record { + Type::Record(RecordType { fields: vec![InterfaceType::S32, InterfaceType::S32], - }, + }), ], )); diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 7cbcfd5bbc5..fb816306846 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -14,6 +14,7 @@ mod keyword { custom_keyword!(implement); custom_keyword!(r#type = "type"); custom_keyword!(record); + custom_keyword!(field); // New types. custom_keyword!(s8); @@ -126,12 +127,32 @@ impl Parse<'_> for InterfaceType { parser.parse::()?; Ok(InterfaceType::I64) + } else if lookahead.peek::() { + Ok(InterfaceType::Record(parser.parse()?)) } else { Err(lookahead.error()) } } } +impl Parse<'_> for RecordType { + fn parse(parser: Parser<'_>) -> Result { + parser.parse::()?; + + let mut fields = vec![]; + + while !parser.is_empty() { + fields.push(parser.parens(|parser| { + parser.parse::()?; + + parser.parse() + })?); + } + + Ok(RecordType { fields }) + } +} + impl<'a> Parse<'a> for Instruction { #[allow(clippy::cognitive_complexity)] fn parse(parser: Parser<'a>) -> Result { @@ -425,15 +446,7 @@ impl<'a> Parse<'a> for Type { outputs: output_types, }) } else if lookahead.peek::() { - parser.parse::()?; - - let mut fields = vec![]; - - while !parser.is_empty() { - fields.push(parser.parse()?); - } - - Ok(Type::Record { fields }) + Ok(Type::Record(parser.parse()?)) } else { Err(lookahead.error()) } @@ -622,8 +635,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, @@ -640,6 +666,9 @@ mod tests { InterfaceType::Anyref, InterfaceType::I32, InterfaceType::I64, + InterfaceType::Record(RecordType { + fields: vec![InterfaceType::String], + }), ]; assert_eq!(inputs.len(), outputs.len()); @@ -652,6 +681,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: vec![InterfaceType::String], + }, + RecordType { + fields: vec![InterfaceType::String, InterfaceType::I32], + }, + RecordType { + fields: vec![ + InterfaceType::String, + InterfaceType::Record(RecordType { + fields: vec![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::(&buffer(input)).unwrap(), + output + ); + } + } + #[test] fn test_instructions() { let inputs = vec![ @@ -790,10 +854,10 @@ mod tests { #[test] fn test_type_record() { - let input = buffer(r#"(@interface type (record string i32))"#); - let output = Interface::Type(Type::Record { + let input = buffer(r#"(@interface type (record (field string) (field i32)))"#); + let output = Interface::Type(Type::Record(RecordType { fields: vec![InterfaceType::String, InterfaceType::I32], - }); + })); assert_eq!(parser::parse::(&input).unwrap(), output); } diff --git a/lib/interface-types/src/encoders/binary.rs b/lib/interface-types/src/encoders/binary.rs index e012fa13d69..2be2dad6c44 100644 --- a/lib/interface-types/src/encoders/binary.rs +++ b/lib/interface-types/src/encoders/binary.rs @@ -108,10 +108,24 @@ 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 ToBytes 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 ToBytes for TypeKind where @@ -156,7 +170,7 @@ where outputs.to_bytes(writer)?; } - Type::Record { fields } => { + Type::Record(RecordType { fields }) => { TypeKind::Record.to_bytes(writer)?; fields.to_bytes(writer)?; } @@ -422,6 +436,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: vec![InterfaceType::String] + }), + &[0x0e, 0x01, 0x0a] + ); + } + + #[test] + fn test_record_type() { + assert_to_bytes!( + RecordType { + fields: vec![InterfaceType::String] + }, + &[ + 0x01, // 1 field + 0x0a, // String + ] + ); + assert_to_bytes!( + RecordType { + fields: vec![InterfaceType::String, InterfaceType::I32] + }, + &[ + 0x02, // 2 fields + 0x0a, // String + 0x0c, // I32 + ] + ); + assert_to_bytes!( + RecordType { + fields: vec![ + InterfaceType::String, + InterfaceType::Record(RecordType { + fields: vec![InterfaceType::I32, InterfaceType::I32], + }), + InterfaceType::F64, + ], + }, + &[ + 0x03, // 3 fields + 0x0a, // String + 0x0e, // Record + 0x02, // 2 fields + 0x0c, // I32 + 0x0c, // I32 + 0x09, // F64 + ] + ); } #[test] @@ -471,9 +534,9 @@ mod tests { #[test] fn test_type_record() { assert_to_bytes!( - Type::Record { + Type::Record(RecordType { fields: vec![InterfaceType::I32, InterfaceType::I64], - }, + }), &[ 0x01, // record type 0x02, // list of 2 items diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 8cc6679cdd1..772bf89935f 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -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 { @@ -174,15 +191,9 @@ impl<'input> ToString for &Type { outputs = output_types_to_result(&outputs), ), - Type::Record { fields } => format!( - r#"(@interface type (record{fields}))"#, - fields = fields - .iter() - .fold(String::new(), |mut accumulator, interface_type| { - accumulator.push(' '); - accumulator.push_str(&interface_type.to_string()); - accumulator - }), + Type::Record(record_type) => format!( + r#"(@interface type ({record_type}))"#, + record_type = record_type.to_string(), ), } } @@ -354,10 +365,58 @@ mod tests { (&InterfaceType::Anyref).to_string(), (&InterfaceType::I32).to_string(), (&InterfaceType::I64).to_string(), + (&InterfaceType::Record(RecordType { + fields: vec![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: vec![InterfaceType::String], + }) + .to_string(), + (&RecordType { + fields: vec![InterfaceType::String, InterfaceType::I32], + }) + .to_string(), + (&RecordType { + fields: vec![ + InterfaceType::String, + InterfaceType::Record(RecordType { + fields: vec![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); @@ -473,9 +532,9 @@ mod tests { outputs: vec![], }) .to_string(), - (&Type::Record { + (&Type::Record(RecordType { fields: vec![InterfaceType::String, InterfaceType::I32], - }) + })) .to_string(), ]; let outputs = vec![ @@ -487,7 +546,7 @@ mod tests { r#"(@interface type (func (result i32)))"#, r#"(@interface type (func))"#, - r#"(@interface type (record string i32))"#, + r#"(@interface type (record (field string) (field i32)))"#, ]; assert_eq!(inputs, outputs); diff --git a/lib/interface-types/tests/binary.rs b/lib/interface-types/tests/binary.rs index 96d1c774401..2238c972a5a 100644 --- a/lib/interface-types/tests/binary.rs +++ b/lib/interface-types/tests/binary.rs @@ -15,9 +15,9 @@ fn test_binary_encoding_decoding_roundtrip() { inputs: vec![InterfaceType::I32, InterfaceType::I32], outputs: vec![InterfaceType::S32], }, - Type::Record { + Type::Record(RecordType { fields: vec![InterfaceType::String, InterfaceType::I32], - }, + }), ], imports: vec![Import { namespace: "a", From a99ae6bdb22215501f1ddf6109a942bfba134d21 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 30 Mar 2020 08:32:31 +0200 Subject: [PATCH 05/43] feat(interface-types) Add the `Record` WIT value. --- .../src/interpreter/wasm/values.rs | 115 +++++++++++++++++- 1 file changed, 111 insertions(+), 4 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 484fa1dae58..a2cb8326019 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -1,25 +1,54 @@ -#![allow(missing_docs)] +//! Defines WIT values and associated operations. -pub use crate::ast::InterfaceType; +pub use crate::ast::{InterfaceType, RecordType}; use crate::errors::WasmValueNativeCastError; use std::convert::TryFrom; +/// 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), } impl From<&InterfaceValue> for InterfaceType { @@ -39,6 +68,12 @@ impl From<&InterfaceValue> for InterfaceType { //InterfaceValue::Anyref(_) => Self::Anyref, InterfaceValue::I32(_) => Self::I32, InterfaceValue::I64(_) => Self::I64, + InterfaceValue::Record(values) => Self::Record(RecordType { + fields: values + .iter() + .map(Into::into) + .collect::>(), + }), } } } @@ -49,7 +84,9 @@ impl Default for InterfaceValue { } } +/// 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 +120,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 +129,73 @@ native!(u64, U64); native!(f32, F32); native!(f64, F64); native!(String, String); -native!(i32, I32); -native!(i64, I64); + +#[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: vec![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: vec![ + InterfaceType::I32, + InterfaceType::Record(RecordType { + fields: vec![InterfaceType::String, InterfaceType::F64] + }), + InterfaceType::S8 + ] + }) + ); + } +} From 154dcba42c60bd837ef970025f7fe2568129d0ae Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 30 Mar 2020 15:42:35 +0200 Subject: [PATCH 06/43] feat(interface-types) Implement Serde deserializing for WIT records to Rust values. WIT values are native Rust values. But records are represented as a vector of WIT values. In order to provide a super neat API to the user, Serde is used to deserialize this vector of WIT values to a large variety of Rust values. --- lib/interface-types/Cargo.toml | 1 + .../src/interpreter/wasm/mod.rs | 1 + .../interpreter/wasm/specific_values/mod.rs | 1 + .../wasm/specific_values/record.rs | 656 ++++++++++++++++++ .../src/interpreter/wasm/values.rs | 1 + 5 files changed, 660 insertions(+) create mode 100644 lib/interface-types/src/interpreter/wasm/specific_values/mod.rs create mode 100644 lib/interface-types/src/interpreter/wasm/specific_values/record.rs diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index 3bcebd33740..f19b6ee34d0 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -10,3 +10,4 @@ edition = "2018" [dependencies] nom = "5.1" wast = "8.0" +serde = { version = "1.0", features = ["derive"] } \ No newline at end of file diff --git a/lib/interface-types/src/interpreter/wasm/mod.rs b/lib/interface-types/src/interpreter/wasm/mod.rs index 1edccc59268..e99321c8501 100644 --- a/lib/interface-types/src/interpreter/wasm/mod.rs +++ b/lib/interface-types/src/interpreter/wasm/mod.rs @@ -2,5 +2,6 @@ //! types, and traits —basically this is the part a runtime should //! take a look to use the `wasmer-interface-types` crate—. +mod specific_values; pub mod structures; pub mod values; diff --git a/lib/interface-types/src/interpreter/wasm/specific_values/mod.rs b/lib/interface-types/src/interpreter/wasm/specific_values/mod.rs new file mode 100644 index 00000000000..2066636cab0 --- /dev/null +++ b/lib/interface-types/src/interpreter/wasm/specific_values/mod.rs @@ -0,0 +1 @@ +pub mod record; diff --git a/lib/interface-types/src/interpreter/wasm/specific_values/record.rs b/lib/interface-types/src/interpreter/wasm/specific_values/record.rs new file mode 100644 index 00000000000..e7b0ddab054 --- /dev/null +++ b/lib/interface-types/src/interpreter/wasm/specific_values/record.rs @@ -0,0 +1,656 @@ +//! Specific operations on records. + +#![allow(missing_docs)] + +use crate::interpreter::wasm::values::InterfaceValue; +use serde::{ + de::{self, DeserializeSeed, SeqAccess, Visitor}, + Deserialize, +}; +use std::{ + fmt::{self, Display}, + iter::Peekable, + slice::Iter, +}; + +/// Iterates over a vector of `InterfaceValues` but flatten all the +/// values for Serde. It means that the ideal representation for Serde +/// regarding our implementation is to get all values flatten. So +/// `I32(1), Record([I32(2), I32(3)]), I32(4)` must be iterated like +/// `I32(1), I32(2), I32(3), I32(4)`. +struct InterfaceValueIterator<'a> { + iterators: Vec>, +} + +impl<'a> InterfaceValueIterator<'a> { + fn new(values: &'a [InterfaceValue]) -> Self { + Self { + iterators: vec![values.iter()], + } + } +} + +impl<'a> Iterator for InterfaceValueIterator<'a> { + type Item = &'a InterfaceValue; + + fn next(&mut self) -> Option { + if self.iterators.is_empty() { + return None; + } + + let index = self.iterators.len() - 1; + + match self.iterators[index].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, + } + } +} + +struct Deserializer<'de> { + iterator: Peekable>, +} + +impl<'de> Deserializer<'de> { + pub fn from_values(input: &'de [InterfaceValue]) -> Deserializer<'de> { + Deserializer { + iterator: InterfaceValueIterator::new(input).peekable(), + } + } +} + +macro_rules! next { + ($method_name:ident, $variant:ident, $type:ty) => { + fn $method_name(&mut self) -> Result<$type, Error> { + match self.iterator.peek() { + Some(InterfaceValue::$variant(v)) => { + self.iterator.next(); + + Ok(*v) + } + + Some(_) => Err(Error::TypeMismatch), + + None => Err(Error::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, Error> { + match self.iterator.peek() { + Some(InterfaceValue::String(v)) => { + self.iterator.next(); + + Ok(v) + } + + Some(_) => Err(Error::TypeMismatch), + + None => Err(Error::InputEmpty), + } + } + + next!(next_i32, I32, i32); + next!(next_i64, I64, i64); +} + +pub fn from_values<'a, T>(s: &'a [InterfaceValue]) -> Result +where + T: Deserialize<'a>, +{ + let mut deserializer = Deserializer::from_values(s); + let t = T::deserialize(&mut deserializer)?; + + if deserializer.iterator.peek().is_none() { + Ok(t) + } else { + Err(Error::InputNotEmpty) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Error { + InputNotEmpty, + InputEmpty, + TypeMismatch, + Message(String), +} + +impl de::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(std::error::Error::description(self)) + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::InputNotEmpty => "unexpected input remaining", + Error::Message(ref msg) => msg, + Error::InputEmpty => "unexpected end of input", + Error::TypeMismatch => "type mismatch detected", + } + } +} + +impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: 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 flatten."), // already flatten + None => Err(Error::InputEmpty), + } + } + + fn deserialize_bool(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + unimplemented!("`bool` is not supported by WIT for the moment.") + } + + fn deserialize_i8(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_i8(self.next_s8()?) + } + + fn deserialize_i16(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_i16(self.next_s16()?) + } + + fn deserialize_i32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + // Both `InterfaceValue::S32` and `InterfaceValue::I32` + // represent `i32`. + visitor.visit_i32(self.next_s32().or(self.next_i32())?) + } + + fn deserialize_i64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + // Both `InterfaceValue::S64` and `InterfaceValue::I64` + // represent `i64`. + visitor.visit_i64(self.next_s64().or(self.next_i64())?) + } + + fn deserialize_u8(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_u8(self.next_u8()?) + } + + fn deserialize_u16(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_u16(self.next_u16()?) + } + + fn deserialize_u32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_u32(self.next_u32()?) + } + + fn deserialize_u64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_u64(self.next_u64()?) + } + + fn deserialize_f32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_f32(self.next_f32()?) + } + + fn deserialize_f64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_f64(self.next_f64()?) + } + + fn deserialize_char(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!("`char` is not supported by WIT for the moment.") + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_borrowed_str(self.next_string()?) + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_bytes(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!("`bytes` is not supported by WIT for the moment.") + } + + fn deserialize_byte_buf(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!("`bytes` buffer is not supported by WIT for the moment.") + } + + fn deserialize_option(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!("`option` is not supported by WIT for the moment.") + } + + fn deserialize_unit(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!("`unit` is not supported by WIT for the moment.") + } + + fn deserialize_unit_struct( + self, + _name: &'static str, + _visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + todo!("`unit_struct` is not supported by WIT for the moment.") + } + + fn deserialize_newtype_struct( + self, + _name: &'static str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_seq(mut self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_seq(Sequence::new(&mut self)) + } + + fn deserialize_tuple(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_map(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!("`map` is not supported by WIT for the moment.") + } + + fn deserialize_struct( + self, + _name: &'static str, + _fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + _visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + todo!("`enum` is not supported by WIT for the moment.") + } + + fn deserialize_identifier(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + todo!("`identifier` is not supported by WIT for the moment."); + } + + fn deserialize_ignored_any(self, _visitor: V) -> Result + where + V: 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> SeqAccess<'de> for Sequence<'a, 'de> { + type Error = Error; + + fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> + where + T: 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::*; + use std::convert::TryInto; + + macro_rules! try_into { + ($ty:ty) => { + impl TryInto<$ty> for Vec { + type Error = Error; + + fn try_into(self) -> Result<$ty, Self::Error> { + from_values(&self) + } + } + + impl TryInto<$ty> for &Vec { + type Error = Error; + + fn try_into(self) -> Result<$ty, Self::Error> { + from_values(self) + } + } + }; + } + + #[test] + fn test_deserialize_basic() { + #[derive(Deserialize, Debug, PartialEq)] + struct S { + x: i32, + y: i64, + } + + try_into!(S); + + let input: S = vec![InterfaceValue::I32(1), InterfaceValue::I64(2)] + .try_into() + .unwrap(); + let output = S { x: 1, y: 2 }; + + assert_eq!(input, output); + } + + #[test] + fn test_deserialize_compound() { + #[derive(Deserialize, Debug, PartialEq)] + struct Point { + x: i32, + y: i32, + } + + #[derive(Deserialize, Debug, PartialEq)] + struct Compound { + points: (Point, Point), + more_points: Vec, + } + + try_into!(Compound); + + let input: Compound = vec![ + InterfaceValue::I32(1), + InterfaceValue::I32(2), + InterfaceValue::I32(3), + InterfaceValue::I32(4), + InterfaceValue::I32(5), + InterfaceValue::I32(6), + InterfaceValue::I32(7), + InterfaceValue::I32(8), + InterfaceValue::I32(9), + InterfaceValue::I32(10), + ] + .try_into() + .unwrap(); + let output = Compound { + points: (Point { x: 1, y: 2 }, Point { x: 3, y: 4 }), + more_points: vec![ + Point { x: 5, y: 6 }, + Point { x: 7, y: 8 }, + Point { x: 9, y: 10 }, + ], + }; + + assert_eq!(input, output); + } + + #[test] + fn test_deserialize_newtype_struct() { + #[derive(Deserialize, Debug, PartialEq)] + struct S(i32); + + try_into!(S); + + let input: S = vec![InterfaceValue::I32(1)].try_into().unwrap(); + let output = S(1); + + assert_eq!(input, output); + } + + #[test] + fn test_deserialize_tuple() { + #[derive(Deserialize, Debug, PartialEq)] + struct S { + x: (i32, i64), + }; + + try_into!(S); + + let input: S = vec![InterfaceValue::I32(1), InterfaceValue::I64(2)] + .try_into() + .unwrap(); + let output = S { x: (1, 2) }; + + assert_eq!(input, output); + } + + #[test] + fn test_deserialize_tuple_struct() { + #[derive(Deserialize, Debug, PartialEq)] + struct S(i32, i64); + + try_into!(S); + + let input: S = vec![InterfaceValue::I32(1), InterfaceValue::I64(2)] + .try_into() + .unwrap(); + let output = S(1, 2); + + assert_eq!(input, output); + } + + macro_rules! value { + ($test_name:ident, $variant:ident, $ty:ident, $value:expr) => { + #[test] + #[allow(non_snake_case)] + fn $test_name() { + #[derive(Deserialize, Debug, PartialEq)] + struct S { + x: $ty, + }; + + try_into!(S); + + let input: S = vec![InterfaceValue::$variant($value)].try_into().unwrap(); + let output = S { x: $value }; + + assert_eq!(input, output); + } + }; + } + + value!(test_deserialize_value__s8, S8, i8, 42); + value!(test_deserialize_value__s16, S16, i16, 42); + value!(test_deserialize_value__s32, S32, i32, 42); + value!(test_deserialize_value__s64, S64, i64, 42); + value!(test_deserialize_value__u8, U8, u8, 42); + value!(test_deserialize_value__u16, U16, u16, 42); + value!(test_deserialize_value__u32, U32, u32, 42); + value!(test_deserialize_value__u64, U64, u64, 42); + value!(test_deserialize_value__f32, F32, f32, 42.); + value!(test_deserialize_value__f64, F32, f32, 42.); + value!( + test_deserialize_value__string, + String, + String, + "foo".to_string() + ); + + #[test] + #[allow(non_snake_case)] + fn test_deserialize_value__str() { + #[derive(Deserialize, Debug, PartialEq)] + struct S<'a> { + x: &'a str, + }; + + let v = vec![InterfaceValue::String("foo".to_string())]; + let input: S = from_values(&v).unwrap(); + let output = S { x: "foo" }; + + assert_eq!(input, output); + } + + value!(test_deserialize_value__i32, I32, i32, 42); + value!(test_deserialize_value__i64, I64, i64, 42); + + #[test] + #[allow(non_snake_case)] + fn test_deserialize_value__record() { + #[derive(Deserialize, Debug, PartialEq)] + struct S { + x: i32, + y: i64, + }; + + #[derive(Deserialize, Debug, PartialEq)] + struct T { + s: S, + }; + + let v = vec![InterfaceValue::Record(vec![ + InterfaceValue::I32(1), + InterfaceValue::I64(2), + ])]; + let input: T = from_values(&v).unwrap(); + let output = T { + s: S { x: 1, y: 2 }, + }; + + assert_eq!(input, output); + } +} diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index a2cb8326019..8c0af3947b9 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -2,6 +2,7 @@ pub use crate::ast::{InterfaceType, RecordType}; use crate::errors::WasmValueNativeCastError; +pub use crate::interpreter::wasm::specific_values::*; use std::convert::TryFrom; /// A WIT value. From c87c2ef33b9c697c0c954dd4661fcf1eabe99530 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 31 Mar 2020 07:47:15 +0200 Subject: [PATCH 07/43] feat(interface-values) Improve the `TypeMismatch` error. --- .../wasm/specific_values/record.rs | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/specific_values/record.rs b/lib/interface-types/src/interpreter/wasm/specific_values/record.rs index e7b0ddab054..58e14738629 100644 --- a/lib/interface-types/src/interpreter/wasm/specific_values/record.rs +++ b/lib/interface-types/src/interpreter/wasm/specific_values/record.rs @@ -2,7 +2,7 @@ #![allow(missing_docs)] -use crate::interpreter::wasm::values::InterfaceValue; +use crate::{ast::InterfaceType, interpreter::wasm::values::InterfaceValue}; use serde::{ de::{self, DeserializeSeed, SeqAccess, Visitor}, Deserialize, @@ -76,13 +76,16 @@ macro_rules! next { ($method_name:ident, $variant:ident, $type:ty) => { fn $method_name(&mut self) -> Result<$type, Error> { match self.iterator.peek() { - Some(InterfaceValue::$variant(v)) => { + Some(InterfaceValue::$variant(value)) => { self.iterator.next(); - Ok(*v) + Ok(*value) } - Some(_) => Err(Error::TypeMismatch), + Some(wrong_value) => Err(Error::TypeMismatch { + expected_type: InterfaceType::$variant, + received_type: (*wrong_value).into(), + }), None => Err(Error::InputEmpty), } @@ -110,7 +113,10 @@ impl<'de> Deserializer<'de> { Ok(v) } - Some(_) => Err(Error::TypeMismatch), + Some(wrong_value) => Err(Error::TypeMismatch { + expected_type: InterfaceType::String, + received_type: (*wrong_value).into(), + }), None => Err(Error::InputEmpty), } @@ -138,7 +144,10 @@ where pub enum Error { InputNotEmpty, InputEmpty, - TypeMismatch, + TypeMismatch { + expected_type: InterfaceType, + received_type: InterfaceType, + }, Message(String), } @@ -150,21 +159,24 @@ impl de::Error for Error { impl Display for Error { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str(std::error::Error::description(self)) - } -} - -impl std::error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::InputNotEmpty => "unexpected input remaining", - Error::Message(ref msg) => msg, - Error::InputEmpty => "unexpected end of input", - Error::TypeMismatch => "type mismatch detected", + match self { + Error::InputNotEmpty => write!(formatter, "Unexpected input remaining"), + Error::Message(ref msg) => write!(formatter, "{}", msg), + Error::InputEmpty => write!(formatter, "Unexpected end of input"), + Error::TypeMismatch { + ref expected_type, + ref received_type, + } => write!( + formatter, + "Type mismatch detected, expected `{:?}` but received `{:?}`", + expected_type, received_type + ), } } } +impl std::error::Error for Error {} + impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { type Error = Error; @@ -649,6 +661,18 @@ mod tests { let input: T = from_values(&v).unwrap(); let output = T { s: S { x: 1, y: 2 }, + b: 3., + }; + + assert_eq!(input, output); + } + + #[test] + fn test_deserialize_error_type_mismatch() { + #[derive(Deserialize, Debug, PartialEq)] + struct S { + x: i32, + y: i64, }; assert_eq!(input, output); From ee57b47770f736e7e3843f12ab88432f23190517 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 31 Mar 2020 07:47:54 +0200 Subject: [PATCH 08/43] feat(interface-types) Improve the `Deserializer` API. --- .../wasm/specific_values/record.rs | 55 ++++++++++++------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/specific_values/record.rs b/lib/interface-types/src/interpreter/wasm/specific_values/record.rs index 58e14738629..b2cce73bbf8 100644 --- a/lib/interface-types/src/interpreter/wasm/specific_values/record.rs +++ b/lib/interface-types/src/interpreter/wasm/specific_values/record.rs @@ -65,7 +65,7 @@ struct Deserializer<'de> { } impl<'de> Deserializer<'de> { - pub fn from_values(input: &'de [InterfaceValue]) -> Deserializer<'de> { + pub fn new(input: &'de [InterfaceValue]) -> Deserializer<'de> { Deserializer { iterator: InterfaceValueIterator::new(input).peekable(), } @@ -126,17 +126,16 @@ impl<'de> Deserializer<'de> { next!(next_i64, I64, i64); } -pub fn from_values<'a, T>(s: &'a [InterfaceValue]) -> Result +pub fn from_values<'a, T>(values: &'a [InterfaceValue]) -> Result where T: Deserialize<'a>, { - let mut deserializer = Deserializer::from_values(s); - let t = T::deserialize(&mut deserializer)?; + let mut deserializer = Deserializer::new(values); + let result = T::deserialize(&mut deserializer)?; - if deserializer.iterator.peek().is_none() { - Ok(t) - } else { - Err(Error::InputNotEmpty) + match deserializer.iterator.peek() { + None => Ok(result), + _ => Err(Error::InputNotEmpty), } } @@ -458,23 +457,23 @@ impl<'de, 'a> SeqAccess<'de> for Sequence<'a, 'de> { #[cfg(test)] mod tests { use super::*; - use std::convert::TryInto; + use std::convert::{TryFrom, TryInto}; macro_rules! try_into { ($ty:ty) => { - impl TryInto<$ty> for Vec { + impl TryFrom> for $ty { type Error = Error; - fn try_into(self) -> Result<$ty, Self::Error> { - from_values(&self) + fn try_from(value: Vec) -> Result { + from_values(&value) } } - impl TryInto<$ty> for &Vec { + impl TryFrom<&Vec> for $ty { type Error = Error; - fn try_into(self) -> Result<$ty, Self::Error> { - from_values(self) + fn try_from(value: &Vec) -> Result { + from_values(value) } } }; @@ -651,15 +650,22 @@ mod tests { #[derive(Deserialize, Debug, PartialEq)] struct T { + a: String, s: S, + b: f32, }; - let v = vec![InterfaceValue::Record(vec![ - InterfaceValue::I32(1), - InterfaceValue::I64(2), - ])]; - let input: T = from_values(&v).unwrap(); + try_into!(T); + + let input: T = vec![ + InterfaceValue::String("abc".to_string()), + InterfaceValue::Record(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]), + InterfaceValue::F32(3.), + ] + .try_into() + .unwrap(); let output = T { + a: "abc".to_string(), s: S { x: 1, y: 2 }, b: 3., }; @@ -675,6 +681,15 @@ mod tests { y: i64, }; + try_into!(S); + + let input: Result = + vec![InterfaceValue::I32(1), InterfaceValue::I32(2)].try_into(); + let output = Err(Error::TypeMismatch { + expected_type: InterfaceType::I64, + received_type: InterfaceType::I32, + }); + assert_eq!(input, output); } } From 3655ef8bb729d4dd53906e99839f843e5e2a529c Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 31 Mar 2020 08:02:46 +0200 Subject: [PATCH 09/43] chore(interface-types) Reorganize the serde module. --- .../src/interpreter/wasm/mod.rs | 2 +- .../{specific_values/record.rs => serde.rs} | 102 +++++++++++++----- .../interpreter/wasm/specific_values/mod.rs | 1 - .../src/interpreter/wasm/values.rs | 2 +- 4 files changed, 79 insertions(+), 28 deletions(-) rename lib/interface-types/src/interpreter/wasm/{specific_values/record.rs => serde.rs} (91%) delete mode 100644 lib/interface-types/src/interpreter/wasm/specific_values/mod.rs diff --git a/lib/interface-types/src/interpreter/wasm/mod.rs b/lib/interface-types/src/interpreter/wasm/mod.rs index e99321c8501..8923d2f1837 100644 --- a/lib/interface-types/src/interpreter/wasm/mod.rs +++ b/lib/interface-types/src/interpreter/wasm/mod.rs @@ -2,6 +2,6 @@ //! types, and traits —basically this is the part a runtime should //! take a look to use the `wasmer-interface-types` crate—. -mod specific_values; +mod serde; pub mod structures; pub mod values; diff --git a/lib/interface-types/src/interpreter/wasm/specific_values/record.rs b/lib/interface-types/src/interpreter/wasm/serde.rs similarity index 91% rename from lib/interface-types/src/interpreter/wasm/specific_values/record.rs rename to lib/interface-types/src/interpreter/wasm/serde.rs index b2cce73bbf8..64324fb6436 100644 --- a/lib/interface-types/src/interpreter/wasm/specific_values/record.rs +++ b/lib/interface-types/src/interpreter/wasm/serde.rs @@ -1,6 +1,6 @@ -//! Specific operations on records. - -#![allow(missing_docs)] +//! Serde is not necessary to use WIT. It only provides a nicer API +//! for the end-user to rebuild its complex types from WIT values, +//! like `record`. use crate::{ast::InterfaceType, interpreter::wasm::values::InterfaceValue}; use serde::{ @@ -13,6 +13,61 @@ use std::{ slice::Iter, }; +/// Deserialize a set of `InterfaceValue`s to a type `T` that +/// implements `Deserialize`. +/// +/// 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_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::String("abc".to_string()), +/// InterfaceValue::Record(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]), +/// InterfaceValue::F32(3.), +/// ]; +/// let t: T = from_values(&values).unwrap(); +/// +/// assert_eq!( +/// t, +/// T { +/// x: "abc", +/// s: S(1, 2), +/// y: 3., +/// } +/// ); +/// ``` +pub fn from_values<'a, T>(values: &'a [InterfaceValue]) -> Result +where + T: Deserialize<'a>, +{ + let mut deserializer = Deserializer::new(values); + let result = T::deserialize(&mut deserializer)?; + + match deserializer.iterator.peek() { + None => Ok(result), + _ => Err(Error::InputNotEmpty), + } +} + /// Iterates over a vector of `InterfaceValues` but flatten all the /// values for Serde. It means that the ideal representation for Serde /// regarding our implementation is to get all values flatten. So @@ -60,6 +115,8 @@ impl<'a> Iterator for InterfaceValueIterator<'a> { } } +/// The deserializer. The iterator iterates over `InterfaceValue`s, +/// all flatten, see `InterfaceValueIterator`. struct Deserializer<'de> { iterator: Peekable>, } @@ -126,27 +183,25 @@ impl<'de> Deserializer<'de> { next!(next_i64, I64, i64); } -pub fn from_values<'a, T>(values: &'a [InterfaceValue]) -> Result -where - T: Deserialize<'a>, -{ - let mut deserializer = Deserializer::new(values); - let result = T::deserialize(&mut deserializer)?; - - match deserializer.iterator.peek() { - None => Ok(result), - _ => Err(Error::InputNotEmpty), - } -} - +/// Represents an error while deserializing. #[derive(Clone, Debug, PartialEq)] pub enum Error { + /// 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), } @@ -643,16 +698,13 @@ mod tests { #[allow(non_snake_case)] fn test_deserialize_value__record() { #[derive(Deserialize, Debug, PartialEq)] - struct S { - x: i32, - y: i64, - }; + struct S(i32, i64); #[derive(Deserialize, Debug, PartialEq)] struct T { - a: String, + x: String, s: S, - b: f32, + y: f32, }; try_into!(T); @@ -665,9 +717,9 @@ mod tests { .try_into() .unwrap(); let output = T { - a: "abc".to_string(), - s: S { x: 1, y: 2 }, - b: 3., + x: "abc".to_string(), + s: S(1, 2), + y: 3., }; assert_eq!(input, output); diff --git a/lib/interface-types/src/interpreter/wasm/specific_values/mod.rs b/lib/interface-types/src/interpreter/wasm/specific_values/mod.rs deleted file mode 100644 index 2066636cab0..00000000000 --- a/lib/interface-types/src/interpreter/wasm/specific_values/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod record; diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 8c0af3947b9..53d677f23f2 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -2,7 +2,7 @@ pub use crate::ast::{InterfaceType, RecordType}; use crate::errors::WasmValueNativeCastError; -pub use crate::interpreter::wasm::specific_values::*; +pub use crate::interpreter::wasm::serde::*; use std::convert::TryFrom; /// A WIT value. From 0af1076eee219a9f943ac7797332181b11f3f07d Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 31 Mar 2020 08:17:52 +0200 Subject: [PATCH 10/43] feat(interface-types) Encodes/decodes the `record.lift` instruction. --- lib/interface-types/src/decoders/binary.rs | 18 +++++++++++++++++- lib/interface-types/src/decoders/wat.rs | 9 +++++++++ lib/interface-types/src/encoders/binary.rs | 9 ++++++++- lib/interface-types/src/encoders/wat.rs | 5 ++++- .../src/interpreter/instructions/mod.rs | 6 ++++++ lib/interface-types/src/interpreter/mod.rs | 2 ++ 6 files changed, 46 insertions(+), 3 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 2ae47a9680a..35c324c356a 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -173,6 +173,7 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( Ok(match opcode { 0x00 => { consume!((input, argument_0) = uleb(input)?); + ( input, Instruction::ArgumentGet { @@ -183,6 +184,7 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x01 => { consume!((input, argument_0) = uleb(input)?); + ( input, Instruction::CallCore { @@ -228,6 +230,7 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x23 => { consume!((input, argument_0) = uleb(input)?); + ( input, Instruction::StringLowerMemory { @@ -238,6 +241,17 @@ 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, + }, + ) + } + _ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))), }) } @@ -701,7 +715,7 @@ mod tests { #[test] fn test_instructions() { let input = &[ - 0x25, // list of 37 items + 0x26, // list of 38 items 0x00, 0x01, // ArgumentGet { index: 1 } 0x01, 0x01, // CallCore { function_index: 1 } 0x02, // S8FromI32 @@ -739,6 +753,7 @@ mod tests { 0x22, // StringLiftMemory 0x23, 0x01, // StringLowerMemory { allocator_index: 1 } 0x24, // StringSize + 0x25, 0x01, // RecordLift { type_index: 1 }, 0x0a, ]; let output = Ok(( @@ -781,6 +796,7 @@ mod tests { Instruction::StringLiftMemory, Instruction::StringLowerMemory { allocator_index: 1 }, Instruction::StringSize, + Instruction::RecordLift { type_index: 1 }, ], )); diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index fb816306846..9abd840821e 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -65,6 +65,7 @@ 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"); } impl Parse<'_> for InterfaceType { @@ -312,6 +313,12 @@ impl<'a> Parse<'a> for Instruction { parser.parse::()?; Ok(Instruction::StringSize) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::RecordLift { + type_index: parser.parse()?, + }) } else { Err(lookahead.error()) } @@ -756,6 +763,7 @@ mod tests { "string.lift_memory", "string.lower_memory 42", "string.size", + "record.lift 42", ]; let outputs = vec![ Instruction::ArgumentGet { index: 7 }, @@ -797,6 +805,7 @@ mod tests { allocator_index: 42, }, Instruction::StringSize, + Instruction::RecordLift { type_index: 42 }, ]; assert_eq!(inputs.len(), outputs.len()); diff --git a/lib/interface-types/src/encoders/binary.rs b/lib/interface-types/src/encoders/binary.rs index 2be2dad6c44..439fe05b71b 100644 --- a/lib/interface-types/src/encoders/binary.rs +++ b/lib/interface-types/src/encoders/binary.rs @@ -338,6 +338,11 @@ where } Instruction::StringSize => 0x24_u8.to_bytes(writer)?, + + Instruction::RecordLift { type_index } => { + 0x25_u8.to_bytes(writer)?; + (*type_index as u64).to_bytes(writer)? + } } Ok(()) @@ -683,9 +688,10 @@ mod tests { Instruction::StringLiftMemory, Instruction::StringLowerMemory { allocator_index: 1 }, Instruction::StringSize, + Instruction::RecordLift { type_index: 1 }, ], &[ - 0x25, // list of 37 items + 0x26, // list of 38 items 0x00, 0x01, // ArgumentGet { index: 1 } 0x01, 0x01, // CallCore { function_index: 1 } 0x02, // S8FromI32 @@ -723,6 +729,7 @@ mod tests { 0x22, // StringLiftMemory 0x23, 0x01, // StringLowerMemory { allocator_index: 1 } 0x24, // StringSize + 0x025, 0x01, // RecordLift { type_index: 1 } ] ); } diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 772bf89935f..68d8cc1f72c 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -136,9 +136,10 @@ 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), } } } @@ -465,6 +466,7 @@ mod tests { }) .to_string(), (&Instruction::StringSize).to_string(), + (&Instruction::RecordLift { type_index: 42 }).to_string(), ]; let outputs = vec![ "arg.get 7", @@ -504,6 +506,7 @@ mod tests { "string.lift_memory", "string.lower_memory 42", "string.size", + "record.lift 42", ]; assert_eq!(inputs, outputs); diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 42e2dad4d9b..383c596ed3c 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -135,6 +135,12 @@ pub enum Instruction { /// The `string.size` instruction. StringSize, + + /// The `record.lift` instruction. + RecordLift { + /// The type index of the record. + type_index: u32, + }, } /// Just a short helper to map the error of a cast from an diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 408d06c0811..7a553d7a986 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -234,6 +234,8 @@ where instructions::string_lower_memory(*allocator_index, *instruction) } Instruction::StringSize => instructions::string_size(*instruction), + + Instruction::RecordLift { type_index: _ } => todo!(), }) .collect(); From 02b7e21345b94bf113764895bb91f5eea85558e0 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 31 Mar 2020 08:54:05 +0200 Subject: [PATCH 11/43] feat(interface-types) Implement the `record.lift` instruction. --- lib/interface-types/src/errors.rs | 43 +++- .../src/interpreter/instructions/mod.rs | 25 +- .../src/interpreter/instructions/records.rs | 224 ++++++++++++++++++ lib/interface-types/src/interpreter/mod.rs | 4 +- .../src/interpreter/wasm/structures.rs | 10 +- .../src/interpreter/wasm/values.rs | 5 +- lib/interface-types/src/lib.rs | 2 +- 7 files changed, 292 insertions(+), 21 deletions(-) create mode 100644 lib/interface-types/src/interpreter/instructions/records.rs diff --git a/lib/interface-types/src/errors.rs b/lib/interface-types/src/errors.rs index 5df5c49e391..f2f4aad0b54 100644 --- a/lib/interface-types/src/errors.rs +++ b/lib/interface-types/src/errors.rs @@ -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}, @@ -149,6 +152,21 @@ pub enum InstructionErrorKind { /// The string contains invalid UTF-8 encoding. String(string::FromUtf8Error), + + /// 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 {} @@ -196,11 +214,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!( @@ -218,14 +232,21 @@ 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::TypeIsMissing { type_index } => write!( + formatter, + "the type `{}` doesn't exist", + type_index ), - Self::String(error) => write!( + Self::InvalidTypeKind { expected_kind, received_kind } => write!( formatter, - "{}", - error + "read a type of kind `{:?}`, but the kind `{:?}` was expected", + received_kind, expected_kind ), } } diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 383c596ed3c..96657b5f6c5 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -1,6 +1,7 @@ mod argument_get; mod call_core; mod numbers; +mod records; mod strings; use crate::{ @@ -10,6 +11,7 @@ use crate::{ 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::*; @@ -158,9 +160,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}; @@ -257,6 +262,7 @@ pub(crate) mod tests { pub(crate) exports: HashMap, pub(crate) locals_or_imports: HashMap, pub(crate) memory: Memory, + pub(crate) wit_types: Vec, } impl Instance { @@ -313,6 +319,15 @@ pub(crate) mod tests { hashmap }, memory: Memory::new(vec![Cell::new(0); 128]), + wit_types: vec![ast::Type::Record(ast::RecordType { + fields: vec![ + InterfaceType::I32, + InterfaceType::Record(ast::RecordType { + fields: vec![InterfaceType::String, InterfaceType::F32], + }), + InterfaceType::I64, + ], + })], } } } @@ -332,5 +347,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) + } } } diff --git a/lib/interface-types/src/interpreter/instructions/records.rs b/lib/interface-types/src/interpreter/instructions/records.rs new file mode 100644 index 00000000000..bea4a5ccd71 --- /dev/null +++ b/lib/interface-types/src/interpreter/instructions/records.rs @@ -0,0 +1,224 @@ +use crate::{ + ast::{InterfaceType, RecordType, Type, TypeKind}, + errors::{InstructionError, InstructionErrorKind}, + interpreter::{ + stack::{Stack, Stackable}, + wasm::values::InterfaceValue, + Instruction, + }, +}; +use std::mem::{transmute, MaybeUninit}; + +/// Build a `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`. To simplify this +/// algorithm that also typed-checks values when hydrating, the number +/// of values to read from the stack isn't known ahead-of-time. Thus, +/// the `Stack::pop` method cannot be used, and `Stack::pop1` is used +/// instead. It implies that values are read one after the other from +/// the stack, in a natural reverse order, from `value_n` to +/// `value_1`. +/// +/// Consequently, record fields are filled in reverse order, from +/// `field_n` to `field_1`. +/// +/// A basic algorithm would then be: +/// +/// ```rust,ignore +/// let mut values = vec![]; +/// +/// // Read fields in reverse-order, from `field_n` to `field_1`. +/// for field in fields.iter().rev() { +/// let value = stack.pop1(); +/// // type-check with `field` and `value`, to finally… +/// values.push(value); +/// } +/// +/// InterfaceValue::Record(values.iter().rev().collect()) +/// ``` +/// +/// Note that it is required to reverse the `values` vector at the end +/// because `InterfaceValue::Record` expects its values to match the +/// original `fields` order. +/// +/// Because this approach allocates two vectors for `values`, another +/// approach has been adopted. `values` is an initialized vector +/// containing uninitialized values of type +/// `MaybeUninit`. With this approach, it is possible +/// to fill `values` from index `n` to `0`. Once `values` is entirely +/// filled, it is `transmute`d to `Vec`. +/// +/// This latter approach allows to allocate one and final vector to +/// hold all the record values. +#[allow(unsafe_code)] +fn record_hydrate( + stack: &mut Stack, + record_type: &RecordType, +) -> Result { + let length = record_type.fields.len(); + let mut values = { + // Initialize a vector of length `length` with `MaybeUninit` + // values. + let mut v = Vec::with_capacity(length); + + for _ in 0..length { + v.push(MaybeUninit::::uninit()); + } + + v + }; + let max = length - 1; + + // Iterate over fields in reverse order to match the stack `pop` + // order. + for (nth, field) in record_type.fields.iter().rev().enumerate() { + match field { + // The record type tells a record is expected. + InterfaceType::Record(record_type) => { + // Build it recursively. + let value = record_hydrate(stack, &record_type)?; + + unsafe { + values[max - nth].as_mut_ptr().write(value); + } + } + // 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, + }); + } + + unsafe { + values[max - nth].as_mut_ptr().write(value); + } + } + } + } + + Ok(InterfaceValue::Record(unsafe { transmute(values) })) +} + +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_hydrate(&mut runtime.stack, &record_type) + .map_err(|k| InstructionError::new(instruction, k))?; + + runtime.stack.push(record); + + Ok(()) + } + } +); + +#[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), + ])], + ); + + 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: vec![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"#, + ); +} diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 7a553d7a986..ac80d36e89f 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -235,7 +235,9 @@ where } Instruction::StringSize => instructions::string_size(*instruction), - Instruction::RecordLift { type_index: _ } => todo!(), + Instruction::RecordLift { type_index } => { + instructions::record_lift(*type_index, *instruction) + } }) .collect(); diff --git a/lib/interface-types/src/interpreter/wasm/structures.rs b/lib/interface-types/src/interpreter/wasm/structures.rs index dbeab832fde..7d9c2676ba9 100644 --- a/lib/interface-types/src/interpreter/wasm/structures.rs +++ b/lib/interface-types/src/interpreter/wasm/structures.rs @@ -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(&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(&mut self, _index: I) -> Option<&LI> { None } + + fn wit_type(&self, _index: u32) -> Option<&ast::Type> { + None + } } diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 53d677f23f2..5cd2ea1f557 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -70,10 +70,7 @@ impl From<&InterfaceValue> for InterfaceType { InterfaceValue::I32(_) => Self::I32, InterfaceValue::I64(_) => Self::I64, InterfaceValue::Record(values) => Self::Record(RecordType { - fields: values - .iter() - .map(Into::into) - .collect::>(), + fields: values.iter().map(Into::into).collect(), }), } } diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 9e88cac281b..ebf8603d2a7 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -41,12 +41,12 @@ missing_docs, nonstandard_style, unreachable_patterns, + unsafe_code, unused_imports, unused_mut, unused_unsafe, unused_variables )] -#![forbid(unsafe_code)] #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] #![doc(html_logo_url = "https://github.com/wasmerio.png")] From 1a17cbb17e918aa177259a1bd8b033ead31a0237 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 31 Mar 2020 14:44:04 +0200 Subject: [PATCH 12/43] test(interface-types) Deserialize WIT record to Rust struct. --- .../src/interpreter/instructions/records.rs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/lib/interface-types/src/interpreter/instructions/records.rs b/lib/interface-types/src/interpreter/instructions/records.rs index bea4a5ccd71..8fc3db5db34 100644 --- a/lib/interface-types/src/interpreter/instructions/records.rs +++ b/lib/interface-types/src/interpreter/instructions/records.rs @@ -165,6 +165,69 @@ mod tests { ])], ); + #[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_values, InterfaceType, InterfaceValue}, + Instruction, Interpreter, + }; + use serde::Deserialize; + use std::{cell::Cell, collections::HashMap, convert::TryInto}; + + let interpreter: Interpreter = (&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_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: [ From 5ba6fda1c91749039a2916a0b9a05e4bad375ab3 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 11:51:34 +0200 Subject: [PATCH 13/43] chore(interface-types) Improve code readibility of `string` instructions. --- .../src/interpreter/instructions/strings.rs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/strings.rs b/lib/interface-types/src/interpreter/instructions/strings.rs index 011cac9516a..f423dade0fb 100644 --- a/lib/interface-types/src/interpreter/instructions/strings.rs +++ b/lib/interface-types/src/interpreter/instructions/strings.rs @@ -88,15 +88,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 }, ) })?; @@ -118,7 +118,7 @@ executable_instruction!( .ok_or_else(|| { InstructionError::new( instruction, - InstructionErrorKind::MemoryIsMissing { memory_index } + InstructionErrorKind::MemoryIsMissing { memory_index }, ) })? .view(); @@ -138,26 +138,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 }, + )), } } } From 3411ac7a1cc326e148341b3f28383e79f98952a7 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 11:55:20 +0200 Subject: [PATCH 14/43] feat(interface-types) Move `serde::InterfaceTypeIterator` into `values::FlattenInterfaceValueIterator`. --- .../src/interpreter/wasm/serde.rs | 59 +++---------------- .../src/interpreter/wasm/values.rs | 47 ++++++++++++++- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde.rs b/lib/interface-types/src/interpreter/wasm/serde.rs index 64324fb6436..7064a8f368d 100644 --- a/lib/interface-types/src/interpreter/wasm/serde.rs +++ b/lib/interface-types/src/interpreter/wasm/serde.rs @@ -2,7 +2,10 @@ //! for the end-user to rebuild its complex types from WIT values, //! like `record`. -use crate::{ast::InterfaceType, interpreter::wasm::values::InterfaceValue}; +use crate::{ + ast::InterfaceType, + interpreter::wasm::values::{FlattenInterfaceValueIterator, InterfaceValue}, +}; use serde::{ de::{self, DeserializeSeed, SeqAccess, Visitor}, Deserialize, @@ -10,7 +13,6 @@ use serde::{ use std::{ fmt::{self, Display}, iter::Peekable, - slice::Iter, }; /// Deserialize a set of `InterfaceValue`s to a type `T` that @@ -68,63 +70,16 @@ where } } -/// Iterates over a vector of `InterfaceValues` but flatten all the -/// values for Serde. It means that the ideal representation for Serde -/// regarding our implementation is to get all values flatten. So -/// `I32(1), Record([I32(2), I32(3)]), I32(4)` must be iterated like -/// `I32(1), I32(2), I32(3), I32(4)`. -struct InterfaceValueIterator<'a> { - iterators: Vec>, -} - -impl<'a> InterfaceValueIterator<'a> { - fn new(values: &'a [InterfaceValue]) -> Self { - Self { - iterators: vec![values.iter()], - } - } -} - -impl<'a> Iterator for InterfaceValueIterator<'a> { - type Item = &'a InterfaceValue; - - fn next(&mut self) -> Option { - if self.iterators.is_empty() { - return None; - } - - let index = self.iterators.len() - 1; - - match self.iterators[index].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, - } - } -} - /// The deserializer. The iterator iterates over `InterfaceValue`s, -/// all flatten, see `InterfaceValueIterator`. +/// all flatten, see `FlattenInterfaceValueIterator`. struct Deserializer<'de> { - iterator: Peekable>, + iterator: Peekable>, } impl<'de> Deserializer<'de> { pub fn new(input: &'de [InterfaceValue]) -> Deserializer<'de> { Deserializer { - iterator: InterfaceValueIterator::new(input).peekable(), + iterator: FlattenInterfaceValueIterator::new(input).peekable(), } } } diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 5cd2ea1f557..472be980918 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -3,7 +3,7 @@ pub use crate::ast::{InterfaceType, RecordType}; use crate::errors::WasmValueNativeCastError; pub use crate::interpreter::wasm::serde::*; -use std::convert::TryFrom; +use std::{convert::TryFrom, slice::Iter}; /// A WIT value. #[derive(Debug, Clone, PartialEq)] @@ -128,6 +128,51 @@ native!(f32, F32); native!(f64, F64); native!(String, String); +/// 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>, +} + +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 { + if self.iterators.is_empty() { + return None; + } + + let index = self.iterators.len() - 1; + + match self.iterators[index].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::*; From aab82c122d31d024ec5e618e64538d5f1ff7ddf0 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 11:55:54 +0200 Subject: [PATCH 15/43] feat(interface-types) Implement `From<&Vec>` for `RecordType`. --- lib/interface-types/src/interpreter/wasm/values.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 472be980918..3895aa06e97 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -69,9 +69,7 @@ impl From<&InterfaceValue> for InterfaceType { //InterfaceValue::Anyref(_) => Self::Anyref, InterfaceValue::I32(_) => Self::I32, InterfaceValue::I64(_) => Self::I64, - InterfaceValue::Record(values) => Self::Record(RecordType { - fields: values.iter().map(Into::into).collect(), - }), + InterfaceValue::Record(values) => Self::Record(values.into()), } } } @@ -82,6 +80,14 @@ impl Default for InterfaceValue { } } +impl From<&Vec> for RecordType { + fn from(values: &Vec) -> Self { + RecordType { + fields: values.iter().map(Into::into).collect(), + } + } +} + /// Represents a native type supported by WIT. pub trait NativeType { /// The associated interface type that maps to the native type. From a1551b52af14551fe5683a23fdc9c506a7a89a16 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 11:58:05 +0200 Subject: [PATCH 16/43] test(interface-types) Rename a test case. --- lib/interface-types/src/interpreter/instructions/strings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interface-types/src/interpreter/instructions/strings.rs b/lib/interface-types/src/interpreter/instructions/strings.rs index f423dade0fb..4a673e14f36 100644 --- a/lib/interface-types/src/interpreter/instructions/strings.rs +++ b/lib/interface-types/src/interpreter/instructions/strings.rs @@ -275,7 +275,7 @@ mod tests { ); test_executable_instruction!( - test_string_memory__roundtrip_with_memory_to_string = + test_string_memory__roundtrip_ = instructions: [ Instruction::ArgumentGet { index: 0 }, Instruction::StringLowerMemory { allocator_index: 43 }, From b8ef82d1d081b24d83ee0eca06ffc80086e0976a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 11:59:35 +0200 Subject: [PATCH 17/43] test(interface-types) Rename test cases. --- .../src/interpreter/instructions/strings.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/strings.rs b/lib/interface-types/src/interpreter/instructions/strings.rs index 4a673e14f36..52970cc9432 100644 --- a/lib/interface-types/src/interpreter/instructions/strings.rs +++ b/lib/interface-types/src/interpreter/instructions/strings.rs @@ -259,7 +259,7 @@ mod tests { ); test_executable_instruction!( - test_string_memory = + test_string_lower_memory = instructions: [ Instruction::ArgumentGet { index: 0 }, Instruction::StringLowerMemory { allocator_index: 43 }, @@ -275,7 +275,7 @@ mod tests { ); test_executable_instruction!( - test_string_memory__roundtrip_ = + test_string__roundtrip = instructions: [ Instruction::ArgumentGet { index: 0 }, Instruction::StringLowerMemory { allocator_index: 43 }, @@ -287,7 +287,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() }, @@ -295,7 +295,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 @@ -306,7 +306,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 } @@ -330,7 +330,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 } From 11687c57caf13829ebf36ad175cc2e281a4af83f Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 12:05:22 +0200 Subject: [PATCH 18/43] feat(interface-types) Encodes/decodes the `record.lower` instruction. --- lib/interface-types/src/decoders/binary.rs | 16 +++++++++++++--- lib/interface-types/src/decoders/wat.rs | 9 +++++++++ lib/interface-types/src/encoders/binary.rs | 10 +++++++--- lib/interface-types/src/encoders/wat.rs | 3 +++ .../src/interpreter/instructions/mod.rs | 6 ++++++ 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 35c324c356a..77e95a73489 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -227,7 +227,6 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x21 => (input, Instruction::I64FromU64), 0x22 => (input, Instruction::StringLiftMemory), - 0x23 => { consume!((input, argument_0) = uleb(input)?); @@ -238,7 +237,6 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( }, ) } - 0x24 => (input, Instruction::StringSize), 0x25 => { @@ -251,6 +249,16 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( }, ) } + 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))), }) @@ -715,7 +723,7 @@ mod tests { #[test] fn test_instructions() { let input = &[ - 0x26, // list of 38 items + 0x27, // list of 39 items 0x00, 0x01, // ArgumentGet { index: 1 } 0x01, 0x01, // CallCore { function_index: 1 } 0x02, // S8FromI32 @@ -754,6 +762,7 @@ mod tests { 0x23, 0x01, // StringLowerMemory { allocator_index: 1 } 0x24, // StringSize 0x25, 0x01, // RecordLift { type_index: 1 }, + 0x26, 0x01, // RecordLower { type_index: 1 }, 0x0a, ]; let output = Ok(( @@ -797,6 +806,7 @@ mod tests { Instruction::StringLowerMemory { allocator_index: 1 }, Instruction::StringSize, Instruction::RecordLift { type_index: 1 }, + Instruction::RecordLower { type_index: 1 }, ], )); diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 9abd840821e..39ea6f30d71 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -66,6 +66,7 @@ mod keyword { 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 { @@ -319,6 +320,12 @@ impl<'a> Parse<'a> for Instruction { Ok(Instruction::RecordLift { type_index: parser.parse()?, }) + } else if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::RecordLower { + type_index: parser.parse()?, + }) } else { Err(lookahead.error()) } @@ -764,6 +771,7 @@ mod tests { "string.lower_memory 42", "string.size", "record.lift 42", + "record.lower 42", ]; let outputs = vec![ Instruction::ArgumentGet { index: 7 }, @@ -806,6 +814,7 @@ mod tests { }, Instruction::StringSize, Instruction::RecordLift { type_index: 42 }, + Instruction::RecordLower { type_index: 42 }, ]; assert_eq!(inputs.len(), outputs.len()); diff --git a/lib/interface-types/src/encoders/binary.rs b/lib/interface-types/src/encoders/binary.rs index 439fe05b71b..2370017a211 100644 --- a/lib/interface-types/src/encoders/binary.rs +++ b/lib/interface-types/src/encoders/binary.rs @@ -331,18 +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(()) @@ -689,9 +691,10 @@ mod tests { Instruction::StringLowerMemory { allocator_index: 1 }, Instruction::StringSize, Instruction::RecordLift { type_index: 1 }, + Instruction::RecordLower { type_index: 1 }, ], &[ - 0x26, // list of 38 items + 0x27, // list of 39 items 0x00, 0x01, // ArgumentGet { index: 1 } 0x01, 0x01, // CallCore { function_index: 1 } 0x02, // S8FromI32 @@ -730,6 +733,7 @@ mod tests { 0x23, 0x01, // StringLowerMemory { allocator_index: 1 } 0x24, // StringSize 0x025, 0x01, // RecordLift { type_index: 1 } + 0x026, 0x01, // RecordLower { type_index: 1 } ] ); } diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 68d8cc1f72c..6ec680ceed0 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -140,6 +140,7 @@ impl ToString for &Instruction { } Instruction::StringSize => "string.size".into(), Instruction::RecordLift { type_index } => format!("record.lift {}", type_index), + Instruction::RecordLower { type_index } => format!("record.lower {}", type_index), } } } @@ -467,6 +468,7 @@ 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", @@ -507,6 +509,7 @@ mod tests { "string.lower_memory 42", "string.size", "record.lift 42", + "record.lower 42", ]; assert_eq!(inputs, outputs); diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 96657b5f6c5..5681620d577 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -143,6 +143,12 @@ pub enum Instruction { /// 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 From 0023eea2757ab97e5674e23dd41846962f6f58d1 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 12:06:27 +0200 Subject: [PATCH 19/43] feat(interface-types) Implement the `record.lower` instruction. --- .../src/interpreter/instructions/records.rs | 136 ++++++++++++++++++ lib/interface-types/src/interpreter/mod.rs | 3 + 2 files changed, 139 insertions(+) diff --git a/lib/interface-types/src/interpreter/instructions/records.rs b/lib/interface-types/src/interpreter/instructions/records.rs index 8fc3db5db34..158880a4380 100644 --- a/lib/interface-types/src/interpreter/instructions/records.rs +++ b/lib/interface-types/src/interpreter/instructions/records.rs @@ -1,6 +1,7 @@ use crate::{ ast::{InterfaceType, RecordType, Type, TypeKind}, errors::{InstructionError, InstructionErrorKind}, + interpreter::wasm::values::FlattenInterfaceValueIterator, interpreter::{ stack::{Stack, Stackable}, wasm::values::InterfaceValue, @@ -135,6 +136,54 @@ executable_instruction!( } ); +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}; @@ -284,4 +333,91 @@ mod tests { 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"#, + ); } diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index ac80d36e89f..7486c816762 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -238,6 +238,9 @@ where Instruction::RecordLift { type_index } => { instructions::record_lift(*type_index, *instruction) } + Instruction::RecordLower { type_index } => { + instructions::record_lower(*type_index, *instruction) + } }) .collect(); From 8f8c5f1bc88366a257763767176460702070de3a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 12:06:54 +0200 Subject: [PATCH 20/43] chore(interface-types) Improve code readabilit of the `record.lift` instruction. --- .../src/interpreter/instructions/records.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/records.rs b/lib/interface-types/src/interpreter/instructions/records.rs index 158880a4380..9faebf1fd01 100644 --- a/lib/interface-types/src/interpreter/instructions/records.rs +++ b/lib/interface-types/src/interpreter/instructions/records.rs @@ -53,7 +53,7 @@ use std::mem::{transmute, MaybeUninit}; /// This latter approach allows to allocate one and final vector to /// hold all the record values. #[allow(unsafe_code)] -fn record_hydrate( +fn record_lift_( stack: &mut Stack, record_type: &RecordType, ) -> Result { @@ -78,7 +78,7 @@ fn record_hydrate( // The record type tells a record is expected. InterfaceType::Record(record_type) => { // Build it recursively. - let value = record_hydrate(stack, &record_type)?; + let value = record_lift_(stack, &record_type)?; unsafe { values[max - nth].as_mut_ptr().write(value); @@ -89,7 +89,7 @@ fn record_hydrate( let value = stack.pop1().unwrap(); let value_type = (&value).into(); - if *ty != value_type { + if ty != &value_type { return Err(InstructionErrorKind::InvalidValueOnTheStack { expected_type: ty.clone(), received_type: value_type, @@ -113,7 +113,7 @@ executable_instruction!( let record_type = match instance.wit_type(type_index).ok_or_else(|| { InstructionError::new( instruction, - InstructionErrorKind::TypeIsMissing { type_index } + InstructionErrorKind::TypeIsMissing { type_index }, ) })? { Type::Record(record_type) => record_type, @@ -126,7 +126,7 @@ executable_instruction!( )), }; - let record = record_hydrate(&mut runtime.stack, &record_type) + let record = record_lift_(&mut runtime.stack, &record_type) .map_err(|k| InstructionError::new(instruction, k))?; runtime.stack.push(record); From f8507533fbd464b61dad08a0bd4f8ff92c66145e Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 12:16:30 +0200 Subject: [PATCH 21/43] fix(interface-types) Fix a `git-merge` issue. --- lib/interface-types/src/interpreter/instructions/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 5681620d577..cf71c6e57d6 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -27,7 +27,7 @@ pub enum Instruction { /// The `call-core` instruction. CallCore { /// The function index. - function_index: usize, + function_index: u32, }, /// The `s8.from_i32` instruction. From 7b182416df1961d352ba4b652a7d2f9ee23451bd Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 13:53:10 +0200 Subject: [PATCH 22/43] feat(interface-types) Make `serde` optional behind a feature flag. --- lib/interface-types/Cargo.toml | 9 ++++++++- .../src/interpreter/instructions/records.rs | 1 + lib/interface-types/src/interpreter/wasm/mod.rs | 2 ++ lib/interface-types/src/interpreter/wasm/values.rs | 4 +++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index f19b6ee34d0..34e19c49cd5 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -10,4 +10,11 @@ edition = "2018" [dependencies] nom = "5.1" wast = "8.0" -serde = { version = "1.0", features = ["derive"] } \ No newline at end of file + +# `serde` is useful only to simplify the users' life. It is not +# required by WIT itself, is is used to transform WIT values like +# `record`s to Rust sum types. +serde = { version = "1.0", features = ["derive"], optional = true } + +[features] +default = ["serde"] \ No newline at end of file diff --git a/lib/interface-types/src/interpreter/instructions/records.rs b/lib/interface-types/src/interpreter/instructions/records.rs index 9faebf1fd01..9a248e88281 100644 --- a/lib/interface-types/src/interpreter/instructions/records.rs +++ b/lib/interface-types/src/interpreter/instructions/records.rs @@ -214,6 +214,7 @@ mod tests { ])], ); + #[cfg(feature = "serde")] #[test] #[allow(non_snake_case, unused)] fn test_record_lift__to_rust_struct() { diff --git a/lib/interface-types/src/interpreter/wasm/mod.rs b/lib/interface-types/src/interpreter/wasm/mod.rs index 8923d2f1837..539e5ff0ef2 100644 --- a/lib/interface-types/src/interpreter/wasm/mod.rs +++ b/lib/interface-types/src/interpreter/wasm/mod.rs @@ -2,6 +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; diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 3895aa06e97..1ba005d8f57 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -2,9 +2,11 @@ pub use crate::ast::{InterfaceType, RecordType}; use crate::errors::WasmValueNativeCastError; -pub use crate::interpreter::wasm::serde::*; use std::{convert::TryFrom, slice::Iter}; +#[cfg(feature = "serde")] +pub use crate::interpreter::wasm::serde::*; + /// A WIT value. #[derive(Debug, Clone, PartialEq)] pub enum InterfaceValue { From 8868d640fa18dddad75f44a333328cd2ab51b913 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 13:56:20 +0200 Subject: [PATCH 23/43] feat(interface-types) Rename `from_values` to `from_interface_values`. --- .../src/interpreter/instructions/records.rs | 4 ++-- lib/interface-types/src/interpreter/wasm/serde.rs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/records.rs b/lib/interface-types/src/interpreter/instructions/records.rs index 9a248e88281..5ed388fc79b 100644 --- a/lib/interface-types/src/interpreter/instructions/records.rs +++ b/lib/interface-types/src/interpreter/instructions/records.rs @@ -221,7 +221,7 @@ mod tests { use crate::interpreter::{ instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView}, stack::Stackable, - wasm::values::{from_values, InterfaceType, InterfaceValue}, + wasm::values::{from_interface_values, InterfaceType, InterfaceValue}, Instruction, Interpreter, }; use serde::Deserialize; @@ -263,7 +263,7 @@ mod tests { y: i64, } - let record: T = from_values(stack.as_slice()).unwrap(); + let record: T = from_interface_values(stack.as_slice()).unwrap(); assert_eq!( record, diff --git a/lib/interface-types/src/interpreter/wasm/serde.rs b/lib/interface-types/src/interpreter/wasm/serde.rs index 7064a8f368d..1e5ac5da285 100644 --- a/lib/interface-types/src/interpreter/wasm/serde.rs +++ b/lib/interface-types/src/interpreter/wasm/serde.rs @@ -16,7 +16,7 @@ use std::{ }; /// Deserialize a set of `InterfaceValue`s to a type `T` that -/// implements `Deserialize`. +/// 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 @@ -27,7 +27,7 @@ use std::{ /// ```rust /// use wasmer_interface_types::interpreter::wasm::values::{ /// InterfaceValue, -/// from_values, +/// from_interface_values, /// }; /// use serde::Deserialize; /// @@ -46,7 +46,7 @@ use std::{ /// InterfaceValue::Record(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]), /// InterfaceValue::F32(3.), /// ]; -/// let t: T = from_values(&values).unwrap(); +/// let t: T = from_interface_values(&values).unwrap(); /// /// assert_eq!( /// t, @@ -57,7 +57,7 @@ use std::{ /// } /// ); /// ``` -pub fn from_values<'a, T>(values: &'a [InterfaceValue]) -> Result +pub fn from_interface_values<'a, T>(values: &'a [InterfaceValue]) -> Result where T: Deserialize<'a>, { @@ -475,7 +475,7 @@ mod tests { type Error = Error; fn try_from(value: Vec) -> Result { - from_values(&value) + from_interface_values(&value) } } @@ -483,7 +483,7 @@ mod tests { type Error = Error; fn try_from(value: &Vec) -> Result { - from_values(value) + from_interface_values(value) } } }; @@ -640,7 +640,7 @@ mod tests { }; let v = vec![InterfaceValue::String("foo".to_string())]; - let input: S = from_values(&v).unwrap(); + let input: S = from_interface_values(&v).unwrap(); let output = S { x: "foo" }; assert_eq!(input, output); From f9832fecafae5613b564a4f2a70932587c814797 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 15:54:39 +0200 Subject: [PATCH 24/43] feat(interface-types) Implement a serializer for WIT values. --- .../src/interpreter/wasm/serde.rs | 661 +++++++++++++++--- 1 file changed, 571 insertions(+), 90 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde.rs b/lib/interface-types/src/interpreter/wasm/serde.rs index 1e5ac5da285..ed5b9841662 100644 --- a/lib/interface-types/src/interpreter/wasm/serde.rs +++ b/lib/interface-types/src/interpreter/wasm/serde.rs @@ -6,10 +6,7 @@ use crate::{ ast::InterfaceType, interpreter::wasm::values::{FlattenInterfaceValueIterator, InterfaceValue}, }; -use serde::{ - de::{self, DeserializeSeed, SeqAccess, Visitor}, - Deserialize, -}; +use serde::{de, ser, Deserialize, Serialize}; use std::{ fmt::{self, Display}, iter::Peekable, @@ -57,7 +54,7 @@ use std::{ /// } /// ); /// ``` -pub fn from_interface_values<'a, T>(values: &'a [InterfaceValue]) -> Result +pub fn from_interface_values<'a, T>(values: &'a [InterfaceValue]) -> Result where T: Deserialize<'a>, { @@ -66,10 +63,23 @@ where match deserializer.iterator.peek() { None => Ok(result), - _ => Err(Error::InputNotEmpty), + _ => Err(DeserializeError::InputNotEmpty), } } +/// foo +pub fn to_interface_value(value: &T) -> Result +where + T: Serialize, +{ + let mut serializer = Serializer::new(); + value.serialize(&mut serializer)?; + + assert_eq!(serializer.values.len(), 1); + + Ok(serializer.values.pop().unwrap().pop().unwrap()) +} + /// The deserializer. The iterator iterates over `InterfaceValue`s, /// all flatten, see `FlattenInterfaceValueIterator`. struct Deserializer<'de> { @@ -86,7 +96,7 @@ impl<'de> Deserializer<'de> { macro_rules! next { ($method_name:ident, $variant:ident, $type:ty) => { - fn $method_name(&mut self) -> Result<$type, Error> { + fn $method_name(&mut self) -> Result<$type, DeserializeError> { match self.iterator.peek() { Some(InterfaceValue::$variant(value)) => { self.iterator.next(); @@ -94,12 +104,12 @@ macro_rules! next { Ok(*value) } - Some(wrong_value) => Err(Error::TypeMismatch { + Some(wrong_value) => Err(DeserializeError::TypeMismatch { expected_type: InterfaceType::$variant, received_type: (*wrong_value).into(), }), - None => Err(Error::InputEmpty), + None => Err(DeserializeError::InputEmpty), } } } @@ -117,7 +127,7 @@ impl<'de> Deserializer<'de> { next!(next_f32, F32, f32); next!(next_f64, F64, f64); - fn next_string(&mut self) -> Result<&'de str, Error> { + fn next_string(&mut self) -> Result<&'de str, DeserializeError> { match self.iterator.peek() { Some(InterfaceValue::String(v)) => { self.iterator.next(); @@ -125,12 +135,12 @@ impl<'de> Deserializer<'de> { Ok(v) } - Some(wrong_value) => Err(Error::TypeMismatch { + Some(wrong_value) => Err(DeserializeError::TypeMismatch { expected_type: InterfaceType::String, received_type: (*wrong_value).into(), }), - None => Err(Error::InputEmpty), + None => Err(DeserializeError::InputEmpty), } } @@ -140,7 +150,7 @@ impl<'de> Deserializer<'de> { /// Represents an error while deserializing. #[derive(Clone, Debug, PartialEq)] -pub enum Error { +pub enum DeserializeError { /// The input isn't empty, i.e. some values aren't deserialized. InputNotEmpty, @@ -160,19 +170,19 @@ pub enum Error { Message(String), } -impl de::Error for Error { +impl de::Error for DeserializeError { fn custom(msg: T) -> Self { - Error::Message(msg.to_string()) + Self::Message(msg.to_string()) } } -impl Display for Error { +impl Display for DeserializeError { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self { - Error::InputNotEmpty => write!(formatter, "Unexpected input remaining"), - Error::Message(ref msg) => write!(formatter, "{}", msg), - Error::InputEmpty => write!(formatter, "Unexpected end of input"), - Error::TypeMismatch { + 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!( @@ -184,14 +194,14 @@ impl Display for Error { } } -impl std::error::Error for Error {} +impl std::error::Error for DeserializeError {} impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { - type Error = Error; + type Error = DeserializeError; fn deserialize_any(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { match self.iterator.peek() { Some(InterfaceValue::S8(_)) => self.deserialize_i8(visitor), @@ -208,34 +218,34 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { Some(InterfaceValue::I32(_)) => self.deserialize_i32(visitor), Some(InterfaceValue::I64(_)) => self.deserialize_i64(visitor), Some(InterfaceValue::Record(_)) => unreachable!("Records should have been flatten."), // already flatten - None => Err(Error::InputEmpty), + None => Err(DeserializeError::InputEmpty), } } fn deserialize_bool(self, _visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { unimplemented!("`bool` is not supported by WIT for the moment.") } fn deserialize_i8(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { visitor.visit_i8(self.next_s8()?) } fn deserialize_i16(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { visitor.visit_i16(self.next_s16()?) } fn deserialize_i32(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { // Both `InterfaceValue::S32` and `InterfaceValue::I32` // represent `i32`. @@ -244,7 +254,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { fn deserialize_i64(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { // Both `InterfaceValue::S64` and `InterfaceValue::I64` // represent `i64`. @@ -253,91 +263,91 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { fn deserialize_u8(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { visitor.visit_u8(self.next_u8()?) } fn deserialize_u16(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { visitor.visit_u16(self.next_u16()?) } fn deserialize_u32(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { visitor.visit_u32(self.next_u32()?) } fn deserialize_u64(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { visitor.visit_u64(self.next_u64()?) } fn deserialize_f32(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { visitor.visit_f32(self.next_f32()?) } fn deserialize_f64(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { visitor.visit_f64(self.next_f64()?) } fn deserialize_char(self, _visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { todo!("`char` is not supported by WIT for the moment.") } fn deserialize_str(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { visitor.visit_borrowed_str(self.next_string()?) } fn deserialize_string(self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { self.deserialize_str(visitor) } fn deserialize_bytes(self, _visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { todo!("`bytes` is not supported by WIT for the moment.") } fn deserialize_byte_buf(self, _visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { todo!("`bytes` buffer is not supported by WIT for the moment.") } fn deserialize_option(self, _visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { todo!("`option` is not supported by WIT for the moment.") } fn deserialize_unit(self, _visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { todo!("`unit` is not supported by WIT for the moment.") } @@ -348,7 +358,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { _visitor: V, ) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { todo!("`unit_struct` is not supported by WIT for the moment.") } @@ -359,21 +369,21 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { visitor: V, ) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { self.deserialize_seq(visitor) } fn deserialize_seq(mut self, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { visitor.visit_seq(Sequence::new(&mut self)) } fn deserialize_tuple(self, _len: usize, visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { self.deserialize_seq(visitor) } @@ -385,14 +395,14 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { visitor: V, ) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { self.deserialize_seq(visitor) } fn deserialize_map(self, _visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { todo!("`map` is not supported by WIT for the moment.") } @@ -404,7 +414,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { visitor: V, ) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { self.deserialize_seq(visitor) } @@ -416,21 +426,21 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { _visitor: V, ) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { todo!("`enum` is not supported by WIT for the moment.") } fn deserialize_identifier(self, _visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { todo!("`identifier` is not supported by WIT for the moment."); } fn deserialize_ignored_any(self, _visitor: V) -> Result where - V: Visitor<'de>, + V: de::Visitor<'de>, { todo!("`ignored_any` is not implemented for the moment.") } @@ -449,12 +459,12 @@ impl<'a, 'de> Sequence<'a, 'de> { } } -impl<'de, 'a> SeqAccess<'de> for Sequence<'a, 'de> { - type Error = Error; +impl<'de, 'a> de::SeqAccess<'de> for Sequence<'a, 'de> { + type Error = DeserializeError; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> where - T: DeserializeSeed<'de>, + T: de::DeserializeSeed<'de>, { if self.de.iterator.peek().is_none() { return Ok(None); @@ -464,6 +474,382 @@ impl<'de, 'a> SeqAccess<'de> for Sequence<'a, 'de> { } } +/// The serializer. +struct Serializer { + values: Vec>, +} + +impl Serializer { + fn new() -> Self { + Self { + values: vec![vec![]], + } + } + + fn last(&mut self) -> &mut Vec { + let index = self.values.len() - 1; + + &mut self.values[index] + } + + fn push_with_capacity(&mut self, capacity: usize) { + self.values.push(Vec::with_capacity(capacity)); + } + + fn pop(&mut self) -> Vec { + assert!(self.values.len() >= 2); + + self.values.pop().unwrap() + } +} + +/// Represents an error while serializing. +#[derive(Clone, Debug, PartialEq)] +pub enum SerializeError { + /// Arbitrary message. + Message(String), +} + +impl ser::Error for SerializeError { + fn custom(msg: T) -> Self { + Self::Message(msg.to_string()) + } +} + +impl Display for SerializeError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + 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 { + unimplemented!("`bool` is not supported by WIT for the moment.") + } + + fn serialize_i8(self, value: i8) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_i16(self, value: i16) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_i32(self, value: i32) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_i64(self, value: i64) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_u8(self, value: u8) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_u16(self, value: u16) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_u32(self, value: u32) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_u64(self, value: u64) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_f32(self, value: f32) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_f64(self, value: f64) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_char(self, _value: char) -> Result { + todo!("`char` is not supported by WIT for the moment.") + } + + fn serialize_str(self, value: &str) -> Result { + self.last().push(value.to_owned().into()); + + Ok(()) + } + + fn serialize_bytes(self, _value: &[u8]) -> Result { + todo!("`bytes` is not supported by WIT for the moment.") + } + + fn serialize_none(self) -> Result { + self.serialize_unit() + } + + fn serialize_some(self, _value: &T) -> Result + where + T: ?Sized + Serialize, + { + todo!("`some` is not supported by WIT for the moment.") + } + + fn serialize_unit(self) -> Result { + todo!("`unit` is not supported by WIT for the moment.") + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + 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 { + todo!("`unit_variant` is not supported by WIT for the moment.") + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + todo!("`newtype_variant` is not supported by WIT for the moment.") + } + + fn serialize_seq(self, _len: Option) -> Result { + todo!("`seq` is not supported by WIT for the moment.") + } + + fn serialize_tuple(self, _len: usize) -> Result { + todo!("`tuple` is not supported by WIT for the moment.") + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + self.push_with_capacity(len); + + Ok(self) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + todo!("`tuple_variant` is not supported by WIT for the moment.") + } + + fn serialize_map(self, _len: Option) -> Result { + todo!("`map` is not supported by WIT for the moment.") + } + + fn serialize_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + self.push_with_capacity(len); + + Ok(self) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + 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(&mut self, _value: &T) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn end(self) -> Result { + unimplemented!() + } +} + +impl<'a> ser::SerializeTuple for &'a mut Serializer { + type Ok = (); + type Error = SerializeError; + + fn serialize_element(&mut self, _value: &T) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn end(self) -> Result { + unimplemented!() + } +} + +impl<'a> ser::SerializeTupleStruct for &'a mut Serializer { + type Ok = (); + type Error = SerializeError; + + fn serialize_field(&mut self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + 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(&mut self, _value: &T) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn end(self) -> Result { + unimplemented!() + } +} + +impl<'a> ser::SerializeMap for &'a mut Serializer { + type Ok = (); + type Error = SerializeError; + + fn serialize_key(&mut self, _key: &T) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn serialize_value(&mut self, _value: &T) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn end(self) -> Result { + unimplemented!() + } +} + +impl<'a> ser::SerializeStruct for &'a mut Serializer { + type Ok = (); + type Error = SerializeError; + + fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + 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( + &mut self, + _key: &'static str, + _value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn end(self) -> Result { + unimplemented!() + } +} + #[cfg(test)] mod tests { use super::*; @@ -472,7 +858,7 @@ mod tests { macro_rules! try_into { ($ty:ty) => { impl TryFrom> for $ty { - type Error = Error; + type Error = DeserializeError; fn try_from(value: Vec) -> Result { from_interface_values(&value) @@ -480,7 +866,7 @@ mod tests { } impl TryFrom<&Vec> for $ty { - type Error = Error; + type Error = DeserializeError; fn try_from(value: &Vec) -> Result { from_interface_values(value) @@ -594,37 +980,31 @@ mod tests { assert_eq!(input, output); } - macro_rules! value { + macro_rules! deserialize_value { ($test_name:ident, $variant:ident, $ty:ident, $value:expr) => { #[test] #[allow(non_snake_case)] fn $test_name() { - #[derive(Deserialize, Debug, PartialEq)] - struct S { - x: $ty, - }; - - try_into!(S); - - let input: S = vec![InterfaceValue::$variant($value)].try_into().unwrap(); - let output = S { x: $value }; + let input: $ty = + from_interface_values(&vec![InterfaceValue::$variant($value)]).unwrap(); + let output: $ty = $value; assert_eq!(input, output); } }; } - value!(test_deserialize_value__s8, S8, i8, 42); - value!(test_deserialize_value__s16, S16, i16, 42); - value!(test_deserialize_value__s32, S32, i32, 42); - value!(test_deserialize_value__s64, S64, i64, 42); - value!(test_deserialize_value__u8, U8, u8, 42); - value!(test_deserialize_value__u16, U16, u16, 42); - value!(test_deserialize_value__u32, U32, u32, 42); - value!(test_deserialize_value__u64, U64, u64, 42); - value!(test_deserialize_value__f32, F32, f32, 42.); - value!(test_deserialize_value__f64, F32, f32, 42.); - value!( + 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, @@ -634,20 +1014,16 @@ mod tests { #[test] #[allow(non_snake_case)] fn test_deserialize_value__str() { - #[derive(Deserialize, Debug, PartialEq)] - struct S<'a> { - x: &'a str, - }; - - let v = vec![InterfaceValue::String("foo".to_string())]; - let input: S = from_interface_values(&v).unwrap(); - let output = S { x: "foo" }; + 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); } - value!(test_deserialize_value__i32, I32, i32, 42); - value!(test_deserialize_value__i64, I64, i64, 42); + deserialize_value!(test_deserialize_value__i32, I32, i32, 42); + deserialize_value!(test_deserialize_value__i64, I64, i64, 42); #[test] #[allow(non_snake_case)] @@ -690,13 +1066,118 @@ mod tests { try_into!(S); - let input: Result = + let input: Result = vec![InterfaceValue::I32(1), InterfaceValue::I32(2)].try_into(); - let output = Err(Error::TypeMismatch { + let output = Err(DeserializeError::TypeMismatch { expected_type: InterfaceType::I64, received_type: InterfaceType::I32, }); assert_eq!(input, output); } + + 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); + } } From d6f7e3ff02d8a1517473b30ae5a58e986af47381 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 15:59:07 +0200 Subject: [PATCH 25/43] doc(interface-types) Describe `to_interface_value`. --- .../src/interpreter/wasm/serde.rs | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde.rs b/lib/interface-types/src/interpreter/wasm/serde.rs index ed5b9841662..06db49fa1b4 100644 --- a/lib/interface-types/src/interpreter/wasm/serde.rs +++ b/lib/interface-types/src/interpreter/wasm/serde.rs @@ -67,7 +67,46 @@ where } } -/// foo +/// 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(value: &T) -> Result where T: Serialize, From ee0596d2c32c85b453f589ef50b980040c52d9ce Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 16:26:24 +0200 Subject: [PATCH 26/43] feat(interface-types) Restrict supported type in the `Deserializer`. `seq`, `map` and `tuple` for instance are not supported officially. It was fun to play with it at the beginning, but it is time to remove it to match the `Serializer` implementation. --- .../src/interpreter/wasm/serde.rs | 245 ++++++------------ 1 file changed, 74 insertions(+), 171 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde.rs b/lib/interface-types/src/interpreter/wasm/serde.rs index 06db49fa1b4..f89aaf62cd5 100644 --- a/lib/interface-types/src/interpreter/wasm/serde.rs +++ b/lib/interface-types/src/interpreter/wasm/serde.rs @@ -410,25 +410,25 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: de::Visitor<'de>, { - self.deserialize_seq(visitor) + visitor.visit_newtype_struct(self) } - fn deserialize_seq(mut self, visitor: V) -> Result + fn deserialize_seq(self, _visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_seq(Sequence::new(&mut self)) + todo!("`seq` is not supported by WIT for the moment.") } - fn deserialize_tuple(self, _len: usize, visitor: V) -> Result + fn deserialize_tuple(self, _len: usize, _visitor: V) -> Result where V: de::Visitor<'de>, { - self.deserialize_seq(visitor) + todo!("`tuple` is not supported by WIT for the moment.") } fn deserialize_tuple_struct( - self, + mut self, _name: &'static str, _len: usize, visitor: V, @@ -436,7 +436,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: de::Visitor<'de>, { - self.deserialize_seq(visitor) + visitor.visit_seq(Sequence::new(&mut self)) } fn deserialize_map(self, _visitor: V) -> Result @@ -447,7 +447,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { } fn deserialize_struct( - self, + mut self, _name: &'static str, _fields: &'static [&'static str], visitor: V, @@ -455,7 +455,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: de::Visitor<'de>, { - self.deserialize_seq(visitor) + visitor.visit_seq(Sequence::new(&mut self)) } fn deserialize_enum( @@ -892,143 +892,16 @@ impl<'a> ser::SerializeStructVariant for &'a mut Serializer { #[cfg(test)] mod tests { use super::*; - use std::convert::{TryFrom, TryInto}; - - macro_rules! try_into { - ($ty:ty) => { - impl TryFrom> for $ty { - type Error = DeserializeError; - - fn try_from(value: Vec) -> Result { - from_interface_values(&value) - } - } - - impl TryFrom<&Vec> for $ty { - type Error = DeserializeError; - - fn try_from(value: &Vec) -> Result { - from_interface_values(value) - } - } - }; - } - - #[test] - fn test_deserialize_basic() { - #[derive(Deserialize, Debug, PartialEq)] - struct S { - x: i32, - y: i64, - } - - try_into!(S); - - let input: S = vec![InterfaceValue::I32(1), InterfaceValue::I64(2)] - .try_into() - .unwrap(); - let output = S { x: 1, y: 2 }; - - assert_eq!(input, output); - } - - #[test] - fn test_deserialize_compound() { - #[derive(Deserialize, Debug, PartialEq)] - struct Point { - x: i32, - y: i32, - } - - #[derive(Deserialize, Debug, PartialEq)] - struct Compound { - points: (Point, Point), - more_points: Vec, - } - - try_into!(Compound); - - let input: Compound = vec![ - InterfaceValue::I32(1), - InterfaceValue::I32(2), - InterfaceValue::I32(3), - InterfaceValue::I32(4), - InterfaceValue::I32(5), - InterfaceValue::I32(6), - InterfaceValue::I32(7), - InterfaceValue::I32(8), - InterfaceValue::I32(9), - InterfaceValue::I32(10), - ] - .try_into() - .unwrap(); - let output = Compound { - points: (Point { x: 1, y: 2 }, Point { x: 3, y: 4 }), - more_points: vec![ - Point { x: 5, y: 6 }, - Point { x: 7, y: 8 }, - Point { x: 9, y: 10 }, - ], - }; - - assert_eq!(input, output); - } - - #[test] - fn test_deserialize_newtype_struct() { - #[derive(Deserialize, Debug, PartialEq)] - struct S(i32); - - try_into!(S); - - let input: S = vec![InterfaceValue::I32(1)].try_into().unwrap(); - let output = S(1); - - assert_eq!(input, output); - } - - #[test] - fn test_deserialize_tuple() { - #[derive(Deserialize, Debug, PartialEq)] - struct S { - x: (i32, i64), - }; - - try_into!(S); - - let input: S = vec![InterfaceValue::I32(1), InterfaceValue::I64(2)] - .try_into() - .unwrap(); - let output = S { x: (1, 2) }; - - assert_eq!(input, output); - } - - #[test] - fn test_deserialize_tuple_struct() { - #[derive(Deserialize, Debug, PartialEq)] - struct S(i32, i64); - - try_into!(S); - - let input: S = vec![InterfaceValue::I32(1), InterfaceValue::I64(2)] - .try_into() - .unwrap(); - let output = S(1, 2); - - assert_eq!(input, output); - } macro_rules! deserialize_value { ($test_name:ident, $variant:ident, $ty:ident, $value:expr) => { #[test] #[allow(non_snake_case)] fn $test_name() { - let input: $ty = - from_interface_values(&vec![InterfaceValue::$variant($value)]).unwrap(); + let input = vec![InterfaceValue::$variant($value)]; let output: $ty = $value; - assert_eq!(input, output); + assert_eq!(from_interface_values::<$ty>(&input).unwrap(), output); } }; } @@ -1066,53 +939,83 @@ mod tests { #[test] #[allow(non_snake_case)] - fn test_deserialize_value__record() { + fn test_deserialize_value__newtype_struct() { #[derive(Deserialize, Debug, PartialEq)] - struct S(i32, i64); + struct S(i8); + + let input = vec![InterfaceValue::S8(42)]; + let output = S(42); + + assert_eq!(from_interface_values::(&input).unwrap(), output); + } + #[test] + #[allow(non_snake_case)] + fn test_deserialize_value__tuple_struct() { #[derive(Deserialize, Debug, PartialEq)] - struct T { - x: String, - s: S, - y: f32, - }; + struct S(i8, f32); - try_into!(T); - - let input: T = vec![ - InterfaceValue::String("abc".to_string()), - InterfaceValue::Record(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]), - InterfaceValue::F32(3.), - ] - .try_into() - .unwrap(); - let output = T { - x: "abc".to_string(), - s: S(1, 2), - y: 3., - }; + let input = vec![InterfaceValue::Record(vec![ + InterfaceValue::S8(7), + InterfaceValue::F32(42.), + ])]; + let output = S(7, 42.); - assert_eq!(input, output); + assert_eq!(from_interface_values::(&input).unwrap(), output); } #[test] - fn test_deserialize_error_type_mismatch() { + #[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::(&input).unwrap(), output); + } + + #[test] + #[allow(non_snake_case)] + fn test_deserialize_value__struct_nested() { + #[derive(Deserialize, Debug, PartialEq)] + struct Point { x: i32, - y: i64, - }; + y: i32, + z: i32, + } - try_into!(S); + #[derive(Deserialize, Debug, PartialEq)] + struct Line { + p1: Point, + p2: Point, + } - let input: Result = - vec![InterfaceValue::I32(1), InterfaceValue::I32(2)].try_into(); - let output = Err(DeserializeError::TypeMismatch { - expected_type: InterfaceType::I64, - received_type: InterfaceType::I32, - }); + 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!(input, output); + assert_eq!(from_interface_values::(&input).unwrap(), output); } macro_rules! serialize_value { From b0af0143393712bd9f5f0b21b6936e91263d146a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 16:29:52 +0200 Subject: [PATCH 27/43] doc(interface-types) Update an example. --- lib/interface-types/src/interpreter/wasm/serde.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde.rs b/lib/interface-types/src/interpreter/wasm/serde.rs index f89aaf62cd5..c1968a12853 100644 --- a/lib/interface-types/src/interpreter/wasm/serde.rs +++ b/lib/interface-types/src/interpreter/wasm/serde.rs @@ -43,7 +43,7 @@ use std::{ /// InterfaceValue::Record(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]), /// InterfaceValue::F32(3.), /// ]; -/// let t: T = from_interface_values(&values).unwrap(); +/// let t = from_interface_values::(&values).unwrap(); /// /// assert_eq!( /// t, From e385bf66c782380c403a625e46d5cda68bfe57a8 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 16:38:40 +0200 Subject: [PATCH 28/43] feat(interface-types) Make `unwrap`s safe by introducing more errors. --- .../src/interpreter/wasm/serde.rs | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde.rs b/lib/interface-types/src/interpreter/wasm/serde.rs index c1968a12853..59709d12cd1 100644 --- a/lib/interface-types/src/interpreter/wasm/serde.rs +++ b/lib/interface-types/src/interpreter/wasm/serde.rs @@ -114,9 +114,19 @@ where let mut serializer = Serializer::new(); value.serialize(&mut serializer)?; - assert_eq!(serializer.values.len(), 1); + 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. - Ok(serializer.values.pop().unwrap().pop().unwrap()) + 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 deserializer. The iterator iterates over `InterfaceValue`s, @@ -535,16 +545,25 @@ impl Serializer { self.values.push(Vec::with_capacity(capacity)); } - fn pop(&mut self) -> Vec { - assert!(self.values.len() >= 2); - - self.values.pop().unwrap() + fn pop(&mut self) -> Result, SerializeError> { + 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), } @@ -558,6 +577,14 @@ impl ser::Error for SerializeError { 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), } } @@ -804,7 +831,7 @@ impl<'a> ser::SerializeTupleStruct for &'a mut Serializer { } fn end(self) -> Result { - let record = InterfaceValue::Record(self.pop()); + let record = InterfaceValue::Record(self.pop()?); self.last().push(record); Ok(()) @@ -862,7 +889,7 @@ impl<'a> ser::SerializeStruct for &'a mut Serializer { } fn end(self) -> Result { - let record = InterfaceValue::Record(self.pop()); + let record = InterfaceValue::Record(self.pop()?); self.last().push(record); Ok(()) From 010b10d7b076b879eb8bf49bac283b78d759ec00 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 16:39:40 +0200 Subject: [PATCH 29/43] chore(cargo) Update `Cargo.lock`. --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index f5bd1eaae9c..40619ad58f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2855,6 +2855,7 @@ name = "wasmer-interface-types" version = "0.16.2" dependencies = [ "nom", + "serde", "wast", ] From be66ac8aa4f342aae2d03a23543fd9736f4d0150 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 16:41:03 +0200 Subject: [PATCH 30/43] doc(interface-types) Improve `serde` module documentation. --- lib/interface-types/src/interpreter/wasm/serde.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde.rs b/lib/interface-types/src/interpreter/wasm/serde.rs index 59709d12cd1..90dec2aed22 100644 --- a/lib/interface-types/src/interpreter/wasm/serde.rs +++ b/lib/interface-types/src/interpreter/wasm/serde.rs @@ -1,6 +1,6 @@ //! Serde is not necessary to use WIT. It only provides a nicer API -//! for the end-user to rebuild its complex types from WIT values, -//! like `record`. +//! for the end-user to send or receive its complex types to/from WIT +//! values, like `record` for instance. use crate::{ ast::InterfaceType, From 2011fda31d9edc3a1f5185ca4f1cbe01928e565e Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 2 Apr 2020 16:46:23 +0200 Subject: [PATCH 31/43] =?UTF-8?q?chore(interface-types)=20Split=20the=20`s?= =?UTF-8?q?erde`=20module=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … into `serde/ser.rs` and `serde/de.rs`. I like smaller files. --- .../wasm/{serde.rs => serde/de.rs} | 566 +---------------- .../src/interpreter/wasm/serde/mod.rs | 6 + .../src/interpreter/wasm/serde/ser.rs | 570 ++++++++++++++++++ .../src/interpreter/wasm/values.rs | 2 +- 4 files changed, 579 insertions(+), 565 deletions(-) rename lib/interface-types/src/interpreter/wasm/{serde.rs => serde/de.rs} (52%) create mode 100644 lib/interface-types/src/interpreter/wasm/serde/mod.rs create mode 100644 lib/interface-types/src/interpreter/wasm/serde/ser.rs diff --git a/lib/interface-types/src/interpreter/wasm/serde.rs b/lib/interface-types/src/interpreter/wasm/serde/de.rs similarity index 52% rename from lib/interface-types/src/interpreter/wasm/serde.rs rename to lib/interface-types/src/interpreter/wasm/serde/de.rs index 90dec2aed22..7ce955adab4 100644 --- a/lib/interface-types/src/interpreter/wasm/serde.rs +++ b/lib/interface-types/src/interpreter/wasm/serde/de.rs @@ -1,12 +1,10 @@ -//! 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. +//! Provides a deserializer from WIT values to Rust value. use crate::{ ast::InterfaceType, interpreter::wasm::values::{FlattenInterfaceValueIterator, InterfaceValue}, }; -use serde::{de, ser, Deserialize, Serialize}; +use serde::{de, Deserialize}; use std::{ fmt::{self, Display}, iter::Peekable, @@ -67,68 +65,6 @@ where } } -/// 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(value: &T) -> Result -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 deserializer. The iterator iterates over `InterfaceValue`s, /// all flatten, see `FlattenInterfaceValueIterator`. struct Deserializer<'de> { @@ -523,399 +459,6 @@ impl<'de, 'a> de::SeqAccess<'de> for Sequence<'a, 'de> { } } -/// The serializer. -struct Serializer { - values: Vec>, -} - -impl Serializer { - fn new() -> Self { - Self { - values: vec![vec![]], - } - } - - fn last(&mut self) -> &mut Vec { - let index = self.values.len() - 1; - - &mut self.values[index] - } - - fn push_with_capacity(&mut self, capacity: usize) { - self.values.push(Vec::with_capacity(capacity)); - } - - fn pop(&mut self) -> Result, SerializeError> { - 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(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 { - unimplemented!("`bool` is not supported by WIT for the moment.") - } - - fn serialize_i8(self, value: i8) -> Result { - self.last().push(value.into()); - - Ok(()) - } - - fn serialize_i16(self, value: i16) -> Result { - self.last().push(value.into()); - - Ok(()) - } - - fn serialize_i32(self, value: i32) -> Result { - self.last().push(value.into()); - - Ok(()) - } - - fn serialize_i64(self, value: i64) -> Result { - self.last().push(value.into()); - - Ok(()) - } - - fn serialize_u8(self, value: u8) -> Result { - self.last().push(value.into()); - - Ok(()) - } - - fn serialize_u16(self, value: u16) -> Result { - self.last().push(value.into()); - - Ok(()) - } - - fn serialize_u32(self, value: u32) -> Result { - self.last().push(value.into()); - - Ok(()) - } - - fn serialize_u64(self, value: u64) -> Result { - self.last().push(value.into()); - - Ok(()) - } - - fn serialize_f32(self, value: f32) -> Result { - self.last().push(value.into()); - - Ok(()) - } - - fn serialize_f64(self, value: f64) -> Result { - self.last().push(value.into()); - - Ok(()) - } - - fn serialize_char(self, _value: char) -> Result { - todo!("`char` is not supported by WIT for the moment.") - } - - fn serialize_str(self, value: &str) -> Result { - self.last().push(value.to_owned().into()); - - Ok(()) - } - - fn serialize_bytes(self, _value: &[u8]) -> Result { - todo!("`bytes` is not supported by WIT for the moment.") - } - - fn serialize_none(self) -> Result { - self.serialize_unit() - } - - fn serialize_some(self, _value: &T) -> Result - where - T: ?Sized + Serialize, - { - todo!("`some` is not supported by WIT for the moment.") - } - - fn serialize_unit(self) -> Result { - todo!("`unit` is not supported by WIT for the moment.") - } - - fn serialize_unit_struct(self, _name: &'static str) -> Result { - 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 { - todo!("`unit_variant` is not supported by WIT for the moment.") - } - - fn serialize_newtype_struct( - self, - _name: &'static str, - value: &T, - ) -> Result - where - T: ?Sized + Serialize, - { - value.serialize(self) - } - - fn serialize_newtype_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _value: &T, - ) -> Result - where - T: ?Sized + Serialize, - { - todo!("`newtype_variant` is not supported by WIT for the moment.") - } - - fn serialize_seq(self, _len: Option) -> Result { - todo!("`seq` is not supported by WIT for the moment.") - } - - fn serialize_tuple(self, _len: usize) -> Result { - todo!("`tuple` is not supported by WIT for the moment.") - } - - fn serialize_tuple_struct( - self, - _name: &'static str, - len: usize, - ) -> Result { - self.push_with_capacity(len); - - Ok(self) - } - - fn serialize_tuple_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - todo!("`tuple_variant` is not supported by WIT for the moment.") - } - - fn serialize_map(self, _len: Option) -> Result { - todo!("`map` is not supported by WIT for the moment.") - } - - fn serialize_struct( - self, - _name: &'static str, - len: usize, - ) -> Result { - self.push_with_capacity(len); - - Ok(self) - } - - fn serialize_struct_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - 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(&mut self, _value: &T) -> Result - where - T: ?Sized + Serialize, - { - unimplemented!() - } - - fn end(self) -> Result { - unimplemented!() - } -} - -impl<'a> ser::SerializeTuple for &'a mut Serializer { - type Ok = (); - type Error = SerializeError; - - fn serialize_element(&mut self, _value: &T) -> Result - where - T: ?Sized + Serialize, - { - unimplemented!() - } - - fn end(self) -> Result { - unimplemented!() - } -} - -impl<'a> ser::SerializeTupleStruct for &'a mut Serializer { - type Ok = (); - type Error = SerializeError; - - fn serialize_field(&mut self, value: &T) -> Result - where - T: ?Sized + Serialize, - { - value.serialize(&mut **self) - } - - fn end(self) -> Result { - 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(&mut self, _value: &T) -> Result - where - T: ?Sized + Serialize, - { - unimplemented!() - } - - fn end(self) -> Result { - unimplemented!() - } -} - -impl<'a> ser::SerializeMap for &'a mut Serializer { - type Ok = (); - type Error = SerializeError; - - fn serialize_key(&mut self, _key: &T) -> Result - where - T: ?Sized + Serialize, - { - unimplemented!() - } - - fn serialize_value(&mut self, _value: &T) -> Result - where - T: ?Sized + Serialize, - { - unimplemented!() - } - - fn end(self) -> Result { - unimplemented!() - } -} - -impl<'a> ser::SerializeStruct for &'a mut Serializer { - type Ok = (); - type Error = SerializeError; - - fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result - where - T: ?Sized + Serialize, - { - value.serialize(&mut **self) - } - - fn end(self) -> Result { - 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( - &mut self, - _key: &'static str, - _value: &T, - ) -> Result - where - T: ?Sized + Serialize, - { - unimplemented!() - } - - fn end(self) -> Result { - unimplemented!() - } -} - #[cfg(test)] mod tests { use super::*; @@ -1044,109 +587,4 @@ mod tests { assert_eq!(from_interface_values::(&input).unwrap(), output); } - - 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); - } } diff --git a/lib/interface-types/src/interpreter/wasm/serde/mod.rs b/lib/interface-types/src/interpreter/wasm/serde/mod.rs new file mode 100644 index 00000000000..6cc35e27871 --- /dev/null +++ b/lib/interface-types/src/interpreter/wasm/serde/mod.rs @@ -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; diff --git a/lib/interface-types/src/interpreter/wasm/serde/ser.rs b/lib/interface-types/src/interpreter/wasm/serde/ser.rs new file mode 100644 index 00000000000..0fdd923588e --- /dev/null +++ b/lib/interface-types/src/interpreter/wasm/serde/ser.rs @@ -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(value: &T) -> Result +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>, +} + +impl Serializer { + fn new() -> Self { + Self { + values: vec![vec![]], + } + } + + fn last(&mut self) -> &mut Vec { + let index = self.values.len() - 1; + + &mut self.values[index] + } + + fn push_with_capacity(&mut self, capacity: usize) { + self.values.push(Vec::with_capacity(capacity)); + } + + fn pop(&mut self) -> Result, SerializeError> { + 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(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 { + unimplemented!("`bool` is not supported by WIT for the moment.") + } + + fn serialize_i8(self, value: i8) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_i16(self, value: i16) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_i32(self, value: i32) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_i64(self, value: i64) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_u8(self, value: u8) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_u16(self, value: u16) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_u32(self, value: u32) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_u64(self, value: u64) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_f32(self, value: f32) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_f64(self, value: f64) -> Result { + self.last().push(value.into()); + + Ok(()) + } + + fn serialize_char(self, _value: char) -> Result { + todo!("`char` is not supported by WIT for the moment.") + } + + fn serialize_str(self, value: &str) -> Result { + self.last().push(value.to_owned().into()); + + Ok(()) + } + + fn serialize_bytes(self, _value: &[u8]) -> Result { + todo!("`bytes` is not supported by WIT for the moment.") + } + + fn serialize_none(self) -> Result { + self.serialize_unit() + } + + fn serialize_some(self, _value: &T) -> Result + where + T: ?Sized + Serialize, + { + todo!("`some` is not supported by WIT for the moment.") + } + + fn serialize_unit(self) -> Result { + todo!("`unit` is not supported by WIT for the moment.") + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + 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 { + todo!("`unit_variant` is not supported by WIT for the moment.") + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + todo!("`newtype_variant` is not supported by WIT for the moment.") + } + + fn serialize_seq(self, _len: Option) -> Result { + todo!("`seq` is not supported by WIT for the moment.") + } + + fn serialize_tuple(self, _len: usize) -> Result { + todo!("`tuple` is not supported by WIT for the moment.") + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + self.push_with_capacity(len); + + Ok(self) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + todo!("`tuple_variant` is not supported by WIT for the moment.") + } + + fn serialize_map(self, _len: Option) -> Result { + todo!("`map` is not supported by WIT for the moment.") + } + + fn serialize_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + self.push_with_capacity(len); + + Ok(self) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + 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(&mut self, _value: &T) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn end(self) -> Result { + unimplemented!() + } +} + +impl<'a> ser::SerializeTuple for &'a mut Serializer { + type Ok = (); + type Error = SerializeError; + + fn serialize_element(&mut self, _value: &T) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn end(self) -> Result { + unimplemented!() + } +} + +impl<'a> ser::SerializeTupleStruct for &'a mut Serializer { + type Ok = (); + type Error = SerializeError; + + fn serialize_field(&mut self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + 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(&mut self, _value: &T) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn end(self) -> Result { + unimplemented!() + } +} + +impl<'a> ser::SerializeMap for &'a mut Serializer { + type Ok = (); + type Error = SerializeError; + + fn serialize_key(&mut self, _key: &T) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn serialize_value(&mut self, _value: &T) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn end(self) -> Result { + unimplemented!() + } +} + +impl<'a> ser::SerializeStruct for &'a mut Serializer { + type Ok = (); + type Error = SerializeError; + + fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + 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( + &mut self, + _key: &'static str, + _value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + unimplemented!() + } + + fn end(self) -> Result { + 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); + } +} diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 1ba005d8f57..44df1d5fc4d 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -5,7 +5,7 @@ use crate::errors::WasmValueNativeCastError; use std::{convert::TryFrom, slice::Iter}; #[cfg(feature = "serde")] -pub use crate::interpreter::wasm::serde::*; +pub use crate::interpreter::wasm::serde::{de::*, ser::*}; /// A WIT value. #[derive(Debug, Clone, PartialEq)] From 6cef1c244ac4294e103d35c3ad3c8849ac2d59e7 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 3 Apr 2020 14:46:59 +0200 Subject: [PATCH 32/43] doc(interface-types) Fix typos. --- lib/interface-types/Cargo.toml | 4 ++-- lib/interface-types/src/ast.rs | 2 +- lib/interface-types/src/decoders/binary.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index 34e19c49cd5..3b9c17996b8 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -12,8 +12,8 @@ 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 transform WIT values like -# `record`s to Rust sum types. +# 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] diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index f0e03086ee8..b4eabd87f9e 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -53,7 +53,7 @@ pub enum InterfaceType { Record(RecordType), } -/// Representing a record type. +/// Represents a record type. #[derive(PartialEq, Debug, Clone)] pub struct RecordType { /// Types representing the fields. diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 1d8284e0536..68396cfd8ed 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -106,7 +106,7 @@ fn ty<'input, E: ParseError<&'input [u8]>>( Ok((input, ty)) } -/// Parse an record type. +/// Parse a record type. fn record_type<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], RecordType, E> { From 3159da5bfefae031859db6b9a0c53faf649cf7f7 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 3 Apr 2020 16:13:44 +0200 Subject: [PATCH 33/43] feat(interface-types) Add the `Vec1` type to represent a non-empty vector. `Vec1` is used by `RecordType` to ensure that a record have at least 1 field. Then an `InterfaceValue::Record` is ensured to get at least one value with the type-checking pass. --- lib/interface-types/src/ast.rs | 4 +- lib/interface-types/src/decoders/binary.rs | 21 ++++--- lib/interface-types/src/decoders/wat.rs | 18 +++--- lib/interface-types/src/encoders/binary.rs | 12 ++-- lib/interface-types/src/encoders/wat.rs | 12 ++-- .../src/interpreter/instructions/mod.rs | 4 +- .../src/interpreter/instructions/records.rs | 2 +- .../src/interpreter/wasm/values.rs | 11 ++-- lib/interface-types/src/lib.rs | 1 + lib/interface-types/src/macros.rs | 25 ++++++++ lib/interface-types/src/vec1.rs | 62 +++++++++++++++++++ lib/interface-types/tests/binary.rs | 3 +- 12 files changed, 136 insertions(+), 39 deletions(-) create mode 100644 lib/interface-types/src/vec1.rs diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index b4eabd87f9e..542ef092641 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -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. @@ -57,7 +57,7 @@ pub enum InterfaceType { #[derive(PartialEq, Debug, Clone)] pub struct RecordType { /// Types representing the fields. - pub fields: Vec, + pub fields: Vec1, } /// Represents the kind of type. diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 68396cfd8ed..5aae1482ac1 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -1,6 +1,6 @@ //! 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, @@ -112,7 +112,12 @@ fn record_type<'input, E: ParseError<&'input [u8]>>( ) -> IResult<&'input [u8], RecordType, E> { let (output, fields) = list(input, ty)?; - Ok((output, RecordType { fields })) + Ok(( + output, + RecordType { + fields: Vec1::new(fields).expect("Record must have at least one field, zero given."), + }, + )) } /// Parse a UTF-8 string. @@ -640,7 +645,7 @@ mod tests { InterfaceType::I32, InterfaceType::I64, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::S32], + fields: vec1![InterfaceType::S32], }), ], )); @@ -670,16 +675,16 @@ mod tests { &[0x01][..], vec![ RecordType { - fields: vec![InterfaceType::String], + fields: vec1![InterfaceType::String], }, RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: vec1![InterfaceType::String, InterfaceType::I32], }, RecordType { - fields: vec![ + fields: vec1![ InterfaceType::String, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I32], + fields: vec1![InterfaceType::I32, InterfaceType::I32], }), InterfaceType::F64, ], @@ -864,7 +869,7 @@ mod tests { outputs: vec![InterfaceType::S32], }, Type::Record(RecordType { - fields: vec![InterfaceType::S32, InterfaceType::S32], + fields: vec1![InterfaceType::S32, InterfaceType::S32], }), ], )); diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index e7fc976b34f..9b07be0490f 100644 --- a/lib/interface-types/src/decoders/wat.rs +++ b/lib/interface-types/src/decoders/wat.rs @@ -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}; @@ -151,7 +151,9 @@ impl Parse<'_> for RecordType { })?); } - Ok(RecordType { fields }) + Ok(RecordType { + fields: Vec1::new(fields).expect("Record must have at least one field, zero given."), + }) } } @@ -681,7 +683,7 @@ mod tests { InterfaceType::I32, InterfaceType::I64, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::String], + fields: vec1![InterfaceType::String], }), ]; @@ -704,16 +706,16 @@ mod tests { ]; let outputs = vec![ RecordType { - fields: vec![InterfaceType::String], + fields: vec1![InterfaceType::String], }, RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: vec1![InterfaceType::String, InterfaceType::I32], }, RecordType { - fields: vec![ + fields: vec1![ InterfaceType::String, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I32], + fields: vec1![InterfaceType::I32, InterfaceType::I32], }), InterfaceType::F64, ], @@ -874,7 +876,7 @@ mod tests { fn test_type_record() { let input = buffer(r#"(@interface type (record (field string) (field i32)))"#); let output = Interface::Type(Type::Record(RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: vec1![InterfaceType::String, InterfaceType::I32], })); assert_eq!(parser::parse::(&input).unwrap(), output); diff --git a/lib/interface-types/src/encoders/binary.rs b/lib/interface-types/src/encoders/binary.rs index 2370017a211..2849dce4927 100644 --- a/lib/interface-types/src/encoders/binary.rs +++ b/lib/interface-types/src/encoders/binary.rs @@ -445,7 +445,7 @@ mod tests { assert_to_bytes!(InterfaceType::I64, &[0x0d]); assert_to_bytes!( InterfaceType::Record(RecordType { - fields: vec![InterfaceType::String] + fields: vec1![InterfaceType::String] }), &[0x0e, 0x01, 0x0a] ); @@ -455,7 +455,7 @@ mod tests { fn test_record_type() { assert_to_bytes!( RecordType { - fields: vec![InterfaceType::String] + fields: vec1![InterfaceType::String] }, &[ 0x01, // 1 field @@ -464,7 +464,7 @@ mod tests { ); assert_to_bytes!( RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32] + fields: vec1![InterfaceType::String, InterfaceType::I32] }, &[ 0x02, // 2 fields @@ -474,10 +474,10 @@ mod tests { ); assert_to_bytes!( RecordType { - fields: vec![ + fields: vec1![ InterfaceType::String, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I32], + fields: vec1![InterfaceType::I32, InterfaceType::I32], }), InterfaceType::F64, ], @@ -542,7 +542,7 @@ mod tests { fn test_type_record() { assert_to_bytes!( Type::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I64], + fields: vec1![InterfaceType::I32, InterfaceType::I64], }), &[ 0x01, // record type diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 6ec680ceed0..a141b23f868 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -368,7 +368,7 @@ mod tests { (&InterfaceType::I32).to_string(), (&InterfaceType::I64).to_string(), (&InterfaceType::Record(RecordType { - fields: vec![InterfaceType::String], + fields: vec1![InterfaceType::String], })) .to_string(), ]; @@ -397,18 +397,18 @@ mod tests { fn test_record_type() { let inputs = vec![ (&RecordType { - fields: vec![InterfaceType::String], + fields: vec1![InterfaceType::String], }) .to_string(), (&RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: vec1![InterfaceType::String, InterfaceType::I32], }) .to_string(), (&RecordType { - fields: vec![ + fields: vec1![ InterfaceType::String, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I32], + fields: vec1![InterfaceType::I32, InterfaceType::I32], }), InterfaceType::F64, ], @@ -539,7 +539,7 @@ mod tests { }) .to_string(), (&Type::Record(RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: vec1![InterfaceType::String, InterfaceType::I32], })) .to_string(), ]; diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index cf71c6e57d6..39f25abcc0d 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -326,10 +326,10 @@ pub(crate) mod tests { }, memory: Memory::new(vec![Cell::new(0); 128]), wit_types: vec![ast::Type::Record(ast::RecordType { - fields: vec![ + fields: vec1![ InterfaceType::I32, InterfaceType::Record(ast::RecordType { - fields: vec![InterfaceType::String, InterfaceType::F32], + fields: vec1![InterfaceType::String, InterfaceType::F32], }), InterfaceType::I64, ], diff --git a/lib/interface-types/src/interpreter/instructions/records.rs b/lib/interface-types/src/interpreter/instructions/records.rs index 5ed388fc79b..15b95518d4e 100644 --- a/lib/interface-types/src/interpreter/instructions/records.rs +++ b/lib/interface-types/src/interpreter/instructions/records.rs @@ -293,7 +293,7 @@ mod tests { let mut instance = Instance::new(); instance.wit_types.push( Type::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I32], + fields: vec1![InterfaceType::I32, InterfaceType::I32], }) ); diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 44df1d5fc4d..66a3d8dcece 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -1,7 +1,7 @@ //! Defines WIT values and associated operations. pub use crate::ast::{InterfaceType, RecordType}; -use crate::errors::WasmValueNativeCastError; +use crate::{errors::WasmValueNativeCastError, vec1::Vec1}; use std::{convert::TryFrom, slice::Iter}; #[cfg(feature = "serde")] @@ -85,7 +85,8 @@ impl Default for InterfaceValue { impl From<&Vec> for RecordType { fn from(values: &Vec) -> Self { RecordType { - fields: values.iter().map(Into::into).collect(), + fields: Vec1::new(values.iter().map(Into::into).collect()) + .expect("Record must have at least one field, zero given."), } } } @@ -225,7 +226,7 @@ mod tests { InterfaceValue::S8(2) ])), InterfaceType::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::S8] + fields: vec1![InterfaceType::I32, InterfaceType::S8] }) ); @@ -239,10 +240,10 @@ mod tests { InterfaceValue::S8(2) ])), InterfaceType::Record(RecordType { - fields: vec![ + fields: vec1![ InterfaceType::I32, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::String, InterfaceType::F64] + fields: vec1![InterfaceType::String, InterfaceType::F64] }), InterfaceType::S8 ] diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index ebf8603d2a7..fd319ac024a 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -57,3 +57,4 @@ pub mod decoders; pub mod encoders; pub mod errors; pub mod interpreter; +pub mod vec1; diff --git a/lib/interface-types/src/macros.rs b/lib/interface-types/src/macros.rs index b2e2cfa807d..8ff41099f8a 100644 --- a/lib/interface-types/src/macros.rs +++ b/lib/interface-types/src/macros.rs @@ -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 { diff --git a/lib/interface-types/src/vec1.rs b/lib/interface-types/src/vec1.rs new file mode 100644 index 00000000000..3e89293a6c3 --- /dev/null +++ b/lib/interface-types/src/vec1.rs @@ -0,0 +1,62 @@ +//! `Vec1` represents a non-empty `Vec`. + +use std::{ + error, + fmt::{self, Debug}, + ops, +}; + +/// `Vec1` represents a non-empty `Vec`. It derefs to `Vec` +/// directly. +#[derive(Clone, PartialEq)] +pub struct Vec1(Vec) +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 Vec1 +where + T: Debug, +{ + /// Creates a new non-empty vector, based on an inner `Vec`. If + /// the inner vector is empty, a `EmptyVec` error is returned. + pub fn new(items: Vec) -> Result { + if items.len() == 0 { + Err(EmptyVec) + } else { + Ok(Self(items)) + } + } +} + +impl fmt::Debug for Vec1 +where + T: Debug, +{ + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "{:?}", self.0) + } +} + +impl ops::Deref for Vec1 +where + T: Debug, +{ + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/lib/interface-types/tests/binary.rs b/lib/interface-types/tests/binary.rs index 2238c972a5a..c9d470921cc 100644 --- a/lib/interface-types/tests/binary.rs +++ b/lib/interface-types/tests/binary.rs @@ -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. @@ -16,7 +17,7 @@ fn test_binary_encoding_decoding_roundtrip() { outputs: vec![InterfaceType::S32], }, Type::Record(RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: Vec1::new(vec![InterfaceType::String, InterfaceType::I32]).unwrap(), }), ], imports: vec![Import { From d186142507cb7d37e79df5b51d9591d920216288 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 3 Apr 2020 16:16:53 +0200 Subject: [PATCH 34/43] doc(interface-types) Fix a typo. --- lib/interface-types/src/interpreter/wasm/serde/de.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde/de.rs b/lib/interface-types/src/interpreter/wasm/serde/de.rs index 7ce955adab4..50cabdddd1c 100644 --- a/lib/interface-types/src/interpreter/wasm/serde/de.rs +++ b/lib/interface-types/src/interpreter/wasm/serde/de.rs @@ -202,7 +202,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { 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 flatten."), // already flatten + Some(InterfaceValue::Record(_)) => unreachable!("Records should have been flattened."), // already flattened None => Err(DeserializeError::InputEmpty), } } From e160feb99a143e4c947088c36e943989db017f50 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 3 Apr 2020 16:18:24 +0200 Subject: [PATCH 35/43] fix(interface-types) Use lazy evaluation in the deserializer. --- lib/interface-types/src/interpreter/wasm/serde/de.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde/de.rs b/lib/interface-types/src/interpreter/wasm/serde/de.rs index 50cabdddd1c..fb6b335bd3e 100644 --- a/lib/interface-types/src/interpreter/wasm/serde/de.rs +++ b/lib/interface-types/src/interpreter/wasm/serde/de.rs @@ -234,7 +234,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { { // Both `InterfaceValue::S32` and `InterfaceValue::I32` // represent `i32`. - visitor.visit_i32(self.next_s32().or(self.next_i32())?) + visitor.visit_i32(self.next_s32().or_else(|_| self.next_i32())?) } fn deserialize_i64(self, visitor: V) -> Result From 398d791c9f1566e73dd91b03fbdbf3e475707e84 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 3 Apr 2020 16:19:07 +0200 Subject: [PATCH 36/43] fix(interface-types) Also apply another lazy evaluation in the deserializer. --- lib/interface-types/src/interpreter/wasm/serde/de.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde/de.rs b/lib/interface-types/src/interpreter/wasm/serde/de.rs index fb6b335bd3e..dba8e8f0000 100644 --- a/lib/interface-types/src/interpreter/wasm/serde/de.rs +++ b/lib/interface-types/src/interpreter/wasm/serde/de.rs @@ -243,7 +243,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { { // Both `InterfaceValue::S64` and `InterfaceValue::I64` // represent `i64`. - visitor.visit_i64(self.next_s64().or(self.next_i64())?) + visitor.visit_i64(self.next_s64().or_else(|_| self.next_i64())?) } fn deserialize_u8(self, visitor: V) -> Result From e4921bd496ea1d8b958b400166c4addf95553f97 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 3 Apr 2020 16:20:22 +0200 Subject: [PATCH 37/43] feat(interface-types) Use `Vec::last_mut` to simplify code. --- lib/interface-types/src/interpreter/wasm/serde/ser.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde/ser.rs b/lib/interface-types/src/interpreter/wasm/serde/ser.rs index 0fdd923588e..e56ecab5360 100644 --- a/lib/interface-types/src/interpreter/wasm/serde/ser.rs +++ b/lib/interface-types/src/interpreter/wasm/serde/ser.rs @@ -79,9 +79,7 @@ impl Serializer { } fn last(&mut self) -> &mut Vec { - let index = self.values.len() - 1; - - &mut self.values[index] + self.values.last_mut().unwrap() } fn push_with_capacity(&mut self, capacity: usize) { From 5119b6a58f83455a68bffe34c780496350244a2b Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 3 Apr 2020 16:22:26 +0200 Subject: [PATCH 38/43] feat(interface-types) Simplify `FlattenInterfaceValueIterator` with `last_mut`. --- lib/interface-types/src/interpreter/wasm/values.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 66a3d8dcece..4e93a3b19b8 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -156,13 +156,7 @@ impl<'a> Iterator for FlattenInterfaceValueIterator<'a> { type Item = &'a InterfaceValue; fn next(&mut self) -> Option { - if self.iterators.is_empty() { - return None; - } - - let index = self.iterators.len() - 1; - - match self.iterators[index].next() { + match self.iterators.last_mut()?.next() { // End of the current iterator, go back to the previous // one. None => { From 4ad3d7f1237e01d2772558e6f0840593e87a33fc Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Sat, 4 Apr 2020 08:10:38 +0200 Subject: [PATCH 39/43] doc(interface-types) Explain a part of an algorithm. --- lib/interface-types/src/interpreter/wasm/serde/ser.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/interface-types/src/interpreter/wasm/serde/ser.rs b/lib/interface-types/src/interpreter/wasm/serde/ser.rs index e56ecab5360..dd3cfb9ea8b 100644 --- a/lib/interface-types/src/interpreter/wasm/serde/ser.rs +++ b/lib/interface-types/src/interpreter/wasm/serde/ser.rs @@ -87,6 +87,8 @@ impl Serializer { } fn pop(&mut self) -> Result, 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 { From 4faf827dc9a595e618e3b37cc9e1aea68ec68070 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 6 Apr 2020 07:54:51 +0200 Subject: [PATCH 40/43] feat(interface-types) Avoid clones by using owned items in an iterator. `into_iter` owned the item, so no need to clone when moving them in the stack. --- lib/interface-types/src/interpreter/instructions/call_core.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/call_core.rs b/lib/interface-types/src/interpreter/instructions/call_core.rs index 7a8e4985d8e..9da19ef176c 100644 --- a/lib/interface-types/src/interpreter/instructions/call_core.rs +++ b/lib/interface-types/src/interpreter/instructions/call_core.rs @@ -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(()) From c2b1b87f9a164c1aa8d0838f616fc8866fa5316c Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 6 Apr 2020 07:55:56 +0200 Subject: [PATCH 41/43] feat(interface-types) Use `VecDeque` to remove unsafe code. --- .../src/interpreter/instructions/records.rs | 30 ++++--------------- lib/interface-types/src/lib.rs | 2 +- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/records.rs b/lib/interface-types/src/interpreter/instructions/records.rs index 15b95518d4e..053bee7cb77 100644 --- a/lib/interface-types/src/interpreter/instructions/records.rs +++ b/lib/interface-types/src/interpreter/instructions/records.rs @@ -8,7 +8,7 @@ use crate::{ Instruction, }, }; -use std::mem::{transmute, MaybeUninit}; +use std::collections::VecDeque; /// Build a `InterfaceValue::Record` based on values on the stack. /// @@ -52,37 +52,21 @@ use std::mem::{transmute, MaybeUninit}; /// /// This latter approach allows to allocate one and final vector to /// hold all the record values. -#[allow(unsafe_code)] fn record_lift_( stack: &mut Stack, record_type: &RecordType, ) -> Result { let length = record_type.fields.len(); - let mut values = { - // Initialize a vector of length `length` with `MaybeUninit` - // values. - let mut v = Vec::with_capacity(length); - - for _ in 0..length { - v.push(MaybeUninit::::uninit()); - } - - v - }; - let max = length - 1; + let mut values = VecDeque::with_capacity(length); // Iterate over fields in reverse order to match the stack `pop` // order. - for (nth, field) in record_type.fields.iter().rev().enumerate() { + 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. - let value = record_lift_(stack, &record_type)?; - - unsafe { - values[max - nth].as_mut_ptr().write(value); - } + values.push_front(record_lift_(stack, &record_type)?) } // Any other type. ty => { @@ -96,14 +80,12 @@ fn record_lift_( }); } - unsafe { - values[max - nth].as_mut_ptr().write(value); - } + values.push_front(value) } } } - Ok(InterfaceValue::Record(unsafe { transmute(values) })) + Ok(InterfaceValue::Record(values.into_iter().collect())) } executable_instruction!( diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index fd319ac024a..1e1c5c24bce 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -41,12 +41,12 @@ missing_docs, nonstandard_style, unreachable_patterns, - unsafe_code, unused_imports, unused_mut, unused_unsafe, unused_variables )] +#![forbid(unsafe_code)] #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] #![doc(html_logo_url = "https://github.com/wasmerio.png")] From 27053d5ca6e8aeadfbecc0d0356b12a7504abd50 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 6 Apr 2020 08:09:10 +0200 Subject: [PATCH 42/43] doc(interface-types) Rewrite `record_lift_` documentation. --- .../src/interpreter/instructions/records.rs | 51 ++++--------------- 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/records.rs b/lib/interface-types/src/interpreter/instructions/records.rs index 053bee7cb77..ad1c58e85fa 100644 --- a/lib/interface-types/src/interpreter/instructions/records.rs +++ b/lib/interface-types/src/interpreter/instructions/records.rs @@ -10,48 +10,19 @@ use crate::{ }; use std::collections::VecDeque; -/// Build a `InterfaceValue::Record` based on values on the stack. +/// 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`. To simplify this -/// algorithm that also typed-checks values when hydrating, the number -/// of values to read from the stack isn't known ahead-of-time. Thus, -/// the `Stack::pop` method cannot be used, and `Stack::pop1` is used -/// instead. It implies that values are read one after the other from -/// the stack, in a natural reverse order, from `value_n` to -/// `value_1`. -/// -/// Consequently, record fields are filled in reverse order, from -/// `field_n` to `field_1`. -/// -/// A basic algorithm would then be: -/// -/// ```rust,ignore -/// let mut values = vec![]; -/// -/// // Read fields in reverse-order, from `field_n` to `field_1`. -/// for field in fields.iter().rev() { -/// let value = stack.pop1(); -/// // type-check with `field` and `value`, to finally… -/// values.push(value); -/// } -/// -/// InterfaceValue::Record(values.iter().rev().collect()) -/// ``` -/// -/// Note that it is required to reverse the `values` vector at the end -/// because `InterfaceValue::Record` expects its values to match the -/// original `fields` order. -/// -/// Because this approach allocates two vectors for `values`, another -/// approach has been adopted. `values` is an initialized vector -/// containing uninitialized values of type -/// `MaybeUninit`. With this approach, it is possible -/// to fill `values` from index `n` to `0`. Once `values` is entirely -/// filled, it is `transmute`d to `Vec`. -/// -/// This latter approach allows to allocate one and final vector to -/// hold all the record values. +/// 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, record_type: &RecordType, From 9f10b7ae50ab9202c24736c25549b68f33a7d923 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 6 Apr 2020 08:13:10 +0200 Subject: [PATCH 43/43] doc(interface-types) Update `from_interface_values`'s doc. Using `InterfaceValue::Record` explicitely doesn't change anything since values are flatten, but it's better for a usual reading to avoid confusion. --- lib/interface-types/src/interpreter/wasm/serde/de.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/interface-types/src/interpreter/wasm/serde/de.rs b/lib/interface-types/src/interpreter/wasm/serde/de.rs index dba8e8f0000..49e166e8340 100644 --- a/lib/interface-types/src/interpreter/wasm/serde/de.rs +++ b/lib/interface-types/src/interpreter/wasm/serde/de.rs @@ -36,11 +36,11 @@ use std::{ /// y: f32, /// }; /// -/// let values = vec![ +/// 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::(&values).unwrap(); /// /// assert_eq!( @@ -513,7 +513,7 @@ mod tests { #[derive(Deserialize, Debug, PartialEq)] struct S(i8); - let input = vec![InterfaceValue::S8(42)]; + let input = vec![InterfaceValue::Record(vec![InterfaceValue::S8(42)])]; let output = S(42); assert_eq!(from_interface_values::(&input).unwrap(), output);