From b3af77c92dd881d516248b1f1f5031f051adbc85 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 11 Sep 2019 23:51:20 +0200 Subject: [PATCH 01/84] feat(wasmer-interface-types) Draft. --- Cargo.lock | 43 ++++ Cargo.toml | 30 +-- lib/interface-types/Cargo.toml | 15 ++ lib/interface-types/src/lib.rs | 213 ++++++++++++++++++ .../tests/assets/hello_world.wasm | Bin 0 -> 444 bytes lib/runtime-core/Cargo.toml | 9 +- lib/runtime/Cargo.toml | 1 + 7 files changed, 293 insertions(+), 18 deletions(-) create mode 100644 lib/interface-types/Cargo.toml create mode 100644 lib/interface-types/src/lib.rs create mode 100644 lib/interface-types/tests/assets/hello_world.wasm diff --git a/Cargo.lock b/Cargo.lock index 11c8633e3..efd30b42c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,6 +708,18 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lexical-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libc" version = "0.2.62" @@ -810,6 +822,16 @@ dependencies = [ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nom" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lexical-core 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-traits" version = "0.1.43" @@ -1236,6 +1258,11 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "static_assertions" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "strsim" version = "0.8.0" @@ -1609,6 +1636,15 @@ dependencies = [ "wasmer-singlepass-backend 0.7.0", ] +[[package]] +name = "wasmer-interface-types" +version = "0.6.0" +dependencies = [ + "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-clif-backend 0.6.0", + "wasmer-runtime-core 0.6.0", +] + [[package]] name = "wasmer-kernel-loader" version = "0.1.0" @@ -1933,7 +1969,12 @@ dependencies = [ "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +<<<<<<< HEAD "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +======= +"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" +"checksum lexical-core 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d11f3928ffd249baadf9f083cdea16d7cf317b2a8be6227e1169102432a36d2" +>>>>>>> feat(wasmer-interface-types) Draft. "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" "checksum llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2110cd4daf9cd8e39dd3b933b1a2a2ac7315e91f7c92b3a20beab526c63b5978" @@ -1946,6 +1987,7 @@ dependencies = [ "checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c618b63422da4401283884e6668d39f819a106ef51f5f59b81add00075da35ca" "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" @@ -1999,6 +2041,7 @@ dependencies = [ "checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum structopt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ac9d6e93dd792b217bf89cda5c14566e3043960c6f9da890c2ba5d09d07804c" "checksum structopt-derive 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ae9e5165d463a0dea76967d021f8d0f9316057bf5163aa2a4843790e842ff37" diff --git a/Cargo.toml b/Cargo.toml index 31ba526bd..7e1e49c14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,25 +40,26 @@ wasmer-emscripten-tests = { path = "lib/emscripten-tests", optional = true } [workspace] members = [ + "examples/plugin-for-example", "lib/clif-backend", - "lib/singlepass-backend", - "lib/runtime", - # "lib/runtime-abi", - "lib/runtime-core", + "lib/dev-utils", "lib/emscripten", - "lib/spectests", - "lib/win-exception-handler", - "lib/runtime-c-api", - "lib/llvm-backend", - "lib/wasi", - "lib/middleware-common", + "lib/emscripten-tests", + "lib/interface-types", "lib/kernel-loader", "lib/kernel-net", - "lib/dev-utils", - "lib/wasi-tests", - "lib/emscripten-tests", + "lib/llvm-backend", + "lib/middleware-common", "lib/middleware-common-tests", - "examples/plugin-for-example" + "lib/runtime", + "lib/runtime-c-api", + "lib/runtime-core", + "lib/singlepass-backend", + "lib/spectests", + "lib/wasi", + "lib/wasi-tests", + "lib/win-exception-handler" + # "lib/runtime-abi", ] [build-dependencies] @@ -100,6 +101,7 @@ backend-singlepass = [ ] wasi = ["wasmer-wasi"] managed = ["backend-singlepass", "wasmer-runtime-core/managed"] +interface-types = ["wasmer-runtime/interface-types"] # vfs = ["wasmer-runtime-abi"] [[example]] diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml new file mode 100644 index 000000000..0c613198e --- /dev/null +++ b/lib/interface-types/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "wasmer-interface-types" +version = "0.6.0" +description = "WebAssembly Interface Types library for Wasmer" +license = "MIT" +authors = ["The Wasmer Engineering Team "] +repository = "https://github.com/wasmerio/wasmer" +edition = "2018" + +[dependencies] +nom = "5.0" + +[dev-dependencies] +wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0", features = ["backend-cranelift"] } +wasmer-clif-backend = { path = "../clif-backend", version = "0.6.0" } \ No newline at end of file diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs new file mode 100644 index 000000000..2ba8a8b3f --- /dev/null +++ b/lib/interface-types/src/lib.rs @@ -0,0 +1,213 @@ +use nom::{ + error::{make_error, ErrorKind, ParseError}, + Err, IResult, +}; +use std::{convert::TryFrom, str}; + +macro_rules! d { + ($expression:expr) => { + match $expression { + tmp => { + eprintln!( + "[{}:{}] {} = {:?}", + file!(), + line!(), + stringify!($expression), + &tmp + ); + + tmp + } + } + }; +} + +#[derive(PartialEq, Debug)] +enum Type { + Int, + Float, + Any, + String, + Seq, + + I32, + I64, + F32, + F64, + AnyRef, +} + +#[derive(Debug)] +struct Export<'input> { + name: &'input str, + input_types: Vec, + output_types: Vec, +} + +impl TryFrom for Type { + type Error = &'static str; + + fn try_from(code: u64) -> Result { + Ok(match code { + 0x7fff => Self::Int, + 0x7ffe => Self::Float, + 0x7ffd => Self::Any, + 0x7ffc => Self::String, + 0x7ffb => Self::Seq, + 0x7f => Self::I32, + 0x7e => Self::I64, + 0x7d => Self::F32, + 0x7c => Self::F64, + 0x6f => Self::AnyRef, + + _ => return Err("Unknown type code."), + }) + } +} + +fn leb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u64, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + let (output, bytes) = match input.iter().position(|&byte| byte & 0x80 == 0) { + Some(position) => (&input[position + 1..], &input[..position + 1]), + None => (&[] as &[u8], input), + }; + + Ok(( + output, + bytes + .iter() + .rev() + .fold(0, |acc, byte| (acc << 7) | (byte & 0x7f) as u64), + )) +} + +fn string<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], &'input str, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + let length = input[0] as usize; + let input = &input[1..]; + + if input.len() < length { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + Ok((&input[length..], unsafe { + str::from_utf8_unchecked(&input[..length]) + })) +} + +fn list<'input, I: ::std::fmt::Debug, E: ParseError<&'input [u8]>>( + input: &'input [u8], + item_parser: fn(&'input [u8]) -> IResult<&'input [u8], I, E>, +) -> IResult<&'input [u8], Vec, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + let length = input[0] as usize; + let mut input = &input[1..]; + + if input.len() < length { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + let mut items = vec![]; + + for _ in 0..length { + let (next_input, item) = item_parser(input)?; + items.push(item); + input = next_input; + } + + Ok((input, items)) +} + +fn ty<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], Type, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + let (input, ty) = leb(input)?; + + match Type::try_from(ty) { + Ok(ty) => Ok((input, ty)), + Err(_) => Err(Err::Error(make_error(input, ErrorKind::ParseTo))), + } +} + +pub fn parse<'input, E: ParseError<&'input [u8]>>( + bytes: &'input [u8], +) -> IResult<&'input [u8], bool, E> { + let input = bytes; + let (mut input, number_of_exports) = leb(input)?; + d!(number_of_exports); + + let mut exports = vec![]; + + for export_nth in 0..number_of_exports { + let (next_input, export_name) = string(input)?; + input = next_input; + + let (next_input, export_input_types) = list(input, ty)?; + input = next_input; + + let (next_input, export_output_types) = list(input, ty)?; + input = next_input; + + exports.push(Export { + name: export_name, + input_types: export_input_types, + output_types: export_output_types, + }); + } + + d!(exports); + + Ok((&[] as &[u8], true)) +} + +#[cfg(test)] +mod tests { + use super::parse; + use std::fs; + use wasmer_clif_backend::CraneliftCompiler; + use wasmer_runtime_core as runtime; + + fn get_module() -> runtime::Module { + runtime::compile_with( + fs::read("tests/assets/hello_world.wasm") + .expect("Failed to read `tests/assets/hello_world.wasm`.") + .as_slice(), + &CraneliftCompiler::new(), + ) + .expect("Failed to parse the `hello_world.wasm` module.") + } + + #[test] + fn test_has_custom_section() { + let module = get_module(); + let custom_section = module.info().custom_sections.get("interface-types"); + + assert!(custom_section.is_some()); + } + + #[test] + fn test_parse() { + let module = get_module(); + let custom_section_bytes = module + .info() + .custom_sections + .get("interface-types") + .unwrap() + .as_slice(); + + parse::<()>(custom_section_bytes); + } +} diff --git a/lib/interface-types/tests/assets/hello_world.wasm b/lib/interface-types/tests/assets/hello_world.wasm new file mode 100644 index 0000000000000000000000000000000000000000..88d3c079ccadb8ee3722e991a139b542f26d797f GIT binary patch literal 444 zcmZ{gO-{ow5QX22oq}-HMr_!+J0#Q#z@}LsvE&A6NDZWNY?QcES*2IwUP$1kAfcje zW=8X)_vS;<)&zjAk|p(6g8C&8%b2;3k?(|j^=(k4cdfT3xBdkfT9cJ8H??sg4^^-R zB#e6>)u#4M$F$=ei$t;z^6W;+x~LpDDXjG+#HbC8H4bvRZK_~$x3M;V>ViRX@hDTG zK1)-iG=oM8l_1-lt83z3EHvi8j5XiOg{G;}$Y>q+XrE{d1vQCRn${U^5i6xG30bPf z4w(FquF!iUjcbdhlqapPA^ R&?%U`Ad&}Od~AmXoZr-0bans$ literal 0 HcmV?d00001 diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 8ce52ebe0..d475bbbb0 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -55,7 +55,8 @@ cc = "1.0" debug = [] trace = ["debug"] # backend flags used in conditional compilation of Backend::variants -"backend-cranelift" = [] -"backend-singlepass" = [] -"backend-llvm" = [] -managed = [] \ No newline at end of file +backend-cranelift = [] +backend-singlepass = [] +backend-llvm = [] +managed = [] +interface-types = [] \ No newline at end of file diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index 2321cf996..c99a1a52e 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -41,6 +41,7 @@ singlepass = ["wasmer-singlepass-backend"] default-backend-singlepass = ["singlepass"] default-backend-llvm = ["llvm"] default-backend-cranelift = ["cranelift"] +interface-types = ["wasmer-runtime-core/interface-types"] [[bench]] name = "nginx" From 7ca546e5c5756d2ad2935ae95b9053431c18c84c Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 12 Sep 2019 15:17:55 +0200 Subject: [PATCH 02/84] feat(interface-types) Continue. --- lib/interface-types/src/lib.rs | 180 +++++++++++++++++++++++++++------ 1 file changed, 149 insertions(+), 31 deletions(-) diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 2ba8a8b3f..9074697a8 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -22,8 +22,15 @@ macro_rules! d { }; } +macro_rules! consume { + (($input:ident, $parser_output:ident) = $parser_expression:expr) => { + let (next_input, $parser_output) = $parser_expression; + $input = next_input; + }; +} + #[derive(PartialEq, Debug)] -enum Type { +enum InterfaceType { Int, Float, Any, @@ -37,14 +44,7 @@ enum Type { AnyRef, } -#[derive(Debug)] -struct Export<'input> { - name: &'input str, - input_types: Vec, - output_types: Vec, -} - -impl TryFrom for Type { +impl TryFrom for InterfaceType { type Error = &'static str; fn try_from(code: u64) -> Result { @@ -59,12 +59,61 @@ impl TryFrom for Type { 0x7d => Self::F32, 0x7c => Self::F64, 0x6f => Self::AnyRef, - - _ => return Err("Unknown type code."), + _ => return Err("Unknown interface type code."), }) } } +#[derive(PartialEq, Debug)] +enum AdapterKind { + Import, + Export, + HelperFunction, +} + +impl TryFrom for AdapterKind { + type Error = &'static str; + + fn try_from(code: u8) -> Result { + Ok(match code { + 0 => Self::Import, + 1 => Self::Export, + 2 => Self::HelperFunction, + _ => return Err("Unknown adapter kind code."), + }) + } +} + +#[derive(Debug)] +struct Export<'input> { + name: &'input str, + input_types: Vec, + output_types: Vec, +} + +#[derive(Debug)] +struct Type<'input> { + name: &'input str, + fields: Vec<&'input str>, + types: Vec, +} + +#[derive(Debug)] +struct ImportedFunction<'input> { + namespace: &'input str, + name: &'input str, + input_types: Vec, + output_types: Vec, +} + +fn byte<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u8, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + Ok((&input[1..], input[0])) +} + fn leb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u64, E> { if input.is_empty() { return Err(Err::Error(make_error(input, ErrorKind::Eof))); @@ -103,7 +152,7 @@ fn string<'input, E: ParseError<&'input [u8]>>( })) } -fn list<'input, I: ::std::fmt::Debug, E: ParseError<&'input [u8]>>( +fn list<'input, I, E: ParseError<&'input [u8]>>( input: &'input [u8], item_parser: fn(&'input [u8]) -> IResult<&'input [u8], I, E>, ) -> IResult<&'input [u8], Vec, E> { @@ -121,22 +170,23 @@ fn list<'input, I: ::std::fmt::Debug, E: ParseError<&'input [u8]>>( let mut items = vec![]; for _ in 0..length { - let (next_input, item) = item_parser(input)?; + consume!((input, item) = item_parser(input)?); items.push(item); - input = next_input; } Ok((input, items)) } -fn ty<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], Type, E> { +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 (input, ty) = leb(input)?; - match Type::try_from(ty) { + match InterfaceType::try_from(ty) { Ok(ty) => Ok((input, ty)), Err(_) => Err(Err::Error(make_error(input, ErrorKind::ParseTo))), } @@ -145,30 +195,98 @@ fn ty<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'inp pub fn parse<'input, E: ParseError<&'input [u8]>>( bytes: &'input [u8], ) -> IResult<&'input [u8], bool, E> { - let input = bytes; - let (mut input, number_of_exports) = leb(input)?; - d!(number_of_exports); + let mut input = bytes; let mut exports = vec![]; + let mut types = vec![]; + let mut imported_functions = vec![]; - for export_nth in 0..number_of_exports { - let (next_input, export_name) = string(input)?; - input = next_input; + { + consume!((input, number_of_exports) = leb(input)?); + d!(number_of_exports); - let (next_input, export_input_types) = list(input, ty)?; - input = next_input; + for _ in 0..number_of_exports { + consume!((input, export_name) = string(input)?); + consume!((input, export_input_types) = list(input, ty)?); + consume!((input, export_output_types) = list(input, ty)?); - let (next_input, export_output_types) = list(input, ty)?; - input = next_input; + exports.push(Export { + name: export_name, + input_types: export_input_types, + output_types: export_output_types, + }); + } + } - exports.push(Export { - name: export_name, - input_types: export_input_types, - output_types: export_output_types, - }); + { + consume!((input, number_of_types) = leb(input)?); + d!(number_of_types); + + 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)?); + + types.push(Type { + name: type_name, + fields: type_fields, + types: type_types, + }); + } + } + + { + consume!((input, number_of_imported_functions) = leb(input)?); + d!(number_of_imported_functions); + + for _ in 0..number_of_imported_functions { + consume!((input, imported_function_namespace) = string(input)?); + consume!((input, imported_function_name) = string(input)?); + consume!((input, imported_function_input_types) = list(input, ty)?); + consume!((input, imported_function_output_types) = list(input, ty)?); + + imported_functions.push(ImportedFunction { + namespace: imported_function_namespace, + name: imported_function_name, + input_types: imported_function_input_types, + output_types: imported_function_output_types, + }); + } + } + + { + consume!((input, number_of_adapters) = leb(input)?); + d!(number_of_adapters); + + for _ in 0..number_of_adapters { + consume!((input, adapter_kind) = byte(input)?); + let adapter_kind = AdapterKind::try_from(adapter_kind) + .map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?; + d!(&adapter_kind); + + match adapter_kind { + AdapterKind::Import => { + consume!((input, adapter_namespace) = string(input)?); + d!(adapter_namespace); + + consume!((input, adapter_name) = string(input)?); + d!(adapter_name); + + consume!((input, adapter_input_types) = list(input, ty)?); + d!(adapter_input_types); + + consume!((input, adapter_output_types) = list(input, ty)?); + d!(adapter_output_types); + } + + _ => println!("kind = {:?}", adapter_kind), + } + } } d!(exports); + d!(types); + d!(imported_functions); Ok((&[] as &[u8], true)) } From 45ba77c5e30a9408106cc0d26b888d7e0cd95c11 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 12 Sep 2019 22:33:44 +0200 Subject: [PATCH 03/84] feat(interface-types) Continue. --- lib/interface-types/src/lib.rs | 446 ++++++++++++++++++++++++++------- 1 file changed, 357 insertions(+), 89 deletions(-) diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 9074697a8..a14534e2d 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -30,7 +30,7 @@ macro_rules! consume { } #[derive(PartialEq, Debug)] -enum InterfaceType { +pub enum InterfaceType { Int, Float, Any, @@ -65,7 +65,7 @@ impl TryFrom for InterfaceType { } #[derive(PartialEq, Debug)] -enum AdapterKind { +pub enum AdapterKind { Import, Export, HelperFunction, @@ -76,36 +76,91 @@ impl TryFrom for AdapterKind { fn try_from(code: u8) -> Result { Ok(match code { - 0 => Self::Import, - 1 => Self::Export, - 2 => Self::HelperFunction, + 0x0 => Self::Import, + 0x1 => Self::Export, + 0x2 => Self::HelperFunction, _ => return Err("Unknown adapter kind code."), }) } } -#[derive(Debug)] -struct Export<'input> { +#[derive(PartialEq, Debug)] +pub enum Instruction<'input> { + ArgumentGet(u64), + Call(u64), + CallExport(&'input str), + ReadUtf8, + WriteUtf8(&'input str), + AsWasm(InterfaceType), + AsInterface(InterfaceType), + TableRefAdd, + TableRefGet, + CallMethod(u64), + MakeRecord(InterfaceType), + GetField(u64, u64), + Const(InterfaceType, u64), + FoldSeq(u64), +} + +#[derive(PartialEq, Debug)] +pub struct Export<'input> { name: &'input str, input_types: Vec, output_types: Vec, } -#[derive(Debug)] -struct Type<'input> { +#[derive(PartialEq, Debug)] +pub struct Type<'input> { name: &'input str, fields: Vec<&'input str>, types: Vec, } -#[derive(Debug)] -struct ImportedFunction<'input> { +#[derive(PartialEq, Debug)] +pub struct ImportedFunction<'input> { namespace: &'input str, name: &'input str, input_types: Vec, output_types: Vec, } +#[derive(PartialEq, Debug)] +pub enum Adapter<'input> { + Import { + namespace: &'input str, + name: &'input str, + input_types: Vec, + output_types: Vec, + instructions: Vec>, + }, + Export { + name: &'input str, + input_types: Vec, + output_types: Vec, + instructions: Vec>, + }, + HelperFunction { + name: &'input str, + input_types: Vec, + output_types: Vec, + instructions: Vec>, + }, +} + +#[derive(PartialEq, Debug)] +pub struct Forward<'input> { + name: &'input str, +} + +#[derive(PartialEq, Debug)] +pub struct Interfaces<'input> { + exports: Vec>, + types: Vec>, + imported_functions: Vec>, + adapters: Vec>, + forwards: Vec>, +} + fn byte<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u8, E> { if input.is_empty() { return Err(Err::Error(make_error(input, ErrorKind::Eof))); @@ -184,116 +239,264 @@ fn ty<'input, E: ParseError<&'input [u8]>>( return Err(Err::Error(make_error(input, ErrorKind::Eof))); } - let (input, ty) = leb(input)?; + let (output, ty) = leb(input)?; match InterfaceType::try_from(ty) { - Ok(ty) => Ok((input, ty)), + Ok(ty) => Ok((output, ty)), Err(_) => Err(Err::Error(make_error(input, ErrorKind::ParseTo))), } } -pub fn parse<'input, E: ParseError<&'input [u8]>>( - bytes: &'input [u8], -) -> IResult<&'input [u8], bool, E> { - let mut input = bytes; +fn instructions<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Instruction, E> { + let (mut input, opcode) = byte(input)?; + Ok(match opcode { + 0x00 => { + consume!((input, argument_0) = leb(input)?); + (input, Instruction::ArgumentGet(argument_0)) + } + + 0x01 => { + consume!((input, argument_0) = leb(input)?); + (input, Instruction::Call(argument_0)) + } + + 0x02 => { + consume!((input, argument_0) = string(input)?); + (input, Instruction::CallExport(argument_0)) + } + + 0x03 => (input, Instruction::ReadUtf8), + + 0x04 => { + consume!((input, argument_0) = string(input)?); + (input, Instruction::WriteUtf8(argument_0)) + } + + 0x05 => { + consume!((input, argument_0) = ty(input)?); + (input, Instruction::AsWasm(argument_0)) + } + + 0x06 => { + consume!((input, argument_0) = ty(input)?); + (input, Instruction::AsInterface(argument_0)) + } + + 0x07 => (input, Instruction::TableRefAdd), + + 0x08 => (input, Instruction::TableRefGet), + + 0x09 => { + consume!((input, argument_0) = leb(input)?); + (input, Instruction::CallMethod(argument_0)) + } + + 0x0a => { + consume!((input, argument_0) = ty(input)?); + (input, Instruction::MakeRecord(argument_0)) + } + + 0x0c => { + consume!((input, argument_0) = leb(input)?); + consume!((input, argument_1) = leb(input)?); + (input, Instruction::GetField(argument_0, argument_1)) + } + + 0x0d => { + consume!((input, argument_0) = ty(input)?); + consume!((input, argument_1) = leb(input)?); + (input, Instruction::Const(argument_0, argument_1)) + } + + 0x0e => { + consume!((input, argument_0) = leb(input)?); + (input, Instruction::FoldSeq(argument_0)) + } + + _ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))), + }) +} + +pub fn exports<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Vec, E> { + let mut input = input; let mut exports = vec![]; + + consume!((input, number_of_exports) = leb(input)?); + + for _ in 0..number_of_exports { + consume!((input, export_name) = string(input)?); + consume!((input, export_input_types) = list(input, ty)?); + consume!((input, export_output_types) = list(input, ty)?); + + exports.push(Export { + name: export_name, + input_types: export_input_types, + output_types: export_output_types, + }); + } + + Ok((input, exports)) +} + +pub fn types<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Vec, E> { + let mut input = input; let mut types = vec![]; + + consume!((input, number_of_types) = leb(input)?); + + 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)?); + + types.push(Type { + name: type_name, + fields: type_fields, + types: type_types, + }); + } + + Ok((input, types)) +} + +pub fn imported_functions<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Vec, E> { + let mut input = input; let mut imported_functions = vec![]; - { - consume!((input, number_of_exports) = leb(input)?); - d!(number_of_exports); + consume!((input, number_of_imported_functions) = leb(input)?); - for _ in 0..number_of_exports { - consume!((input, export_name) = string(input)?); - consume!((input, export_input_types) = list(input, ty)?); - consume!((input, export_output_types) = list(input, ty)?); + for _ in 0..number_of_imported_functions { + consume!((input, imported_function_namespace) = string(input)?); + consume!((input, imported_function_name) = string(input)?); + consume!((input, imported_function_input_types) = list(input, ty)?); + consume!((input, imported_function_output_types) = list(input, ty)?); - exports.push(Export { - name: export_name, - input_types: export_input_types, - output_types: export_output_types, - }); - } + imported_functions.push(ImportedFunction { + namespace: imported_function_namespace, + name: imported_function_name, + input_types: imported_function_input_types, + output_types: imported_function_output_types, + }); } - { - consume!((input, number_of_types) = leb(input)?); - d!(number_of_types); + Ok((input, imported_functions)) +} - 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)?); +pub fn adapters<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Vec, E> { + let mut input = input; + let mut adapters = vec![]; - types.push(Type { - name: type_name, - fields: type_fields, - types: type_types, - }); - } - } + consume!((input, number_of_adapters) = leb(input)?); - { - consume!((input, number_of_imported_functions) = leb(input)?); - d!(number_of_imported_functions); + for _ in 0..number_of_adapters { + consume!((input, adapter_kind) = byte(input)?); + let adapter_kind = AdapterKind::try_from(adapter_kind) + .map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?; - for _ in 0..number_of_imported_functions { - consume!((input, imported_function_namespace) = string(input)?); - consume!((input, imported_function_name) = string(input)?); - consume!((input, imported_function_input_types) = list(input, ty)?); - consume!((input, imported_function_output_types) = list(input, ty)?); + match adapter_kind { + AdapterKind::Import => { + consume!((input, adapter_namespace) = string(input)?); + consume!((input, adapter_name) = string(input)?); + consume!((input, adapter_input_types) = list(input, ty)?); + consume!((input, adapter_output_types) = list(input, ty)?); + consume!((input, adapter_instructions) = list(input, instructions)?); - imported_functions.push(ImportedFunction { - namespace: imported_function_namespace, - name: imported_function_name, - input_types: imported_function_input_types, - output_types: imported_function_output_types, - }); - } - } + adapters.push(Adapter::Import { + namespace: adapter_namespace, + name: adapter_name, + input_types: adapter_input_types, + output_types: adapter_output_types, + instructions: adapter_instructions, + }); + } - { - consume!((input, number_of_adapters) = leb(input)?); - d!(number_of_adapters); + AdapterKind::Export => { + consume!((input, adapter_name) = string(input)?); + consume!((input, adapter_input_types) = list(input, ty)?); + consume!((input, adapter_output_types) = list(input, ty)?); + consume!((input, adapter_instructions) = list(input, instructions)?); - for _ in 0..number_of_adapters { - consume!((input, adapter_kind) = byte(input)?); - let adapter_kind = AdapterKind::try_from(adapter_kind) - .map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?; - d!(&adapter_kind); + adapters.push(Adapter::Export { + name: adapter_name, + input_types: adapter_input_types, + output_types: adapter_output_types, + instructions: adapter_instructions, + }); + } - match adapter_kind { - AdapterKind::Import => { - consume!((input, adapter_namespace) = string(input)?); - d!(adapter_namespace); + AdapterKind::HelperFunction => { + consume!((input, adapter_name) = string(input)?); + consume!((input, adapter_input_types) = list(input, ty)?); + consume!((input, adapter_output_types) = list(input, ty)?); + consume!((input, adapter_instructions) = list(input, instructions)?); - consume!((input, adapter_name) = string(input)?); - d!(adapter_name); - - consume!((input, adapter_input_types) = list(input, ty)?); - d!(adapter_input_types); - - consume!((input, adapter_output_types) = list(input, ty)?); - d!(adapter_output_types); - } - - _ => println!("kind = {:?}", adapter_kind), + adapters.push(Adapter::HelperFunction { + name: adapter_name, + input_types: adapter_input_types, + output_types: adapter_output_types, + instructions: adapter_instructions, + }); } } } - d!(exports); - d!(types); - d!(imported_functions); + Ok((input, adapters)) +} - Ok((&[] as &[u8], true)) +pub fn forwards<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Vec, E> { + let mut input = input; + let mut forwards = vec![]; + + consume!((input, number_of_forwards) = leb(input)?); + + for _ in 0..number_of_forwards { + consume!((input, forward_name) = string(input)?); + + forwards.push(Forward { name: forward_name }); + } + + Ok((input, forwards)) +} + +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, imported_functions) = imported_functions(input)?); + consume!((input, adapters) = adapters(input)?); + consume!((input, forwards) = forwards(input)?); + + Ok(( + input, + Interfaces { + exports, + types, + imported_functions, + adapters, + forwards, + }, + )) } #[cfg(test)] mod tests { - use super::parse; + use super::*; use std::fs; use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core as runtime; @@ -326,6 +529,71 @@ mod tests { .unwrap() .as_slice(); - parse::<()>(custom_section_bytes); + match parse::<()>(custom_section_bytes) { + Ok((remainder, interfaces)) => { + assert!(remainder.is_empty()); + assert_eq!( + interfaces, + Interfaces { + exports: vec![ + Export { + name: "strlen", + input_types: vec![InterfaceType::I32], + output_types: vec![InterfaceType::I32] + }, + Export { + name: "write_null_byte", + input_types: vec![InterfaceType::I32, InterfaceType::I32], + output_types: vec![InterfaceType::I32], + } + ], + types: vec![], + imported_functions: vec![ + ImportedFunction { + namespace: "host", + name: "console_log", + input_types: vec![InterfaceType::String], + output_types: vec![], + }, + ImportedFunction { + namespace: "host", + name: "document_title", + input_types: vec![], + output_types: vec![InterfaceType::String], + } + ], + adapters: vec![ + Adapter::Import { + namespace: "host", + name: "console_log", + input_types: vec![InterfaceType::I32], + output_types: vec![], + instructions: vec![ + Instruction::ArgumentGet(0), + Instruction::ArgumentGet(0), + Instruction::CallExport("strlen"), + Instruction::ReadUtf8, + Instruction::Call(0), + ] + }, + Adapter::Import { + namespace: "host", + name: "document_title", + input_types: vec![], + output_types: vec![InterfaceType::I32], + instructions: vec![ + Instruction::Call(1), + Instruction::WriteUtf8("alloc"), + Instruction::CallExport("write_null_byte"), + ] + } + ], + forwards: vec![Forward { name: "main" }] + } + ); + } + + Err(_) => assert!(false), + } } } From 1c1b74baa1ff083891dc6bda80e253f825df95a9 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 12 Sep 2019 22:44:20 +0200 Subject: [PATCH 04/84] feat(interface-types) Split into multiple files. --- lib/interface-types/src/ast.rs | 133 ++++++++ lib/interface-types/src/lib.rs | 501 +---------------------------- lib/interface-types/src/macros.rs | 25 ++ lib/interface-types/src/parsers.rs | 339 +++++++++++++++++++ 4 files changed, 503 insertions(+), 495 deletions(-) create mode 100644 lib/interface-types/src/ast.rs create mode 100644 lib/interface-types/src/macros.rs create mode 100644 lib/interface-types/src/parsers.rs diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs new file mode 100644 index 000000000..764c96ecf --- /dev/null +++ b/lib/interface-types/src/ast.rs @@ -0,0 +1,133 @@ +use std::{convert::TryFrom, str}; + +#[derive(PartialEq, Debug)] +pub enum InterfaceType { + Int, + Float, + Any, + String, + Seq, + + I32, + I64, + F32, + F64, + AnyRef, +} + +impl TryFrom for InterfaceType { + type Error = &'static str; + + fn try_from(code: u64) -> Result { + Ok(match code { + 0x7fff => Self::Int, + 0x7ffe => Self::Float, + 0x7ffd => Self::Any, + 0x7ffc => Self::String, + 0x7ffb => Self::Seq, + 0x7f => Self::I32, + 0x7e => Self::I64, + 0x7d => Self::F32, + 0x7c => Self::F64, + 0x6f => Self::AnyRef, + _ => return Err("Unknown interface type code."), + }) + } +} + +#[derive(PartialEq, Debug)] +pub enum AdapterKind { + Import, + Export, + HelperFunction, +} + +impl TryFrom for AdapterKind { + type Error = &'static str; + + fn try_from(code: u8) -> Result { + Ok(match code { + 0x0 => Self::Import, + 0x1 => Self::Export, + 0x2 => Self::HelperFunction, + _ => return Err("Unknown adapter kind code."), + }) + } +} + +#[derive(PartialEq, Debug)] +pub enum Instruction<'input> { + ArgumentGet(u64), + Call(u64), + CallExport(&'input str), + ReadUtf8, + WriteUtf8(&'input str), + AsWasm(InterfaceType), + AsInterface(InterfaceType), + TableRefAdd, + TableRefGet, + CallMethod(u64), + MakeRecord(InterfaceType), + GetField(u64, u64), + Const(InterfaceType, u64), + FoldSeq(u64), +} + +#[derive(PartialEq, Debug)] +pub struct Export<'input> { + pub name: &'input str, + pub input_types: Vec, + pub output_types: Vec, +} + +#[derive(PartialEq, Debug)] +pub struct Type<'input> { + pub name: &'input str, + pub fields: Vec<&'input str>, + pub types: Vec, +} + +#[derive(PartialEq, Debug)] +pub struct ImportedFunction<'input> { + pub namespace: &'input str, + pub name: &'input str, + pub input_types: Vec, + pub output_types: Vec, +} + +#[derive(PartialEq, Debug)] +pub enum Adapter<'input> { + Import { + namespace: &'input str, + name: &'input str, + input_types: Vec, + output_types: Vec, + instructions: Vec>, + }, + Export { + name: &'input str, + input_types: Vec, + output_types: Vec, + instructions: Vec>, + }, + HelperFunction { + name: &'input str, + input_types: Vec, + output_types: Vec, + instructions: Vec>, + }, +} + +#[derive(PartialEq, Debug)] +pub struct Forward<'input> { + pub name: &'input str, +} + +#[derive(PartialEq, Debug)] +pub struct Interfaces<'input> { + pub exports: Vec>, + pub types: Vec>, + pub imported_functions: Vec>, + pub adapters: Vec>, + pub forwards: Vec>, +} diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index a14534e2d..e2710761b 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -1,502 +1,13 @@ -use nom::{ - error::{make_error, ErrorKind, ParseError}, - Err, IResult, -}; -use std::{convert::TryFrom, str}; +pub mod ast; +#[macro_use] +mod macros; +pub mod parsers; -macro_rules! d { - ($expression:expr) => { - match $expression { - tmp => { - eprintln!( - "[{}:{}] {} = {:?}", - file!(), - line!(), - stringify!($expression), - &tmp - ); - - tmp - } - } - }; -} - -macro_rules! consume { - (($input:ident, $parser_output:ident) = $parser_expression:expr) => { - let (next_input, $parser_output) = $parser_expression; - $input = next_input; - }; -} - -#[derive(PartialEq, Debug)] -pub enum InterfaceType { - Int, - Float, - Any, - String, - Seq, - - I32, - I64, - F32, - F64, - AnyRef, -} - -impl TryFrom for InterfaceType { - type Error = &'static str; - - fn try_from(code: u64) -> Result { - Ok(match code { - 0x7fff => Self::Int, - 0x7ffe => Self::Float, - 0x7ffd => Self::Any, - 0x7ffc => Self::String, - 0x7ffb => Self::Seq, - 0x7f => Self::I32, - 0x7e => Self::I64, - 0x7d => Self::F32, - 0x7c => Self::F64, - 0x6f => Self::AnyRef, - _ => return Err("Unknown interface type code."), - }) - } -} - -#[derive(PartialEq, Debug)] -pub enum AdapterKind { - Import, - Export, - HelperFunction, -} - -impl TryFrom for AdapterKind { - type Error = &'static str; - - fn try_from(code: u8) -> Result { - Ok(match code { - 0x0 => Self::Import, - 0x1 => Self::Export, - 0x2 => Self::HelperFunction, - _ => return Err("Unknown adapter kind code."), - }) - } -} - -#[derive(PartialEq, Debug)] -pub enum Instruction<'input> { - ArgumentGet(u64), - Call(u64), - CallExport(&'input str), - ReadUtf8, - WriteUtf8(&'input str), - AsWasm(InterfaceType), - AsInterface(InterfaceType), - TableRefAdd, - TableRefGet, - CallMethod(u64), - MakeRecord(InterfaceType), - GetField(u64, u64), - Const(InterfaceType, u64), - FoldSeq(u64), -} - -#[derive(PartialEq, Debug)] -pub struct Export<'input> { - name: &'input str, - input_types: Vec, - output_types: Vec, -} - -#[derive(PartialEq, Debug)] -pub struct Type<'input> { - name: &'input str, - fields: Vec<&'input str>, - types: Vec, -} - -#[derive(PartialEq, Debug)] -pub struct ImportedFunction<'input> { - namespace: &'input str, - name: &'input str, - input_types: Vec, - output_types: Vec, -} - -#[derive(PartialEq, Debug)] -pub enum Adapter<'input> { - Import { - namespace: &'input str, - name: &'input str, - input_types: Vec, - output_types: Vec, - instructions: Vec>, - }, - Export { - name: &'input str, - input_types: Vec, - output_types: Vec, - instructions: Vec>, - }, - HelperFunction { - name: &'input str, - input_types: Vec, - output_types: Vec, - instructions: Vec>, - }, -} - -#[derive(PartialEq, Debug)] -pub struct Forward<'input> { - name: &'input str, -} - -#[derive(PartialEq, Debug)] -pub struct Interfaces<'input> { - exports: Vec>, - types: Vec>, - imported_functions: Vec>, - adapters: Vec>, - forwards: Vec>, -} - -fn byte<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u8, E> { - if input.is_empty() { - return Err(Err::Error(make_error(input, ErrorKind::Eof))); - } - - Ok((&input[1..], input[0])) -} - -fn leb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u64, E> { - if input.is_empty() { - return Err(Err::Error(make_error(input, ErrorKind::Eof))); - } - - let (output, bytes) = match input.iter().position(|&byte| byte & 0x80 == 0) { - Some(position) => (&input[position + 1..], &input[..position + 1]), - None => (&[] as &[u8], input), - }; - - Ok(( - output, - bytes - .iter() - .rev() - .fold(0, |acc, byte| (acc << 7) | (byte & 0x7f) as u64), - )) -} - -fn string<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], -) -> IResult<&'input [u8], &'input str, E> { - if input.is_empty() { - return Err(Err::Error(make_error(input, ErrorKind::Eof))); - } - - let length = input[0] as usize; - let input = &input[1..]; - - if input.len() < length { - return Err(Err::Error(make_error(input, ErrorKind::Eof))); - } - - Ok((&input[length..], unsafe { - str::from_utf8_unchecked(&input[..length]) - })) -} - -fn list<'input, I, E: ParseError<&'input [u8]>>( - input: &'input [u8], - item_parser: fn(&'input [u8]) -> IResult<&'input [u8], I, E>, -) -> IResult<&'input [u8], Vec, E> { - if input.is_empty() { - return Err(Err::Error(make_error(input, ErrorKind::Eof))); - } - - let length = input[0] as usize; - let mut input = &input[1..]; - - if input.len() < length { - return Err(Err::Error(make_error(input, ErrorKind::Eof))); - } - - let mut items = vec![]; - - for _ in 0..length { - consume!((input, item) = item_parser(input)?); - items.push(item); - } - - Ok((input, items)) -} - -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) = leb(input)?; - - match InterfaceType::try_from(ty) { - Ok(ty) => Ok((output, ty)), - Err(_) => Err(Err::Error(make_error(input, ErrorKind::ParseTo))), - } -} - -fn instructions<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], -) -> IResult<&'input [u8], Instruction, E> { - let (mut input, opcode) = byte(input)?; - - Ok(match opcode { - 0x00 => { - consume!((input, argument_0) = leb(input)?); - (input, Instruction::ArgumentGet(argument_0)) - } - - 0x01 => { - consume!((input, argument_0) = leb(input)?); - (input, Instruction::Call(argument_0)) - } - - 0x02 => { - consume!((input, argument_0) = string(input)?); - (input, Instruction::CallExport(argument_0)) - } - - 0x03 => (input, Instruction::ReadUtf8), - - 0x04 => { - consume!((input, argument_0) = string(input)?); - (input, Instruction::WriteUtf8(argument_0)) - } - - 0x05 => { - consume!((input, argument_0) = ty(input)?); - (input, Instruction::AsWasm(argument_0)) - } - - 0x06 => { - consume!((input, argument_0) = ty(input)?); - (input, Instruction::AsInterface(argument_0)) - } - - 0x07 => (input, Instruction::TableRefAdd), - - 0x08 => (input, Instruction::TableRefGet), - - 0x09 => { - consume!((input, argument_0) = leb(input)?); - (input, Instruction::CallMethod(argument_0)) - } - - 0x0a => { - consume!((input, argument_0) = ty(input)?); - (input, Instruction::MakeRecord(argument_0)) - } - - 0x0c => { - consume!((input, argument_0) = leb(input)?); - consume!((input, argument_1) = leb(input)?); - (input, Instruction::GetField(argument_0, argument_1)) - } - - 0x0d => { - consume!((input, argument_0) = ty(input)?); - consume!((input, argument_1) = leb(input)?); - (input, Instruction::Const(argument_0, argument_1)) - } - - 0x0e => { - consume!((input, argument_0) = leb(input)?); - (input, Instruction::FoldSeq(argument_0)) - } - - _ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))), - }) -} - -pub fn exports<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], -) -> IResult<&'input [u8], Vec, E> { - let mut input = input; - let mut exports = vec![]; - - consume!((input, number_of_exports) = leb(input)?); - - for _ in 0..number_of_exports { - consume!((input, export_name) = string(input)?); - consume!((input, export_input_types) = list(input, ty)?); - consume!((input, export_output_types) = list(input, ty)?); - - exports.push(Export { - name: export_name, - input_types: export_input_types, - output_types: export_output_types, - }); - } - - Ok((input, exports)) -} - -pub fn types<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], -) -> IResult<&'input [u8], Vec, E> { - let mut input = input; - let mut types = vec![]; - - consume!((input, number_of_types) = leb(input)?); - - 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)?); - - types.push(Type { - name: type_name, - fields: type_fields, - types: type_types, - }); - } - - Ok((input, types)) -} - -pub fn imported_functions<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], -) -> IResult<&'input [u8], Vec, E> { - let mut input = input; - let mut imported_functions = vec![]; - - consume!((input, number_of_imported_functions) = leb(input)?); - - for _ in 0..number_of_imported_functions { - consume!((input, imported_function_namespace) = string(input)?); - consume!((input, imported_function_name) = string(input)?); - consume!((input, imported_function_input_types) = list(input, ty)?); - consume!((input, imported_function_output_types) = list(input, ty)?); - - imported_functions.push(ImportedFunction { - namespace: imported_function_namespace, - name: imported_function_name, - input_types: imported_function_input_types, - output_types: imported_function_output_types, - }); - } - - Ok((input, imported_functions)) -} - -pub fn adapters<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], -) -> IResult<&'input [u8], Vec, E> { - let mut input = input; - let mut adapters = vec![]; - - consume!((input, number_of_adapters) = leb(input)?); - - for _ in 0..number_of_adapters { - consume!((input, adapter_kind) = byte(input)?); - let adapter_kind = AdapterKind::try_from(adapter_kind) - .map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?; - - match adapter_kind { - AdapterKind::Import => { - consume!((input, adapter_namespace) = string(input)?); - consume!((input, adapter_name) = string(input)?); - consume!((input, adapter_input_types) = list(input, ty)?); - consume!((input, adapter_output_types) = list(input, ty)?); - consume!((input, adapter_instructions) = list(input, instructions)?); - - adapters.push(Adapter::Import { - namespace: adapter_namespace, - name: adapter_name, - input_types: adapter_input_types, - output_types: adapter_output_types, - instructions: adapter_instructions, - }); - } - - AdapterKind::Export => { - consume!((input, adapter_name) = string(input)?); - consume!((input, adapter_input_types) = list(input, ty)?); - consume!((input, adapter_output_types) = list(input, ty)?); - consume!((input, adapter_instructions) = list(input, instructions)?); - - adapters.push(Adapter::Export { - name: adapter_name, - input_types: adapter_input_types, - output_types: adapter_output_types, - instructions: adapter_instructions, - }); - } - - AdapterKind::HelperFunction => { - consume!((input, adapter_name) = string(input)?); - consume!((input, adapter_input_types) = list(input, ty)?); - consume!((input, adapter_output_types) = list(input, ty)?); - consume!((input, adapter_instructions) = list(input, instructions)?); - - adapters.push(Adapter::HelperFunction { - name: adapter_name, - input_types: adapter_input_types, - output_types: adapter_output_types, - instructions: adapter_instructions, - }); - } - } - } - - Ok((input, adapters)) -} - -pub fn forwards<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], -) -> IResult<&'input [u8], Vec, E> { - let mut input = input; - let mut forwards = vec![]; - - consume!((input, number_of_forwards) = leb(input)?); - - for _ in 0..number_of_forwards { - consume!((input, forward_name) = string(input)?); - - forwards.push(Forward { name: forward_name }); - } - - Ok((input, forwards)) -} - -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, imported_functions) = imported_functions(input)?); - consume!((input, adapters) = adapters(input)?); - consume!((input, forwards) = forwards(input)?); - - Ok(( - input, - Interfaces { - exports, - types, - imported_functions, - adapters, - forwards, - }, - )) -} +pub use parsers::parse; #[cfg(test)] mod tests { - use super::*; + use crate::{ast::*, parse}; use std::fs; use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core as runtime; diff --git a/lib/interface-types/src/macros.rs b/lib/interface-types/src/macros.rs new file mode 100644 index 000000000..7dfc839ca --- /dev/null +++ b/lib/interface-types/src/macros.rs @@ -0,0 +1,25 @@ +#[allow(unused)] +macro_rules! d { + ($expression:expr) => { + match $expression { + tmp => { + eprintln!( + "[{}:{}] {} = {:?}", + file!(), + line!(), + stringify!($expression), + &tmp + ); + + tmp + } + } + }; +} + +macro_rules! consume { + (($input:ident, $parser_output:ident) = $parser_expression:expr) => { + let (next_input, $parser_output) = $parser_expression; + $input = next_input; + }; +} diff --git a/lib/interface-types/src/parsers.rs b/lib/interface-types/src/parsers.rs new file mode 100644 index 000000000..4eb93c4d4 --- /dev/null +++ b/lib/interface-types/src/parsers.rs @@ -0,0 +1,339 @@ +use crate::ast::*; +use nom::{ + error::{make_error, ErrorKind, ParseError}, + Err, IResult, +}; +use std::{convert::TryFrom, str}; + +fn byte<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u8, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + Ok((&input[1..], input[0])) +} + +fn leb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u64, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + let (output, bytes) = match input.iter().position(|&byte| byte & 0x80 == 0) { + Some(position) => (&input[position + 1..], &input[..position + 1]), + None => (&[] as &[u8], input), + }; + + Ok(( + output, + bytes + .iter() + .rev() + .fold(0, |acc, byte| (acc << 7) | (byte & 0x7f) as u64), + )) +} + +fn string<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], &'input str, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + let length = input[0] as usize; + let input = &input[1..]; + + if input.len() < length { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + Ok((&input[length..], unsafe { + str::from_utf8_unchecked(&input[..length]) + })) +} + +fn list<'input, I, E: ParseError<&'input [u8]>>( + input: &'input [u8], + item_parser: fn(&'input [u8]) -> IResult<&'input [u8], I, E>, +) -> IResult<&'input [u8], Vec, E> { + if input.is_empty() { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + let length = input[0] as usize; + let mut input = &input[1..]; + + if input.len() < length { + return Err(Err::Error(make_error(input, ErrorKind::Eof))); + } + + let mut items = vec![]; + + for _ in 0..length { + consume!((input, item) = item_parser(input)?); + items.push(item); + } + + Ok((input, items)) +} + +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) = leb(input)?; + + match InterfaceType::try_from(ty) { + Ok(ty) => Ok((output, ty)), + Err(_) => Err(Err::Error(make_error(input, ErrorKind::ParseTo))), + } +} + +fn instructions<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Instruction, E> { + let (mut input, opcode) = byte(input)?; + + Ok(match opcode { + 0x00 => { + consume!((input, argument_0) = leb(input)?); + (input, Instruction::ArgumentGet(argument_0)) + } + + 0x01 => { + consume!((input, argument_0) = leb(input)?); + (input, Instruction::Call(argument_0)) + } + + 0x02 => { + consume!((input, argument_0) = string(input)?); + (input, Instruction::CallExport(argument_0)) + } + + 0x03 => (input, Instruction::ReadUtf8), + + 0x04 => { + consume!((input, argument_0) = string(input)?); + (input, Instruction::WriteUtf8(argument_0)) + } + + 0x05 => { + consume!((input, argument_0) = ty(input)?); + (input, Instruction::AsWasm(argument_0)) + } + + 0x06 => { + consume!((input, argument_0) = ty(input)?); + (input, Instruction::AsInterface(argument_0)) + } + + 0x07 => (input, Instruction::TableRefAdd), + + 0x08 => (input, Instruction::TableRefGet), + + 0x09 => { + consume!((input, argument_0) = leb(input)?); + (input, Instruction::CallMethod(argument_0)) + } + + 0x0a => { + consume!((input, argument_0) = ty(input)?); + (input, Instruction::MakeRecord(argument_0)) + } + + 0x0c => { + consume!((input, argument_0) = leb(input)?); + consume!((input, argument_1) = leb(input)?); + (input, Instruction::GetField(argument_0, argument_1)) + } + + 0x0d => { + consume!((input, argument_0) = ty(input)?); + consume!((input, argument_1) = leb(input)?); + (input, Instruction::Const(argument_0, argument_1)) + } + + 0x0e => { + consume!((input, argument_0) = leb(input)?); + (input, Instruction::FoldSeq(argument_0)) + } + + _ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))), + }) +} + +pub fn exports<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Vec, E> { + let mut input = input; + let mut exports = vec![]; + + consume!((input, number_of_exports) = leb(input)?); + + for _ in 0..number_of_exports { + consume!((input, export_name) = string(input)?); + consume!((input, export_input_types) = list(input, ty)?); + consume!((input, export_output_types) = list(input, ty)?); + + exports.push(Export { + name: export_name, + input_types: export_input_types, + output_types: export_output_types, + }); + } + + Ok((input, exports)) +} + +pub fn types<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Vec, E> { + let mut input = input; + let mut types = vec![]; + + consume!((input, number_of_types) = leb(input)?); + + 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)?); + + types.push(Type { + name: type_name, + fields: type_fields, + types: type_types, + }); + } + + Ok((input, types)) +} + +pub fn imported_functions<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Vec, E> { + let mut input = input; + let mut imported_functions = vec![]; + + consume!((input, number_of_imported_functions) = leb(input)?); + + for _ in 0..number_of_imported_functions { + consume!((input, imported_function_namespace) = string(input)?); + consume!((input, imported_function_name) = string(input)?); + consume!((input, imported_function_input_types) = list(input, ty)?); + consume!((input, imported_function_output_types) = list(input, ty)?); + + imported_functions.push(ImportedFunction { + namespace: imported_function_namespace, + name: imported_function_name, + input_types: imported_function_input_types, + output_types: imported_function_output_types, + }); + } + + Ok((input, imported_functions)) +} + +pub fn adapters<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Vec, E> { + let mut input = input; + let mut adapters = vec![]; + + consume!((input, number_of_adapters) = leb(input)?); + + for _ in 0..number_of_adapters { + consume!((input, adapter_kind) = byte(input)?); + let adapter_kind = AdapterKind::try_from(adapter_kind) + .map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?; + + match adapter_kind { + AdapterKind::Import => { + consume!((input, adapter_namespace) = string(input)?); + consume!((input, adapter_name) = string(input)?); + consume!((input, adapter_input_types) = list(input, ty)?); + consume!((input, adapter_output_types) = list(input, ty)?); + consume!((input, adapter_instructions) = list(input, instructions)?); + + adapters.push(Adapter::Import { + namespace: adapter_namespace, + name: adapter_name, + input_types: adapter_input_types, + output_types: adapter_output_types, + instructions: adapter_instructions, + }); + } + + AdapterKind::Export => { + consume!((input, adapter_name) = string(input)?); + consume!((input, adapter_input_types) = list(input, ty)?); + consume!((input, adapter_output_types) = list(input, ty)?); + consume!((input, adapter_instructions) = list(input, instructions)?); + + adapters.push(Adapter::Export { + name: adapter_name, + input_types: adapter_input_types, + output_types: adapter_output_types, + instructions: adapter_instructions, + }); + } + + AdapterKind::HelperFunction => { + consume!((input, adapter_name) = string(input)?); + consume!((input, adapter_input_types) = list(input, ty)?); + consume!((input, adapter_output_types) = list(input, ty)?); + consume!((input, adapter_instructions) = list(input, instructions)?); + + adapters.push(Adapter::HelperFunction { + name: adapter_name, + input_types: adapter_input_types, + output_types: adapter_output_types, + instructions: adapter_instructions, + }); + } + } + } + + Ok((input, adapters)) +} + +pub fn forwards<'input, E: ParseError<&'input [u8]>>( + input: &'input [u8], +) -> IResult<&'input [u8], Vec, E> { + let mut input = input; + let mut forwards = vec![]; + + consume!((input, number_of_forwards) = leb(input)?); + + for _ in 0..number_of_forwards { + consume!((input, forward_name) = string(input)?); + + forwards.push(Forward { name: forward_name }); + } + + Ok((input, forwards)) +} + +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, imported_functions) = imported_functions(input)?); + consume!((input, adapters) = adapters(input)?); + consume!((input, forwards) = forwards(input)?); + + Ok(( + input, + Interfaces { + exports, + types, + imported_functions, + adapters, + forwards, + }, + )) +} From dc254e084502e802ed8769b8ae7b4c81258c4d7e Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 13 Sep 2019 12:00:05 +0200 Subject: [PATCH 05/84] =?UTF-8?q?test(interface-types)=20Add=20test=20case?= =?UTF-8?q?=20for=20=E2=80=9Cunit=E2=80=9D=20parsers.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/interface-types/src/parsers.rs | 137 +++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/lib/interface-types/src/parsers.rs b/lib/interface-types/src/parsers.rs index 4eb93c4d4..c5456d3ee 100644 --- a/lib/interface-types/src/parsers.rs +++ b/lib/interface-types/src/parsers.rs @@ -337,3 +337,140 @@ pub fn parse<'input, E: ParseError<&'input [u8]>>( }, )) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_byte() { + let input = &[0x01, 0x02, 0x03]; + let output = Ok((&[0x02, 0x03][..], 0x01u8)); + + assert_eq!(byte::<()>(input), output); + } + + #[test] + fn test_leb_1_byte() { + let input = &[0x01, 0x02, 0x03]; + let output = Ok((&[0x02, 0x03][..], 0x01u64)); + + assert_eq!(leb::<()>(input), output); + } + + #[test] + fn test_leb_3_bytes() { + let input = &[0xfc, 0xff, 0x01, 0x02]; + let output = Ok((&[0x02][..], 0x7ffcu64)); + + assert_eq!(leb::<()>(input), 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::<&str, ()>(input, string), output); + } + + #[test] + fn test_ty() { + let input = &[ + 0x0a, // list of 10 items + 0xff, 0xff, 0x01, // Int + 0xfe, 0xff, 0x01, // Float + 0xfd, 0xff, 0x01, // Any + 0xfc, 0xff, 0x01, // String + 0xfb, 0xff, 0x01, // Seq + 0x7f, // I32 + 0x7e, // I64 + 0x7d, // F32 + 0x7c, // F64 + 0x6f, // AnyRef + 0x01, + ]; + let output = Ok(( + &[0x01][..], + vec![ + InterfaceType::Int, + InterfaceType::Float, + InterfaceType::Any, + InterfaceType::String, + InterfaceType::Seq, + InterfaceType::I32, + InterfaceType::I64, + InterfaceType::F32, + InterfaceType::F64, + InterfaceType::AnyRef, + ], + )); + + assert_eq!(list::(input, ty), output); + } + + #[test] + fn test_instructions() { + let input = &[ + 0x0e, // list of 14 items + 0x00, 0x01, // ArgumentGet(1) + 0x01, 0x01, // Call(1) + 0x02, 0x03, 0x61, 0x62, 0x63, // CallExport("abc") + 0x03, // ReadUtf8 + 0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8("abc") + 0x05, 0xff, 0xff, 0x01, // AsWasm(Int) + 0x06, 0x7e, // AsInterface(I64) + 0x07, // TableRefAdd + 0x08, // TableRefGet + 0x09, 0x01, // CallMethod(1) + 0x0a, 0x7f, // MakeRecord(I32) + 0x0c, 0x01, 0x02, // GetField(1, 2) + 0x0d, 0x7f, 0x01, // Const(I32, 1) + 0x0e, 0x01, // FoldSeq(1) + 0x0a, + ]; + let output = Ok(( + &[0x0a][..], + vec![ + Instruction::ArgumentGet(1), + Instruction::Call(1), + Instruction::CallExport("abc"), + Instruction::ReadUtf8, + Instruction::WriteUtf8("abc"), + Instruction::AsWasm(InterfaceType::Int), + Instruction::AsInterface(InterfaceType::I64), + Instruction::TableRefAdd, + Instruction::TableRefGet, + Instruction::CallMethod(1), + Instruction::MakeRecord(InterfaceType::I32), + Instruction::GetField(1, 2), + Instruction::Const(InterfaceType::I32, 1), + Instruction::FoldSeq(1), + ], + )); + + assert_eq!(list::(input, instructions), output); + } +} From 24ac7a6c41f2294d8cdbd1f5eef63246ca0b59fb Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 13 Sep 2019 14:24:03 +0200 Subject: [PATCH 06/84] test(interface-types) Add test cases for higher-level parsers. --- lib/interface-types/src/parsers.rs | 182 +++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/lib/interface-types/src/parsers.rs b/lib/interface-types/src/parsers.rs index c5456d3ee..9d0073e52 100644 --- a/lib/interface-types/src/parsers.rs +++ b/lib/interface-types/src/parsers.rs @@ -473,4 +473,186 @@ mod tests { assert_eq!(list::(input, instructions), output); } + + #[test] + fn test_exports() { + let input = &[ + 0x02, // 2 exports + 0x02, // string of 2 bytes + 0x61, 0x62, // "a", "b" + 0x01, // list of 1 item + 0x7f, // I32 + 0x01, // list of 1 item + 0x7f, // I32 + 0x02, // string of 2 bytes + 0x63, 0x64, // "c", "d" + 0x00, // list of 0 item + 0x00, // list of 0 item + ]; + let output = Ok(( + &[] as &[u8], + vec![ + Export { + name: "ab", + input_types: vec![InterfaceType::I32], + output_types: vec![InterfaceType::I32], + }, + Export { + name: "cd", + input_types: vec![], + output_types: vec![], + }, + ], + )); + + assert_eq!(exports::<()>(input), output); + } + + #[test] + 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 + 0x7f, // I32 + 0x7f, // I32 + ]; + let output = Ok(( + &[] as &[u8], + vec![Type { + name: "ab", + fields: vec!["cd", "e"], + types: vec![InterfaceType::I32, InterfaceType::I32], + }], + )); + + assert_eq!(types::<()>(input), output); + } + + #[test] + fn test_imported_functions() { + let input = &[ + 0x02, // 2 imported functions + 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, // string of 1 byte + 0x63, // "c" + 0x01, // string of 1 byte + 0x64, // "d" + 0x01, // list of 1 item + 0x7f, // I32 + 0x01, // list of 1 item + 0x7e, // I64 + ]; + let output = Ok(( + &[] as &[u8], + vec![ + ImportedFunction { + namespace: "a", + name: "b", + input_types: vec![InterfaceType::I32], + output_types: vec![InterfaceType::I64], + }, + ImportedFunction { + namespace: "c", + name: "d", + input_types: vec![InterfaceType::I32], + output_types: vec![InterfaceType::I64], + }, + ], + )); + + assert_eq!(imported_functions::<()>(input), output); + } + + #[test] + fn test_adapters() { + let input = &[ + 0x03, // 3 adapters + 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(1) + 0x01, // adapter kind: export + 0x01, // string of 1 byte + 0x63, // "c" + 0x01, // list of 1 item + 0x7f, // I32 + 0x01, // list of 1 item + 0x7f, // I32 + 0x01, // list of 1 item + 0x00, 0x01, // ArgumentGet(1) + 0x02, // adapter kind: helper function + 0x01, // string of 1 byte + 0x64, // "d" + 0x01, // list of 1 item + 0x7f, // I32 + 0x01, // list of 1 item + 0x7f, // I32 + 0x01, // list of 1 item + 0x00, 0x01, // ArgumentGet(1) + ]; + let output = Ok(( + &[] as &[u8], + vec![ + Adapter::Import { + namespace: "a", + name: "b", + input_types: vec![InterfaceType::I32], + output_types: vec![InterfaceType::I32], + instructions: vec![Instruction::ArgumentGet(1)], + }, + Adapter::Export { + name: "c", + input_types: vec![InterfaceType::I32], + output_types: vec![InterfaceType::I32], + instructions: vec![Instruction::ArgumentGet(1)], + }, + Adapter::HelperFunction { + name: "d", + input_types: vec![InterfaceType::I32], + output_types: vec![InterfaceType::I32], + instructions: vec![Instruction::ArgumentGet(1)], + }, + ], + )); + + assert_eq!(adapters::<()>(input), output); + } + + #[test] + fn test_forwards() { + let input = &[ + 0x02, // 2 adapters + 0x01, // string of 1 byte + 0x61, // "a" + 0x02, // string of 2 bytes + 0x62, 0x63, // "b", "c" + ]; + let output = Ok(( + &[] as &[u8], + vec![Forward { name: "a" }, Forward { name: "bc" }], + )); + + assert_eq!(forwards::<()>(input), output); + } } From 5a8a2b90ed9daab79099a4c60cd5a83d943d64d7 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 13 Sep 2019 14:26:49 +0200 Subject: [PATCH 07/84] fix(interface-types) Fix visibility of various symbols. --- lib/interface-types/src/ast.rs | 2 +- lib/interface-types/src/parsers.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 764c96ecf..2f5c5b808 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -36,7 +36,7 @@ impl TryFrom for InterfaceType { } #[derive(PartialEq, Debug)] -pub enum AdapterKind { +pub(crate) enum AdapterKind { Import, Export, HelperFunction, diff --git a/lib/interface-types/src/parsers.rs b/lib/interface-types/src/parsers.rs index 9d0073e52..9d88ab130 100644 --- a/lib/interface-types/src/parsers.rs +++ b/lib/interface-types/src/parsers.rs @@ -164,7 +164,7 @@ fn instructions<'input, E: ParseError<&'input [u8]>>( }) } -pub fn exports<'input, E: ParseError<&'input [u8]>>( +fn exports<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { let mut input = input; @@ -187,7 +187,7 @@ pub fn exports<'input, E: ParseError<&'input [u8]>>( Ok((input, exports)) } -pub fn types<'input, E: ParseError<&'input [u8]>>( +fn types<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { let mut input = input; @@ -210,7 +210,7 @@ pub fn types<'input, E: ParseError<&'input [u8]>>( Ok((input, types)) } -pub fn imported_functions<'input, E: ParseError<&'input [u8]>>( +fn imported_functions<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { let mut input = input; @@ -235,7 +235,7 @@ pub fn imported_functions<'input, E: ParseError<&'input [u8]>>( Ok((input, imported_functions)) } -pub fn adapters<'input, E: ParseError<&'input [u8]>>( +fn adapters<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { let mut input = input; @@ -298,7 +298,7 @@ pub fn adapters<'input, E: ParseError<&'input [u8]>>( Ok((input, adapters)) } -pub fn forwards<'input, E: ParseError<&'input [u8]>>( +fn forwards<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { let mut input = input; From a7ffffc8b4329fb217c3960cdbfda65a3089a566 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 13 Sep 2019 14:50:17 +0200 Subject: [PATCH 08/84] feat(interface-types) Move `TryFrom` from the `ast` to the `binary` module. --- lib/interface-types/src/ast.rs | 35 +------------------ .../src/{parsers.rs => decoders/binary.rs} | 33 +++++++++++++++++ 2 files changed, 34 insertions(+), 34 deletions(-) rename lib/interface-types/src/{parsers.rs => decoders/binary.rs} (95%) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 2f5c5b808..8a7822175 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, str}; +use std::str; #[derive(PartialEq, Debug)] pub enum InterfaceType { @@ -15,26 +15,6 @@ pub enum InterfaceType { AnyRef, } -impl TryFrom for InterfaceType { - type Error = &'static str; - - fn try_from(code: u64) -> Result { - Ok(match code { - 0x7fff => Self::Int, - 0x7ffe => Self::Float, - 0x7ffd => Self::Any, - 0x7ffc => Self::String, - 0x7ffb => Self::Seq, - 0x7f => Self::I32, - 0x7e => Self::I64, - 0x7d => Self::F32, - 0x7c => Self::F64, - 0x6f => Self::AnyRef, - _ => return Err("Unknown interface type code."), - }) - } -} - #[derive(PartialEq, Debug)] pub(crate) enum AdapterKind { Import, @@ -42,19 +22,6 @@ pub(crate) enum AdapterKind { HelperFunction, } -impl TryFrom for AdapterKind { - type Error = &'static str; - - fn try_from(code: u8) -> Result { - Ok(match code { - 0x0 => Self::Import, - 0x1 => Self::Export, - 0x2 => Self::HelperFunction, - _ => return Err("Unknown adapter kind code."), - }) - } -} - #[derive(PartialEq, Debug)] pub enum Instruction<'input> { ArgumentGet(u64), diff --git a/lib/interface-types/src/parsers.rs b/lib/interface-types/src/decoders/binary.rs similarity index 95% rename from lib/interface-types/src/parsers.rs rename to lib/interface-types/src/decoders/binary.rs index 9d88ab130..614b7b828 100644 --- a/lib/interface-types/src/parsers.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -5,6 +5,39 @@ use nom::{ }; use std::{convert::TryFrom, str}; +impl TryFrom for InterfaceType { + type Error = &'static str; + + fn try_from(code: u64) -> Result { + Ok(match code { + 0x7fff => Self::Int, + 0x7ffe => Self::Float, + 0x7ffd => Self::Any, + 0x7ffc => Self::String, + 0x7ffb => Self::Seq, + 0x7f => Self::I32, + 0x7e => Self::I64, + 0x7d => Self::F32, + 0x7c => Self::F64, + 0x6f => Self::AnyRef, + _ => return Err("Unknown interface type code."), + }) + } +} + +impl TryFrom for AdapterKind { + type Error = &'static str; + + fn try_from(code: u8) -> Result { + Ok(match code { + 0x0 => Self::Import, + 0x1 => Self::Export, + 0x2 => Self::HelperFunction, + _ => return Err("Unknown adapter kind code."), + }) + } +} + fn byte<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u8, E> { if input.is_empty() { return Err(Err::Error(make_error(input, ErrorKind::Eof))); From 4ba9aace64274e600177c76962bca62d312684ae Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 13 Sep 2019 15:39:46 +0200 Subject: [PATCH 09/84] fix(interface-types) `get-field` #1 argument is of type `InterfaceType`. --- lib/interface-types/src/ast.rs | 5 ++--- lib/interface-types/src/decoders/binary.rs | 6 +++--- lib/interface-types/src/decoders/mod.rs | 1 + 3 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 lib/interface-types/src/decoders/mod.rs diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 8a7822175..b966f010e 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -1,13 +1,12 @@ use std::str; -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Clone, Copy, Debug)] pub enum InterfaceType { Int, Float, Any, String, Seq, - I32, I64, F32, @@ -35,7 +34,7 @@ pub enum Instruction<'input> { TableRefGet, CallMethod(u64), MakeRecord(InterfaceType), - GetField(u64, u64), + GetField(InterfaceType, u64), Const(InterfaceType, u64), FoldSeq(u64), } diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 614b7b828..f3cedc53b 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -177,7 +177,7 @@ fn instructions<'input, E: ParseError<&'input [u8]>>( } 0x0c => { - consume!((input, argument_0) = leb(input)?); + consume!((input, argument_0) = ty(input)?); consume!((input, argument_1) = leb(input)?); (input, Instruction::GetField(argument_0, argument_1)) } @@ -479,7 +479,7 @@ mod tests { 0x08, // TableRefGet 0x09, 0x01, // CallMethod(1) 0x0a, 0x7f, // MakeRecord(I32) - 0x0c, 0x01, 0x02, // GetField(1, 2) + 0x0c, 0xff, 0xff, 0x01, 0x02, // GetField(Int, 2) 0x0d, 0x7f, 0x01, // Const(I32, 1) 0x0e, 0x01, // FoldSeq(1) 0x0a, @@ -498,7 +498,7 @@ mod tests { Instruction::TableRefGet, Instruction::CallMethod(1), Instruction::MakeRecord(InterfaceType::I32), - Instruction::GetField(1, 2), + Instruction::GetField(InterfaceType::Int, 2), Instruction::Const(InterfaceType::I32, 1), Instruction::FoldSeq(1), ], diff --git a/lib/interface-types/src/decoders/mod.rs b/lib/interface-types/src/decoders/mod.rs new file mode 100644 index 000000000..96eab6681 --- /dev/null +++ b/lib/interface-types/src/decoders/mod.rs @@ -0,0 +1 @@ +pub mod binary; From 40613d3d48b393eb3ad321f0deeabf1ea2c7c1cd Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 13 Sep 2019 15:40:23 +0200 Subject: [PATCH 10/84] feat(interface-types) Draft the WAT encoder. --- lib/interface-types/src/encoders/mod.rs | 1 + lib/interface-types/src/encoders/wat.rs | 165 ++++++++++++++++++++++++ lib/interface-types/src/lib.rs | 11 +- 3 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 lib/interface-types/src/encoders/mod.rs create mode 100644 lib/interface-types/src/encoders/wat.rs diff --git a/lib/interface-types/src/encoders/mod.rs b/lib/interface-types/src/encoders/mod.rs new file mode 100644 index 000000000..070bf5893 --- /dev/null +++ b/lib/interface-types/src/encoders/mod.rs @@ -0,0 +1 @@ +pub mod wat; diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs new file mode 100644 index 000000000..560489f40 --- /dev/null +++ b/lib/interface-types/src/encoders/wat.rs @@ -0,0 +1,165 @@ +use crate::ast::{Export, Instruction, InterfaceType}; + +impl From for String { + fn from(interface_type: InterfaceType) -> Self { + match interface_type { + InterfaceType::Int => "Int".into(), + InterfaceType::Float => "Float".into(), + InterfaceType::Any => "Any".into(), + InterfaceType::String => "String".into(), + InterfaceType::Seq => "Seq".into(), + InterfaceType::I32 => "i32".into(), + InterfaceType::I64 => "i64".into(), + InterfaceType::F32 => "f32".into(), + InterfaceType::F64 => "f64".into(), + InterfaceType::AnyRef => "anyref".into(), + } + } +} + +impl<'input> From> for String { + fn from(instruction: Instruction) -> Self { + match instruction { + Instruction::ArgumentGet(index) => format!("arg.get {}", index), + Instruction::Call(index) => format!("call {}", index), + Instruction::CallExport(export_name) => format!("call-export \"{}\"", export_name), + Instruction::ReadUtf8 => "read-utf8".into(), + Instruction::WriteUtf8(string) => format!("write-utf8 \"{}\"", string), + Instruction::AsWasm(interface_type) => { + format!("as-wasm {}", String::from(interface_type)) + } + Instruction::AsInterface(interface_type) => { + format!("as-interface {}", String::from(interface_type)) + } + Instruction::TableRefAdd => "table-ref-add".into(), + Instruction::TableRefGet => "table-ref-get".into(), + Instruction::CallMethod(index) => format!("call-method {}", index), + Instruction::MakeRecord(interface_type) => { + format!("make-record {}", String::from(interface_type)) + } + Instruction::GetField(interface_type, field_index) => { + format!("get-field {} {}", String::from(interface_type), field_index) + } + Instruction::Const(interface_type, value) => { + format!("const {} {}", String::from(interface_type), value) + } + Instruction::FoldSeq(import_index) => format!("fold-seq {}", import_index), + } + } +} + +impl<'input> From> for String { + fn from(export: Export) -> Self { + format!( + "(@interface export \"{}\"{}{})", + export.name, + if export.input_types.is_empty() { + "".into() + } else { + format!( + " (param{})", + export.input_types.iter().fold( + String::new(), + |mut accumulator, interface_type| { + accumulator.push(' '); + accumulator.push_str(&String::from(*interface_type)); + accumulator + } + ) + ) + }, + if export.output_types.is_empty() { + "".into() + } else { + format!( + " (result{})", + export.output_types.iter().fold( + String::new(), + |mut accumulator, interface_type| { + accumulator.push(' '); + accumulator.push_str(&String::from(*interface_type)); + accumulator + } + ) + ) + }, + ) + } +} + +#[cfg(test)] +mod tests { + use crate::ast::*; + + #[test] + fn test_interface_types() { + let inputs: Vec = vec![ + InterfaceType::Int.into(), + InterfaceType::Float.into(), + InterfaceType::Any.into(), + InterfaceType::String.into(), + InterfaceType::Seq.into(), + InterfaceType::I32.into(), + InterfaceType::I64.into(), + InterfaceType::F32.into(), + InterfaceType::F64.into(), + InterfaceType::AnyRef.into(), + ]; + let outputs = vec![ + "Int", "Float", "Any", "String", "Seq", "i32", "i64", "f32", "f64", "anyref", + ]; + + assert_eq!(inputs, outputs); + } + + #[test] + fn test_instructions() { + let inputs: Vec = vec![ + Instruction::ArgumentGet(7).into(), + Instruction::Call(7).into(), + Instruction::CallExport("foo").into(), + Instruction::ReadUtf8.into(), + Instruction::WriteUtf8("foo").into(), + Instruction::AsWasm(InterfaceType::Int).into(), + Instruction::AsInterface(InterfaceType::AnyRef).into(), + Instruction::TableRefAdd.into(), + Instruction::TableRefGet.into(), + Instruction::CallMethod(7).into(), + Instruction::MakeRecord(InterfaceType::Int).into(), + Instruction::GetField(InterfaceType::Int, 7).into(), + Instruction::Const(InterfaceType::I32, 7).into(), + Instruction::FoldSeq(7).into(), + ]; + let outputs = vec![ + "arg.get 7", + "call 7", + "call-export \"foo\"", + "read-utf8", + "write-utf8 \"foo\"", + "as-wasm Int", + "as-interface anyref", + "table-ref-add", + "table-ref-get", + "call-method 7", + "make-record Int", + "get-field Int 7", + "const i32 7", + "fold-seq 7", + ]; + + assert_eq!(inputs, outputs); + } + + #[test] + fn test_exports() { + let inputs: Vec = vec![Export { + name: "foo", + input_types: vec![InterfaceType::I32, InterfaceType::F32], + output_types: vec![InterfaceType::I32], + } + .into()]; + let outputs = vec!["(@interface export \"foo\" (param i32 f32) (result i32))"]; + + assert_eq!(inputs, outputs); + } +} diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index e2710761b..f6c4656db 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -1,13 +1,14 @@ pub mod ast; #[macro_use] mod macros; -pub mod parsers; +pub mod decoders; +pub mod encoders; -pub use parsers::parse; +pub use decoders::binary::parse as parse_binary; #[cfg(test)] mod tests { - use crate::{ast::*, parse}; + use crate::{ast::*, parse_binary}; use std::fs; use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core as runtime; @@ -31,7 +32,7 @@ mod tests { } #[test] - fn test_parse() { + fn test_parse_binary_from_custom_section() { let module = get_module(); let custom_section_bytes = module .info() @@ -40,7 +41,7 @@ mod tests { .unwrap() .as_slice(); - match parse::<()>(custom_section_bytes) { + match parse_binary::<()>(custom_section_bytes) { Ok((remainder, interfaces)) => { assert!(remainder.is_empty()); assert_eq!( From 6279b3e9153d7f7f608de4313af4174e8bae213d Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 18 Sep 2019 16:37:57 +0200 Subject: [PATCH 11/84] feat(interface-types) Continue the WAT encoder. --- lib/interface-types/src/ast.rs | 2 +- lib/interface-types/src/encoders/wat.rs | 401 +++++++++++++++++++----- 2 files changed, 328 insertions(+), 75 deletions(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index b966f010e..28c8682b1 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -1,6 +1,6 @@ use std::str; -#[derive(PartialEq, Clone, Copy, Debug)] +#[derive(PartialEq, Debug)] pub enum InterfaceType { Int, Float, diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 560489f40..eafca5713 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -1,7 +1,7 @@ -use crate::ast::{Export, Instruction, InterfaceType}; +use crate::ast::{Adapter, Export, Forward, ImportedFunction, Instruction, InterfaceType, Type}; -impl From for String { - fn from(interface_type: InterfaceType) -> Self { +impl From<&InterfaceType> for String { + fn from(interface_type: &InterfaceType) -> Self { match interface_type { InterfaceType::Int => "Int".into(), InterfaceType::Float => "Float".into(), @@ -17,14 +17,14 @@ impl From for String { } } -impl<'input> From> for String { - fn from(instruction: Instruction) -> Self { +impl<'input> From<&Instruction<'input>> for String { + fn from(instruction: &Instruction) -> Self { match instruction { Instruction::ArgumentGet(index) => format!("arg.get {}", index), Instruction::Call(index) => format!("call {}", index), - Instruction::CallExport(export_name) => format!("call-export \"{}\"", export_name), + Instruction::CallExport(export_name) => format!(r#"call-export "{}""#, export_name), Instruction::ReadUtf8 => "read-utf8".into(), - Instruction::WriteUtf8(string) => format!("write-utf8 \"{}\"", string), + Instruction::WriteUtf8(string) => format!(r#"write-utf8 "{}""#, string), Instruction::AsWasm(interface_type) => { format!("as-wasm {}", String::from(interface_type)) } @@ -48,41 +48,124 @@ impl<'input> From> for String { } } -impl<'input> From> for String { - fn from(export: Export) -> Self { +fn input_types_to_param(input_types: &Vec) -> String { + if input_types.is_empty() { + "".into() + } else { format!( - "(@interface export \"{}\"{}{})", - export.name, - if export.input_types.is_empty() { - "".into() - } else { - format!( - " (param{})", - export.input_types.iter().fold( - String::new(), - |mut accumulator, interface_type| { - accumulator.push(' '); - accumulator.push_str(&String::from(*interface_type)); - accumulator - } - ) - ) - }, - if export.output_types.is_empty() { - "".into() - } else { - format!( - " (result{})", - export.output_types.iter().fold( - String::new(), - |mut accumulator, interface_type| { - accumulator.push(' '); - accumulator.push_str(&String::from(*interface_type)); - accumulator - } - ) - ) - }, + "\n (param{})", + input_types + .iter() + .fold(String::new(), |mut accumulator, interface_type| { + accumulator.push(' '); + accumulator.push_str(&String::from(interface_type)); + accumulator + }) + ) + } +} + +fn output_types_to_result(output_types: &Vec) -> String { + if output_types.is_empty() { + "".into() + } else { + format!( + "\n (result{})", + output_types + .iter() + .fold(String::new(), |mut accumulator, interface_type| { + accumulator.push(' '); + accumulator.push_str(&String::from(interface_type)); + accumulator + }) + ) + } +} + +impl<'input> From<&Export<'input>> for String { + fn from(export: &Export) -> Self { + format!( + r#"(@interface export "{name}"{inputs}{outputs})"#, + name = export.name, + inputs = input_types_to_param(&export.input_types), + outputs = output_types_to_result(&export.output_types), + ) + } +} + +impl<'input> From<&Type<'input>> for String { + fn from(_ty: &Type) -> Self { + unimplemented!() + } +} + +impl<'input> From<&ImportedFunction<'input>> for String { + fn from(imported_function: &ImportedFunction) -> Self { + format!( + r#"(@interface func ${namespace}_{name} (import "{namespace}" "{name}"){inputs}{outputs})"#, + namespace = imported_function.namespace, + name = imported_function.name, + inputs = input_types_to_param(&imported_function.input_types), + outputs = output_types_to_result(&imported_function.output_types), + ) + } +} + +impl<'input> From<&Adapter<'input>> for String { + fn from(adapter: &Adapter) -> Self { + match adapter { + Adapter::Import { + namespace, + name, + input_types, + output_types, + instructions, + } => format!( + r#"(@interface adapt (import "{namespace}" "{name}"){inputs}{outputs}{instructions})"#, + namespace = namespace, + name = name, + inputs = input_types_to_param(&input_types), + outputs = output_types_to_result(&output_types), + instructions = instructions.iter().fold( + String::new(), + |mut accumulator, instruction| { + accumulator.push_str("\n "); + accumulator.push_str(&String::from(instruction)); + accumulator + } + ), + ), + + Adapter::Export { + name, + input_types, + output_types, + instructions, + } => format!( + r#"(@interface adapt (export "{name}"){inputs}{outputs}{instructions})"#, + name = name, + inputs = input_types_to_param(&input_types), + outputs = output_types_to_result(&output_types), + instructions = instructions.iter().fold( + String::new(), + |mut accumulator, instruction| { + accumulator.push_str("\n "); + accumulator.push_str(&String::from(instruction)); + accumulator + } + ), + ), + + _ => unimplemented!(), + } + } +} + +impl<'input> From<&Forward<'input>> for String { + fn from(forward: &Forward) -> Self { + format!( + r#"(@interface forward (export "{name}"))"#, + name = forward.name, ) } } @@ -94,16 +177,16 @@ mod tests { #[test] fn test_interface_types() { let inputs: Vec = vec![ - InterfaceType::Int.into(), - InterfaceType::Float.into(), - InterfaceType::Any.into(), - InterfaceType::String.into(), - InterfaceType::Seq.into(), - InterfaceType::I32.into(), - InterfaceType::I64.into(), - InterfaceType::F32.into(), - InterfaceType::F64.into(), - InterfaceType::AnyRef.into(), + (&InterfaceType::Int).into(), + (&InterfaceType::Float).into(), + (&InterfaceType::Any).into(), + (&InterfaceType::String).into(), + (&InterfaceType::Seq).into(), + (&InterfaceType::I32).into(), + (&InterfaceType::I64).into(), + (&InterfaceType::F32).into(), + (&InterfaceType::F64).into(), + (&InterfaceType::AnyRef).into(), ]; let outputs = vec![ "Int", "Float", "Any", "String", "Seq", "i32", "i64", "f32", "f64", "anyref", @@ -115,27 +198,27 @@ mod tests { #[test] fn test_instructions() { let inputs: Vec = vec![ - Instruction::ArgumentGet(7).into(), - Instruction::Call(7).into(), - Instruction::CallExport("foo").into(), - Instruction::ReadUtf8.into(), - Instruction::WriteUtf8("foo").into(), - Instruction::AsWasm(InterfaceType::Int).into(), - Instruction::AsInterface(InterfaceType::AnyRef).into(), - Instruction::TableRefAdd.into(), - Instruction::TableRefGet.into(), - Instruction::CallMethod(7).into(), - Instruction::MakeRecord(InterfaceType::Int).into(), - Instruction::GetField(InterfaceType::Int, 7).into(), - Instruction::Const(InterfaceType::I32, 7).into(), - Instruction::FoldSeq(7).into(), + (&Instruction::ArgumentGet(7)).into(), + (&Instruction::Call(7)).into(), + (&Instruction::CallExport("foo")).into(), + (&Instruction::ReadUtf8).into(), + (&Instruction::WriteUtf8("foo")).into(), + (&Instruction::AsWasm(InterfaceType::Int)).into(), + (&Instruction::AsInterface(InterfaceType::AnyRef)).into(), + (&Instruction::TableRefAdd).into(), + (&Instruction::TableRefGet).into(), + (&Instruction::CallMethod(7)).into(), + (&Instruction::MakeRecord(InterfaceType::Int)).into(), + (&Instruction::GetField(InterfaceType::Int, 7)).into(), + (&Instruction::Const(InterfaceType::I32, 7)).into(), + (&Instruction::FoldSeq(7)).into(), ]; let outputs = vec![ "arg.get 7", "call 7", - "call-export \"foo\"", + r#"call-export "foo""#, "read-utf8", - "write-utf8 \"foo\"", + r#"write-utf8 "foo""#, "as-wasm Int", "as-interface anyref", "table-ref-add", @@ -152,14 +235,184 @@ mod tests { #[test] fn test_exports() { - let inputs: Vec = vec![Export { - name: "foo", - input_types: vec![InterfaceType::I32, InterfaceType::F32], - output_types: vec![InterfaceType::I32], - } - .into()]; - let outputs = vec!["(@interface export \"foo\" (param i32 f32) (result i32))"]; + let inputs: Vec = vec![ + (&Export { + name: "foo", + input_types: vec![InterfaceType::I32, InterfaceType::F32], + output_types: vec![InterfaceType::I32], + }) + .into(), + (&Export { + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + }) + .into(), + (&Export { + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::I32], + }) + .into(), + (&Export { + name: "foo", + input_types: vec![], + output_types: vec![], + }) + .into(), + ]; + let outputs = vec![ + r#"(@interface export "foo" + (param i32 f32) + (result i32))"#, + r#"(@interface export "foo" + (param i32))"#, + r#"(@interface export "foo" + (result i32))"#, + r#"(@interface export "foo")"#, + ]; assert_eq!(inputs, outputs); } + + #[test] + fn test_imported_functions() { + let inputs: Vec = vec![ + (&ImportedFunction { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::Int, InterfaceType::String], + output_types: vec![InterfaceType::String], + }) + .into(), + (&ImportedFunction { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::String], + output_types: vec![], + }) + .into(), + (&ImportedFunction { + namespace: "ns", + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::String], + }) + .into(), + (&ImportedFunction { + namespace: "ns", + name: "foo", + input_types: vec![], + output_types: vec![], + }) + .into(), + ]; + let outputs = vec![ + r#"(@interface func $ns_foo (import "ns" "foo") + (param Int String) + (result String))"#, + r#"(@interface func $ns_foo (import "ns" "foo") + (param String))"#, + r#"(@interface func $ns_foo (import "ns" "foo") + (result String))"#, + r#"(@interface func $ns_foo (import "ns" "foo"))"#, + ]; + + assert_eq!(inputs, outputs); + } + + #[test] + fn test_adapters() { + let inputs: Vec = vec![ + (&Adapter::Import { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::I32, InterfaceType::F32], + output_types: vec![InterfaceType::I32], + instructions: vec![ + Instruction::ArgumentGet(0), + Instruction::WriteUtf8("hello"), + Instruction::CallExport("f"), + ], + }) + .into(), + (&Adapter::Import { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + instructions: vec![Instruction::CallExport("f")], + }) + .into(), + (&Adapter::Import { + namespace: "ns", + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::I32], + instructions: vec![Instruction::CallExport("f")], + }) + .into(), + (&Adapter::Export { + name: "foo", + input_types: vec![InterfaceType::I32, InterfaceType::F32], + output_types: vec![InterfaceType::I32], + instructions: vec![ + Instruction::ArgumentGet(0), + Instruction::WriteUtf8("hello"), + Instruction::CallExport("f"), + ], + }) + .into(), + (&Adapter::Export { + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + instructions: vec![Instruction::CallExport("f")], + }) + .into(), + (&Adapter::Export { + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::I32], + instructions: vec![Instruction::CallExport("f")], + }) + .into(), + ]; + let outputs = vec![ + r#"(@interface adapt (import "ns" "foo") + (param i32 f32) + (result i32) + arg.get 0 + write-utf8 "hello" + call-export "f")"#, + r#"(@interface adapt (import "ns" "foo") + (param i32) + call-export "f")"#, + r#"(@interface adapt (import "ns" "foo") + (result i32) + call-export "f")"#, + r#"(@interface adapt (export "foo") + (param i32 f32) + (result i32) + arg.get 0 + write-utf8 "hello" + call-export "f")"#, + r#"(@interface adapt (export "foo") + (param i32) + call-export "f")"#, + r#"(@interface adapt (export "foo") + (result i32) + call-export "f")"#, + ]; + + assert_eq!(inputs, outputs); + } + + #[test] + fn test_forward() { + let input: String = (&Forward { name: "main" }).into(); + let output = r#"(@interface forward (export "main"))"#; + + assert_eq!(input, output); + } } From bd3a8884528f1c8978ff432e8b5bee71889aaeb1 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 18 Sep 2019 17:09:18 +0200 Subject: [PATCH 12/84] feat(interface-types) Add new instructions. --- lib/interface-types/src/ast.rs | 6 +++ lib/interface-types/src/decoders/binary.rs | 44 +++++++++++++++++++++- lib/interface-types/src/encoders/wat.rs | 28 ++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 28c8682b1..63cb7789f 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -37,6 +37,12 @@ pub enum Instruction<'input> { GetField(InterfaceType, u64), Const(InterfaceType, u64), FoldSeq(u64), + Add(InterfaceType), + MemToSeq(InterfaceType, &'input str), + Load(InterfaceType, &'input str), + SeqNew(InterfaceType), + ListPush, + RepeatWhile(u64, u64), } #[derive(PartialEq, Debug)] diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index f3cedc53b..b86cdc303 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -193,6 +193,36 @@ fn instructions<'input, E: ParseError<&'input [u8]>>( (input, Instruction::FoldSeq(argument_0)) } + 0x0f => { + consume!((input, argument_0) = ty(input)?); + (input, Instruction::Add(argument_0)) + } + + 0x10 => { + consume!((input, argument_0) = ty(input)?); + consume!((input, argument_1) = string(input)?); + (input, Instruction::MemToSeq(argument_0, argument_1)) + } + + 0x11 => { + consume!((input, argument_0) = ty(input)?); + consume!((input, argument_1) = string(input)?); + (input, Instruction::Load(argument_0, argument_1)) + } + + 0x12 => { + consume!((input, argument_0) = ty(input)?); + (input, Instruction::SeqNew(argument_0)) + } + + 0x13 => (input, Instruction::ListPush), + + 0x14 => { + consume!((input, argument_0) = leb(input)?); + consume!((input, argument_1) = leb(input)?); + (input, Instruction::RepeatWhile(argument_0, argument_1)) + } + _ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))), }) } @@ -467,7 +497,7 @@ mod tests { #[test] fn test_instructions() { let input = &[ - 0x0e, // list of 14 items + 0x14, // list of 20 items 0x00, 0x01, // ArgumentGet(1) 0x01, 0x01, // Call(1) 0x02, 0x03, 0x61, 0x62, 0x63, // CallExport("abc") @@ -482,6 +512,12 @@ mod tests { 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, // RepeatWhile(1, 2) 0x0a, ]; let output = Ok(( @@ -501,6 +537,12 @@ mod tests { 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::RepeatWhile(1, 2), ], )); diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index eafca5713..41f73b473 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -44,6 +44,22 @@ impl<'input> From<&Instruction<'input>> for String { format!("const {} {}", String::from(interface_type), value) } Instruction::FoldSeq(import_index) => format!("fold-seq {}", import_index), + Instruction::Add(interface_type) => format!("add {}", String::from(interface_type)), + Instruction::MemToSeq(interface_type, memory) => format!( + r#"mem-to-seq {} "{}""#, + String::from(interface_type), + memory + ), + Instruction::Load(interface_type, memory) => { + format!(r#"load {} "{}""#, String::from(interface_type), memory) + } + Instruction::SeqNew(interface_type) => { + format!("seq.new {}", String::from(interface_type)) + } + Instruction::ListPush => "list.push".into(), + Instruction::RepeatWhile(condition_index, step_index) => { + format!("repeat-while {} {}", condition_index, step_index) + } } } } @@ -212,6 +228,12 @@ mod tests { (&Instruction::GetField(InterfaceType::Int, 7)).into(), (&Instruction::Const(InterfaceType::I32, 7)).into(), (&Instruction::FoldSeq(7)).into(), + (&Instruction::Add(InterfaceType::Int)).into(), + (&Instruction::MemToSeq(InterfaceType::Int, "foo")).into(), + (&Instruction::Load(InterfaceType::Int, "foo")).into(), + (&Instruction::SeqNew(InterfaceType::Int)).into(), + (&Instruction::ListPush).into(), + (&Instruction::RepeatWhile(1, 2)).into(), ]; let outputs = vec![ "arg.get 7", @@ -228,6 +250,12 @@ mod tests { "get-field Int 7", "const i32 7", "fold-seq 7", + "add Int", + r#"mem-to-seq Int "foo""#, + r#"load Int "foo""#, + "seq.new Int", + "list.push", + "repeat-while 1 2", ]; assert_eq!(inputs, outputs); From 480fe0fb9bae5546d4f1ca43937d56cef6e96c77 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 18 Sep 2019 17:14:12 +0200 Subject: [PATCH 13/84] chore(interface-types) Update to Wasmer 0.7.0. --- Cargo.lock | 102 ++++++++++++++++----------------- lib/interface-types/Cargo.toml | 4 +- 2 files changed, 51 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index efd30b42c..6fd1994d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,7 +75,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -123,7 +123,7 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -161,8 +161,8 @@ name = "cargo_toml" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -180,7 +180,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -333,8 +333,8 @@ dependencies = [ "rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -398,7 +398,7 @@ dependencies = [ "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -452,7 +452,7 @@ dependencies = [ [[package]] name = "either" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -481,7 +481,7 @@ name = "erased-serde" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -552,7 +552,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -629,7 +629,7 @@ name = "indexmap" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -637,7 +637,7 @@ name = "inkwell" version = "0.1.0" source = "git+https://github.com/wasmerio/inkwell?branch=llvm8-0#8f480124663b812ee76cd4370f3ee170135b9d0e" dependencies = [ - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm8-0)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -682,7 +682,7 @@ name = "itertools" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -983,7 +983,7 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1069,7 +1069,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1200,10 +1200,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1212,7 +1212,7 @@ version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1220,12 +1220,12 @@ name = "serde_bytes" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1240,7 +1240,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1270,16 +1270,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "structopt" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "structopt-derive" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1360,7 +1360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1405,7 +1405,7 @@ name = "tinytemplate" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1414,7 +1414,7 @@ name = "toml" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1422,7 +1422,7 @@ name = "toml" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1438,7 +1438,7 @@ dependencies = [ "erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "typetag-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1497,8 +1497,8 @@ name = "wabt" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "wabt-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1536,8 +1536,8 @@ dependencies = [ "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "typetag 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-backend 0.7.0", @@ -1566,10 +1566,10 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-fork-frontend 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-fork-wasm 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1617,7 +1617,7 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-runtime-core 0.7.0", ] @@ -1641,8 +1641,8 @@ name = "wasmer-interface-types" version = "0.6.0" dependencies = [ "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.6.0", - "wasmer-runtime-core 0.6.0", + "wasmer-clif-backend 0.7.0", + "wasmer-runtime-core 0.7.0", ] [[package]] @@ -1739,10 +1739,10 @@ dependencies = [ "page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "wasmparser 0.35.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1785,8 +1785,8 @@ dependencies = [ "generational-arena 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "typetag 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-runtime-core 0.7.0", @@ -1939,7 +1939,7 @@ dependencies = [ "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dynasm 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f36d49ab6f8ecc642d2c6ee10fda04ba68003ef0277300866745cdde160e6b40" "checksum dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c408a211e7f5762829f5e46bdff0c14bc3b1517a21a4bb781c716bf88b0c68" -"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7798e7da2d4cb0d6d6fc467e8d6b5bf247e9e989f786dde1732d79899c32bb10" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" "checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60" @@ -1969,12 +1969,8 @@ dependencies = [ "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -<<<<<<< HEAD "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -======= -"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lexical-core 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d11f3928ffd249baadf9f083cdea16d7cf317b2a8be6227e1169102432a36d2" ->>>>>>> feat(wasmer-interface-types) Draft. "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" "checksum llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2110cd4daf9cd8e39dd3b933b1a2a2ac7315e91f7c92b3a20beab526c63b5978" @@ -2006,7 +2002,7 @@ dependencies = [ "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" +"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" @@ -2033,18 +2029,18 @@ dependencies = [ "checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a" +"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" "checksum serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d733da87e79faaac25616e33d26299a41143fd4cd42746cbb0e91d8feea243fd" "checksum serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "45af0182ff64abaeea290235eb67da3825a576c5d53e642c4d5b652e12e6effc" -"checksum serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "11e410fde43e157d789fc290d26bc940778ad0fdd47836426fbac36573710dbb" +"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" "checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" "checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum structopt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ac9d6e93dd792b217bf89cda5c14566e3043960c6f9da890c2ba5d09d07804c" -"checksum structopt-derive 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ae9e5165d463a0dea76967d021f8d0f9316057bf5163aa2a4843790e842ff37" +"checksum structopt 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe8d3289b63ef2f196d89e7701f986583c0895e764b78f052a55b9b5d34d84a" +"checksum structopt-derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f3add731f5b4fb85931d362a3c92deb1ad7113649a8d51701fb257673705f122" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index 0c613198e..5ea1db48b 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -11,5 +11,5 @@ edition = "2018" nom = "5.0" [dev-dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0", features = ["backend-cranelift"] } -wasmer-clif-backend = { path = "../clif-backend", version = "0.6.0" } \ No newline at end of file +wasmer-runtime-core = { path = "../runtime-core", version = "0.7.0", features = ["backend-cranelift"] } +wasmer-clif-backend = { path = "../clif-backend", version = "0.7.0" } \ No newline at end of file From 6ec35c8bdc8d82b7dc4b4a59edda4b4e2986f603 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 18 Sep 2019 17:14:57 +0200 Subject: [PATCH 14/84] chore(interface-types) Bump to 0.7.0. --- Cargo.lock | 2 +- lib/interface-types/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6fd1994d5..46019511e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1638,7 +1638,7 @@ dependencies = [ [[package]] name = "wasmer-interface-types" -version = "0.6.0" +version = "0.7.0" dependencies = [ "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-backend 0.7.0", diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index 5ea1db48b..19ddeb22b 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-interface-types" -version = "0.6.0" +version = "0.7.0" description = "WebAssembly Interface Types library for Wasmer" license = "MIT" authors = ["The Wasmer Engineering Team "] From fc9389d9321317cfe74b6e5ccbdd1709e291134f Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 18 Sep 2019 18:02:05 +0200 Subject: [PATCH 15/84] feat(interface-types) Encode `Interfaces` to WAT. --- lib/interface-types/src/encoders/wat.rs | 160 +++++++++++++++++++++++- lib/interface-types/src/lib.rs | 46 ++++++- 2 files changed, 204 insertions(+), 2 deletions(-) diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 41f73b473..e7e02a3fe 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -1,4 +1,6 @@ -use crate::ast::{Adapter, Export, Forward, ImportedFunction, Instruction, InterfaceType, Type}; +use crate::ast::{ + Adapter, Export, Forward, ImportedFunction, Instruction, InterfaceType, Interfaces, Type, +}; impl From<&InterfaceType> for String { fn from(interface_type: &InterfaceType) -> Self { @@ -186,6 +188,83 @@ impl<'input> From<&Forward<'input>> for String { } } +impl<'input> From<&Interfaces<'input>> for String { + fn from(interfaces: &Interfaces) -> Self { + let mut output = String::from(";; Interfaces"); + + let exports = interfaces + .exports + .iter() + .fold(String::new(), |mut accumulator, export| { + accumulator.push_str(&format!("\n\n;; Interface, Export {}\n", export.name)); + accumulator.push_str(&String::from(export)); + accumulator + }); + + let types = interfaces + .types + .iter() + .fold(String::new(), |mut accumulator, ty| { + accumulator.push_str(&format!("\n\n;; Interface, Ty {}\n", ty.name)); + accumulator.push_str(&String::from(ty)); + accumulator + }); + + let imported_functions = interfaces.imported_functions.iter().fold( + String::new(), + |mut accumulator, imported_function| { + accumulator.push_str(&format!( + "\n\n;; Interface, Imported function {}.{}\n", + imported_function.namespace, imported_function.name + )); + accumulator.push_str(&String::from(imported_function)); + accumulator + }, + ); + + let adapters = + interfaces + .adapters + .iter() + .fold(String::new(), |mut accumulator, adapter| { + match adapter { + Adapter::Import { + namespace, name, .. + } => accumulator.push_str(&format!( + "\n\n;; Interface, Adapter {}.{}\n", + namespace, name + )), + + Adapter::Export { name, .. } => { + accumulator.push_str(&format!("\n\n;; Interface, Adapter {}\n", name)) + } + + _ => unimplemented!(), + } + accumulator.push_str(&String::from(adapter)); + accumulator + }); + + let forwards = + interfaces + .forwards + .iter() + .fold(String::new(), |mut accumulator, forward| { + accumulator.push_str(&format!("\n\n;; Interface, Forward {}\n", forward.name)); + accumulator.push_str(&String::from(forward)); + accumulator + }); + + output.push_str(&exports); + output.push_str(&types); + output.push_str(&imported_functions); + output.push_str(&adapters); + output.push_str(&forwards); + + output + } +} + #[cfg(test)] mod tests { use crate::ast::*; @@ -443,4 +522,83 @@ mod tests { assert_eq!(input, output); } + + #[test] + fn test_interfaces() { + let input: String = (&Interfaces { + exports: vec![ + Export { + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + }, + Export { + name: "bar", + input_types: vec![], + output_types: vec![], + }, + ], + types: vec![], + imported_functions: vec![ + ImportedFunction { + namespace: "ns", + name: "foo", + input_types: vec![], + output_types: vec![InterfaceType::I32], + }, + ImportedFunction { + namespace: "ns", + name: "bar", + input_types: vec![], + output_types: vec![], + }, + ], + adapters: vec![ + Adapter::Import { + namespace: "ns", + name: "foo", + input_types: vec![InterfaceType::I32], + output_types: vec![], + instructions: vec![Instruction::ArgumentGet(42)], + }, + Adapter::Export { + name: "bar", + input_types: vec![], + output_types: vec![], + instructions: vec![Instruction::ArgumentGet(42)], + }, + ], + forwards: vec![Forward { name: "main" }], + }) + .into(); + let output = r#";; Interfaces + +;; Interface, Export foo +(@interface export "foo" + (param i32)) + +;; Interface, Export bar +(@interface export "bar") + +;; Interface, Imported function ns.foo +(@interface func $ns_foo (import "ns" "foo") + (result i32)) + +;; Interface, Imported function ns.bar +(@interface func $ns_bar (import "ns" "bar")) + +;; Interface, Adapter ns.foo +(@interface adapt (import "ns" "foo") + (param i32) + arg.get 42) + +;; Interface, Adapter bar +(@interface adapt (export "bar") + arg.get 42) + +;; Interface, Forward main +(@interface forward (export "main"))"#; + + assert_eq!(input, output); + } } diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index f6c4656db..d9fc90ea6 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -8,7 +8,7 @@ pub use decoders::binary::parse as parse_binary; #[cfg(test)] mod tests { - use crate::{ast::*, parse_binary}; + use crate::{ast::*, encoders::wat::*, parse_binary}; use std::fs; use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core as runtime; @@ -103,6 +103,50 @@ mod tests { forwards: vec![Forward { name: "main" }] } ); + + let wat = String::from(&interfaces); + + assert_eq!( + wat, + r#";; Interfaces + +;; Interface, Export strlen +(@interface export "strlen" + (param i32) + (result i32)) + +;; Interface, Export write_null_byte +(@interface export "write_null_byte" + (param i32 i32) + (result i32)) + +;; Interface, Imported function host.console_log +(@interface func $host_console_log (import "host" "console_log") + (param String)) + +;; Interface, Imported function host.document_title +(@interface func $host_document_title (import "host" "document_title") + (result String)) + +;; Interface, Adapter host.console_log +(@interface adapt (import "host" "console_log") + (param i32) + arg.get 0 + arg.get 0 + call-export "strlen" + read-utf8 + call 0) + +;; Interface, Adapter host.document_title +(@interface adapt (import "host" "document_title") + (result i32) + call 1 + write-utf8 "alloc" + call-export "write_null_byte") + +;; Interface, Forward main +(@interface forward (export "main"))"#, + ); } Err(_) => assert!(false), From dc3c72ea1954ef342cd31a9d9231a0d940ae631b Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 19 Sep 2019 00:18:36 +0200 Subject: [PATCH 16/84] feat(interface-types) Draft instruction interpreter. --- lib/interface-types/src/ast.rs | 25 +----- lib/interface-types/src/decoders/binary.rs | 2 +- lib/interface-types/src/encoders/wat.rs | 7 +- .../src/instructions/interpreter.rs | 83 +++++++++++++++++++ lib/interface-types/src/instructions/mod.rs | 28 +++++++ lib/interface-types/src/instructions/stack.rs | 66 +++++++++++++++ lib/interface-types/src/lib.rs | 3 +- 7 files changed, 185 insertions(+), 29 deletions(-) create mode 100644 lib/interface-types/src/instructions/interpreter.rs create mode 100644 lib/interface-types/src/instructions/mod.rs create mode 100644 lib/interface-types/src/instructions/stack.rs diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 63cb7789f..a3df0d8d8 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -1,3 +1,4 @@ +use crate::instructions::Instruction; use std::str; #[derive(PartialEq, Debug)] @@ -21,30 +22,6 @@ pub(crate) enum AdapterKind { HelperFunction, } -#[derive(PartialEq, Debug)] -pub enum Instruction<'input> { - ArgumentGet(u64), - Call(u64), - CallExport(&'input str), - ReadUtf8, - WriteUtf8(&'input str), - AsWasm(InterfaceType), - AsInterface(InterfaceType), - TableRefAdd, - TableRefGet, - CallMethod(u64), - MakeRecord(InterfaceType), - GetField(InterfaceType, u64), - Const(InterfaceType, u64), - FoldSeq(u64), - Add(InterfaceType), - MemToSeq(InterfaceType, &'input str), - Load(InterfaceType, &'input str), - SeqNew(InterfaceType), - ListPush, - RepeatWhile(u64, u64), -} - #[derive(PartialEq, Debug)] pub struct Export<'input> { pub name: &'input str, diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index b86cdc303..2fd6a7b4b 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -1,4 +1,4 @@ -use crate::ast::*; +use crate::{ast::*, instructions::Instruction}; use nom::{ error::{make_error, ErrorKind, ParseError}, Err, IResult, diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index e7e02a3fe..b11b65dc2 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -1,5 +1,6 @@ -use crate::ast::{ - Adapter, Export, Forward, ImportedFunction, Instruction, InterfaceType, Interfaces, Type, +use crate::{ + ast::{Adapter, Export, Forward, ImportedFunction, InterfaceType, Interfaces, Type}, + instructions::Instruction, }; impl From<&InterfaceType> for String { @@ -267,7 +268,7 @@ impl<'input> From<&Interfaces<'input>> for String { #[cfg(test)] mod tests { - use crate::ast::*; + use crate::{ast::*, instructions::Instruction}; #[test] fn test_interface_types() { diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs new file mode 100644 index 000000000..61c113343 --- /dev/null +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -0,0 +1,83 @@ +use crate::instructions::{stack::Stack, Instruction}; +use std::convert::TryFrom; + +type ExecutableInstruction = dyn Fn(&mut Stack); +//trait ExecutableInstruction: Fn(&mut Stack) {} + +pub(crate) struct Interpreter { + stack: Stack, + instructions: Vec>, +} + +impl Interpreter { + pub(crate) fn is_eos(&self) -> bool { + self.stack.is_empty() + } +} + +impl<'binary_input> TryFrom<&Vec>> for Interpreter { + type Error = &'static str; + + fn try_from(instructions: &Vec) -> Result { + let executable_instructions = instructions + .iter() + .map(|instruction| -> Box { + match instruction { + Instruction::ArgumentGet(index) => { + let index = index.to_owned(); + + Box::new(move |stack: &mut Stack| { + println!("argument get {}", index); + }) + } + Instruction::CallExport(export_name) => { + let export_name = (*export_name).to_owned(); + + Box::new(move |stack: &mut Stack| { + println!("call export {}", export_name); + }) + } + Instruction::ReadUtf8 => Box::new(|stack: &mut Stack| { + println!("read utf8"); + }), + Instruction::Call(index) => { + let index = index.to_owned(); + + Box::new(move |stack: &mut Stack| { + println!("call {}", index); + }) + } + _ => unimplemented!(), + } + }) + .collect::>(); + + Ok(Interpreter { + stack: Stack::new(), + instructions: executable_instructions, + }) + } +} + +#[cfg(test)] +mod tests { + use super::Interpreter; + use crate::instructions::Instruction; + use std::convert::TryInto; + + #[test] + fn test_interpreter_from_instructions() { + let instructions = vec![ + Instruction::ArgumentGet(0), + Instruction::ArgumentGet(0), + Instruction::CallExport("strlen"), + Instruction::ReadUtf8, + Instruction::Call(7), + ]; + let interpreter: Result = (&instructions).try_into(); + assert!(interpreter.is_ok()); + + let interpreter = interpreter.unwrap(); + assert_eq!(interpreter.is_eos(), true); + } +} diff --git a/lib/interface-types/src/instructions/mod.rs b/lib/interface-types/src/instructions/mod.rs new file mode 100644 index 000000000..eb62f43d2 --- /dev/null +++ b/lib/interface-types/src/instructions/mod.rs @@ -0,0 +1,28 @@ +use crate::ast::InterfaceType; + +pub mod interpreter; +mod stack; + +#[derive(PartialEq, Debug)] +pub enum Instruction<'input> { + ArgumentGet(u64), + Call(u64), + CallExport(&'input str), + ReadUtf8, + WriteUtf8(&'input str), + AsWasm(InterfaceType), + AsInterface(InterfaceType), + TableRefAdd, + TableRefGet, + CallMethod(u64), + MakeRecord(InterfaceType), + GetField(InterfaceType, u64), + Const(InterfaceType, u64), + FoldSeq(u64), + Add(InterfaceType), + MemToSeq(InterfaceType, &'input str), + Load(InterfaceType, &'input str), + SeqNew(InterfaceType), + ListPush, + RepeatWhile(u64, u64), +} diff --git a/lib/interface-types/src/instructions/stack.rs b/lib/interface-types/src/instructions/stack.rs new file mode 100644 index 000000000..8a5bce471 --- /dev/null +++ b/lib/interface-types/src/instructions/stack.rs @@ -0,0 +1,66 @@ +use std::{iter::Rev, vec::Drain}; + +pub(super) struct Stack { + inner: Vec, +} + +impl Stack { + pub(super) fn new() -> Self { + Self { inner: Vec::new() } + } + + pub(super) fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + pub(super) fn push(&mut self, item: u32) { + self.inner.push(item); + } + + pub(super) fn pop(&mut self) -> Option { + self.inner.pop() + } + + pub(super) fn pop_n(&mut self, n: usize) -> Rev> { + self.inner.drain(self.inner.len() - n..).rev() + } +} + +#[cfg(test)] +mod tests { + use super::Stack; + + #[test] + fn test_is_empty() { + let mut stack = Stack::new(); + assert_eq!(stack.is_empty(), true); + + stack.push(1); + assert_eq!(stack.is_empty(), false); + } + + #[test] + fn test_push_pop() { + let mut stack = Stack::new(); + stack.push(1); + + assert_eq!(stack.pop(), Some(1)); + assert_eq!(stack.is_empty(), true); + } + + #[test] + fn test_pop_n() { + let mut stack = Stack::new(); + stack.push(1); + stack.push(2); + stack.push(3); + stack.push(4); + stack.push(5); + stack.push(6); + + assert_eq!(stack.pop_n(1).collect::>(), &[6]); + assert_eq!(stack.pop_n(2).collect::>(), &[5, 4]); + assert_eq!(stack.pop_n(3).collect::>(), &[3, 2, 1]); + assert_eq!(stack.is_empty(), true); + } +} diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index d9fc90ea6..9ba79d7b9 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -3,12 +3,13 @@ pub mod ast; mod macros; pub mod decoders; pub mod encoders; +pub mod instructions; pub use decoders::binary::parse as parse_binary; #[cfg(test)] mod tests { - use crate::{ast::*, encoders::wat::*, parse_binary}; + use crate::{ast::*, encoders::wat::*, instructions::Instruction, parse_binary}; use std::fs; use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core as runtime; From 2f3c37fbd5ce093fdd46497c976e3030d5277bcc Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 19 Sep 2019 00:25:28 +0200 Subject: [PATCH 17/84] feat(interface-types) Continue. --- lib/interface-types/src/instructions/interpreter.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 61c113343..ee1b8c48c 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -1,12 +1,11 @@ use crate::instructions::{stack::Stack, Instruction}; use std::convert::TryFrom; -type ExecutableInstruction = dyn Fn(&mut Stack); -//trait ExecutableInstruction: Fn(&mut Stack) {} +type ExecutableInstruction = Box; pub(crate) struct Interpreter { stack: Stack, - instructions: Vec>, + instructions: Vec, } impl Interpreter { @@ -21,7 +20,7 @@ impl<'binary_input> TryFrom<&Vec>> for Interpreter { fn try_from(instructions: &Vec) -> Result { let executable_instructions = instructions .iter() - .map(|instruction| -> Box { + .map(|instruction| -> ExecutableInstruction { match instruction { Instruction::ArgumentGet(index) => { let index = index.to_owned(); @@ -50,7 +49,7 @@ impl<'binary_input> TryFrom<&Vec>> for Interpreter { _ => unimplemented!(), } }) - .collect::>(); + .collect(); Ok(Interpreter { stack: Stack::new(), From c63345029ef17f3f41b698873f2385800fb675d2 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 19 Sep 2019 23:05:17 +0200 Subject: [PATCH 18/84] feat(interface-types) Continue. --- .../src/instructions/interpreter.rs | 67 ++++++++++++++----- lib/interface-types/src/instructions/stack.rs | 62 ++++++++++++----- 2 files changed, 95 insertions(+), 34 deletions(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index ee1b8c48c..11d6ddae6 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -1,16 +1,31 @@ -use crate::instructions::{stack::Stack, Instruction}; +use crate::instructions::{ + stack::{Stack, Stackable}, + Instruction, +}; use std::convert::TryFrom; -type ExecutableInstruction = Box; +type ExecutableInstruction = Box) -> Result<(), &'static str>>; pub(crate) struct Interpreter { - stack: Stack, - instructions: Vec, + executable_instructions: Vec, } impl Interpreter { - pub(crate) fn is_eos(&self) -> bool { - self.stack.is_empty() + fn iter(&self) -> impl Iterator + '_ { + self.executable_instructions.iter() + } + + pub(crate) fn run(&self) -> Result, &'static str> { + let mut stack = Stack::new(); + + for executable_instruction in self.iter() { + match executable_instruction(&mut stack) { + Ok(_) => continue, + Err(message) => return Err(message), + } + } + + Ok(stack) } } @@ -25,25 +40,34 @@ impl<'binary_input> TryFrom<&Vec>> for Interpreter { Instruction::ArgumentGet(index) => { let index = index.to_owned(); - Box::new(move |stack: &mut Stack| { + Box::new(move |stack: &mut Stack| -> Result<(), _> { println!("argument get {}", index); + stack.push(index); + + Ok(()) }) } Instruction::CallExport(export_name) => { let export_name = (*export_name).to_owned(); - Box::new(move |stack: &mut Stack| { + Box::new(move |_stack: &mut Stack| -> Result<(), _> { println!("call export {}", export_name); + + Ok(()) }) } - Instruction::ReadUtf8 => Box::new(|stack: &mut Stack| { + Instruction::ReadUtf8 => Box::new(|_stack: &mut Stack| -> Result<(), _> { println!("read utf8"); + + Ok(()) }), Instruction::Call(index) => { let index = index.to_owned(); - Box::new(move |stack: &mut Stack| { + Box::new(move |_stack: &mut Stack| -> Result<(), _> { println!("call {}", index); + + Ok(()) }) } _ => unimplemented!(), @@ -52,8 +76,7 @@ impl<'binary_input> TryFrom<&Vec>> for Interpreter { .collect(); Ok(Interpreter { - stack: Stack::new(), - instructions: executable_instructions, + executable_instructions, }) } } @@ -61,7 +84,7 @@ impl<'binary_input> TryFrom<&Vec>> for Interpreter { #[cfg(test)] mod tests { use super::Interpreter; - use crate::instructions::Instruction; + use crate::instructions::{stack::Stackable, Instruction}; use std::convert::TryInto; #[test] @@ -73,10 +96,20 @@ mod tests { Instruction::ReadUtf8, Instruction::Call(7), ]; - let interpreter: Result = (&instructions).try_into(); - assert!(interpreter.is_ok()); + let interpreter: Interpreter = (&instructions).try_into().unwrap(); - let interpreter = interpreter.unwrap(); - assert_eq!(interpreter.is_eos(), true); + assert_eq!(interpreter.executable_instructions.len(), 5); + } + + #[test] + fn test_interpreter_argument_get() { + let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(42)]).try_into().unwrap(); + let run = interpreter.run(); + + assert!(run.is_ok()); + + let stack = run.unwrap(); + + assert_eq!(stack.as_slice(), &[42]); } } diff --git a/lib/interface-types/src/instructions/stack.rs b/lib/interface-types/src/instructions/stack.rs index 8a5bce471..ff68d9380 100644 --- a/lib/interface-types/src/instructions/stack.rs +++ b/lib/interface-types/src/instructions/stack.rs @@ -1,34 +1,62 @@ -use std::{iter::Rev, vec::Drain}; +pub(crate) trait Stackable { + type Item; -pub(super) struct Stack { - inner: Vec, + fn is_empty(&self) -> bool; + fn as_slice(&self) -> &[Self::Item]; + fn push(&mut self, item: Self::Item); + fn pop1(&mut self) -> Option; + fn pop(&mut self, n: usize) -> Option>; } -impl Stack { - pub(super) fn new() -> Self { +pub(crate) struct Stack { + inner: Vec, +} + +impl Stack { + pub fn new() -> Self { Self { inner: Vec::new() } } +} - pub(super) fn is_empty(&self) -> bool { +impl Stackable for Stack { + type Item = T; + + fn is_empty(&self) -> bool { self.inner.is_empty() } - pub(super) fn push(&mut self, item: u32) { + fn as_slice(&self) -> &[Self::Item] { + self.inner.as_slice() + } + + fn push(&mut self, item: Self::Item) { self.inner.push(item); } - pub(super) fn pop(&mut self) -> Option { + fn pop1(&mut self) -> Option { self.inner.pop() } - pub(super) fn pop_n(&mut self, n: usize) -> Rev> { - self.inner.drain(self.inner.len() - n..).rev() + fn pop(&mut self, n: usize) -> Option> { + if self.inner.len() < n { + None + } else { + let items = self + .inner + .drain(self.inner.len() - n..) + .rev() + .collect::>(); + + assert!(items.len() == n); + + Some(items) + } } } #[cfg(test)] mod tests { - use super::Stack; + use super::{Stack, Stackable}; #[test] fn test_is_empty() { @@ -40,16 +68,16 @@ mod tests { } #[test] - fn test_push_pop() { + fn test_push_pop1() { let mut stack = Stack::new(); stack.push(1); - assert_eq!(stack.pop(), Some(1)); + assert_eq!(stack.pop1(), Some(1)); assert_eq!(stack.is_empty(), true); } #[test] - fn test_pop_n() { + fn test_pop() { let mut stack = Stack::new(); stack.push(1); stack.push(2); @@ -58,9 +86,9 @@ mod tests { stack.push(5); stack.push(6); - assert_eq!(stack.pop_n(1).collect::>(), &[6]); - assert_eq!(stack.pop_n(2).collect::>(), &[5, 4]); - assert_eq!(stack.pop_n(3).collect::>(), &[3, 2, 1]); + assert_eq!(stack.pop(1), Some(vec![6])); + assert_eq!(stack.pop(2), Some(vec![5, 4])); + assert_eq!(stack.pop(3), Some(vec![3, 2, 1])); assert_eq!(stack.is_empty(), true); } } From 62e1f7867ba1329350ed6652bf2c09c9c65b2642 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 20 Sep 2019 00:06:15 +0200 Subject: [PATCH 19/84] feat(interface-types) Add an abstract Wasm instance and a runtime to the interpreter. --- .../src/instructions/interpreter.rs | 225 +++++++++++++----- lib/interface-types/src/instructions/mod.rs | 1 + lib/interface-types/src/instructions/stack.rs | 1 + 3 files changed, 173 insertions(+), 54 deletions(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 11d6ddae6..2859f8069 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -1,78 +1,117 @@ use crate::instructions::{ stack::{Stack, Stackable}, - Instruction, + wasm, Instruction, }; use std::convert::TryFrom; -type ExecutableInstruction = Box) -> Result<(), &'static str>>; - -pub(crate) struct Interpreter { - executable_instructions: Vec, +struct Runtime<'invocation, 'instance, Instance> +where + Instance: wasm::Instance, +{ + invocation_inputs: &'invocation Vec, + stack: Stack, + wasm_instance: &'instance Instance, } -impl Interpreter { - fn iter(&self) -> impl Iterator + '_ { +pub(crate) struct Interpreter +where + Instance: wasm::Instance, +{ + executable_instructions: Vec) -> Result<(), String>>>, +} + +impl Interpreter +where + Instance: wasm::Instance, +{ + fn iter( + &self, + ) -> impl Iterator) -> Result<(), String>>> + '_ { self.executable_instructions.iter() } - pub(crate) fn run(&self) -> Result, &'static str> { - let mut stack = Stack::new(); + pub(crate) fn run( + &self, + invocation_inputs: &Vec, + wasm_instance: &Instance, + ) -> Result, String> { + let mut runtime = Runtime { + invocation_inputs, + stack: Stack::new(), + wasm_instance, + }; for executable_instruction in self.iter() { - match executable_instruction(&mut stack) { + match executable_instruction(&mut runtime) { Ok(_) => continue, Err(message) => return Err(message), } } - Ok(stack) + Ok(runtime.stack) } } -impl<'binary_input> TryFrom<&Vec>> for Interpreter { - type Error = &'static str; +impl<'binary_input, Instance> TryFrom<&Vec>> for Interpreter +where + Instance: wasm::Instance, +{ + type Error = String; fn try_from(instructions: &Vec) -> Result { let executable_instructions = instructions .iter() - .map(|instruction| -> ExecutableInstruction { - match instruction { - Instruction::ArgumentGet(index) => { - let index = index.to_owned(); + .map( + |instruction| -> Box) -> Result<(), String>> { + match instruction { + Instruction::ArgumentGet(index) => { + let index = index.to_owned(); + let instruction_name: String = instruction.into(); - Box::new(move |stack: &mut Stack| -> Result<(), _> { - println!("argument get {}", index); - stack.push(index); + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + let invocation_inputs = runtime.invocation_inputs; - Ok(()) - }) + if index >= (invocation_inputs.len() as u64) { + return Err(format!( + "`{}` cannot access argument #{} because it does't exist.", + instruction_name, index + )); + } + + runtime.stack.push(invocation_inputs[index as usize]); + + Ok(()) + }) + } + Instruction::CallExport(export_name) => { + let export_name = (*export_name).to_owned(); + + Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { + println!("call export {}", export_name); + + Ok(()) + }) + } + Instruction::ReadUtf8 => { + Box::new(|_runtime: &mut Runtime| -> Result<(), _> { + println!("read utf8"); + + Ok(()) + }) + } + Instruction::Call(index) => { + let index = index.to_owned(); + + Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { + println!("call {}", index); + + Ok(()) + }) + } + _ => unimplemented!(), } - Instruction::CallExport(export_name) => { - let export_name = (*export_name).to_owned(); - - Box::new(move |_stack: &mut Stack| -> Result<(), _> { - println!("call export {}", export_name); - - Ok(()) - }) - } - Instruction::ReadUtf8 => Box::new(|_stack: &mut Stack| -> Result<(), _> { - println!("read utf8"); - - Ok(()) - }), - Instruction::Call(index) => { - let index = index.to_owned(); - - Box::new(move |_stack: &mut Stack| -> Result<(), _> { - println!("call {}", index); - - Ok(()) - }) - } - _ => unimplemented!(), - } - }) + }, + ) .collect(); Ok(Interpreter { @@ -84,27 +123,53 @@ impl<'binary_input> TryFrom<&Vec>> for Interpreter { #[cfg(test)] mod tests { use super::Interpreter; - use crate::instructions::{stack::Stackable, Instruction}; - use std::convert::TryInto; + use crate::instructions::{stack::Stackable, wasm, Instruction}; + use std::{collections::HashMap, convert::TryInto}; + + struct Instance { + exports: HashMap, + } + + impl Instance { + fn new() -> Self { + Self { + exports: { + let mut hashmap = HashMap::new(); + hashmap.insert("foo".into(), ()); + + hashmap + }, + } + } + } + + impl wasm::Instance for Instance { + fn export_exists(&self, export_name: &str) -> bool { + self.exports.contains_key(export_name) + } + } #[test] fn test_interpreter_from_instructions() { let instructions = vec![ Instruction::ArgumentGet(0), Instruction::ArgumentGet(0), - Instruction::CallExport("strlen"), + Instruction::CallExport("foo"), Instruction::ReadUtf8, Instruction::Call(7), ]; - let interpreter: Interpreter = (&instructions).try_into().unwrap(); + let interpreter: Interpreter<()> = (&instructions).try_into().unwrap(); assert_eq!(interpreter.executable_instructions.len(), 5); } #[test] fn test_interpreter_argument_get() { - let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(42)]).try_into().unwrap(); - let run = interpreter.run(); + let interpreter: Interpreter = + (&vec![Instruction::ArgumentGet(0)]).try_into().unwrap(); + let invocation_inputs = vec![42]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); assert!(run.is_ok()); @@ -112,4 +177,56 @@ mod tests { assert_eq!(stack.as_slice(), &[42]); } + + #[test] + fn test_interpreter_argument_get_invalid_index() { + let interpreter: Interpreter = + (&vec![Instruction::ArgumentGet(1)]).try_into().unwrap(); + let invocation_inputs = vec![42]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from("`arg.get 1` cannot access argument #1 because it does't exist.") + ); + } + + #[test] + fn test_interpreter_argument_get_argument_get() { + let interpreter: Interpreter = + (&vec![Instruction::ArgumentGet(0), Instruction::ArgumentGet(1)]) + .try_into() + .unwrap(); + let invocation_inputs = vec![7, 42]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_ok()); + + let stack = run.unwrap(); + + assert_eq!(stack.as_slice(), &[7, 42]); + } + + /* + #[test] + fn test_interpreter_call_export() { + let interpreter: Interpreter = + (&vec![Instruction::ArgumentGet(7), Instruction::ArgumentGet(42)]) + .try_into() + .unwrap(); + let run = interpreter.run(&Instance::new()); + + assert!(run.is_ok()); + + let stack = run.unwrap(); + + assert_eq!(stack.as_slice(), &[]); + } + */ } diff --git a/lib/interface-types/src/instructions/mod.rs b/lib/interface-types/src/instructions/mod.rs index eb62f43d2..43e536c4b 100644 --- a/lib/interface-types/src/instructions/mod.rs +++ b/lib/interface-types/src/instructions/mod.rs @@ -2,6 +2,7 @@ use crate::ast::InterfaceType; pub mod interpreter; mod stack; +pub mod wasm; #[derive(PartialEq, Debug)] pub enum Instruction<'input> { diff --git a/lib/interface-types/src/instructions/stack.rs b/lib/interface-types/src/instructions/stack.rs index ff68d9380..c9b42d6ad 100644 --- a/lib/interface-types/src/instructions/stack.rs +++ b/lib/interface-types/src/instructions/stack.rs @@ -8,6 +8,7 @@ pub(crate) trait Stackable { fn pop(&mut self, n: usize) -> Option>; } +#[derive(Debug)] pub(crate) struct Stack { inner: Vec, } From 9d4c983206a70f9e3a2d619403095b2c6f596fd0 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 20 Sep 2019 11:37:38 +0200 Subject: [PATCH 20/84] feat(interface-types) Implement `CallExport` executable instruction. It requires to create the `wasm::Export` trait, plus the `wasm::Type` and the `wasm::Value` enums. --- .../src/instructions/interpreter.rs | 189 ++++++++++++++---- lib/interface-types/src/instructions/stack.rs | 4 +- lib/interface-types/src/instructions/wasm.rs | 91 +++++++++ 3 files changed, 244 insertions(+), 40 deletions(-) create mode 100644 lib/interface-types/src/instructions/wasm.rs diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 2859f8069..7da3800f4 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -2,35 +2,43 @@ use crate::instructions::{ stack::{Stack, Stackable}, wasm, Instruction, }; -use std::convert::TryFrom; +use std::{ + convert::{TryFrom, TryInto}, + marker::PhantomData, +}; -struct Runtime<'invocation, 'instance, Instance> +struct Runtime<'invocation, 'instance, Instance, Export> where - Instance: wasm::Instance, + Export: wasm::Export + 'instance, + Instance: wasm::Instance + 'instance, { invocation_inputs: &'invocation Vec, stack: Stack, wasm_instance: &'instance Instance, + wasm_exports: PhantomData, } -pub(crate) struct Interpreter +pub struct Interpreter where - Instance: wasm::Instance, + Export: wasm::Export, + Instance: wasm::Instance, { - executable_instructions: Vec) -> Result<(), String>>>, + executable_instructions: Vec) -> Result<(), String>>>, } -impl Interpreter +impl Interpreter where - Instance: wasm::Instance, + Export: wasm::Export, + Instance: wasm::Instance, { fn iter( &self, - ) -> impl Iterator) -> Result<(), String>>> + '_ { + ) -> impl Iterator) -> Result<(), String>>> + '_ + { self.executable_instructions.iter() } - pub(crate) fn run( + pub fn run( &self, invocation_inputs: &Vec, wasm_instance: &Instance, @@ -39,6 +47,7 @@ where invocation_inputs, stack: Stack::new(), wasm_instance, + wasm_exports: PhantomData, }; for executable_instruction in self.iter() { @@ -52,9 +61,11 @@ where } } -impl<'binary_input, Instance> TryFrom<&Vec>> for Interpreter +impl<'binary_input, Instance, Export> TryFrom<&Vec>> + for Interpreter where - Instance: wasm::Instance, + Export: wasm::Export, + Instance: wasm::Instance, { type Error = String; @@ -62,18 +73,18 @@ where let executable_instructions = instructions .iter() .map( - |instruction| -> Box) -> Result<(), String>> { + |instruction| -> Box) -> Result<(), String>> { match instruction { Instruction::ArgumentGet(index) => { let index = index.to_owned(); let instruction_name: String = instruction.into(); - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { let invocation_inputs = runtime.invocation_inputs; if index >= (invocation_inputs.len() as u64) { return Err(format!( - "`{}` cannot access argument #{} because it does't exist.", + "`{}` cannot access argument #{} because it doesn't exist.", instruction_name, index )); } @@ -85,15 +96,51 @@ where } Instruction::CallExport(export_name) => { let export_name = (*export_name).to_owned(); + let instruction_name: String = instruction.into(); - Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { - println!("call export {}", export_name); + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + let instance = runtime.wasm_instance; - Ok(()) + match instance.export(&export_name) { + Some(export) => { + let inputs_cardinality = export.inputs_cardinality(); + + match runtime.stack.pop(inputs_cardinality) { + Some(inputs) => { + let inputs: Vec = inputs.iter().map(|i| wasm::Value::I32(*i as i32)).collect(); + + match export.call(&inputs) { + Ok(outputs) => { + for output in outputs.iter() { + let output: i32 = output.try_into().unwrap(); + + runtime.stack.push(output as u64); + } + + Ok(()) + }, + Err(_) => Err("failed".into()), + } + } + None => Err(format!( + "`{}` cannot call the exported function `{}` because there is no enought data in the stack for the arguments (need {}).", + instruction_name, + export_name, + inputs_cardinality, + )) + } + }, + + None => Err(format!( + "`{}` cannot call the exported function `{}` because it doesn't exist.", + instruction_name, + export_name, + )) + } }) } Instruction::ReadUtf8 => { - Box::new(|_runtime: &mut Runtime| -> Result<(), _> { + Box::new(|_runtime: &mut Runtime| -> Result<(), _> { println!("read utf8"); Ok(()) @@ -102,7 +149,7 @@ where Instruction::Call(index) => { let index = index.to_owned(); - Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { + Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { println!("call {}", index); Ok(()) @@ -126,8 +173,36 @@ mod tests { use crate::instructions::{stack::Stackable, wasm, Instruction}; use std::{collections::HashMap, convert::TryInto}; + struct Export { + inputs: Vec, + outputs: Vec, + function: fn(arguments: &[wasm::Value]) -> Result, ()>, + } + + impl wasm::Export for Export { + fn inputs_cardinality(&self) -> usize { + self.inputs.len() as usize + } + + fn outputs_cardinality(&self) -> usize { + self.outputs.len() + } + + fn inputs(&self) -> &[wasm::Type] { + &self.inputs + } + + fn outputs(&self) -> &[wasm::Type] { + &self.outputs + } + + fn call(&self, arguments: &[wasm::Value]) -> Result, ()> { + (self.function)(arguments) + } + } + struct Instance { - exports: HashMap, + exports: HashMap, } impl Instance { @@ -135,7 +210,19 @@ mod tests { Self { exports: { let mut hashmap = HashMap::new(); - hashmap.insert("foo".into(), ()); + hashmap.insert( + "sum".into(), + Export { + inputs: vec![wasm::Type::I32, wasm::Type::I32], + outputs: vec![wasm::Type::I32], + function: |arguments: &[wasm::Value]| { + let a: i32 = (&arguments[0]).try_into().unwrap(); + let b: i32 = (&arguments[1]).try_into().unwrap(); + + Ok(vec![wasm::Value::I32(a + b)]) + }, + }, + ); hashmap }, @@ -143,9 +230,9 @@ mod tests { } } - impl wasm::Instance for Instance { - fn export_exists(&self, export_name: &str) -> bool { - self.exports.contains_key(export_name) + impl wasm::Instance for Instance { + fn export(&self, export_name: &str) -> Option<&Export> { + self.exports.get(export_name) } } @@ -158,15 +245,16 @@ mod tests { Instruction::ReadUtf8, Instruction::Call(7), ]; - let interpreter: Interpreter<()> = (&instructions).try_into().unwrap(); + let interpreter: Interpreter<(), ()> = (&instructions).try_into().unwrap(); assert_eq!(interpreter.executable_instructions.len(), 5); } #[test] fn test_interpreter_argument_get() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(0)]).try_into().unwrap(); + let invocation_inputs = vec![42]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -180,8 +268,9 @@ mod tests { #[test] fn test_interpreter_argument_get_invalid_index() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(1)]).try_into().unwrap(); + let invocation_inputs = vec![42]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -192,16 +281,17 @@ mod tests { assert_eq!( error, - String::from("`arg.get 1` cannot access argument #1 because it does't exist.") + String::from("`arg.get 1` cannot access argument #1 because it doesn't exist.") ); } #[test] fn test_interpreter_argument_get_argument_get() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(0), Instruction::ArgumentGet(1)]) .try_into() .unwrap(); + let invocation_inputs = vec![7, 42]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -213,20 +303,43 @@ mod tests { assert_eq!(stack.as_slice(), &[7, 42]); } - /* #[test] fn test_interpreter_call_export() { - let interpreter: Interpreter = - (&vec![Instruction::ArgumentGet(7), Instruction::ArgumentGet(42)]) - .try_into() - .unwrap(); - let run = interpreter.run(&Instance::new()); + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![3, 4]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); assert!(run.is_ok()); let stack = run.unwrap(); - assert_eq!(stack.as_slice(), &[]); + assert_eq!(stack.as_slice(), &[7]); + } + + #[test] + fn test_interpreter_call_export_invalid_export_name() { + let interpreter: Interpreter = + (&vec![Instruction::CallExport("bar")]).try_into().unwrap(); + + let invocation_inputs = vec![]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from(r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#) + ); } - */ } diff --git a/lib/interface-types/src/instructions/stack.rs b/lib/interface-types/src/instructions/stack.rs index c9b42d6ad..1d139a77c 100644 --- a/lib/interface-types/src/instructions/stack.rs +++ b/lib/interface-types/src/instructions/stack.rs @@ -1,4 +1,4 @@ -pub(crate) trait Stackable { +pub trait Stackable { type Item; fn is_empty(&self) -> bool; @@ -9,7 +9,7 @@ pub(crate) trait Stackable { } #[derive(Debug)] -pub(crate) struct Stack { +pub struct Stack { inner: Vec, } diff --git a/lib/interface-types/src/instructions/wasm.rs b/lib/interface-types/src/instructions/wasm.rs new file mode 100644 index 000000000..382fe17bc --- /dev/null +++ b/lib/interface-types/src/instructions/wasm.rs @@ -0,0 +1,91 @@ +use std::convert::TryFrom; + +pub enum Type { + I32, + I64, + F32, + F64, + V128, +} + +#[derive(Debug)] +pub enum Value { + I32(i32), + I64(i64), + F32(f32), + F64(f64), + V128(u128), +} + +macro_rules! from_x_for_value { + ($native_type:ty, $value_variant:ident) => { + impl From<$native_type> for Value { + fn from(n: $native_type) -> Self { + Self::$value_variant(n) + } + } + + impl TryFrom<&Value> for $native_type { + type Error = &'static str; + + fn try_from(w: &Value) -> Result { + match *w { + Value::$value_variant(n) => Ok(n), + _ => Err("Invalid cast."), + } + } + } + }; +} + +from_x_for_value!(i32, I32); +from_x_for_value!(i64, I64); +from_x_for_value!(f32, F32); +from_x_for_value!(f64, F64); +from_x_for_value!(u128, V128); + +pub trait Export { + fn inputs_cardinality(&self) -> usize; + fn outputs_cardinality(&self) -> usize; + fn inputs(&self) -> &[Type]; + fn outputs(&self) -> &[Type]; + fn call(&self, arguments: &[Value]) -> Result, ()>; +} + +pub trait Instance +where + E: Export, +{ + fn export(&self, export_name: &str) -> Option<&E>; +} + +impl Export for () { + fn inputs_cardinality(&self) -> usize { + 0 + } + + fn outputs_cardinality(&self) -> usize { + 0 + } + + fn inputs(&self) -> &[Type] { + &[] + } + + fn outputs(&self) -> &[Type] { + &[] + } + + fn call(&self, _arguments: &[Value]) -> Result, ()> { + Err(()) + } +} + +impl Instance for () +where + E: Export, +{ + fn export(&self, _export_name: &str) -> Option<&E> { + None + } +} From 39a817817b67f3c0354a46bb9ae3c40ed4c31485 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 20 Sep 2019 11:55:19 +0200 Subject: [PATCH 21/84] feat(interface-types) `Stack` supports `Default`. --- lib/interface-types/src/instructions/stack.rs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/interface-types/src/instructions/stack.rs b/lib/interface-types/src/instructions/stack.rs index 1d139a77c..d62d40d7c 100644 --- a/lib/interface-types/src/instructions/stack.rs +++ b/lib/interface-types/src/instructions/stack.rs @@ -8,18 +8,29 @@ pub trait Stackable { fn pop(&mut self, n: usize) -> Option>; } -#[derive(Debug)] -pub struct Stack { +#[derive(Debug, Default)] +pub struct Stack +where + T: Default, +{ inner: Vec, } -impl Stack { +impl Stack +where + T: Default, +{ pub fn new() -> Self { - Self { inner: Vec::new() } + Self { + ..Default::default() + } } } -impl Stackable for Stack { +impl Stackable for Stack +where + T: Default, +{ type Item = T; fn is_empty(&self) -> bool { From 2237e628ab5686174f6fdc57d0a01f10995ac338 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 20 Sep 2019 12:02:11 +0200 Subject: [PATCH 22/84] chore(interface-types) Fix clippy warnings. --- lib/interface-types/src/decoders/binary.rs | 5 +++-- lib/interface-types/src/encoders/wat.rs | 4 ++-- .../src/instructions/interpreter.rs | 16 ++++++++-------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 2fd6a7b4b..a4fc146e3 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -52,7 +52,7 @@ fn leb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'in } let (output, bytes) = match input.iter().position(|&byte| byte & 0x80 == 0) { - Some(position) => (&input[position + 1..], &input[..position + 1]), + Some(position) => (&input[position + 1..], &input[..=position]), None => (&[] as &[u8], input), }; @@ -61,7 +61,7 @@ fn leb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'in bytes .iter() .rev() - .fold(0, |acc, byte| (acc << 7) | (byte & 0x7f) as u64), + .fold(0, |acc, byte| (acc << 7) | u64::from(byte & 0x7f)), )) } @@ -84,6 +84,7 @@ fn string<'input, E: ParseError<&'input [u8]>>( })) } +#[allow(clippy::type_complexity)] fn list<'input, I, E: ParseError<&'input [u8]>>( input: &'input [u8], item_parser: fn(&'input [u8]) -> IResult<&'input [u8], I, E>, diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index b11b65dc2..f04712a5c 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -67,7 +67,7 @@ impl<'input> From<&Instruction<'input>> for String { } } -fn input_types_to_param(input_types: &Vec) -> String { +fn input_types_to_param(input_types: &[InterfaceType]) -> String { if input_types.is_empty() { "".into() } else { @@ -84,7 +84,7 @@ fn input_types_to_param(input_types: &Vec) -> String { } } -fn output_types_to_result(output_types: &Vec) -> String { +fn output_types_to_result(output_types: &[InterfaceType]) -> String { if output_types.is_empty() { "".into() } else { diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 7da3800f4..5e95cbae6 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -7,12 +7,15 @@ use std::{ marker::PhantomData, }; +type ExecutableInstruction = + Box) -> Result<(), String>>; + struct Runtime<'invocation, 'instance, Instance, Export> where Export: wasm::Export + 'instance, Instance: wasm::Instance + 'instance, { - invocation_inputs: &'invocation Vec, + invocation_inputs: &'invocation [u64], stack: Stack, wasm_instance: &'instance Instance, wasm_exports: PhantomData, @@ -23,7 +26,7 @@ where Export: wasm::Export, Instance: wasm::Instance, { - executable_instructions: Vec) -> Result<(), String>>>, + executable_instructions: Vec>, } impl Interpreter @@ -31,16 +34,13 @@ where Export: wasm::Export, Instance: wasm::Instance, { - fn iter( - &self, - ) -> impl Iterator) -> Result<(), String>>> + '_ - { + fn iter(&self) -> impl Iterator> + '_ { self.executable_instructions.iter() } pub fn run( &self, - invocation_inputs: &Vec, + invocation_inputs: &[u64], wasm_instance: &Instance, ) -> Result, String> { let mut runtime = Runtime { @@ -73,7 +73,7 @@ where let executable_instructions = instructions .iter() .map( - |instruction| -> Box) -> Result<(), String>> { + |instruction| -> ExecutableInstruction { match instruction { Instruction::ArgumentGet(index) => { let index = index.to_owned(); From b7b37d2e9952769b15b2a71d85980184532ce5fb Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 20 Sep 2019 14:07:56 +0200 Subject: [PATCH 23/84] feat(interface-types) The interpreter stack contains Wasm values. --- .../src/instructions/interpreter.rs | 71 +++++++++---------- lib/interface-types/src/instructions/wasm.rs | 8 ++- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 5e95cbae6..ce86bdf59 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -1,26 +1,24 @@ use crate::instructions::{ stack::{Stack, Stackable}, - wasm, Instruction, + wasm::{self, Value}, + Instruction, }; -use std::{ - convert::{TryFrom, TryInto}, - marker::PhantomData, -}; - -type ExecutableInstruction = - Box) -> Result<(), String>>; +use std::{convert::TryFrom, marker::PhantomData}; struct Runtime<'invocation, 'instance, Instance, Export> where Export: wasm::Export + 'instance, Instance: wasm::Instance + 'instance, { - invocation_inputs: &'invocation [u64], - stack: Stack, + invocation_inputs: &'invocation [Value], + stack: Stack, wasm_instance: &'instance Instance, wasm_exports: PhantomData, } +type ExecutableInstruction = + Box) -> Result<(), String>>; + pub struct Interpreter where Export: wasm::Export, @@ -40,9 +38,9 @@ where pub fn run( &self, - invocation_inputs: &[u64], + invocation_inputs: &[Value], wasm_instance: &Instance, - ) -> Result, String> { + ) -> Result, String> { let mut runtime = Runtime { invocation_inputs, stack: Stack::new(), @@ -107,14 +105,10 @@ where match runtime.stack.pop(inputs_cardinality) { Some(inputs) => { - let inputs: Vec = inputs.iter().map(|i| wasm::Value::I32(*i as i32)).collect(); - match export.call(&inputs) { Ok(outputs) => { for output in outputs.iter() { - let output: i32 = output.try_into().unwrap(); - - runtime.stack.push(output as u64); + runtime.stack.push(*output); } Ok(()) @@ -129,8 +123,7 @@ where inputs_cardinality, )) } - }, - + } None => Err(format!( "`{}` cannot call the exported function `{}` because it doesn't exist.", instruction_name, @@ -170,13 +163,17 @@ where #[cfg(test)] mod tests { use super::Interpreter; - use crate::instructions::{stack::Stackable, wasm, Instruction}; + use crate::instructions::{ + stack::Stackable, + wasm::{self, Type, Value}, + Instruction, + }; use std::{collections::HashMap, convert::TryInto}; struct Export { - inputs: Vec, - outputs: Vec, - function: fn(arguments: &[wasm::Value]) -> Result, ()>, + inputs: Vec, + outputs: Vec, + function: fn(arguments: &[Value]) -> Result, ()>, } impl wasm::Export for Export { @@ -188,15 +185,15 @@ mod tests { self.outputs.len() } - fn inputs(&self) -> &[wasm::Type] { + fn inputs(&self) -> &[Type] { &self.inputs } - fn outputs(&self) -> &[wasm::Type] { + fn outputs(&self) -> &[Type] { &self.outputs } - fn call(&self, arguments: &[wasm::Value]) -> Result, ()> { + fn call(&self, arguments: &[Value]) -> Result, ()> { (self.function)(arguments) } } @@ -213,13 +210,13 @@ mod tests { hashmap.insert( "sum".into(), Export { - inputs: vec![wasm::Type::I32, wasm::Type::I32], - outputs: vec![wasm::Type::I32], - function: |arguments: &[wasm::Value]| { + inputs: vec![Type::I32, Type::I32], + outputs: vec![Type::I32], + function: |arguments: &[Value]| { let a: i32 = (&arguments[0]).try_into().unwrap(); let b: i32 = (&arguments[1]).try_into().unwrap(); - Ok(vec![wasm::Value::I32(a + b)]) + Ok(vec![Value::I32(a + b)]) }, }, ); @@ -255,7 +252,7 @@ mod tests { let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(0)]).try_into().unwrap(); - let invocation_inputs = vec![42]; + let invocation_inputs = vec![Value::I32(42)]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -263,7 +260,7 @@ mod tests { let stack = run.unwrap(); - assert_eq!(stack.as_slice(), &[42]); + assert_eq!(stack.as_slice(), &[Value::I32(42)]); } #[test] @@ -271,7 +268,7 @@ mod tests { let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(1)]).try_into().unwrap(); - let invocation_inputs = vec![42]; + let invocation_inputs = vec![Value::I32(42)]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -292,7 +289,7 @@ mod tests { .try_into() .unwrap(); - let invocation_inputs = vec![7, 42]; + let invocation_inputs = vec![Value::I32(7), Value::I32(42)]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -300,7 +297,7 @@ mod tests { let stack = run.unwrap(); - assert_eq!(stack.as_slice(), &[7, 42]); + assert_eq!(stack.as_slice(), &[Value::I32(7), Value::I32(42)]); } #[test] @@ -313,7 +310,7 @@ mod tests { .try_into() .unwrap(); - let invocation_inputs = vec![3, 4]; + let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -321,7 +318,7 @@ mod tests { let stack = run.unwrap(); - assert_eq!(stack.as_slice(), &[7]); + assert_eq!(stack.as_slice(), &[Value::I32(7)]); } #[test] diff --git a/lib/interface-types/src/instructions/wasm.rs b/lib/interface-types/src/instructions/wasm.rs index 382fe17bc..ab5f0e6f8 100644 --- a/lib/interface-types/src/instructions/wasm.rs +++ b/lib/interface-types/src/instructions/wasm.rs @@ -8,7 +8,7 @@ pub enum Type { V128, } -#[derive(Debug)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum Value { I32(i32), I64(i64), @@ -17,6 +17,12 @@ pub enum Value { V128(u128), } +impl Default for Value { + fn default() -> Self { + Self::I32(0) + } +} + macro_rules! from_x_for_value { ($native_type:ty, $value_variant:ident) => { impl From<$native_type> for Value { From 56afb4da6391a6c92965b7873e951c4e95d257d0 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 20 Sep 2019 14:31:15 +0200 Subject: [PATCH 24/84] feat(interface-types) Check signature of the exported function to call. --- .../src/instructions/interpreter.rs | 67 ++++++++++++++++++- lib/interface-types/src/instructions/wasm.rs | 13 ++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index ce86bdf59..fb073765f 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -1,6 +1,6 @@ use crate::instructions::{ stack::{Stack, Stackable}, - wasm::{self, Value}, + wasm::{self, Type, Value}, Instruction, }; use std::{convert::TryFrom, marker::PhantomData}; @@ -105,6 +105,20 @@ where match runtime.stack.pop(inputs_cardinality) { Some(inputs) => { + let input_types = inputs + .iter() + .map(|input| input.into()) + .collect::>(); + + if input_types != export.inputs() { + return Err(format!( + "`{}` cannot call the exported function `{}` because the value types in the stack mismatch the function signature (expects {:?}).", + instruction_name, + export_name, + export.inputs(), + )) + } + match export.call(&inputs) { Ok(outputs) => { for output in outputs.iter() { @@ -117,7 +131,7 @@ where } } None => Err(format!( - "`{}` cannot call the exported function `{}` because there is no enought data in the stack for the arguments (need {}).", + "`{}` cannot call the exported function `{}` because there is no enought data in the stack for the arguments (needs {}).", instruction_name, export_name, inputs_cardinality, @@ -339,4 +353,53 @@ mod tests { String::from(r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#) ); } + + #[test] + fn test_interpreter_call_export_too_small_stack() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + // ^^^ `sum` expects 2 values in the stack, only one is present + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from(r#"`call-export "sum"` cannot call the exported function `sum` because there is no enought data in the stack for the arguments (needs 2)."#) + ); + } + + #[test] + fn test_interpreter_call_export_invalid_types_in_the_stack() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![Value::I32(3), Value::I64(4)]; + // ^^^ mismatch with `sum` signature + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from(r#"`call-export "sum"` cannot call the exported function `sum` because the value types in the stack mismatch the function signature (expects [I32, I32])."#) + ); + } } diff --git a/lib/interface-types/src/instructions/wasm.rs b/lib/interface-types/src/instructions/wasm.rs index ab5f0e6f8..8857b6db5 100644 --- a/lib/interface-types/src/instructions/wasm.rs +++ b/lib/interface-types/src/instructions/wasm.rs @@ -1,5 +1,6 @@ use std::convert::TryFrom; +#[derive(Debug, PartialEq)] pub enum Type { I32, I64, @@ -17,6 +18,18 @@ pub enum Value { V128(u128), } +impl From<&Value> for Type { + fn from(value: &Value) -> Self { + match value { + Value::I32(_) => Type::I32, + Value::I64(_) => Type::I64, + Value::F32(_) => Type::F32, + Value::F64(_) => Type::F64, + Value::V128(_) => Type::V128, + } + } +} + impl Default for Value { fn default() -> Self { Self::I32(0) From 8557e83ce6b8799d0ee1c4ca83491acee90f85ba Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 20 Sep 2019 14:55:37 +0200 Subject: [PATCH 25/84] feat(interface-types) Better error message when calling an exported function failed. --- .../src/instructions/interpreter.rs | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index fb073765f..6fb25240a 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -126,8 +126,12 @@ where } Ok(()) - }, - Err(_) => Err("failed".into()), + } + Err(_) => Err(format!( + "`{}` failed when calling the exported function `{}`.", + instruction_name, + export_name + )) } } None => Err(format!( @@ -402,4 +406,43 @@ mod tests { String::from(r#"`call-export "sum"` cannot call the exported function `sum` because the value types in the stack mismatch the function signature (expects [I32, I32])."#) ); } + + #[test] + fn test_interpreter_call_export_failed_when_calling() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; + let instance = Instance { + exports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + "sum".into(), + Export { + inputs: vec![Type::I32, Type::I32], + outputs: vec![Type::I32], + function: |_| Err(()), + // ^^^^^^ function fails + }, + ); + + hashmap + }, + }; + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from(r#"`call-export "sum"` failed when calling the exported function `sum`."#) + ); + } } From aea18f6250b721263fee4e16ddf48dd10c567a1b Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 20 Sep 2019 14:59:18 +0200 Subject: [PATCH 26/84] test(interface-types) Test calling a void exported function. --- .../src/instructions/interpreter.rs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 6fb25240a..7f7d75aa0 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -427,7 +427,7 @@ mod tests { inputs: vec![Type::I32, Type::I32], outputs: vec![Type::I32], function: |_| Err(()), - // ^^^^^^ function fails + // ^^^^^^^ function fails }, ); @@ -445,4 +445,40 @@ mod tests { String::from(r#"`call-export "sum"` failed when calling the exported function `sum`."#) ); } + + #[test] + fn test_interpreter_call_export_that_returns_nothing() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; + let instance = Instance { + exports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + "sum".into(), + Export { + inputs: vec![Type::I32, Type::I32], + outputs: vec![Type::I32], + function: |_| Ok(vec![]), + // ^^^^^^^^^^ void function + }, + ); + + hashmap + }, + }; + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_ok()); + + let stack = run.unwrap(); + + assert!(stack.is_empty()); + } } From be5624e28bf48cc99aa30852fada92aaee0b7360 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 23 Sep 2019 16:29:01 +0200 Subject: [PATCH 27/84] feat(interface-types) Implement the `read-utf8` instruction. It implies to create the `wasm::Memory` trait. Also, the patch updates `wasm::Type` and `wasm::Value` to `wasm::InterfaceType` and `wasm::InterfaceValue`. It enforces a new rule that is: All values in the stack must be owned by the stack. Any value going in or out must be cloned. --- .../src/instructions/interpreter.rs | 350 ++++++++++++++---- lib/interface-types/src/instructions/stack.rs | 6 +- lib/interface-types/src/instructions/wasm.rs | 109 ++++-- 3 files changed, 351 insertions(+), 114 deletions(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 7f7d75aa0..4028cd7ad 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -1,51 +1,56 @@ use crate::instructions::{ stack::{Stack, Stackable}, - wasm::{self, Type, Value}, + wasm::{self, InterfaceType, InterfaceValue}, Instruction, }; -use std::{convert::TryFrom, marker::PhantomData}; +use std::{cell::Cell, convert::TryFrom, marker::PhantomData}; -struct Runtime<'invocation, 'instance, Instance, Export> +struct Runtime<'invocation, 'instance, Instance, Export, Memory> where Export: wasm::Export + 'instance, - Instance: wasm::Instance + 'instance, + Memory: wasm::Memory + 'instance, + Instance: wasm::Instance + 'instance, { - invocation_inputs: &'invocation [Value], - stack: Stack, + invocation_inputs: &'invocation [InterfaceValue], + stack: Stack, wasm_instance: &'instance Instance, wasm_exports: PhantomData, + wasm_memory: PhantomData, } -type ExecutableInstruction = - Box) -> Result<(), String>>; +type ExecutableInstruction = + Box) -> Result<(), String>>; -pub struct Interpreter +pub struct Interpreter where Export: wasm::Export, - Instance: wasm::Instance, + Memory: wasm::Memory, + Instance: wasm::Instance, { - executable_instructions: Vec>, + executable_instructions: Vec>, } -impl Interpreter +impl Interpreter where Export: wasm::Export, - Instance: wasm::Instance, + Memory: wasm::Memory, + Instance: wasm::Instance, { - fn iter(&self) -> impl Iterator> + '_ { + fn iter(&self) -> impl Iterator> + '_ { self.executable_instructions.iter() } pub fn run( &self, - invocation_inputs: &[Value], + invocation_inputs: &[InterfaceValue], wasm_instance: &Instance, - ) -> Result, String> { + ) -> Result, String> { let mut runtime = Runtime { invocation_inputs, stack: Stack::new(), wasm_instance, wasm_exports: PhantomData, + wasm_memory: PhantomData, }; for executable_instruction in self.iter() { @@ -59,11 +64,12 @@ where } } -impl<'binary_input, Instance, Export> TryFrom<&Vec>> - for Interpreter +impl<'binary_input, Instance, Export, Memory> TryFrom<&Vec>> + for Interpreter where Export: wasm::Export, - Instance: wasm::Instance, + Memory: wasm::Memory, + Instance: wasm::Instance, { type Error = String; @@ -71,13 +77,13 @@ where let executable_instructions = instructions .iter() .map( - |instruction| -> ExecutableInstruction { + |instruction| -> ExecutableInstruction { match instruction { Instruction::ArgumentGet(index) => { let index = index.to_owned(); let instruction_name: String = instruction.into(); - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { let invocation_inputs = runtime.invocation_inputs; if index >= (invocation_inputs.len() as u64) { @@ -87,7 +93,7 @@ where )); } - runtime.stack.push(invocation_inputs[index as usize]); + runtime.stack.push(invocation_inputs[index as usize].clone()); Ok(()) }) @@ -96,7 +102,7 @@ where let export_name = (*export_name).to_owned(); let instruction_name: String = instruction.into(); - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { let instance = runtime.wasm_instance; match instance.export(&export_name) { @@ -108,11 +114,11 @@ where let input_types = inputs .iter() .map(|input| input.into()) - .collect::>(); + .collect::>(); if input_types != export.inputs() { return Err(format!( - "`{}` cannot call the exported function `{}` because the value types in the stack mismatch the function signature (expects {:?}).", + "`{}` cannot call the exported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).", instruction_name, export_name, export.inputs(), @@ -122,7 +128,7 @@ where match export.call(&inputs) { Ok(outputs) => { for output in outputs.iter() { - runtime.stack.push(*output); + runtime.stack.push(output.clone()); } Ok(()) @@ -135,7 +141,7 @@ where } } None => Err(format!( - "`{}` cannot call the exported function `{}` because there is no enought data in the stack for the arguments (needs {}).", + "`{}` cannot call the exported function `{}` because there is no enough data on the stack for the arguments (needs {}).", instruction_name, export_name, inputs_cardinality, @@ -151,16 +157,59 @@ where }) } Instruction::ReadUtf8 => { - Box::new(|_runtime: &mut Runtime| -> Result<(), _> { - println!("read utf8"); + let instruction_name: String = instruction.into(); - Ok(()) + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + match runtime.stack.pop(2) { + Some(inputs) => match runtime.wasm_instance.memory(0) { + Some(memory) => { + let length = i32::try_from(&inputs[0])? as usize; + let pointer = i32::try_from(&inputs[1])? as usize; + let memory_view = memory.view::(); + + if memory_view.len() < pointer + length { + return Err(format!( + "`{}` failed because it has to read out of the memory bounds (index {} > memory length {}).", + instruction_name, + pointer + length, + memory_view.len() + )); + } + + let data: Vec = (&memory_view[pointer..pointer + length]) + .iter() + .map(Cell::get) + .collect(); + + match String::from_utf8(data) { + Ok(string) => { + runtime.stack.push(InterfaceValue::String(string)); + + Ok(()) + } + Err(utf8_error) => Err(format!( + "`{}` failed because the read string isn't UTF-8 valid ({}).", + instruction_name, + utf8_error, + )) + } + } + None => Err(format!( + "`{}` failed because there is no memory to read.", + instruction_name + )) + } + None => Err(format!( + "`{}` failed because there is no enough data on the stack (needs 2).", + instruction_name, + )) + } }) } Instruction::Call(index) => { let index = index.to_owned(); - Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { + Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { println!("call {}", index); Ok(()) @@ -183,15 +232,15 @@ mod tests { use super::Interpreter; use crate::instructions::{ stack::Stackable, - wasm::{self, Type, Value}, + wasm::{self, InterfaceType, InterfaceValue}, Instruction, }; - use std::{collections::HashMap, convert::TryInto}; + use std::{cell::Cell, collections::HashMap, convert::TryInto}; struct Export { - inputs: Vec, - outputs: Vec, - function: fn(arguments: &[Value]) -> Result, ()>, + inputs: Vec, + outputs: Vec, + function: fn(arguments: &[InterfaceValue]) -> Result, ()>, } impl wasm::Export for Export { @@ -203,21 +252,42 @@ mod tests { self.outputs.len() } - fn inputs(&self) -> &[Type] { + fn inputs(&self) -> &[InterfaceType] { &self.inputs } - fn outputs(&self) -> &[Type] { + fn outputs(&self) -> &[InterfaceType] { &self.outputs } - fn call(&self, arguments: &[Value]) -> Result, ()> { + fn call(&self, arguments: &[InterfaceValue]) -> Result, ()> { (self.function)(arguments) } } + #[derive(Default)] + struct Memory { + data: Vec>, + } + + impl Memory { + fn new(data: Vec>) -> Self { + Self { data } + } + } + + impl wasm::Memory for Memory { + fn view(&self) -> &[Cell] { + let slice = self.data.as_slice(); + + unsafe { ::std::slice::from_raw_parts(slice.as_ptr() as *const Cell, slice.len()) } + } + } + + #[derive(Default)] struct Instance { exports: HashMap, + memory: Memory, } impl Instance { @@ -228,27 +298,32 @@ mod tests { hashmap.insert( "sum".into(), Export { - inputs: vec![Type::I32, Type::I32], - outputs: vec![Type::I32], - function: |arguments: &[Value]| { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |arguments: &[InterfaceValue]| { let a: i32 = (&arguments[0]).try_into().unwrap(); let b: i32 = (&arguments[1]).try_into().unwrap(); - Ok(vec![Value::I32(a + b)]) + Ok(vec![InterfaceValue::I32(a + b)]) }, }, ); hashmap }, + memory: Memory::new(vec![]), } } } - impl wasm::Instance for Instance { + impl wasm::Instance for Instance { fn export(&self, export_name: &str) -> Option<&Export> { self.exports.get(export_name) } + + fn memory(&self, _index: usize) -> Option<&Memory> { + Some(&self.memory) + } } #[test] @@ -260,17 +335,17 @@ mod tests { Instruction::ReadUtf8, Instruction::Call(7), ]; - let interpreter: Interpreter<(), ()> = (&instructions).try_into().unwrap(); + let interpreter: Interpreter<(), (), ()> = (&instructions).try_into().unwrap(); assert_eq!(interpreter.executable_instructions.len(), 5); } #[test] fn test_interpreter_argument_get() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(0)]).try_into().unwrap(); - let invocation_inputs = vec![Value::I32(42)]; + let invocation_inputs = vec![InterfaceValue::I32(42)]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -278,15 +353,15 @@ mod tests { let stack = run.unwrap(); - assert_eq!(stack.as_slice(), &[Value::I32(42)]); + assert_eq!(stack.as_slice(), &[InterfaceValue::I32(42)]); } #[test] fn test_interpreter_argument_get_invalid_index() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(1)]).try_into().unwrap(); - let invocation_inputs = vec![Value::I32(42)]; + let invocation_inputs = vec![InterfaceValue::I32(42)]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -302,12 +377,12 @@ mod tests { #[test] fn test_interpreter_argument_get_argument_get() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(0), Instruction::ArgumentGet(1)]) .try_into() .unwrap(); - let invocation_inputs = vec![Value::I32(7), Value::I32(42)]; + let invocation_inputs = vec![InterfaceValue::I32(7), InterfaceValue::I32(42)]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -315,12 +390,15 @@ mod tests { let stack = run.unwrap(); - assert_eq!(stack.as_slice(), &[Value::I32(7), Value::I32(42)]); + assert_eq!( + stack.as_slice(), + &[InterfaceValue::I32(7), InterfaceValue::I32(42)] + ); } #[test] fn test_interpreter_call_export() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -328,7 +406,7 @@ mod tests { .try_into() .unwrap(); - let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -336,12 +414,12 @@ mod tests { let stack = run.unwrap(); - assert_eq!(stack.as_slice(), &[Value::I32(7)]); + assert_eq!(stack.as_slice(), &[InterfaceValue::I32(7)]); } #[test] fn test_interpreter_call_export_invalid_export_name() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::CallExport("bar")]).try_into().unwrap(); let invocation_inputs = vec![]; @@ -359,16 +437,16 @@ mod tests { } #[test] - fn test_interpreter_call_export_too_small_stack() { - let interpreter: Interpreter = (&vec![ + fn test_interpreter_call_export_stack_is_too_small() { + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(0), Instruction::CallExport("sum"), - // ^^^ `sum` expects 2 values in the stack, only one is present + // ^^^ `sum` expects 2 values on the stack, only one is present ]) .try_into() .unwrap(); - let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -378,13 +456,13 @@ mod tests { assert_eq!( error, - String::from(r#"`call-export "sum"` cannot call the exported function `sum` because there is no enought data in the stack for the arguments (needs 2)."#) + String::from(r#"`call-export "sum"` cannot call the exported function `sum` because there is no enough data on the stack for the arguments (needs 2)."#) ); } #[test] fn test_interpreter_call_export_invalid_types_in_the_stack() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -392,7 +470,7 @@ mod tests { .try_into() .unwrap(); - let invocation_inputs = vec![Value::I32(3), Value::I64(4)]; + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I64(4)]; // ^^^ mismatch with `sum` signature let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -403,13 +481,13 @@ mod tests { assert_eq!( error, - String::from(r#"`call-export "sum"` cannot call the exported function `sum` because the value types in the stack mismatch the function signature (expects [I32, I32])."#) + String::from(r#"`call-export "sum"` cannot call the exported function `sum` because the value types on the stack mismatch the function signature (expects [I32, I32])."#) ); } #[test] fn test_interpreter_call_export_failed_when_calling() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -417,15 +495,15 @@ mod tests { .try_into() .unwrap(); - let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; let instance = Instance { exports: { let mut hashmap = HashMap::new(); hashmap.insert( "sum".into(), Export { - inputs: vec![Type::I32, Type::I32], - outputs: vec![Type::I32], + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], function: |_| Err(()), // ^^^^^^^ function fails }, @@ -433,6 +511,7 @@ mod tests { hashmap }, + ..Default::default() }; let run = interpreter.run(&invocation_inputs, &instance); @@ -448,7 +527,7 @@ mod tests { #[test] fn test_interpreter_call_export_that_returns_nothing() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -456,15 +535,15 @@ mod tests { .try_into() .unwrap(); - let invocation_inputs = vec![Value::I32(3), Value::I32(4)]; + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; let instance = Instance { exports: { let mut hashmap = HashMap::new(); hashmap.insert( "sum".into(), Export { - inputs: vec![Type::I32, Type::I32], - outputs: vec![Type::I32], + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], function: |_| Ok(vec![]), // ^^^^^^^^^^ void function }, @@ -472,6 +551,7 @@ mod tests { hashmap }, + ..Default::default() }; let run = interpreter.run(&invocation_inputs, &instance); @@ -481,4 +561,128 @@ mod tests { assert!(stack.is_empty()); } + + #[test] + fn test_interpreter_read_utf8() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::ReadUtf8, + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![InterfaceValue::I32(13), InterfaceValue::I32(0)]; + // ^^^^^^^ length ^^^^^^ pointer + let instance = Instance { + memory: Memory::new( + "Hello, World!" + .as_bytes() + .iter() + .map(|u| Cell::new(*u)) + .collect(), + ), + ..Default::default() + }; + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_ok()); + + let stack = run.unwrap(); + + assert_eq!( + stack.as_slice(), + &[InterfaceValue::String("Hello, World!".into())] + ); + } + + #[test] + fn test_interpreter_read_utf8_out_of_memory() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::ReadUtf8, + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![InterfaceValue::I32(13), InterfaceValue::I32(0)]; + // ^^^^^^^ length ^^^^^^ pointer + // is too long + let instance = Instance { + memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), + ..Default::default() + }; + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from( + r#"`read-utf8` failed because it has to read out of the memory bounds (index 13 > memory length 6)."# + ) + ); + } + + #[test] + fn test_interpreter_read_utf8_invalid_encoding() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::ReadUtf8, + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![InterfaceValue::I32(4), InterfaceValue::I32(0)]; + // ^^^^^^ length ^^^^^^ pointer + let instance = Instance { + memory: Memory::new( + vec![0, 159, 146, 150] + .iter() + .map(|b| Cell::new(*b)) + .collect::>>(), + ), + ..Default::default() + }; + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from(r#"`read-utf8` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#) + ); + } + + #[test] + fn test_interpreter_read_utf8_stack_is_too_small() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(0), + Instruction::ReadUtf8, + // ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present. + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from( + r#"`read-utf8` failed because there is no enough data on the stack (needs 2)."# + ) + ); + } } diff --git a/lib/interface-types/src/instructions/stack.rs b/lib/interface-types/src/instructions/stack.rs index d62d40d7c..57b999df2 100644 --- a/lib/interface-types/src/instructions/stack.rs +++ b/lib/interface-types/src/instructions/stack.rs @@ -11,14 +11,14 @@ pub trait Stackable { #[derive(Debug, Default)] pub struct Stack where - T: Default, + T: Default + Clone, { inner: Vec, } impl Stack where - T: Default, + T: Default + Clone, { pub fn new() -> Self { Self { @@ -29,7 +29,7 @@ where impl Stackable for Stack where - T: Default, + T: Default + Clone, { type Item = T; diff --git a/lib/interface-types/src/instructions/wasm.rs b/lib/interface-types/src/instructions/wasm.rs index 8857b6db5..33a25ed0a 100644 --- a/lib/interface-types/src/instructions/wasm.rs +++ b/lib/interface-types/src/instructions/wasm.rs @@ -1,55 +1,56 @@ -use std::convert::TryFrom; +use std::{cell::Cell, convert::TryFrom}; -#[derive(Debug, PartialEq)] -pub enum Type { - I32, - I64, - F32, - F64, - V128, -} +pub use crate::ast::InterfaceType; -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Value { +#[derive(Debug, Clone, PartialEq)] +pub enum InterfaceValue { + Int(isize), + Float(f64), + Any(isize), + String(String), + // Seq(…), I32(i32), I64(i64), F32(f32), F64(f64), - V128(u128), + // AnyRef(…), } -impl From<&Value> for Type { - fn from(value: &Value) -> Self { +impl From<&InterfaceValue> for InterfaceType { + fn from(value: &InterfaceValue) -> Self { match value { - Value::I32(_) => Type::I32, - Value::I64(_) => Type::I64, - Value::F32(_) => Type::F32, - Value::F64(_) => Type::F64, - Value::V128(_) => Type::V128, + InterfaceValue::Int(_) => Self::Int, + InterfaceValue::Float(_) => Self::Float, + InterfaceValue::Any(_) => Self::Any, + InterfaceValue::String(_) => Self::String, + InterfaceValue::I32(_) => Self::I32, + InterfaceValue::I64(_) => Self::I64, + InterfaceValue::F32(_) => Self::F32, + InterfaceValue::F64(_) => Self::F64, } } } -impl Default for Value { +impl Default for InterfaceValue { fn default() -> Self { Self::I32(0) } } -macro_rules! from_x_for_value { +macro_rules! from_x_for_interface_value { ($native_type:ty, $value_variant:ident) => { - impl From<$native_type> for Value { + impl From<$native_type> for InterfaceValue { fn from(n: $native_type) -> Self { Self::$value_variant(n) } } - impl TryFrom<&Value> for $native_type { + impl TryFrom<&InterfaceValue> for $native_type { type Error = &'static str; - fn try_from(w: &Value) -> Result { + fn try_from(w: &InterfaceValue) -> Result { match *w { - Value::$value_variant(n) => Ok(n), + InterfaceValue::$value_variant(n) => Ok(n), _ => Err("Invalid cast."), } } @@ -57,25 +58,46 @@ macro_rules! from_x_for_value { }; } -from_x_for_value!(i32, I32); -from_x_for_value!(i64, I64); -from_x_for_value!(f32, F32); -from_x_for_value!(f64, F64); -from_x_for_value!(u128, V128); +from_x_for_interface_value!(i32, I32); +from_x_for_interface_value!(i64, I64); +from_x_for_interface_value!(f32, F32); +from_x_for_interface_value!(f64, F64); + +pub trait ValueType: Copy + Sized {} + +macro_rules! value_type { + ($native_type:ty) => { + impl ValueType for $native_type {} + }; + + ($($native_type:ty),*) => { + $( + value_type!($native_type); + )* + }; +} + +value_type!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); pub trait Export { fn inputs_cardinality(&self) -> usize; fn outputs_cardinality(&self) -> usize; - fn inputs(&self) -> &[Type]; - fn outputs(&self) -> &[Type]; - fn call(&self, arguments: &[Value]) -> Result, ()>; + fn inputs(&self) -> &[InterfaceType]; + fn outputs(&self) -> &[InterfaceType]; + fn call(&self, arguments: &[InterfaceValue]) -> Result, ()>; } -pub trait Instance +pub trait Memory { + fn view(&self) -> &[Cell]; +} + +pub trait Instance where E: Export, + M: Memory, { fn export(&self, export_name: &str) -> Option<&E>; + fn memory(&self, index: usize) -> Option<&M>; } impl Export for () { @@ -87,24 +109,35 @@ impl Export for () { 0 } - fn inputs(&self) -> &[Type] { + fn inputs(&self) -> &[InterfaceType] { &[] } - fn outputs(&self) -> &[Type] { + fn outputs(&self) -> &[InterfaceType] { &[] } - fn call(&self, _arguments: &[Value]) -> Result, ()> { + fn call(&self, _arguments: &[InterfaceValue]) -> Result, ()> { Err(()) } } -impl Instance for () +impl Memory for () { + fn view(&self) -> &[Cell] { + &[] + } +} + +impl Instance for () where E: Export, + M: Memory, { fn export(&self, _export_name: &str) -> Option<&E> { None } + + fn memory(&self, _: usize) -> Option<&M> { + None + } } From 4d9dacb482eb909aca63861792ea9815db26194f Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 25 Sep 2019 21:53:23 +0200 Subject: [PATCH 28/84] feat(interface-types) Implement the `call` executable instruction. The patch requires to implement the `wasm::TypedIndex`, `wasm::LocalImportIndex`, and the `wasm::LocalImport` traits. --- lib/interface-types/src/decoders/binary.rs | 2 +- .../src/instructions/interpreter.rs | 359 ++++++++++++++++-- lib/interface-types/src/instructions/mod.rs | 2 +- lib/interface-types/src/instructions/wasm.rs | 83 +++- lib/interface-types/src/lib.rs | 2 +- 5 files changed, 403 insertions(+), 45 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index a4fc146e3..cef85ffed 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -138,7 +138,7 @@ fn instructions<'input, E: ParseError<&'input [u8]>>( 0x01 => { consume!((input, argument_0) = leb(input)?); - (input, Instruction::Call(argument_0)) + (input, Instruction::Call(argument_0 as usize)) } 0x02 => { diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 4028cd7ad..e4494c856 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -1,42 +1,49 @@ use crate::instructions::{ stack::{Stack, Stackable}, - wasm::{self, InterfaceType, InterfaceValue}, + wasm::{self, FunctionIndex, InterfaceType, InterfaceValue, TypedIndex}, Instruction, }; use std::{cell::Cell, convert::TryFrom, marker::PhantomData}; -struct Runtime<'invocation, 'instance, Instance, Export, Memory> +struct Runtime<'invocation, 'instance, Instance, Export, LocalImport, Memory> where Export: wasm::Export + 'instance, + LocalImport: wasm::LocalImport + 'instance, Memory: wasm::Memory + 'instance, - Instance: wasm::Instance + 'instance, + Instance: wasm::Instance + 'instance, { invocation_inputs: &'invocation [InterfaceValue], stack: Stack, wasm_instance: &'instance Instance, wasm_exports: PhantomData, - wasm_memory: PhantomData, + wasm_locals_or_imports: PhantomData, + wasm_memories: PhantomData, } -type ExecutableInstruction = - Box) -> Result<(), String>>; +type ExecutableInstruction = + Box) -> Result<(), String>>; -pub struct Interpreter +pub struct Interpreter where Export: wasm::Export, + LocalImport: wasm::LocalImport, Memory: wasm::Memory, - Instance: wasm::Instance, + Instance: wasm::Instance, { - executable_instructions: Vec>, + executable_instructions: Vec>, } -impl Interpreter +impl Interpreter where Export: wasm::Export, + LocalImport: wasm::LocalImport, Memory: wasm::Memory, - Instance: wasm::Instance, + Instance: wasm::Instance, { - fn iter(&self) -> impl Iterator> + '_ { + fn iter( + &self, + ) -> impl Iterator> + '_ + { self.executable_instructions.iter() } @@ -50,7 +57,8 @@ where stack: Stack::new(), wasm_instance, wasm_exports: PhantomData, - wasm_memory: PhantomData, + wasm_locals_or_imports: PhantomData, + wasm_memories: PhantomData, }; for executable_instruction in self.iter() { @@ -64,12 +72,13 @@ where } } -impl<'binary_input, Instance, Export, Memory> TryFrom<&Vec>> - for Interpreter +impl<'binary_input, Instance, Export, LocalImport, Memory> TryFrom<&Vec>> + for Interpreter where Export: wasm::Export, + LocalImport: wasm::LocalImport, Memory: wasm::Memory, - Instance: wasm::Instance, + Instance: wasm::Instance, { type Error = String; @@ -77,13 +86,13 @@ where let executable_instructions = instructions .iter() .map( - |instruction| -> ExecutableInstruction { + |instruction| -> ExecutableInstruction { match instruction { Instruction::ArgumentGet(index) => { let index = index.to_owned(); let instruction_name: String = instruction.into(); - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { let invocation_inputs = runtime.invocation_inputs; if index >= (invocation_inputs.len() as u64) { @@ -102,7 +111,7 @@ where let export_name = (*export_name).to_owned(); let instruction_name: String = instruction.into(); - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { let instance = runtime.wasm_instance; match instance.export(&export_name) { @@ -159,7 +168,7 @@ where Instruction::ReadUtf8 => { let instruction_name: String = instruction.into(); - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { match runtime.stack.pop(2) { Some(inputs) => match runtime.wasm_instance.memory(0) { Some(memory) => { @@ -208,11 +217,61 @@ where } Instruction::Call(index) => { let index = index.to_owned(); + let instruction_name: String = instruction.into(); - Box::new(move |_runtime: &mut Runtime| -> Result<(), _> { - println!("call {}", index); + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + let instance = runtime.wasm_instance; + let function_index = FunctionIndex::new(index); - Ok(()) + match instance.local_or_import(function_index) { + Some(local_or_import) => { + let inputs_cardinality = local_or_import.inputs_cardinality(); + + match runtime.stack.pop(inputs_cardinality) { + Some(inputs) => { + let input_types = inputs + .iter() + .map(|input| input.into()) + .collect::>(); + + if input_types != local_or_import.inputs() { + return Err(format!( + "`{}` cannot call the local or imported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).", + instruction_name, + index, + local_or_import.inputs(), + )) + } + + match local_or_import.call(&inputs) { + Ok(outputs) => { + for output in outputs.iter() { + runtime.stack.push(output.clone()); + } + + Ok(()) + } + Err(_) => Err(format!( + "`{}` failed when calling the local or imported function `{}`.", + instruction_name, + index + )) + } + } + None => Err(format!( + "`{}` cannot call the local or imported function `{}` because there is no enough data on the stack for the arguments (needs {}).", + instruction_name, + index, + inputs_cardinality, + )) + } + } + None => Err(format!( + "`{}` cannot call the local or imported function `{}` because it doesn't exist.", + instruction_name, + index, + )) + } }) } _ => unimplemented!(), @@ -265,6 +324,34 @@ mod tests { } } + struct LocalImport { + inputs: Vec, + outputs: Vec, + function: fn(arguments: &[InterfaceValue]) -> Result, ()>, + } + + impl wasm::LocalImport for LocalImport { + fn inputs_cardinality(&self) -> usize { + self.inputs.len() as usize + } + + fn outputs_cardinality(&self) -> usize { + self.outputs.len() + } + + fn inputs(&self) -> &[InterfaceType] { + &self.inputs + } + + fn outputs(&self) -> &[InterfaceType] { + &self.outputs + } + + fn call(&self, arguments: &[InterfaceValue]) -> Result, ()> { + (self.function)(arguments) + } + } + #[derive(Default)] struct Memory { data: Vec>, @@ -287,6 +374,7 @@ mod tests { #[derive(Default)] struct Instance { exports: HashMap, + locals_or_imports: HashMap, memory: Memory, } @@ -311,16 +399,41 @@ mod tests { hashmap }, + locals_or_imports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + 42, + LocalImport { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |arguments: &[InterfaceValue]| { + let a: i32 = (&arguments[0]).try_into().unwrap(); + let b: i32 = (&arguments[1]).try_into().unwrap(); + + Ok(vec![InterfaceValue::I32(a * b)]) + }, + }, + ); + + hashmap + }, memory: Memory::new(vec![]), } } } - impl wasm::Instance for Instance { + impl wasm::Instance for Instance { fn export(&self, export_name: &str) -> Option<&Export> { self.exports.get(export_name) } + fn local_or_import( + &self, + index: I, + ) -> Option<&LocalImport> { + self.locals_or_imports.get(&index.index()) + } + fn memory(&self, _index: usize) -> Option<&Memory> { Some(&self.memory) } @@ -335,14 +448,14 @@ mod tests { Instruction::ReadUtf8, Instruction::Call(7), ]; - let interpreter: Interpreter<(), (), ()> = (&instructions).try_into().unwrap(); + let interpreter: Interpreter<(), (), (), ()> = (&instructions).try_into().unwrap(); assert_eq!(interpreter.executable_instructions.len(), 5); } #[test] fn test_interpreter_argument_get() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(0)]).try_into().unwrap(); let invocation_inputs = vec![InterfaceValue::I32(42)]; @@ -358,7 +471,7 @@ mod tests { #[test] fn test_interpreter_argument_get_invalid_index() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(1)]).try_into().unwrap(); let invocation_inputs = vec![InterfaceValue::I32(42)]; @@ -377,7 +490,7 @@ mod tests { #[test] fn test_interpreter_argument_get_argument_get() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::ArgumentGet(0), Instruction::ArgumentGet(1)]) .try_into() .unwrap(); @@ -398,7 +511,7 @@ mod tests { #[test] fn test_interpreter_call_export() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -419,7 +532,7 @@ mod tests { #[test] fn test_interpreter_call_export_invalid_export_name() { - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![Instruction::CallExport("bar")]).try_into().unwrap(); let invocation_inputs = vec![]; @@ -438,7 +551,7 @@ mod tests { #[test] fn test_interpreter_call_export_stack_is_too_small() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(0), Instruction::CallExport("sum"), // ^^^ `sum` expects 2 values on the stack, only one is present @@ -462,7 +575,7 @@ mod tests { #[test] fn test_interpreter_call_export_invalid_types_in_the_stack() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -471,7 +584,7 @@ mod tests { .unwrap(); let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I64(4)]; - // ^^^ mismatch with `sum` signature + // ^^^ mismatch with `sum` signature let instance = Instance::new(); let run = interpreter.run(&invocation_inputs, &instance); @@ -487,7 +600,7 @@ mod tests { #[test] fn test_interpreter_call_export_failed_when_calling() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -527,7 +640,7 @@ mod tests { #[test] fn test_interpreter_call_export_that_returns_nothing() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::CallExport("sum"), @@ -564,7 +677,7 @@ mod tests { #[test] fn test_interpreter_read_utf8() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::ReadUtf8, @@ -598,7 +711,7 @@ mod tests { #[test] fn test_interpreter_read_utf8_out_of_memory() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::ReadUtf8, @@ -629,7 +742,7 @@ mod tests { #[test] fn test_interpreter_read_utf8_invalid_encoding() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(1), Instruction::ArgumentGet(0), Instruction::ReadUtf8, @@ -662,7 +775,7 @@ mod tests { #[test] fn test_interpreter_read_utf8_stack_is_too_small() { - let interpreter: Interpreter = (&vec![ + let interpreter: Interpreter = (&vec![ Instruction::ArgumentGet(0), Instruction::ReadUtf8, // ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present. @@ -685,4 +798,172 @@ mod tests { ) ); } + + #[test] + fn test_interpreter_call() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::Call(42), + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_ok()); + + let stack = run.unwrap(); + + assert_eq!(stack.as_slice(), &[InterfaceValue::I32(12)]); + } + + #[test] + fn test_interpreter_call_invalid_local_import_index() { + let interpreter: Interpreter = + (&vec![Instruction::Call(42)]).try_into().unwrap(); + + let invocation_inputs = vec![]; + let instance = Instance { + ..Default::default() + }; + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from(r#"`call 42` cannot call the local or imported function `42` because it doesn't exist."#) + ); + } + + #[test] + fn test_interpreter_call_stack_is_too_small() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(0), + Instruction::Call(42), + // ^^ `42` expects 2 values on the stack, only one is present + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from(r#"`call 42` cannot call the local or imported function `42` because there is no enough data on the stack for the arguments (needs 2)."#) + ); + } + + #[test] + fn test_interpreter_call_invalid_types_in_the_stack() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::Call(42), + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I64(4)]; + // ^^^ mismatch with `42` signature + let instance = Instance::new(); + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from(r#"`call 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#) + ); + } + + #[test] + fn test_interpreter_call_failed_when_calling() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::Call(42), + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; + let instance = Instance { + locals_or_imports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + 42, + LocalImport { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Err(()), + // ^^^^^^^ function fails + }, + ); + + hashmap + }, + ..Default::default() + }; + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!( + error, + String::from(r#"`call 42` failed when calling the local or imported function `42`."#) + ); + } + + #[test] + fn test_interpreter_call_that_returns_nothing() { + let interpreter: Interpreter = (&vec![ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::Call(42), + ]) + .try_into() + .unwrap(); + + let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; + let instance = Instance { + locals_or_imports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + 42, + LocalImport { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Ok(vec![]), + // ^^^^^^^^^^ void function + }, + ); + + hashmap + }, + ..Default::default() + }; + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_ok()); + + let stack = run.unwrap(); + + assert!(stack.is_empty()); + } } diff --git a/lib/interface-types/src/instructions/mod.rs b/lib/interface-types/src/instructions/mod.rs index 43e536c4b..89b98f9ed 100644 --- a/lib/interface-types/src/instructions/mod.rs +++ b/lib/interface-types/src/instructions/mod.rs @@ -7,7 +7,7 @@ pub mod wasm; #[derive(PartialEq, Debug)] pub enum Instruction<'input> { ArgumentGet(u64), - Call(u64), + Call(usize), CallExport(&'input str), ReadUtf8, WriteUtf8(&'input str), diff --git a/lib/interface-types/src/instructions/wasm.rs b/lib/interface-types/src/instructions/wasm.rs index 33a25ed0a..aa3090f18 100644 --- a/lib/interface-types/src/instructions/wasm.rs +++ b/lib/interface-types/src/instructions/wasm.rs @@ -63,7 +63,11 @@ from_x_for_interface_value!(i64, I64); from_x_for_interface_value!(f32, F32); from_x_for_interface_value!(f64, F64); -pub trait ValueType: Copy + Sized {} +pub trait ValueType: Copy +where + Self: Sized, +{ +} macro_rules! value_type { ($native_type:ty) => { @@ -79,6 +83,42 @@ macro_rules! value_type { value_type!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); +pub trait TypedIndex: Copy + Clone { + fn new(index: usize) -> Self; + fn index(&self) -> usize; +} + +macro_rules! typed_index { + ($type:ident) => { + #[derive(Copy, Clone)] + pub struct $type(usize); + + impl TypedIndex for $type { + fn new(index: usize) -> Self { + Self(index) + } + + fn index(&self) -> usize { + self.0 + } + } + }; +} + +typed_index!(FunctionIndex); +typed_index!(LocalFunctionIndex); +typed_index!(ImportFunctionIndex); + +pub trait LocalImportIndex { + type Local: TypedIndex; + type Import: TypedIndex; +} + +impl LocalImportIndex for FunctionIndex { + type Local = LocalFunctionIndex; + type Import = ImportFunctionIndex; +} + pub trait Export { fn inputs_cardinality(&self) -> usize; fn outputs_cardinality(&self) -> usize; @@ -87,16 +127,26 @@ pub trait Export { fn call(&self, arguments: &[InterfaceValue]) -> Result, ()>; } +pub trait LocalImport { + fn inputs_cardinality(&self) -> usize; + fn outputs_cardinality(&self) -> usize; + fn inputs(&self) -> &[InterfaceType]; + fn outputs(&self) -> &[InterfaceType]; + fn call(&self, arguments: &[InterfaceValue]) -> Result, ()>; +} + pub trait Memory { fn view(&self) -> &[Cell]; } -pub trait Instance +pub trait Instance where E: Export, + LI: LocalImport, M: Memory, { fn export(&self, export_name: &str) -> Option<&E>; + fn local_or_import(&self, index: I) -> Option<&LI>; fn memory(&self, index: usize) -> Option<&M>; } @@ -122,15 +172,38 @@ impl Export for () { } } +impl LocalImport for () { + fn inputs_cardinality(&self) -> usize { + 0 + } + + fn outputs_cardinality(&self) -> usize { + 0 + } + + fn inputs(&self) -> &[InterfaceType] { + &[] + } + + fn outputs(&self) -> &[InterfaceType] { + &[] + } + + fn call(&self, _arguments: &[InterfaceValue]) -> Result, ()> { + Err(()) + } +} + impl Memory for () { fn view(&self) -> &[Cell] { &[] } } -impl Instance for () +impl Instance for () where E: Export, + LI: LocalImport, M: Memory, { fn export(&self, _export_name: &str) -> Option<&E> { @@ -140,4 +213,8 @@ where fn memory(&self, _: usize) -> Option<&M> { None } + + fn local_or_import(&self, _index: I) -> Option<&LI> { + None + } } diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 9ba79d7b9..3760c923f 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -9,7 +9,7 @@ pub use decoders::binary::parse as parse_binary; #[cfg(test)] mod tests { - use crate::{ast::*, encoders::wat::*, instructions::Instruction, parse_binary}; + use crate::{ast::*, instructions::Instruction, parse_binary}; use std::fs; use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core as runtime; From 8d75db9018e0756522a0bb52fd365611d4419cb7 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 25 Sep 2019 23:13:26 +0200 Subject: [PATCH 29/84] test(interface-types) Use macros to reduce test boilerplate. --- .../src/instructions/interpreter.rs | 860 ++++++++---------- 1 file changed, 372 insertions(+), 488 deletions(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index e4494c856..e0cc0f32a 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -453,517 +453,401 @@ mod tests { assert_eq!(interpreter.executable_instructions.len(), 5); } - #[test] - fn test_interpreter_argument_get() { - let interpreter: Interpreter = - (&vec![Instruction::ArgumentGet(0)]).try_into().unwrap(); + macro_rules! test { + ( + $test_name:ident = + instructions: [ $($instructions:expr),* $(,)* ], + invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ], + instance: $instance:expr, + stack: [ $($stack:expr),* $(,)* ] + $(,)* + ) => { + #[test] + #[allow(non_snake_case)] + fn $test_name() { + let interpreter: Interpreter = + (&vec![$($instructions),*]).try_into().unwrap(); - let invocation_inputs = vec![InterfaceValue::I32(42)]; - let instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &instance); + let invocation_inputs = vec![$($invocation_inputs),*]; + let instance = $instance; + let run = interpreter.run(&invocation_inputs, &instance); - assert!(run.is_ok()); + assert!(run.is_ok()); - let stack = run.unwrap(); + let stack = run.unwrap(); - assert_eq!(stack.as_slice(), &[InterfaceValue::I32(42)]); + assert_eq!(stack.as_slice(), &[$($stack),*]); + } + }; + + ( + $test_name:ident = + instructions: [ $($instructions:expr),* $(,)* ], + invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ], + instance: $instance:expr, + error: $error:expr + $(,)* + ) => { + #[test] + #[allow(non_snake_case)] + fn $test_name() { + let interpreter: Interpreter = + (&vec![$($instructions),*]).try_into().unwrap(); + + let invocation_inputs = vec![$($invocation_inputs),*]; + let instance = $instance; + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!(error, String::from($error)); + } + }; } - #[test] - fn test_interpreter_argument_get_invalid_index() { - let interpreter: Interpreter = - (&vec![Instruction::ArgumentGet(1)]).try_into().unwrap(); + test!( + test_interpreter_argument_get = + instructions: [Instruction::ArgumentGet(0)], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); - let invocation_inputs = vec![InterfaceValue::I32(42)]; - let instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &instance); + test!( + test_interpreter_argument_get__twice = + instructions: [ + Instruction::ArgumentGet(0), + Instruction::ArgumentGet(1), + ], + invocation_inputs: [ + InterfaceValue::I32(7), + InterfaceValue::I32(42), + ], + instance: Instance::new(), + stack: [ + InterfaceValue::I32(7), + InterfaceValue::I32(42), + ], + ); - assert!(run.is_err()); + test!( + test_interpreter_argument_get__invalid_index = + instructions: [Instruction::ArgumentGet(1)], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + error: "`arg.get 1` cannot access argument #1 because it doesn't exist." + ); - let error = run.unwrap_err(); + test!( + test_interpreter_call_export = + instructions: [ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance::new(), + stack: [InterfaceValue::I32(7)], + ); - assert_eq!( - error, - String::from("`arg.get 1` cannot access argument #1 because it doesn't exist.") - ); - } + test!( + test_interpreter_call_export__invalid_export_name = + instructions: [Instruction::CallExport("bar")], + invocation_inputs: [], + instance: Instance::new(), + error: r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#, + ); - #[test] - fn test_interpreter_argument_get_argument_get() { - let interpreter: Interpreter = - (&vec![Instruction::ArgumentGet(0), Instruction::ArgumentGet(1)]) - .try_into() - .unwrap(); + test!( + test_interpreter_call_export__stack_is_too_small = + instructions: [ + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance::new(), + error: r#"`call-export "sum"` cannot call the exported function `sum` because there is no enough data on the stack for the arguments (needs 2)."#, + ); - let invocation_inputs = vec![InterfaceValue::I32(7), InterfaceValue::I32(42)]; - let instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &instance); + test!( + test_interpreter_call_export__invalid_types_in_the_stack = + instructions: [ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I64(4), + // ^^^ mismatch with `sum` signature + ], + instance: Instance::new(), + error: r#"`call-export "sum"` cannot call the exported function `sum` because the value types on the stack mismatch the function signature (expects [I32, I32])."#, + ); - assert!(run.is_ok()); + test!( + test_interpreter_call_export__failure_when_calling = + instructions: [ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance { + exports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + "sum".into(), + Export { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Err(()), + // ^^^^^^^ function fails + }, + ); - let stack = run.unwrap(); - - assert_eq!( - stack.as_slice(), - &[InterfaceValue::I32(7), InterfaceValue::I32(42)] - ); - } - - #[test] - fn test_interpreter_call_export() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::CallExport("sum"), - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; - let instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_ok()); - - let stack = run.unwrap(); - - assert_eq!(stack.as_slice(), &[InterfaceValue::I32(7)]); - } - - #[test] - fn test_interpreter_call_export_invalid_export_name() { - let interpreter: Interpreter = - (&vec![Instruction::CallExport("bar")]).try_into().unwrap(); - - let invocation_inputs = vec![]; - let instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_err()); - - let error = run.unwrap_err(); - - assert_eq!( - error, - String::from(r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#) - ); - } - - #[test] - fn test_interpreter_call_export_stack_is_too_small() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(0), - Instruction::CallExport("sum"), - // ^^^ `sum` expects 2 values on the stack, only one is present - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; - let instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_err()); - - let error = run.unwrap_err(); - - assert_eq!( - error, - String::from(r#"`call-export "sum"` cannot call the exported function `sum` because there is no enough data on the stack for the arguments (needs 2)."#) - ); - } - - #[test] - fn test_interpreter_call_export_invalid_types_in_the_stack() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::CallExport("sum"), - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I64(4)]; - // ^^^ mismatch with `sum` signature - let instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_err()); - - let error = run.unwrap_err(); - - assert_eq!( - error, - String::from(r#"`call-export "sum"` cannot call the exported function `sum` because the value types on the stack mismatch the function signature (expects [I32, I32])."#) - ); - } - - #[test] - fn test_interpreter_call_export_failed_when_calling() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::CallExport("sum"), - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; - let instance = Instance { - exports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - "sum".into(), - Export { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Err(()), - // ^^^^^^^ function fails - }, - ); - - hashmap + hashmap + }, + ..Default::default() }, - ..Default::default() - }; - let run = interpreter.run(&invocation_inputs, &instance); + error: r#"`call-export "sum"` failed when calling the exported function `sum`."#, + ); - assert!(run.is_err()); + test!( + test_interpreter_call_export__void = + instructions: [ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::CallExport("sum"), + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance { + exports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + "sum".into(), + Export { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Ok(vec![]), + // ^^^^^^^^^^ void function + }, + ); - let error = run.unwrap_err(); - - assert_eq!( - error, - String::from(r#"`call-export "sum"` failed when calling the exported function `sum`."#) - ); - } - - #[test] - fn test_interpreter_call_export_that_returns_nothing() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::CallExport("sum"), - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; - let instance = Instance { - exports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - "sum".into(), - Export { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Ok(vec![]), - // ^^^^^^^^^^ void function - }, - ); - - hashmap + hashmap + }, + ..Default::default() }, - ..Default::default() - }; - let run = interpreter.run(&invocation_inputs, &instance); + stack: [], + ); - assert!(run.is_ok()); - - let stack = run.unwrap(); - - assert!(stack.is_empty()); - } - - #[test] - fn test_interpreter_read_utf8() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::ReadUtf8, - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(13), InterfaceValue::I32(0)]; - // ^^^^^^^ length ^^^^^^ pointer - let instance = Instance { - memory: Memory::new( - "Hello, World!" - .as_bytes() - .iter() - .map(|u| Cell::new(*u)) - .collect(), - ), - ..Default::default() - }; - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_ok()); - - let stack = run.unwrap(); - - assert_eq!( - stack.as_slice(), - &[InterfaceValue::String("Hello, World!".into())] - ); - } - - #[test] - fn test_interpreter_read_utf8_out_of_memory() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::ReadUtf8, - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(13), InterfaceValue::I32(0)]; - // ^^^^^^^ length ^^^^^^ pointer - // is too long - let instance = Instance { - memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), - ..Default::default() - }; - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_err()); - - let error = run.unwrap_err(); - - assert_eq!( - error, - String::from( - r#"`read-utf8` failed because it has to read out of the memory bounds (index 13 > memory length 6)."# - ) - ); - } - - #[test] - fn test_interpreter_read_utf8_invalid_encoding() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::ReadUtf8, - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(4), InterfaceValue::I32(0)]; - // ^^^^^^ length ^^^^^^ pointer - let instance = Instance { - memory: Memory::new( - vec![0, 159, 146, 150] - .iter() - .map(|b| Cell::new(*b)) - .collect::>>(), - ), - ..Default::default() - }; - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_err()); - - let error = run.unwrap_err(); - - assert_eq!( - error, - String::from(r#"`read-utf8` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#) - ); - } - - #[test] - fn test_interpreter_read_utf8_stack_is_too_small() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(0), - Instruction::ReadUtf8, - // ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present. - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; - let instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_err()); - - let error = run.unwrap_err(); - - assert_eq!( - error, - String::from( - r#"`read-utf8` failed because there is no enough data on the stack (needs 2)."# - ) - ); - } - - #[test] - fn test_interpreter_call() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::Call(42), - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; - let instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_ok()); - - let stack = run.unwrap(); - - assert_eq!(stack.as_slice(), &[InterfaceValue::I32(12)]); - } - - #[test] - fn test_interpreter_call_invalid_local_import_index() { - let interpreter: Interpreter = - (&vec![Instruction::Call(42)]).try_into().unwrap(); - - let invocation_inputs = vec![]; - let instance = Instance { - ..Default::default() - }; - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_err()); - - let error = run.unwrap_err(); - - assert_eq!( - error, - String::from(r#"`call 42` cannot call the local or imported function `42` because it doesn't exist."#) - ); - } - - #[test] - fn test_interpreter_call_stack_is_too_small() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(0), - Instruction::Call(42), - // ^^ `42` expects 2 values on the stack, only one is present - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; - let instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_err()); - - let error = run.unwrap_err(); - - assert_eq!( - error, - String::from(r#"`call 42` cannot call the local or imported function `42` because there is no enough data on the stack for the arguments (needs 2)."#) - ); - } - - #[test] - fn test_interpreter_call_invalid_types_in_the_stack() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::Call(42), - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I64(4)]; - // ^^^ mismatch with `42` signature - let instance = Instance::new(); - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_err()); - - let error = run.unwrap_err(); - - assert_eq!( - error, - String::from(r#"`call 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#) - ); - } - - #[test] - fn test_interpreter_call_failed_when_calling() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::Call(42), - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; - let instance = Instance { - locals_or_imports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - 42, - LocalImport { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Err(()), - // ^^^^^^^ function fails - }, - ); - - hashmap + test!( + test_interpreter_read_utf8 = + instructions: [ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::ReadUtf8, + ], + invocation_inputs: [ + InterfaceValue::I32(13), + // ^^^^^^^ length + InterfaceValue::I32(0), + // ^^^^^^ pointer + ], + instance: Instance { + memory: Memory::new("Hello, World!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), + ..Default::default() }, - ..Default::default() - }; - let run = interpreter.run(&invocation_inputs, &instance); + stack: [InterfaceValue::String("Hello, World!".into())], + ); - assert!(run.is_err()); - - let error = run.unwrap_err(); - - assert_eq!( - error, - String::from(r#"`call 42` failed when calling the local or imported function `42`."#) - ); - } - - #[test] - fn test_interpreter_call_that_returns_nothing() { - let interpreter: Interpreter = (&vec![ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::Call(42), - ]) - .try_into() - .unwrap(); - - let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; - let instance = Instance { - locals_or_imports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - 42, - LocalImport { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Ok(vec![]), - // ^^^^^^^^^^ void function - }, - ); - - hashmap + test!( + test_interpreter_read_utf8__read_out_of_memory = + instructions: [ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::ReadUtf8, + ], + invocation_inputs: [ + InterfaceValue::I32(13), + // ^^^^^^^ length is too long + InterfaceValue::I32(0), + // ^^^^^^ pointer + ], + instance: Instance { + memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), + ..Default::default() }, - ..Default::default() - }; - let run = interpreter.run(&invocation_inputs, &instance); + error: r#"`read-utf8` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#, + ); - assert!(run.is_ok()); + test!( + test_interpreter_read_utf8__invalid_encoding = + instructions: [ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::ReadUtf8, + ], + invocation_inputs: [ + InterfaceValue::I32(4), + // ^^^^^^ length is too long + InterfaceValue::I32(0), + // ^^^^^^ pointer + ], + instance: Instance { + memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::>>()), + ..Default::default() + }, + error: r#"`read-utf8` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#, + ); - let stack = run.unwrap(); + test!( + test_interpreter_read_utf8__stack_is_too_small = + instructions: [ + Instruction::ArgumentGet(0), + Instruction::ReadUtf8, + // ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present. + ], + invocation_inputs: [ + InterfaceValue::I32(13), + InterfaceValue::I32(0), + ], + instance: Instance::new(), + error: r#"`read-utf8` failed because there is no enough data on the stack (needs 2)."#, + ); - assert!(stack.is_empty()); - } + test!( + test_interpreter_call = + instructions: [ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::Call(42), + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance::new(), + stack: [InterfaceValue::I32(12)], + ); + + test!( + test_interpreter_call__invalid_local_import_index = + instructions: [ + Instruction::Call(42), + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance { ..Default::default() }, + error: r#"`call 42` cannot call the local or imported function `42` because it doesn't exist."#, + ); + + test!( + test_interpreter_call__stack_is_too_small = + instructions: [ + Instruction::ArgumentGet(0), + Instruction::Call(42), + // ^^ `42` expects 2 values on the stack, only one is present + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance::new(), + error: r#"`call 42` cannot call the local or imported function `42` because there is no enough data on the stack for the arguments (needs 2)."#, + ); + + test!( + test_interpreter_call__invalid_types_in_the_stack = + instructions: [ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::Call(42), + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I64(4), + // ^^^ mismatch with `42` signature + ], + instance: Instance::new(), + error: r#"`call 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#, + ); + + test!( + test_interpreter_call__failure_when_calling = + instructions: [ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::Call(42), + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance { + locals_or_imports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + 42, + LocalImport { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Err(()), + // ^^^^^^^ function fails + }, + ); + + hashmap + }, + ..Default::default() + }, + error: r#"`call 42` failed when calling the local or imported function `42`."#, + ); + + test!( + test_interpreter_call__void = + instructions: [ + Instruction::ArgumentGet(1), + Instruction::ArgumentGet(0), + Instruction::Call(42), + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance { + locals_or_imports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + 42, + LocalImport { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Ok(vec![]), + // ^^^^^^^^^^ void fails + }, + ); + + hashmap + }, + ..Default::default() + }, + stack: [], + ); } From ef568ca8c4110d019db0a141724d0c3d0e049bd5 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 25 Sep 2019 23:29:08 +0200 Subject: [PATCH 30/84] feat(interface-types) Update `Instruction`. --- lib/interface-types/src/decoders/binary.rs | 53 ++++++---- lib/interface-types/src/encoders/wat.rs | 49 ++++++---- .../src/instructions/interpreter.rs | 98 +++++++++---------- lib/interface-types/src/instructions/mod.rs | 8 +- lib/interface-types/src/lib.rs | 20 ++-- 5 files changed, 130 insertions(+), 98 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index cef85ffed..2b585dd76 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -133,24 +133,39 @@ fn instructions<'input, E: ParseError<&'input [u8]>>( Ok(match opcode { 0x00 => { consume!((input, argument_0) = leb(input)?); - (input, Instruction::ArgumentGet(argument_0)) + (input, Instruction::ArgumentGet { index: argument_0 }) } 0x01 => { consume!((input, argument_0) = leb(input)?); - (input, Instruction::Call(argument_0 as usize)) + ( + input, + Instruction::Call { + function_index: argument_0 as usize, + }, + ) } 0x02 => { consume!((input, argument_0) = string(input)?); - (input, Instruction::CallExport(argument_0)) + ( + input, + Instruction::CallExport { + export_name: argument_0, + }, + ) } 0x03 => (input, Instruction::ReadUtf8), 0x04 => { consume!((input, argument_0) = string(input)?); - (input, Instruction::WriteUtf8(argument_0)) + ( + input, + Instruction::WriteUtf8 { + allocator_name: argument_0, + }, + ) } 0x05 => { @@ -499,11 +514,11 @@ mod tests { fn test_instructions() { let input = &[ 0x14, // list of 20 items - 0x00, 0x01, // ArgumentGet(1) - 0x01, 0x01, // Call(1) - 0x02, 0x03, 0x61, 0x62, 0x63, // CallExport("abc") + 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("abc") + 0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" } 0x05, 0xff, 0xff, 0x01, // AsWasm(Int) 0x06, 0x7e, // AsInterface(I64) 0x07, // TableRefAdd @@ -524,11 +539,13 @@ mod tests { let output = Ok(( &[0x0a][..], vec![ - Instruction::ArgumentGet(1), - Instruction::Call(1), - Instruction::CallExport("abc"), + Instruction::ArgumentGet { index: 1 }, + Instruction::Call { function_index: 1 }, + Instruction::CallExport { export_name: "abc" }, Instruction::ReadUtf8, - Instruction::WriteUtf8("abc"), + Instruction::WriteUtf8 { + allocator_name: "abc", + }, Instruction::AsWasm(InterfaceType::Int), Instruction::AsInterface(InterfaceType::I64), Instruction::TableRefAdd, @@ -667,7 +684,7 @@ mod tests { 0x01, // list of 1 item 0x7f, // I32 0x01, // list of 1 item - 0x00, 0x01, // ArgumentGet(1) + 0x00, 0x01, // ArgumentGet { index: 1 } 0x01, // adapter kind: export 0x01, // string of 1 byte 0x63, // "c" @@ -676,7 +693,7 @@ mod tests { 0x01, // list of 1 item 0x7f, // I32 0x01, // list of 1 item - 0x00, 0x01, // ArgumentGet(1) + 0x00, 0x01, // ArgumentGet { index: 1 } 0x02, // adapter kind: helper function 0x01, // string of 1 byte 0x64, // "d" @@ -685,7 +702,7 @@ mod tests { 0x01, // list of 1 item 0x7f, // I32 0x01, // list of 1 item - 0x00, 0x01, // ArgumentGet(1) + 0x00, 0x01, // ArgumentGet { index: 1 } ]; let output = Ok(( &[] as &[u8], @@ -695,19 +712,19 @@ mod tests { name: "b", input_types: vec![InterfaceType::I32], output_types: vec![InterfaceType::I32], - instructions: vec![Instruction::ArgumentGet(1)], + instructions: vec![Instruction::ArgumentGet { index: 1 }], }, Adapter::Export { name: "c", input_types: vec![InterfaceType::I32], output_types: vec![InterfaceType::I32], - instructions: vec![Instruction::ArgumentGet(1)], + instructions: vec![Instruction::ArgumentGet { index: 1 }], }, Adapter::HelperFunction { name: "d", input_types: vec![InterfaceType::I32], output_types: vec![InterfaceType::I32], - instructions: vec![Instruction::ArgumentGet(1)], + instructions: vec![Instruction::ArgumentGet { index: 1 }], }, ], )); diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index f04712a5c..65684d885 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -23,11 +23,13 @@ impl From<&InterfaceType> for String { impl<'input> From<&Instruction<'input>> for String { fn from(instruction: &Instruction) -> Self { match instruction { - Instruction::ArgumentGet(index) => format!("arg.get {}", index), - Instruction::Call(index) => format!("call {}", index), - Instruction::CallExport(export_name) => format!(r#"call-export "{}""#, export_name), + Instruction::ArgumentGet { index } => format!("arg.get {}", index), + Instruction::Call { function_index } => format!("call {}", function_index), + Instruction::CallExport { export_name } => format!(r#"call-export "{}""#, export_name), Instruction::ReadUtf8 => "read-utf8".into(), - Instruction::WriteUtf8(string) => format!(r#"write-utf8 "{}""#, string), + Instruction::WriteUtf8 { allocator_name } => { + format!(r#"write-utf8 "{}""#, allocator_name) + } Instruction::AsWasm(interface_type) => { format!("as-wasm {}", String::from(interface_type)) } @@ -294,11 +296,14 @@ mod tests { #[test] fn test_instructions() { let inputs: Vec = vec![ - (&Instruction::ArgumentGet(7)).into(), - (&Instruction::Call(7)).into(), - (&Instruction::CallExport("foo")).into(), + (&Instruction::ArgumentGet { index: 7 }).into(), + (&Instruction::Call { function_index: 7 }).into(), + (&Instruction::CallExport { export_name: "foo" }).into(), (&Instruction::ReadUtf8).into(), - (&Instruction::WriteUtf8("foo")).into(), + (&Instruction::WriteUtf8 { + allocator_name: "foo", + }) + .into(), (&Instruction::AsWasm(InterfaceType::Int)).into(), (&Instruction::AsInterface(InterfaceType::AnyRef)).into(), (&Instruction::TableRefAdd).into(), @@ -438,9 +443,11 @@ mod tests { input_types: vec![InterfaceType::I32, InterfaceType::F32], output_types: vec![InterfaceType::I32], instructions: vec![ - Instruction::ArgumentGet(0), - Instruction::WriteUtf8("hello"), - Instruction::CallExport("f"), + Instruction::ArgumentGet { index: 0 }, + Instruction::WriteUtf8 { + allocator_name: "hello", + }, + Instruction::CallExport { export_name: "f" }, ], }) .into(), @@ -449,7 +456,7 @@ mod tests { name: "foo", input_types: vec![InterfaceType::I32], output_types: vec![], - instructions: vec![Instruction::CallExport("f")], + instructions: vec![Instruction::CallExport { export_name: "f" }], }) .into(), (&Adapter::Import { @@ -457,7 +464,7 @@ mod tests { name: "foo", input_types: vec![], output_types: vec![InterfaceType::I32], - instructions: vec![Instruction::CallExport("f")], + instructions: vec![Instruction::CallExport { export_name: "f" }], }) .into(), (&Adapter::Export { @@ -465,9 +472,11 @@ mod tests { input_types: vec![InterfaceType::I32, InterfaceType::F32], output_types: vec![InterfaceType::I32], instructions: vec![ - Instruction::ArgumentGet(0), - Instruction::WriteUtf8("hello"), - Instruction::CallExport("f"), + Instruction::ArgumentGet { index: 0 }, + Instruction::WriteUtf8 { + allocator_name: "hello", + }, + Instruction::CallExport { export_name: "f" }, ], }) .into(), @@ -475,14 +484,14 @@ mod tests { name: "foo", input_types: vec![InterfaceType::I32], output_types: vec![], - instructions: vec![Instruction::CallExport("f")], + instructions: vec![Instruction::CallExport { export_name: "f" }], }) .into(), (&Adapter::Export { name: "foo", input_types: vec![], output_types: vec![InterfaceType::I32], - instructions: vec![Instruction::CallExport("f")], + instructions: vec![Instruction::CallExport { export_name: "f" }], }) .into(), ]; @@ -560,13 +569,13 @@ mod tests { name: "foo", input_types: vec![InterfaceType::I32], output_types: vec![], - instructions: vec![Instruction::ArgumentGet(42)], + instructions: vec![Instruction::ArgumentGet { index: 42 }], }, Adapter::Export { name: "bar", input_types: vec![], output_types: vec![], - instructions: vec![Instruction::ArgumentGet(42)], + instructions: vec![Instruction::ArgumentGet { index: 42 }], }, ], forwards: vec![Forward { name: "main" }], diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index e0cc0f32a..5950dcdb2 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -88,7 +88,7 @@ where .map( |instruction| -> ExecutableInstruction { match instruction { - Instruction::ArgumentGet(index) => { + Instruction::ArgumentGet { index } => { let index = index.to_owned(); let instruction_name: String = instruction.into(); @@ -107,7 +107,7 @@ where Ok(()) }) } - Instruction::CallExport(export_name) => { + Instruction::CallExport { export_name } => { let export_name = (*export_name).to_owned(); let instruction_name: String = instruction.into(); @@ -215,7 +215,7 @@ where } }) } - Instruction::Call(index) => { + Instruction::Call { function_index: index } => { let index = index.to_owned(); let instruction_name: String = instruction.into(); @@ -442,11 +442,11 @@ mod tests { #[test] fn test_interpreter_from_instructions() { let instructions = vec![ - Instruction::ArgumentGet(0), - Instruction::ArgumentGet(0), - Instruction::CallExport("foo"), + Instruction::ArgumentGet { index: 0 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "foo" }, Instruction::ReadUtf8, - Instruction::Call(7), + Instruction::Call { function_index: 7 }, ]; let interpreter: Interpreter<(), (), (), ()> = (&instructions).try_into().unwrap(); @@ -509,7 +509,7 @@ mod tests { test!( test_interpreter_argument_get = - instructions: [Instruction::ArgumentGet(0)], + instructions: [Instruction::ArgumentGet { index: 0 }], invocation_inputs: [InterfaceValue::I32(42)], instance: Instance::new(), stack: [InterfaceValue::I32(42)], @@ -518,8 +518,8 @@ mod tests { test!( test_interpreter_argument_get__twice = instructions: [ - Instruction::ArgumentGet(0), - Instruction::ArgumentGet(1), + Instruction::ArgumentGet { index: 0 }, + Instruction::ArgumentGet { index: 1 }, ], invocation_inputs: [ InterfaceValue::I32(7), @@ -534,7 +534,7 @@ mod tests { test!( test_interpreter_argument_get__invalid_index = - instructions: [Instruction::ArgumentGet(1)], + instructions: [Instruction::ArgumentGet { index: 1 }], invocation_inputs: [InterfaceValue::I32(42)], instance: Instance::new(), error: "`arg.get 1` cannot access argument #1 because it doesn't exist." @@ -543,9 +543,9 @@ mod tests { test!( test_interpreter_call_export = instructions: [ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::CallExport("sum"), + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "sum" }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -557,7 +557,7 @@ mod tests { test!( test_interpreter_call_export__invalid_export_name = - instructions: [Instruction::CallExport("bar")], + instructions: [Instruction::CallExport { export_name: "bar" }], invocation_inputs: [], instance: Instance::new(), error: r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#, @@ -566,8 +566,8 @@ mod tests { test!( test_interpreter_call_export__stack_is_too_small = instructions: [ - Instruction::ArgumentGet(0), - Instruction::CallExport("sum"), + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "sum" }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -580,9 +580,9 @@ mod tests { test!( test_interpreter_call_export__invalid_types_in_the_stack = instructions: [ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::CallExport("sum"), + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "sum" }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -596,9 +596,9 @@ mod tests { test!( test_interpreter_call_export__failure_when_calling = instructions: [ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::CallExport("sum"), + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "sum" }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -627,9 +627,9 @@ mod tests { test!( test_interpreter_call_export__void = instructions: [ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::CallExport("sum"), + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "sum" }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -658,8 +658,8 @@ mod tests { test!( test_interpreter_read_utf8 = instructions: [ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, Instruction::ReadUtf8, ], invocation_inputs: [ @@ -678,8 +678,8 @@ mod tests { test!( test_interpreter_read_utf8__read_out_of_memory = instructions: [ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, Instruction::ReadUtf8, ], invocation_inputs: [ @@ -698,8 +698,8 @@ mod tests { test!( test_interpreter_read_utf8__invalid_encoding = instructions: [ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, Instruction::ReadUtf8, ], invocation_inputs: [ @@ -718,7 +718,7 @@ mod tests { test!( test_interpreter_read_utf8__stack_is_too_small = instructions: [ - Instruction::ArgumentGet(0), + Instruction::ArgumentGet { index: 0 }, Instruction::ReadUtf8, // ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present. ], @@ -733,9 +733,9 @@ mod tests { test!( test_interpreter_call = instructions: [ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::Call(42), + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::Call { function_index: 42 }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -748,7 +748,7 @@ mod tests { test!( test_interpreter_call__invalid_local_import_index = instructions: [ - Instruction::Call(42), + Instruction::Call { function_index: 42 }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -761,9 +761,9 @@ mod tests { test!( test_interpreter_call__stack_is_too_small = instructions: [ - Instruction::ArgumentGet(0), - Instruction::Call(42), - // ^^ `42` expects 2 values on the stack, only one is present + Instruction::ArgumentGet { index: 0 }, + Instruction::Call { function_index: 42 }, + // ^^ `42` expects 2 values on the stack, only one is present ], invocation_inputs: [ InterfaceValue::I32(3), @@ -776,9 +776,9 @@ mod tests { test!( test_interpreter_call__invalid_types_in_the_stack = instructions: [ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::Call(42), + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::Call { function_index: 42 }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -792,9 +792,9 @@ mod tests { test!( test_interpreter_call__failure_when_calling = instructions: [ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::Call(42), + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::Call { function_index: 42 }, ], invocation_inputs: [ InterfaceValue::I32(3), @@ -823,9 +823,9 @@ mod tests { test!( test_interpreter_call__void = instructions: [ - Instruction::ArgumentGet(1), - Instruction::ArgumentGet(0), - Instruction::Call(42), + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::Call { function_index: 42 }, ], invocation_inputs: [ InterfaceValue::I32(3), diff --git a/lib/interface-types/src/instructions/mod.rs b/lib/interface-types/src/instructions/mod.rs index 89b98f9ed..173e4af40 100644 --- a/lib/interface-types/src/instructions/mod.rs +++ b/lib/interface-types/src/instructions/mod.rs @@ -6,11 +6,11 @@ pub mod wasm; #[derive(PartialEq, Debug)] pub enum Instruction<'input> { - ArgumentGet(u64), - Call(usize), - CallExport(&'input str), + ArgumentGet { index: u64 }, + Call { function_index: usize }, + CallExport { export_name: &'input str }, ReadUtf8, - WriteUtf8(&'input str), + WriteUtf8 { allocator_name: &'input str }, AsWasm(InterfaceType), AsInterface(InterfaceType), TableRefAdd, diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 3760c923f..748575cea 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -82,11 +82,13 @@ mod tests { input_types: vec![InterfaceType::I32], output_types: vec![], instructions: vec![ - Instruction::ArgumentGet(0), - Instruction::ArgumentGet(0), - Instruction::CallExport("strlen"), + Instruction::ArgumentGet { index: 0 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { + export_name: "strlen" + }, Instruction::ReadUtf8, - Instruction::Call(0), + Instruction::Call { function_index: 0 }, ] }, Adapter::Import { @@ -95,9 +97,13 @@ mod tests { input_types: vec![], output_types: vec![InterfaceType::I32], instructions: vec![ - Instruction::Call(1), - Instruction::WriteUtf8("alloc"), - Instruction::CallExport("write_null_byte"), + Instruction::Call { function_index: 1 }, + Instruction::WriteUtf8 { + allocator_name: "alloc" + }, + Instruction::CallExport { + export_name: "write_null_byte" + }, ] } ], From 981692ec15cd946786aba4f3dd361e18743bd1dc Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 25 Sep 2019 23:30:41 +0200 Subject: [PATCH 31/84] chore(interface-types) Re-order match arms. --- .../src/instructions/interpreter.rs | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 5950dcdb2..9781d48aa 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -107,6 +107,65 @@ where Ok(()) }) } + Instruction::Call { function_index: index } => { + let index = index.to_owned(); + let instruction_name: String = instruction.into(); + + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + let instance = runtime.wasm_instance; + let function_index = FunctionIndex::new(index); + + match instance.local_or_import(function_index) { + Some(local_or_import) => { + let inputs_cardinality = local_or_import.inputs_cardinality(); + + match runtime.stack.pop(inputs_cardinality) { + Some(inputs) => { + let input_types = inputs + .iter() + .map(|input| input.into()) + .collect::>(); + + if input_types != local_or_import.inputs() { + return Err(format!( + "`{}` cannot call the local or imported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).", + instruction_name, + index, + local_or_import.inputs(), + )) + } + + match local_or_import.call(&inputs) { + Ok(outputs) => { + for output in outputs.iter() { + runtime.stack.push(output.clone()); + } + + Ok(()) + } + Err(_) => Err(format!( + "`{}` failed when calling the local or imported function `{}`.", + instruction_name, + index + )) + } + } + None => Err(format!( + "`{}` cannot call the local or imported function `{}` because there is no enough data on the stack for the arguments (needs {}).", + instruction_name, + index, + inputs_cardinality, + )) + } + } + None => Err(format!( + "`{}` cannot call the local or imported function `{}` because it doesn't exist.", + instruction_name, + index, + )) + } + }) + } Instruction::CallExport { export_name } => { let export_name = (*export_name).to_owned(); let instruction_name: String = instruction.into(); @@ -215,65 +274,6 @@ where } }) } - Instruction::Call { function_index: index } => { - let index = index.to_owned(); - let instruction_name: String = instruction.into(); - - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { - let instance = runtime.wasm_instance; - let function_index = FunctionIndex::new(index); - - match instance.local_or_import(function_index) { - Some(local_or_import) => { - let inputs_cardinality = local_or_import.inputs_cardinality(); - - match runtime.stack.pop(inputs_cardinality) { - Some(inputs) => { - let input_types = inputs - .iter() - .map(|input| input.into()) - .collect::>(); - - if input_types != local_or_import.inputs() { - return Err(format!( - "`{}` cannot call the local or imported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).", - instruction_name, - index, - local_or_import.inputs(), - )) - } - - match local_or_import.call(&inputs) { - Ok(outputs) => { - for output in outputs.iter() { - runtime.stack.push(output.clone()); - } - - Ok(()) - } - Err(_) => Err(format!( - "`{}` failed when calling the local or imported function `{}`.", - instruction_name, - index - )) - } - } - None => Err(format!( - "`{}` cannot call the local or imported function `{}` because there is no enough data on the stack for the arguments (needs {}).", - instruction_name, - index, - inputs_cardinality, - )) - } - } - None => Err(format!( - "`{}` cannot call the local or imported function `{}` because it doesn't exist.", - instruction_name, - index, - )) - } - }) - } _ => unimplemented!(), } }, From 49a7587f335c36774af48a524c949bdecc1532ca Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Sep 2019 00:55:26 +0200 Subject: [PATCH 32/84] feat(interface-types) Implement the `write-utf8` executable instruction. --- .../src/instructions/interpreter.rs | 196 +++++++++++++++++- lib/interface-types/src/instructions/wasm.rs | 5 +- 2 files changed, 191 insertions(+), 10 deletions(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 9781d48aa..0c8a55fef 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -3,7 +3,11 @@ use crate::instructions::{ wasm::{self, FunctionIndex, InterfaceType, InterfaceValue, TypedIndex}, Instruction, }; -use std::{cell::Cell, convert::TryFrom, marker::PhantomData}; +use std::{ + cell::Cell, + convert::{TryFrom, TryInto}, + marker::PhantomData, +}; struct Runtime<'invocation, 'instance, Instance, Export, LocalImport, Memory> where @@ -15,9 +19,9 @@ where invocation_inputs: &'invocation [InterfaceValue], stack: Stack, wasm_instance: &'instance Instance, - wasm_exports: PhantomData, - wasm_locals_or_imports: PhantomData, - wasm_memories: PhantomData, + _wasm_exports: PhantomData, + _wasm_locals_or_imports: PhantomData, + _wasm_memories: PhantomData, } type ExecutableInstruction = @@ -56,9 +60,9 @@ where invocation_inputs, stack: Stack::new(), wasm_instance, - wasm_exports: PhantomData, - wasm_locals_or_imports: PhantomData, - wasm_memories: PhantomData, + _wasm_exports: PhantomData, + _wasm_locals_or_imports: PhantomData, + _wasm_memories: PhantomData, }; for executable_instruction in self.iter() { @@ -274,6 +278,76 @@ where } }) } + Instruction::WriteUtf8 { allocator_name } => { + let allocator_name = (*allocator_name).to_owned(); + let instruction_name: String = instruction.into(); + + Box::new(move |runtime: &mut Runtime| -> Result<(), _> { + let instance = runtime.wasm_instance; + + match instance.export(&allocator_name) { + Some(allocator) => { + if allocator.inputs() != [InterfaceType::I32] || + allocator.outputs() != [InterfaceType::I32] { + return Err(format!( + "`{}` failed because the allocator `{}` has an invalid signature (expects [I32] -> [I32]).", + instruction_name, + allocator_name, + )) + } + + match runtime.wasm_instance.memory(0) { + Some(memory) => match runtime.stack.pop1() { + Some(string) => { + let memory_view = memory.view::(); + + let string: String = (&string).try_into()?; + let string_bytes = string.as_bytes(); + let string_length = (string_bytes.len() as i32) + .try_into() + .map_err(|error| format!("{}", error))?; + + match allocator.call(&[InterfaceValue::I32(string_length)]) { + Ok(outputs) => { + let string_pointer: i32 = (&outputs[0]).try_into()?; + + for (nth, byte) in string_bytes.iter().enumerate() { + memory_view[string_pointer as usize + nth].set(*byte); + } + + runtime.stack.push(InterfaceValue::I32(string_pointer)); + runtime.stack.push(InterfaceValue::I32(string_length)); + + Ok(()) + } + Err(_) => Err(format!( + "`{}` failed when calling the allocator `{}`.", + instruction_name, + allocator_name, + )) + } + } + None => Err(format!( + "`{}` cannot call the allocator `{}` because there is no enough data on the stack for the arguments (needs {}).", + instruction_name, + allocator_name, + 1 + )) + } + None => Err(format!( + "`{}` failed because there is no memory to write into.", + instruction_name + )) + } + } + None => Err(format!( + "`{}` failed because the exported function `{}` (the allocator) doesn't exist.", + instruction_name, + allocator_name + )) + } + }) + } _ => unimplemented!(), } }, @@ -396,6 +470,18 @@ mod tests { }, }, ); + hashmap.insert( + "alloc".into(), + Export { + inputs: vec![InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |arguments: &[InterfaceValue]| { + let _size: i32 = (&arguments[0]).try_into().unwrap(); + + Ok(vec![InterfaceValue::I32(0)]) + }, + }, + ); hashmap }, @@ -417,7 +503,7 @@ mod tests { hashmap }, - memory: Memory::new(vec![]), + memory: Memory::new(vec![Cell::new(0); 128]), } } } @@ -850,4 +936,98 @@ mod tests { }, stack: [], ); + + test!( + test_interpreter_write_utf8 = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::WriteUtf8 { allocator_name: "alloc" }, + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: Instance::new(), + stack: [ + InterfaceValue::I32(0), + // ^^^^^^ pointer + InterfaceValue::I32(13), + // ^^^^^^^ length + ] + ); + + test!( + test_interpreter_write_utf8__roundtrip_with_read_utf8 = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::WriteUtf8 { allocator_name: "alloc" }, + Instruction::ReadUtf8, + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: Instance::new(), + stack: [InterfaceValue::String("Hello, World!".into())], + ); + + test!( + test_interpreter_write_utf8__allocator_does_not_exist = + instructions: [Instruction::WriteUtf8 { allocator_name: "alloc" }], + invocation_inputs: [], + instance: Instance { ..Default::default() }, + error: r#"`write-utf8 "alloc"` failed because the exported function `alloc` (the allocator) doesn't exist."#, + ); + + test!( + test_interpreter_write_utf8__stack_is_too_small = + instructions: [ + Instruction::WriteUtf8 { allocator_name: "alloc" } + // ^^^^^ `alloc` expects 1 value on the stack, none is present + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: Instance::new(), + error: r#"`write-utf8 "alloc"` cannot call the allocator `alloc` because there is no enough data on the stack for the arguments (needs 1)."#, + ); + + test!( + test_interpreter_write_utf8__failure_when_calling_the_allocator = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::WriteUtf8 { allocator_name: "alloc-fail" } + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: { + let mut instance = Instance::new(); + instance.exports.insert( + "alloc-fail".into(), + Export { + inputs: vec![InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Err(()), + // ^^^^^^^ function fails + }, + ); + + instance + }, + error: r#"`write-utf8 "alloc-fail"` failed when calling the allocator `alloc-fail`."#, + ); + + test!( + test_interpreter_write_utf8__invalid_allocator_signature = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::WriteUtf8 { allocator_name: "alloc-fail" } + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: { + let mut instance = Instance::new(); + instance.exports.insert( + "alloc-fail".into(), + Export { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![], + function: |_| Err(()), + }, + ); + + instance + }, + error: r#"`write-utf8 "alloc-fail"` failed because the allocator `alloc-fail` has an invalid signature (expects [I32] -> [I32])."#, + ); } diff --git a/lib/interface-types/src/instructions/wasm.rs b/lib/interface-types/src/instructions/wasm.rs index aa3090f18..014b95c5c 100644 --- a/lib/interface-types/src/instructions/wasm.rs +++ b/lib/interface-types/src/instructions/wasm.rs @@ -49,8 +49,8 @@ macro_rules! from_x_for_interface_value { type Error = &'static str; fn try_from(w: &InterfaceValue) -> Result { - match *w { - InterfaceValue::$value_variant(n) => Ok(n), + match w { + InterfaceValue::$value_variant(n) => Ok(n.clone()), _ => Err("Invalid cast."), } } @@ -58,6 +58,7 @@ macro_rules! from_x_for_interface_value { }; } +from_x_for_interface_value!(String, String); from_x_for_interface_value!(i32, I32); from_x_for_interface_value!(i64, I64); from_x_for_interface_value!(f32, F32); From 5ce18fc447f132de067d6469991f00aa91fd3ec4 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Sep 2019 01:00:17 +0200 Subject: [PATCH 33/84] feat(interface-types) Create vectors with specific capacity when possible. --- lib/interface-types/src/decoders/binary.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 2b585dd76..0c77e5269 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -100,7 +100,7 @@ fn list<'input, I, E: ParseError<&'input [u8]>>( return Err(Err::Error(make_error(input, ErrorKind::Eof))); } - let mut items = vec![]; + let mut items = Vec::with_capacity(length as usize); for _ in 0..length { consume!((input, item) = item_parser(input)?); @@ -247,10 +247,11 @@ fn exports<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { let mut input = input; - let mut exports = vec![]; consume!((input, number_of_exports) = leb(input)?); + let mut exports = Vec::with_capacity(number_of_exports as usize); + for _ in 0..number_of_exports { consume!((input, export_name) = string(input)?); consume!((input, export_input_types) = list(input, ty)?); @@ -270,10 +271,11 @@ fn types<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { let mut input = input; - let mut types = vec![]; consume!((input, number_of_types) = leb(input)?); + 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)?); @@ -293,10 +295,11 @@ fn imported_functions<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { let mut input = input; - let mut imported_functions = vec![]; consume!((input, number_of_imported_functions) = leb(input)?); + let mut imported_functions = Vec::with_capacity(number_of_imported_functions as usize); + for _ in 0..number_of_imported_functions { consume!((input, imported_function_namespace) = string(input)?); consume!((input, imported_function_name) = string(input)?); @@ -318,10 +321,11 @@ fn adapters<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { let mut input = input; - let mut adapters = vec![]; consume!((input, number_of_adapters) = leb(input)?); + let mut adapters = Vec::with_capacity(number_of_adapters as usize); + for _ in 0..number_of_adapters { consume!((input, adapter_kind) = byte(input)?); let adapter_kind = AdapterKind::try_from(adapter_kind) @@ -381,10 +385,11 @@ fn forwards<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { let mut input = input; - let mut forwards = vec![]; consume!((input, number_of_forwards) = leb(input)?); + let mut forwards = Vec::with_capacity(number_of_forwards as usize); + for _ in 0..number_of_forwards { consume!((input, forward_name) = string(input)?); From ade098b81544b38fed599491225e23078e286732 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Sep 2019 01:02:29 +0200 Subject: [PATCH 34/84] fix(interface-types) Fix typos in error messages. --- .../src/instructions/interpreter.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs index 0c8a55fef..79466d7f9 100644 --- a/lib/interface-types/src/instructions/interpreter.rs +++ b/lib/interface-types/src/instructions/interpreter.rs @@ -155,7 +155,7 @@ where } } None => Err(format!( - "`{}` cannot call the local or imported function `{}` because there is no enough data on the stack for the arguments (needs {}).", + "`{}` cannot call the local or imported function `{}` because there is not enough data on the stack for the arguments (needs {}).", instruction_name, index, inputs_cardinality, @@ -213,7 +213,7 @@ where } } None => Err(format!( - "`{}` cannot call the exported function `{}` because there is no enough data on the stack for the arguments (needs {}).", + "`{}` cannot call the exported function `{}` because there is not enough data on the stack for the arguments (needs {}).", instruction_name, export_name, inputs_cardinality, @@ -272,7 +272,7 @@ where )) } None => Err(format!( - "`{}` failed because there is no enough data on the stack (needs 2).", + "`{}` failed because there is not enough data on the stack (needs 2).", instruction_name, )) } @@ -328,7 +328,7 @@ where } } None => Err(format!( - "`{}` cannot call the allocator `{}` because there is no enough data on the stack for the arguments (needs {}).", + "`{}` cannot call the allocator `{}` because there is not enough data on the stack for the arguments (needs {}).", instruction_name, allocator_name, 1 @@ -660,7 +660,7 @@ mod tests { InterfaceValue::I32(4), ], instance: Instance::new(), - error: r#"`call-export "sum"` cannot call the exported function `sum` because there is no enough data on the stack for the arguments (needs 2)."#, + error: r#"`call-export "sum"` cannot call the exported function `sum` because there is not enough data on the stack for the arguments (needs 2)."#, ); test!( @@ -813,7 +813,7 @@ mod tests { InterfaceValue::I32(0), ], instance: Instance::new(), - error: r#"`read-utf8` failed because there is no enough data on the stack (needs 2)."#, + error: r#"`read-utf8` failed because there is not enough data on the stack (needs 2)."#, ); test!( @@ -856,7 +856,7 @@ mod tests { InterfaceValue::I32(4), ], instance: Instance::new(), - error: r#"`call 42` cannot call the local or imported function `42` because there is no enough data on the stack for the arguments (needs 2)."#, + error: r#"`call 42` cannot call the local or imported function `42` because there is not enough data on the stack for the arguments (needs 2)."#, ); test!( @@ -981,7 +981,7 @@ mod tests { ], invocation_inputs: [InterfaceValue::String("Hello, World!".into())], instance: Instance::new(), - error: r#"`write-utf8 "alloc"` cannot call the allocator `alloc` because there is no enough data on the stack for the arguments (needs 1)."#, + error: r#"`write-utf8 "alloc"` cannot call the allocator `alloc` because there is not enough data on the stack for the arguments (needs 1)."#, ); test!( From fce270a8e74da51b88d146b077d2b1b2bd506d25 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Sep 2019 14:14:46 +0200 Subject: [PATCH 35/84] feat(interface-types) Split the interpreter into multiple modules/files. --- lib/interface-types/src/ast.rs | 2 +- lib/interface-types/src/decoders/binary.rs | 2 +- lib/interface-types/src/encoders/wat.rs | 4 +- .../src/instructions/interpreter.rs | 1033 ----------------- .../mod.rs => interpreter/instruction.rs} | 4 - .../interpreter/instructions/argument_get.rs | 54 + .../src/interpreter/instructions/call.rs | 187 +++ .../interpreter/instructions/call_export.rs | 177 +++ .../src/interpreter/instructions/mod.rs | 177 +++ .../src/interpreter/instructions/read_utf8.rs | 131 +++ .../interpreter/instructions/write_utf8.rs | 169 +++ lib/interface-types/src/interpreter/mod.rs | 143 +++ .../{instructions => interpreter}/stack.rs | 0 .../src/interpreter/wasm/mod.rs | 2 + .../wasm/structures.rs} | 87 +- .../src/interpreter/wasm/values.rs | 85 ++ lib/interface-types/src/lib.rs | 4 +- lib/interface-types/src/macros.rs | 89 ++ 18 files changed, 1222 insertions(+), 1128 deletions(-) delete mode 100644 lib/interface-types/src/instructions/interpreter.rs rename lib/interface-types/src/{instructions/mod.rs => interpreter/instruction.rs} (93%) create mode 100644 lib/interface-types/src/interpreter/instructions/argument_get.rs create mode 100644 lib/interface-types/src/interpreter/instructions/call.rs create mode 100644 lib/interface-types/src/interpreter/instructions/call_export.rs create mode 100644 lib/interface-types/src/interpreter/instructions/mod.rs create mode 100644 lib/interface-types/src/interpreter/instructions/read_utf8.rs create mode 100644 lib/interface-types/src/interpreter/instructions/write_utf8.rs create mode 100644 lib/interface-types/src/interpreter/mod.rs rename lib/interface-types/src/{instructions => interpreter}/stack.rs (100%) create mode 100644 lib/interface-types/src/interpreter/wasm/mod.rs rename lib/interface-types/src/{instructions/wasm.rs => interpreter/wasm/structures.rs} (58%) create mode 100644 lib/interface-types/src/interpreter/wasm/values.rs diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index a3df0d8d8..2bcabfb55 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -1,4 +1,4 @@ -use crate::instructions::Instruction; +use crate::interpreter::Instruction; use std::str; #[derive(PartialEq, Debug)] diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 0c77e5269..9a1d77e47 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -1,4 +1,4 @@ -use crate::{ast::*, instructions::Instruction}; +use crate::{ast::*, interpreter::Instruction}; use nom::{ error::{make_error, ErrorKind, ParseError}, Err, IResult, diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 65684d885..89387ed4f 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -1,6 +1,6 @@ use crate::{ ast::{Adapter, Export, Forward, ImportedFunction, InterfaceType, Interfaces, Type}, - instructions::Instruction, + interpreter::Instruction, }; impl From<&InterfaceType> for String { @@ -270,7 +270,7 @@ impl<'input> From<&Interfaces<'input>> for String { #[cfg(test)] mod tests { - use crate::{ast::*, instructions::Instruction}; + use crate::{ast::*, interpreter::Instruction}; #[test] fn test_interface_types() { diff --git a/lib/interface-types/src/instructions/interpreter.rs b/lib/interface-types/src/instructions/interpreter.rs deleted file mode 100644 index 79466d7f9..000000000 --- a/lib/interface-types/src/instructions/interpreter.rs +++ /dev/null @@ -1,1033 +0,0 @@ -use crate::instructions::{ - stack::{Stack, Stackable}, - wasm::{self, FunctionIndex, InterfaceType, InterfaceValue, TypedIndex}, - Instruction, -}; -use std::{ - cell::Cell, - convert::{TryFrom, TryInto}, - marker::PhantomData, -}; - -struct Runtime<'invocation, 'instance, Instance, Export, LocalImport, Memory> -where - Export: wasm::Export + 'instance, - LocalImport: wasm::LocalImport + 'instance, - Memory: wasm::Memory + 'instance, - Instance: wasm::Instance + 'instance, -{ - invocation_inputs: &'invocation [InterfaceValue], - stack: Stack, - wasm_instance: &'instance Instance, - _wasm_exports: PhantomData, - _wasm_locals_or_imports: PhantomData, - _wasm_memories: PhantomData, -} - -type ExecutableInstruction = - Box) -> Result<(), String>>; - -pub struct Interpreter -where - Export: wasm::Export, - LocalImport: wasm::LocalImport, - Memory: wasm::Memory, - Instance: wasm::Instance, -{ - executable_instructions: Vec>, -} - -impl Interpreter -where - Export: wasm::Export, - LocalImport: wasm::LocalImport, - Memory: wasm::Memory, - Instance: wasm::Instance, -{ - fn iter( - &self, - ) -> impl Iterator> + '_ - { - self.executable_instructions.iter() - } - - pub fn run( - &self, - invocation_inputs: &[InterfaceValue], - wasm_instance: &Instance, - ) -> Result, String> { - let mut runtime = Runtime { - invocation_inputs, - stack: Stack::new(), - wasm_instance, - _wasm_exports: PhantomData, - _wasm_locals_or_imports: PhantomData, - _wasm_memories: PhantomData, - }; - - for executable_instruction in self.iter() { - match executable_instruction(&mut runtime) { - Ok(_) => continue, - Err(message) => return Err(message), - } - } - - Ok(runtime.stack) - } -} - -impl<'binary_input, Instance, Export, LocalImport, Memory> TryFrom<&Vec>> - for Interpreter -where - Export: wasm::Export, - LocalImport: wasm::LocalImport, - Memory: wasm::Memory, - Instance: wasm::Instance, -{ - type Error = String; - - fn try_from(instructions: &Vec) -> Result { - let executable_instructions = instructions - .iter() - .map( - |instruction| -> ExecutableInstruction { - match instruction { - Instruction::ArgumentGet { index } => { - let index = index.to_owned(); - let instruction_name: String = instruction.into(); - - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { - let invocation_inputs = runtime.invocation_inputs; - - if index >= (invocation_inputs.len() as u64) { - return Err(format!( - "`{}` cannot access argument #{} because it doesn't exist.", - instruction_name, index - )); - } - - runtime.stack.push(invocation_inputs[index as usize].clone()); - - Ok(()) - }) - } - Instruction::Call { function_index: index } => { - let index = index.to_owned(); - let instruction_name: String = instruction.into(); - - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { - let instance = runtime.wasm_instance; - let function_index = FunctionIndex::new(index); - - match instance.local_or_import(function_index) { - Some(local_or_import) => { - let inputs_cardinality = local_or_import.inputs_cardinality(); - - match runtime.stack.pop(inputs_cardinality) { - Some(inputs) => { - let input_types = inputs - .iter() - .map(|input| input.into()) - .collect::>(); - - if input_types != local_or_import.inputs() { - return Err(format!( - "`{}` cannot call the local or imported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).", - instruction_name, - index, - local_or_import.inputs(), - )) - } - - match local_or_import.call(&inputs) { - Ok(outputs) => { - for output in outputs.iter() { - runtime.stack.push(output.clone()); - } - - Ok(()) - } - Err(_) => Err(format!( - "`{}` failed when calling the local or imported function `{}`.", - instruction_name, - index - )) - } - } - None => Err(format!( - "`{}` cannot call the local or imported function `{}` because there is not enough data on the stack for the arguments (needs {}).", - instruction_name, - index, - inputs_cardinality, - )) - } - } - None => Err(format!( - "`{}` cannot call the local or imported function `{}` because it doesn't exist.", - instruction_name, - index, - )) - } - }) - } - Instruction::CallExport { export_name } => { - let export_name = (*export_name).to_owned(); - let instruction_name: String = instruction.into(); - - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { - let instance = runtime.wasm_instance; - - match instance.export(&export_name) { - Some(export) => { - let inputs_cardinality = export.inputs_cardinality(); - - match runtime.stack.pop(inputs_cardinality) { - Some(inputs) => { - let input_types = inputs - .iter() - .map(|input| input.into()) - .collect::>(); - - if input_types != export.inputs() { - return Err(format!( - "`{}` cannot call the exported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).", - instruction_name, - export_name, - export.inputs(), - )) - } - - match export.call(&inputs) { - Ok(outputs) => { - for output in outputs.iter() { - runtime.stack.push(output.clone()); - } - - Ok(()) - } - Err(_) => Err(format!( - "`{}` failed when calling the exported function `{}`.", - instruction_name, - export_name - )) - } - } - None => Err(format!( - "`{}` cannot call the exported function `{}` because there is not enough data on the stack for the arguments (needs {}).", - instruction_name, - export_name, - inputs_cardinality, - )) - } - } - None => Err(format!( - "`{}` cannot call the exported function `{}` because it doesn't exist.", - instruction_name, - export_name, - )) - } - }) - } - Instruction::ReadUtf8 => { - let instruction_name: String = instruction.into(); - - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { - match runtime.stack.pop(2) { - Some(inputs) => match runtime.wasm_instance.memory(0) { - Some(memory) => { - let length = i32::try_from(&inputs[0])? as usize; - let pointer = i32::try_from(&inputs[1])? as usize; - let memory_view = memory.view::(); - - if memory_view.len() < pointer + length { - return Err(format!( - "`{}` failed because it has to read out of the memory bounds (index {} > memory length {}).", - instruction_name, - pointer + length, - memory_view.len() - )); - } - - let data: Vec = (&memory_view[pointer..pointer + length]) - .iter() - .map(Cell::get) - .collect(); - - match String::from_utf8(data) { - Ok(string) => { - runtime.stack.push(InterfaceValue::String(string)); - - Ok(()) - } - Err(utf8_error) => Err(format!( - "`{}` failed because the read string isn't UTF-8 valid ({}).", - instruction_name, - utf8_error, - )) - } - } - None => Err(format!( - "`{}` failed because there is no memory to read.", - instruction_name - )) - } - None => Err(format!( - "`{}` failed because there is not enough data on the stack (needs 2).", - instruction_name, - )) - } - }) - } - Instruction::WriteUtf8 { allocator_name } => { - let allocator_name = (*allocator_name).to_owned(); - let instruction_name: String = instruction.into(); - - Box::new(move |runtime: &mut Runtime| -> Result<(), _> { - let instance = runtime.wasm_instance; - - match instance.export(&allocator_name) { - Some(allocator) => { - if allocator.inputs() != [InterfaceType::I32] || - allocator.outputs() != [InterfaceType::I32] { - return Err(format!( - "`{}` failed because the allocator `{}` has an invalid signature (expects [I32] -> [I32]).", - instruction_name, - allocator_name, - )) - } - - match runtime.wasm_instance.memory(0) { - Some(memory) => match runtime.stack.pop1() { - Some(string) => { - let memory_view = memory.view::(); - - let string: String = (&string).try_into()?; - let string_bytes = string.as_bytes(); - let string_length = (string_bytes.len() as i32) - .try_into() - .map_err(|error| format!("{}", error))?; - - match allocator.call(&[InterfaceValue::I32(string_length)]) { - Ok(outputs) => { - let string_pointer: i32 = (&outputs[0]).try_into()?; - - for (nth, byte) in string_bytes.iter().enumerate() { - memory_view[string_pointer as usize + nth].set(*byte); - } - - runtime.stack.push(InterfaceValue::I32(string_pointer)); - runtime.stack.push(InterfaceValue::I32(string_length)); - - Ok(()) - } - Err(_) => Err(format!( - "`{}` failed when calling the allocator `{}`.", - instruction_name, - allocator_name, - )) - } - } - None => Err(format!( - "`{}` cannot call the allocator `{}` because there is not enough data on the stack for the arguments (needs {}).", - instruction_name, - allocator_name, - 1 - )) - } - None => Err(format!( - "`{}` failed because there is no memory to write into.", - instruction_name - )) - } - } - None => Err(format!( - "`{}` failed because the exported function `{}` (the allocator) doesn't exist.", - instruction_name, - allocator_name - )) - } - }) - } - _ => unimplemented!(), - } - }, - ) - .collect(); - - Ok(Interpreter { - executable_instructions, - }) - } -} - -#[cfg(test)] -mod tests { - use super::Interpreter; - use crate::instructions::{ - stack::Stackable, - wasm::{self, InterfaceType, InterfaceValue}, - Instruction, - }; - use std::{cell::Cell, collections::HashMap, convert::TryInto}; - - struct Export { - inputs: Vec, - outputs: Vec, - function: fn(arguments: &[InterfaceValue]) -> Result, ()>, - } - - impl wasm::Export for Export { - fn inputs_cardinality(&self) -> usize { - self.inputs.len() as usize - } - - fn outputs_cardinality(&self) -> usize { - self.outputs.len() - } - - fn inputs(&self) -> &[InterfaceType] { - &self.inputs - } - - fn outputs(&self) -> &[InterfaceType] { - &self.outputs - } - - fn call(&self, arguments: &[InterfaceValue]) -> Result, ()> { - (self.function)(arguments) - } - } - - struct LocalImport { - inputs: Vec, - outputs: Vec, - function: fn(arguments: &[InterfaceValue]) -> Result, ()>, - } - - impl wasm::LocalImport for LocalImport { - fn inputs_cardinality(&self) -> usize { - self.inputs.len() as usize - } - - fn outputs_cardinality(&self) -> usize { - self.outputs.len() - } - - fn inputs(&self) -> &[InterfaceType] { - &self.inputs - } - - fn outputs(&self) -> &[InterfaceType] { - &self.outputs - } - - fn call(&self, arguments: &[InterfaceValue]) -> Result, ()> { - (self.function)(arguments) - } - } - - #[derive(Default)] - struct Memory { - data: Vec>, - } - - impl Memory { - fn new(data: Vec>) -> Self { - Self { data } - } - } - - impl wasm::Memory for Memory { - fn view(&self) -> &[Cell] { - let slice = self.data.as_slice(); - - unsafe { ::std::slice::from_raw_parts(slice.as_ptr() as *const Cell, slice.len()) } - } - } - - #[derive(Default)] - struct Instance { - exports: HashMap, - locals_or_imports: HashMap, - memory: Memory, - } - - impl Instance { - fn new() -> Self { - Self { - exports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - "sum".into(), - Export { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |arguments: &[InterfaceValue]| { - let a: i32 = (&arguments[0]).try_into().unwrap(); - let b: i32 = (&arguments[1]).try_into().unwrap(); - - Ok(vec![InterfaceValue::I32(a + b)]) - }, - }, - ); - hashmap.insert( - "alloc".into(), - Export { - inputs: vec![InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |arguments: &[InterfaceValue]| { - let _size: i32 = (&arguments[0]).try_into().unwrap(); - - Ok(vec![InterfaceValue::I32(0)]) - }, - }, - ); - - hashmap - }, - locals_or_imports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - 42, - LocalImport { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |arguments: &[InterfaceValue]| { - let a: i32 = (&arguments[0]).try_into().unwrap(); - let b: i32 = (&arguments[1]).try_into().unwrap(); - - Ok(vec![InterfaceValue::I32(a * b)]) - }, - }, - ); - - hashmap - }, - memory: Memory::new(vec![Cell::new(0); 128]), - } - } - } - - impl wasm::Instance for Instance { - fn export(&self, export_name: &str) -> Option<&Export> { - self.exports.get(export_name) - } - - fn local_or_import( - &self, - index: I, - ) -> Option<&LocalImport> { - self.locals_or_imports.get(&index.index()) - } - - fn memory(&self, _index: usize) -> Option<&Memory> { - Some(&self.memory) - } - } - - #[test] - fn test_interpreter_from_instructions() { - let instructions = vec![ - Instruction::ArgumentGet { index: 0 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "foo" }, - Instruction::ReadUtf8, - Instruction::Call { function_index: 7 }, - ]; - let interpreter: Interpreter<(), (), (), ()> = (&instructions).try_into().unwrap(); - - assert_eq!(interpreter.executable_instructions.len(), 5); - } - - macro_rules! test { - ( - $test_name:ident = - instructions: [ $($instructions:expr),* $(,)* ], - invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ], - instance: $instance:expr, - stack: [ $($stack:expr),* $(,)* ] - $(,)* - ) => { - #[test] - #[allow(non_snake_case)] - fn $test_name() { - let interpreter: Interpreter = - (&vec![$($instructions),*]).try_into().unwrap(); - - let invocation_inputs = vec![$($invocation_inputs),*]; - let instance = $instance; - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_ok()); - - let stack = run.unwrap(); - - assert_eq!(stack.as_slice(), &[$($stack),*]); - } - }; - - ( - $test_name:ident = - instructions: [ $($instructions:expr),* $(,)* ], - invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ], - instance: $instance:expr, - error: $error:expr - $(,)* - ) => { - #[test] - #[allow(non_snake_case)] - fn $test_name() { - let interpreter: Interpreter = - (&vec![$($instructions),*]).try_into().unwrap(); - - let invocation_inputs = vec![$($invocation_inputs),*]; - let instance = $instance; - let run = interpreter.run(&invocation_inputs, &instance); - - assert!(run.is_err()); - - let error = run.unwrap_err(); - - assert_eq!(error, String::from($error)); - } - }; - } - - test!( - test_interpreter_argument_get = - instructions: [Instruction::ArgumentGet { index: 0 }], - invocation_inputs: [InterfaceValue::I32(42)], - instance: Instance::new(), - stack: [InterfaceValue::I32(42)], - ); - - test!( - test_interpreter_argument_get__twice = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::ArgumentGet { index: 1 }, - ], - invocation_inputs: [ - InterfaceValue::I32(7), - InterfaceValue::I32(42), - ], - instance: Instance::new(), - stack: [ - InterfaceValue::I32(7), - InterfaceValue::I32(42), - ], - ); - - test!( - test_interpreter_argument_get__invalid_index = - instructions: [Instruction::ArgumentGet { index: 1 }], - invocation_inputs: [InterfaceValue::I32(42)], - instance: Instance::new(), - error: "`arg.get 1` cannot access argument #1 because it doesn't exist." - ); - - test!( - test_interpreter_call_export = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "sum" }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance::new(), - stack: [InterfaceValue::I32(7)], - ); - - test!( - test_interpreter_call_export__invalid_export_name = - instructions: [Instruction::CallExport { export_name: "bar" }], - invocation_inputs: [], - instance: Instance::new(), - error: r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#, - ); - - test!( - test_interpreter_call_export__stack_is_too_small = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "sum" }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance::new(), - error: r#"`call-export "sum"` cannot call the exported function `sum` because there is not enough data on the stack for the arguments (needs 2)."#, - ); - - test!( - test_interpreter_call_export__invalid_types_in_the_stack = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "sum" }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I64(4), - // ^^^ mismatch with `sum` signature - ], - instance: Instance::new(), - error: r#"`call-export "sum"` cannot call the exported function `sum` because the value types on the stack mismatch the function signature (expects [I32, I32])."#, - ); - - test!( - test_interpreter_call_export__failure_when_calling = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "sum" }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance { - exports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - "sum".into(), - Export { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Err(()), - // ^^^^^^^ function fails - }, - ); - - hashmap - }, - ..Default::default() - }, - error: r#"`call-export "sum"` failed when calling the exported function `sum`."#, - ); - - test!( - test_interpreter_call_export__void = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { export_name: "sum" }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance { - exports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - "sum".into(), - Export { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Ok(vec![]), - // ^^^^^^^^^^ void function - }, - ); - - hashmap - }, - ..Default::default() - }, - stack: [], - ); - - test!( - test_interpreter_read_utf8 = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::ReadUtf8, - ], - invocation_inputs: [ - InterfaceValue::I32(13), - // ^^^^^^^ length - InterfaceValue::I32(0), - // ^^^^^^ pointer - ], - instance: Instance { - memory: Memory::new("Hello, World!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), - ..Default::default() - }, - stack: [InterfaceValue::String("Hello, World!".into())], - ); - - test!( - test_interpreter_read_utf8__read_out_of_memory = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::ReadUtf8, - ], - invocation_inputs: [ - InterfaceValue::I32(13), - // ^^^^^^^ length is too long - InterfaceValue::I32(0), - // ^^^^^^ pointer - ], - instance: Instance { - memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), - ..Default::default() - }, - error: r#"`read-utf8` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#, - ); - - test!( - test_interpreter_read_utf8__invalid_encoding = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::ReadUtf8, - ], - invocation_inputs: [ - InterfaceValue::I32(4), - // ^^^^^^ length is too long - InterfaceValue::I32(0), - // ^^^^^^ pointer - ], - instance: Instance { - memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::>>()), - ..Default::default() - }, - error: r#"`read-utf8` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#, - ); - - test!( - test_interpreter_read_utf8__stack_is_too_small = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::ReadUtf8, - // ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present. - ], - invocation_inputs: [ - InterfaceValue::I32(13), - InterfaceValue::I32(0), - ], - instance: Instance::new(), - error: r#"`read-utf8` failed because there is not enough data on the stack (needs 2)."#, - ); - - test!( - test_interpreter_call = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::Call { function_index: 42 }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance::new(), - stack: [InterfaceValue::I32(12)], - ); - - test!( - test_interpreter_call__invalid_local_import_index = - instructions: [ - Instruction::Call { function_index: 42 }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance { ..Default::default() }, - error: r#"`call 42` cannot call the local or imported function `42` because it doesn't exist."#, - ); - - test!( - test_interpreter_call__stack_is_too_small = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::Call { function_index: 42 }, - // ^^ `42` expects 2 values on the stack, only one is present - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance::new(), - error: r#"`call 42` cannot call the local or imported function `42` because there is not enough data on the stack for the arguments (needs 2)."#, - ); - - test!( - test_interpreter_call__invalid_types_in_the_stack = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::Call { function_index: 42 }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I64(4), - // ^^^ mismatch with `42` signature - ], - instance: Instance::new(), - error: r#"`call 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#, - ); - - test!( - test_interpreter_call__failure_when_calling = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::Call { function_index: 42 }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance { - locals_or_imports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - 42, - LocalImport { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Err(()), - // ^^^^^^^ function fails - }, - ); - - hashmap - }, - ..Default::default() - }, - error: r#"`call 42` failed when calling the local or imported function `42`."#, - ); - - test!( - test_interpreter_call__void = - instructions: [ - Instruction::ArgumentGet { index: 1 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::Call { function_index: 42 }, - ], - invocation_inputs: [ - InterfaceValue::I32(3), - InterfaceValue::I32(4), - ], - instance: Instance { - locals_or_imports: { - let mut hashmap = HashMap::new(); - hashmap.insert( - 42, - LocalImport { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Ok(vec![]), - // ^^^^^^^^^^ void fails - }, - ); - - hashmap - }, - ..Default::default() - }, - stack: [], - ); - - test!( - test_interpreter_write_utf8 = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::WriteUtf8 { allocator_name: "alloc" }, - ], - invocation_inputs: [InterfaceValue::String("Hello, World!".into())], - instance: Instance::new(), - stack: [ - InterfaceValue::I32(0), - // ^^^^^^ pointer - InterfaceValue::I32(13), - // ^^^^^^^ length - ] - ); - - test!( - test_interpreter_write_utf8__roundtrip_with_read_utf8 = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::WriteUtf8 { allocator_name: "alloc" }, - Instruction::ReadUtf8, - ], - invocation_inputs: [InterfaceValue::String("Hello, World!".into())], - instance: Instance::new(), - stack: [InterfaceValue::String("Hello, World!".into())], - ); - - test!( - test_interpreter_write_utf8__allocator_does_not_exist = - instructions: [Instruction::WriteUtf8 { allocator_name: "alloc" }], - invocation_inputs: [], - instance: Instance { ..Default::default() }, - error: r#"`write-utf8 "alloc"` failed because the exported function `alloc` (the allocator) doesn't exist."#, - ); - - test!( - test_interpreter_write_utf8__stack_is_too_small = - instructions: [ - Instruction::WriteUtf8 { allocator_name: "alloc" } - // ^^^^^ `alloc` expects 1 value on the stack, none is present - ], - invocation_inputs: [InterfaceValue::String("Hello, World!".into())], - instance: Instance::new(), - error: r#"`write-utf8 "alloc"` cannot call the allocator `alloc` because there is not enough data on the stack for the arguments (needs 1)."#, - ); - - test!( - test_interpreter_write_utf8__failure_when_calling_the_allocator = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::WriteUtf8 { allocator_name: "alloc-fail" } - ], - invocation_inputs: [InterfaceValue::String("Hello, World!".into())], - instance: { - let mut instance = Instance::new(); - instance.exports.insert( - "alloc-fail".into(), - Export { - inputs: vec![InterfaceType::I32], - outputs: vec![InterfaceType::I32], - function: |_| Err(()), - // ^^^^^^^ function fails - }, - ); - - instance - }, - error: r#"`write-utf8 "alloc-fail"` failed when calling the allocator `alloc-fail`."#, - ); - - test!( - test_interpreter_write_utf8__invalid_allocator_signature = - instructions: [ - Instruction::ArgumentGet { index: 0 }, - Instruction::WriteUtf8 { allocator_name: "alloc-fail" } - ], - invocation_inputs: [InterfaceValue::String("Hello, World!".into())], - instance: { - let mut instance = Instance::new(); - instance.exports.insert( - "alloc-fail".into(), - Export { - inputs: vec![InterfaceType::I32, InterfaceType::I32], - outputs: vec![], - function: |_| Err(()), - }, - ); - - instance - }, - error: r#"`write-utf8 "alloc-fail"` failed because the allocator `alloc-fail` has an invalid signature (expects [I32] -> [I32])."#, - ); -} diff --git a/lib/interface-types/src/instructions/mod.rs b/lib/interface-types/src/interpreter/instruction.rs similarity index 93% rename from lib/interface-types/src/instructions/mod.rs rename to lib/interface-types/src/interpreter/instruction.rs index 173e4af40..e474d2daf 100644 --- a/lib/interface-types/src/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instruction.rs @@ -1,9 +1,5 @@ use crate::ast::InterfaceType; -pub mod interpreter; -mod stack; -pub mod wasm; - #[derive(PartialEq, Debug)] pub enum Instruction<'input> { ArgumentGet { index: u64 }, diff --git a/lib/interface-types/src/interpreter/instructions/argument_get.rs b/lib/interface-types/src/interpreter/instructions/argument_get.rs new file mode 100644 index 000000000..881930297 --- /dev/null +++ b/lib/interface-types/src/interpreter/instructions/argument_get.rs @@ -0,0 +1,54 @@ +executable_instruction!( + argument_get(index: u64, instruction_name: String) -> _ { + Box::new(move |runtime| -> _ { + let invocation_inputs = runtime.invocation_inputs; + + if index >= (invocation_inputs.len() as u64) { + return Err(format!( + "`{}` cannot access argument #{} because it doesn't exist.", + instruction_name, index + )); + } + + runtime.stack.push(invocation_inputs[index as usize].clone()); + + Ok(()) + }) + } +); + +#[cfg(test)] +mod tests { + test_executable_instruction!( + test_argument_get = + instructions: [Instruction::ArgumentGet { index: 0 }], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + stack: [InterfaceValue::I32(42)], + ); + + test_executable_instruction!( + test_argument_get__twice = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::ArgumentGet { index: 1 }, + ], + invocation_inputs: [ + InterfaceValue::I32(7), + InterfaceValue::I32(42), + ], + instance: Instance::new(), + stack: [ + InterfaceValue::I32(7), + InterfaceValue::I32(42), + ], + ); + + test_executable_instruction!( + test_argument_get__invalid_index = + instructions: [Instruction::ArgumentGet { index: 1 }], + invocation_inputs: [InterfaceValue::I32(42)], + instance: Instance::new(), + error: "`arg.get 1` cannot access argument #1 because it doesn't exist." + ); +} diff --git a/lib/interface-types/src/interpreter/instructions/call.rs b/lib/interface-types/src/interpreter/instructions/call.rs new file mode 100644 index 000000000..4a74655c3 --- /dev/null +++ b/lib/interface-types/src/interpreter/instructions/call.rs @@ -0,0 +1,187 @@ +use crate::interpreter::wasm::{ + structures::{FunctionIndex, TypedIndex}, + values::InterfaceType, +}; + +executable_instruction!( + call(function_index: usize, instruction_name: String) -> _ { + Box::new(move |runtime| -> _ { + let instance = runtime.wasm_instance; + let index = FunctionIndex::new(function_index); + + match instance.local_or_import(index) { + Some(local_or_import) => { + let inputs_cardinality = local_or_import.inputs_cardinality(); + + match runtime.stack.pop(inputs_cardinality) { + Some(inputs) => { + let input_types = inputs + .iter() + .map(|input| input.into()) + .collect::>(); + + if input_types != local_or_import.inputs() { + return Err(format!( + "`{}` cannot call the local or imported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).", + instruction_name, + function_index, + local_or_import.inputs(), + )) + } + + match local_or_import.call(&inputs) { + Ok(outputs) => { + for output in outputs.iter() { + runtime.stack.push(output.clone()); + } + + Ok(()) + } + Err(_) => Err(format!( + "`{}` failed when calling the local or imported function `{}`.", + instruction_name, + function_index + )) + } + } + None => Err(format!( + "`{}` cannot call the local or imported function `{}` because there is not enough data on the stack for the arguments (needs {}).", + instruction_name, + function_index, + inputs_cardinality, + )) + } + } + None => Err(format!( + "`{}` cannot call the local or imported function `{}` because it doesn't exist.", + instruction_name, + function_index, + )) + } + }) + } +); + +#[cfg(test)] +mod tests { + test_executable_instruction!( + test_call = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::Call { function_index: 42 }, + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance::new(), + stack: [InterfaceValue::I32(12)], + ); + + test_executable_instruction!( + test_call__invalid_local_import_index = + instructions: [ + Instruction::Call { function_index: 42 }, + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance { ..Default::default() }, + error: r#"`call 42` cannot call the local or imported function `42` because it doesn't exist."#, + ); + + test_executable_instruction!( + test_call__stack_is_too_small = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::Call { function_index: 42 }, + // ^^ `42` expects 2 values on the stack, only one is present + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance::new(), + error: r#"`call 42` cannot call the local or imported function `42` because there is not enough data on the stack for the arguments (needs 2)."#, + ); + + test_executable_instruction!( + test_call__invalid_types_in_the_stack = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::Call { function_index: 42 }, + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I64(4), + // ^^^ mismatch with `42` signature + ], + instance: Instance::new(), + error: r#"`call 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#, + ); + + test_executable_instruction!( + test_call__failure_when_calling = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::Call { function_index: 42 }, + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance { + locals_or_imports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + 42, + LocalImport { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Err(()), + // ^^^^^^^ function fails + }, + ); + + hashmap + }, + ..Default::default() + }, + error: r#"`call 42` failed when calling the local or imported function `42`."#, + ); + + test_executable_instruction!( + test_call__void = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::Call { function_index: 42 }, + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance { + locals_or_imports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + 42, + LocalImport { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Ok(vec![]), + // ^^^^^^^^^^ void fails + }, + ); + + hashmap + }, + ..Default::default() + }, + stack: [], + ); +} diff --git a/lib/interface-types/src/interpreter/instructions/call_export.rs b/lib/interface-types/src/interpreter/instructions/call_export.rs new file mode 100644 index 000000000..05de6470b --- /dev/null +++ b/lib/interface-types/src/interpreter/instructions/call_export.rs @@ -0,0 +1,177 @@ +use crate::interpreter::wasm::values::InterfaceType; + +executable_instruction!( + call_export(export_name: String, instruction_name: String) -> _ { + Box::new(move |runtime| -> _ { + let instance = runtime.wasm_instance; + + match instance.export(&export_name) { + Some(export) => { + let inputs_cardinality = export.inputs_cardinality(); + + match runtime.stack.pop(inputs_cardinality) { + Some(inputs) => { + let input_types = inputs + .iter() + .map(|input| input.into()) + .collect::>(); + + if input_types != export.inputs() { + return Err(format!( + "`{}` cannot call the exported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).", + instruction_name, + export_name, + export.inputs(), + )) + } + + match export.call(&inputs) { + Ok(outputs) => { + for output in outputs.iter() { + runtime.stack.push(output.clone()); + } + + Ok(()) + } + Err(_) => Err(format!( + "`{}` failed when calling the exported function `{}`.", + instruction_name, + export_name + )) + } + } + None => Err(format!( + "`{}` cannot call the exported function `{}` because there is not enough data on the stack for the arguments (needs {}).", + instruction_name, + export_name, + inputs_cardinality, + )) + } + } + None => Err(format!( + "`{}` cannot call the exported function `{}` because it doesn't exist.", + instruction_name, + export_name, + )) + } + }) + } +); + +#[cfg(test)] +mod tests { + test_executable_instruction!( + test_call_export = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "sum" }, + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance::new(), + stack: [InterfaceValue::I32(7)], + ); + + test_executable_instruction!( + test_call_export__invalid_export_name = + instructions: [Instruction::CallExport { export_name: "bar" }], + invocation_inputs: [], + instance: Instance::new(), + error: r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#, + ); + + test_executable_instruction!( + test_call_export__stack_is_too_small = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "sum" }, + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance::new(), + error: r#"`call-export "sum"` cannot call the exported function `sum` because there is not enough data on the stack for the arguments (needs 2)."#, + ); + + test_executable_instruction!( + test_call_export__invalid_types_in_the_stack = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "sum" }, + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I64(4), + // ^^^ mismatch with `sum` signature + ], + instance: Instance::new(), + error: r#"`call-export "sum"` cannot call the exported function `sum` because the value types on the stack mismatch the function signature (expects [I32, I32])."#, + ); + + test_executable_instruction!( + test_call_export__failure_when_calling = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "sum" }, + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance { + exports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + "sum".into(), + Export { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Err(()), + // ^^^^^^^ function fails + }, + ); + + hashmap + }, + ..Default::default() + }, + error: r#"`call-export "sum"` failed when calling the exported function `sum`."#, + ); + + test_executable_instruction!( + test_call_export__void = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "sum" }, + ], + invocation_inputs: [ + InterfaceValue::I32(3), + InterfaceValue::I32(4), + ], + instance: Instance { + exports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + "sum".into(), + Export { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Ok(vec![]), + // ^^^^^^^^^^ void function + }, + ); + + hashmap + }, + ..Default::default() + }, + stack: [], + ); +} diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs new file mode 100644 index 000000000..05c3f9723 --- /dev/null +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -0,0 +1,177 @@ +mod argument_get; +mod call; +mod call_export; +mod read_utf8; +mod write_utf8; + +pub(crate) use argument_get::argument_get; +pub(crate) use call::call; +pub(crate) use call_export::call_export; +pub(crate) use read_utf8::read_utf8; +pub(crate) use write_utf8::write_utf8; + +#[cfg(test)] +pub(crate) mod tests { + use crate::interpreter::wasm::{ + self, + values::{InterfaceType, InterfaceValue, ValueType}, + }; + use std::{cell::Cell, collections::HashMap, convert::TryInto}; + + pub(crate) struct Export { + pub(crate) inputs: Vec, + pub(crate) outputs: Vec, + pub(crate) function: fn(arguments: &[InterfaceValue]) -> Result, ()>, + } + + impl wasm::structures::Export for Export { + fn inputs_cardinality(&self) -> usize { + self.inputs.len() as usize + } + + fn outputs_cardinality(&self) -> usize { + self.outputs.len() + } + + fn inputs(&self) -> &[InterfaceType] { + &self.inputs + } + + fn outputs(&self) -> &[InterfaceType] { + &self.outputs + } + + fn call(&self, arguments: &[InterfaceValue]) -> Result, ()> { + (self.function)(arguments) + } + } + + pub(crate) struct LocalImport { + pub(crate) inputs: Vec, + pub(crate) outputs: Vec, + pub(crate) function: fn(arguments: &[InterfaceValue]) -> Result, ()>, + } + + impl wasm::structures::LocalImport for LocalImport { + fn inputs_cardinality(&self) -> usize { + self.inputs.len() as usize + } + + fn outputs_cardinality(&self) -> usize { + self.outputs.len() + } + + fn inputs(&self) -> &[InterfaceType] { + &self.inputs + } + + fn outputs(&self) -> &[InterfaceType] { + &self.outputs + } + + fn call(&self, arguments: &[InterfaceValue]) -> Result, ()> { + (self.function)(arguments) + } + } + + #[derive(Default)] + pub(crate) struct Memory { + pub(crate) data: Vec>, + } + + impl Memory { + pub(crate) fn new(data: Vec>) -> Self { + Self { data } + } + } + + impl wasm::structures::Memory for Memory { + fn view(&self) -> &[Cell] { + use std::slice; + + let slice = self.data.as_slice(); + + unsafe { slice::from_raw_parts(slice.as_ptr() as *const Cell, slice.len()) } + } + } + + #[derive(Default)] + pub(crate) struct Instance { + pub(crate) exports: HashMap, + pub(crate) locals_or_imports: HashMap, + pub(crate) memory: Memory, + } + + impl Instance { + pub(crate) fn new() -> Self { + Self { + exports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + "sum".into(), + Export { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |arguments: &[InterfaceValue]| { + let a: i32 = (&arguments[0]).try_into().unwrap(); + let b: i32 = (&arguments[1]).try_into().unwrap(); + + Ok(vec![InterfaceValue::I32(a + b)]) + }, + }, + ); + hashmap.insert( + "alloc".into(), + Export { + inputs: vec![InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |arguments: &[InterfaceValue]| { + let _size: i32 = (&arguments[0]).try_into().unwrap(); + + Ok(vec![InterfaceValue::I32(0)]) + }, + }, + ); + + hashmap + }, + locals_or_imports: { + let mut hashmap = HashMap::new(); + hashmap.insert( + 42, + LocalImport { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |arguments: &[InterfaceValue]| { + let a: i32 = (&arguments[0]).try_into().unwrap(); + let b: i32 = (&arguments[1]).try_into().unwrap(); + + Ok(vec![InterfaceValue::I32(a * b)]) + }, + }, + ); + + hashmap + }, + memory: Memory::new(vec![Cell::new(0); 128]), + } + } + } + + impl wasm::structures::Instance for Instance { + fn export(&self, export_name: &str) -> Option<&Export> { + self.exports.get(export_name) + } + + fn local_or_import( + &self, + index: I, + ) -> Option<&LocalImport> { + self.locals_or_imports.get(&index.index()) + } + + fn memory(&self, _index: usize) -> Option<&Memory> { + Some(&self.memory) + } + } +} diff --git a/lib/interface-types/src/interpreter/instructions/read_utf8.rs b/lib/interface-types/src/interpreter/instructions/read_utf8.rs new file mode 100644 index 000000000..8f195fd99 --- /dev/null +++ b/lib/interface-types/src/interpreter/instructions/read_utf8.rs @@ -0,0 +1,131 @@ +use crate::interpreter::wasm::values::InterfaceValue; +use std::{cell::Cell, convert::TryFrom}; + +executable_instruction!( + read_utf8(instruction_name: String) -> _ { + Box::new(move |runtime| -> _ { + match runtime.stack.pop(2) { + Some(inputs) => match runtime.wasm_instance.memory(0) { + Some(memory) => { + let length = i32::try_from(&inputs[0])? as usize; + let pointer = i32::try_from(&inputs[1])? as usize; + let memory_view = memory.view::(); + + if memory_view.len() < pointer + length { + return Err(format!( + "`{}` failed because it has to read out of the memory bounds (index {} > memory length {}).", + instruction_name, + pointer + length, + memory_view.len() + )); + } + + let data: Vec = (&memory_view[pointer..pointer + length]) + .iter() + .map(Cell::get) + .collect(); + + match String::from_utf8(data) { + Ok(string) => { + runtime.stack.push(InterfaceValue::String(string)); + + Ok(()) + } + Err(utf8_error) => Err(format!( + "`{}` failed because the read string isn't UTF-8 valid ({}).", + instruction_name, + utf8_error, + )) + } + } + None => Err(format!( + "`{}` failed because there is no memory to read.", + instruction_name + )) + } + None => Err(format!( + "`{}` failed because there is not enough data on the stack (needs 2).", + instruction_name, + )) + } + }) + } +); + +#[cfg(test)] +mod tests { + test_executable_instruction!( + test_read_utf8 = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::ReadUtf8, + ], + invocation_inputs: [ + InterfaceValue::I32(13), + // ^^^^^^^ length + InterfaceValue::I32(0), + // ^^^^^^ pointer + ], + instance: Instance { + memory: Memory::new("Hello, World!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), + ..Default::default() + }, + stack: [InterfaceValue::String("Hello, World!".into())], + ); + + test_executable_instruction!( + test_read_utf8__read_out_of_memory = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::ReadUtf8, + ], + invocation_inputs: [ + InterfaceValue::I32(13), + // ^^^^^^^ length is too long + InterfaceValue::I32(0), + // ^^^^^^ pointer + ], + instance: Instance { + memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), + ..Default::default() + }, + error: r#"`read-utf8` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#, + ); + + test_executable_instruction!( + test_read_utf8__invalid_encoding = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::ReadUtf8, + ], + invocation_inputs: [ + InterfaceValue::I32(4), + // ^^^^^^ length is too long + InterfaceValue::I32(0), + // ^^^^^^ pointer + ], + instance: Instance { + memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::>>()), + ..Default::default() + }, + error: r#"`read-utf8` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#, + ); + + test_executable_instruction!( + test_read_utf8__stack_is_too_small = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::ReadUtf8, + // ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present. + ], + invocation_inputs: [ + InterfaceValue::I32(13), + InterfaceValue::I32(0), + ], + instance: Instance::new(), + error: r#"`read-utf8` failed because there is not enough data on the stack (needs 2)."#, + ); +} diff --git a/lib/interface-types/src/interpreter/instructions/write_utf8.rs b/lib/interface-types/src/interpreter/instructions/write_utf8.rs new file mode 100644 index 000000000..fa66621b2 --- /dev/null +++ b/lib/interface-types/src/interpreter/instructions/write_utf8.rs @@ -0,0 +1,169 @@ +use crate::interpreter::wasm::values::{InterfaceType, InterfaceValue}; +use std::convert::TryInto; + +executable_instruction!( + write_utf8(allocator_name: String, instruction_name: String) -> _ { + Box::new(move |runtime| -> _ { + let instance = runtime.wasm_instance; + + match instance.export(&allocator_name) { + Some(allocator) => { + if allocator.inputs() != [InterfaceType::I32] || + allocator.outputs() != [InterfaceType::I32] { + return Err(format!( + "`{}` failed because the allocator `{}` has an invalid signature (expects [I32] -> [I32]).", + instruction_name, + allocator_name, + )) + } + + match runtime.wasm_instance.memory(0) { + Some(memory) => match runtime.stack.pop1() { + Some(string) => { + let memory_view = memory.view::(); + + let string: String = (&string).try_into()?; + let string_bytes = string.as_bytes(); + let string_length = (string_bytes.len() as i32) + .try_into() + .map_err(|error| format!("{}", error))?; + + match allocator.call(&[InterfaceValue::I32(string_length)]) { + Ok(outputs) => { + let string_pointer: i32 = (&outputs[0]).try_into()?; + + for (nth, byte) in string_bytes.iter().enumerate() { + memory_view[string_pointer as usize + nth].set(*byte); + } + + runtime.stack.push(InterfaceValue::I32(string_pointer)); + runtime.stack.push(InterfaceValue::I32(string_length)); + + Ok(()) + } + Err(_) => Err(format!( + "`{}` failed when calling the allocator `{}`.", + instruction_name, + allocator_name, + )) + } + } + None => Err(format!( + "`{}` cannot call the allocator `{}` because there is not enough data on the stack for the arguments (needs {}).", + instruction_name, + allocator_name, + 1 + )) + } + None => Err(format!( + "`{}` failed because there is no memory to write into.", + instruction_name + )) + } + } + None => Err(format!( + "`{}` failed because the exported function `{}` (the allocator) doesn't exist.", + instruction_name, + allocator_name + )) + } + }) + } +); + +#[cfg(test)] +mod tests { + test_executable_instruction!( + test_write_utf8 = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::WriteUtf8 { allocator_name: "alloc" }, + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: Instance::new(), + stack: [ + InterfaceValue::I32(0), + // ^^^^^^ pointer + InterfaceValue::I32(13), + // ^^^^^^^ length + ] + ); + + test_executable_instruction!( + test_write_utf8__roundtrip_with_read_utf8 = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::WriteUtf8 { allocator_name: "alloc" }, + Instruction::ReadUtf8, + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: Instance::new(), + stack: [InterfaceValue::String("Hello, World!".into())], + ); + + test_executable_instruction!( + test_write_utf8__allocator_does_not_exist = + instructions: [Instruction::WriteUtf8 { allocator_name: "alloc" }], + invocation_inputs: [], + instance: Instance { ..Default::default() }, + error: r#"`write-utf8 "alloc"` failed because the exported function `alloc` (the allocator) doesn't exist."#, + ); + + test_executable_instruction!( + test_write_utf8__stack_is_too_small = + instructions: [ + Instruction::WriteUtf8 { allocator_name: "alloc" } + // ^^^^^ `alloc` expects 1 value on the stack, none is present + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: Instance::new(), + error: r#"`write-utf8 "alloc"` cannot call the allocator `alloc` because there is not enough data on the stack for the arguments (needs 1)."#, + ); + + test_executable_instruction!( + test_write_utf8__failure_when_calling_the_allocator = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::WriteUtf8 { allocator_name: "alloc-fail" } + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: { + let mut instance = Instance::new(); + instance.exports.insert( + "alloc-fail".into(), + Export { + inputs: vec![InterfaceType::I32], + outputs: vec![InterfaceType::I32], + function: |_| Err(()), + // ^^^^^^^ function fails + }, + ); + + instance + }, + error: r#"`write-utf8 "alloc-fail"` failed when calling the allocator `alloc-fail`."#, + ); + + test_executable_instruction!( + test_write_utf8__invalid_allocator_signature = + instructions: [ + Instruction::ArgumentGet { index: 0 }, + Instruction::WriteUtf8 { allocator_name: "alloc-fail" } + ], + invocation_inputs: [InterfaceValue::String("Hello, World!".into())], + instance: { + let mut instance = Instance::new(); + instance.exports.insert( + "alloc-fail".into(), + Export { + inputs: vec![InterfaceType::I32, InterfaceType::I32], + outputs: vec![], + function: |_| Err(()), + }, + ); + + instance + }, + error: r#"`write-utf8 "alloc-fail"` failed because the allocator `alloc-fail` has an invalid signature (expects [I32] -> [I32])."#, + ); +} diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs new file mode 100644 index 000000000..e8b45bd57 --- /dev/null +++ b/lib/interface-types/src/interpreter/mod.rs @@ -0,0 +1,143 @@ +mod instruction; +mod instructions; +pub mod stack; +pub mod wasm; + +pub use instruction::Instruction; +use stack::Stack; +use std::{convert::TryFrom, marker::PhantomData}; +use wasm::values::InterfaceValue; + +pub(crate) struct Runtime<'invocation, 'instance, Instance, Export, LocalImport, Memory> +where + Export: wasm::structures::Export + 'instance, + LocalImport: wasm::structures::LocalImport + 'instance, + Memory: wasm::structures::Memory + 'instance, + Instance: wasm::structures::Instance + 'instance, +{ + invocation_inputs: &'invocation [InterfaceValue], + stack: Stack, + wasm_instance: &'instance Instance, + _wasm_exports: PhantomData, + _wasm_locals_or_imports: PhantomData, + _wasm_memories: PhantomData, +} + +pub(crate) type ExecutableInstruction = + Box) -> Result<(), String>>; + +pub struct Interpreter +where + Export: wasm::structures::Export, + LocalImport: wasm::structures::LocalImport, + Memory: wasm::structures::Memory, + Instance: wasm::structures::Instance, +{ + executable_instructions: Vec>, +} + +impl Interpreter +where + Export: wasm::structures::Export, + LocalImport: wasm::structures::LocalImport, + Memory: wasm::structures::Memory, + Instance: wasm::structures::Instance, +{ + fn iter( + &self, + ) -> impl Iterator> + '_ + { + self.executable_instructions.iter() + } + + pub fn run( + &self, + invocation_inputs: &[InterfaceValue], + wasm_instance: &Instance, + ) -> Result, String> { + let mut runtime = Runtime { + invocation_inputs, + stack: Stack::new(), + wasm_instance, + _wasm_exports: PhantomData, + _wasm_locals_or_imports: PhantomData, + _wasm_memories: PhantomData, + }; + + for executable_instruction in self.iter() { + match executable_instruction(&mut runtime) { + Ok(_) => continue, + Err(message) => return Err(message), + } + } + + Ok(runtime.stack) + } +} + +impl<'binary_input, Instance, Export, LocalImport, Memory> TryFrom<&Vec>> + for Interpreter +where + Export: wasm::structures::Export, + LocalImport: wasm::structures::LocalImport, + Memory: wasm::structures::Memory, + Instance: wasm::structures::Instance, +{ + type Error = String; + + fn try_from(instructions: &Vec) -> Result { + let executable_instructions = instructions + .iter() + .map( + |instruction| -> ExecutableInstruction { + let instruction_representation: String = instruction.into(); + + match instruction { + Instruction::ArgumentGet { index } => { + instructions::argument_get(*index, instruction_representation) + } + Instruction::Call { function_index } => { + instructions::call(*function_index, instruction_representation) + } + Instruction::CallExport { export_name } => instructions::call_export( + (*export_name).to_owned(), + instruction_representation, + ), + Instruction::ReadUtf8 => { + instructions::read_utf8(instruction_representation) + } + Instruction::WriteUtf8 { allocator_name } => instructions::write_utf8( + (*allocator_name).to_owned(), + instruction_representation, + ), + _ => unimplemented!(), + } + }, + ) + .collect(); + + Ok(Interpreter { + executable_instructions, + }) + } +} + +#[cfg(test)] +mod tests { + use super::{Instruction, Interpreter}; + use std::convert::TryInto; + + #[test] + fn test_interpreter_from_instructions() { + let instructions = vec![ + Instruction::ArgumentGet { index: 0 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::CallExport { export_name: "foo" }, + Instruction::ReadUtf8, + Instruction::Call { function_index: 7 }, + ]; + let interpreter: Interpreter<(), (), (), ()> = (&instructions).try_into().unwrap(); + + assert_eq!(interpreter.executable_instructions.len(), 5); + } +} diff --git a/lib/interface-types/src/instructions/stack.rs b/lib/interface-types/src/interpreter/stack.rs similarity index 100% rename from lib/interface-types/src/instructions/stack.rs rename to lib/interface-types/src/interpreter/stack.rs diff --git a/lib/interface-types/src/interpreter/wasm/mod.rs b/lib/interface-types/src/interpreter/wasm/mod.rs new file mode 100644 index 000000000..0ff75a9af --- /dev/null +++ b/lib/interface-types/src/interpreter/wasm/mod.rs @@ -0,0 +1,2 @@ +pub mod structures; +pub mod values; diff --git a/lib/interface-types/src/instructions/wasm.rs b/lib/interface-types/src/interpreter/wasm/structures.rs similarity index 58% rename from lib/interface-types/src/instructions/wasm.rs rename to lib/interface-types/src/interpreter/wasm/structures.rs index 014b95c5c..d1fa58349 100644 --- a/lib/interface-types/src/instructions/wasm.rs +++ b/lib/interface-types/src/interpreter/wasm/structures.rs @@ -1,88 +1,5 @@ -use std::{cell::Cell, convert::TryFrom}; - -pub use crate::ast::InterfaceType; - -#[derive(Debug, Clone, PartialEq)] -pub enum InterfaceValue { - Int(isize), - Float(f64), - Any(isize), - String(String), - // Seq(…), - I32(i32), - I64(i64), - F32(f32), - F64(f64), - // AnyRef(…), -} - -impl From<&InterfaceValue> for InterfaceType { - fn from(value: &InterfaceValue) -> Self { - match value { - InterfaceValue::Int(_) => Self::Int, - InterfaceValue::Float(_) => Self::Float, - InterfaceValue::Any(_) => Self::Any, - InterfaceValue::String(_) => Self::String, - InterfaceValue::I32(_) => Self::I32, - InterfaceValue::I64(_) => Self::I64, - InterfaceValue::F32(_) => Self::F32, - InterfaceValue::F64(_) => Self::F64, - } - } -} - -impl Default for InterfaceValue { - fn default() -> Self { - Self::I32(0) - } -} - -macro_rules! from_x_for_interface_value { - ($native_type:ty, $value_variant:ident) => { - impl From<$native_type> for InterfaceValue { - fn from(n: $native_type) -> Self { - Self::$value_variant(n) - } - } - - impl TryFrom<&InterfaceValue> for $native_type { - type Error = &'static str; - - fn try_from(w: &InterfaceValue) -> Result { - match w { - InterfaceValue::$value_variant(n) => Ok(n.clone()), - _ => Err("Invalid cast."), - } - } - } - }; -} - -from_x_for_interface_value!(String, String); -from_x_for_interface_value!(i32, I32); -from_x_for_interface_value!(i64, I64); -from_x_for_interface_value!(f32, F32); -from_x_for_interface_value!(f64, F64); - -pub trait ValueType: Copy -where - Self: Sized, -{ -} - -macro_rules! value_type { - ($native_type:ty) => { - impl ValueType for $native_type {} - }; - - ($($native_type:ty),*) => { - $( - value_type!($native_type); - )* - }; -} - -value_type!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); +use super::values::{InterfaceType, InterfaceValue, ValueType}; +use std::cell::Cell; pub trait TypedIndex: Copy + Clone { fn new(index: usize) -> Self; diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs new file mode 100644 index 000000000..1cfac911f --- /dev/null +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -0,0 +1,85 @@ +use std::convert::TryFrom; + +pub use crate::ast::InterfaceType; + +#[derive(Debug, Clone, PartialEq)] +pub enum InterfaceValue { + Int(isize), + Float(f64), + Any(isize), + String(String), + // Seq(…), + I32(i32), + I64(i64), + F32(f32), + F64(f64), + // AnyRef(…), +} + +impl From<&InterfaceValue> for InterfaceType { + fn from(value: &InterfaceValue) -> Self { + match value { + InterfaceValue::Int(_) => Self::Int, + InterfaceValue::Float(_) => Self::Float, + InterfaceValue::Any(_) => Self::Any, + InterfaceValue::String(_) => Self::String, + InterfaceValue::I32(_) => Self::I32, + InterfaceValue::I64(_) => Self::I64, + InterfaceValue::F32(_) => Self::F32, + InterfaceValue::F64(_) => Self::F64, + } + } +} + +impl Default for InterfaceValue { + fn default() -> Self { + Self::I32(0) + } +} + +macro_rules! from_x_for_interface_value { + ($native_type:ty, $value_variant:ident) => { + impl From<$native_type> for InterfaceValue { + fn from(n: $native_type) -> Self { + Self::$value_variant(n) + } + } + + impl TryFrom<&InterfaceValue> for $native_type { + type Error = &'static str; + + fn try_from(w: &InterfaceValue) -> Result { + match w { + InterfaceValue::$value_variant(n) => Ok(n.clone()), + _ => Err("Invalid cast."), + } + } + } + }; +} + +from_x_for_interface_value!(String, String); +from_x_for_interface_value!(i32, I32); +from_x_for_interface_value!(i64, I64); +from_x_for_interface_value!(f32, F32); +from_x_for_interface_value!(f64, F64); + +pub trait ValueType: Copy +where + Self: Sized, +{ +} + +macro_rules! value_type { + ($native_type:ty) => { + impl ValueType for $native_type {} + }; + + ($($native_type:ty),*) => { + $( + value_type!($native_type); + )* + }; +} + +value_type!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 748575cea..2185b2c15 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -3,13 +3,13 @@ pub mod ast; mod macros; pub mod decoders; pub mod encoders; -pub mod instructions; +pub mod interpreter; pub use decoders::binary::parse as parse_binary; #[cfg(test)] mod tests { - use crate::{ast::*, instructions::Instruction, parse_binary}; + use crate::{ast::*, interpreter::Instruction, parse_binary}; use std::fs; use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core as runtime; diff --git a/lib/interface-types/src/macros.rs b/lib/interface-types/src/macros.rs index 7dfc839ca..e6c0abf5f 100644 --- a/lib/interface-types/src/macros.rs +++ b/lib/interface-types/src/macros.rs @@ -23,3 +23,92 @@ macro_rules! consume { $input = next_input; }; } + +macro_rules! executable_instruction { + ($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => { + use crate::interpreter::{ExecutableInstruction, wasm, stack::Stackable}; + + pub(crate) fn $name( + $($argument_name: $argument_type),* + ) -> ExecutableInstruction + where + Export: wasm::structures::Export, + LocalImport: wasm::structures::LocalImport, + Memory: wasm::structures::Memory, + Instance: wasm::structures::Instance, + { + $implementation + } + }; +} + +#[cfg(test)] +macro_rules! test_executable_instruction { + ( + $test_name:ident = + instructions: [ $($instructions:expr),* $(,)* ], + invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ], + instance: $instance:expr, + stack: [ $($stack:expr),* $(,)* ] + $(,)* + ) => { + #[test] + #[allow(non_snake_case, unused)] + fn $test_name() { + use crate::interpreter::{ + instructions::tests::{Export, Instance, LocalImport, Memory}, + stack::Stackable, + wasm::values::{InterfaceType, InterfaceValue}, + Instruction, Interpreter, + }; + use std::{cell::Cell, collections::HashMap, convert::TryInto}; + + let interpreter: Interpreter = + (&vec![$($instructions),*]).try_into().unwrap(); + + let invocation_inputs = vec![$($invocation_inputs),*]; + let instance = $instance; + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_ok()); + + let stack = run.unwrap(); + + assert_eq!(stack.as_slice(), &[$($stack),*]); + } + }; + + ( + $test_name:ident = + instructions: [ $($instructions:expr),* $(,)* ], + invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ], + instance: $instance:expr, + error: $error:expr + $(,)* + ) => { + #[test] + #[allow(non_snake_case, unused)] + fn $test_name() { + use crate::interpreter::{ + instructions::tests::{Export, Instance, LocalImport, Memory}, + stack::Stackable, + wasm::values::{InterfaceType, InterfaceValue}, + Instruction, Interpreter, + }; + use std::{cell::Cell, collections::HashMap, convert::TryInto}; + + let interpreter: Interpreter = + (&vec![$($instructions),*]).try_into().unwrap(); + + let invocation_inputs = vec![$($invocation_inputs),*]; + let instance = $instance; + let run = interpreter.run(&invocation_inputs, &instance); + + assert!(run.is_err()); + + let error = run.unwrap_err(); + + assert_eq!(error, String::from($error)); + } + }; +} From f537b4dfa30b031c1f98656adcbb62dc6def5cc3 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Sep 2019 14:17:43 +0200 Subject: [PATCH 36/84] chore(interface-types) Simplify the `executable_instruction` macro. --- .../src/interpreter/instructions/argument_get.rs | 4 ++-- lib/interface-types/src/interpreter/instructions/call.rs | 4 ++-- .../src/interpreter/instructions/call_export.rs | 4 ++-- lib/interface-types/src/interpreter/instructions/read_utf8.rs | 4 ++-- .../src/interpreter/instructions/write_utf8.rs | 4 ++-- lib/interface-types/src/macros.rs | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/argument_get.rs b/lib/interface-types/src/interpreter/instructions/argument_get.rs index 881930297..0a6790910 100644 --- a/lib/interface-types/src/interpreter/instructions/argument_get.rs +++ b/lib/interface-types/src/interpreter/instructions/argument_get.rs @@ -1,6 +1,6 @@ executable_instruction!( argument_get(index: u64, instruction_name: String) -> _ { - Box::new(move |runtime| -> _ { + move |runtime| -> _ { let invocation_inputs = runtime.invocation_inputs; if index >= (invocation_inputs.len() as u64) { @@ -13,7 +13,7 @@ executable_instruction!( runtime.stack.push(invocation_inputs[index as usize].clone()); Ok(()) - }) + } } ); diff --git a/lib/interface-types/src/interpreter/instructions/call.rs b/lib/interface-types/src/interpreter/instructions/call.rs index 4a74655c3..d58368c1c 100644 --- a/lib/interface-types/src/interpreter/instructions/call.rs +++ b/lib/interface-types/src/interpreter/instructions/call.rs @@ -5,7 +5,7 @@ use crate::interpreter::wasm::{ executable_instruction!( call(function_index: usize, instruction_name: String) -> _ { - Box::new(move |runtime| -> _ { + move |runtime| -> _ { let instance = runtime.wasm_instance; let index = FunctionIndex::new(function_index); @@ -58,7 +58,7 @@ executable_instruction!( function_index, )) } - }) + } } ); diff --git a/lib/interface-types/src/interpreter/instructions/call_export.rs b/lib/interface-types/src/interpreter/instructions/call_export.rs index 05de6470b..a1a43404e 100644 --- a/lib/interface-types/src/interpreter/instructions/call_export.rs +++ b/lib/interface-types/src/interpreter/instructions/call_export.rs @@ -2,7 +2,7 @@ use crate::interpreter::wasm::values::InterfaceType; executable_instruction!( call_export(export_name: String, instruction_name: String) -> _ { - Box::new(move |runtime| -> _ { + move |runtime| -> _ { let instance = runtime.wasm_instance; match instance.export(&export_name) { @@ -54,7 +54,7 @@ executable_instruction!( export_name, )) } - }) + } } ); diff --git a/lib/interface-types/src/interpreter/instructions/read_utf8.rs b/lib/interface-types/src/interpreter/instructions/read_utf8.rs index 8f195fd99..55fe3509e 100644 --- a/lib/interface-types/src/interpreter/instructions/read_utf8.rs +++ b/lib/interface-types/src/interpreter/instructions/read_utf8.rs @@ -3,7 +3,7 @@ use std::{cell::Cell, convert::TryFrom}; executable_instruction!( read_utf8(instruction_name: String) -> _ { - Box::new(move |runtime| -> _ { + move |runtime| -> _ { match runtime.stack.pop(2) { Some(inputs) => match runtime.wasm_instance.memory(0) { Some(memory) => { @@ -48,7 +48,7 @@ executable_instruction!( instruction_name, )) } - }) + } } ); diff --git a/lib/interface-types/src/interpreter/instructions/write_utf8.rs b/lib/interface-types/src/interpreter/instructions/write_utf8.rs index fa66621b2..34e148faf 100644 --- a/lib/interface-types/src/interpreter/instructions/write_utf8.rs +++ b/lib/interface-types/src/interpreter/instructions/write_utf8.rs @@ -3,7 +3,7 @@ use std::convert::TryInto; executable_instruction!( write_utf8(allocator_name: String, instruction_name: String) -> _ { - Box::new(move |runtime| -> _ { + move |runtime| -> _ { let instance = runtime.wasm_instance; match instance.export(&allocator_name) { @@ -67,7 +67,7 @@ executable_instruction!( allocator_name )) } - }) + } } ); diff --git a/lib/interface-types/src/macros.rs b/lib/interface-types/src/macros.rs index e6c0abf5f..2d2e1464f 100644 --- a/lib/interface-types/src/macros.rs +++ b/lib/interface-types/src/macros.rs @@ -37,7 +37,7 @@ macro_rules! executable_instruction { Memory: wasm::structures::Memory, Instance: wasm::structures::Instance, { - $implementation + Box::new($implementation) } }; } From c35395bd9435412ed83d48d75cf042b05203f72a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Sep 2019 14:18:30 +0200 Subject: [PATCH 37/84] feat(interface-types) Rename a variable. --- lib/interface-types/src/interpreter/mod.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index e8b45bd57..6824128ef 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -90,26 +90,22 @@ where .iter() .map( |instruction| -> ExecutableInstruction { - let instruction_representation: String = instruction.into(); + let instruction_name: String = instruction.into(); match instruction { Instruction::ArgumentGet { index } => { - instructions::argument_get(*index, instruction_representation) + instructions::argument_get(*index, instruction_name) } Instruction::Call { function_index } => { - instructions::call(*function_index, instruction_representation) + instructions::call(*function_index, instruction_name) } - Instruction::CallExport { export_name } => instructions::call_export( - (*export_name).to_owned(), - instruction_representation, - ), - Instruction::ReadUtf8 => { - instructions::read_utf8(instruction_representation) + Instruction::CallExport { export_name } => { + instructions::call_export((*export_name).to_owned(), instruction_name) + } + Instruction::ReadUtf8 => instructions::read_utf8(instruction_name), + Instruction::WriteUtf8 { allocator_name } => { + instructions::write_utf8((*allocator_name).to_owned(), instruction_name) } - Instruction::WriteUtf8 { allocator_name } => instructions::write_utf8( - (*allocator_name).to_owned(), - instruction_representation, - ), _ => unimplemented!(), } }, From 7e18be191dc9869b8944f920d505a3693cbaa6cf Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Sep 2019 14:36:31 +0200 Subject: [PATCH 38/84] chore(cargo) Update `Cargo.lock`. --- Cargo.lock | 45 ++++++++------------------------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8faf93b30..f31e1cb97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -429,7 +429,7 @@ dependencies = [ [[package]] name = "either" -version = "1.5.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -614,7 +614,7 @@ name = "inkwell" version = "0.1.0" source = "git+https://github.com/wasmerio/inkwell?branch=llvm8-0#8f480124663b812ee76cd4370f3ee170135b9d0e" dependencies = [ - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm8-0)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -659,7 +659,7 @@ name = "itertools" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -687,7 +687,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lexical-core" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -804,7 +804,7 @@ name = "nom" version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lexical-core 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -940,21 +940,6 @@ dependencies = [ [[package]] name = "rand" -<<<<<<< HEAD -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -======= ->>>>>>> master version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -1041,7 +1026,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1537,15 +1522,9 @@ dependencies = [ "serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", -<<<<<<< HEAD - "target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-fork-frontend 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-fork-wasm 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", -======= "target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-fork-frontend 0.43.1 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-clif-fork-wasm 0.43.1 (registry+https://github.com/rust-lang/crates.io-index)", ->>>>>>> master "wasmer-runtime-core 0.7.0", "wasmer-win-exception-handler 0.7.0", "wasmparser 0.37.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1897,7 +1876,7 @@ dependencies = [ "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dynasm 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f36d49ab6f8ecc642d2c6ee10fda04ba68003ef0277300866745cdde160e6b40" "checksum dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c408a211e7f5762829f5e46bdff0c14bc3b1517a21a4bb781c716bf88b0c68" -"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" "checksum enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7798e7da2d4cb0d6d6fc467e8d6b5bf247e9e989f786dde1732d79899c32bb10" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" "checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60" @@ -1928,7 +1907,7 @@ dependencies = [ "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum lexical-core 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d11f3928ffd249baadf9f083cdea16d7cf317b2a8be6227e1169102432a36d2" +"checksum lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2304bccb228c4b020f3a4835d247df0a02a7c4686098d4167762cfbbe4c5cb14" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" "checksum llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2110cd4daf9cd8e39dd3b933b1a2a2ac7315e91f7c92b3a20beab526c63b5978" @@ -1941,11 +1920,7 @@ dependencies = [ "checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -<<<<<<< HEAD "checksum nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c618b63422da4401283884e6668d39f819a106ef51f5f59b81add00075da35ca" -"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -======= ->>>>>>> master "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" @@ -1962,10 +1937,6 @@ dependencies = [ "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -<<<<<<< HEAD -"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -======= ->>>>>>> master "checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" From 63b13914c3e38d9d155d446bc98265140ca995f8 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Sep 2019 14:37:29 +0200 Subject: [PATCH 39/84] feat(interface-types) Let the type inference engine do the work. --- lib/interface-types/src/interpreter/mod.rs | 38 ++++++++++------------ 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 6824128ef..a6c11f20b 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -88,28 +88,26 @@ where fn try_from(instructions: &Vec) -> Result { let executable_instructions = instructions .iter() - .map( - |instruction| -> ExecutableInstruction { - let instruction_name: String = instruction.into(); + .map(|instruction| { + let instruction_name: String = instruction.into(); - match instruction { - Instruction::ArgumentGet { index } => { - instructions::argument_get(*index, instruction_name) - } - Instruction::Call { function_index } => { - instructions::call(*function_index, instruction_name) - } - Instruction::CallExport { export_name } => { - instructions::call_export((*export_name).to_owned(), instruction_name) - } - Instruction::ReadUtf8 => instructions::read_utf8(instruction_name), - Instruction::WriteUtf8 { allocator_name } => { - instructions::write_utf8((*allocator_name).to_owned(), instruction_name) - } - _ => unimplemented!(), + match instruction { + Instruction::ArgumentGet { index } => { + instructions::argument_get(*index, instruction_name) } - }, - ) + Instruction::Call { function_index } => { + instructions::call(*function_index, instruction_name) + } + Instruction::CallExport { export_name } => { + instructions::call_export((*export_name).to_owned(), instruction_name) + } + Instruction::ReadUtf8 => instructions::read_utf8(instruction_name), + Instruction::WriteUtf8 { allocator_name } => { + instructions::write_utf8((*allocator_name).to_owned(), instruction_name) + } + _ => unimplemented!(), + } + }) .collect(); Ok(Interpreter { From 87f61ab226618fbc7f0af38deb892cf0c7781a0d Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Sep 2019 15:02:20 +0200 Subject: [PATCH 40/84] feat(interface-types) Add attributes. --- lib/interface-types/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 2185b2c15..397a2db5c 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -1,3 +1,15 @@ +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] +#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] +#![doc(html_logo_url = "https://github.com/wasmerio.png")] + pub mod ast; #[macro_use] mod macros; From 9fdaf0cd4fe63b84005acec378c7d472eafcb351 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 26 Sep 2019 15:05:22 +0200 Subject: [PATCH 41/84] chore(cargo) Remove patches from version contraints. --- lib/interface-types/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index 19ddeb22b..e41b0e5d7 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -11,5 +11,5 @@ edition = "2018" nom = "5.0" [dev-dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.7.0", features = ["backend-cranelift"] } -wasmer-clif-backend = { path = "../clif-backend", version = "0.7.0" } \ No newline at end of file +wasmer-runtime-core = { path = "../runtime-core", version = "0.7", features = ["backend-cranelift"] } +wasmer-clif-backend = { path = "../clif-backend", version = "0.7" } \ No newline at end of file From d8134721a854669c5fb221fcc06ffc3e7291e01e Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Sat, 28 Sep 2019 00:55:35 +0200 Subject: [PATCH 42/84] feat(interface-types) Introduce the `wasm::structures::MemoryView` trait. --- lib/interface-types/src/ast.rs | 2 +- .../src/interpreter/instructions/mod.rs | 35 +++++++---- .../src/interpreter/instructions/read_utf8.rs | 2 +- .../interpreter/instructions/write_utf8.rs | 2 +- lib/interface-types/src/interpreter/mod.rs | 58 ++++++++++--------- .../src/interpreter/wasm/structures.rs | 39 +++++++++---- .../src/interpreter/wasm/values.rs | 20 ------- lib/interface-types/src/macros.rs | 17 +++--- 8 files changed, 96 insertions(+), 79 deletions(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 2bcabfb55..1dc9ab961 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -1,7 +1,7 @@ use crate::interpreter::Instruction; use std::str; -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Clone, Debug)] pub enum InterfaceType { Int, Float, diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 05c3f9723..e38dcf374 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -14,9 +14,9 @@ pub(crate) use write_utf8::write_utf8; pub(crate) mod tests { use crate::interpreter::wasm::{ self, - values::{InterfaceType, InterfaceValue, ValueType}, + values::{InterfaceType, InterfaceValue}, }; - use std::{cell::Cell, collections::HashMap, convert::TryInto}; + use std::{cell::Cell, collections::HashMap, convert::TryInto, ops::Deref, rc::Rc}; pub(crate) struct Export { pub(crate) inputs: Vec, @@ -74,24 +74,35 @@ pub(crate) mod tests { } } + #[derive(Default, Clone)] + pub(crate) struct MemoryView(Rc>>); + + impl wasm::structures::MemoryView for MemoryView {} + + impl Deref for MemoryView { + type Target = [Cell]; + + fn deref(&self) -> &[Cell] { + self.0.as_slice() + } + } + #[derive(Default)] pub(crate) struct Memory { - pub(crate) data: Vec>, + pub(crate) view: MemoryView, } impl Memory { pub(crate) fn new(data: Vec>) -> Self { - Self { data } + Self { + view: MemoryView(Rc::new(data)), + } } } - impl wasm::structures::Memory for Memory { - fn view(&self) -> &[Cell] { - use std::slice; - - let slice = self.data.as_slice(); - - unsafe { slice::from_raw_parts(slice.as_ptr() as *const Cell, slice.len()) } + impl wasm::structures::Memory for Memory { + fn view(&self) -> MemoryView { + self.view.clone() } } @@ -158,7 +169,7 @@ pub(crate) mod tests { } } - impl wasm::structures::Instance for Instance { + impl wasm::structures::Instance for Instance { fn export(&self, export_name: &str) -> Option<&Export> { self.exports.get(export_name) } diff --git a/lib/interface-types/src/interpreter/instructions/read_utf8.rs b/lib/interface-types/src/interpreter/instructions/read_utf8.rs index 55fe3509e..a06bc5630 100644 --- a/lib/interface-types/src/interpreter/instructions/read_utf8.rs +++ b/lib/interface-types/src/interpreter/instructions/read_utf8.rs @@ -9,7 +9,7 @@ executable_instruction!( Some(memory) => { let length = i32::try_from(&inputs[0])? as usize; let pointer = i32::try_from(&inputs[1])? as usize; - let memory_view = memory.view::(); + let memory_view = memory.view(); if memory_view.len() < pointer + length { return Err(format!( diff --git a/lib/interface-types/src/interpreter/instructions/write_utf8.rs b/lib/interface-types/src/interpreter/instructions/write_utf8.rs index 34e148faf..b0738f328 100644 --- a/lib/interface-types/src/interpreter/instructions/write_utf8.rs +++ b/lib/interface-types/src/interpreter/instructions/write_utf8.rs @@ -20,7 +20,7 @@ executable_instruction!( match runtime.wasm_instance.memory(0) { Some(memory) => match runtime.stack.pop1() { Some(string) => { - let memory_view = memory.view::(); + let memory_view = memory.view(); let string: String = (&string).try_into()?; let string_bytes = string.as_bytes(); diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index a6c11f20b..1b133a7bf 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -8,45 +8,50 @@ use stack::Stack; use std::{convert::TryFrom, marker::PhantomData}; use wasm::values::InterfaceValue; -pub(crate) struct Runtime<'invocation, 'instance, Instance, Export, LocalImport, Memory> +pub(crate) struct Runtime<'invocation, 'instance, Instance, Export, LocalImport, Memory, MemoryView> where Export: wasm::structures::Export + 'instance, LocalImport: wasm::structures::LocalImport + 'instance, - Memory: wasm::structures::Memory + 'instance, - Instance: wasm::structures::Instance + 'instance, + Memory: wasm::structures::Memory + 'instance, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance + 'instance, { invocation_inputs: &'invocation [InterfaceValue], stack: Stack, wasm_instance: &'instance Instance, - _wasm_exports: PhantomData, - _wasm_locals_or_imports: PhantomData, - _wasm_memories: PhantomData, + _phantom: PhantomData<(Export, LocalImport, Memory, MemoryView)>, } -pub(crate) type ExecutableInstruction = - Box) -> Result<(), String>>; +pub(crate) type ExecutableInstruction = Box< + dyn Fn(&mut Runtime) -> Result<(), String>, +>; -pub struct Interpreter +pub struct Interpreter where Export: wasm::structures::Export, LocalImport: wasm::structures::LocalImport, - Memory: wasm::structures::Memory, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, { - executable_instructions: Vec>, + executable_instructions: + Vec>, } -impl Interpreter +impl + Interpreter where Export: wasm::structures::Export, LocalImport: wasm::structures::LocalImport, - Memory: wasm::structures::Memory, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, { fn iter( &self, - ) -> impl Iterator> + '_ - { + ) -> impl Iterator< + Item = &ExecutableInstruction, + > + '_ { self.executable_instructions.iter() } @@ -59,9 +64,7 @@ where invocation_inputs, stack: Stack::new(), wasm_instance, - _wasm_exports: PhantomData, - _wasm_locals_or_imports: PhantomData, - _wasm_memories: PhantomData, + _phantom: PhantomData, }; for executable_instruction in self.iter() { @@ -75,13 +78,15 @@ where } } -impl<'binary_input, Instance, Export, LocalImport, Memory> TryFrom<&Vec>> - for Interpreter +impl<'binary_input, Instance, Export, LocalImport, Memory, MemoryView> + TryFrom<&Vec>> + for Interpreter where Export: wasm::structures::Export, LocalImport: wasm::structures::LocalImport, - Memory: wasm::structures::Memory, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, { type Error = String; @@ -118,7 +123,7 @@ where #[cfg(test)] mod tests { - use super::{Instruction, Interpreter}; + use super::{wasm::structures::EmptyMemoryView, Instruction, Interpreter}; use std::convert::TryInto; #[test] @@ -130,7 +135,8 @@ mod tests { Instruction::ReadUtf8, Instruction::Call { function_index: 7 }, ]; - let interpreter: Interpreter<(), (), (), ()> = (&instructions).try_into().unwrap(); + let interpreter: Interpreter<(), (), (), (), EmptyMemoryView> = + (&instructions).try_into().unwrap(); assert_eq!(interpreter.executable_instructions.len(), 5); } diff --git a/lib/interface-types/src/interpreter/wasm/structures.rs b/lib/interface-types/src/interpreter/wasm/structures.rs index d1fa58349..eb0a5113a 100644 --- a/lib/interface-types/src/interpreter/wasm/structures.rs +++ b/lib/interface-types/src/interpreter/wasm/structures.rs @@ -1,5 +1,5 @@ -use super::values::{InterfaceType, InterfaceValue, ValueType}; -use std::cell::Cell; +use super::values::{InterfaceType, InterfaceValue}; +use std::{cell::Cell, ops::Deref}; pub trait TypedIndex: Copy + Clone { fn new(index: usize) -> Self; @@ -53,15 +53,21 @@ pub trait LocalImport { fn call(&self, arguments: &[InterfaceValue]) -> Result, ()>; } -pub trait Memory { - fn view(&self) -> &[Cell]; +pub trait MemoryView: Deref]> {} + +pub trait Memory +where + View: MemoryView, +{ + fn view(&self) -> View; } -pub trait Instance +pub trait Instance where E: Export, LI: LocalImport, - M: Memory, + M: Memory, + MV: MemoryView, { fn export(&self, export_name: &str) -> Option<&E>; fn local_or_import(&self, index: I) -> Option<&LI>; @@ -112,17 +118,30 @@ impl LocalImport for () { } } -impl Memory for () { - fn view(&self) -> &[Cell] { +pub(crate) struct EmptyMemoryView; + +impl MemoryView for EmptyMemoryView {} + +impl Deref for EmptyMemoryView { + type Target = [Cell]; + + fn deref(&self) -> &[Cell] { &[] } } -impl Instance for () +impl Memory for () { + fn view(&self) -> EmptyMemoryView { + EmptyMemoryView + } +} + +impl Instance for () where E: Export, LI: LocalImport, - M: Memory, + M: Memory, + MV: MemoryView, { fn export(&self, _export_name: &str) -> Option<&E> { None diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 1cfac911f..632a88f65 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -63,23 +63,3 @@ from_x_for_interface_value!(i32, I32); from_x_for_interface_value!(i64, I64); from_x_for_interface_value!(f32, F32); from_x_for_interface_value!(f64, F64); - -pub trait ValueType: Copy -where - Self: Sized, -{ -} - -macro_rules! value_type { - ($native_type:ty) => { - impl ValueType for $native_type {} - }; - - ($($native_type:ty),*) => { - $( - value_type!($native_type); - )* - }; -} - -value_type!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); diff --git a/lib/interface-types/src/macros.rs b/lib/interface-types/src/macros.rs index 2d2e1464f..aee817824 100644 --- a/lib/interface-types/src/macros.rs +++ b/lib/interface-types/src/macros.rs @@ -28,14 +28,15 @@ macro_rules! executable_instruction { ($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => { use crate::interpreter::{ExecutableInstruction, wasm, stack::Stackable}; - pub(crate) fn $name( + pub(crate) fn $name( $($argument_name: $argument_type),* - ) -> ExecutableInstruction + ) -> ExecutableInstruction where Export: wasm::structures::Export, LocalImport: wasm::structures::LocalImport, - Memory: wasm::structures::Memory, - Instance: wasm::structures::Instance, + Memory: wasm::structures::Memory, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, { Box::new($implementation) } @@ -56,14 +57,14 @@ macro_rules! test_executable_instruction { #[allow(non_snake_case, unused)] fn $test_name() { use crate::interpreter::{ - instructions::tests::{Export, Instance, LocalImport, Memory}, + instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView}, stack::Stackable, wasm::values::{InterfaceType, InterfaceValue}, Instruction, Interpreter, }; use std::{cell::Cell, collections::HashMap, convert::TryInto}; - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![$($instructions),*]).try_into().unwrap(); let invocation_inputs = vec![$($invocation_inputs),*]; @@ -90,14 +91,14 @@ macro_rules! test_executable_instruction { #[allow(non_snake_case, unused)] fn $test_name() { use crate::interpreter::{ - instructions::tests::{Export, Instance, LocalImport, Memory}, + instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView}, stack::Stackable, wasm::values::{InterfaceType, InterfaceValue}, Instruction, Interpreter, }; use std::{cell::Cell, collections::HashMap, convert::TryInto}; - let interpreter: Interpreter = + let interpreter: Interpreter = (&vec![$($instructions),*]).try_into().unwrap(); let invocation_inputs = vec![$($invocation_inputs),*]; From 7b3ab386088a81699782728c5c2cde89638cfeeb Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 2 Oct 2019 14:58:48 +0200 Subject: [PATCH 43/84] chore(interface-types) Simplify the code. --- lib/interface-types/src/interpreter/instructions/mod.rs | 4 ++-- lib/interface-types/src/interpreter/wasm/structures.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index e38dcf374..183e0dc82 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -54,7 +54,7 @@ pub(crate) mod tests { impl wasm::structures::LocalImport for LocalImport { fn inputs_cardinality(&self) -> usize { - self.inputs.len() as usize + self.inputs.len() } fn outputs_cardinality(&self) -> usize { @@ -82,7 +82,7 @@ pub(crate) mod tests { impl Deref for MemoryView { type Target = [Cell]; - fn deref(&self) -> &[Cell] { + fn deref(&self) -> &Self::Target { self.0.as_slice() } } diff --git a/lib/interface-types/src/interpreter/wasm/structures.rs b/lib/interface-types/src/interpreter/wasm/structures.rs index eb0a5113a..a38511c8f 100644 --- a/lib/interface-types/src/interpreter/wasm/structures.rs +++ b/lib/interface-types/src/interpreter/wasm/structures.rs @@ -125,7 +125,7 @@ impl MemoryView for EmptyMemoryView {} impl Deref for EmptyMemoryView { type Target = [Cell]; - fn deref(&self) -> &[Cell] { + fn deref(&self) -> &Self::Target { &[] } } From 99c9fc44dc3769b180f91cf1f6058bc6bf1ca3b3 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 2 Oct 2019 22:14:08 +0200 Subject: [PATCH 44/84] chore(interface-types) The `tests/` directory has moved in `wasmer-runtime-core-tests`. --- lib/interface-types/tests/assets/hello_world.wasm | Bin 444 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 lib/interface-types/tests/assets/hello_world.wasm diff --git a/lib/interface-types/tests/assets/hello_world.wasm b/lib/interface-types/tests/assets/hello_world.wasm deleted file mode 100644 index 88d3c079ccadb8ee3722e991a139b542f26d797f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 444 zcmZ{gO-{ow5QX22oq}-HMr_!+J0#Q#z@}LsvE&A6NDZWNY?QcES*2IwUP$1kAfcje zW=8X)_vS;<)&zjAk|p(6g8C&8%b2;3k?(|j^=(k4cdfT3xBdkfT9cJ8H??sg4^^-R zB#e6>)u#4M$F$=ei$t;z^6W;+x~LpDDXjG+#HbC8H4bvRZK_~$x3M;V>ViRX@hDTG zK1)-iG=oM8l_1-lt83z3EHvi8j5XiOg{G;}$Y>q+XrE{d1vQCRn${U^5i6xG30bPf z4w(FquF!iUjcbdhlqapPA^ R&?%U`Ad&}Od~AmXoZr-0bans$ From 207d69fdbd838855dbad33287c24e3cdf13c309c Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 3 Oct 2019 00:13:07 +0200 Subject: [PATCH 45/84] feat(interface-types) Change `Instance::local_or_import(&self)` to `Instance::local_or_import(&mut self)`. It allows the instance to create or update locals/imports when the `local_or_import` function is called. It's not ideal, but fine enough for a first step. --- Cargo.lock | 2 -- lib/interface-types/src/interpreter/instructions/call.rs | 2 +- .../src/interpreter/instructions/call_export.rs | 2 +- lib/interface-types/src/interpreter/instructions/mod.rs | 2 +- .../src/interpreter/instructions/write_utf8.rs | 4 ++-- lib/interface-types/src/interpreter/mod.rs | 4 ++-- lib/interface-types/src/interpreter/wasm/structures.rs | 4 ++-- lib/interface-types/src/macros.rs | 8 ++++---- 8 files changed, 13 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f31e1cb97..f63153bbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1594,8 +1594,6 @@ name = "wasmer-interface-types" version = "0.7.0" dependencies = [ "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.7.0", - "wasmer-runtime-core 0.7.0", ] [[package]] diff --git a/lib/interface-types/src/interpreter/instructions/call.rs b/lib/interface-types/src/interpreter/instructions/call.rs index d58368c1c..f6375c485 100644 --- a/lib/interface-types/src/interpreter/instructions/call.rs +++ b/lib/interface-types/src/interpreter/instructions/call.rs @@ -6,7 +6,7 @@ use crate::interpreter::wasm::{ executable_instruction!( call(function_index: usize, instruction_name: String) -> _ { move |runtime| -> _ { - let instance = runtime.wasm_instance; + let instance = &mut runtime.wasm_instance; let index = FunctionIndex::new(function_index); match instance.local_or_import(index) { diff --git a/lib/interface-types/src/interpreter/instructions/call_export.rs b/lib/interface-types/src/interpreter/instructions/call_export.rs index a1a43404e..62e360aa8 100644 --- a/lib/interface-types/src/interpreter/instructions/call_export.rs +++ b/lib/interface-types/src/interpreter/instructions/call_export.rs @@ -3,7 +3,7 @@ use crate::interpreter::wasm::values::InterfaceType; executable_instruction!( call_export(export_name: String, instruction_name: String) -> _ { move |runtime| -> _ { - let instance = runtime.wasm_instance; + let instance = &mut runtime.wasm_instance; match instance.export(&export_name) { Some(export) => { diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 183e0dc82..d84d2b47e 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -175,7 +175,7 @@ pub(crate) mod tests { } fn local_or_import( - &self, + &mut self, index: I, ) -> Option<&LocalImport> { self.locals_or_imports.get(&index.index()) diff --git a/lib/interface-types/src/interpreter/instructions/write_utf8.rs b/lib/interface-types/src/interpreter/instructions/write_utf8.rs index b0738f328..a1e21509f 100644 --- a/lib/interface-types/src/interpreter/instructions/write_utf8.rs +++ b/lib/interface-types/src/interpreter/instructions/write_utf8.rs @@ -4,7 +4,7 @@ use std::convert::TryInto; executable_instruction!( write_utf8(allocator_name: String, instruction_name: String) -> _ { move |runtime| -> _ { - let instance = runtime.wasm_instance; + let instance = &mut runtime.wasm_instance; match instance.export(&allocator_name) { Some(allocator) => { @@ -17,7 +17,7 @@ executable_instruction!( )) } - match runtime.wasm_instance.memory(0) { + match instance.memory(0) { Some(memory) => match runtime.stack.pop1() { Some(string) => { let memory_view = memory.view(); diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 1b133a7bf..551311a83 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -18,7 +18,7 @@ where { invocation_inputs: &'invocation [InterfaceValue], stack: Stack, - wasm_instance: &'instance Instance, + wasm_instance: &'instance mut Instance, _phantom: PhantomData<(Export, LocalImport, Memory, MemoryView)>, } @@ -58,7 +58,7 @@ where pub fn run( &self, invocation_inputs: &[InterfaceValue], - wasm_instance: &Instance, + wasm_instance: &mut Instance, ) -> Result, String> { let mut runtime = Runtime { invocation_inputs, diff --git a/lib/interface-types/src/interpreter/wasm/structures.rs b/lib/interface-types/src/interpreter/wasm/structures.rs index a38511c8f..7ec386f68 100644 --- a/lib/interface-types/src/interpreter/wasm/structures.rs +++ b/lib/interface-types/src/interpreter/wasm/structures.rs @@ -70,7 +70,7 @@ where MV: MemoryView, { fn export(&self, export_name: &str) -> Option<&E>; - fn local_or_import(&self, index: I) -> Option<&LI>; + fn local_or_import(&mut self, index: I) -> Option<&LI>; fn memory(&self, index: usize) -> Option<&M>; } @@ -151,7 +151,7 @@ where None } - fn local_or_import(&self, _index: I) -> Option<&LI> { + fn local_or_import(&mut self, _index: I) -> Option<&LI> { None } } diff --git a/lib/interface-types/src/macros.rs b/lib/interface-types/src/macros.rs index aee817824..a0b99940d 100644 --- a/lib/interface-types/src/macros.rs +++ b/lib/interface-types/src/macros.rs @@ -68,8 +68,8 @@ macro_rules! test_executable_instruction { (&vec![$($instructions),*]).try_into().unwrap(); let invocation_inputs = vec![$($invocation_inputs),*]; - let instance = $instance; - let run = interpreter.run(&invocation_inputs, &instance); + let mut instance = $instance; + let run = interpreter.run(&invocation_inputs, &mut instance); assert!(run.is_ok()); @@ -102,8 +102,8 @@ macro_rules! test_executable_instruction { (&vec![$($instructions),*]).try_into().unwrap(); let invocation_inputs = vec![$($invocation_inputs),*]; - let instance = $instance; - let run = interpreter.run(&invocation_inputs, &instance); + let mut instance = $instance; + let run = interpreter.run(&invocation_inputs, &mut instance); assert!(run.is_err()); From b56240010c875f44b40c818a0ce83767cb9e64fa Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Fri, 4 Oct 2019 12:07:21 +0200 Subject: [PATCH 46/84] feat(interface-types) Simplify code. --- lib/interface-types/src/interpreter/instructions/call.rs | 2 +- lib/interface-types/src/interpreter/instructions/call_export.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/call.rs b/lib/interface-types/src/interpreter/instructions/call.rs index f6375c485..b788f5350 100644 --- a/lib/interface-types/src/interpreter/instructions/call.rs +++ b/lib/interface-types/src/interpreter/instructions/call.rs @@ -17,7 +17,7 @@ executable_instruction!( Some(inputs) => { let input_types = inputs .iter() - .map(|input| input.into()) + .map(Into::into) .collect::>(); if input_types != local_or_import.inputs() { diff --git a/lib/interface-types/src/interpreter/instructions/call_export.rs b/lib/interface-types/src/interpreter/instructions/call_export.rs index 62e360aa8..9afe98417 100644 --- a/lib/interface-types/src/interpreter/instructions/call_export.rs +++ b/lib/interface-types/src/interpreter/instructions/call_export.rs @@ -13,7 +13,7 @@ executable_instruction!( Some(inputs) => { let input_types = inputs .iter() - .map(|input| input.into()) + .map(Into::into) .collect::>(); if input_types != export.inputs() { From c8af1b1fb7f086121fe89da9ab29de70f79b61aa Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 13:15:52 +0100 Subject: [PATCH 47/84] feat(interface-types) Remove dev-dependencies to any runtime. --- lib/interface-types/Cargo.toml | 6 +- lib/interface-types/src/lib.rs | 154 --------------------------------- 2 files changed, 1 insertion(+), 159 deletions(-) diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index e41b0e5d7..0e8bf2d97 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -8,8 +8,4 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [dependencies] -nom = "5.0" - -[dev-dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.7", features = ["backend-cranelift"] } -wasmer-clif-backend = { path = "../clif-backend", version = "0.7" } \ No newline at end of file +nom = "5.0" \ No newline at end of file diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 397a2db5c..6798a1e29 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -18,157 +18,3 @@ pub mod encoders; pub mod interpreter; pub use decoders::binary::parse as parse_binary; - -#[cfg(test)] -mod tests { - use crate::{ast::*, interpreter::Instruction, parse_binary}; - use std::fs; - use wasmer_clif_backend::CraneliftCompiler; - use wasmer_runtime_core as runtime; - - fn get_module() -> runtime::Module { - runtime::compile_with( - fs::read("tests/assets/hello_world.wasm") - .expect("Failed to read `tests/assets/hello_world.wasm`.") - .as_slice(), - &CraneliftCompiler::new(), - ) - .expect("Failed to parse the `hello_world.wasm` module.") - } - - #[test] - fn test_has_custom_section() { - let module = get_module(); - let custom_section = module.info().custom_sections.get("interface-types"); - - assert!(custom_section.is_some()); - } - - #[test] - fn test_parse_binary_from_custom_section() { - let module = get_module(); - let custom_section_bytes = module - .info() - .custom_sections - .get("interface-types") - .unwrap() - .as_slice(); - - match parse_binary::<()>(custom_section_bytes) { - Ok((remainder, interfaces)) => { - assert!(remainder.is_empty()); - assert_eq!( - interfaces, - Interfaces { - exports: vec![ - Export { - name: "strlen", - input_types: vec![InterfaceType::I32], - output_types: vec![InterfaceType::I32] - }, - Export { - name: "write_null_byte", - input_types: vec![InterfaceType::I32, InterfaceType::I32], - output_types: vec![InterfaceType::I32], - } - ], - types: vec![], - imported_functions: vec![ - ImportedFunction { - namespace: "host", - name: "console_log", - input_types: vec![InterfaceType::String], - output_types: vec![], - }, - ImportedFunction { - namespace: "host", - name: "document_title", - input_types: vec![], - output_types: vec![InterfaceType::String], - } - ], - adapters: vec![ - Adapter::Import { - namespace: "host", - name: "console_log", - input_types: vec![InterfaceType::I32], - output_types: vec![], - instructions: vec![ - Instruction::ArgumentGet { index: 0 }, - Instruction::ArgumentGet { index: 0 }, - Instruction::CallExport { - export_name: "strlen" - }, - Instruction::ReadUtf8, - Instruction::Call { function_index: 0 }, - ] - }, - Adapter::Import { - namespace: "host", - name: "document_title", - input_types: vec![], - output_types: vec![InterfaceType::I32], - instructions: vec![ - Instruction::Call { function_index: 1 }, - Instruction::WriteUtf8 { - allocator_name: "alloc" - }, - Instruction::CallExport { - export_name: "write_null_byte" - }, - ] - } - ], - forwards: vec![Forward { name: "main" }] - } - ); - - let wat = String::from(&interfaces); - - assert_eq!( - wat, - r#";; Interfaces - -;; Interface, Export strlen -(@interface export "strlen" - (param i32) - (result i32)) - -;; Interface, Export write_null_byte -(@interface export "write_null_byte" - (param i32 i32) - (result i32)) - -;; Interface, Imported function host.console_log -(@interface func $host_console_log (import "host" "console_log") - (param String)) - -;; Interface, Imported function host.document_title -(@interface func $host_document_title (import "host" "document_title") - (result String)) - -;; Interface, Adapter host.console_log -(@interface adapt (import "host" "console_log") - (param i32) - arg.get 0 - arg.get 0 - call-export "strlen" - read-utf8 - call 0) - -;; Interface, Adapter host.document_title -(@interface adapt (import "host" "document_title") - (result i32) - call 1 - write-utf8 "alloc" - call-export "write_null_byte") - -;; Interface, Forward main -(@interface forward (export "main"))"#, - ); - } - - Err(_) => assert!(false), - } - } -} From fe14c5c06b49f177cccfbf68bcaf56881ef32101 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 13:32:04 +0100 Subject: [PATCH 48/84] chore(interface-types) Update crate version. --- lib/interface-types/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index 0e8bf2d97..cd1b97f6c 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-interface-types" -version = "0.7.0" +version = "0.13.1" description = "WebAssembly Interface Types library for Wasmer" license = "MIT" authors = ["The Wasmer Engineering Team "] From 60a905698d62a150489ec6377693c4d7dcb42257 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 14:05:13 +0100 Subject: [PATCH 49/84] feat(interface-types) Use nom 5.1. --- Cargo.lock | 8 ++++---- lib/interface-types/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54af30296..ca64343f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -706,7 +706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "nom" -version = "5.0.1" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1604,9 +1604,9 @@ dependencies = [ [[package]] name = "wasmer-interface-types" -version = "0.7.0" +version = "0.13.1" dependencies = [ - "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1951,7 +1951,7 @@ dependencies = [ "checksum minifb 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "55cbdf43445926b65e07992f06019321e7481df8fd656dcb6871d00cdbd9fc73" "checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c618b63422da4401283884e6668d39f819a106ef51f5f59b81add00075da35ca" +"checksum nom 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c433f4d505fe6ce7ff78523d2fa13a0b9f2690e181fc26168bcbe5ccc5d14e07" "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" "checksum num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" diff --git a/lib/interface-types/Cargo.toml b/lib/interface-types/Cargo.toml index cd1b97f6c..a3d247479 100644 --- a/lib/interface-types/Cargo.toml +++ b/lib/interface-types/Cargo.toml @@ -8,4 +8,4 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [dependencies] -nom = "5.0" \ No newline at end of file +nom = "5.1" \ No newline at end of file From 0b0a89bf0fc652978a8d7f68c3373ec323ef155d Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 14:41:06 +0100 Subject: [PATCH 50/84] test(interface-types) Fix a documentation message. --- lib/interface-types/src/interpreter/instructions/call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interface-types/src/interpreter/instructions/call.rs b/lib/interface-types/src/interpreter/instructions/call.rs index b788f5350..9f4ac0226 100644 --- a/lib/interface-types/src/interpreter/instructions/call.rs +++ b/lib/interface-types/src/interpreter/instructions/call.rs @@ -174,7 +174,7 @@ mod tests { inputs: vec![InterfaceType::I32, InterfaceType::I32], outputs: vec![InterfaceType::I32], function: |_| Ok(vec![]), - // ^^^^^^^^^^ void fails + // ^^^^^^^^^^ void }, ); From 102ebe87f8dddd09b08c33e8548f1dfab0b60d15 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 14:41:40 +0100 Subject: [PATCH 51/84] chore(interface-types) Format code. --- lib/interface-types/src/encoders/wat.rs | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 89387ed4f..fd1cd214c 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -147,14 +147,14 @@ impl<'input> From<&Adapter<'input>> for String { name = name, inputs = input_types_to_param(&input_types), outputs = output_types_to_result(&output_types), - instructions = instructions.iter().fold( - String::new(), - |mut accumulator, instruction| { - accumulator.push_str("\n "); - accumulator.push_str(&String::from(instruction)); - accumulator - } - ), + instructions = + instructions + .iter() + .fold(String::new(), |mut accumulator, instruction| { + accumulator.push_str("\n "); + accumulator.push_str(&String::from(instruction)); + accumulator + }), ), Adapter::Export { @@ -167,14 +167,14 @@ impl<'input> From<&Adapter<'input>> for String { name = name, inputs = input_types_to_param(&input_types), outputs = output_types_to_result(&output_types), - instructions = instructions.iter().fold( - String::new(), - |mut accumulator, instruction| { - accumulator.push_str("\n "); - accumulator.push_str(&String::from(instruction)); - accumulator - } - ), + instructions = + instructions + .iter() + .fold(String::new(), |mut accumulator, instruction| { + accumulator.push_str("\n "); + accumulator.push_str(&String::from(instruction)); + accumulator + }), ), _ => unimplemented!(), From 5ed5ac82c07cda0a2a8601251a35852d5d25357b Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 15:22:22 +0100 Subject: [PATCH 52/84] test(interface-types) Improve test cases for `Stack`. --- lib/interface-types/src/interpreter/stack.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/interface-types/src/interpreter/stack.rs b/lib/interface-types/src/interpreter/stack.rs index 57b999df2..324c94a1f 100644 --- a/lib/interface-types/src/interpreter/stack.rs +++ b/lib/interface-types/src/interpreter/stack.rs @@ -100,7 +100,9 @@ mod tests { assert_eq!(stack.pop(1), Some(vec![6])); assert_eq!(stack.pop(2), Some(vec![5, 4])); + assert_eq!(stack.pop(4), None); // not enough items assert_eq!(stack.pop(3), Some(vec![3, 2, 1])); + assert_eq!(stack.pop1(), None); assert_eq!(stack.is_empty(), true); } } From e0989343289f3e9f8a46c44276904939db55edc5 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 15:22:56 +0100 Subject: [PATCH 53/84] doc(interface-types) Improve documentation of the crate itself. --- lib/interface-types/src/lib.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 6798a1e29..64dac5b56 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -1,3 +1,36 @@ +//! This crate contains an implementation of [WebAssembly Interface +//! Types][wit] (abbreviated WIT). It is composed of 4 parts: +//! +//! 1. AST: To represent the WIT language as a tree (which is not +//! really abstract). This is the central representation of the +//! language. +//! 2. Decoders: To read the AST from a particular data representation; +//! for instance, `decoders::binary` reads the AST from a binary. +//! 3. Encoders: To write the AST into a particular format; for +//! instance, `encoders::wat` writes the AST into a string +//! representing WIT with its textual format. + +//! 4. Interpreter: WIT defines a concept called Adapters. An adapter +//! contains a set of instructions. So, in more details, this +//! module contains: +//! * A very light and generic stack implementation, exposing only +//! the operations required by the interpreter, +//! * A stack-based interpreter, defined by: +//! * A compiler that transforms a set of instructions into a +//! set of executable instructions, +//! * A stack, +//! * A runtime that holds the “invocation inputs” (arguments +//! of the interpreter), the stack, and the WebAssembly +//! instance (which holds the exports, the imports, the +//! memories, the tables etc.), +//! * An hypothetic WebAssembly runtime, represented as a set of +//! enums, types, and traits —basically this is the part a +//! runtime should take a look to use the +//! `wasmer-interface-types` crate—. +//! +//! +//! [wit]: https://github.com/WebAssembly/interface-types + #![deny( dead_code, nonstandard_style, From d63508f19ef69a00b5218f5b98880275f68ce70f Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 15:23:28 +0100 Subject: [PATCH 54/84] doc(interface-types) Improve documentation of the `stack` module. --- lib/interface-types/src/interpreter/stack.rs | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/interface-types/src/interpreter/stack.rs b/lib/interface-types/src/interpreter/stack.rs index 324c94a1f..7877da573 100644 --- a/lib/interface-types/src/interpreter/stack.rs +++ b/lib/interface-types/src/interpreter/stack.rs @@ -1,18 +1,39 @@ +//! A very light and generic stack implementation, exposing only the +//! operations required by the interpreter. + +/// The `Stackable` trait represents a small basic set of operations +/// required by the interpreter. pub trait Stackable { + /// The kind of item the stack holds. type Item; + /// Checks whether the stack is empty. fn is_empty(&self) -> bool; + + /// Extracts a slice containing the entire stack. fn as_slice(&self) -> &[Self::Item]; + + /// Appends one item to the end of the stack. fn push(&mut self, item: Self::Item); + + /// Removes the last item of the stack and returns it, `None` if + /// the stack is empty. fn pop1(&mut self) -> Option; + + /// Removes `n` elements from the end of the stack, `None` if the + /// stack doesn't contain enough elements. + /// Returned items are ordered by FIFO: the last element comes + /// first in the list. fn pop(&mut self, n: usize) -> Option>; } +/// A stack implementation of the `Stackable` trait, based on a vector. #[derive(Debug, Default)] pub struct Stack where T: Default + Clone, { + /// Inner structure holding the items. inner: Vec, } @@ -20,6 +41,7 @@ impl Stack where T: Default + Clone, { + /// Creates a new empty stack. pub fn new() -> Self { Self { ..Default::default() From 32325c1861cfac471f77c9b8e951ae3e65cccabb Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 15:27:04 +0100 Subject: [PATCH 55/84] doc(interface-types) Improve module descriptions. --- lib/interface-types/src/ast.rs | 3 +++ lib/interface-types/src/decoders/mod.rs | 3 +++ lib/interface-types/src/encoders/mod.rs | 4 ++++ lib/interface-types/src/interpreter/mod.rs | 2 ++ 4 files changed, 12 insertions(+) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 1dc9ab961..d2020a898 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -1,3 +1,6 @@ +//! Represents the WIT language as a tree. This is the central +//! representation of the language. + use crate::interpreter::Instruction; use std::str; diff --git a/lib/interface-types/src/decoders/mod.rs b/lib/interface-types/src/decoders/mod.rs index 96eab6681..0ac9d881d 100644 --- a/lib/interface-types/src/decoders/mod.rs +++ b/lib/interface-types/src/decoders/mod.rs @@ -1 +1,4 @@ +//! Reads the AST from a particular data representation; for instance, +//! `decoders::binary` reads the AST from a binary. + pub mod binary; diff --git a/lib/interface-types/src/encoders/mod.rs b/lib/interface-types/src/encoders/mod.rs index 070bf5893..ae1611e89 100644 --- a/lib/interface-types/src/encoders/mod.rs +++ b/lib/interface-types/src/encoders/mod.rs @@ -1 +1,5 @@ +//! Writes the AST into a particular format; for instance, +//! `encoders::wat` writes the AST into a string representing WIT with +//! its textual format. + pub mod wat; diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 551311a83..d9452abce 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -1,3 +1,5 @@ +//! A stack-based interpreter to execute instructions of WIT adapters. + mod instruction; mod instructions; pub mod stack; From 36c7dbd92fac16f334da5e4369c360568220b869 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 15:41:10 +0100 Subject: [PATCH 56/84] feat(interface-types) Rename `ImportedFunction` to `Import`. So that we are consistent with `Export`. --- lib/interface-types/src/decoders/binary.rs | 44 +++++++++--------- lib/interface-types/src/encoders/wat.rs | 52 +++++++++++----------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 9a1d77e47..2853f7eae 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -291,30 +291,30 @@ fn types<'input, E: ParseError<&'input [u8]>>( Ok((input, types)) } -fn imported_functions<'input, E: ParseError<&'input [u8]>>( +fn imports<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], -) -> IResult<&'input [u8], Vec, E> { +) -> IResult<&'input [u8], Vec, E> { let mut input = input; - consume!((input, number_of_imported_functions) = leb(input)?); + consume!((input, number_of_imports) = leb(input)?); - let mut imported_functions = Vec::with_capacity(number_of_imported_functions as usize); + let mut imports = Vec::with_capacity(number_of_imports as usize); - for _ in 0..number_of_imported_functions { - consume!((input, imported_function_namespace) = string(input)?); - consume!((input, imported_function_name) = string(input)?); - consume!((input, imported_function_input_types) = list(input, ty)?); - consume!((input, imported_function_output_types) = list(input, ty)?); + for _ in 0..number_of_imports { + consume!((input, import_namespace) = string(input)?); + consume!((input, import_name) = string(input)?); + consume!((input, import_input_types) = list(input, ty)?); + consume!((input, import_output_types) = list(input, ty)?); - imported_functions.push(ImportedFunction { - namespace: imported_function_namespace, - name: imported_function_name, - input_types: imported_function_input_types, - output_types: imported_function_output_types, + imports.push(Import { + namespace: import_namespace, + name: import_name, + input_types: import_input_types, + output_types: import_output_types, }); } - Ok((input, imported_functions)) + Ok((input, imports)) } fn adapters<'input, E: ParseError<&'input [u8]>>( @@ -406,7 +406,7 @@ pub fn parse<'input, E: ParseError<&'input [u8]>>( consume!((input, exports) = exports(input)?); consume!((input, types) = types(input)?); - consume!((input, imported_functions) = imported_functions(input)?); + consume!((input, imports) = imports(input)?); consume!((input, adapters) = adapters(input)?); consume!((input, forwards) = forwards(input)?); @@ -415,7 +415,7 @@ pub fn parse<'input, E: ParseError<&'input [u8]>>( Interfaces { exports, types, - imported_functions, + imports, adapters, forwards, }, @@ -634,9 +634,9 @@ mod tests { } #[test] - fn test_imported_functions() { + fn test_imports() { let input = &[ - 0x02, // 2 imported functions + 0x02, // 2 imports 0x01, // string of 1 byte 0x61, // "a" 0x01, // string of 1 byte @@ -657,13 +657,13 @@ mod tests { let output = Ok(( &[] as &[u8], vec![ - ImportedFunction { + Import { namespace: "a", name: "b", input_types: vec![InterfaceType::I32], output_types: vec![InterfaceType::I64], }, - ImportedFunction { + Import { namespace: "c", name: "d", input_types: vec![InterfaceType::I32], @@ -672,7 +672,7 @@ mod tests { ], )); - assert_eq!(imported_functions::<()>(input), output); + assert_eq!(imports::<()>(input), output); } #[test] diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index fd1cd214c..8768da733 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -1,5 +1,5 @@ use crate::{ - ast::{Adapter, Export, Forward, ImportedFunction, InterfaceType, Interfaces, Type}, + ast::{Adapter, Export, Forward, Import, InterfaceType, Interfaces, Type}, interpreter::Instruction, }; @@ -120,14 +120,14 @@ impl<'input> From<&Type<'input>> for String { } } -impl<'input> From<&ImportedFunction<'input>> for String { - fn from(imported_function: &ImportedFunction) -> Self { +impl<'input> From<&Import<'input>> for String { + fn from(import: &Import) -> Self { format!( r#"(@interface func ${namespace}_{name} (import "{namespace}" "{name}"){inputs}{outputs})"#, - namespace = imported_function.namespace, - name = imported_function.name, - inputs = input_types_to_param(&imported_function.input_types), - outputs = output_types_to_result(&imported_function.output_types), + namespace = import.namespace, + name = import.name, + inputs = input_types_to_param(&import.input_types), + outputs = output_types_to_result(&import.output_types), ) } } @@ -213,17 +213,17 @@ impl<'input> From<&Interfaces<'input>> for String { accumulator }); - let imported_functions = interfaces.imported_functions.iter().fold( - String::new(), - |mut accumulator, imported_function| { + let imports = interfaces + .imports + .iter() + .fold(String::new(), |mut accumulator, import| { accumulator.push_str(&format!( - "\n\n;; Interface, Imported function {}.{}\n", - imported_function.namespace, imported_function.name + "\n\n;; Interface, Import {}.{}\n", + import.namespace, import.name )); - accumulator.push_str(&String::from(imported_function)); + accumulator.push_str(&String::from(import)); accumulator - }, - ); + }); let adapters = interfaces @@ -260,7 +260,7 @@ impl<'input> From<&Interfaces<'input>> for String { output.push_str(&exports); output.push_str(&types); - output.push_str(&imported_functions); + output.push_str(&imports); output.push_str(&adapters); output.push_str(&forwards); @@ -389,30 +389,30 @@ mod tests { } #[test] - fn test_imported_functions() { + fn test_imports() { let inputs: Vec = vec![ - (&ImportedFunction { + (&Import { namespace: "ns", name: "foo", input_types: vec![InterfaceType::Int, InterfaceType::String], output_types: vec![InterfaceType::String], }) .into(), - (&ImportedFunction { + (&Import { namespace: "ns", name: "foo", input_types: vec![InterfaceType::String], output_types: vec![], }) .into(), - (&ImportedFunction { + (&Import { namespace: "ns", name: "foo", input_types: vec![], output_types: vec![InterfaceType::String], }) .into(), - (&ImportedFunction { + (&Import { namespace: "ns", name: "foo", input_types: vec![], @@ -549,14 +549,14 @@ mod tests { }, ], types: vec![], - imported_functions: vec![ - ImportedFunction { + imports: vec![ + Import { namespace: "ns", name: "foo", input_types: vec![], output_types: vec![InterfaceType::I32], }, - ImportedFunction { + Import { namespace: "ns", name: "bar", input_types: vec![], @@ -590,11 +590,11 @@ mod tests { ;; Interface, Export bar (@interface export "bar") -;; Interface, Imported function ns.foo +;; Interface, Import ns.foo (@interface func $ns_foo (import "ns" "foo") (result i32)) -;; Interface, Imported function ns.bar +;; Interface, Import ns.bar (@interface func $ns_bar (import "ns" "bar")) ;; Interface, Adapter ns.foo From d667cb8e2fad0e59ba0177296fac3e832d146f77 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 15:41:41 +0100 Subject: [PATCH 57/84] doc(interface-types) Improve documentation of the `ast` module. --- lib/interface-types/src/ast.rs | 101 ++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 9 deletions(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index d2020a898..33609ad54 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -4,34 +4,83 @@ use crate::interpreter::Instruction; use std::str; +/// Represents the types supported by WIT. #[derive(PartialEq, Clone, Debug)] pub enum InterfaceType { + /// An integer. Int, + + /// A float. Float, + + /// Opaque reference. Any, + + /// A string. String, + + /// A sequence. Seq, + + /// A 32-bits integer. I32, + + /// A 64-bits integer. I64, + + /// A 32-bits float. F32, + + /// A 64-bits float. F64, + + /// An `any` reference. AnyRef, } +/// Represents the kind of adapter. #[derive(PartialEq, Debug)] pub(crate) enum AdapterKind { + /// An adapter defined for an imported function of a WebAssembly instance. Import, + + /// An adapter defined for an exported function of a WebAssembly instance. Export, + + /// A helper function. HelperFunction, } +/// Represents an exported function signature. #[derive(PartialEq, Debug)] pub struct Export<'input> { + /// The function name. pub name: &'input str, + + /// The function input types. pub input_types: Vec, + + /// The function output types. pub output_types: Vec, } +/// Represents an imported function signature. +#[derive(PartialEq, Debug)] +pub struct Import<'input> { + /// The function namespace. + pub namespace: &'input str, + + /// The function name. + pub name: &'input str, + + /// The function input types. + pub input_types: Vec, + + /// The function output types. + pub output_types: Vec, +} + +/// Represents a type. #[derive(PartialEq, Debug)] pub struct Type<'input> { pub name: &'input str, @@ -39,47 +88,81 @@ pub struct Type<'input> { pub types: Vec, } -#[derive(PartialEq, Debug)] -pub struct ImportedFunction<'input> { - pub namespace: &'input str, - pub name: &'input str, - pub input_types: Vec, - pub output_types: Vec, -} - +/// Represents an adapter. #[derive(PartialEq, Debug)] pub enum Adapter<'input> { + /// An adapter for an imported function. Import { + /// The function namespace. namespace: &'input str, + + /// The function name. name: &'input str, + + /// The function input types. input_types: Vec, + + /// The function output types. output_types: Vec, + + /// The instructions of the adapter. instructions: Vec>, }, + + /// An adapter for an exported function. Export { + /// The function name. name: &'input str, + + /// The function input types. input_types: Vec, + + /// The function output types. output_types: Vec, + + /// The instructions of the adapter. instructions: Vec>, }, + + /// An adapter for a helper function. HelperFunction { + /// The helper name. name: &'input str, + + /// The helper input types. input_types: Vec, + + /// The helper output types. output_types: Vec, + + /// The instructions of the adapter. instructions: Vec>, }, } +/// Represented a forwarded export. #[derive(PartialEq, Debug)] pub struct Forward<'input> { + /// The forwarded export name. pub name: &'input str, } +/// Represents a set of interfaces, i.e. it entirely describes a WIT +/// definition. #[derive(PartialEq, Debug)] pub struct Interfaces<'input> { + /// All the exports. pub exports: Vec>, + + /// All the types. pub types: Vec>, - pub imported_functions: Vec>, + + /// All the imported functions. + pub imports: Vec>, + + /// All the exported functions. pub adapters: Vec>, + + /// All the forwarded functions. pub forwards: Vec>, } From 2fc1fbb7ecebed7c479ede1405a02f06557a019d Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 15:44:28 +0100 Subject: [PATCH 58/84] doc(interface-types) Improve documentation of the `ast` module. --- lib/interface-types/src/ast.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 33609ad54..4adf2f022 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -80,11 +80,16 @@ pub struct Import<'input> { pub output_types: Vec, } -/// Represents a type. +/// Represents a structural type. #[derive(PartialEq, Debug)] pub struct Type<'input> { + /// The type name. pub name: &'input str, + + /// The field names. pub fields: Vec<&'input str>, + + /// The field types. pub types: Vec, } @@ -151,7 +156,7 @@ pub struct Forward<'input> { /// definition. #[derive(PartialEq, Debug)] pub struct Interfaces<'input> { - /// All the exports. + /// All the exported functions. pub exports: Vec>, /// All the types. @@ -160,7 +165,7 @@ pub struct Interfaces<'input> { /// All the imported functions. pub imports: Vec>, - /// All the exported functions. + /// All the adapters. pub adapters: Vec>, /// All the forwarded functions. From 9fda6f945186b770da3725c8559e9bc0cae6c145 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 16:39:06 +0100 Subject: [PATCH 59/84] doc(interface-types) Improve documentation of the `binary` module. --- lib/interface-types/src/decoders/binary.rs | 27 ++++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 2853f7eae..403fc5b6f 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -1,3 +1,5 @@ +//! Parse the WIT binary representation into an AST. + use crate::{ast::*, interpreter::Instruction}; use nom::{ error::{make_error, ErrorKind, ParseError}, @@ -5,6 +7,7 @@ use nom::{ }; use std::{convert::TryFrom, str}; +/// Parse an `InterfaceType`. impl TryFrom for InterfaceType { type Error = &'static str; @@ -25,6 +28,7 @@ impl TryFrom for InterfaceType { } } +/// Parse an adapter kind. impl TryFrom for AdapterKind { type Error = &'static str; @@ -38,6 +42,7 @@ impl TryFrom for AdapterKind { } } +/// Parse a byte. fn byte<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u8, E> { if input.is_empty() { return Err(Err::Error(make_error(input, ErrorKind::Eof))); @@ -46,6 +51,7 @@ fn byte<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'i Ok((&input[1..], input[0])) } +/// Parse an unsigned LEB with value no larger than a 64-bits number. fn leb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u64, E> { if input.is_empty() { return Err(Err::Error(make_error(input, ErrorKind::Eof))); @@ -65,6 +71,7 @@ fn leb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'in )) } +/// Parse a UTF-8 string. fn string<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], &'input str, E> { @@ -84,6 +91,7 @@ fn string<'input, E: ParseError<&'input [u8]>>( })) } +/// Parse a list, with a item parser. #[allow(clippy::type_complexity)] fn list<'input, I, E: ParseError<&'input [u8]>>( input: &'input [u8], @@ -110,6 +118,7 @@ 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> { @@ -125,7 +134,8 @@ fn ty<'input, E: ParseError<&'input [u8]>>( } } -fn instructions<'input, E: ParseError<&'input [u8]>>( +/// Parse an instruction with its arguments. +fn instruction<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Instruction, E> { let (mut input, opcode) = byte(input)?; @@ -243,6 +253,7 @@ fn instructions<'input, E: ParseError<&'input [u8]>>( }) } +/// Parse a list of exports. fn exports<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { @@ -267,6 +278,7 @@ fn exports<'input, E: ParseError<&'input [u8]>>( Ok((input, exports)) } +/// Parse a list of types. fn types<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { @@ -291,6 +303,7 @@ fn types<'input, E: ParseError<&'input [u8]>>( Ok((input, types)) } +/// Parse a list of imports. fn imports<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { @@ -317,6 +330,7 @@ fn imports<'input, E: ParseError<&'input [u8]>>( Ok((input, imports)) } +/// Parse a list of adapters. fn adapters<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { @@ -337,7 +351,7 @@ fn adapters<'input, E: ParseError<&'input [u8]>>( consume!((input, adapter_name) = string(input)?); consume!((input, adapter_input_types) = list(input, ty)?); consume!((input, adapter_output_types) = list(input, ty)?); - consume!((input, adapter_instructions) = list(input, instructions)?); + consume!((input, adapter_instructions) = list(input, instruction)?); adapters.push(Adapter::Import { namespace: adapter_namespace, @@ -352,7 +366,7 @@ fn adapters<'input, E: ParseError<&'input [u8]>>( consume!((input, adapter_name) = string(input)?); consume!((input, adapter_input_types) = list(input, ty)?); consume!((input, adapter_output_types) = list(input, ty)?); - consume!((input, adapter_instructions) = list(input, instructions)?); + consume!((input, adapter_instructions) = list(input, instruction)?); adapters.push(Adapter::Export { name: adapter_name, @@ -366,7 +380,7 @@ fn adapters<'input, E: ParseError<&'input [u8]>>( consume!((input, adapter_name) = string(input)?); consume!((input, adapter_input_types) = list(input, ty)?); consume!((input, adapter_output_types) = list(input, ty)?); - consume!((input, adapter_instructions) = list(input, instructions)?); + consume!((input, adapter_instructions) = list(input, instruction)?); adapters.push(Adapter::HelperFunction { name: adapter_name, @@ -381,6 +395,7 @@ fn adapters<'input, E: ParseError<&'input [u8]>>( Ok((input, adapters)) } +/// Parse a list of forwarded exports. fn forwards<'input, E: ParseError<&'input [u8]>>( input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { @@ -399,6 +414,8 @@ fn forwards<'input, E: ParseError<&'input [u8]>>( Ok((input, forwards)) } +/// Parse a sequence of bytes, expecting it to be a valid WIT binary +/// representation, into an `ast::Interfaces`. pub fn parse<'input, E: ParseError<&'input [u8]>>( bytes: &'input [u8], ) -> IResult<&'input [u8], Interfaces, E> { @@ -569,7 +586,7 @@ mod tests { ], )); - assert_eq!(list::(input, instructions), output); + assert_eq!(list::(input, instruction), output); } #[test] From 98fb0697d1e27929fd09d5a4d19ebca51b24f609 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 16:39:34 +0100 Subject: [PATCH 60/84] test(interface-types) Test the `parse` parser. --- lib/interface-types/src/decoders/binary.rs | 79 ++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 403fc5b6f..4c3af40df 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -770,4 +770,83 @@ mod tests { assert_eq!(forwards::<()>(input), output); } + + #[test] + fn test_parse() { + let input = &[ + 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" + ]; + let output = Ok(( + &[] as &[u8], + Interfaces { + exports: vec![Export { + name: "ab", + input_types: vec![InterfaceType::I32], + output_types: vec![InterfaceType::I32], + }], + types: vec![Type { + name: "ab", + fields: vec!["cd", "e"], + types: 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" }], + }, + )); + + assert_eq!(parse::<()>(input), output); + } } From 55ae0e474ef22f2dbfed0f3d076c92c62aeda600 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 16:48:25 +0100 Subject: [PATCH 61/84] doc(interface-types) Add a doctest for `decoders::binary::parse`. --- lib/interface-types/src/decoders/binary.rs | 88 ++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 4c3af40df..2eb914b3a 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -416,6 +416,94 @@ fn forwards<'input, E: ParseError<&'input [u8]>>( /// Parse a sequence of bytes, expecting it to be a valid WIT binary /// representation, into an `ast::Interfaces`. +/// +/// # Example +/// +/// ```rust +/// use wasmer_interface_types::{ +/// ast::*, +/// decoders::binary::parse, +/// interpreter::Instruction, +/// }; +/// +/// # fn main() { +/// let input = &[ +/// 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" +/// ]; +/// let output = Ok(( +/// &[] as &[u8], +/// Interfaces { +/// exports: vec![Export { +/// name: "ab", +/// input_types: vec![InterfaceType::I32], +/// output_types: vec![InterfaceType::I32], +/// }], +/// types: vec![Type { +/// name: "ab", +/// fields: vec!["cd", "e"], +/// types: 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" }], +/// }, +/// )); +/// +/// assert_eq!(parse::<()>(input), output); +/// # } +/// ``` pub fn parse<'input, E: ParseError<&'input [u8]>>( bytes: &'input [u8], ) -> IResult<&'input [u8], Interfaces, E> { From c5b963b81b8ad7849dbbc275bbda78b1aef8a379 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Mon, 10 Feb 2020 17:12:32 +0100 Subject: [PATCH 62/84] doc(interface-types) Improve documentation of the `wat` module. --- lib/interface-types/src/encoders/wat.rs | 102 ++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 8768da733..fcdc9161b 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -1,8 +1,99 @@ +//! Writes the AST into a string representing WIT with its textual format. +//! +//! # Example +//! +//! ```rust +//! use wasmer_interface_types::{ +//! ast::*, +//! encoders::wat::*, +//! interpreter::Instruction, +//! }; +//! +//! # fn main() { +//! let input: String = (&Interfaces { +//! exports: vec![ +//! Export { +//! name: "foo", +//! input_types: vec![InterfaceType::I32], +//! output_types: vec![], +//! }, +//! Export { +//! name: "bar", +//! input_types: vec![], +//! output_types: vec![], +//! }, +//! ], +//! types: vec![], +//! imports: vec![ +//! Import { +//! namespace: "ns", +//! name: "foo", +//! input_types: vec![], +//! output_types: vec![InterfaceType::I32], +//! }, +//! Import { +//! namespace: "ns", +//! name: "bar", +//! input_types: vec![], +//! output_types: vec![], +//! }, +//! ], +//! adapters: vec![ +//! Adapter::Import { +//! namespace: "ns", +//! name: "foo", +//! input_types: vec![InterfaceType::I32], +//! output_types: vec![], +//! instructions: vec![Instruction::ArgumentGet { index: 42 }], +//! }, +//! Adapter::Export { +//! name: "bar", +//! input_types: vec![], +//! output_types: vec![], +//! instructions: vec![Instruction::ArgumentGet { index: 42 }], +//! }, +//! ], +//! forwards: vec![Forward { name: "main" }], +//! }) +//! .into(); +//! let output = r#";; Interfaces +//! +//! ;; Interface, Export foo +//! (@interface export "foo" +//! (param i32)) +//! +//! ;; Interface, Export bar +//! (@interface export "bar") +//! +//! ;; Interface, Import ns.foo +//! (@interface func $ns_foo (import "ns" "foo") +//! (result i32)) +//! +//! ;; Interface, Import ns.bar +//! (@interface func $ns_bar (import "ns" "bar")) +//! +//! ;; Interface, Adapter ns.foo +//! (@interface adapt (import "ns" "foo") +//! (param i32) +//! arg.get 42) +//! +//! ;; Interface, Adapter bar +//! (@interface adapt (export "bar") +//! arg.get 42) +//! +//! ;; Interface, Forward main +//! (@interface forward (export "main"))"#; +//! +//! assert_eq!(input, output); +//! # } +//! ``` + use crate::{ ast::{Adapter, Export, Forward, Import, InterfaceType, Interfaces, Type}, interpreter::Instruction, }; +/// Encode an `InterfaceType` into a string. impl From<&InterfaceType> for String { fn from(interface_type: &InterfaceType) -> Self { match interface_type { @@ -20,6 +111,7 @@ impl From<&InterfaceType> for String { } } +/// Encode an `Instruction` into a string. impl<'input> From<&Instruction<'input>> for String { fn from(instruction: &Instruction) -> Self { match instruction { @@ -69,6 +161,8 @@ impl<'input> From<&Instruction<'input>> for String { } } +/// Encode a list of `InterfaceType` representing inputs into a +/// string. fn input_types_to_param(input_types: &[InterfaceType]) -> String { if input_types.is_empty() { "".into() @@ -86,6 +180,8 @@ fn input_types_to_param(input_types: &[InterfaceType]) -> String { } } +/// Encode a list of `InterfaceType` representing outputs into a +/// string. fn output_types_to_result(output_types: &[InterfaceType]) -> String { if output_types.is_empty() { "".into() @@ -103,6 +199,7 @@ fn output_types_to_result(output_types: &[InterfaceType]) -> String { } } +/// Encode an `Export` into a string. impl<'input> From<&Export<'input>> for String { fn from(export: &Export) -> Self { format!( @@ -114,12 +211,14 @@ impl<'input> From<&Export<'input>> for String { } } +/// Encode a `Type` into a string. impl<'input> From<&Type<'input>> for String { fn from(_ty: &Type) -> Self { unimplemented!() } } +/// Encode an `Import` into a string. impl<'input> From<&Import<'input>> for String { fn from(import: &Import) -> Self { format!( @@ -132,6 +231,7 @@ impl<'input> From<&Import<'input>> for String { } } +/// Encode an `Adapter` into a string. impl<'input> From<&Adapter<'input>> for String { fn from(adapter: &Adapter) -> Self { match adapter { @@ -182,6 +282,7 @@ impl<'input> From<&Adapter<'input>> for String { } } +/// Encode a `Forward` into a string. impl<'input> From<&Forward<'input>> for String { fn from(forward: &Forward) -> Self { format!( @@ -191,6 +292,7 @@ impl<'input> From<&Forward<'input>> for String { } } +/// Encode an `Interfaces` into a string. impl<'input> From<&Interfaces<'input>> for String { fn from(interfaces: &Interfaces) -> Self { let mut output = String::from(";; Interfaces"); From 98c73099c3e9f370d612e4664611a2f7191a88bc Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 12 Feb 2020 15:52:15 +0100 Subject: [PATCH 63/84] doc(interface-types) Improve documentation of the `macros` module. --- lib/interface-types/src/macros.rs | 47 ++++++++++++++++++------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/lib/interface-types/src/macros.rs b/lib/interface-types/src/macros.rs index a0b99940d..735cecd3c 100644 --- a/lib/interface-types/src/macros.rs +++ b/lib/interface-types/src/macros.rs @@ -1,22 +1,5 @@ -#[allow(unused)] -macro_rules! d { - ($expression:expr) => { - match $expression { - tmp => { - eprintln!( - "[{}:{}] {} = {:?}", - file!(), - line!(), - stringify!($expression), - &tmp - ); - - tmp - } - } - }; -} - +/// This macro runs a parser, extracts the next input and the parser +/// output, and positions the next input on `$input`. macro_rules! consume { (($input:ident, $parser_output:ident) = $parser_expression:expr) => { let (next_input, $parser_output) = $parser_expression; @@ -24,6 +7,32 @@ macro_rules! consume { }; } +/// This macro creates an executable instruction for the interpreter. +/// +/// # Example +/// +/// The following example creates a `foo` executable instruction, +/// which takes 2 arguments (`x` and `y`), and does something +/// mysterious by using the `interpreter::Runtime` API. +/// +/// ```rust,ignore +/// executable_instruction!( +/// foo(x: u64, y: u64, instruction_name: String) -> _ { +/// // ^ output type is purposely blank +/// // ^^^^^^^^^^^^^^^^ the instruction name, for debugging purposes +/// // ^ the `y` argument +/// // ^ the `x` argument +/// +/// // an executable instruction is a closure that takes a `Runtime` instance +/// move |runtime| -> _ { +/// // Do something. +/// +/// Ok(()) +/// } +/// ); +/// ``` +/// +/// Check the existing executable instruction to get more examples. macro_rules! executable_instruction { ($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => { use crate::interpreter::{ExecutableInstruction, wasm, stack::Stackable}; From b3c102da37372c0008d98f924c92af0f6d8540b9 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 12 Feb 2020 15:59:41 +0100 Subject: [PATCH 64/84] doc(interface-types) Improve documentation of the `instruction` module. Also, rename `RepeatWhile` to `RepeatUntil`. --- lib/interface-types/src/decoders/binary.rs | 6 +-- lib/interface-types/src/encoders/wat.rs | 8 ++-- .../src/interpreter/instruction.rs | 42 ++++++++++++++++++- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 2eb914b3a..afa549ab2 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -246,7 +246,7 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x14 => { consume!((input, argument_0) = leb(input)?); consume!((input, argument_1) = leb(input)?); - (input, Instruction::RepeatWhile(argument_0, argument_1)) + (input, Instruction::RepeatUntil(argument_0, argument_1)) } _ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))), @@ -643,7 +643,7 @@ mod tests { 0x11, 0x7f, 0x03, 0x61, 0x62, 0x63, // Load(I32, "abc") 0x12, 0x7f, // SeqNew(I32) 0x13, // ListPush - 0x14, 0x01, 0x02, // RepeatWhile(1, 2) + 0x14, 0x01, 0x02, // RepeatUntil(1, 2) 0x0a, ]; let output = Ok(( @@ -670,7 +670,7 @@ mod tests { Instruction::Load(InterfaceType::I32, "abc"), Instruction::SeqNew(InterfaceType::I32), Instruction::ListPush, - Instruction::RepeatWhile(1, 2), + Instruction::RepeatUntil(1, 2), ], )); diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index fcdc9161b..085732959 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -154,8 +154,8 @@ impl<'input> From<&Instruction<'input>> for String { format!("seq.new {}", String::from(interface_type)) } Instruction::ListPush => "list.push".into(), - Instruction::RepeatWhile(condition_index, step_index) => { - format!("repeat-while {} {}", condition_index, step_index) + Instruction::RepeatUntil(condition_index, step_index) => { + format!("repeat-until {} {}", condition_index, step_index) } } } @@ -420,7 +420,7 @@ mod tests { (&Instruction::Load(InterfaceType::Int, "foo")).into(), (&Instruction::SeqNew(InterfaceType::Int)).into(), (&Instruction::ListPush).into(), - (&Instruction::RepeatWhile(1, 2)).into(), + (&Instruction::RepeatUntil(1, 2)).into(), ]; let outputs = vec![ "arg.get 7", @@ -442,7 +442,7 @@ mod tests { r#"load Int "foo""#, "seq.new Int", "list.push", - "repeat-while 1 2", + "repeat-until 1 2", ]; assert_eq!(inputs, outputs); diff --git a/lib/interface-types/src/interpreter/instruction.rs b/lib/interface-types/src/interpreter/instruction.rs index e474d2daf..20fbd4034 100644 --- a/lib/interface-types/src/interpreter/instruction.rs +++ b/lib/interface-types/src/interpreter/instruction.rs @@ -1,25 +1,65 @@ use crate::ast::InterfaceType; +/// Represents all the possible WIT instructions. #[derive(PartialEq, Debug)] pub enum Instruction<'input> { + /// `arg.get` ArgumentGet { index: u64 }, + + /// `call` Call { function_index: usize }, + + /// `call-export` CallExport { export_name: &'input str }, + + /// `read-utf8` ReadUtf8, + + /// `write-utf8` WriteUtf8 { allocator_name: &'input str }, + + /// `as-wasm` AsWasm(InterfaceType), + + /// `as-interface` AsInterface(InterfaceType), + + /// `table-ref-add` TableRefAdd, + + /// `table-ref-get` TableRefGet, + + /// `call-method` CallMethod(u64), + + /// `make-record` MakeRecord(InterfaceType), + + /// `get-field` GetField(InterfaceType, u64), + + /// `const` Const(InterfaceType, u64), + + /// `fold-seq` FoldSeq(u64), + + /// `add` Add(InterfaceType), + + /// `mem-to-seq` MemToSeq(InterfaceType, &'input str), + + /// `load` Load(InterfaceType, &'input str), + + /// `seq.new` SeqNew(InterfaceType), + + /// `list.push` ListPush, - RepeatWhile(u64, u64), + + /// `repeat-until` + RepeatUntil(u64, u64), } From 6b0e43b7c48d436597c0b35d9f35f2e243099811 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 12 Feb 2020 16:01:33 +0100 Subject: [PATCH 65/84] doc(interface-types) Improve the documentation of the `instruction` module. --- .../src/interpreter/instruction.rs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/interface-types/src/interpreter/instruction.rs b/lib/interface-types/src/interpreter/instruction.rs index 20fbd4034..ddf8be627 100644 --- a/lib/interface-types/src/interpreter/instruction.rs +++ b/lib/interface-types/src/interpreter/instruction.rs @@ -3,63 +3,63 @@ use crate::ast::InterfaceType; /// Represents all the possible WIT instructions. #[derive(PartialEq, Debug)] pub enum Instruction<'input> { - /// `arg.get` + /// The `arg.get` instruction. ArgumentGet { index: u64 }, - /// `call` + /// The `call` instruction. Call { function_index: usize }, - /// `call-export` + /// The `call-export` instruction. CallExport { export_name: &'input str }, - /// `read-utf8` + /// The `read-utf8` instruction. ReadUtf8, - /// `write-utf8` + /// The `write-utf8` instruction. WriteUtf8 { allocator_name: &'input str }, - /// `as-wasm` + /// The `as-wasm` instruction. AsWasm(InterfaceType), - /// `as-interface` + /// The `as-interface` instruction. AsInterface(InterfaceType), - /// `table-ref-add` + /// The `table-ref-add` instruction. TableRefAdd, - /// `table-ref-get` + /// The `table-ref-get` instruction. TableRefGet, - /// `call-method` + /// The `call-method` instruction. CallMethod(u64), - /// `make-record` + /// The `make-record` instruction. MakeRecord(InterfaceType), - /// `get-field` + /// The `get-field` instruction. GetField(InterfaceType, u64), - /// `const` + /// The `const` instruction. Const(InterfaceType, u64), - /// `fold-seq` + /// The `fold-seq` instruction. FoldSeq(u64), - /// `add` + /// The `add` instruction. Add(InterfaceType), - /// `mem-to-seq` + /// The `mem-to-seq` instruction. MemToSeq(InterfaceType, &'input str), - /// `load` + /// The `load` instruction. Load(InterfaceType, &'input str), - /// `seq.new` + /// The `seq.new` instruction. SeqNew(InterfaceType), - /// `list.push` + /// The `list.push` instruction. ListPush, - /// `repeat-until` + /// The `repeat-until` instruction. RepeatUntil(u64, u64), } From 13b9cb3995e91d4db9c4557d770ab1332a731b8a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 12 Feb 2020 17:37:06 +0100 Subject: [PATCH 66/84] doc(interface-types) Improve the documentation of the `interpreter` module. --- lib/interface-types/src/interpreter/mod.rs | 96 +++++++++++++++++++ .../src/interpreter/wasm/mod.rs | 4 + 2 files changed, 100 insertions(+) diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index d9452abce..26916dfe3 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -10,6 +10,8 @@ use stack::Stack; use std::{convert::TryFrom, marker::PhantomData}; use wasm::values::InterfaceValue; +/// Represents the `Runtime`, which is used by an adapter to execute +/// its instructions. pub(crate) struct Runtime<'invocation, 'instance, Instance, Export, LocalImport, Memory, MemoryView> where Export: wasm::structures::Export + 'instance, @@ -18,16 +20,104 @@ where MemoryView: wasm::structures::MemoryView, Instance: wasm::structures::Instance + 'instance, { + /// The invocation inputs are all the arguments received by an + /// adapter. invocation_inputs: &'invocation [InterfaceValue], + + /// Each runtime (so adapter) has its own stack instance. stack: Stack, + + /// The WebAssembly module instance. It is used by adapter's + /// instructions. wasm_instance: &'instance mut Instance, + + /// Phantom data. _phantom: PhantomData<(Export, LocalImport, Memory, MemoryView)>, } +/// Type alias for an executable instruction. It's an implementation +/// details, but an instruction is a boxed closure instance. pub(crate) type ExecutableInstruction = Box< dyn Fn(&mut Runtime) -> Result<(), String>, >; +/// An interpreter is the central piece of this crate. It is a set of +/// executable instructions. Each instruction takes the runtime as +/// argument. The runtime holds the invocation inputs, the stack, and +/// the WebAssembly instance. +/// +/// When the interpreter executes the instructions, each of them can +/// query the WebAssembly instance, operates on the stack, or reads +/// the invocation inputs. At the end of the execution, the stack +/// supposedly contains a result. Since an interpreter is used by a +/// WIT adapter to execute its instructions, the result on the stack +/// is the result of the adapter. +/// +/// # Example +/// +/// ```rust,ignore +/// use std::{cell::Cell, collections::HashMap, convert::TryInto}; +/// use wasmer_interface_types::interpreter::{ +/// instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView}, +/// // ^^^^^^^^^^^^ This is private and for testing purposes only. +/// // It is basically a fake WebAssembly runtime. +/// stack::Stackable, +/// wasm::values::{InterfaceType, InterfaceValue}, +/// Instruction, Interpreter, +/// }; +/// +/// # fn main() { +/// // 1. Creates an interpreter from a set of instructions. They will +/// // be transformed into executable instructions. +/// let interpreter: Interpreter = (&vec![ +/// Instruction::ArgumentGet { index: 1 }, +/// Instruction::ArgumentGet { index: 0 }, +/// Instruction::CallExport { export_name: "sum" }, +/// ]) +/// .try_into() +/// .unwrap(); +/// +/// // 2. Defines the arguments of the adapter. +/// let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)]; +/// +/// // 3. Creates a WebAssembly instance. +/// let mut instance = Instance { +/// // 3.1. Defines one exported function: `fn sum(a: i32, b: i32) -> i32 { a + b }`. +/// exports: { +/// let mut hashmap = HashMap::new(); +/// hashmap.insert( +/// "sum".into(), +/// Export { +/// // Defines the argument types of the function. +/// inputs: vec![InterfaceType::I32, InterfaceType::I32], +/// +/// // Defines the result types. +/// outputs: vec![InterfaceType::I32], +/// +/// // Defines the function implementation. +/// function: |arguments: &[InterfaceValue]| { +/// let a: i32 = (&arguments[0]).try_into().unwrap(); +/// let b: i32 = (&arguments[1]).try_into().unwrap(); +/// +/// Ok(vec![InterfaceValue::I32(a + b)]) +/// }, +/// }, +/// ); +/// }, +/// ..Default::default() +/// }; +/// +/// // 4. Executes the instructions. +/// let run = interpreter.run(&invocation_inputs, &mut instance); +/// +/// assert!(run.is_ok()); +/// +/// let stack = run.unwrap(); +/// +/// // 5. Read the stack to get the result. +/// assert_eq!(stack.as_slice(), &[InterfaceValue::I32(7)]); +/// # } +/// ``` pub struct Interpreter where Export: wasm::structures::Export, @@ -57,6 +147,11 @@ where self.executable_instructions.iter() } + /// Runs the interpreter, such as: + /// 1. Create a fresh stack, + /// 2. Create a fresh stack, + /// 3. Execute the instructions one after the other, and + /// returns the stack. pub fn run( &self, invocation_inputs: &[InterfaceValue], @@ -80,6 +175,7 @@ where } } +/// Transforms a `Vec` into an `Interpreter`. impl<'binary_input, Instance, Export, LocalImport, Memory, MemoryView> TryFrom<&Vec>> for Interpreter diff --git a/lib/interface-types/src/interpreter/wasm/mod.rs b/lib/interface-types/src/interpreter/wasm/mod.rs index 0ff75a9af..1edccc592 100644 --- a/lib/interface-types/src/interpreter/wasm/mod.rs +++ b/lib/interface-types/src/interpreter/wasm/mod.rs @@ -1,2 +1,6 @@ +//! An hypothetic WebAssembly runtime, represented as a set of enums, +//! types, and traits —basically this is the part a runtime should +//! take a look to use the `wasmer-interface-types` crate—. + pub mod structures; pub mod values; From 62e15260378596e1de6aeb1ff0040b97e4492275 Mon Sep 17 00:00:00 2001 From: Syrus Date: Wed, 12 Feb 2020 23:00:30 -0800 Subject: [PATCH 67/84] Fixed WASI isatty --- lib/wasi-tests/tests/wasitests/isatty.rs | 15 +++++++++++++++ lib/wasi-tests/tests/wasitests/mod.rs | 1 + lib/wasi-tests/wasitests/isatty.out | 3 +++ lib/wasi-tests/wasitests/isatty.rs | 11 +++++++++++ lib/wasi-tests/wasitests/isatty.wasm | Bin 0 -> 71573 bytes lib/wasi/src/state/mod.rs | 22 +++++++++++++++++++--- 6 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 lib/wasi-tests/tests/wasitests/isatty.rs create mode 100644 lib/wasi-tests/wasitests/isatty.out create mode 100644 lib/wasi-tests/wasitests/isatty.rs create mode 100755 lib/wasi-tests/wasitests/isatty.wasm diff --git a/lib/wasi-tests/tests/wasitests/isatty.rs b/lib/wasi-tests/tests/wasitests/isatty.rs new file mode 100644 index 000000000..55c97a606 --- /dev/null +++ b/lib/wasi-tests/tests/wasitests/isatty.rs @@ -0,0 +1,15 @@ +// !!! THIS IS A GENERATED FILE !!! +// ANY MANUAL EDITS MAY BE OVERWRITTEN AT ANY TIME +// Files autogenerated with cargo build (build/wasitests.rs). + +#[test] +fn test_isatty() { + assert_wasi_output!( + "../../wasitests/isatty.wasm", + "isatty", + vec![], + vec![], + vec![], + "../../wasitests/isatty.out" + ); +} diff --git a/lib/wasi-tests/tests/wasitests/mod.rs b/lib/wasi-tests/tests/wasitests/mod.rs index d226dc0ee..59092573a 100644 --- a/lib/wasi-tests/tests/wasitests/mod.rs +++ b/lib/wasi-tests/tests/wasitests/mod.rs @@ -18,6 +18,7 @@ mod file_metadata; mod fs_sandbox_test; mod fseek; mod hello; +mod isatty; mod mapdir; mod path_link; mod path_rename; diff --git a/lib/wasi-tests/wasitests/isatty.out b/lib/wasi-tests/wasitests/isatty.out new file mode 100644 index 000000000..497b57818 --- /dev/null +++ b/lib/wasi-tests/wasitests/isatty.out @@ -0,0 +1,3 @@ +stdin: 1 +stdout: 1 +stderr: 1 diff --git a/lib/wasi-tests/wasitests/isatty.rs b/lib/wasi-tests/wasitests/isatty.rs new file mode 100644 index 000000000..5310b5e7d --- /dev/null +++ b/lib/wasi-tests/wasitests/isatty.rs @@ -0,0 +1,11 @@ +// use libc::isatty; + +extern "C" { + pub fn isatty(fd: i32) -> i32; +} + +fn main() { + println!("stdin: {}", unsafe { isatty(0) }); + println!("stdout: {}", unsafe { isatty(1) }); + println!("stderr: {}", unsafe { isatty(2) }); +} diff --git a/lib/wasi-tests/wasitests/isatty.wasm b/lib/wasi-tests/wasitests/isatty.wasm new file mode 100755 index 0000000000000000000000000000000000000000..5a414754f9ff0368f537ee9297a68a6469580fc3 GIT binary patch literal 71573 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 literal 0 HcmV?d00001 diff --git a/lib/wasi/src/state/mod.rs b/lib/wasi/src/state/mod.rs index ceb72fdc8..f7d022aa0 100644 --- a/lib/wasi/src/state/mod.rs +++ b/lib/wasi/src/state/mod.rs @@ -1045,12 +1045,28 @@ impl WasiFs { pub fn fdstat(&self, fd: __wasi_fd_t) -> Result<__wasi_fdstat_t, __wasi_errno_t> { match fd { - __WASI_STDOUT_FILENO => { + __WASI_STDIN_FILENO => { return Ok(__wasi_fdstat_t { fs_filetype: __WASI_FILETYPE_CHARACTER_DEVICE, fs_flags: 0, - fs_rights_base: ALL_RIGHTS, - fs_rights_inheriting: ALL_RIGHTS, + fs_rights_base: STDIN_DEFAULT_RIGHTS, + fs_rights_inheriting: 0, + }) + } + __WASI_STDOUT_FILENO => { + return Ok(__wasi_fdstat_t { + fs_filetype: __WASI_FILETYPE_CHARACTER_DEVICE, + fs_flags: __WASI_FDFLAG_APPEND, + fs_rights_base: STDOUT_DEFAULT_RIGHTS, + fs_rights_inheriting: 0, + }) + } + __WASI_STDERR_FILENO => { + return Ok(__wasi_fdstat_t { + fs_filetype: __WASI_FILETYPE_CHARACTER_DEVICE, + fs_flags: __WASI_FDFLAG_APPEND, + fs_rights_base: STDERR_DEFAULT_RIGHTS, + fs_rights_inheriting: 0, }) } _ => (), From b7941f401957a831a7e1d15003888d8290bd6e23 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 11:24:29 +0100 Subject: [PATCH 68/84] feat(interface-types) Ensure `ast::Type` is always well-formed. As @MarkMcCaskey noted, `Type` can be corrupted because `field_names` and `field_types` must have the same length. This patch removes the public visibility, and adds methods like `new`, `add_field`, `field_names` and `field_types` to encapsulate `Type` internal data. --- lib/interface-types/src/ast.rs | 40 ++++++++++++++++++++-- lib/interface-types/src/decoders/binary.rs | 36 +++++++++---------- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 4adf2f022..d8b82f588 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -87,10 +87,46 @@ pub struct Type<'input> { pub name: &'input str, /// The field names. - pub fields: Vec<&'input str>, + field_names: Vec<&'input str>, /// The field types. - pub types: Vec, + 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. diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index afa549ab2..027e8e5fc 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -293,11 +293,7 @@ fn types<'input, E: ParseError<&'input [u8]>>( consume!((input, type_fields) = list(input, string)?); consume!((input, type_types) = list(input, ty)?); - types.push(Type { - name: type_name, - fields: type_fields, - types: type_types, - }); + types.push(Type::new(type_name, type_fields, type_types)); } Ok((input, types)) @@ -479,11 +475,11 @@ fn forwards<'input, E: ParseError<&'input [u8]>>( /// input_types: vec![InterfaceType::I32], /// output_types: vec![InterfaceType::I32], /// }], -/// types: vec![Type { -/// name: "ab", -/// fields: vec!["cd", "e"], -/// types: vec![InterfaceType::I32, InterfaceType::I32], -/// }], +/// types: vec![Type::new( +/// "ab", +/// vec!["cd", "e"], +/// vec![InterfaceType::I32, InterfaceType::I32], +/// )], /// imports: vec![Import { /// namespace: "a", /// name: "b", @@ -728,11 +724,11 @@ mod tests { ]; let output = Ok(( &[] as &[u8], - vec![Type { - name: "ab", - fields: vec!["cd", "e"], - types: vec![InterfaceType::I32, InterfaceType::I32], - }], + vec![Type::new( + "ab", + vec!["cd", "e"], + vec![InterfaceType::I32, InterfaceType::I32], + )], )); assert_eq!(types::<()>(input), output); @@ -913,11 +909,11 @@ mod tests { input_types: vec![InterfaceType::I32], output_types: vec![InterfaceType::I32], }], - types: vec![Type { - name: "ab", - fields: vec!["cd", "e"], - types: vec![InterfaceType::I32, InterfaceType::I32], - }], + types: vec![Type::new( + "ab", + vec!["cd", "e"], + vec![InterfaceType::I32, InterfaceType::I32], + )], imports: vec![Import { namespace: "a", name: "b", From 1ad42d81cb7b39240adb65247bea942603e8cb5f Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 11:41:02 +0100 Subject: [PATCH 69/84] doc(interface-types) Improve documentation of `decoders::binary::leb`. --- lib/interface-types/src/decoders/binary.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 027e8e5fc..e0a3fad1a 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -51,7 +51,9 @@ fn byte<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'i Ok((&input[1..], input[0])) } -/// Parse an unsigned LEB with value no larger than a 64-bits number. +/// Parse an unsigned Little Endian Based (LEB) with value no larger +/// than a 64-bits number. Read +/// [LEB128](https://en.wikipedia.org/wiki/LEB128) to learn more. fn leb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u64, E> { if input.is_empty() { return Err(Err::Error(make_error(input, ErrorKind::Eof))); From 370fd6d86675d3255dbbed3f012fecb39a28d34a Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 13:36:18 +0100 Subject: [PATCH 70/84] feat(decoders) Improve LEB parser in the `binary` module. The LEB parser is renamed `uleb`. It now checks for overflow, and badly-formed bits, resp. `TooLarge` or `Eof`. More test cases are added, whose from the DWARF 4 standard. --- lib/interface-types/src/decoders/binary.rs | 99 +++++++++++++++++----- 1 file changed, 76 insertions(+), 23 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index e0a3fad1a..64cf1e7c7 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -53,15 +53,18 @@ fn byte<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'i /// Parse an unsigned Little Endian Based (LEB) with value no larger /// than a 64-bits number. Read -/// [LEB128](https://en.wikipedia.org/wiki/LEB128) to learn more. -fn leb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u64, E> { +/// [LEB128](https://en.wikipedia.org/wiki/LEB128) to learn more, or +/// the Variable Length Data Section from the [DWARF 4 +/// standard](http://dwarfstd.org/doc/DWARF4.pdf). +fn uleb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u64, E> { if input.is_empty() { return Err(Err::Error(make_error(input, ErrorKind::Eof))); } - let (output, bytes) = match input.iter().position(|&byte| byte & 0x80 == 0) { - Some(position) => (&input[position + 1..], &input[..=position]), - None => (&[] as &[u8], input), + let (output, bytes) = match dbg!(input.iter().position(|&byte| byte & 0x80 == 0)) { + Some(length) if length <= 8 => (&input[length + 1..], &input[..=length]), + Some(_) => return Err(Err::Error(make_error(input, ErrorKind::TooLarge))), + None => return Err(Err::Error(make_error(input, ErrorKind::Eof))), }; Ok(( @@ -128,7 +131,7 @@ fn ty<'input, E: ParseError<&'input [u8]>>( return Err(Err::Error(make_error(input, ErrorKind::Eof))); } - let (output, ty) = leb(input)?; + let (output, ty) = uleb(input)?; match InterfaceType::try_from(ty) { Ok(ty) => Ok((output, ty)), @@ -144,12 +147,12 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( Ok(match opcode { 0x00 => { - consume!((input, argument_0) = leb(input)?); + consume!((input, argument_0) = uleb(input)?); (input, Instruction::ArgumentGet { index: argument_0 }) } 0x01 => { - consume!((input, argument_0) = leb(input)?); + consume!((input, argument_0) = uleb(input)?); ( input, Instruction::Call { @@ -195,7 +198,7 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x08 => (input, Instruction::TableRefGet), 0x09 => { - consume!((input, argument_0) = leb(input)?); + consume!((input, argument_0) = uleb(input)?); (input, Instruction::CallMethod(argument_0)) } @@ -206,18 +209,18 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x0c => { consume!((input, argument_0) = ty(input)?); - consume!((input, argument_1) = leb(input)?); + consume!((input, argument_1) = uleb(input)?); (input, Instruction::GetField(argument_0, argument_1)) } 0x0d => { consume!((input, argument_0) = ty(input)?); - consume!((input, argument_1) = leb(input)?); + consume!((input, argument_1) = uleb(input)?); (input, Instruction::Const(argument_0, argument_1)) } 0x0e => { - consume!((input, argument_0) = leb(input)?); + consume!((input, argument_0) = uleb(input)?); (input, Instruction::FoldSeq(argument_0)) } @@ -246,8 +249,8 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( 0x13 => (input, Instruction::ListPush), 0x14 => { - consume!((input, argument_0) = leb(input)?); - consume!((input, argument_1) = leb(input)?); + consume!((input, argument_0) = uleb(input)?); + consume!((input, argument_1) = uleb(input)?); (input, Instruction::RepeatUntil(argument_0, argument_1)) } @@ -261,7 +264,7 @@ fn exports<'input, E: ParseError<&'input [u8]>>( ) -> IResult<&'input [u8], Vec, E> { let mut input = input; - consume!((input, number_of_exports) = leb(input)?); + consume!((input, number_of_exports) = uleb(input)?); let mut exports = Vec::with_capacity(number_of_exports as usize); @@ -286,7 +289,7 @@ fn types<'input, E: ParseError<&'input [u8]>>( ) -> IResult<&'input [u8], Vec, E> { let mut input = input; - consume!((input, number_of_types) = leb(input)?); + consume!((input, number_of_types) = uleb(input)?); let mut types = Vec::with_capacity(number_of_types as usize); @@ -307,7 +310,7 @@ fn imports<'input, E: ParseError<&'input [u8]>>( ) -> IResult<&'input [u8], Vec, E> { let mut input = input; - consume!((input, number_of_imports) = leb(input)?); + consume!((input, number_of_imports) = uleb(input)?); let mut imports = Vec::with_capacity(number_of_imports as usize); @@ -334,7 +337,7 @@ fn adapters<'input, E: ParseError<&'input [u8]>>( ) -> IResult<&'input [u8], Vec, E> { let mut input = input; - consume!((input, number_of_adapters) = leb(input)?); + consume!((input, number_of_adapters) = uleb(input)?); let mut adapters = Vec::with_capacity(number_of_adapters as usize); @@ -399,7 +402,7 @@ fn forwards<'input, E: ParseError<&'input [u8]>>( ) -> IResult<&'input [u8], Vec, E> { let mut input = input; - consume!((input, number_of_forwards) = leb(input)?); + consume!((input, number_of_forwards) = uleb(input)?); let mut forwards = Vec::with_capacity(number_of_forwards as usize); @@ -528,6 +531,7 @@ pub fn parse<'input, E: ParseError<&'input [u8]>>( #[cfg(test)] mod tests { use super::*; + use nom::{error, Err}; #[test] fn test_byte() { @@ -538,19 +542,68 @@ mod tests { } #[test] - fn test_leb_1_byte() { + fn test_uleb_1_byte() { let input = &[0x01, 0x02, 0x03]; let output = Ok((&[0x02, 0x03][..], 0x01u64)); - assert_eq!(leb::<()>(input), output); + assert_eq!(uleb::<()>(input), output); } #[test] - fn test_leb_3_bytes() { + fn test_uleb_3_bytes() { let input = &[0xfc, 0xff, 0x01, 0x02]; let output = Ok((&[0x02][..], 0x7ffcu64)); - assert_eq!(leb::<()>(input), output); + assert_eq!(uleb::<()>(input), output); + } + + // Examples from Figure 22 of [DWARF 4 + // standard](http://dwarfstd.org/doc/DWARF4.pdf). + #[test] + fn test_uleb_from_dwarf_standard() { + macro_rules! assert_uleb { + ($to_parse:expr => $expected_result:expr) => { + assert_eq!(uleb::<()>($to_parse), Ok((&[][..], $expected_result))); + }; + } + + assert_uleb!(&[2u8] => 2u64); + assert_uleb!(&[127u8] => 127u64); + assert_uleb!(&[0x80, 1u8] => 128u64); + assert_uleb!(&[1u8 | 0x80, 1] => 129u64); + assert_uleb!(&[2u8 | 0x80, 1] => 130u64); + assert_uleb!(&[57u8 | 0x80, 100] => 12857u64); + } + + #[test] + fn test_uleb_eof() { + let input = &[0x80]; + + assert_eq!( + uleb::<(&[u8], error::ErrorKind)>(input), + Err(Err::Error((&input[..], error::ErrorKind::Eof))), + ); + } + + #[test] + fn test_uleb_overflow() { + let input = &[ + 0x01 | 0x80, + 0x02 | 0x80, + 0x03 | 0x80, + 0x04 | 0x80, + 0x05 | 0x80, + 0x06 | 0x80, + 0x07 | 0x80, + 0x08 | 0x80, + 0x09 | 0x80, + 0x0a, + ]; + + assert_eq!( + uleb::<(&[u8], error::ErrorKind)>(input), + Err(Err::Error((&input[..], error::ErrorKind::TooLarge))), + ); } #[test] From 3f0c32bf44bfb735749ed20a17eeac82884d8792 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 13:52:23 +0100 Subject: [PATCH 71/84] fix(decoders) Remove the `unsafe` block in the `string` parser. Instead of using `str::from_utf8_unchecked`, this patch updates the code to use `str::from_utf8` and handles the error appropriately. --- lib/interface-types/src/decoders/binary.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 64cf1e7c7..642635e3a 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -91,9 +91,11 @@ fn string<'input, E: ParseError<&'input [u8]>>( return Err(Err::Error(make_error(input, ErrorKind::Eof))); } - Ok((&input[length..], unsafe { - str::from_utf8_unchecked(&input[..length]) - })) + Ok(( + &input[length..], + str::from_utf8(&input[..length]) + .map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?, + )) } /// Parse a list, with a item parser. From d8f923091f89686faa99fc7a8af378873f86c514 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 13:54:26 +0100 Subject: [PATCH 72/84] chore(decoders) Simplify code. --- lib/interface-types/src/decoders/binary.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index 642635e3a..ce5c2c04b 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -400,10 +400,8 @@ fn adapters<'input, E: ParseError<&'input [u8]>>( /// Parse a list of forwarded exports. fn forwards<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], + mut input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { - let mut input = input; - consume!((input, number_of_forwards) = uleb(input)?); let mut forwards = Vec::with_capacity(number_of_forwards as usize); From 722727bd56426ada1625dde3c3cce4cbaf00fb25 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 13:56:30 +0100 Subject: [PATCH 73/84] chore(decoders) Simplify code. --- lib/interface-types/src/decoders/binary.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/interface-types/src/decoders/binary.rs b/lib/interface-types/src/decoders/binary.rs index ce5c2c04b..270fc13a7 100644 --- a/lib/interface-types/src/decoders/binary.rs +++ b/lib/interface-types/src/decoders/binary.rs @@ -262,10 +262,8 @@ fn instruction<'input, E: ParseError<&'input [u8]>>( /// Parse a list of exports. fn exports<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], + mut input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { - let mut input = input; - consume!((input, number_of_exports) = uleb(input)?); let mut exports = Vec::with_capacity(number_of_exports as usize); @@ -287,10 +285,8 @@ fn exports<'input, E: ParseError<&'input [u8]>>( /// Parse a list of types. fn types<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], + mut input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { - let mut input = input; - consume!((input, number_of_types) = uleb(input)?); let mut types = Vec::with_capacity(number_of_types as usize); @@ -308,10 +304,8 @@ fn types<'input, E: ParseError<&'input [u8]>>( /// Parse a list of imports. fn imports<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], + mut input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { - let mut input = input; - consume!((input, number_of_imports) = uleb(input)?); let mut imports = Vec::with_capacity(number_of_imports as usize); @@ -335,10 +329,8 @@ fn imports<'input, E: ParseError<&'input [u8]>>( /// Parse a list of adapters. fn adapters<'input, E: ParseError<&'input [u8]>>( - input: &'input [u8], + mut input: &'input [u8], ) -> IResult<&'input [u8], Vec, E> { - let mut input = input; - consume!((input, number_of_adapters) = uleb(input)?); let mut adapters = Vec::with_capacity(number_of_adapters as usize); From 13cee90d17620c76512f315a061d6d5442f11bbd Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 14:19:31 +0100 Subject: [PATCH 74/84] =?UTF-8?q?feat(encoders)=20Implement=20`ToString`?= =?UTF-8?q?=20instead=20of=20`From<=E2=80=A6>=20for=20String`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/interface-types/src/encoders/wat.rs | 249 ++++++++++----------- lib/interface-types/src/interpreter/mod.rs | 2 +- 2 files changed, 124 insertions(+), 127 deletions(-) diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index 085732959..bc4ab5b59 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -55,7 +55,7 @@ //! ], //! forwards: vec![Forward { name: "main" }], //! }) -//! .into(); +//! .to_string(); //! let output = r#";; Interfaces //! //! ;; Interface, Export foo @@ -92,11 +92,12 @@ use crate::{ ast::{Adapter, Export, Forward, Import, InterfaceType, Interfaces, Type}, interpreter::Instruction, }; +use std::string::ToString; /// Encode an `InterfaceType` into a string. -impl From<&InterfaceType> for String { - fn from(interface_type: &InterfaceType) -> Self { - match interface_type { +impl ToString for &InterfaceType { + fn to_string(&self) -> String { + match self { InterfaceType::Int => "Int".into(), InterfaceType::Float => "Float".into(), InterfaceType::Any => "Any".into(), @@ -112,9 +113,9 @@ impl From<&InterfaceType> for String { } /// Encode an `Instruction` into a string. -impl<'input> From<&Instruction<'input>> for String { - fn from(instruction: &Instruction) -> Self { - match instruction { +impl<'input> ToString for &Instruction<'input> { + fn to_string(&self) -> String { + match self { Instruction::ArgumentGet { index } => format!("arg.get {}", index), Instruction::Call { function_index } => format!("call {}", function_index), Instruction::CallExport { export_name } => format!(r#"call-export "{}""#, export_name), @@ -123,35 +124,33 @@ impl<'input> From<&Instruction<'input>> for String { format!(r#"write-utf8 "{}""#, allocator_name) } Instruction::AsWasm(interface_type) => { - format!("as-wasm {}", String::from(interface_type)) + format!("as-wasm {}", interface_type.to_string()) } Instruction::AsInterface(interface_type) => { - format!("as-interface {}", String::from(interface_type)) + format!("as-interface {}", interface_type.to_string()) } Instruction::TableRefAdd => "table-ref-add".into(), Instruction::TableRefGet => "table-ref-get".into(), Instruction::CallMethod(index) => format!("call-method {}", index), Instruction::MakeRecord(interface_type) => { - format!("make-record {}", String::from(interface_type)) + format!("make-record {}", interface_type.to_string()) } Instruction::GetField(interface_type, field_index) => { - format!("get-field {} {}", String::from(interface_type), field_index) + format!("get-field {} {}", interface_type.to_string(), field_index) } Instruction::Const(interface_type, value) => { - format!("const {} {}", String::from(interface_type), value) + format!("const {} {}", interface_type.to_string(), value) } Instruction::FoldSeq(import_index) => format!("fold-seq {}", import_index), - Instruction::Add(interface_type) => format!("add {}", String::from(interface_type)), - Instruction::MemToSeq(interface_type, memory) => format!( - r#"mem-to-seq {} "{}""#, - String::from(interface_type), - memory - ), + Instruction::Add(interface_type) => format!("add {}", interface_type.to_string()), + Instruction::MemToSeq(interface_type, memory) => { + format!(r#"mem-to-seq {} "{}""#, interface_type.to_string(), memory) + } Instruction::Load(interface_type, memory) => { - format!(r#"load {} "{}""#, String::from(interface_type), memory) + format!(r#"load {} "{}""#, interface_type.to_string(), memory) } Instruction::SeqNew(interface_type) => { - format!("seq.new {}", String::from(interface_type)) + format!("seq.new {}", interface_type.to_string()) } Instruction::ListPush => "list.push".into(), Instruction::RepeatUntil(condition_index, step_index) => { @@ -173,7 +172,7 @@ fn input_types_to_param(input_types: &[InterfaceType]) -> String { .iter() .fold(String::new(), |mut accumulator, interface_type| { accumulator.push(' '); - accumulator.push_str(&String::from(interface_type)); + accumulator.push_str(&interface_type.to_string()); accumulator }) ) @@ -192,7 +191,7 @@ fn output_types_to_result(output_types: &[InterfaceType]) -> String { .iter() .fold(String::new(), |mut accumulator, interface_type| { accumulator.push(' '); - accumulator.push_str(&String::from(interface_type)); + accumulator.push_str(&interface_type.to_string()); accumulator }) ) @@ -200,41 +199,41 @@ fn output_types_to_result(output_types: &[InterfaceType]) -> String { } /// Encode an `Export` into a string. -impl<'input> From<&Export<'input>> for String { - fn from(export: &Export) -> Self { +impl<'input> ToString for &Export<'input> { + fn to_string(&self) -> String { format!( r#"(@interface export "{name}"{inputs}{outputs})"#, - name = export.name, - inputs = input_types_to_param(&export.input_types), - outputs = output_types_to_result(&export.output_types), + name = self.name, + inputs = input_types_to_param(&self.input_types), + outputs = output_types_to_result(&self.output_types), ) } } /// Encode a `Type` into a string. -impl<'input> From<&Type<'input>> for String { - fn from(_ty: &Type) -> Self { +impl<'input> ToString for &Type<'input> { + fn to_string(&self) -> String { unimplemented!() } } /// Encode an `Import` into a string. -impl<'input> From<&Import<'input>> for String { - fn from(import: &Import) -> Self { +impl<'input> ToString for &Import<'input> { + fn to_string(&self) -> String { format!( r#"(@interface func ${namespace}_{name} (import "{namespace}" "{name}"){inputs}{outputs})"#, - namespace = import.namespace, - name = import.name, - inputs = input_types_to_param(&import.input_types), - outputs = output_types_to_result(&import.output_types), + namespace = self.namespace, + name = self.name, + inputs = input_types_to_param(&self.input_types), + outputs = output_types_to_result(&self.output_types), ) } } /// Encode an `Adapter` into a string. -impl<'input> From<&Adapter<'input>> for String { - fn from(adapter: &Adapter) -> Self { - match adapter { +impl<'input> ToString for &Adapter<'input> { + fn to_string(&self) -> String { + match self { Adapter::Import { namespace, name, @@ -252,7 +251,7 @@ impl<'input> From<&Adapter<'input>> for String { .iter() .fold(String::new(), |mut accumulator, instruction| { accumulator.push_str("\n "); - accumulator.push_str(&String::from(instruction)); + accumulator.push_str(&instruction.to_string()); accumulator }), ), @@ -272,7 +271,7 @@ impl<'input> From<&Adapter<'input>> for String { .iter() .fold(String::new(), |mut accumulator, instruction| { accumulator.push_str("\n "); - accumulator.push_str(&String::from(instruction)); + accumulator.push_str(&instruction.to_string()); accumulator }), ), @@ -283,39 +282,39 @@ impl<'input> From<&Adapter<'input>> for String { } /// Encode a `Forward` into a string. -impl<'input> From<&Forward<'input>> for String { - fn from(forward: &Forward) -> Self { +impl<'input> ToString for &Forward<'input> { + fn to_string(&self) -> String { format!( r#"(@interface forward (export "{name}"))"#, - name = forward.name, + name = self.name, ) } } /// Encode an `Interfaces` into a string. -impl<'input> From<&Interfaces<'input>> for String { - fn from(interfaces: &Interfaces) -> Self { +impl<'input> ToString for &Interfaces<'input> { + fn to_string(&self) -> String { let mut output = String::from(";; Interfaces"); - let exports = interfaces + let exports = self .exports .iter() .fold(String::new(), |mut accumulator, export| { accumulator.push_str(&format!("\n\n;; Interface, Export {}\n", export.name)); - accumulator.push_str(&String::from(export)); + accumulator.push_str(&export.to_string()); accumulator }); - let types = interfaces + let types = self .types .iter() .fold(String::new(), |mut accumulator, ty| { accumulator.push_str(&format!("\n\n;; Interface, Ty {}\n", ty.name)); - accumulator.push_str(&String::from(ty)); + accumulator.push_str(&ty.to_string()); accumulator }); - let imports = interfaces + let imports = self .imports .iter() .fold(String::new(), |mut accumulator, import| { @@ -323,42 +322,40 @@ impl<'input> From<&Interfaces<'input>> for String { "\n\n;; Interface, Import {}.{}\n", import.namespace, import.name )); - accumulator.push_str(&String::from(import)); + accumulator.push_str(&import.to_string()); accumulator }); - let adapters = - interfaces - .adapters - .iter() - .fold(String::new(), |mut accumulator, adapter| { - match adapter { - Adapter::Import { - namespace, name, .. - } => accumulator.push_str(&format!( - "\n\n;; Interface, Adapter {}.{}\n", - namespace, name - )), + let adapters = self + .adapters + .iter() + .fold(String::new(), |mut accumulator, adapter| { + match adapter { + Adapter::Import { + namespace, name, .. + } => accumulator.push_str(&format!( + "\n\n;; Interface, Adapter {}.{}\n", + namespace, name + )), - Adapter::Export { name, .. } => { - accumulator.push_str(&format!("\n\n;; Interface, Adapter {}\n", name)) - } - - _ => unimplemented!(), + Adapter::Export { name, .. } => { + accumulator.push_str(&format!("\n\n;; Interface, Adapter {}\n", name)) } - accumulator.push_str(&String::from(adapter)); - accumulator - }); - let forwards = - interfaces - .forwards - .iter() - .fold(String::new(), |mut accumulator, forward| { - accumulator.push_str(&format!("\n\n;; Interface, Forward {}\n", forward.name)); - accumulator.push_str(&String::from(forward)); - accumulator - }); + _ => unimplemented!(), + } + accumulator.push_str(&adapter.to_string()); + accumulator + }); + + let forwards = self + .forwards + .iter() + .fold(String::new(), |mut accumulator, forward| { + accumulator.push_str(&format!("\n\n;; Interface, Forward {}\n", forward.name)); + accumulator.push_str(&forward.to_string()); + accumulator + }); output.push_str(&exports); output.push_str(&types); @@ -377,16 +374,16 @@ mod tests { #[test] fn test_interface_types() { let inputs: Vec = vec![ - (&InterfaceType::Int).into(), - (&InterfaceType::Float).into(), - (&InterfaceType::Any).into(), - (&InterfaceType::String).into(), - (&InterfaceType::Seq).into(), - (&InterfaceType::I32).into(), - (&InterfaceType::I64).into(), - (&InterfaceType::F32).into(), - (&InterfaceType::F64).into(), - (&InterfaceType::AnyRef).into(), + (&InterfaceType::Int).to_string(), + (&InterfaceType::Float).to_string(), + (&InterfaceType::Any).to_string(), + (&InterfaceType::String).to_string(), + (&InterfaceType::Seq).to_string(), + (&InterfaceType::I32).to_string(), + (&InterfaceType::I64).to_string(), + (&InterfaceType::F32).to_string(), + (&InterfaceType::F64).to_string(), + (&InterfaceType::AnyRef).to_string(), ]; let outputs = vec![ "Int", "Float", "Any", "String", "Seq", "i32", "i64", "f32", "f64", "anyref", @@ -398,29 +395,29 @@ mod tests { #[test] fn test_instructions() { let inputs: Vec = vec![ - (&Instruction::ArgumentGet { index: 7 }).into(), - (&Instruction::Call { function_index: 7 }).into(), - (&Instruction::CallExport { export_name: "foo" }).into(), - (&Instruction::ReadUtf8).into(), + (&Instruction::ArgumentGet { index: 7 }).to_string(), + (&Instruction::Call { function_index: 7 }).to_string(), + (&Instruction::CallExport { export_name: "foo" }).to_string(), + (&Instruction::ReadUtf8).to_string(), (&Instruction::WriteUtf8 { allocator_name: "foo", }) - .into(), - (&Instruction::AsWasm(InterfaceType::Int)).into(), - (&Instruction::AsInterface(InterfaceType::AnyRef)).into(), - (&Instruction::TableRefAdd).into(), - (&Instruction::TableRefGet).into(), - (&Instruction::CallMethod(7)).into(), - (&Instruction::MakeRecord(InterfaceType::Int)).into(), - (&Instruction::GetField(InterfaceType::Int, 7)).into(), - (&Instruction::Const(InterfaceType::I32, 7)).into(), - (&Instruction::FoldSeq(7)).into(), - (&Instruction::Add(InterfaceType::Int)).into(), - (&Instruction::MemToSeq(InterfaceType::Int, "foo")).into(), - (&Instruction::Load(InterfaceType::Int, "foo")).into(), - (&Instruction::SeqNew(InterfaceType::Int)).into(), - (&Instruction::ListPush).into(), - (&Instruction::RepeatUntil(1, 2)).into(), + .to_string(), + (&Instruction::AsWasm(InterfaceType::Int)).to_string(), + (&Instruction::AsInterface(InterfaceType::AnyRef)).to_string(), + (&Instruction::TableRefAdd).to_string(), + (&Instruction::TableRefGet).to_string(), + (&Instruction::CallMethod(7)).to_string(), + (&Instruction::MakeRecord(InterfaceType::Int)).to_string(), + (&Instruction::GetField(InterfaceType::Int, 7)).to_string(), + (&Instruction::Const(InterfaceType::I32, 7)).to_string(), + (&Instruction::FoldSeq(7)).to_string(), + (&Instruction::Add(InterfaceType::Int)).to_string(), + (&Instruction::MemToSeq(InterfaceType::Int, "foo")).to_string(), + (&Instruction::Load(InterfaceType::Int, "foo")).to_string(), + (&Instruction::SeqNew(InterfaceType::Int)).to_string(), + (&Instruction::ListPush).to_string(), + (&Instruction::RepeatUntil(1, 2)).to_string(), ]; let outputs = vec![ "arg.get 7", @@ -456,25 +453,25 @@ mod tests { input_types: vec![InterfaceType::I32, InterfaceType::F32], output_types: vec![InterfaceType::I32], }) - .into(), + .to_string(), (&Export { name: "foo", input_types: vec![InterfaceType::I32], output_types: vec![], }) - .into(), + .to_string(), (&Export { name: "foo", input_types: vec![], output_types: vec![InterfaceType::I32], }) - .into(), + .to_string(), (&Export { name: "foo", input_types: vec![], output_types: vec![], }) - .into(), + .to_string(), ]; let outputs = vec![ r#"(@interface export "foo" @@ -499,28 +496,28 @@ mod tests { input_types: vec![InterfaceType::Int, InterfaceType::String], output_types: vec![InterfaceType::String], }) - .into(), + .to_string(), (&Import { namespace: "ns", name: "foo", input_types: vec![InterfaceType::String], output_types: vec![], }) - .into(), + .to_string(), (&Import { namespace: "ns", name: "foo", input_types: vec![], output_types: vec![InterfaceType::String], }) - .into(), + .to_string(), (&Import { namespace: "ns", name: "foo", input_types: vec![], output_types: vec![], }) - .into(), + .to_string(), ]; let outputs = vec![ r#"(@interface func $ns_foo (import "ns" "foo") @@ -552,7 +549,7 @@ mod tests { Instruction::CallExport { export_name: "f" }, ], }) - .into(), + .to_string(), (&Adapter::Import { namespace: "ns", name: "foo", @@ -560,7 +557,7 @@ mod tests { output_types: vec![], instructions: vec![Instruction::CallExport { export_name: "f" }], }) - .into(), + .to_string(), (&Adapter::Import { namespace: "ns", name: "foo", @@ -568,7 +565,7 @@ mod tests { output_types: vec![InterfaceType::I32], instructions: vec![Instruction::CallExport { export_name: "f" }], }) - .into(), + .to_string(), (&Adapter::Export { name: "foo", input_types: vec![InterfaceType::I32, InterfaceType::F32], @@ -581,21 +578,21 @@ mod tests { Instruction::CallExport { export_name: "f" }, ], }) - .into(), + .to_string(), (&Adapter::Export { name: "foo", input_types: vec![InterfaceType::I32], output_types: vec![], instructions: vec![Instruction::CallExport { export_name: "f" }], }) - .into(), + .to_string(), (&Adapter::Export { name: "foo", input_types: vec![], output_types: vec![InterfaceType::I32], instructions: vec![Instruction::CallExport { export_name: "f" }], }) - .into(), + .to_string(), ]; let outputs = vec![ r#"(@interface adapt (import "ns" "foo") @@ -629,7 +626,7 @@ mod tests { #[test] fn test_forward() { - let input: String = (&Forward { name: "main" }).into(); + let input: String = (&Forward { name: "main" }).to_string(); let output = r#"(@interface forward (export "main"))"#; assert_eq!(input, output); @@ -682,7 +679,7 @@ mod tests { ], forwards: vec![Forward { name: "main" }], }) - .into(); + .to_string(); let output = r#";; Interfaces ;; Interface, Export foo diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 26916dfe3..792c91e89 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -192,7 +192,7 @@ where let executable_instructions = instructions .iter() .map(|instruction| { - let instruction_name: String = instruction.into(); + let instruction_name = instruction.to_string(); match instruction { Instruction::ArgumentGet { index } => { From 6891517c8befbb0f8abbbcf88f8cdaca658eb1ba Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 14:24:07 +0100 Subject: [PATCH 75/84] chore(encoders) Changing `unimplemented!()` to `todo!()`. --- lib/interface-types/src/encoders/wat.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/interface-types/src/encoders/wat.rs b/lib/interface-types/src/encoders/wat.rs index bc4ab5b59..08813a271 100644 --- a/lib/interface-types/src/encoders/wat.rs +++ b/lib/interface-types/src/encoders/wat.rs @@ -213,7 +213,7 @@ impl<'input> ToString for &Export<'input> { /// Encode a `Type` into a string. impl<'input> ToString for &Type<'input> { fn to_string(&self) -> String { - unimplemented!() + todo!("To be implemented.") } } @@ -276,7 +276,7 @@ impl<'input> ToString for &Adapter<'input> { }), ), - _ => unimplemented!(), + _ => todo!("To be implemented."), } } } @@ -342,7 +342,7 @@ impl<'input> ToString for &Interfaces<'input> { accumulator.push_str(&format!("\n\n;; Interface, Adapter {}\n", name)) } - _ => unimplemented!(), + _ => todo!("To be implemented."), } accumulator.push_str(&adapter.to_string()); accumulator From ae6e26158f0713c93cfdf4ddcb476f5706a2c504 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 14:26:50 +0100 Subject: [PATCH 76/84] chore(interface-types) Simplify code. --- lib/interface-types/src/interpreter/instructions/call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interface-types/src/interpreter/instructions/call.rs b/lib/interface-types/src/interpreter/instructions/call.rs index 9f4ac0226..5fe896bb9 100644 --- a/lib/interface-types/src/interpreter/instructions/call.rs +++ b/lib/interface-types/src/interpreter/instructions/call.rs @@ -88,7 +88,7 @@ mod tests { InterfaceValue::I32(3), InterfaceValue::I32(4), ], - instance: Instance { ..Default::default() }, + instance: Default::default(), error: r#"`call 42` cannot call the local or imported function `42` because it doesn't exist."#, ); From 95597473947446551c7b9f8da817714df689e9f7 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 14:34:45 +0100 Subject: [PATCH 77/84] doc(interface-types) Add `#[deny(missing_docs)]`. --- .../src/interpreter/instruction.rs | 20 +++++++++++++++---- .../src/interpreter/wasm/structures.rs | 2 ++ .../src/interpreter/wasm/values.rs | 2 ++ lib/interface-types/src/lib.rs | 5 +++-- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/interface-types/src/interpreter/instruction.rs b/lib/interface-types/src/interpreter/instruction.rs index ddf8be627..5364ac55a 100644 --- a/lib/interface-types/src/interpreter/instruction.rs +++ b/lib/interface-types/src/interpreter/instruction.rs @@ -4,19 +4,31 @@ use crate::ast::InterfaceType; #[derive(PartialEq, Debug)] pub enum Instruction<'input> { /// The `arg.get` instruction. - ArgumentGet { index: u64 }, + ArgumentGet { + /// The argument index. + index: u64, + }, /// The `call` instruction. - Call { function_index: usize }, + Call { + /// The function index. + function_index: usize, + }, /// The `call-export` instruction. - CallExport { export_name: &'input str }, + CallExport { + /// The exported function name. + export_name: &'input str, + }, /// The `read-utf8` instruction. ReadUtf8, /// The `write-utf8` instruction. - WriteUtf8 { allocator_name: &'input str }, + WriteUtf8 { + /// The allocator function name. + allocator_name: &'input str, + }, /// The `as-wasm` instruction. AsWasm(InterfaceType), diff --git a/lib/interface-types/src/interpreter/wasm/structures.rs b/lib/interface-types/src/interpreter/wasm/structures.rs index 7ec386f68..dbeab832f 100644 --- a/lib/interface-types/src/interpreter/wasm/structures.rs +++ b/lib/interface-types/src/interpreter/wasm/structures.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use super::values::{InterfaceType, InterfaceValue}; use std::{cell::Cell, ops::Deref}; diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 632a88f65..f50763fc0 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use std::convert::TryFrom; pub use crate::ast::InterfaceType; diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 64dac5b56..99a8f8312 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -33,12 +33,13 @@ #![deny( dead_code, + missing_docs, nonstandard_style, + unreachable_patterns, unused_imports, unused_mut, - unused_variables, unused_unsafe, - unreachable_patterns + unused_variables )] #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] #![doc(html_logo_url = "https://github.com/wasmerio.png")] From eb2f9db2304f814c4d49ccf66f899e93643ed176 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 14:36:17 +0100 Subject: [PATCH 78/84] feat(interface-types) Forbids unsafe code. --- lib/interface-types/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 99a8f8312..f4deaba97 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -41,6 +41,7 @@ unused_unsafe, unused_variables )] +#![forbid(unsafe_code)] #![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")] #![doc(html_logo_url = "https://github.com/wasmerio.png")] From 7f8d9165d4c79b7013b43043078b6181598f582e Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 14:38:05 +0100 Subject: [PATCH 79/84] fix(interface-types) Clean up cargo features. --- lib/runtime-core/Cargo.toml | 1 - lib/runtime/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 6f3a775ca..7f07a9d2d 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -52,4 +52,3 @@ cc = "1.0" [features] managed = [] deterministic-execution = ["wasmparser/deterministic"] -interface-types = [] diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index 1d51eb563..a8dd63dbc 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -52,7 +52,6 @@ singlepass = ["wasmer-singlepass-backend"] default-backend-singlepass = ["singlepass"] default-backend-llvm = ["llvm"] default-backend-cranelift = ["cranelift"] -interface-types = ["wasmer-runtime-core/interface-types"] deterministic-execution = ["wasmer-singlepass-backend/deterministic-execution", "wasmer-runtime-core/deterministic-execution"] [[bench]] From 2e2c5ab9c1f16288739990c573f953085ff532a6 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 15:38:21 +0100 Subject: [PATCH 80/84] doc(changelog) Add #787. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1308dbad4..edd5587bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## **[Unreleased]** +- [#787](https://github.com/wasmerio/wasmer/pull/787) New crate `wasmer-interface-types` to implement WebAssembly Interface Types. - [#1192](https://github.com/wasmerio/wasmer/pull/1192) Use `ExceptionCode` for error representation. - [#1191](https://github.com/wasmerio/wasmer/pull/1191) Fix singlepass miscompilation on `Operator::CallIndirect`. - [#1180](https://github.com/wasmerio/wasmer/pull/1180) Fix compilation for target `x86_64-unknown-linux-musl`. From 07ba2c82b94f921925f84c47130725622f3c14ec Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 13 Feb 2020 15:38:35 +0100 Subject: [PATCH 81/84] test(interface-types) Add `wasmer-interface-types` to `make check`. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 700ae82e4..6e34078af 100644 --- a/Makefile +++ b/Makefile @@ -201,7 +201,7 @@ check-bench: check-bench-singlepass check-bench-llvm # TODO: We wanted `--workspace --exclude wasmer-runtime`, but can't due # to https://github.com/rust-lang/cargo/issues/6745 . -NOT_RUNTIME_CRATES = -p wasmer-clif-backend -p wasmer-singlepass-backend -p wasmer-middleware-common -p wasmer-runtime-core -p wasmer-emscripten -p wasmer-llvm-backend -p wasmer-wasi -p wasmer-kernel-loader -p wasmer-dev-utils -p wasmer-wasi-tests -p wasmer-middleware-common-tests -p wasmer-emscripten-tests +NOT_RUNTIME_CRATES = -p wasmer-clif-backend -p wasmer-singlepass-backend -p wasmer-middleware-common -p wasmer-runtime-core -p wasmer-emscripten -p wasmer-llvm-backend -p wasmer-wasi -p wasmer-kernel-loader -p wasmer-dev-utils -p wasmer-wasi-tests -p wasmer-middleware-common-tests -p wasmer-emscripten-tests -p wasmer-interface-types RUNTIME_CHECK = cargo check --manifest-path lib/runtime/Cargo.toml --no-default-features check: check-bench cargo check $(NOT_RUNTIME_CRATES) From c287170166798e3b3b87ab58049a1a7487847739 Mon Sep 17 00:00:00 2001 From: Syrus Date: Thu, 13 Feb 2020 10:33:27 -0800 Subject: [PATCH 82/84] Fixed wasi isatty tests when being generated --- lib/wasi-tests/wasitests/isatty.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/wasi-tests/wasitests/isatty.rs b/lib/wasi-tests/wasitests/isatty.rs index 5310b5e7d..57d343568 100644 --- a/lib/wasi-tests/wasitests/isatty.rs +++ b/lib/wasi-tests/wasitests/isatty.rs @@ -1,3 +1,5 @@ +// We don't have access to libc, so we just use isatty +// as an external function // use libc::isatty; extern "C" { @@ -5,7 +7,14 @@ extern "C" { } fn main() { - println!("stdin: {}", unsafe { isatty(0) }); - println!("stdout: {}", unsafe { isatty(1) }); - println!("stderr: {}", unsafe { isatty(2) }); + #[cfg(target = "wasi")] { + println!("stdin: {}", unsafe { isatty(0) }); + println!("stdout: {}", unsafe { isatty(1) }); + println!("stderr: {}", unsafe { isatty(2) }); + } + #[cfg(not(target = "wasi"))] { + println!("stdin: 1"); + println!("stdout: 1"); + println!("stderr: 1"); + } } From 61c85b3fbf0f777232935b73d48f908d725ce602 Mon Sep 17 00:00:00 2001 From: Syrus Date: Thu, 13 Feb 2020 10:35:48 -0800 Subject: [PATCH 83/84] Improved fd_write debugging --- lib/wasi/src/syscalls/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 6c60863ec..ea253968c 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -1221,7 +1221,14 @@ pub fn fd_write( iovs_len: u32, nwritten: WasmPtr, ) -> __wasi_errno_t { - debug!("wasi::fd_write: fd={}", fd); + // If we are writing to stdout or stderr + // we skip debug to not pollute the stdout/err + // and do debugging happily after :) + if fd != __WASI_STDOUT_FILENO && fd != __WASI_STDERR_FILENO { + debug!("wasi::fd_write: fd={}", fd); + } else { + trace!("wasi::fd_write: fd={}", fd); + } let (memory, state) = get_memory_and_wasi_state(ctx, 0); let iovs_arr_cell = wasi_try!(iovs.deref(memory, 0, iovs_len)); let nwritten_cell = wasi_try!(nwritten.deref(memory)); From 60e686ff76518406d4fabc8f04c97d061211406e Mon Sep 17 00:00:00 2001 From: Syrus Date: Thu, 13 Feb 2020 10:37:27 -0800 Subject: [PATCH 84/84] Added change into changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d690b402b..a7e2d4f78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## **[Unreleased]** +- [#1213](https://github.com/wasmerio/wasmer/pull/1213) Fixed WASI `fdstat` to detect `isatty` properly. - [#1192](https://github.com/wasmerio/wasmer/pull/1192) Use `ExceptionCode` for error representation. - [#1180](https://github.com/wasmerio/wasmer/pull/1180) Fix compilation for target `x86_64-unknown-linux-musl`. - [#1170](https://github.com/wasmerio/wasmer/pull/1170) Improve the WasiFs builder API with convenience methods for overriding stdin, stdout, and stderr as well as a new sub-builder for controlling the permissions and properties of preopened directories. Also breaks that implementations of `WasiFile` must be `Send` -- please file an issue if this change causes you any issues.