From 410d8d4476e0f2765fabb1b0bc2bef7148a55ef1 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 24 Feb 2020 16:23:31 +0100 Subject: [PATCH] feat(interface-types) Re-implement `Type`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The semantics of “types” have changed since the previous draft. Now, a type is like a regular WebAssembly type but with Interface Types. --- src/ast.rs | 65 +++++++++--------------------------------- src/decoders/binary.rs | 60 +++++++++++++------------------------- src/decoders/wat.rs | 43 ++++++++++++++++++++++++++-- src/encoders/binary.rs | 43 ++++++++++------------------ src/encoders/wat.rs | 47 ++++++++++++++++++++++++++++-- tests/binary.rs | 9 +++--- 6 files changed, 137 insertions(+), 130 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 81ecd13..5d587b6 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -60,6 +60,16 @@ pub(crate) enum AdapterKind { Export, } +/// Represents a type signature. +#[derive(PartialEq, Debug)] +pub struct Type { + /// Types for the parameters. + pub inputs: Vec, + + /// Types for the results. + pub outputs: Vec, +} + /// Represents an exported function signature. #[derive(PartialEq, Debug)] pub struct Export<'input> { @@ -89,55 +99,6 @@ pub struct Import<'input> { pub output_types: Vec, } -/// Represents a structural type. -#[derive(PartialEq, Debug)] -pub struct Type<'input> { - /// The type name. - pub name: &'input str, - - /// The field names. - field_names: Vec<&'input str>, - - /// The field types. - field_types: Vec, -} - -impl<'input> Type<'input> { - /// Creates a new `Type`. - /// - /// The constructor panics if there is the length of `names` is - /// different than the length of `types`. - pub fn new(type_name: &'input str, names: Vec<&'input str>, types: Vec) -> Self { - assert_eq!( - names.len(), - types.len(), - "There must be the same number of field names than field types." - ); - - Self { - name: type_name, - field_names: names, - field_types: types, - } - } - - /// Adds a new field to the type. - pub fn add_field(&mut self, name: &'input str, ty: InterfaceType) { - self.field_names.push(name); - self.field_types.push(ty); - } - - /// Returns the field names. - pub fn field_names(&self) -> &Vec<&'input str> { - &self.field_names - } - - /// Returns the field types. - pub fn field_types(&self) -> &Vec { - &self.field_types - } -} - /// Represents an adapter. #[derive(PartialEq, Debug)] pub enum Adapter<'input> { @@ -179,12 +140,12 @@ pub enum Adapter<'input> { /// definition. #[derive(PartialEq, Default, Debug)] pub struct Interfaces<'input> { + /// All the types. + pub types: Vec, + /// All the exported functions. pub exports: Vec>, - /// All the types. - pub types: Vec>, - /// All the imported functions. pub imports: Vec>, diff --git a/src/decoders/binary.rs b/src/decoders/binary.rs index 6d39760..68474d7 100644 --- a/src/decoders/binary.rs +++ b/src/decoders/binary.rs @@ -295,11 +295,10 @@ 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, type_name) = string(input)?); - consume!((input, type_fields) = list(input, string)?); - consume!((input, type_types) = list(input, ty)?); + consume!((input, inputs) = list(input, ty)?); + consume!((input, outputs) = list(input, ty)?); - types.push(Type::new(type_name, type_fields, type_types)); + types.push(Type { inputs, outputs }); } Ok((input, types)) @@ -424,16 +423,11 @@ fn interfaces<'input, E: ParseError<&'input [u8]>>( /// 0x01, // list of 1 item /// 0x02, // S32 /// 0x01, // 1 type -/// 0x02, // string of 2 bytes -/// 0x61, 0x62, // "a", "b" -/// 0x02, // list of 2 items -/// 0x02, // string of 2 bytes -/// 0x63, 0x64, // "c", "d" -/// 0x01, // string of 1 byte -/// 0x65, // "e" /// 0x02, // list of 2 items /// 0x02, // S32 /// 0x02, // S32 +/// 0x01, // list of 1 item +/// 0x02, // S32 /// 0x01, // 1 import /// 0x01, // string of 1 byte /// 0x61, // "a" @@ -464,11 +458,10 @@ fn interfaces<'input, E: ParseError<&'input [u8]>>( /// input_types: vec![InterfaceType::S32], /// output_types: vec![InterfaceType::S32], /// }], -/// types: vec![Type::new( -/// "ab", -/// vec!["cd", "e"], -/// vec![InterfaceType::S32, InterfaceType::S32], -/// )], +/// types: vec![Type { +/// inputs: vec![InterfaceType::S32, InterfaceType::S32], +/// outputs: vec![InterfaceType::S32], +/// }], /// imports: vec![Import { /// namespace: "a", /// name: "b", @@ -740,24 +733,18 @@ mod tests { fn test_types() { let input = &[ 0x01, // 1 type - 0x02, // string of 2 bytes - 0x61, 0x62, // "a", "b" - 0x02, // list of 2 items - 0x02, // string of 2 bytes - 0x63, 0x64, // "c", "d" - 0x01, // string of 1 byte - 0x65, // "e" 0x02, // list of 2 items 0x02, // S32 0x02, // S32 + 0x01, // list of 2 items + 0x02, // S32 ]; let output = Ok(( &[] as &[u8], - vec![Type::new( - "ab", - vec!["cd", "e"], - vec![InterfaceType::S32, InterfaceType::S32], - )], + vec![Type { + inputs: vec![InterfaceType::S32, InterfaceType::S32], + outputs: vec![InterfaceType::S32], + }], )); assert_eq!(types::<()>(input), output); @@ -863,16 +850,10 @@ mod tests { 0x01, // list of 1 item 0x0c, // I32 0x01, // 1 type - 0x02, // string of 2 bytes - 0x61, 0x62, // "a", "b" - 0x02, // list of 2 items - 0x02, // string of 2 bytes - 0x63, 0x64, // "c", "d" - 0x01, // string of 1 byte - 0x65, // "e" 0x02, // list of 2 items 0x0c, // I32 0x0c, // I32 + 0x00, // list of 0 item 0x01, // 1 import 0x01, // string of 1 byte 0x61, // "a" @@ -903,11 +884,10 @@ mod tests { input_types: vec![InterfaceType::I32], output_types: vec![InterfaceType::I32], }], - types: vec![Type::new( - "ab", - vec!["cd", "e"], - vec![InterfaceType::I32, InterfaceType::I32], - )], + types: vec![Type { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![], + }], imports: vec![Import { namespace: "a", name: "b", diff --git a/src/decoders/wat.rs b/src/decoders/wat.rs index 049521a..1d7804f 100644 --- a/src/decoders/wat.rs +++ b/src/decoders/wat.rs @@ -15,6 +15,7 @@ mod keyword { // New keywords. custom_keyword!(adapt); + custom_keyword!(r#type = "type"); // New types. custom_keyword!(s8); @@ -283,8 +284,7 @@ impl Parse<'_> for FunctionType { #[derive(PartialEq, Debug)] enum Interface<'a> { Export(Export<'a>), - #[allow(dead_code)] - Type(Type<'a>), + Type(Type), Import(Import<'a>), Adapter(Adapter<'a>), } @@ -305,6 +305,8 @@ impl<'a> Parse<'a> for Interface<'a> { Ok(Interface::Import(parser.parse()?)) } else if lookahead.peek::() { Ok(Interface::Adapter(parser.parse()?)) + } else if lookahead.peek::() { + Ok(Interface::Type(parser.parse()?)) } else { Err(lookahead.error()) } @@ -315,6 +317,32 @@ impl<'a> Parse<'a> for Interface<'a> { } } +impl<'a> Parse<'a> for Type { + fn parse(parser: Parser<'a>) -> Result { + parser.parse::()?; + + let (inputs, outputs) = parser.parens(|parser| { + parser.parse::()?; + + let mut input_types = vec![]; + let mut output_types = vec![]; + + 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 { inputs, outputs }) + } +} + impl<'a> Parse<'a> for Export<'a> { fn parse(parser: Parser<'a>) -> Result { parser.parse::()?; @@ -663,6 +691,17 @@ mod tests { assert_eq!(parser::parse::(&input).unwrap(), output); } + #[test] + fn test_type() { + let input = buffer(r#"(@interface type (func (param i32 i32) (result i32)))"#); + let output = Interface::Type(Type { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + }); + + assert_eq!(parser::parse::(&input).unwrap(), output); + } + #[test] fn test_export_with_no_param_no_result() { let input = buffer(r#"(@interface export "foo")"#); diff --git a/src/encoders/binary.rs b/src/encoders/binary.rs index c3f2eb4..40c8a96 100644 --- a/src/encoders/binary.rs +++ b/src/encoders/binary.rs @@ -147,14 +147,13 @@ where /// Encode a `Type` into bytes. /// /// Decoder is in `decoders::binary::types`. -impl ToBytes for Type<'_> +impl ToBytes for Type where W: Write, { fn to_bytes(&self, writer: &mut W) -> io::Result<()> { - self.name.to_bytes(writer)?; - self.field_names().to_bytes(writer)?; - self.field_types().to_bytes(writer)?; + self.inputs.to_bytes(writer)?; + self.outputs.to_bytes(writer)?; Ok(()) } @@ -468,22 +467,16 @@ mod tests { #[test] fn test_type() { assert_to_bytes!( - Type::new( - "a", - vec!["b", "c"], - vec![InterfaceType::I32, InterfaceType::I64], - ), + Type { + inputs: vec![InterfaceType::I32, InterfaceType::I64], + outputs: vec![InterfaceType::S32], + }, &[ - 0x01, // string of length 1 - 0x61, // "a" - 0x02, // list of 2 items - 0x01, // string of length 1 - 0x62, // "b" - 0x01, // string of length 1 - 0x63, // "c" 0x02, // list of 2 items 0x0c, // I32 0x0d, // I64 + 0x01, // list of 1 items + 0x02, // I64 ] ); } @@ -571,11 +564,10 @@ mod tests { input_types: vec![InterfaceType::I32], output_types: vec![InterfaceType::I32], }], - types: vec![Type::new( - "ab", - vec!["cd", "e"], - vec![InterfaceType::I32, InterfaceType::I32], - )], + types: vec![Type { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::S32], + }], imports: vec![Import { namespace: "a", name: "b", @@ -599,16 +591,11 @@ mod tests { 0x01, // list of 1 item 0x0c, // I32 0x01, // 1 type - 0x02, // string of 2 bytes - 0x61, 0x62, // "a", "b" - 0x02, // list of 2 items - 0x02, // string of 2 bytes - 0x63, 0x64, // "c", "d" - 0x01, // string of 1 byte - 0x65, // "e" 0x02, // list of 2 items 0x0c, // I32 0x0c, // I32 + 0x01, // list of 1 item + 0x02, // S32 0x01, // 1 import 0x01, // string of 1 byte 0x61, // "a" diff --git a/src/encoders/wat.rs b/src/encoders/wat.rs index bb54e85..a6b8767 100644 --- a/src/encoders/wat.rs +++ b/src/encoders/wat.rs @@ -211,9 +211,13 @@ impl<'input> ToString for &Export<'input> { } /// Encode a `Type` into a string. -impl<'input> ToString for &Type<'input> { +impl<'input> ToString for &Type { fn to_string(&self) -> String { - todo!("To be implemented.") + format!( + r#"(@interface type (func{inputs}{outputs}))"#, + inputs = input_types_to_param(&self.inputs), + outputs = output_types_to_result(&self.outputs), + ) } } @@ -297,7 +301,6 @@ impl<'input> ToString for &Interfaces<'input> { .types .iter() .fold(String::new(), |mut accumulator, ty| { - accumulator.push_str(&format!("\n\n;; Interface, Ty {}\n", ty.name)); accumulator.push_str(&ty.to_string()); accumulator }); @@ -426,6 +429,44 @@ mod tests { assert_eq!(inputs, outputs); } + #[test] + fn test_types() { + let inputs: Vec = vec![ + (&Type { + inputs: vec![InterfaceType::I32, InterfaceType::F32], + outputs: vec![InterfaceType::I32], + }) + .to_string(), + (&Type { + inputs: vec![InterfaceType::I32], + outputs: vec![], + }) + .to_string(), + (&Type { + inputs: vec![], + outputs: vec![InterfaceType::I32], + }) + .to_string(), + (&Type { + inputs: vec![], + outputs: vec![], + }) + .to_string(), + ]; + let outputs = vec![ + r#"(@interface type (func + (param i32 f32) + (result i32)))"#, + r#"(@interface type (func + (param i32)))"#, + r#"(@interface type (func + (result i32)))"#, + r#"(@interface type (func))"#, + ]; + + assert_eq!(inputs, outputs); + } + #[test] fn test_exports() { let inputs: Vec = vec![ diff --git a/tests/binary.rs b/tests/binary.rs index ffd24a4..365d753 100644 --- a/tests/binary.rs +++ b/tests/binary.rs @@ -11,11 +11,10 @@ fn test_binary_encoding_decoding_roundtrip() { input_types: vec![InterfaceType::I32], output_types: vec![InterfaceType::I32], }], - types: vec![Type::new( - "ab", - vec!["cd", "e"], - vec![InterfaceType::I32, InterfaceType::I32], - )], + types: vec![Type { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::S32], + }], imports: vec![Import { namespace: "a", name: "b",