diff --git a/src/decoders/binary.rs b/src/decoders/binary.rs index fbe2e29..e77238e 100644 --- a/src/decoders/binary.rs +++ b/src/decoders/binary.rs @@ -407,6 +407,30 @@ fn forwards<'input, E: ParseError<&'input [u8]>>( Ok((input, forwards)) } +/// Parse complete interfaces. +fn interfaces<'input, E: ParseError<&'input [u8]>>( + bytes: &'input [u8], +) -> IResult<&'input [u8], Interfaces, E> { + let mut input = bytes; + + consume!((input, exports) = exports(input)?); + consume!((input, types) = types(input)?); + consume!((input, imports) = imports(input)?); + consume!((input, adapters) = adapters(input)?); + consume!((input, forwards) = forwards(input)?); + + Ok(( + input, + Interfaces { + exports, + types, + imports, + adapters, + forwards, + }, + )) +} + /// Parse a sequence of bytes, expecting it to be a valid WIT binary /// representation, into an `ast::Interfaces`. /// @@ -500,24 +524,7 @@ fn forwards<'input, E: ParseError<&'input [u8]>>( pub fn parse<'input, E: ParseError<&'input [u8]>>( bytes: &'input [u8], ) -> IResult<&'input [u8], Interfaces, E> { - let mut input = bytes; - - consume!((input, exports) = exports(input)?); - consume!((input, types) = types(input)?); - consume!((input, imports) = imports(input)?); - consume!((input, adapters) = adapters(input)?); - consume!((input, forwards) = forwards(input)?); - - Ok(( - input, - Interfaces { - exports, - types, - imports, - adapters, - forwards, - }, - )) + interfaces(bytes) } #[cfg(test)] @@ -978,6 +985,6 @@ mod tests { }, )); - assert_eq!(parse::<()>(input), output); + assert_eq!(interfaces::<()>(input), output); } } diff --git a/src/encoders/binary.rs b/src/encoders/binary.rs index f7e0e69..9a61f8c 100644 --- a/src/encoders/binary.rs +++ b/src/encoders/binary.rs @@ -1,13 +1,13 @@ //! Writes the AST into bytes representing WIT with its binary format. use crate::{ - ast::{Adapter, AdapterKind, Export, Import, InterfaceType, Interfaces, Type}, + ast::{Adapter, AdapterKind, Export, Forward, Import, InterfaceType, Interfaces, Type}, interpreter::Instruction, }; use std::io::{self, Write}; /// A trait for converting a value to bytes. -trait ToBytes +pub trait ToBytes where W: Write, { @@ -105,7 +105,7 @@ where InterfaceType::I32 => 0x7f_u64.to_bytes(writer), InterfaceType::I64 => 0x7e_u64.to_bytes(writer), InterfaceType::F32 => 0x7d_u64.to_bytes(writer), - InterfaceType::F64 => 0x7d_u64.to_bytes(writer), + InterfaceType::F64 => 0x7c_u64.to_bytes(writer), InterfaceType::AnyRef => 0x6f_u64.to_bytes(writer), } } @@ -229,6 +229,18 @@ where } } +/// Encode an `Forward` into bytes. +/// +/// Decoder is `decoders::binary::forwards`. +impl ToBytes for Forward<'_> +where + W: Write, +{ + fn to_bytes(&self, writer: &mut W) -> io::Result<()> { + self.name.to_bytes(writer) + } +} + /// Encode an `Interfaces` into bytes. /// /// Decoder is `decoders::binary::parse`. @@ -241,6 +253,7 @@ where self.types.to_bytes(writer)?; self.imports.to_bytes(writer)?; self.adapters.to_bytes(writer)?; + self.forwards.to_bytes(writer)?; Ok(()) } @@ -352,3 +365,385 @@ where Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! assert_to_bytes { + ($expr:expr, $expected_output:expr) => {{ + let mut output = vec![]; + + $expr.to_bytes(&mut output).expect(concat!( + "Unable to encode the expression `", + stringify!($expr), + "` to bytes." + )); + + assert_eq!(output.as_slice(), &$expected_output[..]); + }}; + } + + #[test] + fn test_u8() { + assert_to_bytes!(0x01_u8, &[0x01]); + } + + #[test] + fn test_uleb_1_byte() { + assert_to_bytes!(0x01_u64, &[0x01]); + } + + #[test] + fn test_uleb_3_bytes() { + assert_to_bytes!(0x7ffc_u64, &[0xfc, 0xff, 0x01]); + } + + // Examples from Figure 22 of [DWARF 4 + // standard](http://dwarfstd.org/doc/DWARF4.pdf). + #[test] + fn test_uleb_from_dward_standard() { + assert_to_bytes!(2u64, &[2u8]); + assert_to_bytes!(127u64, &[127u8]); + assert_to_bytes!(128u64, &[0x80, 1u8]); + assert_to_bytes!(129u64, &[1u8 | 0x80, 1]); + assert_to_bytes!(130u64, &[2u8 | 0x80, 1]); + assert_to_bytes!(12857u64, &[57u8 | 0x80, 100]); + } + + #[test] + fn test_empty_str() { + assert_to_bytes!("", &[0x00]); + } + + #[test] + fn test_str() { + assert_to_bytes!("abc", &[0x03, 0x61, 0x62, 0x63]); + } + + #[test] + fn test_empty_vec() { + assert_to_bytes!(Vec::::new(), &[0x00]); + } + + #[test] + fn test_vec() { + assert_to_bytes!( + vec!["a", "b", "c"], + &[ + 0x03, // list of 3 items + 0x01, // string of 1 byte + 0x61, // "a" + 0x01, // string of 1 byte + 0x62, // "b" + 0x01, // string of 1 byte + 0x63, // "c" + ] + ); + } + + #[test] + fn test_interface_type() { + assert_to_bytes!(InterfaceType::Int, &[0xff, 0xff, 0x01]); + assert_to_bytes!(InterfaceType::Float, &[0xfe, 0xff, 0x01]); + assert_to_bytes!(InterfaceType::Any, &[0xfd, 0xff, 0x01]); + assert_to_bytes!(InterfaceType::String, &[0xfc, 0xff, 0x01]); + assert_to_bytes!(InterfaceType::Seq, &[0xfb, 0xff, 0x01]); + assert_to_bytes!(InterfaceType::I32, &[0x7f]); + assert_to_bytes!(InterfaceType::I64, &[0x7e]); + assert_to_bytes!(InterfaceType::F32, &[0x7d]); + assert_to_bytes!(InterfaceType::F64, &[0x7c]); + assert_to_bytes!(InterfaceType::AnyRef, &[0x6f]); + } + + #[test] + fn test_adapter_kind() { + assert_to_bytes!(AdapterKind::Import, &[0x00]); + assert_to_bytes!(AdapterKind::Export, &[0x01]); + assert_to_bytes!(AdapterKind::HelperFunction, &[0x02]); + } + + #[test] + fn test_export() { + assert_to_bytes!( + Export { + name: "abc", + input_types: vec![InterfaceType::I32, InterfaceType::I64], + output_types: vec![InterfaceType::I32] + }, + &[ + 0x03, // string of length 3 + 0x61, // "a" + 0x62, // "b" + 0x63, // "c" + 0x02, // list of 2 items + 0x7f, // I32 + 0x7e, // I64 + 0x01, // list of 1 items + 0x7f, // I32 + ] + ); + } + + #[test] + fn test_type() { + assert_to_bytes!( + Type::new( + "a", + vec!["b", "c"], + vec![InterfaceType::I32, InterfaceType::I64], + ), + &[ + 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 + 0x7f, // I32 + 0x7e, // I64 + ] + ); + } + + #[test] + fn test_import() { + assert_to_bytes!( + Import { + namespace: "a", + name: "b", + input_types: vec![InterfaceType::I32, InterfaceType::I64], + output_types: vec![InterfaceType::I32], + }, + &[ + 0x01, // string of length 1 + 0x61, // "a" + 0x01, // string of length 1 + 0x62, // "b" + 0x02, // list of 2 items + 0x7f, // I32 + 0x7e, // I64 + 0x01, // list of 1 items + 0x7f, // I32 + ] + ); + } + + #[test] + fn test_adapter_import() { + assert_to_bytes!( + Adapter::Import { + namespace: "a", + name: "b", + input_types: vec![InterfaceType::I32, InterfaceType::I64], + output_types: vec![InterfaceType::I32], + instructions: vec![Instruction::ArgumentGet { index: 1 }], + }, + &[ + 0x00, // AdapterKind::Import + 0x01, // string of length 1 + 0x61, // "a" + 0x01, // string of length 1 + 0x62, // "b" + 0x02, // list of 2 items + 0x7f, // I32 + 0x7e, // I64 + 0x01, // list of 1 items + 0x7f, // I32 + 0x01, // list of 1 item + 0x00, 0x01, // ArgumentGet { index: 1 } + ] + ); + } + + #[test] + fn test_adapter_export() { + assert_to_bytes!( + Adapter::Export { + name: "a", + input_types: vec![InterfaceType::I32, InterfaceType::I64], + output_types: vec![InterfaceType::I32], + instructions: vec![Instruction::ArgumentGet { index: 1 }], + }, + &[ + 0x01, // AdapterKind::Export + 0x01, // string of length 1 + 0x61, // "a" + 0x02, // list of 2 items + 0x7f, // I32 + 0x7e, // I64 + 0x01, // list of 1 items + 0x7f, // I32 + 0x01, // list of 1 item + 0x00, 0x01, // ArgumentGet { index: 1 } + ] + ); + } + + #[test] + fn test_adapter_helper_function() { + assert_to_bytes!( + Adapter::HelperFunction { + name: "a", + input_types: vec![InterfaceType::I32, InterfaceType::I64], + output_types: vec![InterfaceType::I32], + instructions: vec![Instruction::ArgumentGet { index: 1 }], + }, + &[ + 0x02, // AdapterKind::HelperFunction + 0x01, // string of length 1 + 0x61, // "a" + 0x02, // list of 2 items + 0x7f, // I32 + 0x7e, // I64 + 0x01, // list of 1 items + 0x7f, // I32 + 0x01, // list of 1 item + 0x00, 0x01, // ArgumentGet { index: 1 } + ] + ); + } + + #[test] + fn test_forward() { + assert_to_bytes!( + Forward { name: "ab" }, + &[ + 0x02, // string of length 2 + 0x61, // "a" + 0x62, // "b" + ] + ); + } + + #[test] + fn test_interfaces() { + assert_to_bytes!( + Interfaces { + exports: vec![Export { + name: "ab", + input_types: vec![InterfaceType::I32], + output_types: vec![InterfaceType::I32], + }], + types: vec![Type::new( + "ab", + vec!["cd", "e"], + vec![InterfaceType::I32, InterfaceType::I32], + )], + imports: vec![Import { + namespace: "a", + name: "b", + input_types: vec![InterfaceType::I32], + output_types: vec![InterfaceType::I64], + }], + adapters: vec![Adapter::Import { + namespace: "a", + name: "b", + input_types: vec![InterfaceType::I32], + output_types: vec![InterfaceType::I32], + instructions: vec![Instruction::ArgumentGet { index: 1 }], + }], + forwards: vec![Forward { name: "a" }], + }, + &[ + 0x01, // 1 export + 0x02, // string of 2 bytes + 0x61, 0x62, // "a", "b" + 0x01, // list of 1 item + 0x7f, // I32 + 0x01, // list of 1 item + 0x7f, // 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 + 0x7f, // I32 + 0x7f, // I32 + 0x01, // 1 import + 0x01, // string of 1 byte + 0x61, // "a" + 0x01, // string of 1 byte + 0x62, // "b" + 0x01, // list of 1 item + 0x7f, // I32 + 0x01, // list of 1 item + 0x7e, // I64 + 0x01, // 1 adapter + 0x00, // adapter kind: import + 0x01, // string of 1 byte + 0x61, // "a" + 0x01, // string of 1 byte + 0x62, // "b" + 0x01, // list of 1 item + 0x7f, // I32 + 0x01, // list of 1 item + 0x7f, // I32 + 0x01, // list of 1 item + 0x00, 0x01, // ArgumentGet { index: 1 } + 0x01, // 1 adapter + 0x01, // string of 1 byte + 0x61, // "a" + ] + ); + } + + #[test] + fn test_instructions() { + assert_to_bytes!( + vec![ + Instruction::ArgumentGet { index: 1 }, + Instruction::Call { function_index: 1 }, + Instruction::CallExport { export_name: "abc" }, + Instruction::ReadUtf8, + Instruction::WriteUtf8 { + allocator_name: "abc", + }, + Instruction::AsWasm(InterfaceType::Int), + Instruction::AsInterface(InterfaceType::I64), + Instruction::TableRefAdd, + Instruction::TableRefGet, + Instruction::CallMethod(1), + Instruction::MakeRecord(InterfaceType::I32), + Instruction::GetField(InterfaceType::Int, 2), + Instruction::Const(InterfaceType::I32, 1), + Instruction::FoldSeq(1), + Instruction::Add(InterfaceType::I32), + Instruction::MemToSeq(InterfaceType::I32, "abc"), + Instruction::Load(InterfaceType::I32, "abc"), + Instruction::SeqNew(InterfaceType::I32), + Instruction::ListPush, + Instruction::RepeatUntil(1, 2), + ], + &[ + 0x14, // list of 20 items + 0x00, 0x01, // ArgumentGet { index: 1 } + 0x01, 0x01, // Call { function_index: 1 } + 0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" } + 0x03, // ReadUtf8 + 0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" } + 0x05, 0xff, 0xff, 0x01, // AsWasm(Int) + 0x06, 0x7e, // AsInterface(I64) + 0x07, // TableRefAdd + 0x08, // TableRefGet + 0x09, 0x01, // CallMethod(1) + 0x0a, 0x7f, // MakeRecord(I32) + 0x0c, 0xff, 0xff, 0x01, 0x02, // GetField(Int, 2) + 0x0d, 0x7f, 0x01, // Const(I32, 1) + 0x0e, 0x01, // FoldSeq(1) + 0x0f, 0x7f, // Add(I32) + 0x10, 0x7f, 0x03, 0x61, 0x62, 0x63, // MemToSeq(I32, "abc") + 0x11, 0x7f, 0x03, 0x61, 0x62, 0x63, // Load(I32, "abc") + 0x12, 0x7f, // SeqNew(I32) + 0x13, // ListPush + 0x14, 0x01, 0x02, // RepeatUntil(1, 2) + ] + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index f4deaba..975d949 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,5 +51,3 @@ mod macros; pub mod decoders; pub mod encoders; pub mod interpreter; - -pub use decoders::binary::parse as parse_binary;