diff --git a/Cargo.lock b/Cargo.lock index a04c4ef..07f108e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "arrayvec" version = "0.5.2" @@ -83,6 +85,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + [[package]] name = "proc-macro2" version = "1.0.24" @@ -113,6 +124,24 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50b8b2cd387f744f69469aaed197954ba4c0ecdb31e02edf99b023e0df11178a" +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.118" @@ -161,6 +190,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "unicode-xid" version = "0.2.1" @@ -175,13 +210,14 @@ checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "wasmer-interface-types-fl" -version = "0.17.24" +version = "0.17.25" dependencies = [ "fluence-it-types", "it-to-bytes", "log", "nom", "safe-transmute", + "semver", "serde", "serde_json", "wast", diff --git a/crates/to-bytes/src/lib.rs b/crates/to-bytes/src/lib.rs index e35fd8d..0f6a448 100644 --- a/crates/to-bytes/src/lib.rs +++ b/crates/to-bytes/src/lib.rs @@ -72,13 +72,7 @@ where W: Write, { fn to_bytes(&self, writer: &mut W) -> io::Result<()> { - // Size first. - writer.write_all(&[self.len() as u8])?; - - // Then the string. - writer.write_all(self.as_bytes())?; - - Ok(()) + self.as_str().to_bytes(writer) } } diff --git a/wasmer-it/Cargo.toml b/wasmer-it/Cargo.toml index c75b34b..ab52545 100644 --- a/wasmer-it/Cargo.toml +++ b/wasmer-it/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-interface-types-fl" -version = "0.17.24" +version = "0.17.25" description = "WebAssembly Interface Types library for Wasmer" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -22,5 +22,7 @@ serde_json = "1.0" safe-transmute = "0.11.0" log = "0.4.11" +semver = "0.11.0" + [features] default = ["serde"] diff --git a/wasmer-it/src/ast.rs b/wasmer-it/src/ast.rs index e332ce0..da898b7 100644 --- a/wasmer-it/src/ast.rs +++ b/wasmer-it/src/ast.rs @@ -98,6 +98,9 @@ pub struct Implementation { /// Represents the kind of interface. #[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)] pub enum InterfaceKind { + /// A version. + Version, + /// A type. Type, @@ -116,8 +119,11 @@ pub enum InterfaceKind { /// Represents a set of interfaces, i.e. it entirely describes a WIT /// definition. -#[derive(PartialEq, Eq, Debug, Default, Clone, Hash)] +#[derive(PartialEq, Eq, Debug, Clone, Hash)] pub struct Interfaces<'input> { + /// Version of IT. + pub version: semver::Version, + /// All the types. pub types: Vec, diff --git a/wasmer-it/src/decoders/binary.rs b/wasmer-it/src/decoders/binary.rs index 77482ff..aeff345 100644 --- a/wasmer-it/src/decoders/binary.rs +++ b/wasmer-it/src/decoders/binary.rs @@ -35,6 +35,7 @@ impl TryFrom for InterfaceKind { 0x02 => Self::Adapter, 0x03 => Self::Export, 0x04 => Self::Implementation, + 0x05 => Self::Version, _ => return Err("Unknown interface kind code."), }) } @@ -487,6 +488,7 @@ fn interfaces<'input, E: ParseError<&'input [u8]>>( ) -> IResult<&'input [u8], Interfaces, E> { let mut input = bytes; + let mut all_versions = vec![]; let mut all_types = vec![]; let mut all_imports = vec![]; let mut all_adapters = vec![]; @@ -500,6 +502,10 @@ fn interfaces<'input, E: ParseError<&'input [u8]>>( .map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?; match interface_kind { + InterfaceKind::Version => { + consume!((input, new_version) = string(input)?); + all_versions.push(new_version); + } InterfaceKind::Type => { consume!((input, mut new_types) = types(input)?); all_types.append(&mut new_types); @@ -527,9 +533,12 @@ fn interfaces<'input, E: ParseError<&'input [u8]>>( } } + let version = try_into_version(all_versions).map_err(|e| Err::Error(make_error(input, e)))?; + Ok(( input, Interfaces { + version, types: all_types, imports: all_imports, adapters: all_adapters, @@ -539,6 +548,22 @@ fn interfaces<'input, E: ParseError<&'input [u8]>>( )) } +fn try_into_version(versions: Vec<&str>) -> Result { + use std::str::FromStr; + + if versions.is_empty() { + return Err(ErrorKind::NoneOf); + } + + if versions.len() != 1 { + return Err(ErrorKind::Many0); + } + + let version = semver::Version::from_str(&versions[0]).map_err(|_| ErrorKind::IsNot)?; + + Ok(version) +} + /// Parse a sequence of bytes, expecting it to be a valid WIT binary /// representation, into an [`Interfaces`](crate::ast::Interfaces) /// structure. diff --git a/wasmer-it/src/decoders/wat.rs b/wasmer-it/src/decoders/wat.rs index 668a30f..e2c145f 100644 --- a/wasmer-it/src/decoders/wat.rs +++ b/wasmer-it/src/decoders/wat.rs @@ -18,6 +18,7 @@ mod keyword { custom_keyword!(r#type = "type"); custom_keyword!(record); custom_keyword!(field); + custom_keyword!(version); // Special symbols custom_keyword!(comma = ","); @@ -380,6 +381,7 @@ impl Parse<'_> for FunctionType { #[derive(PartialEq, Debug)] enum Interface<'a> { + Version(String), Type(Type), Import(Import<'a>), Adapter(Adapter), @@ -397,7 +399,9 @@ impl<'a> Parse<'a> for Interface<'a> { let mut lookahead = parser.lookahead1(); - if lookahead.peek::() { + if lookahead.peek::() { + Ok(Interface::Version(parser.parse()?)) + } else if lookahead.peek::() { Ok(Interface::Type(parser.parse()?)) } else if lookahead.peek::() { Ok(Interface::Import(parser.parse()?)) @@ -548,26 +552,66 @@ impl<'a> Parse<'a> for Adapter { impl<'a> Parse<'a> for Interfaces<'a> { fn parse(parser: Parser<'a>) -> Result { - let mut interfaces: Interfaces = Default::default(); + let mut version: Option = None; + let mut types = vec![]; + let mut imports = vec![]; + let mut adapters = vec![]; + let mut exports = vec![]; + let mut implementations = vec![]; while !parser.is_empty() { let interface = parser.parse::()?; match interface { - Interface::Type(ty) => interfaces.types.push(ty), - Interface::Import(import) => interfaces.imports.push(import), - Interface::Adapter(adapter) => interfaces.adapters.push(adapter), - Interface::Export(export) => interfaces.exports.push(export), - Interface::Implementation(implementation) => { - interfaces.implementations.push(implementation) + Interface::Version(version_str) => { + try_handle_version(&version_str, &mut version, &parser)? } + Interface::Type(ty) => types.push(ty), + Interface::Import(import) => imports.push(import), + Interface::Adapter(adapter) => adapters.push(adapter), + Interface::Export(export) => exports.push(export), + Interface::Implementation(implementation) => implementations.push(implementation), } } + let version = version.ok_or_else(|| { + wast::Error::new(parser.cur_span(), String::from("version must be specified")) + })?; + + let interfaces = Interfaces { + version, + types, + imports, + adapters, + exports, + implementations, + }; + Ok(interfaces) } } +fn try_handle_version( + version_str: &str, + version: &mut Option, + parser: &Parser<'_>, +) -> Result<()> { + use std::str::FromStr; + + if version.is_some() { + return Err(wast::Error::new( + parser.cur_span(), + String::from("only one version directive is possible"), + )); + } + + let parsed_version = semver::Version::from_str(version_str) + .map_err(|e| wast::Error::new(parser.cur_span(), format!("version is corrupted: {}", e)))?; + *version = Some(parsed_version); + + Ok(()) +} + /// Parse a WIT definition in its textual format, and produces an /// [AST](crate::ast) with the [`Interfaces`](crate::ast::Interfaces) /// structure upon succesful. diff --git a/wasmer-it/src/encoders/binary.rs b/wasmer-it/src/encoders/binary.rs index 260c428..7857dc6 100644 --- a/wasmer-it/src/encoders/binary.rs +++ b/wasmer-it/src/encoders/binary.rs @@ -33,6 +33,7 @@ where Self::Adapter => 0x02_u8.to_bytes(writer), Self::Export => 0x03_u8.to_bytes(writer), Self::Implementation => 0x04_u8.to_bytes(writer), + Self::Version => 0x05_u8.to_bytes(writer), } } } @@ -144,6 +145,9 @@ where W: Write, { fn to_bytes(&self, writer: &mut W) -> io::Result<()> { + InterfaceKind::Version.to_bytes(writer)?; + self.version.to_string().to_bytes(writer)?; + if !self.types.is_empty() { InterfaceKind::Type.to_bytes(writer)?; self.types.to_bytes(writer)?; diff --git a/wasmer-it/src/encoders/wat.rs b/wasmer-it/src/encoders/wat.rs index cff8aa1..b95dbec 100644 --- a/wasmer-it/src/encoders/wat.rs +++ b/wasmer-it/src/encoders/wat.rs @@ -296,6 +296,10 @@ impl<'input> ToString for &Interfaces<'input> { } }; + output.push_str("Version: "); + output.push_str(&self.version.to_string()); + separator(&mut output); + if !types.is_empty() { output.push_str(";; Types"); output.push_str(&types);