From 1b1d48d0a0a625d37f371c8282e0c76a8ba3882e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 18 Jan 2023 09:00:49 -0800 Subject: [PATCH 01/25] Update the Rust guest generator This commit updates the `wit-parser` and `wit-component` dependencies to the latest 0.4.0 versions on crates.io, namely pulling in support for `use`. This updates the `*.wit` codegen tests and gets all the codegen tests working for the `gen-guest-rust` crate. This does not get the entire repository's tests running. The major changes here are: * The `WorldGenerator` trait has been adapted for worlds and the new APIs of `wit-parser`. * The Rust guest generator now supports bare function imports as well as bare function exports. * The syntax in the Rust macro now mirrors the syntax in the Wasmtime `bindgen!` macro where `generate!("foo")` will look for the document `foo.wit` in `$CARGO_MANIFEST_DIR/wit/foo.wit` and look for a `default world` inside there. * The codegen and runtime macros have been refactored to get separated and additionally have conditional compilation working so one generator can be tested in isolation without requiring all others working. * The `rust-macro-shared` crate is now removed since Wasmtime no longer resides here and it's merged directly into the `guest-rust-macro` crate. Note that many changes happened here since the macro syntax and integration with `wit-parser` has changed. Note that no new tests were added in this commit, and that'll be a follow-up. --- Cargo.lock | 237 +++++------------- Cargo.toml | 13 +- crates/bindgen-core/src/component.rs | 18 +- crates/bindgen-core/src/lib.rs | 181 ++++++++----- crates/gen-guest-c/Cargo.toml | 2 +- crates/gen-guest-rust/Cargo.toml | 2 +- crates/gen-guest-rust/src/lib.rs | 180 ++++++++----- crates/gen-guest-rust/tests/codegen.rs | 17 +- crates/gen-guest-teavm-java/Cargo.toml | 2 +- crates/gen-rust-lib/src/lib.rs | 24 +- crates/guest-rust-macro/Cargo.toml | 3 +- crates/guest-rust-macro/src/lib.rs | 219 ++++++++++++++-- crates/rust-macro-shared/Cargo.toml | 14 -- crates/rust-macro-shared/src/lib.rs | 155 ------------ crates/test-helpers/Cargo.toml | 15 +- crates/test-helpers/codegen-macro/Cargo.toml | 16 ++ .../{macros => codegen-macro}/src/lib.rs | 46 +--- .../{macros => runtime-macro}/Cargo.toml | 17 +- .../{macros => runtime-macro}/build.rs | 33 ++- crates/test-helpers/runtime-macro/src/lib.rs | 42 ++++ crates/test-helpers/src/lib.rs | 132 ++++------ tests/codegen/char.wit | 7 +- tests/codegen/conventions.wit | 7 +- tests/codegen/empty.wit | 2 +- tests/codegen/flags.wit | 7 +- tests/codegen/floats.wit | 7 +- tests/codegen/integers.wit | 7 +- tests/codegen/keywords.wit | 7 +- tests/codegen/lists.wit | 7 +- tests/codegen/many-arguments.wit | 7 +- tests/codegen/multi-return.wit | 7 +- tests/codegen/records.wit | 7 +- tests/codegen/simple-functions.wit | 7 +- tests/codegen/simple-lists.wit | 7 +- tests/codegen/small-anonymous.wit | 7 +- tests/codegen/smoke-default.wit | 6 +- tests/codegen/smoke-export.wit | 2 +- tests/codegen/smoke.wit | 2 +- tests/codegen/strings.wit | 7 +- tests/codegen/unions.wit | 7 +- tests/codegen/variants.wit | 7 +- 41 files changed, 732 insertions(+), 760 deletions(-) delete mode 100644 crates/rust-macro-shared/Cargo.toml delete mode 100644 crates/rust-macro-shared/src/lib.rs create mode 100644 crates/test-helpers/codegen-macro/Cargo.toml rename crates/test-helpers/{macros => codegen-macro}/src/lib.rs (51%) rename crates/test-helpers/{macros => runtime-macro}/Cargo.toml (51%) rename crates/test-helpers/{macros => runtime-macro}/build.rs (94%) create mode 100644 crates/test-helpers/runtime-macro/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 9fe8ba5fe..ea8a8b18c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "ahash" version = "0.7.6" @@ -60,21 +45,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "backtrace" -version = "0.3.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "base64" version = "0.13.1" @@ -96,12 +66,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "cc" -version = "1.0.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" - [[package]] name = "cfg-if" version = "1.0.0" @@ -145,6 +109,15 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "codegen-macro" +version = "0.3.0" +dependencies = [ + "heck", + "ignore", + "quote", +] + [[package]] name = "cranelift-entity" version = "0.91.0" @@ -177,18 +150,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" -[[package]] -name = "filetime" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "windows-sys", -] - [[package]] name = "fnv" version = "1.0.7" @@ -380,15 +341,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "miniz_oxide" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" -dependencies = [ - "adler", -] - [[package]] name = "object" version = "0.29.0" @@ -484,15 +436,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" version = "1.7.0" @@ -511,10 +454,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +name = "runtime-macro" +version = "0.3.0" +dependencies = [ + "heck", + "quote", + "wit-bindgen-core", + "wit-bindgen-gen-guest-c", + "wit-bindgen-gen-guest-teavm-java", + "wit-component", +] [[package]] name = "same-file" @@ -596,30 +545,15 @@ dependencies = [ name = "test-helpers" version = "0.3.0" dependencies = [ - "test-helpers-macros", + "codegen-macro", + "runtime-macro", + "wasm-encoder 0.21.0", "wat", "wit-bindgen-core", "wit-component", "wit-parser", ] -[[package]] -name = "test-helpers-macros" -version = "0.3.0" -dependencies = [ - "backtrace", - "filetime", - "heck", - "ignore", - "proc-macro2", - "quote", - "wit-bindgen-core", - "wit-bindgen-gen-guest-c", - "wit-bindgen-gen-guest-teavm-java", - "wit-component", - "wit-parser", -] - [[package]] name = "test-rust-wasm" version = "0.3.0" @@ -771,6 +705,15 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-encoder" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ab2fe77b325731603297debb4573e002d06ae0aa1f4dc108585c81961e0609" +dependencies = [ + "leb128", +] + [[package]] name = "wasmparser" version = "0.95.0" @@ -781,14 +724,24 @@ dependencies = [ "url", ] +[[package]] +name = "wasmparser" +version = "0.97.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98123a0d2bacf9286239231b116cbd66c65d9b89793f7c9bba3a3ae7f1b15f3" +dependencies = [ + "indexmap", + "url", +] + [[package]] name = "wasmprinter" -version = "0.2.44" +version = "0.2.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae24500f9cc27a4b2b338e66693ff53c08b17cf920bdc81e402a09fe7a204eea" +checksum = "595cca929e47a7bec3c941b5a8e133f51b17e6d9dd8c82ab97902196f5a07b42" dependencies = [ "anyhow", - "wasmparser", + "wasmparser 0.97.0", ] [[package]] @@ -810,8 +763,8 @@ dependencies = [ "serde", "target-lexicon", "thiserror", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.20.0", + "wasmparser 0.95.0", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -825,26 +778,26 @@ dependencies = [ "cranelift-entity", "serde", "thiserror", - "wasmparser", + "wasmparser 0.95.0", ] [[package]] name = "wast" -version = "50.0.0" +version = "51.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2cbb59d4ac799842791fe7e806fa5dbbf6b5554d538e51cc8e176db6ff0ae34" +checksum = "a1f621e6e9af96438d3e05f0699da5b1dae59f2df964a2982166aa9b03c5b599" dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder", + "wasm-encoder 0.21.0", ] [[package]] name = "wat" -version = "1.0.52" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584aaf7a1ecf4d383bbe1a25eeab0cbb8ff96acc6796707ff65cde48f4632f15" +checksum = "5dd18c1168d7e8743d9b4f713c0203924f5dcc4a3983eb5e584de9614f9fccde" dependencies = [ "wast", ] @@ -880,63 +833,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" - [[package]] name = "wit-bindgen-cli" version = "0.3.0" @@ -988,7 +884,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder", + "wasm-encoder 0.21.0", "wit-bindgen-core", "wit-component", ] @@ -1063,47 +959,42 @@ dependencies = [ name = "wit-bindgen-guest-rust-macro" version = "0.3.0" dependencies = [ + "anyhow", "proc-macro2", "syn", "wit-bindgen-core", "wit-bindgen-gen-guest-rust", - "wit-bindgen-rust-macro-shared", -] - -[[package]] -name = "wit-bindgen-rust-macro-shared" -version = "0.3.0" -dependencies = [ - "proc-macro2", - "syn", - "wit-bindgen-core", "wit-component", ] [[package]] name = "wit-component" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "292a4db4b7de170ce349e2575f305ad592623ce16cbe824344894a10e60ab879" +checksum = "7a3ab5250e9a9057ec777e8ea9082d716edaee2e2abafe7c6ba98d07d9218992" dependencies = [ "anyhow", "bitflags", "indexmap", "log", - "wasm-encoder", - "wasmparser", + "url", + "wasm-encoder 0.21.0", + "wasmparser 0.97.0", + "wat", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703eb1d2f89ff2c52d50f7ff002735e423cea75f0a5dc5c8a4626c4c47cd9ca6" +checksum = "02cfa79275011530f37e0e164183c606bae1cdc466ea90bcd364d50605486a4d" dependencies = [ "anyhow", "id-arena", "indexmap", + "log", "pulldown-cmark", "unicode-xid", + "url", ] diff --git a/Cargo.toml b/Cargo.toml index 3d389e838..0b36301df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,14 +25,13 @@ clap = { version = "4.0.9", features = ["derive"] } env_logger = "0.9.1" indexmap = "1.9.1" -wasmtime = { git = 'https://github.com/bytecodealliance/wasmtime', features = ["component-model"] } wasmtime-environ = { git = 'https://github.com/bytecodealliance/wasmtime' } -wasmprinter = "0.2.44" -wasmparser = "0.95.0" -wasm-encoder = "0.20.0" -wat = "1.0.52" -wit-parser = "0.3.1" -wit-component = "0.3.2" +wasmprinter = "0.2.46" +wasmparser = "0.97.0" +wasm-encoder = "0.21.0" +wat = "1.0.53" +wit-parser = "0.4.0" +wit-component = "0.4.0" wit-bindgen-core = { path = 'crates/bindgen-core', version = '0.3.0' } wit-bindgen-gen-guest-c = { path = 'crates/gen-guest-c', version = '0.3.0' } diff --git a/crates/bindgen-core/src/component.rs b/crates/bindgen-core/src/component.rs index b8f7f8360..77cc17fb6 100644 --- a/crates/bindgen-core/src/component.rs +++ b/crates/bindgen-core/src/component.rs @@ -8,13 +8,14 @@ //! `generate` function. use crate::{Files, WorldGenerator}; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use wasmtime_environ::component::{ Component, ComponentTypesBuilder, StaticModuleIndex, Translator, }; use wasmtime_environ::wasmparser::{Validator, WasmFeatures}; use wasmtime_environ::{ModuleTranslation, PrimaryMap, ScopeVec, Tunables}; -use wit_parser::World; +use wit_component::DecodedWasm; +use wit_parser::{Resolve, World, WorldId}; /// Generate bindings to load and instantiate the specific binary component /// provided. @@ -31,8 +32,12 @@ pub fn generate( // will likely change as worlds are iterated on in the component model // standard. Regardless though this is the step where types are learned // and `Interface`s are constructed for further code generation below. - let world = wit_component::decode_world(name, binary) + let decoded = wit_component::decode(name, binary) .context("failed to extract interface information from component")?; + let (resolve, world) = match decoded { + DecodedWasm::WitPackage(..) => bail!("unexpected wit package as input"), + DecodedWasm::Component(resolve, world) => (resolve, world), + }; // Components are complicated, there's no real way around that. To // handle all the work of parsing a component and figuring out how to @@ -68,12 +73,12 @@ pub fn generate( // With all that prep work delegate to `WorldGenerator::generate` here // to generate all the type-level descriptions for this component now // that the interfaces in/out are understood. - gen.generate(&world, files); + gen.generate(&resolve, world, files); // And finally generate the code necessary to instantiate the given // component to this method using the `Component` that // `wasmtime-environ` parsed. - gen.instantiate(&component, &modules, &world); + gen.instantiate(&component, &modules, &resolve, world); gen.finish_component(name, files); @@ -94,7 +99,8 @@ pub trait ComponentGenerator: WorldGenerator { &mut self, component: &Component, modules: &PrimaryMap>, - world: &World, + resolve: &Resolve, + world: WorldId, ); fn core_file_name(&mut self, name: &str, idx: u32) -> String { diff --git a/crates/bindgen-core/src/lib.rs b/crates/bindgen-core/src/lib.rs index 4e76643bd..e65ba418b 100644 --- a/crates/bindgen-core/src/lib.rs +++ b/crates/bindgen-core/src/lib.rs @@ -44,16 +44,18 @@ impl std::ops::BitOrAssign for TypeInfo { } impl Types { - pub fn analyze(&mut self, iface: &Interface) { - for (t, _) in iface.types.iter() { - self.type_id_info(iface, t); + pub fn analyze(&mut self, resolve: &Resolve) { + for (t, _) in resolve.types.iter() { + self.type_id_info(resolve, t); } - for f in iface.functions.iter() { - for (_, ty) in f.params.iter() { - self.set_param_result_ty(iface, ty, true, false, false); - } - for ty in f.results.iter_types() { - self.set_param_result_ty(iface, ty, false, true, false); + for (_, iface) in resolve.interfaces.iter() { + for (_, f) in iface.functions.iter() { + for (_, ty) in f.params.iter() { + self.set_param_result_ty(resolve, ty, true, false, false); + } + for ty in f.results.iter_types() { + self.set_param_result_ty(resolve, ty, false, true, false); + } } } } @@ -62,134 +64,148 @@ impl Types { self.type_info[&id] } - pub fn type_id_info(&mut self, iface: &Interface, ty: TypeId) -> TypeInfo { + pub fn type_id_info(&mut self, resolve: &Resolve, ty: TypeId) -> TypeInfo { if let Some(info) = self.type_info.get(&ty) { return *info; } let mut info = TypeInfo::default(); - match &iface.types[ty].kind { + match &resolve.types[ty].kind { TypeDefKind::Record(r) => { for field in r.fields.iter() { - info |= self.type_info(iface, &field.ty); + info |= self.type_info(resolve, &field.ty); } } TypeDefKind::Tuple(t) => { for ty in t.types.iter() { - info |= self.type_info(iface, ty); + info |= self.type_info(resolve, ty); } } TypeDefKind::Flags(_) => {} TypeDefKind::Enum(_) => {} TypeDefKind::Variant(v) => { for case in v.cases.iter() { - info |= self.optional_type_info(iface, case.ty.as_ref()); + info |= self.optional_type_info(resolve, case.ty.as_ref()); } } TypeDefKind::List(ty) => { - info = self.type_info(iface, ty); + info = self.type_info(resolve, ty); info.has_list = true; } TypeDefKind::Type(ty) => { - info = self.type_info(iface, ty); + info = self.type_info(resolve, ty); } TypeDefKind::Option(ty) => { - info = self.type_info(iface, ty); + info = self.type_info(resolve, ty); } TypeDefKind::Result(r) => { - info = self.optional_type_info(iface, r.ok.as_ref()); - info |= self.optional_type_info(iface, r.err.as_ref()); + info = self.optional_type_info(resolve, r.ok.as_ref()); + info |= self.optional_type_info(resolve, r.err.as_ref()); } TypeDefKind::Union(u) => { for case in u.cases.iter() { - info |= self.type_info(iface, &case.ty); + info |= self.type_info(resolve, &case.ty); } } TypeDefKind::Future(ty) => { - info = self.optional_type_info(iface, ty.as_ref()); + info = self.optional_type_info(resolve, ty.as_ref()); } TypeDefKind::Stream(stream) => { - info = self.optional_type_info(iface, stream.element.as_ref()); - info |= self.optional_type_info(iface, stream.end.as_ref()); + info = self.optional_type_info(resolve, stream.element.as_ref()); + info |= self.optional_type_info(resolve, stream.end.as_ref()); } + TypeDefKind::Unknown => unreachable!(), } self.type_info.insert(ty, info); info } - pub fn type_info(&mut self, iface: &Interface, ty: &Type) -> TypeInfo { + pub fn type_info(&mut self, resolve: &Resolve, ty: &Type) -> TypeInfo { let mut info = TypeInfo::default(); match ty { Type::String => info.has_list = true, - Type::Id(id) => return self.type_id_info(iface, *id), + Type::Id(id) => return self.type_id_info(resolve, *id), _ => {} } info } - fn optional_type_info(&mut self, iface: &Interface, ty: Option<&Type>) -> TypeInfo { + fn optional_type_info(&mut self, resolve: &Resolve, ty: Option<&Type>) -> TypeInfo { match ty { - Some(ty) => self.type_info(iface, ty), + Some(ty) => self.type_info(resolve, ty), None => TypeInfo::default(), } } fn set_param_result_id( &mut self, - iface: &Interface, + resolve: &Resolve, ty: TypeId, param: bool, result: bool, error: bool, ) { - match &iface.types[ty].kind { + match &resolve.types[ty].kind { TypeDefKind::Record(r) => { for field in r.fields.iter() { - self.set_param_result_ty(iface, &field.ty, param, result, error) + self.set_param_result_ty(resolve, &field.ty, param, result, error) } } TypeDefKind::Tuple(t) => { for ty in t.types.iter() { - self.set_param_result_ty(iface, ty, param, result, error) + self.set_param_result_ty(resolve, ty, param, result, error) } } TypeDefKind::Flags(_) => {} TypeDefKind::Enum(_) => {} TypeDefKind::Variant(v) => { for case in v.cases.iter() { - self.set_param_result_optional_ty(iface, case.ty.as_ref(), param, result, error) + self.set_param_result_optional_ty( + resolve, + case.ty.as_ref(), + param, + result, + error, + ) } } TypeDefKind::List(ty) | TypeDefKind::Type(ty) | TypeDefKind::Option(ty) => { - self.set_param_result_ty(iface, ty, param, result, error) + self.set_param_result_ty(resolve, ty, param, result, error) } TypeDefKind::Result(r) => { - self.set_param_result_optional_ty(iface, r.ok.as_ref(), param, result, error); - self.set_param_result_optional_ty(iface, r.err.as_ref(), param, result, result); + self.set_param_result_optional_ty(resolve, r.ok.as_ref(), param, result, error); + self.set_param_result_optional_ty(resolve, r.err.as_ref(), param, result, result); } TypeDefKind::Union(u) => { for case in u.cases.iter() { - self.set_param_result_ty(iface, &case.ty, param, result, error) + self.set_param_result_ty(resolve, &case.ty, param, result, error) } } TypeDefKind::Future(ty) => { - self.set_param_result_optional_ty(iface, ty.as_ref(), param, result, error) + self.set_param_result_optional_ty(resolve, ty.as_ref(), param, result, error) } TypeDefKind::Stream(stream) => { self.set_param_result_optional_ty( - iface, + resolve, stream.element.as_ref(), param, result, error, ); - self.set_param_result_optional_ty(iface, stream.end.as_ref(), param, result, error); + self.set_param_result_optional_ty( + resolve, + stream.end.as_ref(), + param, + result, + error, + ); } + TypeDefKind::Unknown => unreachable!(), } } fn set_param_result_ty( &mut self, - iface: &Interface, + resolve: &Resolve, ty: &Type, param: bool, result: bool, @@ -197,13 +213,13 @@ impl Types { ) { match ty { Type::Id(id) => { - self.type_id_info(iface, *id); + self.type_id_info(resolve, *id); let info = self.type_info.get_mut(id).unwrap(); if (param && !info.param) || (result && !info.result) || (error && !info.error) { info.param = info.param || param; info.result = info.result || result; info.error = info.error || error; - self.set_param_result_id(iface, *id, param, result, error); + self.set_param_result_id(resolve, *id, param, result, error); } } _ => {} @@ -212,14 +228,14 @@ impl Types { fn set_param_result_optional_ty( &mut self, - iface: &Interface, + resolve: &Resolve, ty: Option<&Type>, param: bool, result: bool, error: bool, ) { match ty { - Some(ty) => self.set_param_result_ty(iface, ty, param, result, error), + Some(ty) => self.set_param_result_ty(resolve, ty, param, result, error), None => (), } } @@ -412,28 +428,67 @@ mod tests { } pub trait WorldGenerator { - fn generate(&mut self, world: &World, files: &mut Files) { - self.preprocess(&world.name); + fn generate(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) { + let world = &resolve.worlds[id]; + self.preprocess(resolve, &world.name); + + let mut funcs = Vec::new(); for (name, import) in world.imports.iter() { - self.import(name, import, files); + match import { + WorldItem::Function(f) => funcs.push((name.as_str(), f)), + WorldItem::Interface(id) => self.import_interface(resolve, name, *id, files), + } } + if !funcs.is_empty() { + self.import_funcs(resolve, id, &funcs, files); + } + funcs.clear(); for (name, export) in world.exports.iter() { - self.export(name, export, files); + match export { + WorldItem::Function(f) => funcs.push((name.as_str(), f)), + WorldItem::Interface(id) => self.export_interface(resolve, name, *id, files), + } } - if let Some(iface) = &world.default { - self.export_default(&world.name, iface, files); + if !funcs.is_empty() { + self.export_funcs(resolve, id, &funcs, files); } - self.finish(world, files); + self.finish(resolve, id, files); } - fn preprocess(&mut self, name: &str) { + fn preprocess(&mut self, resolve: &Resolve, name: &str) { + drop(resolve); drop(name); } - fn import(&mut self, name: &str, iface: &Interface, files: &mut Files); - fn export(&mut self, name: &str, iface: &Interface, files: &mut Files); - fn export_default(&mut self, name: &str, iface: &Interface, files: &mut Files); - fn finish(&mut self, world: &World, files: &mut Files); + fn import_interface( + &mut self, + resolve: &Resolve, + name: &str, + iface: InterfaceId, + files: &mut Files, + ); + fn export_interface( + &mut self, + resolve: &Resolve, + name: &str, + iface: InterfaceId, + files: &mut Files, + ); + fn import_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + files: &mut Files, + ); + fn export_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + files: &mut Files, + ); + fn finish(&mut self, resolve: &Resolve, world: WorldId, files: &mut Files); } /// This is a possible replacement for the `Generator` trait above, currently @@ -445,7 +500,7 @@ pub trait WorldGenerator { /// change for JS though. In any case it's something that was useful for JS and /// is suitable to replace otherwise at any time. pub trait InterfaceGenerator<'a> { - fn iface(&self) -> &'a Interface; + fn resolve(&self) -> &'a Resolve; fn type_record(&mut self, id: TypeId, name: &str, record: &Record, docs: &Docs); fn type_flags(&mut self, id: TypeId, name: &str, flags: &Flags, docs: &Docs); @@ -459,12 +514,11 @@ pub trait InterfaceGenerator<'a> { fn type_list(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); - fn types(&mut self) { - for (id, ty) in self.iface().types.iter() { - let name = match &ty.name { - Some(name) => name, - None => continue, - }; + fn types(&mut self, iface: InterfaceId) { + let iface = &self.resolve().interfaces[iface]; + for (name, id) in iface.types.iter() { + let id = *id; + let ty = &self.resolve().types[id]; match &ty.kind { TypeDefKind::Record(record) => self.type_record(id, name, record, &ty.docs), TypeDefKind::Flags(flags) => self.type_flags(id, name, flags, &ty.docs), @@ -478,6 +532,7 @@ pub trait InterfaceGenerator<'a> { TypeDefKind::Type(t) => self.type_alias(id, name, t, &ty.docs), TypeDefKind::Future(_) => todo!("generate for future"), TypeDefKind::Stream(_) => todo!("generate for stream"), + TypeDefKind::Unknown => unreachable!(), } } } diff --git a/crates/gen-guest-c/Cargo.toml b/crates/gen-guest-c/Cargo.toml index 66e55f052..72332f925 100644 --- a/crates/gen-guest-c/Cargo.toml +++ b/crates/gen-guest-c/Cargo.toml @@ -17,4 +17,4 @@ heck = { workspace = true } clap = { workspace = true, optional = true } [dev-dependencies] -test-helpers = { path = '../test-helpers', default-features = false, features = ['macros'] } +test-helpers = { path = '../test-helpers' } diff --git a/crates/gen-guest-rust/Cargo.toml b/crates/gen-guest-rust/Cargo.toml index 1b71347fc..a226bbda5 100644 --- a/crates/gen-guest-rust/Cargo.toml +++ b/crates/gen-guest-rust/Cargo.toml @@ -17,4 +17,4 @@ clap = { workspace = true, optional = true } [dev-dependencies] wit-bindgen-guest-rust = { path = '../guest-rust' } -test-helpers = { path = '../test-helpers', default-features = false, features = ['macros'] } +test-helpers = { path = '../test-helpers' } diff --git a/crates/gen-guest-rust/src/lib.rs b/crates/gen-guest-rust/src/lib.rs index faca76a94..e5a3ab271 100644 --- a/crates/gen-guest-rust/src/lib.rs +++ b/crates/gen-guest-rust/src/lib.rs @@ -16,6 +16,7 @@ use wit_bindgen_gen_rust_lib::{ #[derive(Default)] struct RustWasm { + types: Types, src: Source, opts: Opts, exports: Vec, @@ -86,24 +87,21 @@ impl RustWasm { fn interface<'a>( &'a mut self, - name: &'a str, - iface: &'a Interface, + wasm_import_module: Option<&'a str>, + resolve: &'a Resolve, default_param_mode: TypeMode, in_import: bool, ) -> InterfaceGenerator<'a> { let mut sizes = SizeAlign::default(); - sizes.fill(iface); - let mut types = Types::default(); - types.analyze(iface); + sizes.fill(resolve); InterfaceGenerator { - name, + wasm_import_module, src: Source::default(), in_import, gen: self, - types, sizes, - iface, + resolve, default_param_mode, return_pointer_area_size: 0, return_pointer_area_align: 0, @@ -112,33 +110,78 @@ impl RustWasm { } impl WorldGenerator for RustWasm { - fn import(&mut self, name: &str, iface: &Interface, _files: &mut Files) { - let mut gen = self.interface(name, iface, TypeMode::AllBorrowed("'a"), true); - gen.types(); + fn preprocess(&mut self, resolve: &Resolve, _name: &str) { + self.types.analyze(resolve); + } + + fn import_interface( + &mut self, + resolve: &Resolve, + name: &str, + id: InterfaceId, + _files: &mut Files, + ) { + let mut gen = self.interface(Some(name), resolve, TypeMode::AllBorrowed("'a"), true); + gen.types(id); - for func in iface.functions.iter() { + for (_, func) in resolve.interfaces[id].functions.iter() { gen.generate_guest_import(func); } - gen.append_submodule(name); + gen.finish_append_submodule(name); } - fn export(&mut self, name: &str, iface: &Interface, _files: &mut Files) { - self.interface(name, iface, TypeMode::Owned, false) - .generate_exports(name, Some(name)); + fn import_funcs( + &mut self, + resolve: &Resolve, + _world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let mut gen = self.interface(Some(""), resolve, TypeMode::AllBorrowed("'a"), true); + + for (_, func) in funcs { + gen.generate_guest_import(func); + } + + let src = gen.finish(); + self.src.push_str(&src); } - fn export_default(&mut self, name: &str, iface: &Interface, _files: &mut Files) { - self.interface(name, iface, TypeMode::Owned, false) - .generate_exports(name, None); + fn export_interface( + &mut self, + resolve: &Resolve, + name: &str, + id: InterfaceId, + _files: &mut Files, + ) { + let mut gen = self.interface(None, resolve, TypeMode::Owned, false); + gen.types(id); + gen.generate_exports(name, Some(name), resolve.interfaces[id].functions.values()); + gen.finish_append_submodule(name); + } + + fn export_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let name = &resolve.worlds[world].name; + let mut gen = self.interface(None, resolve, TypeMode::Owned, false); + gen.generate_exports(name, None, funcs.iter().map(|f| f.1)); + let src = gen.finish(); + self.src.push_str(&src); } - fn finish(&mut self, world: &World, files: &mut Files) { + fn finish(&mut self, resolve: &Resolve, world: WorldId, files: &mut Files) { + let name = &resolve.worlds[world].name; if !self.exports.is_empty() { let macro_name = if let Some(name) = self.opts.export_macro_name.as_ref() { name.to_snake_case() } else { - format!("export_{}", world.name.to_snake_case()) + format!("export_{}", name.to_snake_case()) }; let macro_export = if self.opts.macro_export { "#[macro_export]" @@ -184,13 +227,12 @@ impl WorldGenerator for RustWasm { // otherwise is attempted to be unique here to ensure that this doesn't get // concatenated to other custom sections by LLD by accident since LLD will // concatenate custom sections of the same name. - self.src.push_str(&format!( - "#[link_section = \"component-type:{}\"]\n", - world.name, - )); + self.src + .push_str(&format!("#[link_section = \"component-type:{}\"]\n", name,)); let component_type = - wit_component::metadata::encode(world, wit_component::StringEncoding::UTF8); + wit_component::metadata::encode(resolve, world, wit_component::StringEncoding::UTF8) + .unwrap(); self.src.push_str(&format!( "pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; {}] = ", component_type.len() @@ -231,30 +273,32 @@ impl WorldGenerator for RustWasm { assert!(status.success()); } - files.push(&format!("{}.rs", world.name), src.as_bytes()); + files.push(&format!("{name}.rs"), src.as_bytes()); } } struct InterfaceGenerator<'a> { src: Source, in_import: bool, - types: Types, sizes: SizeAlign, gen: &'a mut RustWasm, - name: &'a str, - iface: &'a Interface, + wasm_import_module: Option<&'a str>, + resolve: &'a Resolve, default_param_mode: TypeMode, return_pointer_area_size: usize, return_pointer_area_align: usize, } impl InterfaceGenerator<'_> { - fn generate_exports(mut self, name: &str, interface_name: Option<&str>) { - self.types(); - + fn generate_exports<'a>( + &mut self, + name: &str, + interface_name: Option<&str>, + funcs: impl Iterator + Clone, + ) { let camel = name.to_upper_camel_case(); uwriteln!(self.src, "pub trait {camel} {{"); - for func in self.iface.functions.iter() { + for func in funcs.clone() { if self.gen.skip.contains(&func.name) { continue; } @@ -265,30 +309,31 @@ impl InterfaceGenerator<'_> { } uwriteln!(self.src, "}}"); - for func in self.iface.functions.iter() { + for func in funcs { self.generate_guest_export(name, func, interface_name); } - - self.append_submodule(name); } - fn append_submodule(mut self, name: &str) { + fn finish(&mut self) -> String { if self.return_pointer_area_align > 0 { - let camel = name.to_upper_camel_case(); uwrite!( self.src, " #[repr(align({align}))] - struct {camel}RetArea([u8; {size}]); - static mut RET_AREA: {camel}RetArea = {camel}RetArea([0; {size}]); + struct _RetArea([u8; {size}]); + static mut _RET_AREA: _RetArea = _RetArea([0; {size}]); ", align = self.return_pointer_area_align, size = self.return_pointer_area_size, ); } + mem::take(&mut self.src).into() + } + + fn finish_append_submodule(mut self, name: &str) { + let module = self.finish(); let snake = name.to_snake_case(); - let module = &self.src[..]; uwriteln!( self.gen.src, " @@ -313,12 +358,19 @@ impl InterfaceGenerator<'_> { match &func.kind { FunctionKind::Freestanding => {} } + self.src.push_str("#[allow(clippy::all)]\n"); let params = self.print_signature(func, param_mode, &sig); self.src.push_str("{\n"); + self.src.push_str( + " + #[allow(unused_imports)] + use wit_bindgen_guest_rust::rt::{{alloc, vec::Vec, string::String}}; + ", + ); self.src.push_str("unsafe {\n"); let mut f = FunctionBindgen::new(self, params); - f.gen.iface.call( + f.gen.resolve.call( AbiVariant::GuestImport, LiftLower::LowerArgsLiftResults, func, @@ -355,7 +407,6 @@ impl InterfaceGenerator<'_> { let module_name = module_name.to_snake_case(); let trait_bound = module_name.to_upper_camel_case(); - let iface_snake = self.iface.name.to_snake_case(); let name_snake = func.name.to_snake_case(); let export_name = func.core_export_name(interface_name); let mut macro_src = Source::default(); @@ -380,11 +431,11 @@ impl InterfaceGenerator<'_> { #[doc(hidden)] #[export_name = \"{export_name}\"] #[allow(non_snake_case)] - unsafe extern \"C\" fn __export_{iface_snake}_{name_snake}(\ + unsafe extern \"C\" fn __export_{module_name}_{name_snake}(\ ", ); - let sig = self.iface.wasm_signature(AbiVariant::GuestExport, func); + let sig = self.resolve.wasm_signature(AbiVariant::GuestExport, func); let mut params = Vec::new(); for (i, param) in sig.params.iter().enumerate() { let name = format!("arg{}", i); @@ -419,7 +470,7 @@ impl InterfaceGenerator<'_> { uwriteln!(macro_src, ")\n}}"); let mut f = FunctionBindgen::new(self, params); - f.gen.iface.call( + f.gen.resolve.call( AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, func, @@ -434,7 +485,7 @@ impl InterfaceGenerator<'_> { self.src.push_str(&String::from(src)); self.src.push_str("}\n"); - if self.iface.guest_export_needs_post_return(func) { + if self.resolve.guest_export_needs_post_return(func) { // Like above, generate both a generic function in the module itself // as well as something to go in the export macro. uwrite!( @@ -450,7 +501,7 @@ impl InterfaceGenerator<'_> { #[doc(hidden)] #[export_name = \"cabi_post_{export_name}\"] #[allow(non_snake_case)] - unsafe extern \"C\" fn __post_return_{iface_snake}_{name_snake}(\ + unsafe extern \"C\" fn __post_return_{module_name}_{name_snake}(\ " ); let mut params = Vec::new(); @@ -475,7 +526,7 @@ impl InterfaceGenerator<'_> { uwriteln!(macro_src, ")\n}}"); let mut f = FunctionBindgen::new(self, params); - f.gen.iface.post_return(func, &mut f); + f.gen.resolve.post_return(func, &mut f); let FunctionBindgen { needs_cleanup_list, src, @@ -491,8 +542,8 @@ impl InterfaceGenerator<'_> { } impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { - fn iface(&self) -> &'a Interface { - self.iface + fn resolve(&self) -> &'a Resolve { + self.resolve } fn use_std(&self) -> bool { @@ -512,11 +563,11 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { } fn info(&self, ty: TypeId) -> TypeInfo { - self.types.get(ty) + self.gen.types.get(ty) } fn types_mut(&mut self) -> &mut Types { - &mut self.types + &mut self.gen.types } fn print_borrowed_slice(&mut self, mutbl: bool, ty: &Type, lifetime: &'static str) { @@ -538,8 +589,8 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { } impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { - fn iface(&self) -> &'a Interface { - self.iface + fn resolve(&self) -> &'a Resolve { + self.resolve } fn type_record(&mut self, id: TypeId, _name: &str, record: &Record, docs: &Docs) { @@ -764,7 +815,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } } - fn return_pointer(&mut self, _iface: &Interface, size: usize, align: usize) -> String { + fn return_pointer(&mut self, size: usize, align: usize) -> String { let tmp = self.tmp(); if self.gen.in_import { @@ -780,7 +831,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } else { self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); - uwriteln!(self.src, "let ptr{tmp} = RET_AREA.0.as_mut_ptr() as i32;"); + uwriteln!(self.src, "let ptr{tmp} = _RET_AREA.0.as_mut_ptr() as i32;"); } format!("ptr{}", tmp) } @@ -789,13 +840,13 @@ impl Bindgen for FunctionBindgen<'_, '_> { &self.gen.sizes } - fn is_list_canonical(&self, iface: &Interface, ty: &Type) -> bool { - iface.all_bits_valid(ty) + fn is_list_canonical(&self, resolve: &Resolve, ty: &Type) -> bool { + resolve.all_bits_valid(ty) } fn emit( &mut self, - _iface: &Interface, + _resolve: &Resolve, inst: &Instruction<'_>, operands: &mut Vec, results: &mut Vec, @@ -1317,7 +1368,12 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::IterBasePointer => results.push("base".to_string()), Instruction::CallWasm { name, sig, .. } => { - let func = self.declare_import(self.gen.name, name, &sig.params, &sig.results); + let func = self.declare_import( + self.gen.wasm_import_module.unwrap(), + name, + &sig.params, + &sig.results, + ); // ... then call the function with all our operands if sig.results.len() > 0 { diff --git a/crates/gen-guest-rust/tests/codegen.rs b/crates/gen-guest-rust/tests/codegen.rs index b31c7f2e7..deb2ebf78 100644 --- a/crates/gen-guest-rust/tests/codegen.rs +++ b/crates/gen-guest-rust/tests/codegen.rs @@ -2,9 +2,9 @@ mod codegen_tests { macro_rules! codegen_test { - ($name:ident $test:tt) => { - mod $name { - wit_bindgen_guest_rust::generate!($test); + ($id:ident $name:tt $test:tt) => { + mod $id { + wit_bindgen_guest_rust::generate!($name in $test); #[test] fn works() {} @@ -12,6 +12,7 @@ mod codegen_tests { mod unchecked { wit_bindgen_guest_rust::generate!({ path: $test, + world: $name, unchecked, }); @@ -28,7 +29,7 @@ mod codegen_tests { mod strings { wit_bindgen_guest_rust::generate!({ inline: " - world not-used-name { + default world not-used-name { import cat: interface { foo: func(x: string) bar: func() -> string @@ -51,7 +52,7 @@ mod strings { mod raw_strings { wit_bindgen_guest_rust::generate!({ inline: " - world not-used-name { + default world not-used-name { import cat: interface { foo: func(x: string) bar: func() -> string @@ -78,7 +79,7 @@ mod prefix { mod bindings { wit_bindgen_guest_rust::generate!({ inline: " - world baz { + default world baz { export exports1: interface { foo: func(x: string) bar: func() -> string @@ -111,7 +112,7 @@ mod prefix { mod macro_name { wit_bindgen_guest_rust::generate!({ inline: " - world baz { + default world baz { export exports2: interface { foo: func(x: string) } @@ -134,7 +135,7 @@ mod macro_name { mod skip { wit_bindgen_guest_rust::generate!({ inline: " - world baz { + default world baz { export exports: interface { foo: func() bar: func() diff --git a/crates/gen-guest-teavm-java/Cargo.toml b/crates/gen-guest-teavm-java/Cargo.toml index f38b3b91d..bab1b62c7 100644 --- a/crates/gen-guest-teavm-java/Cargo.toml +++ b/crates/gen-guest-teavm-java/Cargo.toml @@ -10,4 +10,4 @@ heck = { workspace = true } clap = { workspace = true, optional = true } [dev-dependencies] -test-helpers = { path = '../test-helpers', default-features = false, features = ['macros'] } +test-helpers = { path = '../test-helpers' } diff --git a/crates/gen-rust-lib/src/lib.rs b/crates/gen-rust-lib/src/lib.rs index 0b71bd38e..bdf69f2cd 100644 --- a/crates/gen-rust-lib/src/lib.rs +++ b/crates/gen-rust-lib/src/lib.rs @@ -13,7 +13,7 @@ pub enum TypeMode { } pub trait RustGenerator<'a> { - fn iface(&self) -> &'a Interface; + fn resolve(&self) -> &'a Resolve; /// Return true iff the generator can use `std` features in its output. fn use_std(&self) -> bool { @@ -199,7 +199,7 @@ pub trait RustGenerator<'a> { fn print_tyid(&mut self, id: TypeId, mode: TypeMode) { let info = self.info(id); let lt = self.lifetime_for(&info, mode); - let ty = &self.iface().types[id]; + let ty = &self.resolve().types[id]; if ty.name.is_some() { let name = if lt.is_some() { self.param_name(id) @@ -211,13 +211,13 @@ pub trait RustGenerator<'a> { // If the type recursively owns data and it's a // variant/record/list, then we need to place the // lifetime parameter on the type as well. - if info.owns_data() && needs_generics(self.iface(), &ty.kind) { + if info.owns_data() && needs_generics(self.resolve(), &ty.kind) { self.print_generics(lt); } return; - fn needs_generics(iface: &Interface, ty: &TypeDefKind) -> bool { + fn needs_generics(resolve: &Resolve, ty: &TypeDefKind) -> bool { match ty { TypeDefKind::Variant(_) | TypeDefKind::Record(_) @@ -230,9 +230,12 @@ pub trait RustGenerator<'a> { | TypeDefKind::Enum(_) | TypeDefKind::Tuple(_) | TypeDefKind::Union(_) => true, - TypeDefKind::Type(Type::Id(t)) => needs_generics(iface, &iface.types[*t].kind), + TypeDefKind::Type(Type::Id(t)) => { + needs_generics(resolve, &resolve.types[*t].kind) + } TypeDefKind::Type(Type::String) => true, TypeDefKind::Type(_) => false, + TypeDefKind::Unknown => unreachable!(), } } } @@ -293,6 +296,8 @@ pub trait RustGenerator<'a> { } TypeDefKind::Type(t) => self.print_ty(t, mode), + + TypeDefKind::Unknown => unreachable!(), } } @@ -302,7 +307,7 @@ pub trait RustGenerator<'a> { self.print_borrowed_slice(false, ty, lt); } TypeMode::LeafBorrowed(lt) => { - if self.iface().all_bits_valid(ty) { + if self.resolve().all_bits_valid(ty) { self.print_borrowed_slice(false, ty, lt); } else { self.push_str("Vec<"); @@ -381,7 +386,7 @@ pub trait RustGenerator<'a> { Type::Char => out.push_str("Char"), Type::String => out.push_str("String"), Type::Id(id) => { - let ty = &self.iface().types[*id]; + let ty = &self.resolve().types[*id]; match &ty.name { Some(name) => out.push_str(&name.to_upper_camel_case()), None => match &ty.kind { @@ -411,6 +416,7 @@ pub trait RustGenerator<'a> { TypeDefKind::Variant(_) => out.push_str("Variant"), TypeDefKind::Enum(_) => out.push_str("Enum"), TypeDefKind::Union(_) => out.push_str("Union"), + TypeDefKind::Unknown => unreachable!(), }, } } @@ -918,7 +924,7 @@ pub trait RustGenerator<'a> { fn param_name(&self, ty: TypeId) -> String { let info = self.info(ty); - let name = self.iface().types[ty] + let name = self.resolve().types[ty] .name .as_ref() .unwrap() @@ -932,7 +938,7 @@ pub trait RustGenerator<'a> { fn result_name(&self, ty: TypeId) -> String { let info = self.info(ty); - let name = self.iface().types[ty] + let name = self.resolve().types[ty] .name .as_ref() .unwrap() diff --git a/crates/guest-rust-macro/Cargo.toml b/crates/guest-rust-macro/Cargo.toml index 76cfd9e25..62a014694 100644 --- a/crates/guest-rust-macro/Cargo.toml +++ b/crates/guest-rust-macro/Cargo.toml @@ -14,4 +14,5 @@ proc-macro2 = "1.0" syn = "1.0" wit-bindgen-core = { workspace = true } wit-bindgen-gen-guest-rust = { workspace = true } -wit-bindgen-rust-macro-shared = { workspace = true } +wit-component = { workspace = true } +anyhow = { workspace = true } diff --git a/crates/guest-rust-macro/src/lib.rs b/crates/guest-rust-macro/src/lib.rs index 0d8704473..cb60a761f 100644 --- a/crates/guest-rust-macro/src/lib.rs +++ b/crates/guest-rust-macro/src/lib.rs @@ -1,14 +1,177 @@ -use proc_macro::TokenStream; -use syn::{ - parse::{Parse, ParseStream, Result}, - punctuated::Punctuated, - LitStr, Token, -}; +use proc_macro2::{Span, TokenStream}; +use std::path::{Path, PathBuf}; +use syn::parse::{Error, Parse, ParseStream, Result}; +use syn::punctuated::Punctuated; +use syn::{token, Token}; +use wit_bindgen_core::wit_parser::{PackageId, Resolve, UnresolvedPackage, WorldId}; use wit_bindgen_gen_guest_rust::Opts; #[proc_macro] -pub fn generate(input: TokenStream) -> TokenStream { - wit_bindgen_rust_macro_shared::generate::(input, |opts| opts.build()) +pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + syn::parse_macro_input!(input as Config) + .expand() + .unwrap_or_else(Error::into_compile_error) + .into() +} + +struct Config { + opts: Opts, + resolve: Resolve, + world: WorldId, + files: Vec, +} + +enum Source { + Path(String), + Inline(String), +} + +impl Parse for Config { + fn parse(input: ParseStream<'_>) -> Result { + let call_site = Span::call_site(); + let mut opts = Opts::default(); + let mut world = None; + let mut source = None; + + let document = if input.peek(token::Brace) { + let content; + syn::braced!(content in input); + let fields = Punctuated::::parse_terminated(&content)?; + let mut document = None; + for field in fields.into_pairs() { + match field.into_value() { + Opt::Path(s) => { + if source.is_some() { + return Err(Error::new(s.span(), "cannot specify second source")); + } + source = Some(Source::Path(s.value())); + } + Opt::World(s) => { + if document.is_some() { + return Err(Error::new(s.span(), "cannot specify second document")); + } + document = Some(parse_doc(&s.value(), &mut world)); + } + Opt::Inline(s) => { + if source.is_some() { + return Err(Error::new(s.span(), "cannot specify second source")); + } + source = Some(Source::Inline(s.value())); + } + Opt::Unchecked => opts.unchecked = true, + Opt::NoStd => opts.no_std = true, + Opt::RawStrings => opts.raw_strings = true, + Opt::MacroExport => opts.macro_export = true, + Opt::MacroCallPrefix(prefix) => opts.macro_call_prefix = Some(prefix.value()), + Opt::ExportMacroName(name) => opts.export_macro_name = Some(name.value()), + Opt::Skip(list) => opts.skip.extend(list.iter().map(|i| i.value())), + } + } + match (document, &source) { + (Some(doc), _) => doc, + (None, Some(Source::Inline(_))) => "macro-input".to_string(), + _ => { + return Err(Error::new( + call_site, + "must specify a `world` to generate bindings for", + )) + } + } + } else { + let document = input.parse::()?; + if input.parse::>()?.is_some() { + source = Some(Source::Path(input.parse::()?.value())); + } + parse_doc(&document.value(), &mut world) + }; + let (resolve, pkg, files) = + parse_source(&source).map_err(|err| Error::new(call_site, format!("{err:?}")))?; + let doc = resolve.packages[pkg] + .documents + .get(&document) + .copied() + .ok_or_else(|| { + Error::new(call_site, format!("no document named `{document}` found")) + })?; + + let world = match &world { + Some(name) => resolve.documents[doc] + .worlds + .get(name) + .copied() + .ok_or_else(|| Error::new(call_site, format!("no world named `{name}` found")))?, + None => resolve.documents[doc].default_world.ok_or_else(|| { + Error::new(call_site, format!("no default world found in `{document}`")) + })?, + }; + Ok(Config { + opts, + resolve, + world, + files, + }) + } +} + +fn parse_source(source: &Option) -> anyhow::Result<(Resolve, PackageId, Vec)> { + let mut resolve = Resolve::default(); + let mut files = Vec::new(); + let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); + let mut parse = |path: &Path| -> anyhow::Result<_> { + if path.is_dir() { + let (pkg, sources) = resolve.push_dir(&path)?; + files = sources; + Ok(pkg) + } else { + let pkg = UnresolvedPackage::parse_file(path)?; + files.extend(pkg.source_files().map(|s| s.to_owned())); + resolve.push(pkg, &Default::default()) + } + }; + let pkg = match source { + Some(Source::Inline(s)) => resolve.push( + UnresolvedPackage::parse("macro-input".as_ref(), &s)?, + &Default::default(), + )?, + Some(Source::Path(s)) => parse(&root.join(&s))?, + None => parse(&root.join("wit"))?, + }; + + Ok((resolve, pkg, files)) +} + +fn parse_doc(s: &str, world: &mut Option) -> String { + match s.find('.') { + Some(pos) => { + *world = Some(s[pos + 1..].to_string()); + s[..pos].to_string() + } + None => s.to_string(), + } +} + +impl Config { + fn expand(self) -> Result { + let mut files = Default::default(); + self.opts + .build() + .generate(&self.resolve, self.world, &mut files); + let (_, src) = files.iter().next().unwrap(); + let src = std::str::from_utf8(src).unwrap(); + let mut contents = src.parse::().unwrap(); + + // Include a dummy `include_str!` for any files we read so rustc knows that + // we depend on the contents of those files. + for file in self.files.iter() { + contents.extend( + format!("const _: &str = include_str!(r#\"{}\"#);\n", file.display()) + .parse::() + .unwrap(), + ); + } + + Ok(contents) + } } mod kw { @@ -19,22 +182,40 @@ mod kw { syn::custom_keyword!(macro_call_prefix); syn::custom_keyword!(export_macro_name); syn::custom_keyword!(skip); + syn::custom_keyword!(world); + syn::custom_keyword!(path); + syn::custom_keyword!(inline); } enum Opt { + World(syn::LitStr), + Path(syn::LitStr), + Inline(syn::LitStr), Unchecked, NoStd, RawStrings, MacroExport, - MacroCallPrefix(LitStr), - ExportMacroName(LitStr), - Skip(Vec), + MacroCallPrefix(syn::LitStr), + ExportMacroName(syn::LitStr), + Skip(Vec), } impl Parse for Opt { fn parse(input: ParseStream<'_>) -> Result { let l = input.lookahead1(); - if l.peek(kw::unchecked) { + if l.peek(kw::path) { + input.parse::()?; + input.parse::()?; + Ok(Opt::Path(input.parse()?)) + } else if l.peek(kw::inline) { + input.parse::()?; + input.parse::()?; + Ok(Opt::Inline(input.parse()?)) + } else if l.peek(kw::world) { + input.parse::()?; + input.parse::()?; + Ok(Opt::World(input.parse()?)) + } else if l.peek(kw::unchecked) { input.parse::()?; Ok(Opt::Unchecked) } else if l.peek(kw::no_std) { @@ -66,17 +247,3 @@ impl Parse for Opt { } } } - -impl wit_bindgen_rust_macro_shared::Configure for Opt { - fn configure(self, opts: &mut Opts) { - match self { - Opt::Unchecked => opts.unchecked = true, - Opt::NoStd => opts.no_std = true, - Opt::RawStrings => opts.raw_strings = true, - Opt::MacroExport => opts.macro_export = true, - Opt::MacroCallPrefix(prefix) => opts.macro_call_prefix = Some(prefix.value()), - Opt::ExportMacroName(name) => opts.export_macro_name = Some(name.value()), - Opt::Skip(list) => opts.skip.extend(list.iter().map(|i| i.value())), - } - } -} diff --git a/crates/rust-macro-shared/Cargo.toml b/crates/rust-macro-shared/Cargo.toml deleted file mode 100644 index afde4d5ab..000000000 --- a/crates/rust-macro-shared/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "wit-bindgen-rust-macro-shared" -version.workspace = true -edition.workspace = true - -[lib] -doctest = false -test = false - -[dependencies] -proc-macro2 = "1.0" -syn = "1.0" -wit-bindgen-core = { workspace = true } -wit-component = { workspace = true } diff --git a/crates/rust-macro-shared/src/lib.rs b/crates/rust-macro-shared/src/lib.rs deleted file mode 100644 index a356391fb..000000000 --- a/crates/rust-macro-shared/src/lib.rs +++ /dev/null @@ -1,155 +0,0 @@ -extern crate proc_macro; - -use proc_macro::TokenStream; -use proc_macro2::Span; -use std::marker; -use std::path::{Path, PathBuf}; -use syn::parse::{Error, Parse, ParseStream, Result}; -use syn::punctuated::Punctuated; -use syn::{token, Token}; -use wit_bindgen_core::{ - wit_parser::{Document, World}, - Files, WorldGenerator, -}; - -pub fn generate( - input: TokenStream, - mkgen: impl FnOnce(O) -> Box, -) -> TokenStream -where - F: Parse + Configure, - O: Default, -{ - let input = syn::parse_macro_input!(input as Opts); - let mut gen = mkgen(input.opts); - let mut files = Files::default(); - gen.generate(&input.world, &mut files); - - let (_, contents) = files.iter().next().unwrap(); - - let contents = std::str::from_utf8(contents).unwrap(); - let mut contents = contents.parse::().unwrap(); - - // Include a dummy `include_str!` for any files we read so rustc knows that - // we depend on the contents of those files. - let cwd = std::env::var("CARGO_MANIFEST_DIR").unwrap(); - for file in input.files.iter() { - contents.extend( - format!( - "const _: &str = include_str!(r#\"{}\"#);\n", - Path::new(&cwd).join(file).display() - ) - .parse::() - .unwrap(), - ); - } - - return contents; -} - -pub trait Configure { - fn configure(self, opts: &mut O); -} - -struct Opts { - opts: O, - world: World, - files: Vec, - _marker: marker::PhantomData, -} - -mod kw { - syn::custom_keyword!(path); - syn::custom_keyword!(inline); -} - -impl Parse for Opts -where - F: Parse + Configure, - O: Default, -{ - fn parse(input: ParseStream<'_>) -> Result { - let call_site = proc_macro2::Span::call_site(); - let mut world = None; - let mut ret = Opts { - opts: O::default(), - world: World::default(), - files: Vec::new(), - _marker: marker::PhantomData, - }; - - if input.peek(token::Brace) { - let content; - syn::braced!(content in input); - let fields = Punctuated::, Token![,]>::parse_terminated(&content)?; - for field in fields.into_pairs() { - match field.into_value() { - ConfigField::Path(path) => { - if world.is_some() { - return Err(Error::new(path.span(), "cannot specify second world")); - } - world = Some(ret.parse(path)?); - } - ConfigField::Inline(span, w) => { - if world.is_some() { - return Err(Error::new(span, "cannot specify second world")); - } - world = Some(w); - } - ConfigField::Other(other) => other.configure(&mut ret.opts), - } - } - } else { - let s = input.parse::()?; - world = Some(ret.parse(s)?); - } - ret.world = world.ok_or_else(|| { - Error::new( - call_site, - "must specify a `*.wit` file to generate bindings for", - ) - })?; - Ok(ret) - } -} - -impl Opts { - fn parse(&mut self, path: syn::LitStr) -> Result { - let span = path.span(); - let path = path.value(); - let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); - let path = manifest_dir.join(path); - self.files.push(path.to_str().unwrap().to_string()); - World::parse_file(path).map_err(|e| Error::new(span, e)) - } -} - -enum ConfigField { - Path(syn::LitStr), - Inline(Span, World), - Other(F), -} - -impl Parse for ConfigField { - fn parse(input: ParseStream<'_>) -> Result { - let l = input.lookahead1(); - if l.peek(kw::path) { - input.parse::()?; - input.parse::()?; - Ok(ConfigField::Path(input.parse()?)) - } else if l.peek(kw::inline) { - let span = input.parse::()?.span; - Ok(ConfigField::Inline(span, parse_inline(input)?)) - } else { - Ok(ConfigField::Other(input.parse()?)) - } - } -} - -fn parse_inline(input: ParseStream<'_>) -> Result { - input.parse::()?; - let s = input.parse::()?; - Document::parse(Path::new(""), &s.value()) - .and_then(Document::into_world) - .map_err(|e| Error::new(s.span(), e)) -} diff --git a/crates/test-helpers/Cargo.toml b/crates/test-helpers/Cargo.toml index 81c7d651c..d53e47418 100644 --- a/crates/test-helpers/Cargo.toml +++ b/crates/test-helpers/Cargo.toml @@ -9,15 +9,16 @@ doctest = false test = false [dependencies] -test-helpers-macros = { path = 'macros', optional = true } +runtime-macro = { path = 'runtime-macro', optional = true } +codegen-macro = { path = 'codegen-macro' } wit-bindgen-core = { workspace = true } wit-parser = { workspace = true } -wit-component = { workspace = true } +wit-component = { workspace = true, features = ['dummy-module'] } wat = { workspace = true } +wasm-encoder = { workspace = true } [features] -default = ['guest-rust', 'guest-c', 'guest-teavm-java', 'macros'] -macros = ['dep:test-helpers-macros'] -guest-rust = ['test-helpers-macros?/guest-rust'] -guest-c = ['test-helpers-macros?/guest-c'] -guest-teavm-java = ['test-helpers-macros?/guest-teavm-java'] +default = ['guest-rust', 'guest-c', 'guest-teavm-java'] +guest-rust = ['runtime-macro?/guest-rust'] +guest-c = ['runtime-macro?/guest-c'] +guest-teavm-java = ['runtime-macro?/guest-teavm-java'] diff --git a/crates/test-helpers/codegen-macro/Cargo.toml b/crates/test-helpers/codegen-macro/Cargo.toml new file mode 100644 index 000000000..71e368a0b --- /dev/null +++ b/crates/test-helpers/codegen-macro/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "codegen-macro" +authors = ["Alex Crichton "] +version.workspace = true +edition.workspace = true +publish = false + +[lib] +proc-macro = true +doctest = false +test = false + +[dependencies] +heck = { workspace = true } +ignore = "0.4" +quote = "1.0.9" diff --git a/crates/test-helpers/macros/src/lib.rs b/crates/test-helpers/codegen-macro/src/lib.rs similarity index 51% rename from crates/test-helpers/macros/src/lib.rs rename to crates/test-helpers/codegen-macro/src/lib.rs index 66c46abc6..065d7bae6 100644 --- a/crates/test-helpers/macros/src/lib.rs +++ b/crates/test-helpers/codegen-macro/src/lib.rs @@ -3,8 +3,6 @@ use ignore::gitignore::GitignoreBuilder; use proc_macro::{TokenStream, TokenTree}; use std::env; -include!(concat!(env!("OUT_DIR"), "/wasms.rs")); - /// This macro is invoked with a list of string literals as arguments which are /// gitignore-style filters of tests to run in the `test/codegen` directory. /// @@ -40,49 +38,11 @@ pub fn codegen_tests(input: TokenStream) -> TokenStream { }) .map(|test| { let name = test.file_stem().unwrap().to_str().unwrap(); - let test = test.to_str().unwrap(); - let test_name = quote::format_ident!("{}", name.to_snake_case()); + let path = test.to_str().unwrap(); + let ident = quote::format_ident!("{}", name.to_snake_case()); quote::quote! { - codegen_test!(#test_name #test); + codegen_test!(#ident #name #path); } }); (quote::quote!(#(#tests)*)).into() } - -/// Invoked as `runtime_component_tests!("js")` to run a top-level `execute` -/// function with all host tests that use the "js" extension. -#[proc_macro] -pub fn runtime_component_tests(input: TokenStream) -> TokenStream { - let host_extension = input.to_string(); - let host_extension = host_extension.trim_matches('"'); - let host_file = format!("host.{}", host_extension); - let mut tests = Vec::new(); - let cwd = std::env::current_dir().unwrap(); - for entry in std::fs::read_dir(cwd.join("tests/runtime")).unwrap() { - let entry = entry.unwrap().path(); - if !entry.join(&host_file).exists() { - continue; - } - let name_str = entry.file_name().unwrap().to_str().unwrap(); - for (lang, name, _wasm, component) in WASMS { - if *name != name_str { - continue; - } - let name = quote::format_ident!("{}_{}", name_str, lang); - let host_file = entry.join(&host_file).to_str().unwrap().to_string(); - tests.push(quote::quote! { - #[test] - fn #name() { - crate::execute( - #name_str, - #lang, - #component.as_ref(), - #host_file.as_ref(), - ) - } - }); - } - } - - (quote::quote!(#(#tests)*)).into() -} diff --git a/crates/test-helpers/macros/Cargo.toml b/crates/test-helpers/runtime-macro/Cargo.toml similarity index 51% rename from crates/test-helpers/macros/Cargo.toml rename to crates/test-helpers/runtime-macro/Cargo.toml index 43bda7e4a..8037f39e0 100644 --- a/crates/test-helpers/macros/Cargo.toml +++ b/crates/test-helpers/runtime-macro/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "test-helpers-macros" +name = "runtime-macro" authors = ["Alex Crichton "] version.workspace = true edition.workspace = true @@ -11,23 +11,16 @@ doctest = false test = false [dependencies] -backtrace = "0.3" -heck = { workspace = true } -ignore = "0.4" -proc-macro2 = "1.0.27" quote = "1.0.9" -wit-bindgen-core = { workspace = true } -wit-parser = { workspace = true } -filetime = "0.2" [build-dependencies] heck = { workspace = true } -wit-bindgen-gen-guest-c = { workspace = true } -wit-bindgen-gen-guest-teavm-java = { workspace = true } +wit-bindgen-gen-guest-c = { workspace = true, optional = true } +wit-bindgen-gen-guest-teavm-java = { workspace = true, optional = true } wit-bindgen-core = { workspace = true } wit-component = { workspace = true } [features] guest-rust = [] -guest-c = [] -guest-teavm-java = [] +guest-c = ['dep:wit-bindgen-gen-guest-c'] +guest-teavm-java = ['dep:wit-bindgen-gen-guest-teavm-java'] diff --git a/crates/test-helpers/macros/build.rs b/crates/test-helpers/runtime-macro/build.rs similarity index 94% rename from crates/test-helpers/macros/build.rs rename to crates/test-helpers/runtime-macro/build.rs index ed97f1a07..f70787a72 100644 --- a/crates/test-helpers/macros/build.rs +++ b/crates/test-helpers/runtime-macro/build.rs @@ -1,17 +1,18 @@ -use heck::{ToSnakeCase, ToUpperCamelCase}; -use std::env; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; -use wit_bindgen_core::wit_parser::World; -use wit_component::{ComponentEncoder, StringEncoding}; +use wit_bindgen_core::wit_parser::{PackageId, Resolve}; +use wit_component::ComponentEncoder; +#[cfg(feature = "guest-c")] fn guest_c( wasms: &mut Vec<(String, String, String, String)>, out_dir: &PathBuf, wasi_adapter: &[u8], utf_16: bool, ) { + use heck::{ToSnakeCase, ToUpperCamelCase}; + let utf16_suffix = if utf_16 { "_utf16" } else { "" }; for test_dir in fs::read_dir("../../../tests/runtime").unwrap() { let test_dir = test_dir.unwrap().path(); @@ -25,7 +26,7 @@ fn guest_c( let mut files = Default::default(); let mut opts = wit_bindgen_gen_guest_c::Opts::default(); if utf_16 { - opts.string_encoding = StringEncoding::UTF16; + opts.string_encoding = wit_component::StringEncoding::UTF16; } opts.build().generate(&world, &mut files); @@ -42,7 +43,7 @@ fn guest_c( } let path = - PathBuf::from(env::var_os("WASI_SDK_PATH").expect( + PathBuf::from(std::env::var_os("WASI_SDK_PATH").expect( "point the `WASI_SDK_PATH` environment variable to the path of your wasi-sdk", )); let mut cmd = Command::new(path.join("bin/clang")); @@ -182,12 +183,14 @@ fn main() { println!("cargo:rerun-if-changed=../../test-rust-wasm/Cargo.toml"); } - if cfg!(feature = "guest-c") { + #[cfg(feature = "guest-c")] + { guest_c(&mut wasms, &out_dir, &wasi_adapter, false); guest_c(&mut wasms, &out_dir, &wasi_adapter, true); } - if cfg!(feature = "guest-teavm-java") { + #[cfg(feature = "guest-teavm-java")] + { for test_dir in fs::read_dir("../../../tests/runtime").unwrap() { let test_dir = test_dir.unwrap().path(); let java_impl = test_dir.join("wasm.java"); @@ -284,13 +287,16 @@ fn main() { std::fs::write(out_dir.join("wasms.rs"), src).unwrap(); } -fn read_world(dir: &Path) -> World { - let world = dir.join("world.wit"); - println!("cargo:rerun-if-changed={}", world.display()); - - World::parse_file(&world).unwrap() +fn read_world(dir: &Path) -> (Resolve, PackageId) { + let mut resolve = Resolve::new(); + let (pkg, files) = resolve.push_dir(dir).unwrap(); + for file in files { + println!("cargo:rerun-if-changed={}", file.display()); + } + (resolve, pkg) } +#[cfg(feature = "guest-teavm-java")] fn mvn() -> Command { if cfg!(windows) { let mut cmd = Command::new("cmd"); @@ -301,6 +307,7 @@ fn mvn() -> Command { } } +#[cfg(feature = "guest-teavm-java")] fn pom_xml(classes_to_preserve: &[&str]) -> Vec { let xml = include_str!("../../gen-guest-teavm-java/tests/pom.xml"); let position = xml.find("").unwrap(); diff --git a/crates/test-helpers/runtime-macro/src/lib.rs b/crates/test-helpers/runtime-macro/src/lib.rs new file mode 100644 index 000000000..965e5b1c6 --- /dev/null +++ b/crates/test-helpers/runtime-macro/src/lib.rs @@ -0,0 +1,42 @@ +use proc_macro::{TokenStream, TokenTree}; +use std::env; + +include!(concat!(env!("OUT_DIR"), "/wasms.rs")); + +/// Invoked as `runtime_component_tests!("js")` to run a top-level `execute` +/// function with all host tests that use the "js" extension. +#[proc_macro] +pub fn runtime_component_tests(input: TokenStream) -> TokenStream { + let host_extension = input.to_string(); + let host_extension = host_extension.trim_matches('"'); + let host_file = format!("host.{}", host_extension); + let mut tests = Vec::new(); + let cwd = std::env::current_dir().unwrap(); + for entry in std::fs::read_dir(cwd.join("tests/runtime")).unwrap() { + let entry = entry.unwrap().path(); + if !entry.join(&host_file).exists() { + continue; + } + let name_str = entry.file_name().unwrap().to_str().unwrap(); + for (lang, name, _wasm, component) in WASMS { + if *name != name_str { + continue; + } + let name = quote::format_ident!("{}_{}", name_str, lang); + let host_file = entry.join(&host_file).to_str().unwrap().to_string(); + tests.push(quote::quote! { + #[test] + fn #name() { + crate::execute( + #name_str, + #lang, + #component.as_ref(), + #host_file.as_ref(), + ) + } + }); + } + } + + (quote::quote!(#(#tests)*)).into() +} diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index bb3ef2cb3..8311f8827 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -1,17 +1,14 @@ -#[cfg(feature = "macros")] -pub use test_helpers_macros::*; +pub use codegen_macro::*; +#[cfg(feature = "runtime-macro")] +pub use runtime_macro::*; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; +use wasm_encoder::{Encode, Section}; use wit_bindgen_core::Files; -use wit_parser::abi::{AbiVariant, WasmType}; -use wit_parser::{Function, Interface, World}; - -pub enum Direction { - Import, - Export, -} +use wit_component::StringEncoding; +use wit_parser::{Resolve, UnresolvedPackage, WorldId}; /// Returns a suitable directory to place output for tests within. /// @@ -63,24 +60,25 @@ stderr --- pub fn run_world_codegen_test( gen_name: &str, wit_path: &Path, - generate: fn(&World, &mut Files), + generate: fn(&Resolve, WorldId, &mut Files), verify: fn(&Path, &str), ) { - let world = World::parse_file(wit_path).unwrap(); + let (resolve, world) = parse_wit(wit_path); + let world_name = &resolve.worlds[world].name; let wit_name = wit_path.file_stem().and_then(|s| s.to_str()).unwrap(); let gen_name = format!("{gen_name}-{wit_name}"); - let dir = test_directory("codegen", &gen_name, &world.name); + let dir = test_directory("codegen", &gen_name, &world_name); let mut files = Default::default(); - generate(&world, &mut files); + generate(&resolve, world, &mut files); for (file, contents) in files.iter() { let dst = dir.join(file); std::fs::create_dir_all(dst.parent().unwrap()).unwrap(); std::fs::write(&dst, contents).unwrap(); } - verify(&dir, &world.name); + verify(&dir, &world_name); } pub fn run_component_codegen_test( @@ -89,98 +87,58 @@ pub fn run_component_codegen_test( generate: fn(&str, &[u8], &mut Files), verify: fn(&Path, &str), ) { - let world = World::parse_file(wit_path).unwrap(); - let wasm = dummy_module(&world); + let (resolve, world) = parse_wit(wit_path); + let world_name = &resolve.worlds[world].name; + let mut wasm = wit_component::dummy_module(&resolve, world); + let encoded = wit_component::metadata::encode(&resolve, world, StringEncoding::UTF8).unwrap(); + let section = wasm_encoder::CustomSection { + name: "component-type", + data: &encoded, + }; + wasm.push(section.id()); + section.encode(&mut wasm); + let component = wit_component::ComponentEncoder::default() .module(&wasm) .unwrap() .validate(true) - .world(world.clone(), wit_component::StringEncoding::UTF8) - .unwrap() .encode() .unwrap(); let wit_name = wit_path.file_stem().and_then(|s| s.to_str()).unwrap(); let gen_name = format!("{gen_name}-{wit_name}",); - let dir = test_directory("codegen", &gen_name, &world.name); + let dir = test_directory("codegen", &gen_name, &world_name); std::fs::write(dir.join("component.wasm"), &component).unwrap(); let mut files = Default::default(); - generate(&world.name, &component, &mut files); + generate(&world_name, &component, &mut files); for (file, contents) in files.iter() { let dst = dir.join(file); std::fs::create_dir_all(dst.parent().unwrap()).unwrap(); std::fs::write(&dst, contents).unwrap(); } - verify(&dir, &world.name); + verify(&dir, &world_name); } -pub fn dummy_module(world: &World) -> Vec { - let mut wat = String::new(); - wat.push_str("(module\n"); - for (name, import) in world.imports.iter() { - for func in import.functions.iter() { - let sig = import.wasm_signature(AbiVariant::GuestImport, func); - - wat.push_str(&format!("(import \"{name}\" \"{}\" (func", func.name)); - push_tys(&mut wat, "param", &sig.params); - push_tys(&mut wat, "result", &sig.results); - wat.push_str("))\n"); - } - } - - for (name, export) in world.exports.iter() { - for func in export.functions.iter() { - let name = func.core_export_name(Some(name)); - push_func(&mut wat, &name, export, func); - } - } - - if let Some(default) = &world.default { - for func in default.functions.iter() { - push_func(&mut wat, &func.name, default, func); - } - } - - wat.push_str("(memory (export \"memory\") 0)\n"); - wat.push_str( - "(func (export \"cabi_realloc\") (param i32 i32 i32 i32) (result i32) unreachable)\n", - ); - wat.push_str(")\n"); - - return wat::parse_str(&wat).unwrap(); - - fn push_func(wat: &mut String, name: &str, iface: &Interface, func: &Function) { - let sig = iface.wasm_signature(AbiVariant::GuestExport, func); - wat.push_str(&format!("(func (export \"{name}\")")); - push_tys(wat, "param", &sig.params); - push_tys(wat, "result", &sig.results); - wat.push_str(" unreachable)\n"); - - if iface.guest_export_needs_post_return(func) { - wat.push_str(&format!("(func (export \"cabi_post_{name}\")")); - push_tys(wat, "param", &sig.results); - wat.push_str(")\n"); - } - } - - fn push_tys(dst: &mut String, desc: &str, params: &[WasmType]) { - if params.is_empty() { - return; - } - dst.push_str(" ("); - dst.push_str(desc); - for ty in params { - dst.push_str(" "); - match ty { - WasmType::I32 => dst.push_str("i32"), - WasmType::I64 => dst.push_str("i64"), - WasmType::F32 => dst.push_str("f32"), - WasmType::F64 => dst.push_str("f64"), - } - } - dst.push_str(")"); - } +fn parse_wit(path: &Path) -> (Resolve, WorldId) { + let mut resolve = Resolve::default(); + let pkg = if path.is_dir() { + resolve.push_dir(path).unwrap().0 + } else { + resolve + .push( + UnresolvedPackage::parse_file(path).unwrap(), + &Default::default(), + ) + .unwrap() + }; + let world = resolve.packages[pkg] + .documents + .iter() + .filter_map(|(_, doc)| resolve.documents[*doc].default_world) + .next() + .expect("no `default world` found"); + (resolve, world) } diff --git a/tests/codegen/char.wit b/tests/codegen/char.wit index 85787e840..01b20f700 100644 --- a/tests/codegen/char.wit +++ b/tests/codegen/char.wit @@ -5,8 +5,7 @@ interface chars { return-char: func() -> char } -world the-world { - import imports: chars - export exports: chars - default export chars +default world the-world { + import imports: self.chars + export exports: self.chars } diff --git a/tests/codegen/conventions.wit b/tests/codegen/conventions.wit index 23c472c18..2c5645c66 100644 --- a/tests/codegen/conventions.wit +++ b/tests/codegen/conventions.wit @@ -32,8 +32,7 @@ interface conventions { %bool: func() } -world the-world { - import imports: conventions - export exports: conventions - default export conventions +default world the-world { + import imports: self.conventions + export exports: self.conventions } diff --git a/tests/codegen/empty.wit b/tests/codegen/empty.wit index 48c1b8597..1f99081f5 100644 --- a/tests/codegen/empty.wit +++ b/tests/codegen/empty.wit @@ -1 +1 @@ -world empty {} +default world empty {} diff --git a/tests/codegen/flags.wit b/tests/codegen/flags.wit index d92111ff9..b5a2fa2d1 100644 --- a/tests/codegen/flags.wit +++ b/tests/codegen/flags.wit @@ -47,8 +47,7 @@ interface flegs { roundtrip-flag64: func(x: flag64) -> flag64 } -world the-flags { - import import-flags: flegs - export export-flags: flegs - default export flegs +default world the-flags { + import import-flags: self.flegs + export export-flags: self.flegs } diff --git a/tests/codegen/floats.wit b/tests/codegen/floats.wit index 274770cc1..4a0c67ce2 100644 --- a/tests/codegen/floats.wit +++ b/tests/codegen/floats.wit @@ -5,8 +5,7 @@ interface floats { float64-result: func() -> float64 } -world the-world { - import imports: floats - export exports: floats - default export floats +default world the-world { + import imports: self.floats + export exports: self.floats } diff --git a/tests/codegen/integers.wit b/tests/codegen/integers.wit index c5e7f096c..bfad27288 100644 --- a/tests/codegen/integers.wit +++ b/tests/codegen/integers.wit @@ -32,8 +32,7 @@ interface integers { pair-ret: func() -> tuple } -world the-world { - import imports: integers - export exports: integers - default export integers +default world the-world { + import imports: self.integers + export exports: self.integers } diff --git a/tests/codegen/keywords.wit b/tests/codegen/keywords.wit index 64038086a..eda6c5501 100644 --- a/tests/codegen/keywords.wit +++ b/tests/codegen/keywords.wit @@ -2,8 +2,7 @@ interface keywords { %type: func(%type: u32) -> (%type: u32, %flags: s32) } -world the-world { - import imports: keywords - export exports: keywords - default export keywords +default world the-world { + import imports: self.keywords + export exports: self.keywords } diff --git a/tests/codegen/lists.wit b/tests/codegen/lists.wit index cb5b60435..19b946068 100644 --- a/tests/codegen/lists.wit +++ b/tests/codegen/lists.wit @@ -77,8 +77,7 @@ interface lists { load-store-everything: func(a: load-store-all-sizes) -> load-store-all-sizes } -world the-lists { - import import-lists: lists - export export-lists: lists - default export lists +default world the-lists { + import import-lists: self.lists + export export-lists: self.lists } diff --git a/tests/codegen/many-arguments.wit b/tests/codegen/many-arguments.wit index b7915ad36..a5b67b218 100644 --- a/tests/codegen/many-arguments.wit +++ b/tests/codegen/many-arguments.wit @@ -44,8 +44,7 @@ interface manyarg { big-argument: func(x: big-struct) } -world the-world { - import imports: manyarg - export exports: manyarg - default export manyarg +default world the-world { + import imports: self.manyarg + export exports: self.manyarg } diff --git a/tests/codegen/multi-return.wit b/tests/codegen/multi-return.wit index 4239e1efe..716e77a6c 100644 --- a/tests/codegen/multi-return.wit +++ b/tests/codegen/multi-return.wit @@ -6,8 +6,7 @@ interface multi-return { mre: func() -> (a: u32, b: float32) } -world the-world { - import imports: multi-return - export exports: multi-return - default export multi-return +default world the-world { + import imports: self.multi-return + export exports: self.multi-return } diff --git a/tests/codegen/records.wit b/tests/codegen/records.wit index 6d8f1712e..39d5e8169 100644 --- a/tests/codegen/records.wit +++ b/tests/codegen/records.wit @@ -53,8 +53,7 @@ interface records { typedef-inout: func(e: tuple-typedef2) -> s32 } -world the-world { - import imports: records - export exports: records - default export records +default world the-world { + import imports: self.records + export exports: self.records } diff --git a/tests/codegen/simple-functions.wit b/tests/codegen/simple-functions.wit index 0f25e6c86..cdb118379 100644 --- a/tests/codegen/simple-functions.wit +++ b/tests/codegen/simple-functions.wit @@ -9,8 +9,7 @@ interface simple { f6: func(a: u32, b: u32, c: u32) -> tuple } -world the-world { - import imports: simple - export exports: simple - default export simple +default world the-world { + import imports: self.simple + export exports: self.simple } diff --git a/tests/codegen/simple-lists.wit b/tests/codegen/simple-lists.wit index 416c94107..c664c0c87 100644 --- a/tests/codegen/simple-lists.wit +++ b/tests/codegen/simple-lists.wit @@ -6,8 +6,7 @@ interface simple-lists { simple-list4: func(l: list>) -> list> } -world my-world { - import imports: simple-lists - export exports: simple-lists - default export simple-lists +default world my-world { + import imports: self.simple-lists + export exports: self.simple-lists } diff --git a/tests/codegen/small-anonymous.wit b/tests/codegen/small-anonymous.wit index 8f7f69007..964360913 100644 --- a/tests/codegen/small-anonymous.wit +++ b/tests/codegen/small-anonymous.wit @@ -7,8 +7,7 @@ interface anon { option-test: func() -> result, error> } -world the-world { - import imports: anon - export exports: anon - default export anon +default world the-world { + import imports: self.anon + export exports: self.anon } diff --git a/tests/codegen/smoke-default.wit b/tests/codegen/smoke-default.wit index e1560ac55..0d2693590 100644 --- a/tests/codegen/smoke-default.wit +++ b/tests/codegen/smoke-default.wit @@ -1,5 +1,3 @@ -world the-world { - default export interface { - y: func() - } +default world the-world { + export y: func() } diff --git a/tests/codegen/smoke-export.wit b/tests/codegen/smoke-export.wit index 84500ce18..cefdc1982 100644 --- a/tests/codegen/smoke-export.wit +++ b/tests/codegen/smoke-export.wit @@ -1,4 +1,4 @@ -world the-world { +default world the-world { export the-name: interface { y: func() } diff --git a/tests/codegen/smoke.wit b/tests/codegen/smoke.wit index eddf74392..6d8e80cce 100644 --- a/tests/codegen/smoke.wit +++ b/tests/codegen/smoke.wit @@ -1,4 +1,4 @@ -world the-world { +default world the-world { import imports: interface { y: func() } diff --git a/tests/codegen/strings.wit b/tests/codegen/strings.wit index a7511c920..7fc188552 100644 --- a/tests/codegen/strings.wit +++ b/tests/codegen/strings.wit @@ -4,8 +4,7 @@ interface strings { c: func(a: string, b: string) -> string } -world the-world { - import imports: strings - export exports: strings - default export strings +default world the-world { + import imports: self.strings + export exports: self.strings } diff --git a/tests/codegen/unions.wit b/tests/codegen/unions.wit index ea2d191c6..07e0f380a 100644 --- a/tests/codegen/unions.wit +++ b/tests/codegen/unions.wit @@ -58,8 +58,7 @@ interface unions { identify-distinguishable-num: func(num: distinguishable-num) -> u8 } -world the-unions { - import import-unions: unions - export export-unions: unions - default export unions +default world the-unions { + import import-unions: self.unions + export export-unions: self.unions } diff --git a/tests/codegen/variants.wit b/tests/codegen/variants.wit index 288ee19f3..590675dcf 100644 --- a/tests/codegen/variants.wit +++ b/tests/codegen/variants.wit @@ -139,8 +139,7 @@ interface variants { return-named-result: func() -> (a: result) } -world my-world { - import imports: variants - export exports: variants - default export variants +default world my-world { + import imports: self.variants + export exports: self.variants } From 41da3bb59e5f4ba443f9f6afe0dd9d1c64bdd30c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 18 Jan 2023 09:28:35 -0800 Subject: [PATCH 02/25] Add support for using types across interfaces to Rust --- crates/gen-guest-rust/src/lib.rs | 18 ++++++++++++++++++ crates/gen-rust-lib/src/lib.rs | 7 +++++++ tests/codegen/import-func.wit | 6 ++++++ tests/codegen/use-across-interfaces.wit | 18 ++++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 tests/codegen/import-func.wit create mode 100644 tests/codegen/use-across-interfaces.wit diff --git a/crates/gen-guest-rust/src/lib.rs b/crates/gen-guest-rust/src/lib.rs index e5a3ab271..e4a1100d4 100644 --- a/crates/gen-guest-rust/src/lib.rs +++ b/crates/gen-guest-rust/src/lib.rs @@ -96,6 +96,7 @@ impl RustWasm { sizes.fill(resolve); InterfaceGenerator { + current_interface: None, wasm_import_module, src: Source::default(), in_import, @@ -122,6 +123,7 @@ impl WorldGenerator for RustWasm { _files: &mut Files, ) { let mut gen = self.interface(Some(name), resolve, TypeMode::AllBorrowed("'a"), true); + gen.current_interface = Some(id); gen.types(id); for (_, func) in resolve.interfaces[id].functions.iter() { @@ -156,6 +158,7 @@ impl WorldGenerator for RustWasm { _files: &mut Files, ) { let mut gen = self.interface(None, resolve, TypeMode::Owned, false); + gen.current_interface = Some(id); gen.types(id); gen.generate_exports(name, Some(name), resolve.interfaces[id].functions.values()); gen.finish_append_submodule(name); @@ -279,6 +282,7 @@ impl WorldGenerator for RustWasm { struct InterfaceGenerator<'a> { src: Source, + current_interface: Option, in_import: bool, sizes: SizeAlign, gen: &'a mut RustWasm, @@ -546,6 +550,20 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { self.resolve } + fn path_to_interface(&self, interface: InterfaceId) -> Option { + match self.current_interface { + Some(id) if id == interface => None, + _ => { + let name = self.resolve.interfaces[interface].name.clone().unwrap(); + Some(if self.current_interface.is_some() { + format!("super::{name}") + } else { + name + }) + } + } + } + fn use_std(&self) -> bool { !self.gen.opts.no_std } diff --git a/crates/gen-rust-lib/src/lib.rs b/crates/gen-rust-lib/src/lib.rs index bdf69f2cd..df4b1aa16 100644 --- a/crates/gen-rust-lib/src/lib.rs +++ b/crates/gen-rust-lib/src/lib.rs @@ -14,6 +14,7 @@ pub enum TypeMode { pub trait RustGenerator<'a> { fn resolve(&self) -> &'a Resolve; + fn path_to_interface(&self, interface: InterfaceId) -> Option; /// Return true iff the generator can use `std` features in its output. fn use_std(&self) -> bool { @@ -206,6 +207,12 @@ pub trait RustGenerator<'a> { } else { self.result_name(id) }; + if let TypeOwner::Interface(id) = ty.owner { + if let Some(path) = self.path_to_interface(id) { + self.push_str(&path); + self.push_str("::"); + } + } self.push_str(&name); // If the type recursively owns data and it's a diff --git a/tests/codegen/import-func.wit b/tests/codegen/import-func.wit new file mode 100644 index 000000000..a49a92678 --- /dev/null +++ b/tests/codegen/import-func.wit @@ -0,0 +1,6 @@ +default world foo { + import foo: func() + import foo1: func() -> string + import foo2: func(x: string) + import foo3: func(x: list) -> result, string> +} diff --git a/tests/codegen/use-across-interfaces.wit b/tests/codegen/use-across-interfaces.wit new file mode 100644 index 000000000..821509501 --- /dev/null +++ b/tests/codegen/use-across-interfaces.wit @@ -0,0 +1,18 @@ +interface foo { + record a {} + x: func() -> a +} + +interface bar { + use self.foo.{a} + x: func() -> a +} + +default world baz { + import foo: self.foo + import bar: self.bar + import baz: interface { + use self.foo.{a} + x: func() -> a + } +} From b1a4752afbed7e11d7d3aa53438f22a9a7080080 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 18 Jan 2023 11:33:25 -0800 Subject: [PATCH 03/25] Support renaming interfaces in imports --- crates/gen-guest-rust/src/lib.rs | 10 +++++++--- tests/codegen/rename-interface.wit | 12 ++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 tests/codegen/rename-interface.wit diff --git a/crates/gen-guest-rust/src/lib.rs b/crates/gen-guest-rust/src/lib.rs index e4a1100d4..98b6cb0da 100644 --- a/crates/gen-guest-rust/src/lib.rs +++ b/crates/gen-guest-rust/src/lib.rs @@ -1,5 +1,5 @@ use heck::*; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::fmt::Write as _; use std::io::{Read, Write}; use std::mem; @@ -21,6 +21,7 @@ struct RustWasm { opts: Opts, exports: Vec, skip: HashSet, + interface_names: HashMap, } #[derive(Default, Debug, Clone)] @@ -122,6 +123,8 @@ impl WorldGenerator for RustWasm { id: InterfaceId, _files: &mut Files, ) { + let prev = self.interface_names.insert(id, name.to_snake_case()); + assert!(prev.is_none()); let mut gen = self.interface(Some(name), resolve, TypeMode::AllBorrowed("'a"), true); gen.current_interface = Some(id); gen.types(id); @@ -157,6 +160,7 @@ impl WorldGenerator for RustWasm { id: InterfaceId, _files: &mut Files, ) { + self.interface_names.insert(id, name.to_snake_case()); let mut gen = self.interface(None, resolve, TypeMode::Owned, false); gen.current_interface = Some(id); gen.types(id); @@ -554,11 +558,11 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { match self.current_interface { Some(id) if id == interface => None, _ => { - let name = self.resolve.interfaces[interface].name.clone().unwrap(); + let name = &self.gen.interface_names[&interface]; Some(if self.current_interface.is_some() { format!("super::{name}") } else { - name + name.clone() }) } } diff --git a/tests/codegen/rename-interface.wit b/tests/codegen/rename-interface.wit new file mode 100644 index 000000000..af8b3c1cd --- /dev/null +++ b/tests/codegen/rename-interface.wit @@ -0,0 +1,12 @@ +interface foo { + record bar {} +} + +default world the-world { + import different-name: self.foo + import other-name: interface { + use self.foo.{bar} + + a: func() -> bar + } +} From 3786aeece278e700d0a983cc8e5d5c579d43208d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:28:41 -0800 Subject: [PATCH 04/25] Get all codegen tests for JS passing again --- Cargo.lock | 69 +++--- Cargo.toml | 4 + crates/bindgen-core/src/component.rs | 2 +- crates/gen-host-js/src/lib.rs | 306 +++++++++++++++++---------- crates/gen-host-js/tests/codegen.rs | 4 +- 5 files changed, 242 insertions(+), 143 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea8a8b18c..65375f7a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,8 +120,8 @@ dependencies = [ [[package]] name = "cranelift-entity" -version = "0.91.0" -source = "git+https://github.com/bytecodealliance/wasmtime#28cf995fd3f9926a95036758c78390f54a0289ae" +version = "0.93.0" +source = "git+https://github.com/bytecodealliance/wasmtime#da03ff47f1b611d5540b05997473f471247f38bb" dependencies = [ "serde", ] @@ -547,8 +547,8 @@ version = "0.3.0" dependencies = [ "codegen-macro", "runtime-macro", - "wasm-encoder 0.21.0", - "wat", + "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wat 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "wit-bindgen-core", "wit-component", "wit-parser", @@ -698,9 +698,7 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05632e0a66a6ed8cca593c24223aabd6262f256c3693ad9822c315285f010614" +version = "0.21.0" dependencies = [ "leb128", ] @@ -716,9 +714,7 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.95.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ea896273ea99b15132414be1da01ab0d8836415083298ecaffbe308eaac87a" +version = "0.97.0" dependencies = [ "indexmap", "url", @@ -741,18 +737,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595cca929e47a7bec3c941b5a8e133f51b17e6d9dd8c82ab97902196f5a07b42" dependencies = [ "anyhow", - "wasmparser 0.97.0", + "wasmparser 0.97.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmtime-component-util" -version = "4.0.0" -source = "git+https://github.com/bytecodealliance/wasmtime#28cf995fd3f9926a95036758c78390f54a0289ae" +version = "6.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime#da03ff47f1b611d5540b05997473f471247f38bb" [[package]] name = "wasmtime-environ" -version = "4.0.0" -source = "git+https://github.com/bytecodealliance/wasmtime#28cf995fd3f9926a95036758c78390f54a0289ae" +version = "6.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime#da03ff47f1b611d5540b05997473f471247f38bb" dependencies = [ "anyhow", "cranelift-entity", @@ -763,8 +759,8 @@ dependencies = [ "serde", "target-lexicon", "thiserror", - "wasm-encoder 0.20.0", - "wasmparser 0.95.0", + "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.97.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -772,13 +768,23 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "4.0.0" -source = "git+https://github.com/bytecodealliance/wasmtime#28cf995fd3f9926a95036758c78390f54a0289ae" +version = "6.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime#da03ff47f1b611d5540b05997473f471247f38bb" dependencies = [ "cranelift-entity", "serde", "thiserror", - "wasmparser 0.95.0", + "wasmparser 0.97.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wast" +version = "51.0.0" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.21.0", ] [[package]] @@ -790,7 +796,14 @@ dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.21.0", + "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wat" +version = "1.0.53" +dependencies = [ + "wast 51.0.0", ] [[package]] @@ -799,7 +812,7 @@ version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd18c1168d7e8743d9b4f713c0203924f5dcc4a3983eb5e584de9614f9fccde" dependencies = [ - "wast", + "wast 51.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -839,7 +852,7 @@ version = "0.3.0" dependencies = [ "anyhow", "clap", - "wat", + "wat 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "wit-bindgen-core", "wit-bindgen-gen-guest-c", "wit-bindgen-gen-guest-rust", @@ -884,7 +897,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.21.0", + "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "wit-bindgen-core", "wit-component", ] @@ -969,9 +982,7 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a3ab5250e9a9057ec777e8ea9082d716edaee2e2abafe7c6ba98d07d9218992" +version = "0.4.1" dependencies = [ "anyhow", "bitflags", @@ -980,15 +991,13 @@ dependencies = [ "url", "wasm-encoder 0.21.0", "wasmparser 0.97.0", - "wat", + "wat 1.0.53", "wit-parser", ] [[package]] name = "wit-parser" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02cfa79275011530f37e0e164183c606bae1cdc466ea90bcd364d50605486a4d" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index 0b36301df..d9a05a26d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,3 +58,7 @@ wit-bindgen-gen-markdown = { path = 'crates/gen-markdown', features = ['clap'] } wit-bindgen-gen-guest-teavm-java = { path = 'crates/gen-guest-teavm-java', features = ['clap'] } wat = { workspace = true } wit-component = { workspace = true } + +[patch.crates-io] +wit-component = { path = '../wasm-tools/crates/wit-component' } +wit-parser = { path = '../wasm-tools/crates/wit-parser' } diff --git a/crates/bindgen-core/src/component.rs b/crates/bindgen-core/src/component.rs index 77cc17fb6..01b1484af 100644 --- a/crates/bindgen-core/src/component.rs +++ b/crates/bindgen-core/src/component.rs @@ -15,7 +15,7 @@ use wasmtime_environ::component::{ use wasmtime_environ::wasmparser::{Validator, WasmFeatures}; use wasmtime_environ::{ModuleTranslation, PrimaryMap, ScopeVec, Tunables}; use wit_component::DecodedWasm; -use wit_parser::{Resolve, World, WorldId}; +use wit_parser::{Resolve, WorldId}; /// Generate bindings to load and instantiate the specific binary component /// provided. diff --git a/crates/gen-host-js/src/lib.rs b/crates/gen-host-js/src/lib.rs index 4e5700e53..2703a14f7 100644 --- a/crates/gen-host-js/src/lib.rs +++ b/crates/gen-host-js/src/lib.rs @@ -190,7 +190,7 @@ impl Intrinsic { struct JsInterface<'a> { src: Source, gen: &'a mut Js, - iface: &'a Interface, + resolve: &'a Resolve, needs_ty_option: bool, needs_ty_result: bool, } @@ -200,9 +200,11 @@ impl ComponentGenerator for Js { &mut self, component: &Component, modules: &PrimaryMap>, - world: &World, + resolve: &Resolve, + id: WorldId, ) { self.core_module_cnt = modules.len(); + let world = &resolve.worlds[id]; // Generate the TypeScript definition of the `instantiate` function // which is the main workhorse of the generated bindings. @@ -243,12 +245,15 @@ impl ComponentGenerator for Js { // structure. let mut instantiator = Instantiator { src: Source::default(), + sizes: SizeAlign::default(), gen: self, modules, instances: Default::default(), - world, + resolve, + world: id, component, }; + instantiator.sizes.fill(resolve); instantiator.instantiate(); instantiator.gen.src.js(&instantiator.src.js); instantiator.gen.src.js_init(&instantiator.src.js_init); @@ -386,10 +391,17 @@ impl ComponentGenerator for Js { } impl WorldGenerator for Js { - fn import(&mut self, name: &str, iface: &Interface, files: &mut Files) { + fn import_interface( + &mut self, + resolve: &Resolve, + name: &str, + id: InterfaceId, + files: &mut Files, + ) { self.generate_interface( name, - iface, + resolve, + id, "imports", "Imports", files, @@ -402,10 +414,32 @@ impl WorldGenerator for Js { ); } - fn export(&mut self, name: &str, iface: &Interface, files: &mut Files) { + fn import_funcs( + &mut self, + resolve: &Resolve, + _world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let mut gen = self.js_interface(resolve); + for (_, func) in funcs { + gen.ts_func(func, AbiVariant::GuestImport); + } + gen.gen.import_object.push_str(&gen.src.ts); + assert!(gen.src.js.is_empty()); + } + + fn export_interface( + &mut self, + resolve: &Resolve, + name: &str, + id: InterfaceId, + files: &mut Files, + ) { self.generate_interface( name, - iface, + resolve, + id, "exports", "Exports", files, @@ -415,24 +449,40 @@ impl WorldGenerator for Js { uwriteln!(self.src.ts, "export const {name}: typeof {camel}Exports;"); } - fn export_default(&mut self, _name: &str, iface: &Interface, _files: &mut Files) { - let instantiation = self.opts.instantiation; - let mut gen = self.js_interface(iface); - for func in iface.functions.iter() { + fn export_funcs( + &mut self, + resolve: &Resolve, + _world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let mut gen = self.js_interface(resolve); + for (_, func) in funcs { gen.ts_func(func, AbiVariant::GuestExport); } - if instantiation { - gen.gen.export_object.push_str(&mem::take(&mut gen.src.ts)); - } - - // After the default interface has its function definitions - // inlined the rest of the types are generated here as well. - gen.types(); - gen.post_types(); - gen.gen.src.ts(&mem::take(&mut gen.src.ts)); + gen.gen.import_object.push_str(&gen.src.ts); + assert!(gen.src.js.is_empty()); } - fn finish(&mut self, world: &World, _files: &mut Files) { + // fn export_default(&mut self, _name: &str, iface: &Interface, _files: &mut Files) { + // let instantiation = self.opts.instantiation; + // let mut gen = self.js_interface(iface); + // for func in iface.functions.iter() { + // gen.ts_func(func, AbiVariant::GuestExport); + // } + // if instantiation { + // gen.gen.export_object.push_str(&mem::take(&mut gen.src.ts)); + // } + + // // After the default interface has its function definitions + // // inlined the rest of the types are generated here as well. + // gen.types(); + // gen.post_types(); + // gen.gen.src.ts(&mem::take(&mut gen.src.ts)); + // } + + fn finish(&mut self, resolve: &Resolve, id: WorldId, _files: &mut Files) { + let world = &resolve.worlds[id]; let camel = world.name.to_upper_camel_case(); // Generate a type definition for the import object to type-check @@ -461,19 +511,20 @@ impl Js { fn generate_interface( &mut self, name: &str, - iface: &Interface, + resolve: &Resolve, + id: InterfaceId, dir: &str, extra: &str, files: &mut Files, abi: AbiVariant, ) { let camel = name.to_upper_camel_case(); - let mut gen = self.js_interface(iface); - gen.types(); + let mut gen = self.js_interface(resolve); + gen.types(id); gen.post_types(); uwriteln!(gen.src.ts, "export namespace {camel} {{"); - for func in iface.functions.iter() { + for (_, func) in resolve.interfaces[id].functions.iter() { gen.ts_func(func, abi); } uwriteln!(gen.src.ts, "}}"); @@ -506,11 +557,11 @@ impl Js { impt.into() } - fn js_interface<'a>(&'a mut self, iface: &'a Interface) -> JsInterface<'a> { + fn js_interface<'a>(&'a mut self, resolve: &'a Resolve) -> JsInterface<'a> { JsInterface { src: Source::default(), gen: self, - iface, + resolve, needs_ty_option: false, needs_ty_result: false, } @@ -795,7 +846,7 @@ impl Js { name } - fn array_ty(&self, iface: &Interface, ty: &Type) -> Option<&'static str> { + fn array_ty(&self, resolve: &Resolve, ty: &Type) -> Option<&'static str> { match ty { Type::Bool => None, Type::U8 => Some("Uint8Array"), @@ -810,28 +861,28 @@ impl Js { Type::Float64 => Some("Float64Array"), Type::Char => None, Type::String => None, - Type::Id(id) => match &iface.types[*id].kind { - TypeDefKind::Type(t) => self.array_ty(iface, t), + Type::Id(id) => match &resolve.types[*id].kind { + TypeDefKind::Type(t) => self.array_ty(resolve, t), _ => None, }, } } /// Returns whether `null` is a valid value of type `ty` - fn maybe_null(&self, iface: &Interface, ty: &Type) -> bool { - self.as_nullable(iface, ty).is_some() + fn maybe_null(&self, resolve: &Resolve, ty: &Type) -> bool { + self.as_nullable(resolve, ty).is_some() } /// Tests whether `ty` can be represented with `null`, and if it can then /// the "other type" is returned. If `Some` is returned that means that `ty` /// is `null | `. If `None` is returned that means that `null` can't /// be used to represent `ty`. - fn as_nullable<'a>(&self, iface: &'a Interface, ty: &'a Type) -> Option<&'a Type> { + fn as_nullable<'a>(&self, resolve: &'a Resolve, ty: &'a Type) -> Option<&'a Type> { let id = match ty { Type::Id(id) => *id, _ => return None, }; - match &iface.types[id].kind { + match &resolve.types[id].kind { // If `ty` points to an `option`, then `ty` can be represented // with `null` if `t` itself can't be represented with null. For // example `option>` can't be represented with `null` @@ -848,13 +899,13 @@ impl Js { // It's doubtful anyone would actually rely on that though due to // how confusing it is. TypeDefKind::Option(t) => { - if !self.maybe_null(iface, t) { + if !self.maybe_null(resolve, t) { Some(t) } else { None } } - TypeDefKind::Type(t) => self.as_nullable(iface, t), + TypeDefKind::Type(t) => self.as_nullable(resolve, t), _ => None, } } @@ -868,7 +919,9 @@ struct Instantiator<'a> { gen: &'a mut Js, modules: &'a PrimaryMap>, instances: PrimaryMap, - world: &'a World, + resolve: &'a Resolve, + world: WorldId, + sizes: SizeAlign, component: &'a Component, } @@ -897,11 +950,7 @@ impl Instantiator<'_> { self.src.js("return "); } - self.exports( - &self.component.exports, - 0, - self.world.default.as_ref().map(|i| (None, i)), - ); + self.exports(&self.component.exports); } fn instantiation_global_initializer(&mut self, init: &GlobalInitializer) { @@ -999,9 +1048,7 @@ impl Instantiator<'_> { uwriteln!(self.src.js, "let exports{iu32};"); uwriteln!( self.src.js_init, - " - ({{ exports: exports{iu32} }} = await {instantiate}(await module{}{imports}));\ - ", + "({{ exports: exports{iu32} }} = await {instantiate}(await module{}{imports}));", idx.as_u32() ); } @@ -1012,9 +1059,16 @@ impl Instantiator<'_> { // where instances export functions. let (import_index, path) = &self.component.imports[import.import]; let (import_name, _import_ty) = &self.component.import_types[*import_index]; - assert_eq!(path.len(), 1); - let iface = &self.world.imports[import_name.as_str()]; - let func = iface.functions.iter().find(|f| f.name == path[0]).unwrap(); + let func = match &self.resolve.worlds[self.world].imports[import_name.as_str()] { + WorldItem::Function(f) => { + assert_eq!(path.len(), 0); + f + } + WorldItem::Interface(i) => { + assert_eq!(path.len(), 1); + &self.resolve.interfaces[*i].functions[&path[0]] + } + }; let index = import.index.as_u32(); let callee = format!("lowering{index}Callee"); @@ -1045,7 +1099,8 @@ impl Instantiator<'_> { } uwrite!(self.src.js_init, "\nfunction lowering{index}"); - let nparams = iface + let nparams = self + .resolve .wasm_signature(AbiVariant::GuestImport, func) .params .len(); @@ -1054,7 +1109,6 @@ impl Instantiator<'_> { nparams, callee, &import.options, - iface, func, AbiVariant::GuestImport, ); @@ -1071,7 +1125,6 @@ impl Instantiator<'_> { nparams: usize, callee: String, opts: &CanonicalOptions, - iface: &Interface, func: &Function, abi: AbiVariant, ) { @@ -1110,12 +1163,10 @@ impl Instantiator<'_> { ); } - let mut sizes = SizeAlign::default(); - sizes.fill(iface); let mut f = FunctionBindgen { - sizes, + sizes: &self.sizes, gen: self.gen, - err: if func.results.throws(iface).is_some() { + err: if func.results.throws(self.resolve).is_some() { match abi { AbiVariant::GuestExport => ErrHandling::ThrowResultErr, AbiVariant::GuestImport => ErrHandling::ResultCatchHandler, @@ -1134,7 +1185,7 @@ impl Instantiator<'_> { encoding: opts.string_encoding, src: Source::default(), }; - iface.call( + self.resolve.call( abi, match abi { AbiVariant::GuestImport => LiftLower::LiftArgsLowerResults, @@ -1185,12 +1236,7 @@ impl Instantiator<'_> { } } - fn exports( - &mut self, - exports: &IndexMap, - depth: usize, - iface: Option<(Option<&str>, &Interface)>, - ) { + fn exports(&mut self, exports: &IndexMap) { if exports.is_empty() { if self.gen.opts.instantiation { self.src.js("{}"); @@ -1203,52 +1249,58 @@ impl Instantiator<'_> { } for (name, export) in exports { - let js_name = if self.gen.opts.instantiation { - name.clone() - } else { - // When generating direct ES-module exports namespace functions - // by their exported interface name, if applicable. - match iface { - Some((Some(iface_name), _)) => format!("{iface_name}-{name}"), - _ => name.clone(), - } - }; - let camel = js_name.to_lower_camel_case(); + // let js_name = if self.gen.opts.instantiation { + // name.clone() + // } else { + // // When generating direct ES-module exports namespace functions + // // by their exported interface name, if applicable. + // match iface { + // Some((Some(iface_name), _)) => format!("{iface_name}-{name}"), + // _ => name.clone(), + // } + // }; + // let camel = js_name.to_lower_camel_case(); + let item = &self.resolve.worlds[self.world].exports[name]; + let camel = name.to_lower_camel_case(); match export { Export::LiftedFunction { ty: _, func, options, } => { - assert!(depth < 2); - if self.gen.opts.instantiation { - uwrite!(self.src.js, "{camel}"); - } else { - uwrite!(self.src.js, "\nexport function {camel}"); - } - let callee = self.core_def(func); - let (_, iface) = iface.unwrap(); - let func = iface.functions.iter().find(|f| f.name == *name).unwrap(); - self.bindgen( - func.params.len(), - callee, - options, - iface, + self.export_bindgen( + name, + None, func, - AbiVariant::GuestExport, + options, + match item { + WorldItem::Function(f) => f, + WorldItem::Interface(_) => unreachable!(), + }, ); - if self.gen.opts.instantiation { - self.src.js(",\n"); - } else { - self.src.js("\n"); - } } Export::Instance(exports) => { + let id = match item { + WorldItem::Interface(id) => *id, + WorldItem::Function(_) => unreachable!(), + }; if self.gen.opts.instantiation { uwrite!(self.src.js, "{camel}: "); } - let iface = &self.world.exports[name.as_str()]; - self.exports(exports, depth + 1, Some((Some(name.as_str()), iface))); + for (func_name, export) in exports { + let (func, options) = match export { + Export::LiftedFunction { func, options, .. } => (func, options), + Export::Type(_) => continue, // ignored + _ => unreachable!(), + }; + self.export_bindgen( + func_name, + Some(name), + func, + options, + &self.resolve.interfaces[id].functions[func_name], + ); + } if self.gen.opts.instantiation { self.src.js(",\n"); } @@ -1265,6 +1317,39 @@ impl Instantiator<'_> { self.src.js("}"); } } + + fn export_bindgen( + &mut self, + name: &str, + instance_name: Option<&str>, + def: &CoreDef, + options: &CanonicalOptions, + func: &Function, + ) { + if self.gen.opts.instantiation { + uwrite!(self.src.js, "{}", name.to_lower_camel_case()); + } else { + // Namespace with the instance name when using ESM + let camel = match instance_name { + Some(instance) => format!("{instance}-{name}").to_lower_camel_case(), + None => name.to_lower_camel_case(), + }; + uwrite!(self.src.js, "\nexport function {camel}"); + } + let callee = self.core_def(def); + self.bindgen( + func.params.len(), + callee, + options, + func, + AbiVariant::GuestExport, + ); + if self.gen.opts.instantiation { + self.src.js(",\n"); + } else { + self.src.js("\n"); + } + } } #[derive(Copy, Clone)] @@ -1290,7 +1375,7 @@ impl<'a> JsInterface<'a> { } fn array_ty(&self, ty: &Type) -> Option<&'static str> { - self.gen.array_ty(self.iface, ty) + self.gen.array_ty(self.resolve, ty) } fn print_ty(&mut self, ty: &Type, mode: Mode) { @@ -1308,7 +1393,7 @@ impl<'a> JsInterface<'a> { Type::Char => self.src.ts("string"), Type::String => self.src.ts("string"), Type::Id(id) => { - let ty = &self.iface.types[*id]; + let ty = &self.resolve.types[*id]; if let Some(name) = &ty.name { return self.src.ts(&name.to_upper_camel_case()); } @@ -1342,6 +1427,7 @@ impl<'a> JsInterface<'a> { TypeDefKind::List(v) => self.print_list(v, mode), TypeDefKind::Future(_) => todo!("anonymous future"), TypeDefKind::Stream(_) => todo!("anonymous stream"), + TypeDefKind::Unknown => unreachable!(), } } } @@ -1409,7 +1495,7 @@ impl<'a> JsInterface<'a> { AbiVariant::GuestExport => Mode::Lift, AbiVariant::GuestImport => Mode::Lower, }; - if let Some((ok_ty, _)) = func.results.throws(self.iface) { + if let Some((ok_ty, _)) = func.results.throws(self.resolve) { self.print_optional_ty(ok_ty, result_mode); } else { match func.results.len() { @@ -1431,14 +1517,14 @@ impl<'a> JsInterface<'a> { } fn maybe_null(&self, ty: &Type) -> bool { - self.gen.maybe_null(self.iface, ty) + self.gen.maybe_null(self.resolve, ty) } fn as_nullable<'b>(&self, ty: &'b Type) -> Option<&'b Type> where 'a: 'b, { - self.gen.as_nullable(self.iface, ty) + self.gen.as_nullable(self.resolve, ty) } fn post_types(&mut self) { @@ -1454,8 +1540,8 @@ impl<'a> JsInterface<'a> { } impl<'a> InterfaceGenerator<'a> for JsInterface<'a> { - fn iface(&self) -> &'a Interface { - self.iface + fn resolve(&self) -> &'a Resolve { + self.resolve } fn type_record(&mut self, _id: TypeId, name: &str, record: &Record, docs: &Docs) { @@ -1646,7 +1732,7 @@ enum ErrHandling { struct FunctionBindgen<'a> { gen: &'a mut Js, - sizes: SizeAlign, + sizes: &'a SizeAlign, err: ErrHandling, tmp: usize, src: Source, @@ -1735,17 +1821,17 @@ impl Bindgen for FunctionBindgen<'_> { self.blocks.push((src.into(), mem::take(operands))); } - fn return_pointer(&mut self, _iface: &Interface, _size: usize, _align: usize) -> String { + fn return_pointer(&mut self, _size: usize, _align: usize) -> String { unimplemented!() } - fn is_list_canonical(&self, iface: &Interface, ty: &Type) -> bool { - self.gen.array_ty(iface, ty).is_some() + fn is_list_canonical(&self, resolve: &Resolve, ty: &Type) -> bool { + self.gen.array_ty(resolve, ty).is_some() } fn emit( &mut self, - iface: &Interface, + resolve: &Resolve, inst: &Instruction<'_>, operands: &mut Vec, results: &mut Vec, @@ -2242,7 +2328,7 @@ impl Bindgen for FunctionBindgen<'_> { uwriteln!(none, "variant{tmp}_{i} = {none_result};"); } - if self.gen.maybe_null(iface, payload) { + if self.gen.maybe_null(resolve, payload) { uwriteln!( self.src.js, "switch (variant{tmp}.tag) {{ @@ -2283,7 +2369,7 @@ impl Bindgen for FunctionBindgen<'_> { let tmp = self.tmp(); let operand = &operands[0]; - let (v_none, v_some) = if self.gen.maybe_null(iface, payload) { + let (v_none, v_some) = if self.gen.maybe_null(resolve, payload) { ( "{ tag: 'none' }", format!( @@ -2553,7 +2639,7 @@ impl Bindgen for FunctionBindgen<'_> { uwriteln!(self.src.js, "const ptr{tmp} = {};", operands[0]); uwriteln!(self.src.js, "const len{tmp} = {};", operands[1]); // TODO: this is the wrong endianness - let array_ty = self.gen.array_ty(iface, element).unwrap(); + let array_ty = self.gen.array_ty(resolve, element).unwrap(); uwriteln!( self.src.js, "const result{tmp} = new {array_ty}({memory}.buffer.slice(ptr{tmp}, ptr{tmp} + len{tmp} * {}));", diff --git a/crates/gen-host-js/tests/codegen.rs b/crates/gen-host-js/tests/codegen.rs index dcdb5be3d..f12fe641d 100644 --- a/crates/gen-host-js/tests/codegen.rs +++ b/crates/gen-host-js/tests/codegen.rs @@ -2,9 +2,9 @@ use std::path::Path; use std::process::Command; macro_rules! codegen_test { - ($name:ident $test:tt) => { + ($id:ident $name:tt $test:tt) => { #[test] - fn $name() { + fn $id() { drop(include_str!($test)); test_helpers::run_component_codegen_test( "js", From 55d5eebe17a2482d7d34ae9381150c42650cc0f8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:28:59 -0800 Subject: [PATCH 05/25] Fix build of wasi_snapshot_preview1 for this repo --- crates/wasi_snapshot_preview1/src/lib.rs | 11 ++++++++++- crates/wasi_snapshot_preview1/testwasi.wit | 8 -------- 2 files changed, 10 insertions(+), 9 deletions(-) delete mode 100644 crates/wasi_snapshot_preview1/testwasi.wit diff --git a/crates/wasi_snapshot_preview1/src/lib.rs b/crates/wasi_snapshot_preview1/src/lib.rs index 5a84cb811..fdc8d8db5 100644 --- a/crates/wasi_snapshot_preview1/src/lib.rs +++ b/crates/wasi_snapshot_preview1/src/lib.rs @@ -18,7 +18,16 @@ use std::arch::wasm32::unreachable; use wasi::*; -wit_bindgen_guest_rust::generate!("testwasi.wit"); +wit_bindgen_guest_rust::generate!({ + inline: " + default world testwasi { + import testwasi: interface { + log: func(bytes: list) + log-err: func(bytes: list) + } + } + ", +}); #[no_mangle] pub extern "C" fn environ_get(environ: *mut *mut u8, environ_buf: *mut u8) -> Errno { diff --git a/crates/wasi_snapshot_preview1/testwasi.wit b/crates/wasi_snapshot_preview1/testwasi.wit deleted file mode 100644 index a18da464b..000000000 --- a/crates/wasi_snapshot_preview1/testwasi.wit +++ /dev/null @@ -1,8 +0,0 @@ -interface testwasi { - log: func(bytes: list) - log-err: func(bytes: list) -} - -world testwasi { - import testwasi: testwasi -} From 5a9f62ad0a4dc5d083d7bdf498b94fd2b34b4ded Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:29:36 -0800 Subject: [PATCH 06/25] Get unions tests building again --- crates/gen-guest-rust/src/lib.rs | 13 ++++-- tests/runtime/unions/wasm.rs | 6 ++- tests/runtime/unions/world.wit | 70 +++----------------------------- 3 files changed, 18 insertions(+), 71 deletions(-) diff --git a/crates/gen-guest-rust/src/lib.rs b/crates/gen-guest-rust/src/lib.rs index 98b6cb0da..0229daa9e 100644 --- a/crates/gen-guest-rust/src/lib.rs +++ b/crates/gen-guest-rust/src/lib.rs @@ -467,11 +467,16 @@ impl InterfaceGenerator<'_> { // Finish out the macro-generated export implementation. macro_src.push_str(" {\n"); - uwrite!( - macro_src, - "{prefix}{module_name}::call_{name_snake}::<$t>(", - prefix = self.gen.opts.macro_call_prefix.as_deref().unwrap_or("") + let prefix = format!( + "{}{}", + self.gen.opts.macro_call_prefix.as_deref().unwrap_or(""), + match interface_name { + Some(_) => format!("{module_name}::"), + None => String::new(), + }, ); + + uwrite!(macro_src, "{prefix}call_{name_snake}::<$t>(",); for param in params.iter() { uwrite!(macro_src, "{param},"); } diff --git a/tests/runtime/unions/wasm.rs b/tests/runtime/unions/wasm.rs index e5b492f8b..a5bffba37 100644 --- a/tests/runtime/unions/wasm.rs +++ b/tests/runtime/unions/wasm.rs @@ -1,6 +1,6 @@ -wit_bindgen_guest_rust::generate!("../../tests/runtime/unions/world.wit"); +wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/unions/world.wit"); -use unions::*; +use exports::*; struct Component; @@ -161,7 +161,9 @@ impl Unions for Component { println!("There is power in a union!"); } +} +impl exports::Exports for Component { fn add_one_integer(num: AllIntegers) -> AllIntegers { match num { // Boolean diff --git a/tests/runtime/unions/world.wit b/tests/runtime/unions/world.wit index 1303d68b3..b79a0a538 100644 --- a/tests/runtime/unions/world.wit +++ b/tests/runtime/unions/world.wit @@ -1,4 +1,4 @@ -interface imports { +interface test { /// A union of all of the integral types union all-integers { /// Bool is equivalent to a 1 bit integer @@ -58,69 +58,9 @@ interface imports { identify-distinguishable-num: func(num: distinguishable-num) -> u8 } -interface exports { - test-imports: func() +default world unions { + import imports: self.test + export exports: self.test - /// A union of all of the integral types - union all-integers { - /// Bool is equivalent to a 1 bit integer - /// and is treated that way in some languages - bool, - u8, u16, u32, u64, - s8, s16, s32, s64 - } - union all-floats { - float32, float64 - } - union all-text { - char, string - } - - // Returns the same case as the input but with 1 added - add-one-integer: func(num: all-integers) -> all-integers - // Returns the same case as the input but with 1 added - add-one-float: func(num: all-floats) -> all-floats - // Returns the same case as the input but with the first character replaced - replace-first-char: func(text: all-text, letter: char) -> all-text - - // Returns the index of the case provided - identify-integer: func(num: all-integers) -> u8 - // Returns the index of the case provided - identify-float: func(num: all-floats) -> u8 - // Returns the index of the case provided - identify-text: func(text: all-text) -> u8 - - union duplicated-s32 { - /// The first s32 - s32, - /// The second s32 - s32, - /// The third s32 - s32 - } - - // Returns the same case as the input but with 1 added - add-one-duplicated: func(num: duplicated-s32) -> duplicated-s32 - - // Returns the index of the case provided - identify-duplicated: func(num: duplicated-s32) -> u8 - - /// A type containing numeric types that are distinct in most languages - union distinguishable-num { - /// A Floating Point Number - float64, - /// A Signed Integer - s64 - } - - // Returns the same case as the input but with 1 added - add-one-distinguishable-num: func(num: distinguishable-num) -> distinguishable-num - - // Returns the index of the case provided - identify-distinguishable-num: func(num: distinguishable-num) -> u8 -} - -world unions { - import imports: imports - default export exports + export test-imports: func() } From 61e460a3a548dd516fb11f627e7351f37b307103 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:33:00 -0800 Subject: [PATCH 07/25] Get the flavorful test compiling again --- tests/runtime/flavorful/wasm.rs | 14 ++++++++-- tests/runtime/flavorful/world.wit | 43 ++++--------------------------- tests/runtime/unions/wasm.rs | 2 +- 3 files changed, 18 insertions(+), 41 deletions(-) diff --git a/tests/runtime/flavorful/wasm.rs b/tests/runtime/flavorful/wasm.rs index 627c2f372..6fc6e6797 100644 --- a/tests/runtime/flavorful/wasm.rs +++ b/tests/runtime/flavorful/wasm.rs @@ -1,6 +1,6 @@ -wit_bindgen_guest_rust::generate!("../../tests/runtime/flavorful/world.wit"); +wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/flavorful"); -use flavorful::*; +use exports::*; struct Component; @@ -59,7 +59,9 @@ impl Flavorful for Component { assert_eq!(b, [Err(()), Ok(())]); assert_eq!(c, [MyErrno::A, MyErrno::B]); } +} +impl exports::Exports for Component { fn f_list_in_record1(ty: ListInRecord1) { assert_eq!(ty.a, "list_in_record1"); } @@ -116,4 +118,12 @@ impl Flavorful for Component { assert_eq!(b[0], "typedef2"); (b"typedef3".to_vec(), vec!["typedef4".to_string()]) } + + fn list_of_variants( + a: Vec, + b: Vec>, + c: Vec, + ) -> (Vec, Vec>, Vec) { + (a, b, c) + } } diff --git a/tests/runtime/flavorful/world.wit b/tests/runtime/flavorful/world.wit index 114808cb1..ed2e86c4c 100644 --- a/tests/runtime/flavorful/world.wit +++ b/tests/runtime/flavorful/world.wit @@ -1,4 +1,4 @@ -interface imports { +interface test { record list-in-record1 { a: string } record list-in-record2 { a: string } record list-in-record3 { a: string } @@ -33,42 +33,9 @@ interface imports { -> (a: list, b: list, c: list) } -interface exports { - test-imports: func() +default world flavorful { + import imports: self.test + export exports: self.test - record list-in-record1 { a: string } - record list-in-record2 { a: string } - record list-in-record3 { a: string } - record list-in-record4 { a: string } - type list-in-alias = list-in-record4 - - f-list-in-record1: func(a: list-in-record1) - f-list-in-record2: func() -> list-in-record2 - f-list-in-record3: func(a: list-in-record3) -> list-in-record3 - f-list-in-record4: func(a: list-in-alias) -> list-in-alias - - type list-in-variant1-v1 = option - type list-in-variant1-v2 = result<_, string> - union list-in-variant1-v3 { string, float32 } - f-list-in-variant1: func(a: list-in-variant1-v1, b: list-in-variant1-v2, c: list-in-variant1-v3) - - type list-in-variant2 = option - f-list-in-variant2: func() -> list-in-variant2 - - type list-in-variant3 = option - f-list-in-variant3: func(a: list-in-variant3) -> list-in-variant3 - - enum my-errno { success, a, b } - errno-result: func() -> result<_, my-errno> - - type list-typedef = string - type list-typedef2 = list - type list-typedef3 = list - list-typedefs: func(a: list-typedef, c: list-typedef3) - -> (a: list-typedef2, b: list-typedef3) -} - -world flavorful { - import imports: imports - default export exports + export test-imports: func() } diff --git a/tests/runtime/unions/wasm.rs b/tests/runtime/unions/wasm.rs index a5bffba37..4e0c29760 100644 --- a/tests/runtime/unions/wasm.rs +++ b/tests/runtime/unions/wasm.rs @@ -1,4 +1,4 @@ -wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/unions/world.wit"); +wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/unions"); use exports::*; From 20215ae5471219df248c91500ce542d16ef3d9f6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:34:51 -0800 Subject: [PATCH 08/25] Get the records test compiling again --- tests/runtime/records/wasm.rs | 13 ++++++-- tests/runtime/records/world.wit | 58 +++------------------------------ 2 files changed, 15 insertions(+), 56 deletions(-) diff --git a/tests/runtime/records/wasm.rs b/tests/runtime/records/wasm.rs index a8c4df0ee..600af523f 100644 --- a/tests/runtime/records/wasm.rs +++ b/tests/runtime/records/wasm.rs @@ -1,6 +1,6 @@ -wit_bindgen_guest_rust::generate!("../../tests/runtime/records/world.wit"); +wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/records"); -use records::*; +use exports::*; struct Component; @@ -45,7 +45,9 @@ impl Records for Component { assert_eq!(tuple0(()), ()); assert_eq!(tuple1((1,)), (1,)); } +} +impl Exports for Component { fn multiple_results() -> (u8, u16) { (100, 200) } @@ -62,7 +64,12 @@ impl Records for Component { a } - fn roundtrip_flags3(a: F8, b: F16, c: F32, d: F64) -> (F8, F16, F32, F64) { + fn roundtrip_flags3( + a: Flag8, + b: Flag16, + c: Flag32, + d: Flag64, + ) -> (Flag8, Flag16, Flag32, Flag64) { (a, b, c, d) } diff --git a/tests/runtime/records/world.wit b/tests/runtime/records/world.wit index 2faed1c59..31933ad29 100644 --- a/tests/runtime/records/world.wit +++ b/tests/runtime/records/world.wit @@ -1,4 +1,4 @@ -interface imports { +interface test { multiple-results: func() -> (a: u8, b: u16) swap-tuple: func(a: tuple) -> tuple @@ -46,57 +46,9 @@ interface imports { tuple1: func(a: tuple) -> tuple } -interface exports { - test-imports: func() +default world records { + import imports: self.test + export exports: self.test - multiple-results: func() -> (a: u8, b: u16) - - swap-tuple: func(a: tuple) -> tuple - - flags f1 { a, b } - roundtrip-flags1: func(a: f1) -> f1 - - flags f2 { c, d, e } - roundtrip-flags2: func(a: f2) -> f2 - - flags f8 { - b0, b1, b2, b3, b4, b5, b6, b7, - } - - flags f16 { - b0, b1, b2, b3, b4, b5, b6, b7, - b8, b9, b10, b11, b12, b13, b14, b15, - } - - flags f32 { - b0, b1, b2, b3, b4, b5, b6, b7, - b8, b9, b10, b11, b12, b13, b14, b15, - b16, b17, b18, b19, b20, b21, b22, b23, - b24, b25, b26, b27, b28, b29, b30, b31, - } - - flags f64 { - b0, b1, b2, b3, b4, b5, b6, b7, - b8, b9, b10, b11, b12, b13, b14, b15, - b16, b17, b18, b19, b20, b21, b22, b23, - b24, b25, b26, b27, b28, b29, b30, b31, - b32, b33, b34, b35, b36, b37, b38, b39, - b40, b41, b42, b43, b44, b45, b46, b47, - b48, b49, b50, b51, b52, b53, b54, b55, - b56, b57, b58, b59, b60, b61, b62, b63, - } - - roundtrip-flags3: func(a: f8, b: f16, c: f32, d: f64) -> - (f8: f8, f16: f16, f32: f32, f64: f64) - - record r1 { a: u8, b: f1 } - roundtrip-record1: func(a: r1) -> r1 - - tuple0: func(a: tuple<>) -> tuple<> - tuple1: func(a: tuple) -> tuple -} - -world records { - import imports: imports - default export exports + export test-imports: func() } From 1a1df403e159ad633def05b85edd25bff6c00473 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:36:05 -0800 Subject: [PATCH 09/25] Get the many arguments test compiling again --- tests/runtime/many_arguments/wasm.rs | 4 +-- tests/runtime/many_arguments/world.wit | 50 ++++++++++++-------------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/tests/runtime/many_arguments/wasm.rs b/tests/runtime/many_arguments/wasm.rs index b23c01245..7ba59bab6 100644 --- a/tests/runtime/many_arguments/wasm.rs +++ b/tests/runtime/many_arguments/wasm.rs @@ -1,10 +1,10 @@ -wit_bindgen_guest_rust::generate!("../../tests/runtime/many_arguments/world.wit"); +wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/many_arguments"); struct Component; export_many_arguments!(Component); -impl many_arguments::ManyArguments for Component { +impl ManyArguments for Component { fn many_arguments( a1: u64, a2: u64, diff --git a/tests/runtime/many_arguments/world.wit b/tests/runtime/many_arguments/world.wit index 3da66d54e..74a3c6afc 100644 --- a/tests/runtime/many_arguments/world.wit +++ b/tests/runtime/many_arguments/world.wit @@ -1,5 +1,25 @@ -interface imports { - many-arguments: func( +default world many-arguments { + import imports: interface { + many-arguments: func( + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + ) + } + export many-arguments: func( a1: u64, a2: u64, a3: u64, @@ -18,29 +38,3 @@ interface imports { a16: u64, ) } - -interface exports { - many-arguments: func( - a1: u64, - a2: u64, - a3: u64, - a4: u64, - a5: u64, - a6: u64, - a7: u64, - a8: u64, - a9: u64, - a10: u64, - a11: u64, - a12: u64, - a13: u64, - a14: u64, - a15: u64, - a16: u64, - ) -} - -world many-arguments { - import imports: imports - default export exports -} From 9068c275f1f3e0fd9297611954abfe829bfb9b1b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:36:56 -0800 Subject: [PATCH 10/25] Get the smoke test compiling again --- tests/runtime/smoke/wasm.rs | 4 ++-- tests/runtime/smoke/world.wit | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/runtime/smoke/wasm.rs b/tests/runtime/smoke/wasm.rs index e8d9b7577..1d6c4602b 100644 --- a/tests/runtime/smoke/wasm.rs +++ b/tests/runtime/smoke/wasm.rs @@ -1,10 +1,10 @@ -wit_bindgen_guest_rust::generate!("../../tests/runtime/smoke/world.wit"); +wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/smoke"); struct Exports; export_smoke!(Exports); -impl smoke::Smoke for Exports { +impl Smoke for Exports { fn thunk() { imports::thunk(); } diff --git a/tests/runtime/smoke/world.wit b/tests/runtime/smoke/world.wit index 43f79fdfd..2329de42f 100644 --- a/tests/runtime/smoke/world.wit +++ b/tests/runtime/smoke/world.wit @@ -2,11 +2,8 @@ interface imports { thunk: func() } -interface exports { - thunk: func() -} +default world smoke { + import imports: self.imports -world smoke { - import imports: imports - default export exports + export thunk: func() } From 6314b8f1f2bb6cbd3b52751b2a6ffacf5f9521ef Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:44:15 -0800 Subject: [PATCH 11/25] Get numbers test compiling again --- tests/runtime/numbers/wasm.rs | 8 +++++--- tests/runtime/numbers/world.wit | 27 +++++---------------------- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/tests/runtime/numbers/wasm.rs b/tests/runtime/numbers/wasm.rs index 2453b6041..e86d7edbc 100644 --- a/tests/runtime/numbers/wasm.rs +++ b/tests/runtime/numbers/wasm.rs @@ -1,6 +1,5 @@ -wit_bindgen_guest_rust::generate!("../../tests/runtime/numbers/world.wit"); +wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/numbers"); -use imports::*; use std::sync::atomic::{AtomicU32, Ordering::SeqCst}; static SCALAR: AtomicU32 = AtomicU32::new(0); @@ -9,8 +8,9 @@ struct Component; export_numbers!(Component); -impl numbers::Numbers for Component { +impl Numbers for Component { fn test_imports() { + use imports::*; assert_eq!(roundtrip_u8(1), 1); assert_eq!(roundtrip_u8(u8::min_value()), u8::min_value()); assert_eq!(roundtrip_u8(u8::max_value()), u8::max_value()); @@ -62,7 +62,9 @@ impl numbers::Numbers for Component { set_scalar(4); assert_eq!(get_scalar(), 4); } +} +impl exports::Exports for Component { fn roundtrip_u8(a: u8) -> u8 { a } diff --git a/tests/runtime/numbers/world.wit b/tests/runtime/numbers/world.wit index b8529bf91..7c51cbc89 100644 --- a/tests/runtime/numbers/world.wit +++ b/tests/runtime/numbers/world.wit @@ -1,4 +1,4 @@ -interface imports { +interface test { roundtrip-u8: func(a: u8) -> u8 roundtrip-s8: func(a: s8) -> s8 roundtrip-u16: func(a: u16) -> u16 @@ -15,26 +15,9 @@ interface imports { get-scalar: func() -> u32 } -interface exports { - test-imports: func() +default world numbers { + import imports: self.test + export exports: self.test - roundtrip-u8: func(a: u8) -> u8 - roundtrip-s8: func(a: s8) -> s8 - roundtrip-u16: func(a: u16) -> u16 - roundtrip-s16: func(a: s16) -> s16 - roundtrip-u32: func(a: u32) -> u32 - roundtrip-s32: func(a: s32) -> s32 - roundtrip-u64: func(a: u64) -> u64 - roundtrip-s64: func(a: s64) -> s64 - roundtrip-float32: func(a: float32) -> float32 - roundtrip-float64: func(a: float64) -> float64 - roundtrip-char: func(a: char) -> char - - set-scalar: func(a: u32) - get-scalar: func() -> u32 -} - -world numbers { - import imports: imports - default export exports + export test-imports: func() } From 83fd245c6a521ba1f895d3de4a6d4a9fadccb63b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:47:15 -0800 Subject: [PATCH 12/25] Get list test compiling again --- tests/runtime/lists/wasm.rs | 26 ++++++++++++++++++++++++-- tests/runtime/lists/world.wit | 30 ++++++------------------------ 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/tests/runtime/lists/wasm.rs b/tests/runtime/lists/wasm.rs index 58c1f8d43..a00b8b74c 100644 --- a/tests/runtime/lists/wasm.rs +++ b/tests/runtime/lists/wasm.rs @@ -1,10 +1,10 @@ -wit_bindgen_guest_rust::generate!("../../tests/runtime/lists/world.wit"); +wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/lists"); struct Component; export_lists!(Component); -impl lists::Lists for Component { +impl Lists for Component { fn allocated_bytes() -> u32 { test_rust_wasm::get() as u32 } @@ -63,7 +63,9 @@ impl lists::Lists for Component { ), ); } +} +impl exports::Exports for Component { fn empty_list_param(a: Vec) { assert!(a.is_empty()); } @@ -121,4 +123,24 @@ impl lists::Lists for Component { fn string_roundtrip(x: String) -> String { x.clone() } + + fn list_minmax8(a: Vec, b: Vec) -> (Vec, Vec) { + (a, b) + } + + fn list_minmax16(a: Vec, b: Vec) -> (Vec, Vec) { + (a, b) + } + + fn list_minmax32(a: Vec, b: Vec) -> (Vec, Vec) { + (a, b) + } + + fn list_minmax64(a: Vec, b: Vec) -> (Vec, Vec) { + (a, b) + } + + fn list_minmax_float(a: Vec, b: Vec) -> (Vec, Vec) { + (a, b) + } } diff --git a/tests/runtime/lists/world.wit b/tests/runtime/lists/world.wit index 5cb79ff60..986631eb6 100644 --- a/tests/runtime/lists/world.wit +++ b/tests/runtime/lists/world.wit @@ -1,4 +1,4 @@ -interface imports { +interface test { empty-list-param: func(a: list) empty-string-param: func(a: string) empty-list-result: func() -> list @@ -24,28 +24,10 @@ interface imports { string-roundtrip: func(a: string) -> string } -interface exports { - test-imports: func() - allocated-bytes: func() -> u32 +default world lists { + import imports: self.test + export exports: self.test - empty-list-param: func(a: list) - empty-string-param: func(a: string) - empty-list-result: func() -> list - empty-string-result: func() -> string - - list-param: func(a: list) - list-param2: func(a: string) - list-param3: func(a: list) - list-param4: func(a: list>) - list-result: func() -> list - list-result2: func() -> string - list-result3: func() -> list - - list-roundtrip: func(a: list) -> list - string-roundtrip: func(a: string) -> string -} - -world lists { - import imports: imports - default export exports + export test-imports: func() + export allocated-bytes: func() -> u32 } From 3c6ec5445976104e9ae199c69ad33650e794bccd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:49:20 -0800 Subject: [PATCH 13/25] Get variants test compiling again --- tests/runtime/variants/wasm.rs | 10 ++++++-- tests/runtime/variants/world.wit | 41 ++++---------------------------- 2 files changed, 13 insertions(+), 38 deletions(-) diff --git a/tests/runtime/variants/wasm.rs b/tests/runtime/variants/wasm.rs index afa6cf6ba..eaf4627ad 100644 --- a/tests/runtime/variants/wasm.rs +++ b/tests/runtime/variants/wasm.rs @@ -1,6 +1,6 @@ -wit_bindgen_guest_rust::generate!("../../tests/runtime/variants/world.wit"); +wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/variants"); -use variants::*; +use exports::*; struct Component; @@ -66,7 +66,9 @@ impl Variants for Component { (false, Err(()), MyErrno::A) ); } +} +impl Exports for Component { fn roundtrip_option(a: Option) -> Option { a.map(|x| x as u8) } @@ -96,4 +98,8 @@ impl Variants for Component { } fn variant_typedefs(_: Option, _: bool, _: Result) {} + + fn variant_enums(a: bool, b: Result<(), ()>, c: MyErrno) -> (bool, Result<(), ()>, MyErrno) { + (a, b, c) + } } diff --git a/tests/runtime/variants/world.wit b/tests/runtime/variants/world.wit index 9a5774558..0d907cb97 100644 --- a/tests/runtime/variants/world.wit +++ b/tests/runtime/variants/world.wit @@ -1,4 +1,4 @@ -interface imports { +interface test { roundtrip-option: func(a: option) -> option roundtrip-result: func(a: result) -> result @@ -32,40 +32,9 @@ interface imports { variant-enums: func(a: bool, b: result, c: my-errno) -> tuple } -interface exports { - test-imports: func() +default world variants { + import imports: self.test + export exports: self.test - roundtrip-option: func(a: option) -> option - roundtrip-result: func(a: result) -> result - - enum e1 { a, b } - roundtrip-enum: func(a: e1) -> e1 - - invert-bool: func(a: bool) -> bool - - variant c1 { a(s32), b(s64) } - variant c2 { a(s32), b(float32) } - variant c3 { a(s32), b(float64) } - variant c4 { a(s64), b(float32) } - variant c5 { a(s64), b(float64) } - variant c6 { a(float32), b(float64) } - type casts = tuple - variant-casts: func(a: casts) -> casts - - variant z1 { a(s32), b } - variant z2 { a(s64), b } - variant z3 { a(float32), b } - variant z4 { a(float64), b } - type zeros = tuple - variant-zeros: func(a: zeros) -> zeros - - type option-typedef = option - type bool-typedef = bool - type result-typedef = result - variant-typedefs: func(a: option-typedef, b: bool-typedef, c: result-typedef) -} - -world variants { - import imports: imports - default export exports + export test-imports: func() } From ce1e007838a727d935f04f1e34bda3920de739ed Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:50:38 -0800 Subject: [PATCH 14/25] Get results test compiling again --- tests/runtime/results/wasm.rs | 26 +++++++++++++------------- tests/runtime/results/world.wit | 24 ++++-------------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/tests/runtime/results/wasm.rs b/tests/runtime/results/wasm.rs index 4fb85a289..ea36cd21b 100644 --- a/tests/runtime/results/wasm.rs +++ b/tests/runtime/results/wasm.rs @@ -1,36 +1,36 @@ -wit_bindgen_guest_rust::generate!("../../tests/runtime/results/world.wit"); +wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/results"); struct Exports; export_results!(Exports); -impl results::Results for Exports { +impl exports::Exports for Exports { fn string_error(a: f32) -> Result { imports::string_error(a) } - fn enum_error(a: f64) -> Result { + fn enum_error(a: f64) -> Result { match imports::enum_error(a) { Ok(b) => Ok(b), - Err(imports::E::A) => Err(results::E::A), - Err(imports::E::B) => Err(results::E::B), - Err(imports::E::C) => Err(results::E::C), + Err(imports::E::A) => Err(exports::E::A), + Err(imports::E::B) => Err(exports::E::B), + Err(imports::E::C) => Err(exports::E::C), } } - fn record_error(a: f64) -> Result { + fn record_error(a: f64) -> Result { match imports::record_error(a) { Ok(b) => Ok(b), - Err(imports::E2 { line, column }) => Err(results::E2 { line, column }), + Err(imports::E2 { line, column }) => Err(exports::E2 { line, column }), } } - fn variant_error(a: f64) -> Result { + fn variant_error(a: f64) -> Result { match imports::variant_error(a) { Ok(b) => Ok(b), - Err(imports::E3::E1(imports::E::A)) => Err(results::E3::E1(results::E::A)), - Err(imports::E3::E1(imports::E::B)) => Err(results::E3::E1(results::E::B)), - Err(imports::E3::E1(imports::E::C)) => Err(results::E3::E1(results::E::C)), + Err(imports::E3::E1(imports::E::A)) => Err(exports::E3::E1(exports::E::A)), + Err(imports::E3::E1(imports::E::B)) => Err(exports::E3::E1(exports::E::B)), + Err(imports::E3::E1(imports::E::C)) => Err(exports::E3::E1(exports::E::C)), Err(imports::E3::E2(imports::E2 { line, column })) => { - Err(results::E3::E2(results::E2 { line, column })) + Err(exports::E3::E2(exports::E2 { line, column })) } } } diff --git a/tests/runtime/results/world.wit b/tests/runtime/results/world.wit index cea1540bf..0c6e5d962 100644 --- a/tests/runtime/results/world.wit +++ b/tests/runtime/results/world.wit @@ -1,4 +1,4 @@ -interface imports { +interface test { string-error: func(a: float32) -> result enum e { a, b, c } @@ -14,23 +14,7 @@ interface imports { empty-error: func(a: u32) -> result } -interface exports { - string-error: func(a: float32) -> result - - enum e { a, b, c } - enum-error: func(a: float64) -> result - - record e2 { line: u32, column: u32 } - record-error: func(a: float64) -> result - - - variant e3 { e1(e), e2(e2) } - variant-error: func(a: float64) -> result - - empty-error: func(a: u32) -> result -} - -world results { - import imports: imports - default export exports +default world results { + import imports: self.test + export exports: self.test } From bd0fbe8fec269bba47be250ea4b7220cc5c6ef86 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 08:51:45 -0800 Subject: [PATCH 15/25] Get invalid test compiling again --- tests/runtime/invalid/wasm.rs | 4 ++-- tests/runtime/invalid/world.wit | 42 +++++++++++++++------------------ 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/tests/runtime/invalid/wasm.rs b/tests/runtime/invalid/wasm.rs index 34b58f37c..d919da908 100644 --- a/tests/runtime/invalid/wasm.rs +++ b/tests/runtime/invalid/wasm.rs @@ -1,4 +1,4 @@ -wit_bindgen_guest_rust::generate!("../../tests/runtime/invalid/world.wit"); +wit_bindgen_guest_rust::generate!("world" in "../../tests/runtime/invalid"); #[link(wasm_import_module = "imports")] extern "C" { @@ -42,7 +42,7 @@ struct Exports; export_invalid!(Exports); -impl invalid::Invalid for Exports { +impl Invalid for Exports { fn invalid_bool() { unsafe { let b = roundtrip_bool(2); diff --git a/tests/runtime/invalid/world.wit b/tests/runtime/invalid/world.wit index eb1eb5157..58d583b9d 100644 --- a/tests/runtime/invalid/world.wit +++ b/tests/runtime/invalid/world.wit @@ -43,28 +43,24 @@ interface imports { unaligned10: func(a: list>) } -interface exports { - invalid-u8: func() - invalid-s8: func() - invalid-u16: func() - invalid-s16: func() - invalid-char: func() - invalid-bool: func() - invalid-enum: func() +default world invalid { + import imports: self.imports - unaligned1: func() - unaligned2: func() - unaligned3: func() - unaligned4: func() - unaligned5: func() - unaligned6: func() - unaligned7: func() - unaligned8: func() - unaligned9: func() - unaligned10: func() -} - -world invalid { - import imports: imports - default export exports + export invalid-u8: func() + export invalid-s8: func() + export invalid-u16: func() + export invalid-s16: func() + export invalid-char: func() + export invalid-bool: func() + export invalid-enum: func() + export unaligned1: func() + export unaligned2: func() + export unaligned3: func() + export unaligned4: func() + export unaligned5: func() + export unaligned6: func() + export unaligned7: func() + export unaligned8: func() + export unaligned9: func() + export unaligned10: func() } From ca2e2783b698f0598f044b213a0335fb535dab17 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 10:46:45 -0800 Subject: [PATCH 16/25] Get all runtime JS tests passing Change a few bits and pieces of the JS host generator as necessary and temporarily disable other guest generators. --- .github/workflows/main.yml | 9 +-- crates/gen-host-js/Cargo.toml | 6 +- crates/gen-host-js/src/lib.rs | 68 ++++++------------- crates/test-helpers/Cargo.toml | 4 +- crates/test-helpers/runtime-macro/src/lib.rs | 2 +- tests/runtime/flavorful/host.ts | 18 ++--- tests/runtime/lists/host.ts | 30 ++++----- tests/runtime/numbers/host.ts | 70 ++++++++++---------- tests/runtime/records/host.ts | 26 ++++---- tests/runtime/variants/host.ts | 32 ++++----- 10 files changed, 117 insertions(+), 148 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3dc105569..bca1e82f9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,7 +22,6 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - mode: [debug, release] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 @@ -56,12 +55,8 @@ jobs: with: java-version: '18' distribution: 'adopt' - - if: matrix.mode == 'release' - name: Test release build - run: cargo test --workspace --release - - if: matrix.mode != 'release' - name: Test debug build - run: cargo test --workspace + - run: cargo test --workspace + - run: cargo test --p wit-bindgen-gen-host-js --test runtime --features runtime-tests rustfmt: name: Rustfmt diff --git a/crates/gen-host-js/Cargo.toml b/crates/gen-host-js/Cargo.toml index 58be287cd..d907ce4e9 100644 --- a/crates/gen-host-js/Cargo.toml +++ b/crates/gen-host-js/Cargo.toml @@ -23,4 +23,8 @@ test-helpers = { path = '../test-helpers' } [[test]] name = "runtime" -required-features = ["clap"] +required-features = ["runtime-tests"] + +[features] +runtime-tests = ["clap", 'test-helpers/runtime-macro'] +clap = ["dep:clap"] diff --git a/crates/gen-host-js/src/lib.rs b/crates/gen-host-js/src/lib.rs index 2703a14f7..363d611cb 100644 --- a/crates/gen-host-js/src/lib.rs +++ b/crates/gen-host-js/src/lib.rs @@ -446,7 +446,10 @@ impl WorldGenerator for Js { AbiVariant::GuestExport, ); let camel = name.to_upper_camel_case(); - uwriteln!(self.src.ts, "export const {name}: typeof {camel}Exports;"); + uwriteln!( + self.export_object, + "export const {name}: typeof {camel}Exports;" + ); } fn export_funcs( @@ -460,27 +463,10 @@ impl WorldGenerator for Js { for (_, func) in funcs { gen.ts_func(func, AbiVariant::GuestExport); } - gen.gen.import_object.push_str(&gen.src.ts); + gen.gen.export_object.push_str(&gen.src.ts); assert!(gen.src.js.is_empty()); } - // fn export_default(&mut self, _name: &str, iface: &Interface, _files: &mut Files) { - // let instantiation = self.opts.instantiation; - // let mut gen = self.js_interface(iface); - // for func in iface.functions.iter() { - // gen.ts_func(func, AbiVariant::GuestExport); - // } - // if instantiation { - // gen.gen.export_object.push_str(&mem::take(&mut gen.src.ts)); - // } - - // // After the default interface has its function definitions - // // inlined the rest of the types are generated here as well. - // gen.types(); - // gen.post_types(); - // gen.gen.src.ts(&mem::take(&mut gen.src.ts)); - // } - fn finish(&mut self, resolve: &Resolve, id: WorldId, _files: &mut Files) { let world = &resolve.worlds[id]; let camel = world.name.to_upper_camel_case(); @@ -503,6 +489,8 @@ impl WorldGenerator for Js { uwriteln!(self.src.ts, "export namespace {camel} {{",); self.src.ts(&self.export_object); uwriteln!(self.src.ts, "}}"); + } else { + self.src.ts(&self.export_object); } } } @@ -536,15 +524,7 @@ impl Js { uwriteln!( self.src.ts, - "{} {{ {camel} as {camel}{extra} }} from './{dir}/{name}';", - // In instance mode, we have no way to assert the imported types - // in the ambient declaration file. Instead we just export the - // import namespace types for users to use. - if self.opts.instantiation { - "import" - } else { - "export" - } + "import {{ {camel} as {camel}{extra} }} from './{dir}/{name}';", ); } @@ -1249,17 +1229,6 @@ impl Instantiator<'_> { } for (name, export) in exports { - // let js_name = if self.gen.opts.instantiation { - // name.clone() - // } else { - // // When generating direct ES-module exports namespace functions - // // by their exported interface name, if applicable. - // match iface { - // Some((Some(iface_name), _)) => format!("{iface_name}-{name}"), - // _ => name.clone(), - // } - // }; - // let camel = js_name.to_lower_camel_case(); let item = &self.resolve.worlds[self.world].exports[name]; let camel = name.to_lower_camel_case(); match export { @@ -1285,7 +1254,9 @@ impl Instantiator<'_> { WorldItem::Function(_) => unreachable!(), }; if self.gen.opts.instantiation { - uwrite!(self.src.js, "{camel}: "); + uwriteln!(self.src.js, "{camel}: {{"); + } else { + uwriteln!(self.src.js, "export const {camel} = {{"); } for (func_name, export) in exports { let (func, options) = match export { @@ -1301,8 +1272,11 @@ impl Instantiator<'_> { &self.resolve.interfaces[id].functions[func_name], ); } + self.src.js("\n}"); if self.gen.opts.instantiation { self.src.js(",\n"); + } else { + self.src.js(";\n"); } } @@ -1326,15 +1300,11 @@ impl Instantiator<'_> { options: &CanonicalOptions, func: &Function, ) { - if self.gen.opts.instantiation { - uwrite!(self.src.js, "{}", name.to_lower_camel_case()); + let name = name.to_lower_camel_case(); + if self.gen.opts.instantiation || instance_name.is_some() { + self.src.js.push_str(&name); } else { - // Namespace with the instance name when using ESM - let camel = match instance_name { - Some(instance) => format!("{instance}-{name}").to_lower_camel_case(), - None => name.to_lower_camel_case(), - }; - uwrite!(self.src.js, "\nexport function {camel}"); + uwrite!(self.src.js, "\nexport function {name}"); } let callee = self.core_def(def); self.bindgen( @@ -1344,7 +1314,7 @@ impl Instantiator<'_> { func, AbiVariant::GuestExport, ); - if self.gen.opts.instantiation { + if self.gen.opts.instantiation || instance_name.is_some() { self.src.js(",\n"); } else { self.src.js("\n"); diff --git a/crates/test-helpers/Cargo.toml b/crates/test-helpers/Cargo.toml index d53e47418..aa031b425 100644 --- a/crates/test-helpers/Cargo.toml +++ b/crates/test-helpers/Cargo.toml @@ -20,5 +20,5 @@ wasm-encoder = { workspace = true } [features] default = ['guest-rust', 'guest-c', 'guest-teavm-java'] guest-rust = ['runtime-macro?/guest-rust'] -guest-c = ['runtime-macro?/guest-c'] -guest-teavm-java = ['runtime-macro?/guest-teavm-java'] +guest-c = []#'runtime-macro?/guest-c'] +guest-teavm-java = []#'runtime-macro?/guest-teavm-java'] diff --git a/crates/test-helpers/runtime-macro/src/lib.rs b/crates/test-helpers/runtime-macro/src/lib.rs index 965e5b1c6..b9bfe75f9 100644 --- a/crates/test-helpers/runtime-macro/src/lib.rs +++ b/crates/test-helpers/runtime-macro/src/lib.rs @@ -1,4 +1,4 @@ -use proc_macro::{TokenStream, TokenTree}; +use proc_macro::TokenStream; use std::env; include!(concat!(env!("OUT_DIR"), "/wasms.rs")); diff --git a/tests/runtime/flavorful/host.ts b/tests/runtime/flavorful/host.ts index ff17d831f..e3fc65317 100644 --- a/tests/runtime/flavorful/host.ts +++ b/tests/runtime/flavorful/host.ts @@ -51,26 +51,26 @@ export async function run () { const wasm = await import('./flavorful.js'); wasm.testImports(); - wasm.fListInRecord1({ a: "list_in_record1" }); - assert.deepStrictEqual(wasm.fListInRecord2(), { a: "list_in_record2" }); + wasm.exports.fListInRecord1({ a: "list_in_record1" }); + assert.deepStrictEqual(wasm.exports.fListInRecord2(), { a: "list_in_record2" }); assert.deepStrictEqual( - wasm.fListInRecord3({ a: "list_in_record3 input" }), + wasm.exports.fListInRecord3({ a: "list_in_record3 input" }), { a: "list_in_record3 output" }, ); assert.deepStrictEqual( - wasm.fListInRecord4({ a: "input4" }), + wasm.exports.fListInRecord4({ a: "input4" }), { a: "result4" }, ); - wasm.fListInVariant1("foo", { tag: 'err', val: 'bar' }, { tag: 0, val: 'baz' }); + wasm.exports.fListInVariant1("foo", { tag: 'err', val: 'bar' }, { tag: 0, val: 'baz' }); - assert.deepStrictEqual(wasm.fListInVariant2(), "list_in_variant2"); - assert.deepStrictEqual(wasm.fListInVariant3("input3"), "output3"); + assert.deepStrictEqual(wasm.exports.fListInVariant2(), "list_in_variant2"); + assert.deepStrictEqual(wasm.exports.fListInVariant3("input3"), "output3"); try { - wasm.errnoResult(); + wasm.exports.errnoResult(); assert.ok(false); } catch (e: any) { @@ -79,7 +79,7 @@ export async function run () { assert.strictEqual(e.payload, 'b'); } - const [r1, r2] = wasm.listTypedefs("typedef1", ["typedef2"]); + const [r1, r2] = wasm.exports.listTypedefs("typedef1", ["typedef2"]); assert.deepStrictEqual(r1, (new TextEncoder()).encode('typedef3')); assert.deepStrictEqual(r2, ['typedef4']); } diff --git a/tests/runtime/lists/host.ts b/tests/runtime/lists/host.ts index 4beb63dd7..c1f72b0be 100644 --- a/tests/runtime/lists/host.ts +++ b/tests/runtime/lists/host.ts @@ -104,27 +104,27 @@ async function run() { const bytes = wasm.allocatedBytes(); wasm.testImports(); - wasm.emptyListParam(new Uint8Array([])); - wasm.emptyStringParam(''); - wasm.listParam(new Uint8Array([1, 2, 3, 4]).buffer); - wasm.listParam2("foo"); - wasm.listParam3(["foo", "bar", "baz"]); - wasm.listParam4([["foo", "bar"], ["baz"]]); - assert.deepStrictEqual(Array.from(wasm.emptyListResult()), []); - assert.deepStrictEqual(wasm.emptyStringResult(), ""); - assert.deepStrictEqual(Array.from(wasm.listResult()), [1, 2, 3, 4, 5]); - assert.deepStrictEqual(wasm.listResult2(), "hello!"); - assert.deepStrictEqual(wasm.listResult3(), ["hello,", "world!"]); + wasm.exports.emptyListParam(new Uint8Array([])); + wasm.exports.emptyStringParam(''); + wasm.exports.listParam(new Uint8Array([1, 2, 3, 4]).buffer); + wasm.exports.listParam2("foo"); + wasm.exports.listParam3(["foo", "bar", "baz"]); + wasm.exports.listParam4([["foo", "bar"], ["baz"]]); + assert.deepStrictEqual(Array.from(wasm.exports.emptyListResult()), []); + assert.deepStrictEqual(wasm.exports.emptyStringResult(), ""); + assert.deepStrictEqual(Array.from(wasm.exports.listResult()), [1, 2, 3, 4, 5]); + assert.deepStrictEqual(wasm.exports.listResult2(), "hello!"); + assert.deepStrictEqual(wasm.exports.listResult3(), ["hello,", "world!"]); const buffer = new ArrayBuffer(8); (new Uint8Array(buffer)).set(new Uint8Array([1, 2, 3, 4]), 2); // Create a view of the four bytes in the middle of the buffer const view = new Uint8Array(buffer, 2, 4); - assert.deepStrictEqual(Array.from(wasm.listRoundtrip(view)), [1, 2, 3, 4]); + assert.deepStrictEqual(Array.from(wasm.exports.listRoundtrip(view)), [1, 2, 3, 4]); - assert.deepStrictEqual(wasm.stringRoundtrip("x"), "x"); - assert.deepStrictEqual(wasm.stringRoundtrip(""), ""); - assert.deepStrictEqual(wasm.stringRoundtrip("hello ⚑ world"), "hello ⚑ world"); + assert.deepStrictEqual(wasm.exports.stringRoundtrip("x"), "x"); + assert.deepStrictEqual(wasm.exports.stringRoundtrip(""), ""); + assert.deepStrictEqual(wasm.exports.stringRoundtrip("hello ⚑ world"), "hello ⚑ world"); // Ensure that we properly called `free` everywhere in all the glue that we // needed to. diff --git a/tests/runtime/numbers/host.ts b/tests/runtime/numbers/host.ts index 8a9822bf6..cdf69f3a6 100644 --- a/tests/runtime/numbers/host.ts +++ b/tests/runtime/numbers/host.ts @@ -36,52 +36,52 @@ async function run() { wasm.testImports(); - assertEq(wasm.roundtripU8(1), 1); - assertEq(wasm.roundtripU8((1 << 8) - 1), (1 << 8) - 1); + assertEq(wasm.exports.roundtripU8(1), 1); + assertEq(wasm.exports.roundtripU8((1 << 8) - 1), (1 << 8) - 1); - assertEq(wasm.roundtripS8(1), 1); - assertEq(wasm.roundtripS8((1 << 7) - 1), (1 << 7) - 1); - assertEq(wasm.roundtripS8(-(1 << 7)), -(1 << 7)); + assertEq(wasm.exports.roundtripS8(1), 1); + assertEq(wasm.exports.roundtripS8((1 << 7) - 1), (1 << 7) - 1); + assertEq(wasm.exports.roundtripS8(-(1 << 7)), -(1 << 7)); - assertEq(wasm.roundtripU16(1), 1); - assertEq(wasm.roundtripU16((1 << 16) - 1), (1 << 16) - 1); + assertEq(wasm.exports.roundtripU16(1), 1); + assertEq(wasm.exports.roundtripU16((1 << 16) - 1), (1 << 16) - 1); - assertEq(wasm.roundtripS16(1), 1); - assertEq(wasm.roundtripS16((1 << 15) - 1), (1 << 15) - 1); - assertEq(wasm.roundtripS16(-(1 << 15)), -(1 << 15)); + assertEq(wasm.exports.roundtripS16(1), 1); + assertEq(wasm.exports.roundtripS16((1 << 15) - 1), (1 << 15) - 1); + assertEq(wasm.exports.roundtripS16(-(1 << 15)), -(1 << 15)); - assertEq(wasm.roundtripU32(1), 1); - assertEq(wasm.roundtripU32(~0 >>> 0), ~0 >>> 0); + assertEq(wasm.exports.roundtripU32(1), 1); + assertEq(wasm.exports.roundtripU32(~0 >>> 0), ~0 >>> 0); - assertEq(wasm.roundtripS32(1), 1); - assertEq(wasm.roundtripS32(((1 << 31) - 1) >>> 0), ((1 << 31) - 1) >>> 0); - assertEq(wasm.roundtripS32(1 << 31), 1 << 31); + assertEq(wasm.exports.roundtripS32(1), 1); + assertEq(wasm.exports.roundtripS32(((1 << 31) - 1) >>> 0), ((1 << 31) - 1) >>> 0); + assertEq(wasm.exports.roundtripS32(1 << 31), 1 << 31); - assertEq(wasm.roundtripU64(1n), 1n); - assertEq(wasm.roundtripU64((1n << 64n) - 1n), (1n << 64n) - 1n); + assertEq(wasm.exports.roundtripU64(1n), 1n); + assertEq(wasm.exports.roundtripU64((1n << 64n) - 1n), (1n << 64n) - 1n); - assertEq(wasm.roundtripS64(1n), 1n); - assertEq(wasm.roundtripS64((1n << 63n) - 1n), (1n << 63n) - 1n); - assertEq(wasm.roundtripS64(-(1n << 63n)), -(1n << 63n)); + assertEq(wasm.exports.roundtripS64(1n), 1n); + assertEq(wasm.exports.roundtripS64((1n << 63n) - 1n), (1n << 63n) - 1n); + assertEq(wasm.exports.roundtripS64(-(1n << 63n)), -(1n << 63n)); - assertEq(wasm.roundtripFloat32(1), 1); - assertEq(wasm.roundtripFloat32(Infinity), Infinity); - assertEq(wasm.roundtripFloat32(-Infinity), -Infinity); - assert(Number.isNaN(wasm.roundtripFloat32(NaN))); + assertEq(wasm.exports.roundtripFloat32(1), 1); + assertEq(wasm.exports.roundtripFloat32(Infinity), Infinity); + assertEq(wasm.exports.roundtripFloat32(-Infinity), -Infinity); + assert(Number.isNaN(wasm.exports.roundtripFloat32(NaN))); - assertEq(wasm.roundtripFloat64(1), 1); - assertEq(wasm.roundtripFloat64(Infinity), Infinity); - assertEq(wasm.roundtripFloat64(-Infinity), -Infinity); - assert(Number.isNaN(wasm.roundtripFloat64(NaN))); + assertEq(wasm.exports.roundtripFloat64(1), 1); + assertEq(wasm.exports.roundtripFloat64(Infinity), Infinity); + assertEq(wasm.exports.roundtripFloat64(-Infinity), -Infinity); + assert(Number.isNaN(wasm.exports.roundtripFloat64(NaN))); - assertEq(wasm.roundtripChar('a'), 'a'); - assertEq(wasm.roundtripChar(' '), ' '); - assertEq(wasm.roundtripChar('🚩'), '🚩'); + assertEq(wasm.exports.roundtripChar('a'), 'a'); + assertEq(wasm.exports.roundtripChar(' '), ' '); + assertEq(wasm.exports.roundtripChar('🚩'), '🚩'); - wasm.setScalar(2); - assertEq(wasm.getScalar(), 2); - wasm.setScalar(4); - assertEq(wasm.getScalar(), 4); + wasm.exports.setScalar(2); + assertEq(wasm.exports.getScalar(), 2); + wasm.exports.setScalar(4); + assertEq(wasm.exports.getScalar(), 4); } await run() diff --git a/tests/runtime/records/host.ts b/tests/runtime/records/host.ts index 7169e4bd5..e302254e0 100644 --- a/tests/runtime/records/host.ts +++ b/tests/runtime/records/host.ts @@ -21,31 +21,31 @@ async function run() { }); wasm.testImports(); - assert.deepEqual(wasm.multipleResults(), [100, 200]); - assert.deepStrictEqual(wasm.swapTuple([1, 2]), [2, 1]); - assert.deepEqual(wasm.roundtripFlags1({ a: true }), { a: true, b: false }); - assert.deepEqual(wasm.roundtripFlags1({}), { a: false, b: false }); - assert.deepEqual(wasm.roundtripFlags1({ a: true, b: true }), { a: true, b: true }); + assert.deepEqual(wasm.exports.multipleResults(), [100, 200]); + assert.deepStrictEqual(wasm.exports.swapTuple([1, 2]), [2, 1]); + assert.deepEqual(wasm.exports.roundtripFlags1({ a: true }), { a: true, b: false }); + assert.deepEqual(wasm.exports.roundtripFlags1({}), { a: false, b: false }); + assert.deepEqual(wasm.exports.roundtripFlags1({ a: true, b: true }), { a: true, b: true }); - assert.deepEqual(wasm.roundtripFlags2({ c: true }), { c: true, d: false, e: false }); - assert.deepEqual(wasm.roundtripFlags2({}), { c: false, d: false, e: false }); - assert.deepEqual(wasm.roundtripFlags2({ d: true }), { c: false, d: true, e: false }); - assert.deepEqual(wasm.roundtripFlags2({ c: true, e: true }), { c: true, d: false, e: true }); + assert.deepEqual(wasm.exports.roundtripFlags2({ c: true }), { c: true, d: false, e: false }); + assert.deepEqual(wasm.exports.roundtripFlags2({}), { c: false, d: false, e: false }); + assert.deepEqual(wasm.exports.roundtripFlags2({ d: true }), { c: false, d: true, e: false }); + assert.deepEqual(wasm.exports.roundtripFlags2({ c: true, e: true }), { c: true, d: false, e: true }); { - const { a, b } = wasm.roundtripRecord1({ a: 8, b: {} }); + const { a, b } = wasm.exports.roundtripRecord1({ a: 8, b: {} }); assert.deepEqual(a, 8); assert.deepEqual(b, { a: false, b: false }); } { - const { a, b } = wasm.roundtripRecord1({ a: 0, b: { a: true, b: true } }); + const { a, b } = wasm.exports.roundtripRecord1({ a: 0, b: { a: true, b: true } }); assert.deepEqual(a, 0); assert.deepEqual(b, { a: true, b: true }); } - assert.deepStrictEqual(wasm.tuple0([]), []); - assert.deepStrictEqual(wasm.tuple1([1]), [1]); + assert.deepStrictEqual(wasm.exports.tuple0([]), []); + assert.deepStrictEqual(wasm.exports.tuple1([1]), [1]); } await run() diff --git a/tests/runtime/variants/host.ts b/tests/runtime/variants/host.ts index cc8332040..2f3f09210 100644 --- a/tests/runtime/variants/host.ts +++ b/tests/runtime/variants/host.ts @@ -36,19 +36,19 @@ async function run() { }); wasm.testImports(); - assert.deepStrictEqual(wasm.roundtripOption(1), 1); - assert.deepStrictEqual(wasm.roundtripOption(null), null); + assert.deepStrictEqual(wasm.exports.roundtripOption(1), 1); + assert.deepStrictEqual(wasm.exports.roundtripOption(null), null); // @ts-ignore - assert.deepStrictEqual(wasm.roundtripOption(undefined), null); + assert.deepStrictEqual(wasm.exports.roundtripOption(undefined), null); // @ts-ignore - assert.deepStrictEqual(wasm.roundtripOption(), null); - assert.deepStrictEqual(wasm.roundtripOption(2), 2); - assert.deepStrictEqual(wasm.roundtripResult({ tag: 'ok', val: 2 }), 2); - assert.deepStrictEqual(wasm.roundtripResult({ tag: 'ok', val: 4 }), 4); + assert.deepStrictEqual(wasm.exports.roundtripOption(), null); + assert.deepStrictEqual(wasm.exports.roundtripOption(2), 2); + assert.deepStrictEqual(wasm.exports.roundtripResult({ tag: 'ok', val: 2 }), 2); + assert.deepStrictEqual(wasm.exports.roundtripResult({ tag: 'ok', val: 4 }), 4); const f = Math.fround(5.2); try { - wasm.roundtripResult({ tag: 'err', val: f }); + wasm.exports.roundtripResult({ tag: 'err', val: f }); assert.fail('Expected an error'); } catch (e: any) { assert.strictEqual(e.constructor.name, 'ComponentError'); @@ -56,14 +56,14 @@ async function run() { assert.strictEqual(e.payload, 5); } - assert.deepStrictEqual(wasm.roundtripEnum("a"), "a"); - assert.deepStrictEqual(wasm.roundtripEnum("b"), "b"); + assert.deepStrictEqual(wasm.exports.roundtripEnum("a"), "a"); + assert.deepStrictEqual(wasm.exports.roundtripEnum("b"), "b"); - assert.deepStrictEqual(wasm.invertBool(true), false); - assert.deepStrictEqual(wasm.invertBool(false), true); + assert.deepStrictEqual(wasm.exports.invertBool(true), false); + assert.deepStrictEqual(wasm.exports.invertBool(false), true); { - const [a1, a2, a3, a4, a5, a6] = wasm.variantCasts([ + const [a1, a2, a3, a4, a5, a6] = wasm.exports.variantCasts([ { tag: 'a', val: 1 }, { tag: 'a', val: 2 }, { tag: 'a', val: 3 }, @@ -79,7 +79,7 @@ async function run() { assert.deepStrictEqual(a6, { tag: 'a', val: 6 }); } { - const [b1, b2, b3, b4, b5, b6] = wasm.variantCasts([ + const [b1, b2, b3, b4, b5, b6] = wasm.exports.variantCasts([ { tag: 'b', val: 1n }, { tag: 'b', val: 2 }, { tag: 'b', val: 3 }, @@ -96,7 +96,7 @@ async function run() { } { - const [a1, a2, a3, a4] = wasm.variantZeros([ + const [a1, a2, a3, a4] = wasm.exports.variantZeros([ { tag: 'a', val: 1 }, { tag: 'a', val: 2n }, { tag: 'a', val: 3 }, @@ -108,7 +108,7 @@ async function run() { assert.deepStrictEqual(a4, { tag: 'a', val: 4 }); } - wasm.variantTypedefs(null, false, { tag: 'err', val: undefined }); + wasm.exports.variantTypedefs(null, false, { tag: 'err', val: undefined }); } await run() From a4352e60960493bb1db2ad42152d9eea3621abd2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 10:51:04 -0800 Subject: [PATCH 17/25] Use git repo for wit-{component,parser} patch --- Cargo.lock | 36 +++++++++++++++++++++--------------- Cargo.toml | 4 ++-- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65375f7a6..9b36524f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -699,6 +699,8 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ab2fe77b325731603297debb4573e002d06ae0aa1f4dc108585c81961e0609" dependencies = [ "leb128", ] @@ -706,8 +708,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ab2fe77b325731603297debb4573e002d06ae0aa1f4dc108585c81961e0609" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" dependencies = [ "leb128", ] @@ -715,6 +716,8 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.97.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98123a0d2bacf9286239231b116cbd66c65d9b89793f7c9bba3a3ae7f1b15f3" dependencies = [ "indexmap", "url", @@ -723,8 +726,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.97.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98123a0d2bacf9286239231b116cbd66c65d9b89793f7c9bba3a3ae7f1b15f3" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" dependencies = [ "indexmap", "url", @@ -780,39 +782,41 @@ dependencies = [ [[package]] name = "wast" version = "51.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f621e6e9af96438d3e05f0699da5b1dae59f2df964a2982166aa9b03c5b599" dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.21.0", + "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wast" version = "51.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f621e6e9af96438d3e05f0699da5b1dae59f2df964a2982166aa9b03c5b599" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.21.0 (git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs)", ] [[package]] name = "wat" version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd18c1168d7e8743d9b4f713c0203924f5dcc4a3983eb5e584de9614f9fccde" dependencies = [ - "wast 51.0.0", + "wast 51.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wat" version = "1.0.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd18c1168d7e8743d9b4f713c0203924f5dcc4a3983eb5e584de9614f9fccde" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" dependencies = [ - "wast 51.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wast 51.0.0 (git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs)", ] [[package]] @@ -983,21 +987,23 @@ dependencies = [ [[package]] name = "wit-component" version = "0.4.1" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" dependencies = [ "anyhow", "bitflags", "indexmap", "log", "url", - "wasm-encoder 0.21.0", - "wasmparser 0.97.0", - "wat 1.0.53", + "wasm-encoder 0.21.0 (git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs)", + "wasmparser 0.97.0 (git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs)", + "wat 1.0.53 (git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs)", "wit-parser", ] [[package]] name = "wit-parser" version = "0.4.0" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index d9a05a26d..77ece975e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,5 +60,5 @@ wat = { workspace = true } wit-component = { workspace = true } [patch.crates-io] -wit-component = { path = '../wasm-tools/crates/wit-component' } -wit-parser = { path = '../wasm-tools/crates/wit-parser' } +wit-component = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'fix-wit-component-bugs' } +wit-parser = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'fix-wit-component-bugs' } From d526269aa2001104672999be299f2ffc492f4a46 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 12:59:26 -0800 Subject: [PATCH 18/25] Update C generator for new WIT --- .../gen-guest-c/src/component_type_object.rs | 11 +- crates/gen-guest-c/src/lib.rs | 162 ++++++++++++------ crates/gen-guest-c/tests/codegen.rs | 8 +- crates/test-helpers/Cargo.toml | 2 +- crates/test-helpers/runtime-macro/build.rs | 16 +- tests/runtime/exports_only/world.wit | 8 +- tests/runtime/flavorful/wasm.c | 38 ++-- tests/runtime/lists/wasm.c | 52 ++++-- tests/runtime/numbers/wasm.c | 26 +-- tests/runtime/records/wasm.c | 16 +- tests/runtime/strings/world.wit | 12 +- tests/runtime/variants/wasm.c | 18 +- 12 files changed, 226 insertions(+), 143 deletions(-) diff --git a/crates/gen-guest-c/src/component_type_object.rs b/crates/gen-guest-c/src/component_type_object.rs index 16d83fcb5..c551f0c90 100644 --- a/crates/gen-guest-c/src/component_type_object.rs +++ b/crates/gen-guest-c/src/component_type_object.rs @@ -3,7 +3,7 @@ use heck::ToSnakeCase; use wasm_encoder::{ CodeSection, CustomSection, Encode, Function, FunctionSection, Module, TypeSection, }; -use wit_bindgen_core::wit_parser::World; +use wit_bindgen_core::wit_parser::{Resolve, WorldId}; use wit_component::StringEncoding; pub fn linking_symbol(name: &str) -> String { @@ -11,7 +11,7 @@ pub fn linking_symbol(name: &str) -> String { format!("__component_type_object_force_link_{snake}") } -pub fn object(world: &World, encoding: StringEncoding) -> Result> { +pub fn object(resolve: &Resolve, world: WorldId, encoding: StringEncoding) -> Result> { let mut module = Module::new(); // Build a module with one function that's a "dummy function" @@ -25,13 +25,14 @@ pub fn object(world: &World, encoding: StringEncoding) -> Result> { code.function(&Function::new([])); module.section(&code); - let data = wit_component::metadata::encode(world, encoding); + let data = wit_component::metadata::encode(resolve, world, encoding).unwrap(); // The custom section name here must start with "component-type" but // otherwise is attempted to be unique here to ensure that this doesn't get // concatenated to other custom sections by LLD by accident since LLD will // concatenate custom sections of the same name. - let section_name = format!("component-type:{}", world.name); + let world_name = &resolve.worlds[world].name; + let section_name = format!("component-type:{world_name}"); // Add our custom section module.section(&CustomSection { @@ -48,7 +49,7 @@ pub fn object(world: &World, encoding: StringEncoding) -> Result> { subsection.push(0x00); // SYMTAB_FUNCTION 0u32.encode(&mut subsection); // flags 0u32.encode(&mut subsection); // index - linking_symbol(&world.name).encode(&mut subsection); // name + linking_symbol(&world_name).encode(&mut subsection); // name data.push(0x08); // `WASM_SYMBOL_TABLE` subsection.encode(&mut data); diff --git a/crates/gen-guest-c/src/lib.rs b/crates/gen-guest-c/src/lib.rs index 8d99b18ae..a143875d8 100644 --- a/crates/gen-guest-c/src/lib.rs +++ b/crates/gen-guest-c/src/lib.rs @@ -22,6 +22,8 @@ struct C { names: Ns, needs_string: bool, world: String, + sizes: SizeAlign, + interface_names: HashMap, } #[derive(Default, Debug, Clone)] @@ -67,15 +69,25 @@ enum Scalar { } impl WorldGenerator for C { - fn preprocess(&mut self, name: &str) { + fn preprocess(&mut self, resolve: &Resolve, name: &str) { self.world = name.to_string(); + self.sizes.fill(resolve); } - fn import(&mut self, name: &str, iface: &Interface, _files: &mut Files) { - let mut gen = self.interface(name, iface, true); - gen.types(); + fn import_interface( + &mut self, + resolve: &Resolve, + name: &str, + id: InterfaceId, + _files: &mut Files, + ) { + let prev = self.interface_names.insert(id, name.to_string()); + assert!(prev.is_none()); + let mut gen = self.interface(name, resolve, true); + gen.interface = Some(id); + gen.types(id); - for (i, func) in iface.functions.iter().enumerate() { + for (i, (_name, func)) in resolve.interfaces[id].functions.iter().enumerate() { if i == 0 { uwriteln!(gen.src.h_fns, "\n// Imported Functions from `{name}`"); } @@ -87,11 +99,41 @@ impl WorldGenerator for C { gen.gen.src.append(&gen.src); } - fn export(&mut self, name: &str, iface: &Interface, _files: &mut Files) { - let mut gen = self.interface(name, iface, false); - gen.types(); + fn import_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let name = &resolve.worlds[world].name; + let mut gen = self.interface(name, resolve, true); - for (i, func) in iface.functions.iter().enumerate() { + for (i, (_name, func)) in funcs.iter().enumerate() { + if i == 0 { + uwriteln!(gen.src.h_fns, "\n// Imported Functions from `{name}`"); + } + gen.import(func); + } + + gen.finish(); + + gen.gen.src.append(&gen.src); + } + + fn export_interface( + &mut self, + resolve: &Resolve, + name: &str, + id: InterfaceId, + _files: &mut Files, + ) { + self.interface_names.insert(id, name.to_string()); + let mut gen = self.interface(name, resolve, false); + gen.interface = Some(id); + gen.types(id); + + for (i, (_name, func)) in resolve.interfaces[id].functions.iter().enumerate() { if i == 0 { uwriteln!(gen.src.h_fns, "\n// Exported Functions from `{name}`"); } @@ -103,11 +145,17 @@ impl WorldGenerator for C { gen.gen.src.append(&gen.src); } - fn export_default(&mut self, name: &str, iface: &Interface, _files: &mut Files) { - let mut gen = self.interface(name, iface, false); - gen.types(); + fn export_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let name = &resolve.worlds[world].name; + let mut gen = self.interface(name, resolve, false); - for (i, func) in iface.functions.iter().enumerate() { + for (i, (_name, func)) in funcs.iter().enumerate() { if i == 0 { uwriteln!(gen.src.h_fns, "\n// Exported Functions from `{name}`"); } @@ -119,7 +167,8 @@ impl WorldGenerator for C { gen.gen.src.append(&gen.src); } - fn finish(&mut self, world: &World, files: &mut Files) { + fn finish(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) { + let world = &resolve.worlds[id]; let linking_symbol = component_type_object::linking_symbol(&world.name); self.include(""); let snake = world.name.to_snake_case(); @@ -285,7 +334,7 @@ impl WorldGenerator for C { files.push(&format!("{snake}.h"), h_str.as_bytes()); files.push( &format!("{snake}_component_type.o",), - component_type_object::object(world, self.opts.string_encoding) + component_type_object::object(resolve, id, self.opts.string_encoding) .unwrap() .as_slice(), ); @@ -296,17 +345,15 @@ impl C { fn interface<'a>( &'a mut self, name: &'a str, - iface: &'a Interface, + resolve: &'a Resolve, in_import: bool, ) -> InterfaceGenerator<'a> { - let mut sizes = SizeAlign::default(); - sizes.fill(iface); InterfaceGenerator { name, src: Source::default(), gen: self, - iface, - sizes, + resolve, + interface: None, public_anonymous_types: Default::default(), private_anonymous_types: Default::default(), types: Default::default(), @@ -332,8 +379,8 @@ struct InterfaceGenerator<'a> { src: Source, in_import: bool, gen: &'a mut C, - iface: &'a Interface, - sizes: SizeAlign, + resolve: &'a Resolve, + interface: Option, // The set of types that are considered public (aka need to be in the // header file) which are anonymous and we're effectively monomorphizing. @@ -369,7 +416,7 @@ impl C { } impl Return { - fn return_single(&mut self, iface: &Interface, ty: &Type, orig_ty: &Type) { + fn return_single(&mut self, resolve: &Resolve, ty: &Type, orig_ty: &Type) { let id = match ty { Type::Id(id) => *id, Type::String => { @@ -381,8 +428,8 @@ impl Return { return; } }; - match &iface.types[id].kind { - TypeDefKind::Type(t) => return self.return_single(iface, t, orig_ty), + match &resolve.types[id].kind { + TypeDefKind::Type(t) => return self.return_single(resolve, t, orig_ty), // Flags are returned as their bare values, and enums are scalars TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => { @@ -423,6 +470,7 @@ impl Return { TypeDefKind::Future(_) => todo!("return_single for future"), TypeDefKind::Stream(_) => todo!("return_single for stream"), + TypeDefKind::Unknown => unreachable!(), } self.retptrs.push(*orig_ty); @@ -430,8 +478,8 @@ impl Return { } impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { - fn iface(&self) -> &'a Interface { - self.iface + fn resolve(&self) -> &'a Resolve { + self.resolve } fn type_record(&mut self, id: TypeId, name: &str, record: &Record, docs: &Docs) { @@ -653,7 +701,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { impl InterfaceGenerator<'_> { fn import(&mut self, func: &Function) { - let sig = self.iface.wasm_signature(AbiVariant::GuestImport, func); + let sig = self.resolve.wasm_signature(AbiVariant::GuestImport, func); self.src.c_fns("\n"); @@ -703,7 +751,7 @@ impl InterfaceGenerator<'_> { for (i, (_, param)) in c_sig.params.iter().enumerate() { let ty = &func.params[i].1; if let Type::Id(id) = ty { - if let TypeDefKind::Option(option_ty) = &self.iface.types[*id].kind { + if let TypeDefKind::Option(option_ty) = &self.resolve.types[*id].kind { let ty = self.type_string(ty); uwrite!( optional_adapters, @@ -735,7 +783,7 @@ impl InterfaceGenerator<'_> { f.locals.insert(ptr).unwrap(); } f.src.push_str(&optional_adapters); - f.gen.iface.call( + f.gen.resolve.call( AbiVariant::GuestImport, LiftLower::LowerArgsLiftResults, func, @@ -749,7 +797,7 @@ impl InterfaceGenerator<'_> { } fn export(&mut self, func: &Function, interface_name: Option<&str>) { - let sig = self.iface.wasm_signature(AbiVariant::GuestExport, func); + let sig = self.resolve.wasm_signature(AbiVariant::GuestExport, func); let export_name = func.core_export_name(interface_name); @@ -792,7 +840,7 @@ impl InterfaceGenerator<'_> { f.gen.src.c_adapters(") {\n"); // Perform all lifting/lowering and append it to our src. - f.gen.iface.call( + f.gen.resolve.call( AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, func, @@ -802,7 +850,7 @@ impl InterfaceGenerator<'_> { self.src.c_adapters(&src); self.src.c_adapters("}\n"); - if self.iface.guest_export_needs_post_return(func) { + if self.resolve.guest_export_needs_post_return(func) { uwriteln!( self.src.c_fns, "__attribute__((weak, export_name(\"cabi_post_{export_name}\")))" @@ -831,7 +879,7 @@ impl InterfaceGenerator<'_> { let mut f = FunctionBindgen::new(self, c_sig, &import_name); f.params = params; - f.gen.iface.post_return(func, &mut f); + f.gen.resolve.post_return(func, &mut f); let FunctionBindgen { src, .. } = f; self.src.c_fns(&src); self.src.c_fns("}\n"); @@ -869,7 +917,7 @@ impl InterfaceGenerator<'_> { } } - for id in self.iface.topological_types() { + for (id, _) in self.resolve.types.iter() { if let Some(ty) = self.types.get(&id) { if private_types.contains(&id) { // It's private; print it in the .c file. @@ -917,7 +965,7 @@ impl InterfaceGenerator<'_> { let pointer = self.is_arg_by_pointer(ty); // optional param pointer flattening let optional_type = if let Type::Id(id) = ty { - if let TypeDefKind::Option(option_ty) = &self.iface.types[*id].kind { + if let TypeDefKind::Option(option_ty) = &self.resolve.types[*id].kind { Some(option_ty) } else { None @@ -988,7 +1036,7 @@ impl InterfaceGenerator<'_> { 0 => ret.scalar = Some(Scalar::Void), 1 => { let ty = func.results.iter_types().next().unwrap(); - ret.return_single(self.iface, ty, ty); + ret.return_single(self.resolve, ty, ty); } _ => { ret.return_multiple = true; @@ -1000,7 +1048,7 @@ impl InterfaceGenerator<'_> { fn is_arg_by_pointer(&self, ty: &Type) -> bool { match ty { - Type::Id(id) => match &self.iface.types[*id].kind { + Type::Id(id) => match &self.resolve.types[*id].kind { TypeDefKind::Type(t) => self.is_arg_by_pointer(t), TypeDefKind::Variant(_) => true, TypeDefKind::Union(_) => true, @@ -1011,6 +1059,7 @@ impl InterfaceGenerator<'_> { TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) => true, TypeDefKind::Future(_) => todo!("is_arg_by_pointer for future"), TypeDefKind::Stream(_) => todo!("is_arg_by_pointer for stream"), + TypeDefKind::Unknown => unreachable!(), }, Type::String => true, _ => false, @@ -1055,10 +1104,15 @@ impl InterfaceGenerator<'_> { self.gen.needs_string = true; } Type::Id(id) => { - let ty = &self.iface.types[*id]; + let ty = &self.resolve.types[*id]; match &ty.name { Some(name) => { - self.print_namespace(stype); + if let TypeOwner::Interface(owner) = ty.owner { + self.src + .print(stype, &self.gen.interface_names[&owner].to_snake_case()); + self.src.print(stype, "_"); + } + self.src.print(stype, &name.to_snake_case()); self.src.print(stype, "_t"); } @@ -1093,7 +1147,7 @@ impl InterfaceGenerator<'_> { Type::Float64 => self.src.print(stype, "float64"), Type::String => self.src.print(stype, "string"), Type::Id(id) => { - let ty = &self.iface.types[*id]; + let ty = &self.resolve.types[*id]; if let Some(name) = &ty.name { return self.src.print(stype, &name.to_snake_case()); } @@ -1138,6 +1192,7 @@ impl InterfaceGenerator<'_> { self.src.print(stype, "_"); self.print_optional_ty_name(stype, s.end.as_ref()); } + TypeDefKind::Unknown => unreachable!(), } } } @@ -1167,7 +1222,7 @@ impl InterfaceGenerator<'_> { Type::Id(id) => *id, _ => return false, }; - match &self.iface.types[id].kind { + match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.is_empty_type(t), TypeDefKind::Record(r) => r.fields.is_empty(), TypeDefKind::Tuple(t) => t.types.is_empty(), @@ -1224,7 +1279,7 @@ impl InterfaceGenerator<'_> { fn print_anonymous_type(&mut self, ty: TypeId) { let prev = mem::take(&mut self.src.h_defs); self.src.h_defs("\ntypedef "); - let kind = &self.iface.types[ty].kind; + let kind = &self.resolve.types[ty].kind; match kind { TypeDefKind::Type(_) | TypeDefKind::Flags(_) @@ -1278,6 +1333,7 @@ impl InterfaceGenerator<'_> { } TypeDefKind::Future(_) => todo!("print_anonymous_type for future"), TypeDefKind::Stream(_) => todo!("print_anonymous_type for stream"), + TypeDefKind::Unknown => unreachable!(), } self.src.h_defs(" "); self.print_namespace(SourceType::HDefs); @@ -1304,7 +1360,7 @@ impl InterfaceGenerator<'_> { self.src.c_helpers(&self.src.h_helpers[pos..].to_string()); self.src.h_helpers(";"); self.src.c_helpers(" {\n"); - match &self.iface.types[id].kind { + match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.free(t, "ptr"), TypeDefKind::Flags(_) => {} @@ -1397,6 +1453,7 @@ impl InterfaceGenerator<'_> { } TypeDefKind::Future(_) => todo!("print_dtor for future"), TypeDefKind::Stream(_) => todo!("print_dtor for stream"), + TypeDefKind::Unknown => unreachable!(), } self.src.c_helpers("}\n"); } @@ -1407,7 +1464,7 @@ impl InterfaceGenerator<'_> { Type::String => return true, _ => return false, }; - match &self.iface.types[id].kind { + match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.owns_anything(t), TypeDefKind::Record(r) => r.fields.iter().any(|t| self.owns_anything(&t.ty)), TypeDefKind::Tuple(t) => t.types.iter().any(|t| self.owns_anything(t)), @@ -1426,6 +1483,7 @@ impl InterfaceGenerator<'_> { } TypeDefKind::Future(_) => todo!("owns_anything for future"), TypeDefKind::Stream(_) => todo!("owns_anything for stream"), + TypeDefKind::Unknown => unreachable!(), } } @@ -1537,7 +1595,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { type Operand = String; fn sizes(&self) -> &SizeAlign { - &self.gen.sizes + &self.gen.gen.sizes } fn push_block(&mut self) { @@ -1551,7 +1609,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.blocks.push((src.into(), mem::take(operands))); } - fn return_pointer(&mut self, _iface: &Interface, size: usize, align: usize) -> String { + fn return_pointer(&mut self, size: usize, align: usize) -> String { let ptr = self.locals.tmp("ptr"); if self.gen.in_import { @@ -1579,13 +1637,13 @@ impl Bindgen for FunctionBindgen<'_, '_> { ptr } - fn is_list_canonical(&self, iface: &Interface, ty: &Type) -> bool { - iface.all_bits_valid(ty) + fn is_list_canonical(&self, resolve: &Resolve, ty: &Type) -> bool { + resolve.all_bits_valid(ty) } fn emit( &mut self, - _iface: &Interface, + _resolve: &Resolve, inst: &Instruction<'_>, operands: &mut Vec, results: &mut Vec, @@ -2161,7 +2219,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { if !self.gen.in_import { if let Type::Id(id) = ty { if let TypeDefKind::Option(option_ty) = - &self.gen.iface.types[*id].kind + &self.gen.resolve.types[*id].kind { if self.gen.is_empty_type(option_ty) { uwrite!(args, "{op}.is_some ? (void*)1 : NULL"); @@ -2409,7 +2467,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "int32_t {len} = {};", operands[1]); let i = self.locals.tmp("i"); uwriteln!(self.src, "for (int32_t {i} = 0; {i} < {len}; {i}++) {{"); - let size = self.gen.sizes.size(element); + let size = self.gen.gen.sizes.size(element); uwriteln!(self.src, "int32_t base = {ptr} + {i} * {size};"); uwriteln!(self.src, "(void) base;"); uwrite!(self.src, "{body}"); diff --git a/crates/gen-guest-c/tests/codegen.rs b/crates/gen-guest-c/tests/codegen.rs index 6dc03c261..aefea83bf 100644 --- a/crates/gen-guest-c/tests/codegen.rs +++ b/crates/gen-guest-c/tests/codegen.rs @@ -4,16 +4,16 @@ use std::path::{Path, PathBuf}; use std::process::Command; macro_rules! codegen_test { - ($name:ident $test:tt) => { + ($id:ident $name:tt $test:tt) => { #[test] - fn $name() { + fn $id() { test_helpers::run_world_codegen_test( "guest-c", $test.as_ref(), - |world, files| { + |resolve, world, files| { wit_bindgen_gen_guest_c::Opts::default() .build() - .generate(world, files) + .generate(resolve, world, files) }, verify, ) diff --git a/crates/test-helpers/Cargo.toml b/crates/test-helpers/Cargo.toml index aa031b425..6c5d6eb85 100644 --- a/crates/test-helpers/Cargo.toml +++ b/crates/test-helpers/Cargo.toml @@ -20,5 +20,5 @@ wasm-encoder = { workspace = true } [features] default = ['guest-rust', 'guest-c', 'guest-teavm-java'] guest-rust = ['runtime-macro?/guest-rust'] -guest-c = []#'runtime-macro?/guest-c'] +guest-c = ['runtime-macro?/guest-c'] guest-teavm-java = []#'runtime-macro?/guest-teavm-java'] diff --git a/crates/test-helpers/runtime-macro/build.rs b/crates/test-helpers/runtime-macro/build.rs index f70787a72..d979aa341 100644 --- a/crates/test-helpers/runtime-macro/build.rs +++ b/crates/test-helpers/runtime-macro/build.rs @@ -11,8 +11,6 @@ fn guest_c( wasi_adapter: &[u8], utf_16: bool, ) { - use heck::{ToSnakeCase, ToUpperCamelCase}; - let utf16_suffix = if utf_16 { "_utf16" } else { "" }; for test_dir in fs::read_dir("../../../tests/runtime").unwrap() { let test_dir = test_dir.unwrap().path(); @@ -21,14 +19,20 @@ fn guest_c( continue; } println!("cargo:rerun-if-changed={}", c_impl.display()); - let world = read_world(&test_dir); - let snake = world.name.replace("-", "_"); + let (resolve, pkg) = read_world(&test_dir); + let world = resolve.packages[pkg] + .documents + .iter() + .filter_map(|(_, doc)| resolve.documents[*doc].default_world) + .next() + .expect("no default world found"); + let snake = resolve.worlds[world].name.replace("-", "_"); let mut files = Default::default(); let mut opts = wit_bindgen_gen_guest_c::Opts::default(); if utf_16 { opts.string_encoding = wit_component::StringEncoding::UTF16; } - opts.build().generate(&world, &mut files); + opts.build().generate(&resolve, world, &mut files); let out_dir = out_dir.join(format!( "c{}-{}", @@ -95,7 +99,7 @@ fn guest_c( wasms.push(( format!("c{}", utf16_suffix), - world.name.to_string(), + resolve.worlds[world].name.to_string(), out_wasm.to_str().unwrap().to_string(), component_path.to_str().unwrap().to_string(), )); diff --git a/tests/runtime/exports_only/world.wit b/tests/runtime/exports_only/world.wit index 949c084e1..f290a1d70 100644 --- a/tests/runtime/exports_only/world.wit +++ b/tests/runtime/exports_only/world.wit @@ -1,7 +1,3 @@ -interface exports { - thunk: func() -> string -} - -world exports-only { - default export exports +default world exports-only { + export thunk: func() -> string } diff --git a/tests/runtime/flavorful/wasm.c b/tests/runtime/flavorful/wasm.c index d45e1ecba..00711fff2 100644 --- a/tests/runtime/flavorful/wasm.c +++ b/tests/runtime/flavorful/wasm.c @@ -135,47 +135,47 @@ void flavorful_test_imports() { } } -void flavorful_f_list_in_record1(flavorful_list_in_record1_t *a) { +void exports_f_list_in_record1(exports_list_in_record1_t *a) { assert(memcmp(a->a.ptr, "list_in_record1", a->a.len) == 0); - flavorful_list_in_record1_free(a); + exports_list_in_record1_free(a); } -void flavorful_f_list_in_record2(flavorful_list_in_record2_t *ret0) { +void exports_f_list_in_record2(exports_list_in_record2_t *ret0) { flavorful_string_dup(&ret0->a, "list_in_record2"); } -void flavorful_f_list_in_record3(flavorful_list_in_record3_t *a, flavorful_list_in_record3_t *ret0) { +void exports_f_list_in_record3(exports_list_in_record3_t *a, exports_list_in_record3_t *ret0) { assert(memcmp(a->a.ptr, "list_in_record3 input", a->a.len) == 0); - flavorful_list_in_record3_free(a); + exports_list_in_record3_free(a); flavorful_string_dup(&ret0->a, "list_in_record3 output"); } -void flavorful_f_list_in_record4(flavorful_list_in_alias_t *a, flavorful_list_in_alias_t *ret0) { +void exports_f_list_in_record4(exports_list_in_alias_t *a, exports_list_in_alias_t *ret0) { assert(memcmp(a->a.ptr, "input4", a->a.len) == 0); - flavorful_list_in_alias_free(a); + exports_list_in_alias_free(a); flavorful_string_dup(&ret0->a, "result4"); } -void flavorful_f_list_in_variant1(flavorful_string_t *maybe_a, flavorful_list_in_variant1_v2_t *b, flavorful_list_in_variant1_v3_t *c) { +void exports_f_list_in_variant1(flavorful_string_t *maybe_a, exports_list_in_variant1_v2_t *b, exports_list_in_variant1_v3_t *c) { assert(maybe_a != NULL); assert(memcmp(maybe_a->ptr, "foo", maybe_a->len) == 0); flavorful_string_free(maybe_a); assert(b->is_err); assert(memcmp(b->val.err.ptr, "bar", b->val.err.len) == 0); - flavorful_list_in_variant1_v2_free(b); + exports_list_in_variant1_v2_free(b); assert(c->tag == 0); assert(memcmp(c->val.f0.ptr, "baz", c->val.f0.len) == 0); - flavorful_list_in_variant1_v3_free(c); + exports_list_in_variant1_v3_free(c); } -bool flavorful_f_list_in_variant2(flavorful_string_t *ret0) { +bool exports_f_list_in_variant2(flavorful_string_t *ret0) { flavorful_string_dup(ret0, "list_in_variant2"); return true; } -bool flavorful_f_list_in_variant3(flavorful_string_t *maybe_a, flavorful_string_t *ret) { +bool exports_f_list_in_variant3(flavorful_string_t *maybe_a, flavorful_string_t *ret) { assert(maybe_a != NULL); assert(memcmp(maybe_a->ptr, "input3", maybe_a->len) == 0); flavorful_string_free(maybe_a); @@ -183,18 +183,18 @@ bool flavorful_f_list_in_variant3(flavorful_string_t *maybe_a, flavorful_string_ return true; } -bool flavorful_errno_result(flavorful_my_errno_t *err) { - *err = FLAVORFUL_MY_ERRNO_B; +bool exports_errno_result(exports_my_errno_t *err) { + *err = EXPORTS_MY_ERRNO_B; return false; } -void flavorful_list_typedefs(flavorful_list_typedef_t *a, flavorful_list_typedef3_t *c, flavorful_list_typedef2_t *ret0, flavorful_list_typedef3_t *ret1) { +void exports_list_typedefs(exports_list_typedef_t *a, exports_list_typedef3_t *c, exports_list_typedef2_t *ret0, exports_list_typedef3_t *ret1) { assert(memcmp(a->ptr, "typedef1", a->len) == 0); - flavorful_list_typedef_free(a); + exports_list_typedef_free(a); assert(c->len == 1); assert(memcmp(c->ptr[0].ptr, "typedef2", c->ptr[0].len) == 0); - flavorful_list_typedef3_free(c); + exports_list_typedef3_free(c); ret0->ptr = malloc(8); ret0->len = 8; @@ -204,3 +204,7 @@ void flavorful_list_typedefs(flavorful_list_typedef_t *a, flavorful_list_typedef ret1->len = 1; flavorful_string_dup(&ret1->ptr[0], "typedef4"); } + +void exports_list_of_variants(exports_list_bool_t *a, exports_list_result_void_void_t *b, exports_list_my_errno_t *c, exports_list_bool_t *ret0, exports_list_result_void_void_t *ret1, exports_list_my_errno_t *ret2) { + assert(0); // unimplemented +} diff --git a/tests/runtime/lists/wasm.c b/tests/runtime/lists/wasm.c index 79ab024aa..fc6230746 100644 --- a/tests/runtime/lists/wasm.c +++ b/tests/runtime/lists/wasm.c @@ -228,34 +228,34 @@ void lists_test_imports() { } } -void lists_empty_list_param(lists_list_u8_t *a) { +void exports_empty_list_param(exports_list_u8_t *a) { assert(a->len == 0); } -void lists_empty_string_param(lists_string_t *a) { +void exports_empty_string_param(lists_string_t *a) { assert(a->len == 0); } -void lists_empty_list_result(lists_list_u8_t *ret0) { +void exports_empty_list_result(exports_list_u8_t *ret0) { ret0->ptr = 0; ret0->len = 0; } -void lists_empty_string_result(lists_string_t *ret0) { +void exports_empty_string_result(lists_string_t *ret0) { ret0->ptr = 0; ret0->len = 0; } -void lists_list_param(lists_list_u8_t *a) { +void exports_list_param(exports_list_u8_t *a) { assert(a->len == 4); assert(a->ptr[0] == 1); assert(a->ptr[1] == 2); assert(a->ptr[2] == 3); assert(a->ptr[3] == 4); - lists_list_u8_free(a); + exports_list_u8_free(a); } -void lists_list_param2(lists_string_t *a) { +void exports_list_param2(lists_string_t *a) { assert(a->len == 3); assert(a->ptr[0] == 'f'); assert(a->ptr[1] == 'o'); @@ -263,7 +263,7 @@ void lists_list_param2(lists_string_t *a) { lists_string_free(a); } -void lists_list_param3(lists_list_string_t *a) { +void exports_list_param3(exports_list_string_t *a) { assert(a->len == 3); assert(a->ptr[0].len == 3); assert(a->ptr[0].ptr[0] == 'f'); @@ -280,10 +280,10 @@ void lists_list_param3(lists_list_string_t *a) { assert(a->ptr[2].ptr[1] == 'a'); assert(a->ptr[2].ptr[2] == 'z'); - lists_list_string_free(a); + exports_list_string_free(a); } -void lists_list_param4(lists_list_list_string_t *a) { +void exports_list_param4(exports_list_list_string_t *a) { assert(a->len == 2); assert(a->ptr[0].len == 2); assert(a->ptr[1].len == 1); @@ -303,10 +303,10 @@ void lists_list_param4(lists_list_list_string_t *a) { assert(a->ptr[1].ptr[0].ptr[1] == 'a'); assert(a->ptr[1].ptr[0].ptr[2] == 'z'); - lists_list_list_string_free(a); + exports_list_list_string_free(a); } -void lists_list_result(lists_list_u8_t *ret0) { +void exports_list_result(exports_list_u8_t *ret0) { ret0->ptr = malloc(5); ret0->len = 5; ret0->ptr[0] = 1; @@ -316,11 +316,11 @@ void lists_list_result(lists_list_u8_t *ret0) { ret0->ptr[4] = 5; } -void lists_list_result2(lists_string_t *ret0) { +void exports_list_result2(lists_string_t *ret0) { lists_string_dup(ret0, "hello!"); } -void lists_list_result3(lists_list_string_t *ret0) { +void exports_list_result3(exports_list_string_t *ret0) { ret0->len = 2; ret0->ptr = malloc(2 * sizeof(lists_string_t)); @@ -328,10 +328,30 @@ void lists_list_result3(lists_list_string_t *ret0) { lists_string_dup(&ret0->ptr[1], "world!"); } -void lists_list_roundtrip(lists_list_u8_t *a, lists_list_u8_t *ret0) { +void exports_list_roundtrip(exports_list_u8_t *a, exports_list_u8_t *ret0) { *ret0 = *a; } -void lists_string_roundtrip(lists_string_t *a, lists_string_t *ret0) { +void exports_string_roundtrip(lists_string_t *a, lists_string_t *ret0) { *ret0 = *a; } + +void exports_list_minmax8(exports_list_u8_t *a, exports_list_s8_t *b, exports_list_u8_t *ret0, exports_list_s8_t *ret1) { + assert(0); // unimplemented +} + +void exports_list_minmax16(exports_list_u16_t *a, exports_list_s16_t *b, exports_list_u16_t *ret0, exports_list_s16_t *ret1) { + assert(0); // unimplemented +} + +void exports_list_minmax32(exports_list_u32_t *a, exports_list_s32_t *b, exports_list_u32_t *ret0, exports_list_s32_t *ret1) { + assert(0); // unimplemented +} + +void exports_list_minmax64(exports_list_u64_t *a, exports_list_s64_t *b, exports_list_u64_t *ret0, exports_list_s64_t *ret1) { + assert(0); // unimplemented +} + +void exports_list_minmax_float(exports_list_float32_t *a, exports_list_float64_t *b, exports_list_float32_t *ret0, exports_list_float64_t *ret1) { + assert(0); // unimplemented +} diff --git a/tests/runtime/numbers/wasm.c b/tests/runtime/numbers/wasm.c index a638d61cd..fb23bf2f8 100644 --- a/tests/runtime/numbers/wasm.c +++ b/tests/runtime/numbers/wasm.c @@ -3,57 +3,57 @@ #include #include -uint8_t numbers_roundtrip_u8(uint8_t a) { +uint8_t exports_roundtrip_u8(uint8_t a) { return a; } -int8_t numbers_roundtrip_s8(int8_t a) { +int8_t exports_roundtrip_s8(int8_t a) { return a; } -uint16_t numbers_roundtrip_u16(uint16_t a) { +uint16_t exports_roundtrip_u16(uint16_t a) { return a; } -int16_t numbers_roundtrip_s16(int16_t a) { +int16_t exports_roundtrip_s16(int16_t a) { return a; } -uint32_t numbers_roundtrip_u32(uint32_t a) { +uint32_t exports_roundtrip_u32(uint32_t a) { return a; } -int32_t numbers_roundtrip_s32(int32_t a) { +int32_t exports_roundtrip_s32(int32_t a) { return a; } -uint64_t numbers_roundtrip_u64(uint64_t a) { +uint64_t exports_roundtrip_u64(uint64_t a) { return a; } -int64_t numbers_roundtrip_s64(int64_t a) { +int64_t exports_roundtrip_s64(int64_t a) { return a; } -float numbers_roundtrip_float32(float a) { +float exports_roundtrip_float32(float a) { return a; } -double numbers_roundtrip_float64(double a) { +double exports_roundtrip_float64(double a) { return a; } -uint32_t numbers_roundtrip_char(uint32_t a) { +uint32_t exports_roundtrip_char(uint32_t a) { return a; } static uint32_t SCALAR = 0; -void numbers_set_scalar(uint32_t a) { +void exports_set_scalar(uint32_t a) { SCALAR = a; } -uint32_t numbers_get_scalar(void) { +uint32_t exports_get_scalar(void) { return SCALAR; } diff --git a/tests/runtime/records/wasm.c b/tests/runtime/records/wasm.c index 4deca9ef1..c2d7e1740 100644 --- a/tests/runtime/records/wasm.c +++ b/tests/runtime/records/wasm.c @@ -66,38 +66,38 @@ void records_test_imports() { assert(t2.f0 == 1); } -void records_multiple_results(uint8_t *ret0, uint16_t *ret1) { +void exports_multiple_results(uint8_t *ret0, uint16_t *ret1) { *ret0 = 100; *ret1 = 200; } -void records_swap_tuple(records_tuple2_u8_u32_t *a, records_tuple2_u32_u8_t *b) { +void exports_swap_tuple(exports_tuple2_u8_u32_t *a, exports_tuple2_u32_u8_t *b) { b->f0 = a->f1; b->f1 = a->f0; } -records_f1_t records_roundtrip_flags1(records_f1_t a) { +exports_f1_t exports_roundtrip_flags1(exports_f1_t a) { return a; } -records_f2_t records_roundtrip_flags2(records_f2_t a) { +exports_f2_t exports_roundtrip_flags2(exports_f2_t a) { return a; } -void records_roundtrip_flags3(records_f8_t a, records_f16_t b, records_f32_t c, records_f64_t d, records_f8_t *ret0, records_f16_t *ret1, records_f32_t *ret2, records_f64_t *ret3) { +void exports_roundtrip_flags3(exports_flag8_t a, exports_flag16_t b, exports_flag32_t c, exports_flag64_t d, exports_flag8_t *ret0, exports_flag16_t *ret1, exports_flag32_t *ret2, exports_flag64_t *ret3) { *ret0 = a; *ret1 = b; *ret2 = c; *ret3 = d; } -void records_roundtrip_record1(records_r1_t *a, records_r1_t *ret0) { +void exports_roundtrip_record1(exports_r1_t *a, exports_r1_t *ret0) { *ret0 = *a; } -void records_tuple0(records_tuple0_t *a, records_tuple0_t *b) { +void exports_tuple0(exports_tuple0_t *a, exports_tuple0_t *b) { } -void records_tuple1(records_tuple1_u8_t *a, records_tuple1_u8_t *b) { +void exports_tuple1(exports_tuple1_u8_t *a, exports_tuple1_u8_t *b) { b->f0 = a->f0; } diff --git a/tests/runtime/strings/world.wit b/tests/runtime/strings/world.wit index d33f4ee4d..2ba567196 100644 --- a/tests/runtime/strings/world.wit +++ b/tests/runtime/strings/world.wit @@ -3,13 +3,9 @@ interface imports { return-unicode: func() -> string } -interface exports { - test-imports: func() +default world strings { + import imports: self.imports - roundtrip: func(s: string) -> string -} - -world strings { - import imports: imports - default export exports + export test-imports: func() + export roundtrip: func(s: string) -> string } diff --git a/tests/runtime/variants/wasm.c b/tests/runtime/variants/wasm.c index 04396198a..89d4f2134 100644 --- a/tests/runtime/variants/wasm.c +++ b/tests/runtime/variants/wasm.c @@ -133,14 +133,14 @@ void variants_test_imports() { } } -bool variants_roundtrip_option(float *a, uint8_t *ret0) { +bool exports_roundtrip_option(float *a, uint8_t *ret0) { if (a) { *ret0 = *a; } return a != NULL; } -bool variants_roundtrip_result(variants_result_u32_float32_t *a, double *ok, uint8_t *err) { +bool exports_roundtrip_result(exports_result_u32_float32_t *a, double *ok, uint8_t *err) { if (a->is_err) { *err = a->val.err; return false; @@ -150,22 +150,26 @@ bool variants_roundtrip_result(variants_result_u32_float32_t *a, double *ok, uin } } -variants_e1_t variants_roundtrip_enum(variants_e1_t a) { +exports_e1_t exports_roundtrip_enum(exports_e1_t a) { return a; } -bool variants_invert_bool(bool a) { +bool exports_invert_bool(bool a) { return !a; } -void variants_variant_casts(variants_casts_t *a, variants_casts_t *ret) { +void exports_variant_casts(exports_casts_t *a, exports_casts_t *ret) { *ret = *a; } -void variants_variant_zeros(variants_zeros_t *a, variants_zeros_t *b) { +void exports_variant_zeros(exports_zeros_t *a, exports_zeros_t *b) { *b = *a; } -void variants_variant_typedefs(uint32_t *a, variants_bool_typedef_t b, variants_result_typedef_t *c) { +void exports_variant_typedefs(uint32_t *a, exports_bool_typedef_t b, exports_result_typedef_t *c) { +} + +void exports_variant_enums(bool a, exports_result_void_void_t *b, exports_my_errno_t c, exports_tuple3_bool_result_void_void_my_errno_t *ret) { + assert(0); } From 71898e17b70299f6234b9fb706f2de60b4c8c32d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 13:12:43 -0800 Subject: [PATCH 19/25] Get the bare bones of teavm-java working Runtime tests will need some more work to get passing. --- crates/gen-guest-teavm-java/src/lib.rs | 125 ++++++++++++------- crates/gen-guest-teavm-java/tests/codegen.rs | 11 +- crates/test-helpers/Cargo.toml | 2 +- crates/test-helpers/runtime-macro/build.rs | 35 +++--- 4 files changed, 107 insertions(+), 66 deletions(-) diff --git a/crates/gen-guest-teavm-java/src/lib.rs b/crates/gen-guest-teavm-java/src/lib.rs index 7c45a14e3..9b184f6dc 100644 --- a/crates/gen-guest-teavm-java/src/lib.rs +++ b/crates/gen-guest-teavm-java/src/lib.rs @@ -9,8 +9,8 @@ use wit_bindgen_core::{ uwrite, uwriteln, wit_parser::{ abi::{AbiVariant, Bindgen, Bitcast, Instruction, LiftLower, WasmType}, - Case, Docs, Enum, Flags, FlagsRepr, Function, FunctionKind, Int, Interface, Record, - Result_, SizeAlign, Tuple, Type, TypeDefKind, TypeId, Union, Variant, World, + Case, Docs, Enum, Flags, FlagsRepr, Function, FunctionKind, Int, InterfaceId, Record, + Resolve, Result_, SizeAlign, Tuple, Type, TypeDefKind, TypeId, Union, Variant, WorldId, }, Files, InterfaceGenerator as _, Ns, WorldGenerator, }; @@ -52,6 +52,7 @@ pub struct TeaVmJava { needs_cleanup: bool, needs_result: bool, classes: HashMap, + sizes: SizeAlign, } impl TeaVmJava { @@ -60,59 +61,93 @@ impl TeaVmJava { format!("{world}World.") } - fn interface<'a>(&'a mut self, iface: &'a Interface, name: &'a str) -> InterfaceGenerator<'a> { - let mut sizes = SizeAlign::default(); - sizes.fill(iface); + fn interface<'a>(&'a mut self, resolve: &'a Resolve, name: &'a str) -> InterfaceGenerator<'a> { InterfaceGenerator { src: String::new(), stub: String::new(), gen: self, - iface, - sizes, + resolve, name, } } } impl WorldGenerator for TeaVmJava { - fn preprocess(&mut self, name: &str) { + fn preprocess(&mut self, resolve: &Resolve, name: &str) { self.name = name.to_string(); + self.sizes.fill(resolve); } - fn import(&mut self, name: &str, iface: &Interface, _files: &mut Files) { - let mut gen = self.interface(iface, name); - gen.types(); + fn import_interface( + &mut self, + resolve: &Resolve, + name: &str, + id: InterfaceId, + _files: &mut Files, + ) { + let mut gen = self.interface(resolve, name); + gen.types(id); - for func in iface.functions.iter() { + for (_, func) in resolve.interfaces[id].functions.iter() { gen.import(name, func); } gen.add_class(); } - fn export(&mut self, name: &str, iface: &Interface, _files: &mut Files) { - let mut gen = self.interface(iface, name); - gen.types(); + fn import_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let name = &resolve.worlds[world].name; + let mut gen = self.interface(resolve, name); - for func in iface.functions.iter() { + for (_, func) in funcs { + gen.import(name, func); + } + + gen.add_class(); + } + + fn export_interface( + &mut self, + resolve: &Resolve, + name: &str, + id: InterfaceId, + _files: &mut Files, + ) { + let mut gen = self.interface(resolve, name); + gen.types(id); + + for (_, func) in resolve.interfaces[id].functions.iter() { gen.export(func, Some(name)); } gen.add_class(); } - fn export_default(&mut self, name: &str, iface: &Interface, _files: &mut Files) { - let mut gen = self.interface(iface, name); - gen.types(); + fn export_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let name = &resolve.worlds[world].name; + let mut gen = self.interface(resolve, name); - for func in iface.functions.iter() { + for (_, func) in funcs { gen.export(func, None); } gen.add_class(); } - fn finish(&mut self, world: &World, files: &mut Files) { + fn finish(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) { + let world = &resolve.worlds[id]; let package = format!("wit_{}", world.name.to_snake_case()); let name = world.name.to_upper_camel_case(); @@ -131,7 +166,8 @@ impl WorldGenerator for TeaVmJava { ); let component_type = - wit_component::metadata::encode(world, wit_component::StringEncoding::UTF8) + wit_component::metadata::encode(resolve, id, wit_component::StringEncoding::UTF8) + .unwrap() .into_iter() .map(|byte| format!("{byte:02x}")) .collect::>() @@ -276,9 +312,8 @@ impl WorldGenerator for TeaVmJava { struct InterfaceGenerator<'a> { src: String, stub: String, - sizes: SizeAlign, gen: &'a mut TeaVmJava, - iface: &'a Interface, + resolve: &'a Resolve, name: &'a str, } @@ -340,7 +375,7 @@ impl InterfaceGenerator<'_> { func.params.iter().map(|(name, _)| name.clone()).collect(), ); - bindgen.gen.iface.call( + bindgen.gen.resolve.call( AbiVariant::GuestImport, LiftLower::LowerArgsLiftResults, func, @@ -362,7 +397,7 @@ impl InterfaceGenerator<'_> { let name = &func.name; - let sig = self.iface.wasm_signature(AbiVariant::GuestImport, func); + let sig = self.resolve.wasm_signature(AbiVariant::GuestImport, func); let result_type = match &sig.results[..] { [] => "void", @@ -398,7 +433,7 @@ impl InterfaceGenerator<'_> { } fn export(&mut self, func: &Function, interface_name: Option<&str>) { - let sig = self.iface.wasm_signature(AbiVariant::GuestExport, func); + let sig = self.resolve.wasm_signature(AbiVariant::GuestExport, func); let export_name = func.core_export_name(interface_name); @@ -408,7 +443,7 @@ impl InterfaceGenerator<'_> { (0..sig.params.len()).map(|i| format!("p{i}")).collect(), ); - bindgen.gen.iface.call( + bindgen.gen.resolve.call( AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, func, @@ -448,7 +483,7 @@ impl InterfaceGenerator<'_> { "# ); - if self.iface.guest_export_needs_post_return(func) { + if self.resolve.guest_export_needs_post_return(func) { let params = sig .results .iter() @@ -466,7 +501,7 @@ impl InterfaceGenerator<'_> { (0..sig.results.len()).map(|i| format!("p{i}")).collect(), ); - bindgen.gen.iface.post_return(func, &mut bindgen); + bindgen.gen.resolve.post_return(func, &mut bindgen); let src = bindgen.src; @@ -510,7 +545,7 @@ impl InterfaceGenerator<'_> { Type::Float64 => "double".into(), Type::String => "String".into(), Type::Id(id) => { - let ty = &self.iface.types[*id]; + let ty = &self.resolve.types[*id]; match &ty.kind { TypeDefKind::Type(ty) => self.type_name_with_qualifier(ty, qualifier), TypeDefKind::List(ty) => { @@ -583,7 +618,7 @@ impl InterfaceGenerator<'_> { Type::Float32 => "Float".into(), Type::Float64 => "Double".into(), Type::Id(id) => { - let def = &self.iface.types[*id]; + let def = &self.resolve.types[*id]; match &def.kind { TypeDefKind::Type(ty) => self.type_name_boxed(ty, qualifier), _ => self.type_name_with_qualifier(ty, qualifier), @@ -619,7 +654,7 @@ impl InterfaceGenerator<'_> { Type::Id(id) => *id, _ => return Some(ty), }; - match &self.iface.types[id].kind { + match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.non_empty_type(Some(t)).map(|_| ty), TypeDefKind::Record(r) => (!r.fields.is_empty()).then_some(ty), TypeDefKind::Tuple(t) => (!t.types.is_empty()).then_some(ty), @@ -668,8 +703,8 @@ impl InterfaceGenerator<'_> { } impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { - fn iface(&self) -> &'a Interface { - self.iface + fn resolve(&self) -> &'a Resolve { + self.resolve } fn type_record(&mut self, _id: TypeId, name: &str, record: &Record, docs: &Docs) { @@ -1132,7 +1167,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { fn emit( &mut self, - _iface: &Interface, + _resolve: &Resolve, inst: &Instruction<'_>, operands: &mut Vec, results: &mut Vec, @@ -1561,8 +1596,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { assert!(block_results.is_empty()); let op = &operands[0]; - let size = self.gen.sizes.size(element); - let align = self.gen.sizes.align(element); + let size = self.gen.gen.sizes.size(element); + let align = self.gen.gen.sizes.align(element); let address = self.locals.tmp("address"); let ty = self.gen.type_name(element); let index = self.locals.tmp("index"); @@ -1602,8 +1637,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { let length = &operands[1]; let array = self.locals.tmp("array"); let ty = self.gen.type_name(element); - let size = self.gen.sizes.size(element); - let align = self.gen.sizes.align(element); + let size = self.gen.gen.sizes.size(element); + let align = self.gen.gen.sizes.align(element); let index = self.locals.tmp("index"); let result = match &block_results[..] { @@ -1896,8 +1931,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { let address = &operands[0]; let length = &operands[1]; - let size = self.gen.sizes.size(element); - let align = self.gen.sizes.align(element); + let size = self.gen.gen.sizes.size(element); + let align = self.gen.gen.sizes.align(element); if !body.trim().is_empty() { let index = self.locals.tmp("index"); @@ -1921,7 +1956,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } } - fn return_pointer(&mut self, _iface: &Interface, size: usize, align: usize) -> String { + fn return_pointer(&mut self, size: usize, align: usize) -> String { self.gen.gen.return_area_size = self.gen.gen.return_area_size.max(size); self.gen.gen.return_area_align = self.gen.gen.return_area_align.max(align); format!("{}RETURN_AREA", self.gen.gen.qualifier()) @@ -1972,10 +2007,10 @@ impl Bindgen for FunctionBindgen<'_, '_> { } fn sizes(&self) -> &SizeAlign { - &self.gen.sizes + &self.gen.gen.sizes } - fn is_list_canonical(&self, _iface: &Interface, element: &Type) -> bool { + fn is_list_canonical(&self, _resolve: &Resolve, element: &Type) -> bool { is_primitive(element) } } diff --git a/crates/gen-guest-teavm-java/tests/codegen.rs b/crates/gen-guest-teavm-java/tests/codegen.rs index ffcaf446c..0b8fa84c6 100644 --- a/crates/gen-guest-teavm-java/tests/codegen.rs +++ b/crates/gen-guest-teavm-java/tests/codegen.rs @@ -4,18 +4,21 @@ use std::path::Path; use std::process::Command; macro_rules! codegen_test { - ($name:ident $test:tt) => { + // TODO: should get this test working + (use_across_interfaces $($x:tt)*) => {}; + + ($id:ident $name:tt $test:tt) => { #[test] - fn $name() { + fn $id() { test_helpers::run_world_codegen_test( "guest-teavm-java", $test.as_ref(), - |world, files| { + |resolve, world, files| { wit_bindgen_gen_guest_teavm_java::Opts { generate_stub: true, } .build() - .generate(world, files) + .generate(resolve, world, files) }, verify, ) diff --git a/crates/test-helpers/Cargo.toml b/crates/test-helpers/Cargo.toml index 6c5d6eb85..d53e47418 100644 --- a/crates/test-helpers/Cargo.toml +++ b/crates/test-helpers/Cargo.toml @@ -21,4 +21,4 @@ wasm-encoder = { workspace = true } default = ['guest-rust', 'guest-c', 'guest-teavm-java'] guest-rust = ['runtime-macro?/guest-rust'] guest-c = ['runtime-macro?/guest-c'] -guest-teavm-java = []#'runtime-macro?/guest-teavm-java'] +guest-teavm-java = ['runtime-macro?/guest-teavm-java'] diff --git a/crates/test-helpers/runtime-macro/build.rs b/crates/test-helpers/runtime-macro/build.rs index d979aa341..6d11bbbf7 100644 --- a/crates/test-helpers/runtime-macro/build.rs +++ b/crates/test-helpers/runtime-macro/build.rs @@ -1,7 +1,7 @@ use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; -use wit_bindgen_core::wit_parser::{PackageId, Resolve}; +use wit_bindgen_core::wit_parser::{Resolve, WorldId}; use wit_component::ComponentEncoder; #[cfg(feature = "guest-c")] @@ -19,13 +19,7 @@ fn guest_c( continue; } println!("cargo:rerun-if-changed={}", c_impl.display()); - let (resolve, pkg) = read_world(&test_dir); - let world = resolve.packages[pkg] - .documents - .iter() - .filter_map(|(_, doc)| resolve.documents[*doc].default_world) - .next() - .expect("no default world found"); + let (resolve, world) = read_world(&test_dir); let snake = resolve.worlds[world].name.replace("-", "_"); let mut files = Default::default(); let mut opts = wit_bindgen_gen_guest_c::Opts::default(); @@ -195,6 +189,8 @@ fn main() { #[cfg(feature = "guest-teavm-java")] { + use heck::*; + for test_dir in fs::read_dir("../../../tests/runtime").unwrap() { let test_dir = test_dir.unwrap().path(); let java_impl = test_dir.join("wasm.java"); @@ -203,25 +199,26 @@ fn main() { } println!("cargo:rerun-if-changed={}", java_impl.display()); - let world = read_world(&test_dir); - let out_dir = out_dir.join(format!("java-{}", world.name)); + let (resolve, world) = read_world(&test_dir); + let world_name = &resolve.worlds[world].name; + let out_dir = out_dir.join(format!("java-{}", world_name)); drop(fs::remove_dir_all(&out_dir)); let java_dir = out_dir.join("src/main/java"); let mut files = Default::default(); wit_bindgen_gen_guest_teavm_java::Opts::default() .build() - .generate(&world, &mut files); + .generate(&resolve, world, &mut files); - let package_dir = java_dir.join(&format!("wit_{}", world.name)); + let package_dir = java_dir.join(&format!("wit_{}", world_name)); fs::create_dir_all(&package_dir).unwrap(); for (file, contents) in files.iter() { let dst = package_dir.join(file); fs::write(dst, contents).unwrap(); } - let snake = world.name.to_snake_case(); - let upper = world.name.to_upper_camel_case(); + let snake = world_name.to_snake_case(); + let upper = world_name.to_upper_camel_case(); fs::copy( &java_impl, java_dir.join(&format!("wit_{snake}/{upper}Impl.java")), @@ -291,13 +288,19 @@ fn main() { std::fs::write(out_dir.join("wasms.rs"), src).unwrap(); } -fn read_world(dir: &Path) -> (Resolve, PackageId) { +fn read_world(dir: &Path) -> (Resolve, WorldId) { let mut resolve = Resolve::new(); let (pkg, files) = resolve.push_dir(dir).unwrap(); for file in files { println!("cargo:rerun-if-changed={}", file.display()); } - (resolve, pkg) + let world = resolve.packages[pkg] + .documents + .iter() + .filter_map(|(_, doc)| resolve.documents[*doc].default_world) + .next() + .expect("no default world found"); + (resolve, world) } #[cfg(feature = "guest-teavm-java")] From 821376bba476d87959af0558a5637a9f44e89443 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 13:29:34 -0800 Subject: [PATCH 20/25] Get CLI and markdown crate building again --- crates/gen-markdown/src/lib.rs | 182 +++++++++++++++++++++------------ src/bin/wit-bindgen.rs | 76 ++++++++++---- 2 files changed, 168 insertions(+), 90 deletions(-) diff --git a/crates/gen-markdown/src/lib.rs b/crates/gen-markdown/src/lib.rs index d04e3ae60..1d63efd2c 100644 --- a/crates/gen-markdown/src/lib.rs +++ b/crates/gen-markdown/src/lib.rs @@ -12,6 +12,7 @@ struct Markdown { src: Source, opts: Opts, hrefs: HashMap, + sizes: SizeAlign, } #[derive(Default, Debug, Clone)] @@ -29,28 +30,68 @@ impl Opts { } impl WorldGenerator for Markdown { - fn import(&mut self, name: &str, iface: &Interface, _files: &mut Files) { + fn preprocess(&mut self, resolve: &Resolve, _name: &str) { + self.sizes.fill(resolve); + } + + fn import_interface( + &mut self, + resolve: &Resolve, + name: &str, + id: InterfaceId, + _files: &mut Files, + ) { uwriteln!(self.src, "# Import interface `{name}`\n"); - let mut gen = self.interface(iface); - gen.types(); - gen.funcs(); + let mut gen = self.interface(resolve); + gen.types(id); + gen.funcs(id); } - fn export(&mut self, name: &str, iface: &Interface, _files: &mut Files) { + fn import_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let name = &resolve.worlds[world].name; + uwriteln!(self.src, "# Imported functions to world `{name}`\n"); + let mut gen = self.interface(resolve); + for (_, func) in funcs { + gen.func(func); + } + } + + fn export_interface( + &mut self, + resolve: &Resolve, + name: &str, + id: InterfaceId, + _files: &mut Files, + ) { uwriteln!(self.src, "# Export interface `{name}`\n"); - let mut gen = self.interface(iface); - gen.types(); - gen.funcs(); + let mut gen = self.interface(resolve); + gen.types(id); + gen.funcs(id); } - fn export_default(&mut self, name: &str, iface: &Interface, _files: &mut Files) { - uwriteln!(self.src, "# Default exported interface of `{name}`\n"); - let mut gen = self.interface(iface); - gen.types(); - gen.funcs(); + fn export_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + let name = &resolve.worlds[world].name; + uwriteln!(self.src, "# Exported functions from world `{name}`\n"); + let mut gen = self.interface(resolve); + for (_, func) in funcs { + gen.func(func); + } } - fn finish(&mut self, world: &World, files: &mut Files) { + fn finish(&mut self, resolve: &Resolve, world: WorldId, files: &mut Files) { + let world = &resolve.worlds[world]; let parser = Parser::new(&self.src); let mut events = Vec::new(); for event in parser { @@ -74,13 +115,10 @@ impl WorldGenerator for Markdown { } impl Markdown { - fn interface<'a>(&'a mut self, iface: &'a Interface) -> InterfaceGenerator<'_> { - let mut sizes = SizeAlign::default(); - sizes.fill(iface); + fn interface<'a>(&'a mut self, resolve: &'a Resolve) -> InterfaceGenerator<'_> { InterfaceGenerator { gen: self, - iface, - sizes, + resolve, types_header_printed: false, } } @@ -88,61 +126,65 @@ impl Markdown { struct InterfaceGenerator<'a> { gen: &'a mut Markdown, - iface: &'a Interface, - sizes: SizeAlign, + resolve: &'a Resolve, types_header_printed: bool, } impl InterfaceGenerator<'_> { - fn funcs(&mut self) { - if self.iface.functions.is_empty() { + fn funcs(&mut self, id: InterfaceId) { + let iface = &self.resolve.interfaces[id]; + if iface.functions.is_empty() { return; } self.push_str("## Functions\n\n"); - for func in self.iface.functions.iter() { - self.push_str("----\n\n"); - self.push_str(&format!( - "#### `", - func.name.to_snake_case() - )); - self.gen - .hrefs - .insert(func.name.clone(), format!("#{}", func.name.to_snake_case())); - self.push_str(&func.name); - self.push_str("` "); - self.push_str("\n\n"); - self.docs(&func.docs); - - if func.params.len() > 0 { - self.push_str("##### Params\n\n"); - for (name, ty) in func.params.iter() { - self.push_str(&format!( - "- `{}`: ", - name, - f = func.name.to_snake_case(), - p = name.to_snake_case(), - )); - self.print_ty(ty, false); - self.push_str("\n"); - } - } + for (_name, func) in iface.functions.iter() { + self.func(func); + } + } - if func.results.len() > 0 { - self.push_str("##### Results\n\n"); - for (i, ty) in func.results.iter_types().enumerate() { - self.push_str(&format!( - "- `{}{i}`: ", - "result", - f = func.name.to_snake_case(), - p = "result", - )); - self.print_ty(ty, false); - self.push_str("\n"); - } + fn func(&mut self, func: &Function) { + self.push_str("----\n\n"); + self.push_str(&format!( + "#### `", + func.name.to_snake_case() + )); + self.gen + .hrefs + .insert(func.name.clone(), format!("#{}", func.name.to_snake_case())); + self.push_str(&func.name); + self.push_str("` "); + self.push_str("\n\n"); + self.docs(&func.docs); + + if func.params.len() > 0 { + self.push_str("##### Params\n\n"); + for (name, ty) in func.params.iter() { + self.push_str(&format!( + "- `{}`: ", + name, + f = func.name.to_snake_case(), + p = name.to_snake_case(), + )); + self.print_ty(ty, false); + self.push_str("\n"); } + } - self.push_str("\n"); + if func.results.len() > 0 { + self.push_str("##### Results\n\n"); + for (i, ty) in func.results.iter_types().enumerate() { + self.push_str(&format!( + "- `{}{i}`: ", + "result", + f = func.name.to_snake_case(), + p = "result", + )); + self.print_ty(ty, false); + self.push_str("\n"); + } } + + self.push_str("\n"); } fn push_str(&mut self, s: &str) { @@ -165,7 +207,7 @@ impl InterfaceGenerator<'_> { Type::Char => self.push_str("`char`"), Type::String => self.push_str("`string`"), Type::Id(id) => { - let ty = &self.iface.types[*id]; + let ty = &self.resolve.types[*id]; if !skip_name { if let Some(name) = &ty.name { self.push_str("[`"); @@ -259,6 +301,7 @@ impl InterfaceGenerator<'_> { self.push_str("stream"); } }, + TypeDefKind::Unknown => unreachable!(), } } } @@ -293,14 +336,17 @@ impl InterfaceGenerator<'_> { fn print_type_info(&mut self, ty: TypeId, docs: &Docs) { self.docs(docs); self.push_str("\n"); - self.push_str(&format!("Size: {}, ", self.sizes.size(&Type::Id(ty)))); - self.push_str(&format!("Alignment: {}\n", self.sizes.align(&Type::Id(ty)))); + self.push_str(&format!("Size: {}, ", self.gen.sizes.size(&Type::Id(ty)))); + self.push_str(&format!( + "Alignment: {}\n", + self.gen.sizes.align(&Type::Id(ty)) + )); } } impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { - fn iface(&self) -> &'a Interface { - self.iface + fn resolve(&self) -> &'a Resolve { + self.resolve } fn type_record(&mut self, id: TypeId, name: &str, record: &Record, docs: &Docs) { diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 696c1800a..791957e4a 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -1,9 +1,9 @@ use anyhow::{anyhow, bail, Context, Result}; use clap::Parser; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use wit_bindgen_core::component::ComponentGenerator; use wit_bindgen_core::{wit_parser, Files, WorldGenerator}; -use wit_parser::World; +use wit_parser::{Resolve, UnresolvedPackage}; /// Helper for passing VERSION to opt. /// If CARGO_VERSION_INFO is set, use it, otherwise use CARGO_PKG_VERSION. @@ -81,25 +81,16 @@ enum GuestGenerator { #[derive(Debug, Parser)] struct WorldOpt { - /// Generate bindings for the WIT document. - #[clap(value_name = "DOCUMENT", value_parser = parse_world)] - wit: World, -} - -fn parse_world(s: &str) -> Result { - let path = Path::new(s); - if !path.is_file() { - bail!("wit file `{}` does not exist", path.display()); - } - - let world = World::parse_file(&path) - .with_context(|| format!("failed to parse wit file `{}`", path.display())) - .map_err(|e| { - eprintln!("{e:?}"); - e - })?; - - Ok(world) + /// WIT document to generate bindings for. + #[clap(value_name = "DOCUMENT")] + wit: PathBuf, + + /// World within the WIT document specified to generate bindings for. + /// + /// This can either be `foo` which is the default world in document `foo` or + /// it's `foo.bar` which is the world named `bar` within document `foo`. + #[clap(short, long)] + world: Option, } #[derive(Debug, Parser)] @@ -179,7 +170,48 @@ fn gen_world( opts: WorldOpt, files: &mut Files, ) -> Result<()> { - generator.generate(&opts.wit, files); + let mut resolve = Resolve::default(); + let pkg = if opts.wit.is_dir() { + resolve.push_dir(&opts.wit)?.0 + } else { + resolve.push( + UnresolvedPackage::parse_file(&opts.wit)?, + &Default::default(), + )? + }; + let world = match &opts.world { + Some(world) => { + let mut parts = world.splitn(2, '.'); + let doc = parts.next().unwrap(); + let world = parts.next(); + let doc = *resolve.packages[pkg] + .documents + .get(doc) + .ok_or_else(|| anyhow!("no document named `{doc}` in package"))?; + match world { + Some(name) => *resolve.documents[doc] + .worlds + .get(name) + .ok_or_else(|| anyhow!("no world named `{name}` in document"))?, + None => resolve.documents[doc] + .default_world + .ok_or_else(|| anyhow!("no default world in document"))?, + } + } + None => { + let mut docs = resolve.packages[pkg].documents.iter(); + let (_, doc) = docs + .next() + .ok_or_else(|| anyhow!("no documents found in package"))?; + if docs.next().is_some() { + bail!("multiple documents found in package, specify which to bind with `--world` argument") + } + resolve.documents[*doc] + .default_world + .ok_or_else(|| anyhow!("no default world in document"))? + } + }; + generator.generate(&resolve, world, files); Ok(()) } From badf2e1fc6bfbd03b0e3766b5fcdb0132c9c2997 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 13:37:47 -0800 Subject: [PATCH 21/25] Update build of demo --- Cargo.lock | 1 + crates/wit-bindgen-demo/Cargo.toml | 3 ++- crates/wit-bindgen-demo/main.ts | 5 +++- crates/wit-bindgen-demo/src/lib.rs | 31 +++++++++++++++++----- crates/wit-bindgen-demo/{ => wit}/demo.wit | 4 +-- 5 files changed, 33 insertions(+), 11 deletions(-) rename crates/wit-bindgen-demo/{ => wit}/demo.wit (89%) diff --git a/Cargo.lock b/Cargo.lock index 9b36524f2..bb794bafe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -882,6 +882,7 @@ version = "0.3.0" dependencies = [ "anyhow", "test-helpers", + "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmprinter", "wit-bindgen-core", "wit-bindgen-gen-guest-c", diff --git a/crates/wit-bindgen-demo/Cargo.toml b/crates/wit-bindgen-demo/Cargo.toml index 30a5f9133..e6c283520 100644 --- a/crates/wit-bindgen-demo/Cargo.toml +++ b/crates/wit-bindgen-demo/Cargo.toml @@ -20,5 +20,6 @@ wit-bindgen-gen-guest-teavm-java = { workspace = true } wit-bindgen-gen-markdown = { workspace = true } wit-bindgen-guest-rust = { workspace = true, features = ['default'] } wasmprinter = { workspace = true } -wit-component = { workspace = true } +wit-component = { workspace = true, features = ['dummy-module'] } test-helpers = { path = '../test-helpers', default-features = false } +wasm-encoder = { workspace = true } diff --git a/crates/wit-bindgen-demo/main.ts b/crates/wit-bindgen-demo/main.ts index 0fd7d0db7..6668d01a0 100644 --- a/crates/wit-bindgen-demo/main.ts +++ b/crates/wit-bindgen-demo/main.ts @@ -1,4 +1,7 @@ -import { render, Options } from './demo.js'; +import { demo } from './demo.js'; +import { Options } from './exports/demo.js'; + +const { render } = demo; class Editor { input: HTMLTextAreaElement; diff --git a/crates/wit-bindgen-demo/src/lib.rs b/crates/wit-bindgen-demo/src/lib.rs index 98cac65a7..66274d3ed 100644 --- a/crates/wit-bindgen-demo/src/lib.rs +++ b/crates/wit-bindgen-demo/src/lib.rs @@ -1,11 +1,12 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use std::path::Path; use std::sync::Once; +use wasm_encoder::{CustomSection, Encode, Section}; use wit_bindgen_core::component::ComponentGenerator; -use wit_bindgen_core::wit_parser::{Document, World}; +use wit_bindgen_core::wit_parser::{Resolve, UnresolvedPackage}; use wit_bindgen_core::{Files, WorldGenerator}; -wit_bindgen_guest_rust::generate!("demo.wit"); +wit_bindgen_guest_rust::generate!("demo"); struct Demo; @@ -49,10 +50,19 @@ fn init() { } fn render(lang: demo::Lang, wit: &str, files: &mut Files, options: &demo::Options) -> Result<()> { - let world = Document::parse(Path::new("input"), &wit)?.into_world()?; + let mut resolve = Resolve::default(); + let pkg = resolve.push( + UnresolvedPackage::parse(Path::new("input"), &wit)?, + &Default::default(), + )?; + let (_, doc) = resolve.packages[pkg].documents.iter().next().unwrap(); + let doc = &resolve.documents[*doc]; + let world = doc + .default_world + .ok_or_else(|| anyhow!("no `default world` specified in document"))?; let gen_world = |mut gen: Box, files: &mut Files| { - gen.generate(&world, files); + gen.generate(&resolve, world, files); }; // This generator takes a component as input as opposed to an `Interface`. @@ -63,10 +73,17 @@ fn render(lang: demo::Lang, wit: &str, files: &mut Files, options: &demo::Option // interface and dummy module. Finally this component is fed into the host // generator which gives us the files we want. let gen_component = |mut gen: Box, files: &mut Files| { - let dummy = test_helpers::dummy_module(&world); + let mut dummy = wit_component::dummy_module(&resolve, world); + let metadata = + wit_component::metadata::encode(&resolve, world, wit_component::StringEncoding::UTF8)?; + let section = CustomSection { + name: "component-type", + data: &metadata, + }; + dummy.push(section.id()); + section.encode(&mut dummy); let wasm = wit_component::ComponentEncoder::default() .module(&dummy)? - .world(world.clone(), wit_component::StringEncoding::UTF8)? .encode()?; wit_bindgen_core::component::generate(&mut *gen, "input", &wasm, files) }; diff --git a/crates/wit-bindgen-demo/demo.wit b/crates/wit-bindgen-demo/wit/demo.wit similarity index 89% rename from crates/wit-bindgen-demo/demo.wit rename to crates/wit-bindgen-demo/wit/demo.wit index eef28c216..95c8bc1ea 100644 --- a/crates/wit-bindgen-demo/demo.wit +++ b/crates/wit-bindgen-demo/wit/demo.wit @@ -1,10 +1,10 @@ -world demo { +default world demo { import console: interface { log: func(msg: string) error: func(msg: string) } - default export interface { + export demo: interface { type files = list> enum lang { From 85defb0329f8d068f543a8c4a79374cfb482f019 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Jan 2023 13:41:06 -0800 Subject: [PATCH 22/25] Use updated crates.io dep for wit-component --- Cargo.lock | 70 ++++++++++++++---------------------------------------- Cargo.toml | 6 +---- 2 files changed, 19 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb794bafe..78c8ad457 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -547,8 +547,8 @@ version = "0.3.0" dependencies = [ "codegen-macro", "runtime-macro", - "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wat 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder", + "wat", "wit-bindgen-core", "wit-component", "wit-parser", @@ -705,14 +705,6 @@ dependencies = [ "leb128", ] -[[package]] -name = "wasm-encoder" -version = "0.21.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" -dependencies = [ - "leb128", -] - [[package]] name = "wasmparser" version = "0.97.0" @@ -723,15 +715,6 @@ dependencies = [ "url", ] -[[package]] -name = "wasmparser" -version = "0.97.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" -dependencies = [ - "indexmap", - "url", -] - [[package]] name = "wasmprinter" version = "0.2.46" @@ -739,7 +722,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595cca929e47a7bec3c941b5a8e133f51b17e6d9dd8c82ab97902196f5a07b42" dependencies = [ "anyhow", - "wasmparser 0.97.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", ] [[package]] @@ -761,8 +744,8 @@ dependencies = [ "serde", "target-lexicon", "thiserror", - "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.97.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder", + "wasmparser", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -776,7 +759,7 @@ dependencies = [ "cranelift-entity", "serde", "thiserror", - "wasmparser 0.97.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", ] [[package]] @@ -788,18 +771,7 @@ dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wast" -version = "51.0.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" -dependencies = [ - "leb128", - "memchr", - "unicode-width", - "wasm-encoder 0.21.0 (git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs)", + "wasm-encoder", ] [[package]] @@ -808,15 +780,7 @@ version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd18c1168d7e8743d9b4f713c0203924f5dcc4a3983eb5e584de9614f9fccde" dependencies = [ - "wast 51.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wat" -version = "1.0.53" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" -dependencies = [ - "wast 51.0.0 (git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs)", + "wast", ] [[package]] @@ -856,7 +820,7 @@ version = "0.3.0" dependencies = [ "anyhow", "clap", - "wat 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", + "wat", "wit-bindgen-core", "wit-bindgen-gen-guest-c", "wit-bindgen-gen-guest-rust", @@ -882,7 +846,7 @@ version = "0.3.0" dependencies = [ "anyhow", "test-helpers", - "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder", "wasmprinter", "wit-bindgen-core", "wit-bindgen-gen-guest-c", @@ -902,7 +866,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder", "wit-bindgen-core", "wit-component", ] @@ -988,23 +952,25 @@ dependencies = [ [[package]] name = "wit-component" version = "0.4.1" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b38a4c9dbf15093437b69a37d44046434440a946ff521d9e2fc71d35a84713" dependencies = [ "anyhow", "bitflags", "indexmap", "log", "url", - "wasm-encoder 0.21.0 (git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs)", - "wasmparser 0.97.0 (git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs)", - "wat 1.0.53 (git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs)", + "wasm-encoder", + "wasmparser", + "wat", "wit-parser", ] [[package]] name = "wit-parser" version = "0.4.0" -source = "git+https://github.com/alexcrichton/wasm-tools?branch=fix-wit-component-bugs#5a74501d5af11900b745c57fccd98fbc7dd3834d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02cfa79275011530f37e0e164183c606bae1cdc466ea90bcd364d50605486a4d" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index 77ece975e..f400c7012 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ wasmparser = "0.97.0" wasm-encoder = "0.21.0" wat = "1.0.53" wit-parser = "0.4.0" -wit-component = "0.4.0" +wit-component = "0.4.1" wit-bindgen-core = { path = 'crates/bindgen-core', version = '0.3.0' } wit-bindgen-gen-guest-c = { path = 'crates/gen-guest-c', version = '0.3.0' } @@ -58,7 +58,3 @@ wit-bindgen-gen-markdown = { path = 'crates/gen-markdown', features = ['clap'] } wit-bindgen-gen-guest-teavm-java = { path = 'crates/gen-guest-teavm-java', features = ['clap'] } wat = { workspace = true } wit-component = { workspace = true } - -[patch.crates-io] -wit-component = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'fix-wit-component-bugs' } -wit-parser = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'fix-wit-component-bugs' } From 25c44e658f5b4d45fbda6394313ec2d855b74a70 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 19 Jan 2023 23:18:59 -0700 Subject: [PATCH 23/25] fix Java runtime test regressions (#1) Signed-off-by: Joel Dice Signed-off-by: Joel Dice --- .github/workflows/main.yml | 2 +- crates/test-helpers/runtime-macro/build.rs | 33 +++++-- tests/runtime/lists/ExportsImpl.java | 97 +++++++++++++++++++ .../lists/{wasm.java => ListsImpl.java} | 70 +------------ tests/runtime/numbers/ExportsImpl.java | 57 +++++++++++ .../numbers/{wasm.java => NumbersImpl.java} | 61 +----------- tests/runtime/records/ExportsImpl.java | 42 ++++++++ .../records/{wasm.java => RecordsImpl.java} | 34 ------- .../smoke/{wasm.java => SmokeImpl.java} | 0 tests/runtime/variants/ExportsImpl.java | 49 ++++++++++ .../variants/{wasm.java => VariantsImpl.java} | 34 ------- 11 files changed, 276 insertions(+), 203 deletions(-) create mode 100644 tests/runtime/lists/ExportsImpl.java rename tests/runtime/lists/{wasm.java => ListsImpl.java} (74%) create mode 100644 tests/runtime/numbers/ExportsImpl.java rename tests/runtime/numbers/{wasm.java => NumbersImpl.java} (73%) create mode 100644 tests/runtime/records/ExportsImpl.java rename tests/runtime/records/{wasm.java => RecordsImpl.java} (76%) rename tests/runtime/smoke/{wasm.java => SmokeImpl.java} (100%) create mode 100644 tests/runtime/variants/ExportsImpl.java rename tests/runtime/variants/{wasm.java => VariantsImpl.java} (82%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bca1e82f9..abfeb69ba 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,7 +56,7 @@ jobs: java-version: '18' distribution: 'adopt' - run: cargo test --workspace - - run: cargo test --p wit-bindgen-gen-host-js --test runtime --features runtime-tests + - run: cargo test -p wit-bindgen-gen-host-js --test runtime --features runtime-tests rustfmt: name: Rustfmt diff --git a/crates/test-helpers/runtime-macro/build.rs b/crates/test-helpers/runtime-macro/build.rs index 6d11bbbf7..230d9667b 100644 --- a/crates/test-helpers/runtime-macro/build.rs +++ b/crates/test-helpers/runtime-macro/build.rs @@ -193,11 +193,23 @@ fn main() { for test_dir in fs::read_dir("../../../tests/runtime").unwrap() { let test_dir = test_dir.unwrap().path(); - let java_impl = test_dir.join("wasm.java"); - if !java_impl.exists() { + let java_impls = fs::read_dir(&test_dir) + .unwrap() + .filter_map(|entry| { + let path = entry.unwrap().path(); + if let Some("java") = path.extension().map(|ext| ext.to_str().unwrap()) { + Some(path) + } else { + None + } + }) + .collect::>(); + if java_impls.is_empty() { continue; } - println!("cargo:rerun-if-changed={}", java_impl.display()); + for java_impl in &java_impls { + println!("cargo:rerun-if-changed={}", java_impl.display()); + } let (resolve, world) = read_world(&test_dir); let world_name = &resolve.worlds[world].name; @@ -219,17 +231,22 @@ fn main() { let snake = world_name.to_snake_case(); let upper = world_name.to_upper_camel_case(); - fs::copy( - &java_impl, - java_dir.join(&format!("wit_{snake}/{upper}Impl.java")), - ) - .unwrap(); + for java_impl in &java_impls { + fs::copy( + &java_impl, + java_dir + .join(&format!("wit_{snake}")) + .join(java_impl.file_name().unwrap()), + ) + .unwrap(); + } fs::write( out_dir.join("pom.xml"), pom_xml(&[ &format!("wit_{snake}.{upper}"), &format!("wit_{snake}.{upper}World"), &format!("wit_{snake}.Imports"), + &format!("wit_{snake}.Exports"), ]), ) .unwrap(); diff --git a/tests/runtime/lists/ExportsImpl.java b/tests/runtime/lists/ExportsImpl.java new file mode 100644 index 000000000..b970996d9 --- /dev/null +++ b/tests/runtime/lists/ExportsImpl.java @@ -0,0 +1,97 @@ +package wit_lists; + +import static wit_lists.ListsImpl.expect; + +import java.util.ArrayList; + +import wit_lists.ListsWorld.Tuple2; + +public class ExportsImpl { + public static void emptyListParam(byte[] a) { + expect(a.length == 0); + } + + public static void emptyStringParam(String a) { + expect(a.length() == 0); + } + + public static byte[] emptyListResult() { + return new byte[0]; + } + + public static String emptyStringResult() { + return ""; + } + + public static void listParam(byte[] a) { + expect(a.length == 4); + expect(a[0] == 1); + expect(a[1] == 2); + expect(a[2] == 3); + expect(a[3] == 4); + } + + public static void listParam2(String a) { + expect(a.equals("foo")); + } + + public static void listParam3(ArrayList a) { + expect(a.size() == 3); + expect(a.get(0).equals("foo")); + expect(a.get(1).equals("bar")); + expect(a.get(2).equals("baz")); + } + + public static void listParam4(ArrayList> a) { + expect(a.size() == 2); + expect(a.get(0).size() == 2); + expect(a.get(1).size() == 1); + + expect(a.get(0).get(0).equals("foo")); + expect(a.get(0).get(1).equals("bar")); + expect(a.get(1).get(0).equals("baz")); + } + + public static byte[] listResult() { + return new byte[] { (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5 }; + } + + public static String listResult2() { + return "hello!"; + } + + public static ArrayList listResult3() { + return new ArrayList() {{ + add("hello,"); + add("world!"); + }}; + } + + public static byte[] listRoundtrip(byte[] a) { + return a; + } + + public static String stringRoundtrip(String a) { + return a; + } + + public static Tuple2 listMinmax8(byte[] a, byte[] b) { + return new Tuple2<>(a, b); + } + + public static Tuple2 listMinmax16(short[] a, short[] b) { + return new Tuple2<>(a, b); + } + + public static Tuple2 listMinmax32(int[] a, int[] b) { + return new Tuple2<>(a, b); + } + + public static Tuple2 listMinmax64(long[] a, long[] b) { + return new Tuple2<>(a, b); + } + + public static Tuple2 listMinmaxFloat(float[] a, double[] b) { + return new Tuple2<>(a, b); + } +} diff --git a/tests/runtime/lists/wasm.java b/tests/runtime/lists/ListsImpl.java similarity index 74% rename from tests/runtime/lists/wasm.java rename to tests/runtime/lists/ListsImpl.java index 4559c49b2..c9226dabd 100644 --- a/tests/runtime/lists/wasm.java +++ b/tests/runtime/lists/ListsImpl.java @@ -142,75 +142,7 @@ public static void testImports() { } } - public static void emptyListParam(byte[] a) { - expect(a.length == 0); - } - - public static void emptyStringParam(String a) { - expect(a.length() == 0); - } - - public static byte[] emptyListResult() { - return new byte[0]; - } - - public static String emptyStringResult() { - return ""; - } - - public static void listParam(byte[] a) { - expect(a.length == 4); - expect(a[0] == 1); - expect(a[1] == 2); - expect(a[2] == 3); - expect(a[3] == 4); - } - - public static void listParam2(String a) { - expect(a.equals("foo")); - } - - public static void listParam3(ArrayList a) { - expect(a.size() == 3); - expect(a.get(0).equals("foo")); - expect(a.get(1).equals("bar")); - expect(a.get(2).equals("baz")); - } - - public static void listParam4(ArrayList> a) { - expect(a.size() == 2); - expect(a.get(0).size() == 2); - expect(a.get(1).size() == 1); - - expect(a.get(0).get(0).equals("foo")); - expect(a.get(0).get(1).equals("bar")); - expect(a.get(1).get(0).equals("baz")); - } - - public static byte[] listResult() { - return new byte[] { (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5 }; - } - - public static String listResult2() { - return "hello!"; - } - - public static ArrayList listResult3() { - return new ArrayList() {{ - add("hello,"); - add("world!"); - }}; - } - - public static byte[] listRoundtrip(byte[] a) { - return a; - } - - public static String stringRoundtrip(String a) { - return a; - } - - private static void expect(boolean v) { + public static void expect(boolean v) { if (!v) { throw new AssertionError(); } diff --git a/tests/runtime/numbers/ExportsImpl.java b/tests/runtime/numbers/ExportsImpl.java new file mode 100644 index 000000000..975c50efc --- /dev/null +++ b/tests/runtime/numbers/ExportsImpl.java @@ -0,0 +1,57 @@ +package wit_numbers; + +public class ExportsImpl { + public static byte roundtripU8(byte a) { + return a; + } + + public static byte roundtripS8(byte a) { + return a; + } + + public static short roundtripU16(short a) { + return a; + } + + public static short roundtripS16(short a) { + return a; + } + + public static int roundtripU32(int a) { + return a; + } + + public static int roundtripS32(int a) { + return a; + } + + public static long roundtripU64(long a) { + return a; + } + + public static long roundtripS64(long a) { + return a; + } + + public static float roundtripFloat32(float a) { + return a; + } + + public static double roundtripFloat64(double a) { + return a; + } + + public static int roundtripChar(int a) { + return a; + } + + private static int scalar = 0; + + public static void setScalar(int a) { + scalar = a; + } + + public static int getScalar() { + return scalar; + } +} diff --git a/tests/runtime/numbers/wasm.java b/tests/runtime/numbers/NumbersImpl.java similarity index 73% rename from tests/runtime/numbers/wasm.java rename to tests/runtime/numbers/NumbersImpl.java index 7b4440922..47b343974 100644 --- a/tests/runtime/numbers/wasm.java +++ b/tests/runtime/numbers/NumbersImpl.java @@ -1,58 +1,10 @@ package wit_numbers; public class NumbersImpl { - public static byte roundtripU8(byte a) { - return a; - } - - public static byte roundtripS8(byte a) { - return a; - } - - public static short roundtripU16(short a) { - return a; - } - - public static short roundtripS16(short a) { - return a; - } - - public static int roundtripU32(int a) { - return a; - } - - public static int roundtripS32(int a) { - return a; - } - - public static long roundtripU64(long a) { - return a; - } - - public static long roundtripS64(long a) { - return a; - } - - public static float roundtripFloat32(float a) { - return a; - } - - public static double roundtripFloat64(double a) { - return a; - } - - public static int roundtripChar(int a) { - return a; - } - - private static int scalar = 0; - - public static void setScalar(int a) { - scalar = a; - } - - public static int getScalar() { - return scalar; + private static void expect(boolean v) { + if (!v) { + throw new AssertionError(); + } } public static void testImports() { @@ -108,9 +60,4 @@ public static void testImports() { expect(Imports.getScalar() == 4); } - private static void expect(boolean v) { - if (!v) { - throw new AssertionError(); - } - } } diff --git a/tests/runtime/records/ExportsImpl.java b/tests/runtime/records/ExportsImpl.java new file mode 100644 index 000000000..cfce3df37 --- /dev/null +++ b/tests/runtime/records/ExportsImpl.java @@ -0,0 +1,42 @@ +package wit_records; + +import wit_records.RecordsWorld.Tuple0; +import wit_records.RecordsWorld.Tuple1; +import wit_records.RecordsWorld.Tuple2; +import wit_records.RecordsWorld.Tuple4; + +public class ExportsImpl { + public static Tuple2 multipleResults() { + return new Tuple2<>((byte) 100, (short) 200); + } + + public static Tuple2 swapTuple(Tuple2 tuple) { + return new Tuple2<>(tuple.f1, tuple.f0); + } + + public static Exports.F1 roundtripFlags1(Exports.F1 a) { + return a; + } + + public static Exports.F2 roundtripFlags2(Exports.F2 a) { + return a; + } + + public static Tuple4 roundtripFlags3 + (Exports.Flag8 a, Exports.Flag16 b, Exports.Flag32 c, Exports.Flag64 d) + { + return new Tuple4<>(a, b, c, d); + } + + public static Exports.R1 roundtripRecord1(Exports.R1 a) { + return a; + } + + public static Tuple0 tuple0(Tuple0 a) { + return a; + } + + public static Tuple1 tuple1(Tuple1 a) { + return a; + } +} diff --git a/tests/runtime/records/wasm.java b/tests/runtime/records/RecordsImpl.java similarity index 76% rename from tests/runtime/records/wasm.java rename to tests/runtime/records/RecordsImpl.java index 6685e2642..e6088868c 100644 --- a/tests/runtime/records/wasm.java +++ b/tests/runtime/records/RecordsImpl.java @@ -67,40 +67,6 @@ public static void testImports() { } } - public static Tuple2 multipleResults() { - return new Tuple2<>((byte) 100, (short) 200); - } - - public static Tuple2 swapTuple(Tuple2 tuple) { - return new Tuple2<>(tuple.f1, tuple.f0); - } - - public static Records.F1 roundtripFlags1(Records.F1 a) { - return a; - } - - public static Records.F2 roundtripFlags2(Records.F2 a) { - return a; - } - - public static Tuple4 roundtripFlags3 - (Records.F8 a, Records.F16 b, Records.F32 c, Records.F64 d) - { - return new Tuple4<>(a, b, c, d); - } - - public static Records.R1 roundtripRecord1(Records.R1 a) { - return a; - } - - public static Tuple0 tuple0(Tuple0 a) { - return a; - } - - public static Tuple1 tuple1(Tuple1 a) { - return a; - } - private static void expect(boolean v) { if (!v) { throw new AssertionError(); diff --git a/tests/runtime/smoke/wasm.java b/tests/runtime/smoke/SmokeImpl.java similarity index 100% rename from tests/runtime/smoke/wasm.java rename to tests/runtime/smoke/SmokeImpl.java diff --git a/tests/runtime/variants/ExportsImpl.java b/tests/runtime/variants/ExportsImpl.java new file mode 100644 index 000000000..153809935 --- /dev/null +++ b/tests/runtime/variants/ExportsImpl.java @@ -0,0 +1,49 @@ +package wit_variants; + +import wit_variants.VariantsWorld.Result; +import wit_variants.VariantsWorld.Tuple0; +import wit_variants.VariantsWorld.Tuple3; +import wit_variants.VariantsWorld.Tuple4; +import wit_variants.VariantsWorld.Tuple6; + +public class ExportsImpl { + public static Byte roundtripOption(Float a) { + return a == null ? null : (byte) (float) a; + } + + public static Result roundtripResult(Result a) { + switch (a.tag) { + case Result.OK: return Result.ok((double) a.getOk()); + case Result.ERR: return Result.err((byte) (float) a.getErr()); + default: throw new AssertionError(); + } + } + + public static Exports.E1 roundtripEnum(Exports.E1 a) { + return a; + } + + public static boolean invertBool(boolean a) { + return !a; + } + + public static Tuple6 + variantCasts(Tuple6 a) + { + return a; + } + + public static Tuple4 + variantZeros(Tuple4 a) + { + return a; + } + + public static void variantTypedefs(Integer a, boolean b, Result c) { } + + public static Tuple3, Exports.MyErrno> + variantEnums(boolean a, Result b, Exports.MyErrno c) + { + return new Tuple3<>(a, b, c); + } +} diff --git a/tests/runtime/variants/wasm.java b/tests/runtime/variants/VariantsImpl.java similarity index 82% rename from tests/runtime/variants/wasm.java rename to tests/runtime/variants/VariantsImpl.java index 1b51fdc81..f458083bd 100644 --- a/tests/runtime/variants/wasm.java +++ b/tests/runtime/variants/VariantsImpl.java @@ -106,40 +106,6 @@ public static void testImports() { } } - public static Byte roundtripOption(Float a) { - return a == null ? null : (byte) (float) a; - } - - public static Result roundtripResult(Result a) { - switch (a.tag) { - case Result.OK: return Result.ok((double) a.getOk()); - case Result.ERR: return Result.err((byte) (float) a.getErr()); - default: throw new AssertionError(); - } - } - - public static Variants.E1 roundtripEnum(Variants.E1 a) { - return a; - } - - public static boolean invertBool(boolean a) { - return !a; - } - - public static Tuple6 - variantCasts(Tuple6 a) - { - return a; - } - - public static Tuple4 - variantZeros(Tuple4 a) - { - return a; - } - - public static void variantTypedefs(Integer a, boolean b, Result c) { } - private static void expect(boolean v) { if (!v) { throw new AssertionError(); From adeef4415bf4ad577de374961c5481dab39e1b5d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 20 Jan 2023 07:37:29 -0800 Subject: [PATCH 24/25] Update wasm-tools on CI --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index abfeb69ba..9573b8c63 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -83,11 +83,11 @@ jobs: - uses: actions/cache@v3 with: path: ${{ runner.tool_cache }}/wasm-tools - key: wasm-tools-bin-1.0.15-${{ runner.os }} + key: wasm-tools-bin-1.0.17-${{ runner.os }} - run: echo '${{ runner.tool_cache }}/wasm-tools/bin' >> $GITHUB_PATH - run: | cargo install \ - wasm-tools@1.0.15 \ + wasm-tools@1.0.17 \ --root '${{ runner.tool_cache }}/wasm-tools' \ --locked \ --no-default-features \ From 260613e45647db5eae32ace363b3c7061212e11a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 20 Jan 2023 13:41:22 -0800 Subject: [PATCH 25/25] Review comments --- crates/bindgen-core/src/component.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/bindgen-core/src/component.rs b/crates/bindgen-core/src/component.rs index 01b1484af..c18cb6e90 100644 --- a/crates/bindgen-core/src/component.rs +++ b/crates/bindgen-core/src/component.rs @@ -26,12 +26,11 @@ pub fn generate( files: &mut Files, ) -> Result<()> { // Use the `wit-component` crate here to parse `binary` and discover - // the type-level descriptions and `Interface`s corresponding to the - // component binary. This is effectively a step that infers a "world" of - // a component. Right now `interfaces` is a world-like thing and this - // will likely change as worlds are iterated on in the component model - // standard. Regardless though this is the step where types are learned - // and `Interface`s are constructed for further code generation below. + // the type-level descriptions and `Resolve` corresponding to the + // component binary. This will synthesize a `Resolve` which has a top-level + // package which has a single document and `world` within it which describes + // the state of the component. This is then further used afterwards for + // bindings generation as-if a `*.wit` file was input. let decoded = wit_component::decode(name, binary) .context("failed to extract interface information from component")?; let (resolve, world) = match decoded {