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", ] diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index 3bcebd33740..3b9c17996b8 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -10,3 +10,11 @@ edition = "2018" [dependencies] nom = "5.1" wast = "8.0" + +# `serde` is useful only to simplify the users' life. It is not +# required by WIT itself, is is used to cross the boundary between the +# host and WIT more easily, but it is not used inside Wasm. +serde = { version = "1.0", features = ["derive"], optional = true } + +[features] +default = ["serde"] \ No newline at end of file diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 9828043db0d..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. @@ -48,20 +48,50 @@ pub enum InterfaceType { /// A 64-bits integer (as defiend in WebAssembly core). I64, + + /// A record. + Record(RecordType), +} + +/// Represents a record type. +#[derive(PartialEq, Debug, Clone)] +pub struct RecordType { + /// Types representing the fields. + pub fields: Vec1, } -/// 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(RecordType), } /// Represents an imported function. diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index d8699a61fe3..5aae1482ac1 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -1,33 +1,21 @@ //! Parse the WIT binary representation into an [AST](crate::ast). -use crate::{ast::*, interpreter::Instruction}; +use crate::{ast::*, interpreter::Instruction, vec1::Vec1}; use nom::{ error::{make_error, ErrorKind, ParseError}, Err, IResult, }; use std::{convert::TryFrom, str}; -/// Parse an `InterfaceType`. -impl TryFrom for InterfaceType { +/// Parse a type kind. +impl TryFrom for TypeKind { 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."), + 0x00 => Self::Function, + 0x01 => Self::Record, + _ => return Err("Unknown type kind code."), }) } } @@ -82,6 +70,56 @@ fn uleb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'i )) } +/// Parse an interface type. +fn ty<'input, E: ParseError<&'input [u8]>>( + mut input: &'input [u8], +) -> IResult<&'input [u8], InterfaceType, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + consume!((input, opcode) = byte(input)?); + + let ty = match opcode { + 0x00 => InterfaceType::S8, + 0x01 => InterfaceType::S16, + 0x02 => InterfaceType::S32, + 0x03 => InterfaceType::S64, + 0x04 => InterfaceType::U8, + 0x05 => InterfaceType::U16, + 0x06 => InterfaceType::U32, + 0x07 => InterfaceType::U64, + 0x08 => InterfaceType::F32, + 0x09 => InterfaceType::F64, + 0x0a => InterfaceType::String, + 0x0b => InterfaceType::Anyref, + 0x0c => InterfaceType::I32, + 0x0d => InterfaceType::I64, + 0x0e => { + consume!((input, record_type) = record_type(input)?); + + InterfaceType::Record(record_type) + } + _ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))), + }; + + Ok((input, ty)) +} + +/// Parse a record type. +fn record_type<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], RecordType, E> { + let (output, fields) = list(input, ty)?; + + Ok(( + output, + RecordType { + fields: Vec1::new(fields).expect("Record must have at least one field, zero given."), + }, + )) +} + /// Parse a UTF-8 string. fn string<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], @@ -131,22 +169,6 @@ fn list<'input, I, E: ParseError<&'input [u8]>>( Ok((input, items)) } -/// Parse a type. -fn ty<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], -) -> IResult<&'input [u8], InterfaceType, E> { - if input.is_empty() { - return Err(Err::Error(make_error(input, ErrorKind::Eof))); - } - - let (output, ty) = byte(input)?; - - match InterfaceType::try_from(ty) { - Ok(ty) => Ok((output, ty)), - Err(_) => Err(Err::Error(make_error(input, ErrorKind::ParseTo))), - } -} - /// Parse an instruction with its arguments. fn instruction<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], @@ -156,6 +178,7 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( Ok(match opcode { 0x00 => { consume!((input, argument_0) = uleb(input)?); + ( input, Instruction::ArgumentGet { @@ -166,6 +189,7 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x01 => { consume!((input, argument_0) = uleb(input)?); + ( input, Instruction::CallCore { @@ -208,9 +232,9 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x21 => (input, Instruction::I64FromU64), 0x22 => (input, Instruction::StringLiftMemory), - 0x23 => { consume!((input, argument_0) = uleb(input)?); + ( input, Instruction::StringLowerMemory { @@ -218,9 +242,29 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( }, ) } - 0x24 => (input, Instruction::StringSize), + 0x25 => { + consume!((input, argument_0) = uleb(input)?); + + ( + input, + Instruction::RecordLift { + type_index: argument_0 as u32, + }, + ) + } + 0x26 => { + consume!((input, argument_0) = uleb(input)?); + + ( + input, + Instruction::RecordLower { + type_index: argument_0 as u32, + }, + ) + } + _ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))), }) } @@ -234,10 +278,25 @@ fn types<'input, E: ParseError<&'input [u8]>>( let mut types = Vec::with_capacity(number_of_types as usize); for _ in 0..number_of_types { - consume!((input, inputs) = list(input, ty)?); - consume!((input, outputs) = list(input, ty)?); + consume!((input, type_kind) = byte(input)?); + + 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 }); + } - types.push(Type { inputs, outputs }); + TypeKind::Record => { + consume!((input, record_type) = record_type(input)?); + + types.push(Type::Record(record_type)); + } + } } Ok((input, types)) @@ -403,6 +462,7 @@ fn interfaces<'input, E: ParseError<&'input [u8]>>( /// let input = &[ /// 0x00, // type section /// 0x01, // 1 type +/// 0x00, // function type /// 0x01, // list of 1 item /// 0x00, // S8 /// 0x01, // list of 1 item @@ -436,7 +496,7 @@ fn interfaces<'input, E: ParseError<&'input [u8]>>( /// let output = Ok(( /// &[] as &[u8], /// Interfaces { -/// types: vec![Type { +/// types: vec![Type::Function { /// inputs: vec![InterfaceType::S8], /// outputs: vec![InterfaceType::S16], /// }], @@ -546,40 +606,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 @@ -594,6 +624,7 @@ mod tests { 0x0b, // Anyref 0x0c, // I32 0x0d, // I64 + 0x0e, 0x01, 0x02, // Record 0x01, ]; let output = Ok(( @@ -613,16 +644,91 @@ mod tests { InterfaceType::Anyref, InterfaceType::I32, InterfaceType::I64, + InterfaceType::Record(RecordType { + fields: vec1![InterfaceType::S32], + }), + ], + )); + + assert_eq!(list::<_, ()>(input, ty), output); + } + + #[test] + fn test_record_type() { + let input = &[ + 0x03, // list of 3 items + 0x01, // 1 field + 0x0a, // String + 0x02, // 2 fields + 0x0a, // String + 0x0c, // I32 + 0x03, // 3 fields + 0x0a, // String + 0x0e, // Record + 0x02, // 2 fields + 0x0c, // I32 + 0x0c, // I32 + 0x09, // F64 + 0x01, + ]; + let output = Ok(( + &[0x01][..], + vec![ + RecordType { + fields: vec1![InterfaceType::String], + }, + RecordType { + fields: vec1![InterfaceType::String, InterfaceType::I32], + }, + RecordType { + fields: vec1![ + InterfaceType::String, + InterfaceType::Record(RecordType { + fields: vec1![InterfaceType::I32, InterfaceType::I32], + }), + InterfaceType::F64, + ], + }, ], )); - assert_eq!(list::(input, ty), output); + 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] fn test_instructions() { let input = &[ - 0x25, // list of 37 items + 0x27, // list of 39 items 0x00, 0x01, // ArgumentGet { index: 1 } 0x01, 0x01, // CallCore { function_index: 1 } 0x02, // S8FromI32 @@ -660,6 +766,8 @@ mod tests { 0x22, // StringLiftMemory 0x23, 0x01, // StringLowerMemory { allocator_index: 1 } 0x24, // StringSize + 0x25, 0x01, // RecordLift { type_index: 1 }, + 0x26, 0x01, // RecordLower { type_index: 1 }, 0x0a, ]; let output = Ok(( @@ -702,10 +810,12 @@ mod tests { Instruction::StringLiftMemory, Instruction::StringLowerMemory { allocator_index: 1 }, Instruction::StringSize, + Instruction::RecordLift { type_index: 1 }, + Instruction::RecordLower { type_index: 1 }, ], )); - assert_eq!(list::(input, instruction), output); + assert_eq!(list::<_, ()>(input, instruction), output); } #[test] @@ -739,19 +849,29 @@ mod tests { #[test] fn test_types() { let input = &[ - 0x01, // 1 type + 0x02, // 2 type + 0x00, // function type 0x02, // list of 2 items 0x02, // S32 0x02, // S32 0x01, // list of 2 items 0x02, // S32 + 0x01, // record type + 0x02, // list of 2 items + 0x02, // S32 + 0x02, // S32 ]; let output = Ok(( &[] as &[u8], - vec![Type { - inputs: vec![InterfaceType::S32, InterfaceType::S32], - outputs: vec![InterfaceType::S32], - }], + vec![ + Type::Function { + inputs: vec![InterfaceType::S32, InterfaceType::S32], + outputs: vec![InterfaceType::S32], + }, + Type::Record(RecordType { + fields: vec1![InterfaceType::S32, InterfaceType::S32], + }), + ], )); assert_eq!(types::<()>(input), output); @@ -815,6 +935,7 @@ mod tests { let input = &[ 0x00, // type section 0x01, // 1 type + 0x00, // function type 0x01, // list of 1 item 0x00, // S8 0x01, // list of 1 item @@ -848,7 +969,7 @@ mod tests { let output = Ok(( &[] as &[u8], Interfaces { - types: vec![Type { + types: vec![Type::Function { inputs: vec![InterfaceType::S8], outputs: vec![InterfaceType::S16], }], diff --git a/lib/interface-types/src/decoders/wat.rs b/lib/interface-types/src/decoders/wat.rs index 4a77857f757..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}; @@ -13,6 +13,8 @@ mod keyword { // New keywords. custom_keyword!(implement); custom_keyword!(r#type = "type"); + custom_keyword!(record); + custom_keyword!(field); // New types. custom_keyword!(s8); @@ -63,6 +65,8 @@ mod keyword { custom_keyword!(string_lift_memory = "string.lift_memory"); custom_keyword!(string_lower_memory = "string.lower_memory"); custom_keyword!(string_size = "string.size"); + custom_keyword!(record_lift = "record.lift"); + custom_keyword!(record_lower = "record.lower"); } impl Parse<'_> for InterfaceType { @@ -125,12 +129,34 @@ impl Parse<'_> for InterfaceType { parser.parse::()?; 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: Vec1::new(fields).expect("Record must have at least one field, zero given."), + }) + } +} + impl<'a> Parse<'a> for Instruction { #[allow(clippy::cognitive_complexity)] fn parse(parser: Parser<'a>) -> Result { @@ -290,6 +316,18 @@ 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 if lookahead.peek::() { + parser.parse::()?; + + Ok(Instruction::RecordLower { + type_index: parser.parse()?, + }) } else { Err(lookahead.error()) } @@ -401,25 +439,36 @@ impl<'a> Parse<'a> for Type { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; - let (inputs, outputs) = parser.parens(|parser| { - parser.parse::()?; + let ty = parser.parens(|parser| { + let mut lookahead = parser.lookahead1(); - let mut input_types = vec![]; - let mut output_types = vec![]; + if lookahead.peek::() { + parser.parse::()?; - while !parser.is_empty() { - let function_type = parser.parse::()?; + let mut input_types = vec![]; + let mut output_types = vec![]; - match function_type { - FunctionType::Input(mut inputs) => input_types.append(&mut inputs), - FunctionType::Output(mut outputs) => output_types.append(&mut outputs), + while !parser.is_empty() { + let function_type = parser.parse::()?; + + 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::() { + Ok(Type::Record(parser.parse()?)) + } else { + Err(lookahead.error()) + } })?; - Ok(Type { inputs, outputs }) + Ok(ty) } } @@ -561,7 +610,7 @@ impl<'a> Parse<'a> for Interfaces<'a> { /// ) /// .unwrap(); /// let output = Interfaces { -/// types: vec![Type { +/// types: vec![Type::Function { /// inputs: vec![InterfaceType::I32], /// outputs: vec![InterfaceType::S8], /// }], @@ -602,8 +651,21 @@ mod tests { #[test] fn test_interface_type() { let inputs = vec![ - "s8", "s16", "s32", "s64", "u8", "u16", "u32", "u64", "f32", "f64", "string", "anyref", - "i32", "i64", + "s8", + "s16", + "s32", + "s64", + "u8", + "u16", + "u32", + "u64", + "f32", + "f64", + "string", + "anyref", + "i32", + "i64", + "record (field string)", ]; let outputs = vec![ InterfaceType::S8, @@ -620,6 +682,9 @@ mod tests { InterfaceType::Anyref, InterfaceType::I32, InterfaceType::I64, + InterfaceType::Record(RecordType { + fields: vec1![InterfaceType::String], + }), ]; assert_eq!(inputs.len(), outputs.len()); @@ -632,6 +697,41 @@ mod tests { } } + #[test] + fn test_record_type() { + let inputs = vec![ + "record (field string)", + "record (field string) (field i32)", + "record (field string) (field record (field i32) (field i32)) (field f64)", + ]; + let outputs = vec![ + RecordType { + fields: vec1![InterfaceType::String], + }, + RecordType { + fields: vec1![InterfaceType::String, InterfaceType::I32], + }, + RecordType { + fields: vec1![ + InterfaceType::String, + InterfaceType::Record(RecordType { + fields: vec1![InterfaceType::I32, InterfaceType::I32], + }), + InterfaceType::F64, + ], + }, + ]; + + assert_eq!(inputs.len(), outputs.len()); + + for (input, output) in inputs.iter().zip(outputs.iter()) { + assert_eq!( + &parser::parse::(&buffer(input)).unwrap(), + output + ); + } + } + #[test] fn test_instructions() { let inputs = vec![ @@ -672,6 +772,8 @@ mod tests { "string.lift_memory", "string.lower_memory 42", "string.size", + "record.lift 42", + "record.lower 42", ]; let outputs = vec![ Instruction::ArgumentGet { index: 7 }, @@ -713,6 +815,8 @@ mod tests { allocator_index: 42, }, Instruction::StringSize, + Instruction::RecordLift { type_index: 42 }, + Instruction::RecordLower { type_index: 42 }, ]; assert_eq!(inputs.len(), outputs.len()); @@ -758,9 +862,9 @@ mod tests { } #[test] - fn test_type() { + fn test_type_function() { let input = buffer(r#"(@interface type (func (param i32 i32) (result i32)))"#); - let output = Interface::Type(Type { + let output = Interface::Type(Type::Function { inputs: vec![InterfaceType::I32, InterfaceType::I32], outputs: vec![InterfaceType::I32], }); @@ -768,6 +872,16 @@ mod tests { assert_eq!(parser::parse::(&input).unwrap(), output); } + #[test] + fn test_type_record() { + let input = buffer(r#"(@interface type (record (field string) (field i32)))"#); + let output = Interface::Type(Type::Record(RecordType { + fields: vec1![InterfaceType::String, InterfaceType::I32], + })); + + assert_eq!(parser::parse::(&input).unwrap(), output); + } + #[test] fn test_export() { let input = buffer(r#"(@interface export "foo" (func 0))"#); @@ -838,7 +952,7 @@ mod tests { (@interface implement (func 0) (func 1))"#, ); let output = Interfaces { - types: vec![Type { + types: vec![Type::Function { inputs: vec![InterfaceType::I32], outputs: vec![InterfaceType::S8], }], diff --git a/lib/interface-types/src/encoders/binary.rs b/lib/interface-types/src/encoders/binary.rs index 7a2466952b8..2849dce4927 100644 --- a/lib/interface-types/src/encoders/binary.rs +++ b/lib/interface-types/src/encoders/binary.rs @@ -108,6 +108,33 @@ where InterfaceType::Anyref => 0x0b_u8.to_bytes(writer), InterfaceType::I32 => 0x0c_u8.to_bytes(writer), InterfaceType::I64 => 0x0d_u8.to_bytes(writer), + InterfaceType::Record(record_type) => { + 0x0e_u8.to_bytes(writer)?; + record_type.to_bytes(writer) + } + } + } +} + +/// Encode a `RecordType` into bytes. +impl 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 + W: Write, +{ + fn to_bytes(&self, writer: &mut W) -> io::Result<()> { + match self { + TypeKind::Function => 0x00_u8.to_bytes(writer), + TypeKind::Record => 0x01_u8.to_bytes(writer), } } } @@ -136,8 +163,18 @@ where W: Write, { fn to_bytes(&self, writer: &mut W) -> io::Result<()> { - self.inputs.to_bytes(writer)?; - self.outputs.to_bytes(writer)?; + match self { + Type::Function { inputs, outputs } => { + TypeKind::Function.to_bytes(writer)?; + inputs.to_bytes(writer)?; + outputs.to_bytes(writer)?; + } + + Type::Record(RecordType { fields }) => { + TypeKind::Record.to_bytes(writer)?; + fields.to_bytes(writer)?; + } + } Ok(()) } @@ -294,13 +331,20 @@ where Instruction::I64FromU64 => 0x21_u8.to_bytes(writer)?, Instruction::StringLiftMemory => 0x22_u8.to_bytes(writer)?, - Instruction::StringLowerMemory { allocator_index } => { 0x23_u8.to_bytes(writer)?; (*allocator_index as u64).to_bytes(writer)?; } - Instruction::StringSize => 0x24_u8.to_bytes(writer)?, + + Instruction::RecordLift { type_index } => { + 0x25_u8.to_bytes(writer)?; + (*type_index as u64).to_bytes(writer)? + } + Instruction::RecordLower { type_index } => { + 0x26_u8.to_bytes(writer)?; + (*type_index as u64).to_bytes(writer)? + } } Ok(()) @@ -399,6 +443,55 @@ mod tests { assert_to_bytes!(InterfaceType::Anyref, &[0x0b]); assert_to_bytes!(InterfaceType::I32, &[0x0c]); assert_to_bytes!(InterfaceType::I64, &[0x0d]); + assert_to_bytes!( + InterfaceType::Record(RecordType { + fields: vec1![InterfaceType::String] + }), + &[0x0e, 0x01, 0x0a] + ); + } + + #[test] + fn test_record_type() { + assert_to_bytes!( + RecordType { + fields: vec1![InterfaceType::String] + }, + &[ + 0x01, // 1 field + 0x0a, // String + ] + ); + assert_to_bytes!( + RecordType { + fields: vec1![InterfaceType::String, InterfaceType::I32] + }, + &[ + 0x02, // 2 fields + 0x0a, // String + 0x0c, // I32 + ] + ); + assert_to_bytes!( + RecordType { + fields: vec1![ + InterfaceType::String, + InterfaceType::Record(RecordType { + fields: vec1![InterfaceType::I32, InterfaceType::I32], + }), + InterfaceType::F64, + ], + }, + &[ + 0x03, // 3 fields + 0x0a, // String + 0x0e, // Record + 0x02, // 2 fields + 0x0c, // I32 + 0x0c, // I32 + 0x09, // F64 + ] + ); } #[test] @@ -428,13 +521,14 @@ mod tests { } #[test] - fn test_type() { + fn test_type_function() { assert_to_bytes!( - Type { + Type::Function { inputs: vec![InterfaceType::I32, InterfaceType::I64], outputs: vec![InterfaceType::S32], }, &[ + 0x00, // function type 0x02, // list of 2 items 0x0c, // I32 0x0d, // I64 @@ -444,6 +538,21 @@ mod tests { ); } + #[test] + fn test_type_record() { + assert_to_bytes!( + Type::Record(RecordType { + fields: vec1![InterfaceType::I32, InterfaceType::I64], + }), + &[ + 0x01, // record type + 0x02, // list of 2 items + 0x0c, // I32 + 0x0d, // I64 + ] + ); + } + #[test] fn test_import() { assert_to_bytes!( @@ -481,7 +590,7 @@ mod tests { fn test_interfaces() { assert_to_bytes!( Interfaces { - types: vec![Type { + types: vec![Type::Function { inputs: vec![InterfaceType::S8], outputs: vec![InterfaceType::S16], }], @@ -506,6 +615,7 @@ mod tests { &[ 0x00, // type section 0x01, // 1 type + 0x00, // function type 0x01, // list of 1 item 0x00, // S8 0x01, // list of 1 item @@ -580,9 +690,11 @@ mod tests { Instruction::StringLiftMemory, Instruction::StringLowerMemory { allocator_index: 1 }, Instruction::StringSize, + Instruction::RecordLift { type_index: 1 }, + Instruction::RecordLower { type_index: 1 }, ], &[ - 0x25, // list of 37 items + 0x27, // list of 39 items 0x00, 0x01, // ArgumentGet { index: 1 } 0x01, 0x01, // CallCore { function_index: 1 } 0x02, // S8FromI32 @@ -620,6 +732,8 @@ mod tests { 0x22, // StringLiftMemory 0x23, 0x01, // StringLowerMemory { allocator_index: 1 } 0x24, // StringSize + 0x025, 0x01, // RecordLift { type_index: 1 } + 0x026, 0x01, // RecordLower { type_index: 1 } ] ); } diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 7856e59b26a..a141b23f868 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], //! }], @@ -61,24 +61,41 @@ use std::string::ToString; impl ToString for &InterfaceType { fn to_string(&self) -> String { match self { - InterfaceType::S8 => "s8".into(), - InterfaceType::S16 => "s16".into(), - InterfaceType::S32 => "s32".into(), - InterfaceType::S64 => "s64".into(), - InterfaceType::U8 => "u8".into(), - InterfaceType::U16 => "u16".into(), - InterfaceType::U32 => "u32".into(), - InterfaceType::U64 => "u64".into(), - InterfaceType::F32 => "f32".into(), - InterfaceType::F64 => "f64".into(), - InterfaceType::String => "string".into(), - InterfaceType::Anyref => "anyref".into(), - InterfaceType::I32 => "i32".into(), - InterfaceType::I64 => "i64".into(), + InterfaceType::S8 => "s8".to_string(), + InterfaceType::S16 => "s16".to_string(), + InterfaceType::S32 => "s32".to_string(), + InterfaceType::S64 => "s64".to_string(), + InterfaceType::U8 => "u8".to_string(), + InterfaceType::U16 => "u16".to_string(), + InterfaceType::U32 => "u32".to_string(), + InterfaceType::U64 => "u64".to_string(), + InterfaceType::F32 => "f32".to_string(), + InterfaceType::F64 => "f64".to_string(), + InterfaceType::String => "string".to_string(), + InterfaceType::Anyref => "anyref".to_string(), + InterfaceType::I32 => "i32".to_string(), + InterfaceType::I64 => "i64".to_string(), + InterfaceType::Record(record_type) => record_type.to_string(), } } } +impl ToString for &RecordType { + fn to_string(&self) -> String { + format!( + "record{fields}", + fields = self + .fields + .iter() + .fold(String::new(), |mut accumulator, interface_type| { + accumulator.push(' '); + accumulator.push_str(&format!("(field {})", &interface_type.to_string())); + accumulator + }), + ) + } +} + /// Encode an `Instruction` into a string. impl ToString for &Instruction { fn to_string(&self) -> String { @@ -119,9 +136,11 @@ impl ToString for &Instruction { Instruction::I64FromU64 => "i64.from_u64".into(), Instruction::StringLiftMemory => "string.lift_memory".into(), Instruction::StringLowerMemory { allocator_index } => { - format!(r#"string.lower_memory {}"#, allocator_index) + format!("string.lower_memory {}", allocator_index) } Instruction::StringSize => "string.size".into(), + Instruction::RecordLift { type_index } => format!("record.lift {}", type_index), + Instruction::RecordLower { type_index } => format!("record.lower {}", type_index), } } } @@ -167,11 +186,18 @@ fn output_types_to_result(output_types: &[InterfaceType]) -> String { /// Encode a `Type` into a string. impl<'input> ToString for &Type { fn to_string(&self) -> String { - format!( - r#"(@interface type (func{inputs}{outputs}))"#, - inputs = input_types_to_param(&self.inputs), - outputs = output_types_to_result(&self.outputs), - ) + match self { + Type::Function { inputs, outputs } => format!( + r#"(@interface type (func{inputs}{outputs}))"#, + inputs = input_types_to_param(&inputs), + outputs = output_types_to_result(&outputs), + ), + + Type::Record(record_type) => format!( + r#"(@interface type ({record_type}))"#, + record_type = record_type.to_string(), + ), + } } } @@ -341,10 +367,58 @@ mod tests { (&InterfaceType::Anyref).to_string(), (&InterfaceType::I32).to_string(), (&InterfaceType::I64).to_string(), + (&InterfaceType::Record(RecordType { + fields: vec1![InterfaceType::String], + })) + .to_string(), ]; let outputs = vec![ - "s8", "s16", "s32", "s64", "u8", "u16", "u32", "u64", "f32", "f64", "string", "anyref", - "i32", "i64", + "s8", + "s16", + "s32", + "s64", + "u8", + "u16", + "u32", + "u64", + "f32", + "f64", + "string", + "anyref", + "i32", + "i64", + "record (field string)", + ]; + + assert_eq!(inputs, outputs); + } + + #[test] + fn test_record_type() { + let inputs = vec![ + (&RecordType { + fields: vec1![InterfaceType::String], + }) + .to_string(), + (&RecordType { + fields: vec1![InterfaceType::String, InterfaceType::I32], + }) + .to_string(), + (&RecordType { + fields: vec1![ + InterfaceType::String, + InterfaceType::Record(RecordType { + fields: vec1![InterfaceType::I32, InterfaceType::I32], + }), + InterfaceType::F64, + ], + }) + .to_string(), + ]; + let outputs = vec![ + "record (field string)", + "record (field string) (field i32)", + "record (field string) (field record (field i32) (field i32)) (field f64)", ]; assert_eq!(inputs, outputs); @@ -393,6 +467,8 @@ mod tests { }) .to_string(), (&Instruction::StringSize).to_string(), + (&Instruction::RecordLift { type_index: 42 }).to_string(), + (&Instruction::RecordLower { type_index: 42 }).to_string(), ]; let outputs = vec![ "arg.get 7", @@ -432,6 +508,8 @@ mod tests { "string.lift_memory", "string.lower_memory 42", "string.size", + "record.lift 42", + "record.lower 42", ]; assert_eq!(inputs, outputs); @@ -440,26 +518,30 @@ mod tests { #[test] fn test_types() { let inputs: Vec = vec![ - (&Type { + (&Type::Function { inputs: vec![InterfaceType::I32, InterfaceType::F32], outputs: vec![InterfaceType::I32], }) .to_string(), - (&Type { + (&Type::Function { inputs: vec![InterfaceType::I32], outputs: vec![], }) .to_string(), - (&Type { + (&Type::Function { inputs: vec![], outputs: vec![InterfaceType::I32], }) .to_string(), - (&Type { + (&Type::Function { inputs: vec![], outputs: vec![], }) .to_string(), + (&Type::Record(RecordType { + fields: vec1![InterfaceType::String, InterfaceType::I32], + })) + .to_string(), ]; let outputs = vec![ r#"(@interface type (func @@ -470,6 +552,7 @@ mod tests { r#"(@interface type (func (result i32)))"#, r#"(@interface type (func))"#, + r#"(@interface type (record (field string) (field i32)))"#, ]; assert_eq!(inputs, outputs); @@ -516,7 +599,7 @@ mod tests { #[test] fn test_interfaces() { let input: String = (&Interfaces { - types: vec![Type { + types: vec![Type::Function { inputs: vec![InterfaceType::I32], outputs: vec![InterfaceType::S8], }], diff --git a/lib/interface-types/src/errors.rs b/lib/interface-types/src/errors.rs index bf9612a8af7..fb443abc278 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}, @@ -156,6 +159,21 @@ pub enum InstructionErrorKind { /// The variable name that triggered the error. subject: &'static str, }, + + /// The type doesn't exist. + TypeIsMissing { + /// The type index. + type_index: u32, + }, + + /// Read a type that has an unexpected type. + InvalidTypeKind { + /// The expected kind. + expected_kind: TypeKind, + + /// The received kind. + received_kind: TypeKind, + }, } impl Error for InstructionErrorKind {} @@ -203,11 +221,7 @@ impl Display for InstructionErrorKind { Self::LocalOrImportSignatureMismatch { function_index, expected, received } => write!( formatter, "the local or import function `{}` has the signature `{:?} -> {:?}` but it received values of kind `{:?} -> {:?}`", - function_index, - expected.0, - expected.1, - received.0, - received.1, + function_index, expected.0, expected.1, received.0, received.1, ), Self::LocalOrImportCall { function_index } => write!( @@ -225,21 +239,28 @@ impl Display for InstructionErrorKind { Self::MemoryOutOfBoundsAccess { index, length } => write!( formatter, "read out of the memory bounds (index {} > memory length {})", - index, - length, + index, length, ), - Self::String(error) => write!( - formatter, - "{}", - error - ), + Self::String(error) => write!(formatter, "{}", error), Self::NegativeValue { subject } => write!( formatter, "attempted to convert `{}` but it appears to be a negative value", subject ), + + Self::TypeIsMissing { type_index } => write!( + formatter, + "the type `{}` doesn't exist", + type_index + ), + + Self::InvalidTypeKind { expected_kind, received_kind } => write!( + formatter, + "read a type of kind `{:?}`, but the kind `{:?}` was expected", + received_kind, expected_kind + ), } } } diff --git a/lib/interface-types/src/interpreter/instruction.rs b/lib/interface-types/src/interpreter/instruction.rs deleted file mode 100644 index 37f81626b6d..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: u32, - }, - - /// The `s8.from_i32` instruction. - S8FromI32, - - /// The `s8.from_i64` instruction. - S8FromI64, - - /// The `s16.from_i32` instruction. - S16FromI32, - - /// The `s16.from_i64` instruction. - S16FromI64, - - /// The `s32.from_i32` instruction. - S32FromI32, - - /// The `s32.from_i64` instruction. - S32FromI64, - - /// The `s64.from_i32` instruction. - S64FromI32, - - /// The `s64.from_i64` instruction. - S64FromI64, - - /// The `i32.from_s8` instruction. - I32FromS8, - - /// The `i32.from_s16` instruction. - I32FromS16, - - /// The `i32.from_s32` instruction. - I32FromS32, - - /// The `i32.from_s64` instruction. - I32FromS64, - - /// The `i64.from_s8` instruction. - I64FromS8, - - /// The `i64.from_s16` instruction. - I64FromS16, - - /// The `i64.from_s32` instruction. - I64FromS32, - - /// The `i64.from_s64` instruction. - I64FromS64, - - /// The `u8.from_i32` instruction. - U8FromI32, - - /// The `u8.from_i64` instruction. - U8FromI64, - - /// The `u16.from_i32` instruction. - U16FromI32, - - /// The `u16.from_i64` instruction. - U16FromI64, - - /// The `u32.from_i32` instruction. - U32FromI32, - - /// The `u32.from_i64` instruction. - U32FromI64, - - /// The `u64.from_i32` instruction. - U64FromI32, - - /// The `u64.from_i64` instruction. - U64FromI64, - - /// The `i32.from_u8` instruction. - I32FromU8, - - /// The `i32.from_u16` instruction. - I32FromU16, - - /// The `i32.from_u32` instruction. - I32FromU32, - - /// The `i32.from_u64` instruction. - I32FromU64, - - /// The `i64.from_u8` instruction. - I64FromU8, - - /// The `i64.from_u16` instruction. - I64FromU16, - - /// The `i64.from_u32` instruction. - I64FromU32, - - /// The `i64.from_u64` instruction. - I64FromU64, - - /// The `string.lift_memory` instruction. - StringLiftMemory, - - /// The `string.lower_memory` instruction. - StringLowerMemory { - /// The allocator function index. - allocator_index: u32, - }, - - /// The `string.size` instruction. - StringSize, -} 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(()) diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 9d7f0722768..39f25abcc0d 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -1,21 +1,156 @@ mod argument_get; mod call_core; mod numbers; +mod records; mod strings; use crate::{ errors::{InstructionError, InstructionErrorKind, InstructionResult, WasmValueNativeCastError}, - interpreter::{ - wasm::values::{InterfaceValue, NativeType}, - Instruction, - }, + interpreter::wasm::values::{InterfaceValue, NativeType}, }; pub(crate) use argument_get::argument_get; pub(crate) use call_core::call_core; pub(crate) use numbers::*; +pub(crate) use records::*; use std::convert::TryFrom; pub(crate) use strings::*; +/// Represents all the possible WIT instructions. +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum Instruction { + /// The `arg.get` instruction. + ArgumentGet { + /// The argument index. + index: u32, + }, + + /// The `call-core` instruction. + CallCore { + /// The function index. + function_index: u32, + }, + + /// The `s8.from_i32` instruction. + S8FromI32, + + /// The `s8.from_i64` instruction. + S8FromI64, + + /// The `s16.from_i32` instruction. + S16FromI32, + + /// The `s16.from_i64` instruction. + S16FromI64, + + /// The `s32.from_i32` instruction. + S32FromI32, + + /// The `s32.from_i64` instruction. + S32FromI64, + + /// The `s64.from_i32` instruction. + S64FromI32, + + /// The `s64.from_i64` instruction. + S64FromI64, + + /// The `i32.from_s8` instruction. + I32FromS8, + + /// The `i32.from_s16` instruction. + I32FromS16, + + /// The `i32.from_s32` instruction. + I32FromS32, + + /// The `i32.from_s64` instruction. + I32FromS64, + + /// The `i64.from_s8` instruction. + I64FromS8, + + /// The `i64.from_s16` instruction. + I64FromS16, + + /// The `i64.from_s32` instruction. + I64FromS32, + + /// The `i64.from_s64` instruction. + I64FromS64, + + /// The `u8.from_i32` instruction. + U8FromI32, + + /// The `u8.from_i64` instruction. + U8FromI64, + + /// The `u16.from_i32` instruction. + U16FromI32, + + /// The `u16.from_i64` instruction. + U16FromI64, + + /// The `u32.from_i32` instruction. + U32FromI32, + + /// The `u32.from_i64` instruction. + U32FromI64, + + /// The `u64.from_i32` instruction. + U64FromI32, + + /// The `u64.from_i64` instruction. + U64FromI64, + + /// The `i32.from_u8` instruction. + I32FromU8, + + /// The `i32.from_u16` instruction. + I32FromU16, + + /// The `i32.from_u32` instruction. + I32FromU32, + + /// The `i32.from_u64` instruction. + I32FromU64, + + /// The `i64.from_u8` instruction. + I64FromU8, + + /// The `i64.from_u16` instruction. + I64FromU16, + + /// The `i64.from_u32` instruction. + I64FromU32, + + /// The `i64.from_u64` instruction. + I64FromU64, + + /// The `string.lift_memory` instruction. + StringLiftMemory, + + /// The `string.lower_memory` instruction. + StringLowerMemory { + /// The allocator function index. + allocator_index: u32, + }, + + /// The `string.size` instruction. + StringSize, + + /// The `record.lift` instruction. + RecordLift { + /// The type index of the record. + type_index: u32, + }, + + /// The `record.lower` instruction. + RecordLower { + /// The type index of the record. + type_index: u32, + }, +} + /// Just a short helper to map the error of a cast from an /// `InterfaceValue` to a native value. pub(crate) fn to_native<'a, T>( @@ -31,9 +166,12 @@ where #[cfg(test)] pub(crate) mod tests { - use crate::interpreter::wasm::{ - self, - values::{InterfaceType, InterfaceValue}, + use crate::{ + ast, + interpreter::wasm::{ + self, + values::{InterfaceType, InterfaceValue}, + }, }; use std::{cell::Cell, collections::HashMap, convert::TryInto, ops::Deref, rc::Rc}; @@ -130,6 +268,7 @@ pub(crate) mod tests { pub(crate) exports: HashMap, pub(crate) locals_or_imports: HashMap, pub(crate) memory: Memory, + pub(crate) wit_types: Vec, } impl Instance { @@ -186,6 +325,15 @@ pub(crate) mod tests { hashmap }, memory: Memory::new(vec![Cell::new(0); 128]), + wit_types: vec![ast::Type::Record(ast::RecordType { + fields: vec1![ + InterfaceType::I32, + InterfaceType::Record(ast::RecordType { + fields: vec1![InterfaceType::String, InterfaceType::F32], + }), + InterfaceType::I64, + ], + })], } } } @@ -205,5 +353,9 @@ pub(crate) mod tests { fn memory(&self, _index: usize) -> Option<&Memory> { Some(&self.memory) } + + fn wit_type(&self, index: u32) -> Option<&ast::Type> { + self.wit_types.get(index as usize) + } } } 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..ad1c58e85fa --- /dev/null +++ b/lib/interface-types/src/interpreter/instructions/records.rs @@ -0,0 +1,377 @@ +use crate::{ + ast::{InterfaceType, RecordType, Type, TypeKind}, + errors::{InstructionError, InstructionErrorKind}, + interpreter::wasm::values::FlattenInterfaceValueIterator, + interpreter::{ + stack::{Stack, Stackable}, + wasm::values::InterfaceValue, + Instruction, + }, +}; +use std::collections::VecDeque; + +/// Build an `InterfaceValue::Record` based on values on the stack. +/// +/// To fill a record, every field `field_1` to `field_n` must get its +/// value from the stack with `value_1` to `value_n`. It is not +/// possible to use `Stack::pop` because the one-pass algorithm does +/// not know exactly the number of values to read from the stack +/// ahead-of-time, so `Stack::pop1` is used. It implies that values +/// are read one after the other from the stack, in a natural reverse +/// order, from `value_n` to `value_1`. Thus, the `values` vector must +/// be filled from the end to the beginning. It is not safely possible +/// to fill the `values` vector with empty values though (so that it +/// is possible to access to last positions). So a `VecDeque` type is +/// used: it is a double-ended queue. +fn record_lift_( + stack: &mut Stack, + record_type: &RecordType, +) -> Result { + let length = record_type.fields.len(); + let mut values = VecDeque::with_capacity(length); + + // Iterate over fields in reverse order to match the stack `pop` + // order. + for field in record_type.fields.iter().rev() { + match field { + // The record type tells a record is expected. + InterfaceType::Record(record_type) => { + // Build it recursively. + values.push_front(record_lift_(stack, &record_type)?) + } + // Any other type. + ty => { + let value = stack.pop1().unwrap(); + let value_type = (&value).into(); + + if ty != &value_type { + return Err(InstructionErrorKind::InvalidValueOnTheStack { + expected_type: ty.clone(), + received_type: value_type, + }); + } + + values.push_front(value) + } + } + } + + Ok(InterfaceValue::Record(values.into_iter().collect())) +} + +executable_instruction!( + record_lift(type_index: u32, instruction: Instruction) -> _ { + move |runtime| -> _ { + let instance = &runtime.wasm_instance; + let record_type = match instance.wit_type(type_index).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::TypeIsMissing { type_index }, + ) + })? { + Type::Record(record_type) => record_type, + Type::Function { .. } => return Err(InstructionError::new( + instruction, + InstructionErrorKind::InvalidTypeKind { + expected_kind: TypeKind::Record, + received_kind: TypeKind::Function + } + )), + }; + + let record = record_lift_(&mut runtime.stack, &record_type) + .map_err(|k| InstructionError::new(instruction, k))?; + + runtime.stack.push(record); + + Ok(()) + } + } +); + +executable_instruction!( + record_lower(type_index: u32, instruction: Instruction) -> _ { + move |runtime| -> _ { + let instance = &runtime.wasm_instance; + let record_type = match instance.wit_type(type_index).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::TypeIsMissing { type_index }, + ) + })? { + Type::Record(record_type) => record_type, + Type::Function { .. } => return Err(InstructionError::new( + instruction, + InstructionErrorKind::InvalidTypeKind { + expected_kind: TypeKind::Record, + received_kind: TypeKind::Function + } + )), + }; + + match runtime.stack.pop1() { + Some(InterfaceValue::Record(record_values)) if record_type == &(&record_values).into() => { + let values = FlattenInterfaceValueIterator::new(&record_values); + + for value in values { + runtime.stack.push(value.clone()); + } + + Ok(()) + }, + + Some(value) => Err(InstructionError::new( + instruction, + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: InterfaceType::Record(record_type.clone()), + received_type: (&value).into(), + } + )), + + None => Err(InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 1 }, + )), + } + } + } +); + +#[cfg(test)] +mod tests { + use crate::ast::{RecordType, Type}; + + test_executable_instruction!( + test_record_lift = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 2 }, + Instruction::ArgumentGet { index: 3 }, + Instruction::RecordLift { type_index: 0 }, + ], + invocation_inputs: [ + InterfaceValue::I32(1), + InterfaceValue::String("Hello".to_string()), + InterfaceValue::F32(2.), + InterfaceValue::I64(3), + ], + instance: Instance::new(), + stack: [InterfaceValue::Record(vec![ + InterfaceValue::I32(1), + InterfaceValue::Record(vec![ + InterfaceValue::String("Hello".to_string()), + InterfaceValue::F32(2.), + ]), + InterfaceValue::I64(3), + ])], + ); + + #[cfg(feature = "serde")] + #[test] + #[allow(non_snake_case, unused)] + fn test_record_lift__to_rust_struct() { + use crate::interpreter::{ + instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView}, + stack::Stackable, + wasm::values::{from_interface_values, InterfaceType, InterfaceValue}, + Instruction, Interpreter, + }; + use serde::Deserialize; + use std::{cell::Cell, collections::HashMap, convert::TryInto}; + + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet { index: 0 }, + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 2 }, + Instruction::ArgumentGet { index: 3 }, + Instruction::RecordLift { type_index: 0 }, + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![ + InterfaceValue::I32(1), + InterfaceValue::String("Hello".to_string()), + InterfaceValue::F32(2.), + InterfaceValue::I64(3), + ]; + let mut instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &mut instance); + + assert!(run.is_ok()); + + let stack = run.unwrap(); + + #[derive(Deserialize, Debug, PartialEq)] + struct S { + a: String, + b: f32, + } + + #[derive(Deserialize, Debug, PartialEq)] + struct T { + x: i32, + s: S, + y: i64, + } + + let record: T = from_interface_values(stack.as_slice()).unwrap(); + + assert_eq!( + record, + T { + x: 1, + s: S { + a: "Hello".to_string(), + b: 2., + }, + y: 3, + } + ); + } + + test_executable_instruction!( + test_record_lift__one_dimension = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::ArgumentGet { index: 1 }, + Instruction::RecordLift { type_index: 1 }, + ], + invocation_inputs: [ + InterfaceValue::I32(1), + InterfaceValue::I32(2), + ], + instance: { + let mut instance = Instance::new(); + instance.wit_types.push( + Type::Record(RecordType { + fields: vec1![InterfaceType::I32, InterfaceType::I32], + }) + ); + + instance + }, + stack: [InterfaceValue::Record(vec![ + InterfaceValue::I32(1), + InterfaceValue::I32(2), + ])], + ); + + test_executable_instruction!( + test_record_lift__type_is_missing = + instructions: [ + Instruction::RecordLift { type_index: 0 }, + ], + invocation_inputs: [], + instance: Default::default(), + error: r#"`record.lift 0` the type `0` doesn't exist"#, + ); + + test_executable_instruction!( + test_record_lift__invalid_value_on_the_stack = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 2 }, + Instruction::ArgumentGet { index: 3 }, + Instruction::RecordLift { type_index: 0 }, + ], + invocation_inputs: [ + InterfaceValue::I32(1), + InterfaceValue::String("Hello".to_string()), + InterfaceValue::F64(2.), + // ^^^ F32 is expected + InterfaceValue::I64(3), + ], + instance: Instance::new(), + error: r#"`record.lift 0` read a value of type `F64` from the stack, but the type `F32` was expected"#, + ); + + test_executable_instruction!( + test_record_lower = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::RecordLower { type_index: 0 }, + ], + invocation_inputs: [ + InterfaceValue::Record(vec![ + InterfaceValue::I32(1), + InterfaceValue::Record(vec![ + InterfaceValue::String("Hello".to_string()), + InterfaceValue::F32(2.), + ]), + InterfaceValue::I64(3), + ]) + ], + instance: Instance::new(), + stack: [ + InterfaceValue::I32(1), + InterfaceValue::String("Hello".to_string()), + InterfaceValue::F32(2.), + InterfaceValue::I64(3), + ], + ); + + test_executable_instruction!( + test_record__roundtrip = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::RecordLower { type_index: 0 }, + Instruction::RecordLift { type_index: 0 }, + ], + invocation_inputs: [ + InterfaceValue::Record(vec![ + InterfaceValue::I32(1), + InterfaceValue::Record(vec![ + InterfaceValue::String("Hello".to_string()), + InterfaceValue::F32(2.), + ]), + InterfaceValue::I64(3), + ]) + ], + instance: Instance::new(), + stack: [ + InterfaceValue::Record(vec![ + InterfaceValue::I32(1), + InterfaceValue::Record(vec![ + InterfaceValue::String("Hello".to_string()), + InterfaceValue::F32(2.), + ]), + InterfaceValue::I64(3), + ]) + ], + ); + + test_executable_instruction!( + test_record_lower__invalid_value_on_the_stack = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::RecordLower { type_index: 0 }, + ], + invocation_inputs: [ + InterfaceValue::I32(1), + ], + instance: Instance::new(), + error: r#"`record.lower 0` read a value of type `I32` from the stack, but the type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String, F32] }), I64] })` was expected"#, + ); + + test_executable_instruction!( + test_record_lower__invalid_value_on_the_stack__different_record_type = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::RecordLower { type_index: 0 }, + ], + invocation_inputs: [ + InterfaceValue::Record(vec![ + InterfaceValue::I32(1), + InterfaceValue::Record(vec![ + InterfaceValue::String("Hello".to_string()), + ]), + InterfaceValue::I64(3), + ]) + ], + instance: Instance::new(), + error: r#"`record.lower 0` read a value of type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String] }), I64] })` from the stack, but the type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String, F32] }), I64] })` was expected"#, + ); +} diff --git a/lib/interface-types/src/interpreter/instructions/strings.rs b/lib/interface-types/src/interpreter/instructions/strings.rs index 66afa8d72d5..0a332cb881f 100644 --- a/lib/interface-types/src/interpreter/instructions/strings.rs +++ b/lib/interface-types/src/interpreter/instructions/strings.rs @@ -93,15 +93,15 @@ executable_instruction!( InstructionErrorKind::LocalOrImportSignatureMismatch { function_index: allocator_index, expected: (vec![InterfaceType::I32], vec![InterfaceType::I32]), - received: (allocator.inputs().to_vec(), allocator.outputs().to_vec()) - } + received: (allocator.inputs().to_vec(), allocator.outputs().to_vec()), + }, )) } let string = runtime.stack.pop1().ok_or_else(|| { InstructionError::new( instruction, - InstructionErrorKind::StackIsTooSmall { needed: 1 } + InstructionErrorKind::StackIsTooSmall { needed: 1 }, ) })?; @@ -133,7 +133,7 @@ executable_instruction!( .ok_or_else(|| { InstructionError::new( instruction, - InstructionErrorKind::MemoryIsMissing { memory_index } + InstructionErrorKind::MemoryIsMissing { memory_index }, ) })? .view(); @@ -153,26 +153,26 @@ executable_instruction!( executable_instruction!( string_size(instruction: Instruction) -> _ { move |runtime| -> _ { - let value = runtime.stack.peek1().ok_or_else(|| { - InstructionError::new( - instruction, - InstructionErrorKind::StackIsTooSmall { needed: 1 }, - ) - })?; + match runtime.stack.peek1() { + Some(InterfaceValue::String(string)) => { + let length = string.len() as i32; + runtime.stack.push(InterfaceValue::I32(length)); - if let InterfaceValue::String(string) = value { - let length = string.len() as i32; - runtime.stack.push(InterfaceValue::I32(length)); + Ok(()) + }, - Ok(()) - } else { - Err(InstructionError::new( + Some(value) => Err(InstructionError::new( instruction, InstructionErrorKind::InvalidValueOnTheStack { expected_type: InterfaceType::String, received_type: value.into(), - } - )) + }, + )), + + None => Err(InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 1 }, + )), } } } @@ -310,7 +310,7 @@ mod tests { ); test_executable_instruction!( - test_string_memory = + test_string_lower_memory = instructions: [ Instruction::ArgumentGet { index: 0 }, Instruction::StringLowerMemory { allocator_index: 43 }, @@ -326,7 +326,7 @@ mod tests { ); test_executable_instruction!( - test_string_memory__roundtrip_with_memory_to_string = + test_string__roundtrip = instructions: [ Instruction::ArgumentGet { index: 0 }, Instruction::StringLowerMemory { allocator_index: 43 }, @@ -338,7 +338,7 @@ mod tests { ); test_executable_instruction!( - test_string_memory__allocator_does_not_exist = + test_string_lower_memory__allocator_does_not_exist = instructions: [Instruction::StringLowerMemory { allocator_index: 43 }], invocation_inputs: [], instance: Instance { ..Default::default() }, @@ -346,7 +346,7 @@ mod tests { ); test_executable_instruction!( - test_string_memory__stack_is_too_small = + test_string_lower_memory__stack_is_too_small = instructions: [ Instruction::StringLowerMemory { allocator_index: 43 } // ^^ `43` expects 1 value on the stack, none is present @@ -357,7 +357,7 @@ mod tests { ); test_executable_instruction!( - test_string_memory__failure_when_calling_the_allocator = + test_string_lower_memory__failure_when_calling_the_allocator = instructions: [ Instruction::ArgumentGet { index: 0 }, Instruction::StringLowerMemory { allocator_index: 153 } @@ -381,7 +381,7 @@ mod tests { ); test_executable_instruction!( - test_string_memory__invalid_allocator_signature = + test_string_lower_memory__invalid_allocator_signature = instructions: [ Instruction::ArgumentGet { index: 0 }, Instruction::StringLowerMemory { allocator_index: 153 } diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 22252aea71b..7486c816762 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; @@ -235,6 +234,13 @@ where instructions::string_lower_memory(*allocator_index, *instruction) } Instruction::StringSize => instructions::string_size(*instruction), + + Instruction::RecordLift { type_index } => { + instructions::record_lift(*type_index, *instruction) + } + Instruction::RecordLower { type_index } => { + instructions::record_lower(*type_index, *instruction) + } }) .collect(); diff --git a/lib/interface-types/src/interpreter/wasm/mod.rs b/lib/interface-types/src/interpreter/wasm/mod.rs index 1edccc59268..539e5ff0ef2 100644 --- a/lib/interface-types/src/interpreter/wasm/mod.rs +++ b/lib/interface-types/src/interpreter/wasm/mod.rs @@ -2,5 +2,8 @@ //! types, and traits —basically this is the part a runtime should //! take a look to use the `wasmer-interface-types` crate—. +#[cfg(feature = "serde")] +mod serde; + pub mod structures; pub mod values; diff --git a/lib/interface-types/src/interpreter/wasm/serde/de.rs b/lib/interface-types/src/interpreter/wasm/serde/de.rs new file mode 100644 index 00000000000..49e166e8340 --- /dev/null +++ b/lib/interface-types/src/interpreter/wasm/serde/de.rs @@ -0,0 +1,590 @@ +//! Provides a deserializer from WIT values to Rust value. + +use crate::{ + ast::InterfaceType, + interpreter::wasm::values::{FlattenInterfaceValueIterator, InterfaceValue}, +}; +use serde::{de, Deserialize}; +use std::{ + fmt::{self, Display}, + iter::Peekable, +}; + +/// Deserialize a set of `InterfaceValue`s to a type `T` that +/// implements the `Deserialize` trait. +/// +/// This is not a requirement to use WIT, but Serde provides an even +/// nicer API to the user to rebuild its complex types from WIT +/// values. +/// +/// # Example +/// +/// ```rust +/// use wasmer_interface_types::interpreter::wasm::values::{ +/// InterfaceValue, +/// from_interface_values, +/// }; +/// use serde::Deserialize; +/// +/// #[derive(Deserialize, Debug, PartialEq)] +/// struct S(i32, i64); +/// +/// #[derive(Deserialize, Debug, PartialEq)] +/// struct T<'a> { +/// x: &'a str, +/// s: S, +/// y: f32, +/// }; +/// +/// let values = vec![InterfaceValue::Record(vec![ +/// InterfaceValue::String("abc".to_string()), +/// InterfaceValue::Record(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]), +/// InterfaceValue::F32(3.), +/// ])]; +/// let t = from_interface_values::(&values).unwrap(); +/// +/// assert_eq!( +/// t, +/// T { +/// x: "abc", +/// s: S(1, 2), +/// y: 3., +/// } +/// ); +/// ``` +pub fn from_interface_values<'a, T>(values: &'a [InterfaceValue]) -> Result +where + T: Deserialize<'a>, +{ + let mut deserializer = Deserializer::new(values); + let result = T::deserialize(&mut deserializer)?; + + match deserializer.iterator.peek() { + None => Ok(result), + _ => Err(DeserializeError::InputNotEmpty), + } +} + +/// The deserializer. The iterator iterates over `InterfaceValue`s, +/// all flatten, see `FlattenInterfaceValueIterator`. +struct Deserializer<'de> { + iterator: Peekable>, +} + +impl<'de> Deserializer<'de> { + pub fn new(input: &'de [InterfaceValue]) -> Deserializer<'de> { + Deserializer { + iterator: FlattenInterfaceValueIterator::new(input).peekable(), + } + } +} + +macro_rules! next { + ($method_name:ident, $variant:ident, $type:ty) => { + fn $method_name(&mut self) -> Result<$type, DeserializeError> { + match self.iterator.peek() { + Some(InterfaceValue::$variant(value)) => { + self.iterator.next(); + + Ok(*value) + } + + Some(wrong_value) => Err(DeserializeError::TypeMismatch { + expected_type: InterfaceType::$variant, + received_type: (*wrong_value).into(), + }), + + None => Err(DeserializeError::InputEmpty), + } + } + } +} + +impl<'de> Deserializer<'de> { + next!(next_s8, S8, i8); + next!(next_s16, S16, i16); + next!(next_s32, S32, i32); + next!(next_s64, S64, i64); + next!(next_u8, U8, u8); + next!(next_u16, U16, u16); + next!(next_u32, U32, u32); + next!(next_u64, U64, u64); + next!(next_f32, F32, f32); + next!(next_f64, F64, f64); + + fn next_string(&mut self) -> Result<&'de str, DeserializeError> { + match self.iterator.peek() { + Some(InterfaceValue::String(v)) => { + self.iterator.next(); + + Ok(v) + } + + Some(wrong_value) => Err(DeserializeError::TypeMismatch { + expected_type: InterfaceType::String, + received_type: (*wrong_value).into(), + }), + + None => Err(DeserializeError::InputEmpty), + } + } + + next!(next_i32, I32, i32); + next!(next_i64, I64, i64); +} + +/// Represents an error while deserializing. +#[derive(Clone, Debug, PartialEq)] +pub enum DeserializeError { + /// The input isn't empty, i.e. some values aren't deserialized. + InputNotEmpty, + + /// The input is too short! + InputEmpty, + + /// The current value hasn't the expected type. + TypeMismatch { + /// The expected type. + expected_type: InterfaceType, + + /// The received type. + received_type: InterfaceType, + }, + + /// Arbitrary message. + Message(String), +} + +impl de::Error for DeserializeError { + fn custom(msg: T) -> Self { + Self::Message(msg.to_string()) + } +} + +impl Display for DeserializeError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::InputNotEmpty => write!(formatter, "Unexpected input remaining"), + Self::Message(ref msg) => write!(formatter, "{}", msg), + Self::InputEmpty => write!(formatter, "Unexpected end of input"), + Self::TypeMismatch { + ref expected_type, + ref received_type, + } => write!( + formatter, + "Type mismatch detected, expected `{:?}` but received `{:?}`", + expected_type, received_type + ), + } + } +} + +impl std::error::Error for DeserializeError {} + +impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { + type Error = DeserializeError; + + fn deserialize_any(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + match self.iterator.peek() { + Some(InterfaceValue::S8(_)) => self.deserialize_i8(visitor), + Some(InterfaceValue::S16(_)) => self.deserialize_i16(visitor), + Some(InterfaceValue::S32(_)) => self.deserialize_i32(visitor), + Some(InterfaceValue::S64(_)) => self.deserialize_i64(visitor), + Some(InterfaceValue::U8(_)) => self.deserialize_u8(visitor), + Some(InterfaceValue::U16(_)) => self.deserialize_u16(visitor), + Some(InterfaceValue::U32(_)) => self.deserialize_u32(visitor), + Some(InterfaceValue::U64(_)) => self.deserialize_u64(visitor), + Some(InterfaceValue::F32(_)) => self.deserialize_f32(visitor), + Some(InterfaceValue::F64(_)) => self.deserialize_f64(visitor), + Some(InterfaceValue::String(_)) => self.deserialize_string(visitor), + Some(InterfaceValue::I32(_)) => self.deserialize_i32(visitor), + Some(InterfaceValue::I64(_)) => self.deserialize_i64(visitor), + Some(InterfaceValue::Record(_)) => unreachable!("Records should have been flattened."), // already flattened + None => Err(DeserializeError::InputEmpty), + } + } + + fn deserialize_bool(self, _visitor: V) -> Result + where + V: de::Visitor<'de>, + { + unimplemented!("`bool` is not supported by WIT for the moment.") + } + + fn deserialize_i8(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + visitor.visit_i8(self.next_s8()?) + } + + fn deserialize_i16(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + visitor.visit_i16(self.next_s16()?) + } + + fn deserialize_i32(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + // Both `InterfaceValue::S32` and `InterfaceValue::I32` + // represent `i32`. + visitor.visit_i32(self.next_s32().or_else(|_| self.next_i32())?) + } + + fn deserialize_i64(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + // Both `InterfaceValue::S64` and `InterfaceValue::I64` + // represent `i64`. + visitor.visit_i64(self.next_s64().or_else(|_| self.next_i64())?) + } + + fn deserialize_u8(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + visitor.visit_u8(self.next_u8()?) + } + + fn deserialize_u16(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + visitor.visit_u16(self.next_u16()?) + } + + fn deserialize_u32(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + visitor.visit_u32(self.next_u32()?) + } + + fn deserialize_u64(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + visitor.visit_u64(self.next_u64()?) + } + + fn deserialize_f32(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + visitor.visit_f32(self.next_f32()?) + } + + fn deserialize_f64(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + visitor.visit_f64(self.next_f64()?) + } + + fn deserialize_char(self, _visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!("`char` is not supported by WIT for the moment.") + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + visitor.visit_borrowed_str(self.next_string()?) + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_bytes(self, _visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!("`bytes` is not supported by WIT for the moment.") + } + + fn deserialize_byte_buf(self, _visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!("`bytes` buffer is not supported by WIT for the moment.") + } + + fn deserialize_option(self, _visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!("`option` is not supported by WIT for the moment.") + } + + fn deserialize_unit(self, _visitor: V) -> Result + where + V: de::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: de::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: de::Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + fn deserialize_seq(self, _visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!("`seq` is not supported by WIT for the moment.") + } + + fn deserialize_tuple(self, _len: usize, _visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!("`tuple` is not supported by WIT for the moment.") + } + + fn deserialize_tuple_struct( + mut self, + _name: &'static str, + _len: usize, + visitor: V, + ) -> Result + where + V: de::Visitor<'de>, + { + visitor.visit_seq(Sequence::new(&mut self)) + } + + fn deserialize_map(self, _visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!("`map` is not supported by WIT for the moment.") + } + + fn deserialize_struct( + mut self, + _name: &'static str, + _fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: de::Visitor<'de>, + { + visitor.visit_seq(Sequence::new(&mut self)) + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + _visitor: V, + ) -> Result + where + V: de::Visitor<'de>, + { + todo!("`enum` is not supported by WIT for the moment.") + } + + fn deserialize_identifier(self, _visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!("`identifier` is not supported by WIT for the moment."); + } + + fn deserialize_ignored_any(self, _visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!("`ignored_any` is not implemented for the moment.") + } +} + +struct Sequence<'a, 'de> +where + 'de: 'a, +{ + de: &'a mut Deserializer<'de>, +} + +impl<'a, 'de> Sequence<'a, 'de> { + fn new(de: &'a mut Deserializer<'de>) -> Self { + Sequence { de } + } +} + +impl<'de, 'a> de::SeqAccess<'de> for Sequence<'a, 'de> { + type Error = DeserializeError; + + fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> + where + T: de::DeserializeSeed<'de>, + { + if self.de.iterator.peek().is_none() { + return Ok(None); + } + + seed.deserialize(&mut *self.de).map(Some) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! deserialize_value { + ($test_name:ident, $variant:ident, $ty:ident, $value:expr) => { + #[test] + #[allow(non_snake_case)] + fn $test_name() { + let input = vec![InterfaceValue::$variant($value)]; + let output: $ty = $value; + + assert_eq!(from_interface_values::<$ty>(&input).unwrap(), output); + } + }; + } + + deserialize_value!(test_deserialize_value__s8, S8, i8, 42); + deserialize_value!(test_deserialize_value__s16, S16, i16, 42); + deserialize_value!(test_deserialize_value__s32, S32, i32, 42); + deserialize_value!(test_deserialize_value__s64, S64, i64, 42); + deserialize_value!(test_deserialize_value__u8, U8, u8, 42); + deserialize_value!(test_deserialize_value__u16, U16, u16, 42); + deserialize_value!(test_deserialize_value__u32, U32, u32, 42); + deserialize_value!(test_deserialize_value__u64, U64, u64, 42); + deserialize_value!(test_deserialize_value__f32, F32, f32, 42.); + deserialize_value!(test_deserialize_value__f64, F32, f32, 42.); + deserialize_value!( + test_deserialize_value__string, + String, + String, + "foo".to_string() + ); + + #[test] + #[allow(non_snake_case)] + fn test_deserialize_value__str() { + let foo = "foo".to_string(); + let values = vec![InterfaceValue::String(foo)]; + let input: &str = from_interface_values(&values).unwrap(); + let output: &str = "foo"; + + assert_eq!(input, output); + } + + deserialize_value!(test_deserialize_value__i32, I32, i32, 42); + deserialize_value!(test_deserialize_value__i64, I64, i64, 42); + + #[test] + #[allow(non_snake_case)] + fn test_deserialize_value__newtype_struct() { + #[derive(Deserialize, Debug, PartialEq)] + struct S(i8); + + let input = vec![InterfaceValue::Record(vec![InterfaceValue::S8(42)])]; + let output = S(42); + + assert_eq!(from_interface_values::(&input).unwrap(), output); + } + + #[test] + #[allow(non_snake_case)] + fn test_deserialize_value__tuple_struct() { + #[derive(Deserialize, Debug, PartialEq)] + struct S(i8, f32); + + let input = vec![InterfaceValue::Record(vec![ + InterfaceValue::S8(7), + InterfaceValue::F32(42.), + ])]; + let output = S(7, 42.); + + assert_eq!(from_interface_values::(&input).unwrap(), output); + } + + #[test] + #[allow(non_snake_case)] + fn test_deserialize_value__struct() { + #[derive(Deserialize, Debug, PartialEq)] + struct S { + x: i8, + y: f32, + } + + let input = vec![InterfaceValue::Record(vec![ + InterfaceValue::S8(7), + InterfaceValue::F32(42.), + ])]; + let output = S { x: 7, y: 42. }; + + assert_eq!(from_interface_values::(&input).unwrap(), output); + } + + #[test] + #[allow(non_snake_case)] + fn test_deserialize_value__struct_nested() { + #[derive(Deserialize, Debug, PartialEq)] + struct Point { + x: i32, + y: i32, + z: i32, + } + + #[derive(Deserialize, Debug, PartialEq)] + struct Line { + p1: Point, + p2: Point, + } + + let input = vec![InterfaceValue::Record(vec![ + InterfaceValue::Record(vec![ + InterfaceValue::I32(1), + InterfaceValue::I32(2), + InterfaceValue::I32(3), + ]), + InterfaceValue::Record(vec![ + InterfaceValue::I32(4), + InterfaceValue::I32(5), + InterfaceValue::I32(6), + ]), + ])]; + let output = Line { + p1: Point { x: 1, y: 2, z: 3 }, + p2: Point { x: 4, y: 5, z: 6 }, + }; + + assert_eq!(from_interface_values::(&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..dd3cfb9ea8b --- /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 { + self.values.last_mut().unwrap() + } + + fn push_with_capacity(&mut self, capacity: usize) { + self.values.push(Vec::with_capacity(capacity)); + } + + fn pop(&mut self) -> Result, SerializeError> { + // The first `vec` contains the final result. It is forbidden + // to `pop` it as is. + if self.values.len() < 2 { + Err(SerializeError::InternalValuesCorrupted) + } else { + Ok(self.values.pop().unwrap()) // this `unwrap` is safe before `self.values` contains at least 2 items + } + } +} + +/// Represents an error while serializing. +#[derive(Clone, Debug, PartialEq)] +pub enum SerializeError { + /// The serialization still has pending values internally. + TransformationNotFinished, + + /// The internal values have been corrupted during the + /// serialization. + InternalValuesCorrupted, + + /// Arbitrary message. + Message(String), +} + +impl ser::Error for SerializeError { + fn custom(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/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 484fa1dae58..4e93a3b19b8 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -1,25 +1,57 @@ -#![allow(missing_docs)] +//! Defines WIT values and associated operations. -pub use crate::ast::InterfaceType; -use crate::errors::WasmValueNativeCastError; -use std::convert::TryFrom; +pub use crate::ast::{InterfaceType, RecordType}; +use crate::{errors::WasmValueNativeCastError, vec1::Vec1}; +use std::{convert::TryFrom, slice::Iter}; +#[cfg(feature = "serde")] +pub use crate::interpreter::wasm::serde::{de::*, ser::*}; + +/// A WIT value. #[derive(Debug, Clone, PartialEq)] pub enum InterfaceValue { + /// A 8-bits signed integer. S8(i8), + + /// A 16-bits signed integer. S16(i16), + + /// A 32-bits signed integer. S32(i32), + + /// A 64-bits signed integer. S64(i64), + + /// A 8-bits unsigned integer. U8(u8), + + /// A 16-bits unsigned integer. U16(u16), + + /// A 32-bits unsigned integer. U32(u32), + + /// A 64-bits unsigned integer. U64(u64), + + /// A 32-bits float. F32(f32), + + /// A 64-bits float. F64(f64), + + /// A string. String(String), + //Anyref(?), + /// A 32-bits integer (as defined in WebAssembly core). I32(i32), + + /// A 64-bits integer (as defiend in WebAssembly core). I64(i64), + + /// A record. + Record(Vec), } impl From<&InterfaceValue> for InterfaceType { @@ -39,6 +71,7 @@ impl From<&InterfaceValue> for InterfaceType { //InterfaceValue::Anyref(_) => Self::Anyref, InterfaceValue::I32(_) => Self::I32, InterfaceValue::I64(_) => Self::I64, + InterfaceValue::Record(values) => Self::Record(values.into()), } } } @@ -49,7 +82,18 @@ impl Default for InterfaceValue { } } +impl From<&Vec> for RecordType { + fn from(values: &Vec) -> Self { + RecordType { + fields: Vec1::new(values.iter().map(Into::into).collect()) + .expect("Record must have at least one field, zero given."), + } + } +} + +/// Represents a native type supported by WIT. pub trait NativeType { + /// The associated interface type that maps to the native type. const INTERFACE_TYPE: InterfaceType; } @@ -83,6 +127,8 @@ macro_rules! native { native!(i8, S8); native!(i16, S16); +native!(i32, I32); +native!(i64, I64); native!(u8, U8); native!(u16, U16); native!(u32, U32); @@ -90,5 +136,112 @@ native!(u64, U64); native!(f32, F32); native!(f64, F64); native!(String, String); -native!(i32, I32); -native!(i64, I64); + +/// Iterates over a vector of `InterfaceValues` but flatten all the +/// values. So `I32(1), Record([I32(2), I32(3)]), I32(4)` will be +/// iterated like `I32(1), I32(2), I32(3), I32(4)`. +pub(crate) struct FlattenInterfaceValueIterator<'a> { + iterators: Vec>, +} + +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 { + match self.iterators.last_mut()?.next() { + // End of the current iterator, go back to the previous + // one. + None => { + self.iterators.pop(); + self.next() + } + + // Recursively iterate over the record. + Some(InterfaceValue::Record(values)) => { + self.iterators.push(values.iter()); + self.next() + } + + // A regular item. + e @ Some(_) => e, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! value_to_type { + ($test_name:ident, $ty:ident, $value:expr) => { + #[test] + #[allow(non_snake_case)] + fn $test_name() { + assert_eq!( + InterfaceType::from(&InterfaceValue::$ty($value)), + InterfaceType::$ty + ); + } + }; + } + + value_to_type!(interface_type_from_interface_value__s8, S8, 42); + value_to_type!(interface_type_from_interface_value__s16, S16, 42); + value_to_type!(interface_type_from_interface_value__s32, S32, 42); + value_to_type!(interface_type_from_interface_value__s64, S64, 42); + value_to_type!(interface_type_from_interface_value__u8, U8, 42); + value_to_type!(interface_type_from_interface_value__u16, U16, 42); + value_to_type!(interface_type_from_interface_value__u32, U32, 42); + value_to_type!(interface_type_from_interface_value__u64, U64, 42); + value_to_type!(interface_type_from_interface_value__f32, F32, 42.); + value_to_type!(interface_type_from_interface_value__f64, F64, 42.); + value_to_type!( + interface_type_from_interface_value__string, + String, + "foo".to_string() + ); + value_to_type!(interface_type_from_interface_value__i32, I32, 42); + value_to_type!(interface_type_from_interface_value__i64, I64, 42); + + #[test] + #[allow(non_snake_case)] + fn interface_type_from_interface_value__record() { + assert_eq!( + InterfaceType::from(&InterfaceValue::Record(vec![ + InterfaceValue::I32(1), + InterfaceValue::S8(2) + ])), + InterfaceType::Record(RecordType { + fields: vec1![InterfaceType::I32, InterfaceType::S8] + }) + ); + + assert_eq!( + InterfaceType::from(&InterfaceValue::Record(vec![ + InterfaceValue::I32(1), + InterfaceValue::Record(vec![ + InterfaceValue::String("a".to_string()), + InterfaceValue::F64(42.) + ]), + InterfaceValue::S8(2) + ])), + InterfaceType::Record(RecordType { + fields: vec1![ + InterfaceType::I32, + InterfaceType::Record(RecordType { + fields: vec1![InterfaceType::String, InterfaceType::F64] + }), + InterfaceType::S8 + ] + }) + ); + } +} diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 9e88cac281b..1e1c5c24bce 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 0e89173924f..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. @@ -7,14 +8,17 @@ use wasmer_interface_types::{ fn test_binary_encoding_decoding_roundtrip() { let original_ast = Interfaces { types: vec![ - Type { + Type::Function { inputs: vec![], outputs: vec![], }, - Type { + Type::Function { inputs: vec![InterfaceType::I32, InterfaceType::I32], outputs: vec![InterfaceType::S32], }, + Type::Record(RecordType { + fields: Vec1::new(vec![InterfaceType::String, InterfaceType::I32]).unwrap(), + }), ], imports: vec![Import { namespace: "a",