From 247851234bef9af6258ed81f7d235c2865d5445a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 18 Jan 2023 09:37:03 -0600 Subject: [PATCH] Update WIT tooling used by Wasmtime (#5565) * Update WIT tooling used by Wasmtime This commit updates the WIT tooling, namely the wasm-tools family of crates, with recent updates. Notably: * bytecodealliance/wasm-tools#867 * bytecodealliance/wasm-tools#871 This updates index spaces in components and additionally bumps the minimum required version of the component binary format to be consumed by Wasmtime (because of the index space changes). Additionally WIT tooling now fully supports `use`. Note that WIT tooling doesn't, at this time, fully support packages and depending on remotely defined WIT packages. Currently WIT still needs to be vendored in the project. It's hoped that future work with `cargo component` and possible integration here could make the story about depending on remotely-defined WIT more ergonomic and streamlined. * Fix `bindgen!` codegen tests * Add a test for `use` paths an implement support * Update to crates.io versions of wasm-tools * Uncomment codegen tests --- Cargo.lock | 41 +- Cargo.toml | 16 +- crates/component-macro/Cargo.toml | 1 + crates/component-macro/src/bindgen.rs | 189 +++-- .../component-macro/test-helpers/src/lib.rs | 4 +- crates/component-macro/tests/codegen.rs | 6 +- crates/component-macro/tests/codegen/char.wit | 7 +- .../tests/codegen/conventions.wit | 7 +- .../component-macro/tests/codegen/empty.wit | 2 +- .../component-macro/tests/codegen/flags.wit | 7 +- .../component-macro/tests/codegen/floats.wit | 7 +- .../tests/codegen/integers.wit | 7 +- .../component-macro/tests/codegen/lists.wit | 7 +- .../tests/codegen/many-arguments.wit | 7 +- .../tests/codegen/multi-return.wit | 7 +- .../component-macro/tests/codegen/records.wit | 7 +- .../tests/codegen/simple-functions.wit | 7 +- .../tests/codegen/simple-lists.wit | 10 +- .../tests/codegen/small-anonymous.wit | 7 +- .../tests/codegen/smoke-default.wit | 6 +- .../tests/codegen/smoke-export.wit | 2 +- .../component-macro/tests/codegen/smoke.wit | 2 +- .../component-macro/tests/codegen/strings.wit | 7 +- .../component-macro/tests/codegen/unions.wit | 7 +- .../tests/codegen/use-paths.wit | 27 + .../tests/codegen/variants.wit | 7 +- crates/environ/src/component/translate.rs | 6 + .../environ/src/component/translate/inline.rs | 23 + crates/wasmtime/src/component/mod.rs | 248 +++++- crates/wit-bindgen/src/lib.rs | 746 +++++++++++------- crates/wit-bindgen/src/rust.rs | 38 +- crates/wit-bindgen/src/types.rs | 138 ++-- supply-chain/audits.toml | 48 ++ tests/all/component_model/bindgen.rs | 12 +- tests/all/component_model/bindgen/results.rs | 75 +- 35 files changed, 1192 insertions(+), 546 deletions(-) create mode 100644 crates/component-macro/tests/codegen/use-paths.wit diff --git a/Cargo.lock b/Cargo.lock index 44a54b450b..2ae1764cff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3255,18 +3255,18 @@ checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" [[package]] name = "wasm-encoder" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05632e0a66a6ed8cca593c24223aabd6262f256c3693ad9822c315285f010614" +checksum = "29ab2fe77b325731603297debb4573e002d06ae0aa1f4dc108585c81961e0609" dependencies = [ "leb128", ] [[package]] name = "wasm-mutate" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0e9666b36c1e7e88bf1e0d114c8a4fe7d4e32d3aa38c1e8e9d71a972b99764a" +checksum = "c2f8b34ecab2aadb3a974fc96d38a37780793b1a44f9e681ed68f7e69757ca90" dependencies = [ "egg", "log", @@ -3278,9 +3278,9 @@ dependencies = [ [[package]] name = "wasm-smith" -version = "0.11.10" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ca3f5a24b691771929ac6adf330972e8ecbfe48dcf8809d7bf83216c124cfd" +checksum = "00e76ca2ad5d10fdcd08d9af00ac02585a1e53a60020b0eda8874922f5d49bff" dependencies = [ "arbitrary", "flagset", @@ -3330,9 +3330,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.96.0" +version = "0.97.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adde01ade41ab9a5d10ec8ed0bb954238cf8625b5cd5a13093d6de2ad9c2be1a" +checksum = "b98123a0d2bacf9286239231b116cbd66c65d9b89793f7c9bba3a3ae7f1b15f3" dependencies = [ "indexmap", "url", @@ -3349,9 +3349,9 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.2.45" +version = "0.2.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3045e1aa2cac847f4f94a1e25db9f084a947aeff47d9099fb9c5ccd16d335040" +checksum = "595cca929e47a7bec3c941b5a8e133f51b17e6d9dd8c82ab97902196f5a07b42" dependencies = [ "anyhow", "wasmparser", @@ -3502,7 +3502,7 @@ dependencies = [ "wasmtime-wasi-crypto", "wasmtime-wasi-nn", "wasmtime-wast", - "wast 50.0.0", + "wast 51.0.0", "wat", "windows-sys", ] @@ -3523,6 +3523,7 @@ dependencies = [ name = "wasmtime-component-macro" version = "6.0.0" dependencies = [ + "anyhow", "component-macro-test-helpers", "proc-macro2", "quote", @@ -3775,7 +3776,7 @@ dependencies = [ "anyhow", "log", "wasmtime", - "wast 50.0.0", + "wast 51.0.0", ] [[package]] @@ -3810,9 +3811,9 @@ dependencies = [ [[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", @@ -3822,11 +3823,11 @@ dependencies = [ [[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 50.0.0", + "wast 51.0.0", ] [[package]] @@ -4032,15 +4033,17 @@ dependencies = [ [[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", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5b6e55a8c7..55e238f042 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,14 +158,14 @@ winch-codegen = { path = "winch/codegen", version = "=0.4.0" } target-lexicon = { version = "0.12.3", default-features = false, features = ["std"] } anyhow = "1.0.22" -wasmparser = "0.96.0" -wat = "1.0.52" -wast = "50.0.0" -wasmprinter = "0.2.45" -wasm-encoder = "0.20.0" -wasm-smith = "0.11.10" -wasm-mutate = "0.2.13" -wit-parser = "0.3" +wasmparser = "0.97.0" +wat = "1.0.53" +wast = "51.0.0" +wasmprinter = "0.2.46" +wasm-encoder = "0.21.0" +wasm-smith = "0.11.11" +wasm-mutate = "0.2.14" +wit-parser = "0.4" windows-sys = "0.42.0" env_logger = "0.9" rustix = "0.36.0" diff --git a/crates/component-macro/Cargo.toml b/crates/component-macro/Cargo.toml index ecc319821b..ea816d8a50 100644 --- a/crates/component-macro/Cargo.toml +++ b/crates/component-macro/Cargo.toml @@ -16,6 +16,7 @@ test = false doctest = false [dependencies] +anyhow = "1.0" proc-macro2 = "1.0" quote = "1.0" syn = { version = "1.0", features = ["extra-traits"] } diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs index 4141afff89..435abbb42b 100644 --- a/crates/component-macro/src/bindgen.rs +++ b/crates/component-macro/src/bindgen.rs @@ -3,14 +3,14 @@ use std::path::{Path, PathBuf}; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::{braced, token, Ident, Token}; -use wasmtime_wit_bindgen::Opts; -use wit_parser::{Document, World}; +use wasmtime_wit_bindgen::{Opts, TrappableError}; +use wit_parser::{PackageId, Resolve, UnresolvedPackage, WorldId}; -#[derive(Default)] pub struct Config { - opts: Opts, // ... - world: World, - files: Vec, + opts: Opts, + resolve: Resolve, + world: WorldId, + files: Vec, } pub fn expand(input: &Config) -> Result { @@ -21,93 +21,162 @@ pub fn expand(input: &Config) -> Result { )); } - let src = input.opts.generate(&input.world); + let src = input.opts.generate(&input.resolve, input.world); 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. - 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(), + format!("const _: &str = include_str!(r#\"{}\"#);\n", file.display()) + .parse::() + .unwrap(), ); } Ok(contents) } +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 ret = Config::default(); + let mut source = None; - if input.peek(token::Brace) { + 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(path) => { - if world.is_some() { - return Err(Error::new(path.span(), "cannot specify second world")); + Opt::Path(s) => { + if source.is_some() { + return Err(Error::new(s.span(), "cannot specify second source")); } - world = Some(ret.parse(path)?); + source = Some(Source::Path(s.value())); } - Opt::Inline(span, w) => { - if world.is_some() { - return Err(Error::new(span, "cannot specify second world")); + Opt::World(s) => { + if document.is_some() { + return Err(Error::new(s.span(), "cannot specify second document")); } - world = Some(w); + document = Some(parse_doc(&s.value(), &mut world)); } - Opt::Tracing(val) => ret.opts.tracing = val, - Opt::Async(val) => ret.opts.async_ = val, - Opt::TrappableErrorType(val) => ret.opts.trappable_error_type = val, + Opt::Inline(s) => { + if source.is_some() { + return Err(Error::new(s.span(), "cannot specify second source")); + } + source = Some(Source::Inline(s.value())); + } + Opt::Tracing(val) => opts.tracing = val, + Opt::Async(val) => opts.async_ = val, + Opt::TrappableErrorType(val) => opts.trappable_error_type = val, + } + } + 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 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) + 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, + }) } } -impl Config { - 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)) +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(), } } mod kw { - syn::custom_keyword!(path); syn::custom_keyword!(inline); + syn::custom_keyword!(path); syn::custom_keyword!(tracing); syn::custom_keyword!(trappable_error_type); + syn::custom_keyword!(world); } enum Opt { + World(syn::LitStr), Path(syn::LitStr), - Inline(Span, World), + Inline(syn::LitStr), Tracing(bool), Async(bool), - TrappableErrorType(Vec<(String, String, String)>), + TrappableErrorType(Vec), } impl Parse for Opt { @@ -118,14 +187,13 @@ impl Parse for Opt { input.parse::()?; Ok(Opt::Path(input.parse()?)) } else if l.peek(kw::inline) { - let span = input.parse::()?.span; + input.parse::()?; input.parse::()?; - let s = input.parse::()?; - let world = Document::parse("".as_ref(), &s.value()) - .map_err(|e| Error::new(s.span(), e))? - .into_world() - .map_err(|e| Error::new(s.span(), e))?; - Ok(Opt::Inline(span, world)) + 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::tracing) { input.parse::()?; input.parse::()?; @@ -141,7 +209,16 @@ impl Parse for Opt { let _lbrace = braced!(contents in input); let fields: Punctuated<(String, String, String), Token![,]> = contents.parse_terminated(trappable_error_field_parse)?; - Ok(Opt::TrappableErrorType(fields.into_iter().collect())) + Ok(Opt::TrappableErrorType( + fields + .into_iter() + .map(|(wit_owner, wit_name, rust_name)| TrappableError { + wit_owner: Some(wit_owner), + wit_name, + rust_name, + }) + .collect(), + )) } else { Err(l.error()) } diff --git a/crates/component-macro/test-helpers/src/lib.rs b/crates/component-macro/test-helpers/src/lib.rs index 8b4e7059b5..28ea3ca465 100644 --- a/crates/component-macro/test-helpers/src/lib.rs +++ b/crates/component-macro/test-helpers/src/lib.rs @@ -12,10 +12,10 @@ pub fn foreach(input: TokenStream) -> TokenStream { let f = f.unwrap().path(); if f.extension().and_then(|s| s.to_str()) == Some("wit") { let name = f.file_stem().unwrap().to_str().unwrap(); - let name = Ident::new(&name.replace("-", "_"), Span::call_site()); + let ident = Ident::new(&name.replace("-", "_"), Span::call_site()); let path = f.to_str().unwrap(); result.push(quote! { - #input!(#name #path); + #input!(#ident #name #path); }); } } diff --git a/crates/component-macro/tests/codegen.rs b/crates/component-macro/tests/codegen.rs index 10aee99efb..13d826a8dd 100644 --- a/crates/component-macro/tests/codegen.rs +++ b/crates/component-macro/tests/codegen.rs @@ -1,17 +1,19 @@ macro_rules! gentest { - ($id:ident $path:tt) => { + ($id:ident $name:tt $path:tt) => { mod $id { mod normal { - wasmtime::component::bindgen!($path); + wasmtime::component::bindgen!($name in $path); } mod async_ { wasmtime::component::bindgen!({ + world: $name, path: $path, async: true, }); } mod tracing { wasmtime::component::bindgen!({ + world: $name, path: $path, tracing: true, }); diff --git a/crates/component-macro/tests/codegen/char.wit b/crates/component-macro/tests/codegen/char.wit index 85787e840c..01b20f7007 100644 --- a/crates/component-macro/tests/codegen/char.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/conventions.wit b/crates/component-macro/tests/codegen/conventions.wit index 23c472c18b..2c5645c663 100644 --- a/crates/component-macro/tests/codegen/conventions.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/empty.wit b/crates/component-macro/tests/codegen/empty.wit index 48c1b8597d..1f99081f58 100644 --- a/crates/component-macro/tests/codegen/empty.wit +++ b/crates/component-macro/tests/codegen/empty.wit @@ -1 +1 @@ -world empty {} +default world empty {} diff --git a/crates/component-macro/tests/codegen/flags.wit b/crates/component-macro/tests/codegen/flags.wit index d92111ff9b..b5a2fa2d1c 100644 --- a/crates/component-macro/tests/codegen/flags.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/floats.wit b/crates/component-macro/tests/codegen/floats.wit index 274770cc1b..4a0c67ce2c 100644 --- a/crates/component-macro/tests/codegen/floats.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/integers.wit b/crates/component-macro/tests/codegen/integers.wit index c5e7f096c1..bfad272882 100644 --- a/crates/component-macro/tests/codegen/integers.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/lists.wit b/crates/component-macro/tests/codegen/lists.wit index cb5b604353..19b9460687 100644 --- a/crates/component-macro/tests/codegen/lists.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/many-arguments.wit b/crates/component-macro/tests/codegen/many-arguments.wit index b7915ad36b..a5b67b2184 100644 --- a/crates/component-macro/tests/codegen/many-arguments.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/multi-return.wit b/crates/component-macro/tests/codegen/multi-return.wit index 4239e1efed..716e77a6c8 100644 --- a/crates/component-macro/tests/codegen/multi-return.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/records.wit b/crates/component-macro/tests/codegen/records.wit index 6d8f1712e4..39d5e81694 100644 --- a/crates/component-macro/tests/codegen/records.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/simple-functions.wit b/crates/component-macro/tests/codegen/simple-functions.wit index 0f25e6c866..cdb1183790 100644 --- a/crates/component-macro/tests/codegen/simple-functions.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/simple-lists.wit b/crates/component-macro/tests/codegen/simple-lists.wit index 416c94107b..885cdeb743 100644 --- a/crates/component-macro/tests/codegen/simple-lists.wit +++ b/crates/component-macro/tests/codegen/simple-lists.wit @@ -1,13 +1,11 @@ interface simple-lists { simple-list1: func(l: list) simple-list2: func() -> list - // TODO: reenable this when smw implements this - // simple-list3: func(a: list, b: list) -> tuple, list> + simple-list3: func(a: list, b: list) -> tuple, list> 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/crates/component-macro/tests/codegen/small-anonymous.wit b/crates/component-macro/tests/codegen/small-anonymous.wit index 8f7f690078..9643609134 100644 --- a/crates/component-macro/tests/codegen/small-anonymous.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/smoke-default.wit b/crates/component-macro/tests/codegen/smoke-default.wit index e1560ac552..0d26935906 100644 --- a/crates/component-macro/tests/codegen/smoke-default.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/smoke-export.wit b/crates/component-macro/tests/codegen/smoke-export.wit index 84500ce18e..cefdc19823 100644 --- a/crates/component-macro/tests/codegen/smoke-export.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/smoke.wit b/crates/component-macro/tests/codegen/smoke.wit index eddf743921..6d8e80ccea 100644 --- a/crates/component-macro/tests/codegen/smoke.wit +++ b/crates/component-macro/tests/codegen/smoke.wit @@ -1,4 +1,4 @@ -world the-world { +default world the-world { import imports: interface { y: func() } diff --git a/crates/component-macro/tests/codegen/strings.wit b/crates/component-macro/tests/codegen/strings.wit index a7511c9203..7fc1885529 100644 --- a/crates/component-macro/tests/codegen/strings.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/unions.wit b/crates/component-macro/tests/codegen/unions.wit index ea2d191c62..07e0f380a0 100644 --- a/crates/component-macro/tests/codegen/unions.wit +++ b/crates/component-macro/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/crates/component-macro/tests/codegen/use-paths.wit b/crates/component-macro/tests/codegen/use-paths.wit new file mode 100644 index 0000000000..1e877379ca --- /dev/null +++ b/crates/component-macro/tests/codegen/use-paths.wit @@ -0,0 +1,27 @@ +interface a { + record foo {} + + a: func() -> foo +} + +interface b { + use self.a.{foo} + + a: func() -> foo +} + +interface c { + use self.b.{foo} + + a: func() -> foo +} + +default world d { + import a: self.a + import b: self.b + import d: interface { + use self.c.{foo} + + b: func() -> foo + } +} diff --git a/crates/component-macro/tests/codegen/variants.wit b/crates/component-macro/tests/codegen/variants.wit index 288ee19f38..590675dcf6 100644 --- a/crates/component-macro/tests/codegen/variants.wit +++ b/crates/component-macro/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 } diff --git a/crates/environ/src/component/translate.rs b/crates/environ/src/component/translate.rs index 715ba2a2c7..678aa6306e 100644 --- a/crates/environ/src/component/translate.rs +++ b/crates/environ/src/component/translate.rs @@ -191,6 +191,9 @@ enum LocalInitializer<'data> { AliasComponentExport(ComponentInstanceIndex, &'data str), AliasModule(ClosedOverModule), AliasComponent(ClosedOverComponent), + + // export section + Export(ComponentItem), } /// The "closure environment" of components themselves. @@ -615,6 +618,9 @@ impl<'a, 'data> Translator<'a, 'data> { let item = self.kind_to_item(export.kind, export.index); let prev = self.result.exports.insert(export.name, item); assert!(prev.is_none()); + self.result + .initializers + .push(LocalInitializer::Export(item)); } } diff --git a/crates/environ/src/component/translate/inline.rs b/crates/environ/src/component/translate/inline.rs index aaa2eea706..352f9883d1 100644 --- a/crates/environ/src/component/translate/inline.rs +++ b/crates/environ/src/component/translate/inline.rs @@ -731,6 +731,29 @@ impl<'a> Inliner<'a> { AliasComponent(idx) => { frame.components.push(frame.closed_over_component(idx)); } + + Export(item) => match item { + ComponentItem::Func(i) => { + frame + .component_funcs + .push(frame.component_funcs[*i].clone()); + } + ComponentItem::Module(i) => { + frame.modules.push(frame.modules[*i].clone()); + } + ComponentItem::Component(i) => { + frame.components.push(frame.components[*i].clone()); + } + ComponentItem::ComponentInstance(i) => { + frame + .component_instances + .push(frame.component_instances[*i].clone()); + } + + // Type index spaces aren't maintained during this inlining pass + // so ignore this. + ComponentItem::Type(_) => {} + }, } Ok(None) diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs index ad91e8fddb..c795d2f70b 100644 --- a/crates/wasmtime/src/component/mod.rs +++ b/crates/wasmtime/src/component/mod.rs @@ -20,7 +20,7 @@ pub use self::instance::{ExportInstance, Exports, Instance, InstancePre}; pub use self::linker::{Linker, LinkerInstance}; pub use self::types::Type; pub use self::values::Val; -pub use wasmtime_component_macro::{bindgen, flags, ComponentType, Lift, Lower}; +pub use wasmtime_component_macro::{flags, ComponentType, Lift, Lower}; // These items are expected to be used by an eventual // `#[derive(ComponentType)]`, they are not part of Wasmtime's API stability @@ -42,3 +42,249 @@ pub mod __internal { } pub(crate) use self::store::ComponentStoreData; + +/// Generate bindings for a WIT package. +/// +/// This macro ingests a [WIT package] and will generate all the necessary +/// bindings for instantiating and invoking a particular `world` in the +/// package. A `world` in a WIT package is a description of imports and exports +/// for a component. This provides a higher-level representation of working with +/// a component than the raw [`Instance`] type which must be manually-type-check +/// and manually have its imports provided via the [`Linker`] type. +/// +/// The most basic usage of this macro is: +/// +/// ```rust,ignore +/// wasmtime::component::bindgen!("my-component"); +/// ``` +/// +/// This will parse your projects WIT package in a `wit` directory adjacent to +/// your crate's `Cargo.toml`. All of the `*.wit` files in that directory are +/// parsed and then the `default world` will be looked up within +/// `my-component.wit`. This world is then used as the basis for generating +/// bindings. +/// +/// For example if your project contained: +/// +/// ```text,ignore +/// // wit/my-component.wit +/// +/// default world hello-world { +/// import name: func() -> string +/// export greet: func() +/// } +/// ``` +/// +/// Then you can interact with the generated bindings like so: +/// +/// ```rust,ignore +/// use anyhow::Result; +/// use wasmtime::component::*; +/// use wasmtime::{Config, Engine, Store}; +/// +/// bindgen!("my-component"); +/// +/// struct MyState { +/// name: String, +/// } +/// +/// // Imports into the world, like the `name` import for this world, are satisfied +/// // through traits. +/// impl HelloWorldImports for MyState { +/// // Note the `Result` return value here where `Ok` is returned back to +/// // the component and `Err` will raise a trap. +/// fn name(&mut self) -> Result { +/// Ok(self.name.clone()) +/// } +/// } +/// +/// fn main() -> Result<()> { +/// // Configure an `Engine` and compile the `Component` that is being run for +/// // the application. +/// let mut config = Config::new(); +/// config.wasm_component_model(true); +/// let engine = Engine::new(&config)?; +/// let component = Component::from_file(&engine, "./your-component.wasm")?; +/// +/// // Instantiation of bindings always happens through a `Linker`. +/// // Configuration of the linker is done through a generated `add_to_linker` +/// // method on the bindings structure. +/// // +/// // Note that the closure provided here is a projection from `T` in +/// // `Store` to `&mut U` where `U` implements the `HelloWorldImports` +/// // trait. In this case the `T`, `MyState`, is stored directly in the +/// // structure so no projection is necessary here. +/// let mut linker = Linker::new(&engine); +/// HelloWorld::add_to_linker(&mut linker, |state: &mut MyState| state)?; +/// +/// // As with the core wasm API of Wasmtime instantiation occurs within a +/// // `Store`. The bindings structure contains an `instantiate` method which +/// // takes the store, component, and linker. This returns the `bindings` +/// // structure which is an instance of `HelloWorld` and supports typed access +/// // to the exports of the component. +/// let mut store = Store::new( +/// &engine, +/// MyState { +/// name: "me".to_string(), +/// }, +/// ); +/// let (bindings, _) = HelloWorld::instantiate(&mut store, &component, &linker)?; +/// +/// // Here our `greet` function doesn't take any parameters for the component, +/// // but in the Wasmtime embedding API the first argument is always a `Store`. +/// bindings.greet(&mut store)?; +/// Ok(()) +/// } +/// ``` +/// +/// The function signatures within generated traits and on generated exports +/// match the component-model signatures as specified in the WIT `world` item. +/// Note that WIT also has support for importing and exports interfaces within +/// worlds, which can be bound here as well: +/// +/// For example this WIT input +/// +/// ```text,ignore +/// // wit/my-component.wit +/// +/// interface host { +/// gen-random-integer: func() -> u32 +/// sha256: func(bytes: list) -> string +/// } +/// +/// default world hello-world { +/// import host: self.host +/// +/// export demo: interface { +/// run: func() +/// } +/// } +/// ``` +/// +/// Then you can interact with the generated bindings like so: +/// +/// ```rust,ignore +/// use anyhow::Result; +/// use wasmtime::component::*; +/// use wasmtime::{Config, Engine, Store}; +/// +/// bindgen!("my-component"); +/// +/// struct MyState { +/// // ... +/// } +/// +/// // Note that the trait here is per-interface and within a submodule now. +/// impl host::Host for MyState { +/// fn gen_random_integer(&mut self) -> Result { +/// Ok(rand::thread_rng().gen()) +/// } +/// +/// fn sha256(&mut self, bytes: Vec) -> Result { +/// // ... +/// } +/// } +/// +/// fn main() -> Result<()> { +/// let mut config = Config::new(); +/// config.wasm_component_model(true); +/// let engine = Engine::new(&config)?; +/// let component = Component::from_file(&engine, "./your-component.wasm")?; +/// +/// let mut linker = Linker::new(&engine); +/// HelloWorld::add_to_linker(&mut linker, |state: &mut MyState| state)?; +/// +/// let mut store = Store::new( +/// &engine, +/// MyState { /* ... */ }, +/// ); +/// let (bindings, _) = HelloWorld::instantiate(&mut store, &component, &linker)?; +/// +/// // Note that the `demo` method returns a `&Demo` through which we can +/// // run the methods on that interface. +/// bindings.demo().run(&mut store)?; +/// Ok(()) +/// } +/// ``` +/// +/// The generated bindings can additionally be explored more fully with `cargo +/// doc` to see what types and traits and such are generated. +/// +/// # Syntax +/// +/// This procedural macro accepts a few different syntaxes. The primary purpose +/// of this macro is to locate a WIT package, parse it, and then extract a +/// `world` from the parsed package. There are then codegen-specific options to +/// the bindings themselves which can additionally be specified. +/// +/// Basic usage of this macro looks like: +/// +/// ```rust,ignore +/// // Parse the `wit/` folder adjacent to this crate's `Cargo.toml` and look +/// // for the document `foo`, which must have a `default world` contained +/// // within it. +/// bindgen!("foo"); +/// +/// // Parse the `wit/` folder adjacent to `Cargo.toml` and look up the document +/// // `foo` and the world named `bar`. +/// bindgen!("foo.bar"); +/// +/// // Parse the folder `other/wit/folder` adjacent to `Cargo.toml`. +/// bindgen!("foo" in "other/wit/folder"); +/// bindgen!("foo.bar" in "other/wit/folder"); +/// +/// // Parse the file `foo.wit` as a single-file WIT package with no +/// // dependencies. +/// bindgen!("foo" in "foo.wit"); +/// ``` +/// +/// A more configured version of invoking this macro looks like: +/// +/// ```rust,ignore +/// bindgen!({ +/// world: "foo", // or "foo.bar", same as in `bindgen!("foo")` +/// +/// // same as in `bindgen!("foo" in "other/wit/folder") +/// path: "other/wit/folder", +/// +/// // Instead of `path` the WIT document can be provided inline if +/// // desired. +/// inline: " +/// default world foo { +/// // ... +/// } +/// ", +/// +/// // Add calls to `tracing::span!` before each import or export is called +/// // to log arguments and return values. +/// // +/// // This option defaults to `false`. +/// tracing: true, +/// +/// // Imports will be async functions through #[async_trait] and exports +/// // are also invoked as async functions. Requires `Config::async_support` +/// // to be `true`. +/// // +/// // Note that this is only async for the host as the guest will still +/// // appear as if it's invoking blocking functions. +/// // +/// // This option defaults to `false`. +/// async: true, +/// +/// // This can be used to translate WIT return values of the form +/// // `result` into `Result` in Rust. +/// // The `RustErrorType` structure will have an automatically generated +/// // implementation of `From for RustErrorType`. The +/// // `RustErrorType` additionally can also represent a trap to +/// // conveniently flatten all errors into one container. +/// // +/// // By default this option is not specified. +/// trappable_error_type: { +/// interface::ErrorType: RustErrorType, +/// }, +/// +/// }); +/// ``` +/// +/// [WIT package]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md +pub use wasmtime_component_macro::bindgen; diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs index 393cc3b6fa..6499ccaaee 100644 --- a/crates/wit-bindgen/src/lib.rs +++ b/crates/wit-bindgen/src/lib.rs @@ -29,8 +29,14 @@ use source::Source; struct Wasmtime { src: Source, opts: Opts, - imports: Vec, + imports: Vec, exports: Exports, + types: Types, +} + +enum Import { + Interface { snake: String }, + Function { add_to_linker: String, sig: String }, } #[derive(Default)] @@ -50,163 +56,181 @@ pub struct Opts { /// Whether or not to use async rust functions and traits. pub async_: bool, - /// For a given wit interface and type name, generate a "trappable error type" - /// of the following Rust type name - pub trappable_error_type: Vec<(String, String, String)>, + /// A list of "trappable errors" which are used to replace the `E` in + /// `result` found in WIT. + pub trappable_error_type: Vec, +} + +#[derive(Debug, Clone)] +pub struct TrappableError { + /// The name of the error in WIT that is being mapped. + pub wit_name: String, + + /// The owner container of the error in WIT of the error that's being + /// mapped. + /// + /// This is, for example, the name of the WIT interface or the WIT world + /// which owns the type. If this is set to `None` then any error type with + /// `wit_name` is remapped to `rust_name`. + pub wit_owner: Option, + + /// The name, in Rust, of the error type to generate. + pub rust_name: String, } impl Opts { - pub fn generate(&self, world: &World) -> String { + pub fn generate(&self, resolve: &Resolve, world: WorldId) -> String { let mut r = Wasmtime::default(); r.opts = self.clone(); - r.generate(world) + r.generate(resolve, world) } } impl Wasmtime { - fn generate(&mut self, world: &World) -> String { + fn generate(&mut self, resolve: &Resolve, id: WorldId) -> String { + self.types.analyze(resolve, id); + let world = &resolve.worlds[id]; for (name, import) in world.imports.iter() { - self.import(name, import); + self.import(resolve, name, import); } for (name, export) in world.exports.iter() { - self.export(name, export); + self.export(resolve, name, export); } - if let Some(iface) = &world.default { - self.export_default(&world.name, iface); - } - self.finish(world) + self.finish(resolve, id) } - fn import(&mut self, name: &str, iface: &Interface) { - let mut gen = InterfaceGenerator::new(self, iface, TypeMode::Owned); - gen.types(); - gen.generate_trappable_error_types(); - gen.generate_add_to_linker(name); - + fn import(&mut self, resolve: &Resolve, name: &str, item: &WorldItem) { let snake = name.to_snake_case(); - let module = &gen.src[..]; + let mut gen = InterfaceGenerator::new(self, resolve, TypeMode::Owned); + let import = match item { + WorldItem::Function(func) => { + gen.generate_function_trait_sig(TypeOwner::None, &func); + let sig = mem::take(&mut gen.src).into(); + gen.generate_add_function_to_linker(TypeOwner::None, &func, "linker"); + let add_to_linker = gen.src.into(); + Import::Function { sig, add_to_linker } + } + WorldItem::Interface(id) => { + gen.current_interface = Some(*id); + gen.types(*id); + gen.generate_trappable_error_types(TypeOwner::Interface(*id)); + gen.generate_add_to_linker(*id, name); - uwriteln!( - self.src, - " - #[allow(clippy::all)] - pub mod {snake} {{ - #[allow(unused_imports)] - use wasmtime::component::__internal::anyhow; + let module = &gen.src[..]; - {module} - }} - " - ); + uwriteln!( + self.src, + " + #[allow(clippy::all)] + pub mod {snake} {{ + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; - self.imports.push(snake); + {module} + }} + " + ); + Import::Interface { snake } + } + }; + + self.imports.push(import); } - fn export(&mut self, name: &str, iface: &Interface) { - let mut gen = InterfaceGenerator::new(self, iface, TypeMode::AllBorrowed("'a")); - gen.types(); - gen.generate_trappable_error_types(); - - let camel = name.to_upper_camel_case(); - uwriteln!(gen.src, "pub struct {camel} {{"); - for func in iface.functions.iter() { - uwriteln!( - gen.src, - "{}: wasmtime::component::Func,", - func.name.to_snake_case() - ); - } - uwriteln!(gen.src, "}}"); - - uwriteln!(gen.src, "impl {camel} {{"); - uwrite!( - gen.src, - " - pub fn new( - __exports: &mut wasmtime::component::ExportInstance<'_, '_>, - ) -> anyhow::Result<{camel}> {{ - " - ); - let fields = gen.extract_typed_functions(); - for (name, getter) in fields.iter() { - uwriteln!(gen.src, "let {name} = {getter};"); - } - uwriteln!(gen.src, "Ok({camel} {{"); - for (name, _) in fields.iter() { - uwriteln!(gen.src, "{name},"); - } - uwriteln!(gen.src, "}})"); - uwriteln!(gen.src, "}}"); - for func in iface.functions.iter() { - gen.define_rust_guest_export(Some(name), func); - } - uwriteln!(gen.src, "}}"); - + fn export(&mut self, resolve: &Resolve, name: &str, item: &WorldItem) { let snake = name.to_snake_case(); - let module = &gen.src[..]; + let mut gen = InterfaceGenerator::new(self, resolve, TypeMode::AllBorrowed("'a")); + let (ty, getter) = match item { + WorldItem::Function(func) => { + gen.define_rust_guest_export(None, func); + let body = mem::take(&mut gen.src).into(); + let (_name, getter) = gen.extract_typed_function(func); + assert!(gen.src.is_empty()); + self.exports.funcs.push(body); + (format!("wasmtime::component::Func"), getter) + } + WorldItem::Interface(id) => { + gen.current_interface = Some(*id); + gen.types(*id); + gen.generate_trappable_error_types(TypeOwner::Interface(*id)); + let iface = &resolve.interfaces[*id]; - uwriteln!( - self.src, - " - #[allow(clippy::all)] - pub mod {snake} {{ - #[allow(unused_imports)] - use wasmtime::component::__internal::anyhow; + let camel = name.to_upper_camel_case(); + uwriteln!(gen.src, "pub struct {camel} {{"); + for (_, func) in iface.functions.iter() { + uwriteln!( + gen.src, + "{}: wasmtime::component::Func,", + func.name.to_snake_case() + ); + } + uwriteln!(gen.src, "}}"); - {module} - }} - " - ); + uwriteln!(gen.src, "impl {camel} {{"); + uwrite!( + gen.src, + " + pub fn new( + __exports: &mut wasmtime::component::ExportInstance<'_, '_>, + ) -> anyhow::Result<{camel}> {{ + " + ); + let mut fields = Vec::new(); + for (_, func) in iface.functions.iter() { + let (name, getter) = gen.extract_typed_function(func); + uwriteln!(gen.src, "let {name} = {getter};"); + fields.push(name); + } + uwriteln!(gen.src, "Ok({camel} {{"); + for name in fields { + uwriteln!(gen.src, "{name},"); + } + uwriteln!(gen.src, "}})"); + uwriteln!(gen.src, "}}"); + for (_, func) in iface.functions.iter() { + gen.define_rust_guest_export(Some(name), func); + } + uwriteln!(gen.src, "}}"); - let getter = format!( - "\ - {snake}::{camel}::new( - &mut __exports.instance(\"{name}\") - .ok_or_else(|| anyhow::anyhow!(\"exported instance `{name}` not present\"))? - )?\ - " - ); - let prev = self - .exports - .fields - .insert(snake.clone(), (format!("{snake}::{camel}"), getter)); + let module = &gen.src[..]; + + uwriteln!( + self.src, + " + #[allow(clippy::all)] + pub mod {snake} {{ + #[allow(unused_imports)] + use wasmtime::component::__internal::anyhow; + + {module} + }} + " + ); + + let getter = format!( + "\ + {snake}::{camel}::new( + &mut __exports.instance(\"{name}\") + .ok_or_else(|| anyhow::anyhow!(\"exported instance `{name}` not present\"))? + )?\ + " + ); + self.exports.funcs.push(format!( + " + pub fn {snake}(&self) -> &{snake}::{camel} {{ + &self.{snake} + }} + " + )); + (format!("{snake}::{camel}"), getter) + } + }; + let prev = self.exports.fields.insert(snake.clone(), (ty, getter)); assert!(prev.is_none()); - self.exports.funcs.push(format!( - " - pub fn {snake}(&self) -> &{snake}::{camel} {{ - &self.{snake} - }} - " - )); } - fn export_default(&mut self, _name: &str, iface: &Interface) { - let mut gen = InterfaceGenerator::new(self, iface, TypeMode::AllBorrowed("'a")); - gen.types(); - gen.generate_trappable_error_types(); - let fields = gen.extract_typed_functions(); - for (name, getter) in fields { - let prev = gen - .gen - .exports - .fields - .insert(name, ("wasmtime::component::Func".to_string(), getter)); - assert!(prev.is_none()); - } - - for func in iface.functions.iter() { - let prev = mem::take(&mut gen.src); - gen.define_rust_guest_export(None, func); - let func = mem::replace(&mut gen.src, prev); - gen.gen.exports.funcs.push(func.to_string()); - } - - let src = gen.src; - self.src.push_str(&src); - } - - fn finish(&mut self, world: &World) -> String { - let camel = world.name.to_upper_camel_case(); + fn finish(&mut self, resolve: &Resolve, world: WorldId) -> String { + let camel = resolve.worlds[world].name.to_upper_camel_case(); uwriteln!(self.src, "pub struct {camel} {{"); for (name, (ty, _)) in self.exports.fields.iter() { uwriteln!(self.src, "{name}: {ty},"); @@ -219,40 +243,43 @@ impl Wasmtime { ("", "", "", "") }; + self.toplevel_import_trait(resolve, world); + uwriteln!(self.src, "const _: () = {{"); uwriteln!(self.src, "use wasmtime::component::__internal::anyhow;"); + uwriteln!(self.src, "impl {camel} {{"); + self.toplevel_add_to_linker(resolve, world); uwriteln!( self.src, " - impl {camel} {{ - /// Instantiates the provided `module` using the specified - /// parameters, wrapping up the result in a structure that - /// translates between wasm and the host. - pub {async_} fn instantiate{async__}( - mut store: impl wasmtime::AsContextMut, - component: &wasmtime::component::Component, - linker: &wasmtime::component::Linker, - ) -> anyhow::Result<(Self, wasmtime::component::Instance)> {{ - let instance = linker.instantiate{async__}(&mut store, component){await_}?; - Ok((Self::new(store, &instance)?, instance)) - }} + /// Instantiates the provided `module` using the specified + /// parameters, wrapping up the result in a structure that + /// translates between wasm and the host. + pub {async_} fn instantiate{async__}( + mut store: impl wasmtime::AsContextMut, + component: &wasmtime::component::Component, + linker: &wasmtime::component::Linker, + ) -> anyhow::Result<(Self, wasmtime::component::Instance)> {{ + let instance = linker.instantiate{async__}(&mut store, component){await_}?; + Ok((Self::new(store, &instance)?, instance)) + }} - /// Low-level creation wrapper for wrapping up the exports - /// of the `instance` provided in this structure of wasm - /// exports. - /// - /// This function will extract exports from the `instance` - /// defined within `store` and wrap them all up in the - /// returned structure which can be used to interact with - /// the wasm module. - pub fn new( - mut store: impl wasmtime::AsContextMut, - instance: &wasmtime::component::Instance, - ) -> anyhow::Result {{ - let mut store = store.as_context_mut(); - let mut exports = instance.exports(&mut store); - let mut __exports = exports.root(); + /// Low-level creation wrapper for wrapping up the exports + /// of the `instance` provided in this structure of wasm + /// exports. + /// + /// This function will extract exports from the `instance` + /// defined within `store` and wrap them all up in the + /// returned structure which can be used to interact with + /// the wasm module. + pub fn new( + mut store: impl wasmtime::AsContextMut, + instance: &wasmtime::component::Instance, + ) -> anyhow::Result {{ + let mut store = store.as_context_mut(); + let mut exports = instance.exports(&mut store); + let mut __exports = exports.root(); ", ); for (name, (_, get)) in self.exports.fields.iter() { @@ -303,52 +330,138 @@ impl Wasmtime { } impl Wasmtime { - fn trappable_error_types<'a>( - &'a self, - iface: &'a Interface, - ) -> impl Iterator + 'a { - self.opts - .trappable_error_type + fn toplevel_import_trait(&mut self, resolve: &Resolve, world: WorldId) { + let mut functions = Vec::new(); + for import in self.imports.iter() { + match import { + Import::Interface { .. } => continue, + Import::Function { + sig, + add_to_linker: _, + } => functions.push(sig), + } + } + if functions.is_empty() { + return; + } + + let world_camel = resolve.worlds[world].name.to_upper_camel_case(); + uwriteln!(self.src, "pub trait {world_camel}Imports {{"); + for sig in functions { + self.src.push_str(sig); + self.src.push_str("\n"); + } + uwriteln!(self.src, "}}"); + } + + fn toplevel_add_to_linker(&mut self, resolve: &Resolve, world: WorldId) { + if self.imports.is_empty() { + return; + } + let mut functions = Vec::new(); + let mut interfaces = Vec::new(); + for import in self.imports.iter() { + match import { + Import::Interface { snake } => interfaces.push(snake), + Import::Function { + add_to_linker, + sig: _, + } => functions.push(add_to_linker), + } + } + + uwrite!( + self.src, + " + pub fn add_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> anyhow::Result<()> + where U: \ + " + ); + let world_camel = resolve.worlds[world].name.to_upper_camel_case(); + let world_trait = format!("{world_camel}Imports"); + for (i, name) in interfaces .iter() - .filter(|(interface_name, _, _)| iface.name == *interface_name) - .filter_map(|(_, wit_typename, rust_typename)| { - let wit_type = iface.type_lookup.get(wit_typename)?; - Some((wit_typename, wit_type, rust_typename)) + .map(|n| format!("{n}::{}", n.to_upper_camel_case())) + .chain(if functions.is_empty() { + None + } else { + Some(world_trait.clone()) }) + .enumerate() + { + if i > 0 { + self.src.push_str(" + "); + } + self.src.push_str(&name); + } + let maybe_send = if self.opts.async_ { + " + Send, T: Send" + } else { + "" + }; + self.src.push_str(maybe_send); + self.src.push_str(",\n{\n"); + for name in interfaces.iter() { + uwriteln!(self.src, "{name}::add_to_linker(linker, get)?;"); + } + if !functions.is_empty() { + uwriteln!(self.src, "Self::add_root_to_linker(linker, get)?;"); + } + uwriteln!(self.src, "Ok(())\n}}"); + if functions.is_empty() { + return; + } + + uwrite!( + self.src, + " + pub fn add_root_to_linker( + linker: &mut wasmtime::component::Linker, + get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> anyhow::Result<()> + where U: {world_trait}{maybe_send} + {{ + let mut linker = linker.root(); + ", + ); + for add_to_linker in functions { + self.src.push_str(add_to_linker); + self.src.push_str("\n"); + } + uwriteln!(self.src, "Ok(())\n}}"); } } struct InterfaceGenerator<'a> { src: Source, gen: &'a mut Wasmtime, - iface: &'a Interface, + resolve: &'a Resolve, default_param_mode: TypeMode, - types: Types, + current_interface: Option, } impl<'a> InterfaceGenerator<'a> { fn new( gen: &'a mut Wasmtime, - iface: &'a Interface, + resolve: &'a Resolve, default_param_mode: TypeMode, ) -> InterfaceGenerator<'a> { - let mut types = Types::default(); - types.analyze(iface); InterfaceGenerator { src: Source::default(), gen, - iface, - types, + resolve, default_param_mode, + current_interface: None, } } - 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, id: InterfaceId) { + for (name, id) in self.resolve.interfaces[id].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), @@ -362,6 +475,7 @@ impl<'a> 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!(), } } } @@ -790,33 +904,39 @@ impl<'a> InterfaceGenerator<'a> { } } - fn special_case_trappable_error(&self, results: &Results) -> Option<(Result_, String)> { + fn special_case_trappable_error( + &self, + owner: TypeOwner, + results: &Results, + ) -> Option<(&'a Result_, String)> { // We fillin a special trappable error type in the case when a function has just one // result, which is itself a `result`, and the `e` is *not* a primitive // (i.e. defined in std) type, and matches the typename given by the user. let mut i = results.iter_types(); - if i.len() == 1 { - match i.next().unwrap() { - Type::Id(id) => match &self.iface.types[*id].kind { - TypeDefKind::Result(r) => match r.err { - Some(Type::Id(error_typeid)) => self - .gen - .trappable_error_types(&self.iface) - .find(|(_, wit_error_typeid, _)| error_typeid == **wit_error_typeid) - .map(|(_, _, rust_errortype)| (r.clone(), rust_errortype.clone())), - _ => None, - }, - _ => None, - }, - _ => None, - } - } else { - None + let id = match i.next()? { + Type::Id(id) => id, + _ => return None, + }; + if i.next().is_some() { + return None; } + let result = match &self.resolve.types[*id].kind { + TypeDefKind::Result(r) => r, + _ => return None, + }; + let error_typeid = match result.err? { + Type::Id(id) => id, + _ => return None, + }; + self.trappable_error_types(owner) + .find(|(wit_error_typeid, _)| error_typeid == *wit_error_typeid) + .map(|(_, rust_errortype)| (result, rust_errortype)) } - fn generate_add_to_linker(&mut self, name: &str) { + fn generate_add_to_linker(&mut self, id: InterfaceId, name: &str) { + let iface = &self.resolve.interfaces[id]; let camel = name.to_upper_camel_case(); + let owner = TypeOwner::Interface(id); if self.gen.opts.async_ { uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]") @@ -824,47 +944,8 @@ impl<'a> InterfaceGenerator<'a> { // Generate the `pub trait` which represents the host functionality for // this import. uwriteln!(self.src, "pub trait {camel}: Sized {{"); - for func in self.iface.functions.iter() { - self.rustdoc(&func.docs); - - if self.gen.opts.async_ { - self.push_str("async "); - } - self.push_str("fn "); - self.push_str(&to_rust_ident(&func.name)); - self.push_str("(&mut self, "); - for (name, param) in func.params.iter() { - let name = to_rust_ident(name); - self.push_str(&name); - self.push_str(": "); - self.print_ty(param, TypeMode::Owned); - self.push_str(","); - } - self.push_str(")"); - self.push_str(" -> "); - - if let Some((r, error_typename)) = self.special_case_trappable_error(&func.results) { - // Functions which have a single result `result` get special - // cased to use the host_wasmtime_rust::Error, making it possible - // for them to trap or use `?` to propogate their errors - self.push_str("Result<"); - if let Some(ok) = r.ok { - self.print_ty(&ok, TypeMode::Owned); - } else { - self.push_str("()"); - } - self.push_str(","); - self.push_str(&error_typename); - self.push_str(">"); - } else { - // All other functions get their return values wrapped in an anyhow::Result. - // Returning the anyhow::Error case can be used to trap. - self.push_str("anyhow::Result<"); - self.print_result_ty(&func.results, TypeMode::Owned); - self.push_str(">"); - } - - self.push_str(";\n"); + for (_, func) in iface.functions.iter() { + self.generate_function_trait_sig(owner, func); } uwriteln!(self.src, "}}"); @@ -885,25 +966,29 @@ impl<'a> InterfaceGenerator<'a> { " ); uwriteln!(self.src, "let mut inst = linker.instance(\"{name}\")?;"); - for func in self.iface.functions.iter() { - uwrite!( - self.src, - "inst.{}(\"{}\", ", - if self.gen.opts.async_ { - "func_wrap_async" - } else { - "func_wrap" - }, - func.name - ); - self.generate_guest_import_closure(func); - uwriteln!(self.src, ")?;") + for (_, func) in iface.functions.iter() { + self.generate_add_function_to_linker(owner, func, "inst"); } uwriteln!(self.src, "Ok(())"); uwriteln!(self.src, "}}"); } - fn generate_guest_import_closure(&mut self, func: &Function) { + fn generate_add_function_to_linker(&mut self, owner: TypeOwner, func: &Function, linker: &str) { + uwrite!( + self.src, + "{linker}.{}(\"{}\", ", + if self.gen.opts.async_ { + "func_wrap_async" + } else { + "func_wrap" + }, + func.name + ); + self.generate_guest_import_closure(owner, func); + uwriteln!(self.src, ")?;") + } + + fn generate_guest_import_closure(&mut self, owner: TypeOwner, func: &Function) { // Generate the closure that's passed to a `Linker`, the final piece of // codegen here. self.src @@ -936,7 +1021,15 @@ impl<'a> InterfaceGenerator<'a> { ); let _enter = span.enter(); ", - self.iface.name, func.name, + match owner { + TypeOwner::Interface(id) => self.resolve.interfaces[id] + .name + .as_deref() + .unwrap_or(""), + TypeOwner::World(id) => &self.resolve.worlds[id].name, + TypeOwner::None => "", + }, + func.name, )); } @@ -952,7 +1045,10 @@ impl<'a> InterfaceGenerator<'a> { uwrite!(self.src, ");\n"); } - if self.special_case_trappable_error(&func.results).is_some() { + if self + .special_case_trappable_error(owner, &func.results) + .is_some() + { uwrite!( self.src, "match r {{ @@ -977,27 +1073,67 @@ impl<'a> InterfaceGenerator<'a> { } } - fn extract_typed_functions(&mut self) -> Vec<(String, String)> { - let prev = mem::take(&mut self.src); - let mut ret = Vec::new(); - for func in self.iface.functions.iter() { - let snake = func.name.to_snake_case(); - uwrite!(self.src, "*__exports.typed_func::<("); - for (_, ty) in func.params.iter() { - self.print_ty(ty, TypeMode::AllBorrowed("'_")); - self.push_str(", "); - } - self.src.push_str("), ("); - for ty in func.results.iter_types() { - self.print_ty(ty, TypeMode::Owned); - self.push_str(", "); - } - self.src.push_str(")>(\""); - self.src.push_str(&func.name); - self.src.push_str("\")?.func()"); + fn generate_function_trait_sig(&mut self, owner: TypeOwner, func: &Function) { + self.rustdoc(&func.docs); - ret.push((snake, mem::take(&mut self.src).to_string())); + if self.gen.opts.async_ { + self.push_str("async "); } + self.push_str("fn "); + self.push_str(&to_rust_ident(&func.name)); + self.push_str("(&mut self, "); + for (name, param) in func.params.iter() { + let name = to_rust_ident(name); + self.push_str(&name); + self.push_str(": "); + self.print_ty(param, TypeMode::Owned); + self.push_str(","); + } + self.push_str(")"); + self.push_str(" -> "); + + if let Some((r, error_typename)) = self.special_case_trappable_error(owner, &func.results) { + // Functions which have a single result `result` get special + // cased to use the host_wasmtime_rust::Error, making it possible + // for them to trap or use `?` to propogate their errors + self.push_str("Result<"); + if let Some(ok) = r.ok { + self.print_ty(&ok, TypeMode::Owned); + } else { + self.push_str("()"); + } + self.push_str(","); + self.push_str(&error_typename); + self.push_str(">"); + } else { + // All other functions get their return values wrapped in an anyhow::Result. + // Returning the anyhow::Error case can be used to trap. + self.push_str("anyhow::Result<"); + self.print_result_ty(&func.results, TypeMode::Owned); + self.push_str(">"); + } + + self.push_str(";\n"); + } + + fn extract_typed_function(&mut self, func: &Function) -> (String, String) { + let prev = mem::take(&mut self.src); + let snake = func.name.to_snake_case(); + uwrite!(self.src, "*__exports.typed_func::<("); + for (_, ty) in func.params.iter() { + self.print_ty(ty, TypeMode::AllBorrowed("'_")); + self.push_str(", "); + } + self.src.push_str("), ("); + for ty in func.results.iter_types() { + self.print_ty(ty, TypeMode::Owned); + self.push_str(", "); + } + self.src.push_str(")>(\""); + self.src.push_str(&func.name); + self.src.push_str("\")?.func()"); + + let ret = (snake, mem::take(&mut self.src).to_string()); self.src = prev; return ret; } @@ -1097,17 +1233,49 @@ impl<'a> InterfaceGenerator<'a> { self.src.push_str("}\n"); } - fn generate_trappable_error_types(&mut self) { - for (wit_typename, wit_type, trappable_type) in self.gen.trappable_error_types(&self.iface) - { - let info = self.info(*wit_type); + fn trappable_error_types( + &self, + owner: TypeOwner, + ) -> impl Iterator + '_ { + let resolve = self.resolve; + self.gen + .opts + .trappable_error_type + .iter() + .filter_map(move |trappable| { + if let Some(name) = &trappable.wit_owner { + let owner_name = match owner { + TypeOwner::Interface(id) => resolve.interfaces[id].name.as_deref()?, + TypeOwner::World(id) => &resolve.worlds[id].name, + TypeOwner::None => return None, + }; + if owner_name != name { + return None; + } + } + let id = match owner { + TypeOwner::Interface(id) => { + *resolve.interfaces[id].types.get(&trappable.wit_name)? + } + // TODO: right now worlds can't have types defined within + // them but that's just a temporary limitation of + // `wit-parser`. Once that's filled in this should be + // replaced with a type-lookup in the world. + TypeOwner::World(_id) => unimplemented!(), + TypeOwner::None => return None, + }; + + Some((id, trappable.rust_name.clone())) + }) + } + + fn generate_trappable_error_types(&mut self, owner: TypeOwner) { + for (wit_type, trappable_type) in self.trappable_error_types(owner).collect::>() { + let info = self.info(wit_type); if self.lifetime_for(&info, TypeMode::Owned).is_some() { - panic!( - "type {:?} in interface {:?} is not 'static", - wit_typename, self.iface.name - ) + panic!("wit error for {trappable_type} is not 'static") } - let abi_type = self.param_name(*wit_type); + let abi_type = self.param_name(wit_type); uwriteln!( self.src, @@ -1164,8 +1332,12 @@ impl<'a> InterfaceGenerator<'a> { } impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { - fn iface(&self) -> &'a Interface { - self.iface + fn resolve(&self) -> &'a Resolve { + self.resolve + } + + fn current_interface(&self) -> Option { + self.current_interface } fn default_param_mode(&self) -> TypeMode { @@ -1177,6 +1349,6 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { } fn info(&self, ty: TypeId) -> TypeInfo { - self.types.get(ty) + self.gen.types.get(ty) } } diff --git a/crates/wit-bindgen/src/rust.rs b/crates/wit-bindgen/src/rust.rs index d791478356..bd936b0efc 100644 --- a/crates/wit-bindgen/src/rust.rs +++ b/crates/wit-bindgen/src/rust.rs @@ -11,11 +11,12 @@ pub enum TypeMode { } pub trait RustGenerator<'a> { - fn iface(&self) -> &'a Interface; + fn resolve(&self) -> &'a Resolve; fn push_str(&mut self, s: &str); fn info(&self, ty: TypeId) -> TypeInfo; fn default_param_mode(&self) -> TypeMode; + fn current_interface(&self) -> Option; fn print_ty(&mut self, ty: &Type, mode: TypeMode) { match ty { @@ -56,25 +57,41 @@ 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) } else { self.result_name(id) }; + if let TypeOwner::Interface(id) = ty.owner { + if let Some(name) = &self.resolve().interfaces[id].name { + match self.current_interface() { + Some(cur) if cur == id => {} + Some(_other) => { + self.push_str("super::"); + self.push_str(&name.to_snake_case()); + self.push_str("::"); + } + None => { + self.push_str(&name.to_snake_case()); + self.push_str("::"); + } + } + } + } self.push_str(&name); // 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.has_list && needs_generics(self.iface(), &ty.kind) { + if info.has_list && 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(_) @@ -87,9 +104,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!(), } } } @@ -150,6 +170,7 @@ pub trait RustGenerator<'a> { } TypeDefKind::Type(t) => self.print_ty(t, mode), + TypeDefKind::Unknown => unreachable!(), } } @@ -214,7 +235,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 { @@ -244,6 +265,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!(), }, } } @@ -309,7 +331,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() @@ -323,7 +345,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/wit-bindgen/src/types.rs b/crates/wit-bindgen/src/types.rs index 3709987f31..4805aa01db 100644 --- a/crates/wit-bindgen/src/types.rs +++ b/crates/wit-bindgen/src/types.rs @@ -34,31 +34,45 @@ 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, world: WorldId) { + let world = &resolve.worlds[world]; + for (_, item) in world.imports.iter().chain(world.exports.iter()) { + match item { + WorldItem::Function(f) => self.type_info_func(resolve, f), + WorldItem::Interface(id) => { + let iface = &resolve.interfaces[*id]; + + for (_, t) in iface.types.iter() { + self.type_id_info(resolve, *t); + } + for (_, f) in iface.functions.iter() { + self.type_info_func(resolve, f); + } + } + } } - for f in iface.functions.iter() { - for (_, ty) in f.params.iter() { - self.set_param_result_ty( - iface, - ty, - TypeInfo { - param: true, - ..TypeInfo::default() - }, - ); - } - for ty in f.results.iter_types() { - self.set_param_result_ty( - iface, - ty, - TypeInfo { - result: true, - ..TypeInfo::default() - }, - ); - } + } + + fn type_info_func(&mut self, resolve: &Resolve, func: &Function) { + for (_, ty) in func.params.iter() { + self.set_param_result_ty( + resolve, + ty, + TypeInfo { + param: true, + ..TypeInfo::default() + }, + ); + } + for ty in func.results.iter_types() { + self.set_param_result_ty( + resolve, + ty, + TypeInfo { + result: true, + ..TypeInfo::default() + }, + ); } } @@ -66,127 +80,131 @@ impl Types { self.type_info[&id] } - fn type_id_info(&mut self, iface: &Interface, ty: TypeId) -> TypeInfo { + 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 } - fn type_info(&mut self, iface: &Interface, ty: &Type) -> TypeInfo { + 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, ty: TypeId, info: TypeInfo) { - match &iface.types[ty].kind { + fn set_param_result_id(&mut self, resolve: &Resolve, ty: TypeId, info: TypeInfo) { + match &resolve.types[ty].kind { TypeDefKind::Record(r) => { for field in r.fields.iter() { - self.set_param_result_ty(iface, &field.ty, info) + self.set_param_result_ty(resolve, &field.ty, info) } } TypeDefKind::Tuple(t) => { for ty in t.types.iter() { - self.set_param_result_ty(iface, ty, info) + self.set_param_result_ty(resolve, ty, info) } } TypeDefKind::Flags(_) => {} TypeDefKind::Enum(_) => {} TypeDefKind::Variant(v) => { for case in v.cases.iter() { - self.set_param_result_optional_ty(iface, case.ty.as_ref(), info) + self.set_param_result_optional_ty(resolve, case.ty.as_ref(), info) } } TypeDefKind::List(ty) | TypeDefKind::Type(ty) | TypeDefKind::Option(ty) => { - self.set_param_result_ty(iface, ty, info) + self.set_param_result_ty(resolve, ty, info) } TypeDefKind::Result(r) => { - self.set_param_result_optional_ty(iface, r.ok.as_ref(), info); + self.set_param_result_optional_ty(resolve, r.ok.as_ref(), info); let mut info2 = info; info2.error = info.result; - self.set_param_result_optional_ty(iface, r.err.as_ref(), info2); + self.set_param_result_optional_ty(resolve, r.err.as_ref(), info2); } TypeDefKind::Union(u) => { for case in u.cases.iter() { - self.set_param_result_ty(iface, &case.ty, info) + self.set_param_result_ty(resolve, &case.ty, info) } } - TypeDefKind::Future(ty) => self.set_param_result_optional_ty(iface, ty.as_ref(), info), - TypeDefKind::Stream(stream) => { - self.set_param_result_optional_ty(iface, stream.element.as_ref(), info); - self.set_param_result_optional_ty(iface, stream.end.as_ref(), info); + TypeDefKind::Future(ty) => { + self.set_param_result_optional_ty(resolve, ty.as_ref(), info) } + TypeDefKind::Stream(stream) => { + self.set_param_result_optional_ty(resolve, stream.element.as_ref(), info); + self.set_param_result_optional_ty(resolve, stream.end.as_ref(), info); + } + TypeDefKind::Unknown => unreachable!(), } } - fn set_param_result_ty(&mut self, iface: &Interface, ty: &Type, info: TypeInfo) { + fn set_param_result_ty(&mut self, resolve: &Resolve, ty: &Type, info: TypeInfo) { match ty { Type::Id(id) => { - self.type_id_info(iface, *id); + self.type_id_info(resolve, *id); let cur = self.type_info.get_mut(id).unwrap(); let prev = *cur; *cur |= info; if prev != *cur { - self.set_param_result_id(iface, *id, info); + self.set_param_result_id(resolve, *id, info); } } _ => {} @@ -195,12 +213,12 @@ impl Types { fn set_param_result_optional_ty( &mut self, - iface: &Interface, + resolve: &Resolve, ty: Option<&Type>, info: TypeInfo, ) { match ty { - Some(ty) => self.set_param_result_ty(iface, ty, info), + Some(ty) => self.set_param_result_ty(resolve, ty, info), None => (), } } diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 4e4d3a75b6..adc74e69a4 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -585,6 +585,12 @@ criteria = "safe-to-deploy" version = "0.20.0" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wasm-encoder]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.21.0" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wasm-encoder]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -645,6 +651,12 @@ criteria = "safe-to-run" version = "0.2.13" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wasm-mutate]] +who = "Alex Crichton " +criteria = "safe-to-run" +version = "0.2.14" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wasm-smith]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -699,6 +711,12 @@ criteria = "safe-to-run" version = "0.11.10" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wasm-smith]] +who = "Alex Crichton " +criteria = "safe-to-run" +version = "0.11.11" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wasmi]] who = "Robin Freyler " criteria = "safe-to-run" @@ -801,6 +819,12 @@ criteria = "safe-to-deploy" version = "0.96.0" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wasmparser]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.97.0" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wasmparser-nostd]] who = "Alex Crichton " criteria = "safe-to-run" @@ -866,6 +890,12 @@ criteria = "safe-to-deploy" version = "0.2.45" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wasmprinter]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.2.46" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wast]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -920,6 +950,12 @@ criteria = "safe-to-deploy" version = "50.0.0" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wast]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "51.0.0" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wat]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -956,6 +992,12 @@ criteria = "safe-to-deploy" version = "1.0.52" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wat]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "1.0.53" +notes = "The Bytecode Alliance is the author of this crate." + [[audits.wat]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -1028,3 +1070,9 @@ criteria = "safe-to-deploy" version = "0.3.1" notes = "The Bytecode Alliance is the author of this crate." +[[audits.wit-parser]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.4.0" +notes = "The Bytecode Alliance is the author of this crate." + diff --git a/tests/all/component_model/bindgen.rs b/tests/all/component_model/bindgen.rs index a52655dbdc..8b41cb4854 100644 --- a/tests/all/component_model/bindgen.rs +++ b/tests/all/component_model/bindgen.rs @@ -12,14 +12,12 @@ mod no_imports { wasmtime::component::bindgen!({ inline: " - world no-imports { + default world no-imports { export foo: interface { foo: func() } - default export interface { - bar: func() - } + export bar: func() } ", }); @@ -59,14 +57,12 @@ mod one_import { wasmtime::component::bindgen!({ inline: " - world one-import { + default world one-import { import foo: interface { foo: func() } - default export interface { - bar: func() - } + export bar: func() } ", }); diff --git a/tests/all/component_model/bindgen/results.rs b/tests/all/component_model/bindgen/results.rs index 84c906d8e8..995a2e5289 100644 --- a/tests/all/component_model/bindgen/results.rs +++ b/tests/all/component_model/bindgen/results.rs @@ -9,14 +9,12 @@ mod empty_error { use super::*; wasmtime::component::bindgen!({ inline: " - world result-playground { + default world result-playground { import imports: interface { empty-error: func(a: float64) -> result } - default export interface { - empty-error: func(a: float64) -> result - } + export empty-error: func(a: float64) -> result }", }); @@ -108,14 +106,12 @@ mod string_error { use super::*; wasmtime::component::bindgen!({ inline: " - world result-playground { + default world result-playground { import imports: interface { string-error: func(a: float64) -> result } - default export interface { - string-error: func(a: float64) -> result - } + export string-error: func(a: float64) -> result }", }); @@ -223,9 +219,9 @@ mod enum_error { enum e1 { a, b, c } enum-error: func(a: float64) -> result } - world result-playground { - import imports: imports - default export interface { + default world result-playground { + import imports: self.imports + export foo: interface { enum e1 { a, b, c } enum-error: func(a: float64) -> result } @@ -273,11 +269,14 @@ mod enum_error { (with "libc" (instance $libc)) )) (func $f_enum_error - (export "enum-error") (param "a" float64) (result (result float64 (error (enum "a" "b" "c")))) (canon lift (core func $i "core_enum_error_export") (memory $libc "memory")) ) + + (instance (export "foo") + (export "enum-error" (func $f_enum_error)) + ) ) "# ), @@ -328,6 +327,7 @@ mod enum_error { assert_eq!( results + .foo() .enum_error(&mut store, 0.0) .expect("no trap") .expect("no error returned"), @@ -335,13 +335,18 @@ mod enum_error { ); let e = results + .foo() .enum_error(&mut store, 1.0) .expect("no trap") .err() .expect("error returned"); - assert_eq!(e, enum_error::E1::A); + assert_eq!(e, enum_error::foo::E1::A); - let e = results.enum_error(&mut store, 2.0).err().expect("trap"); + let e = results + .foo() + .enum_error(&mut store, 2.0) + .err() + .expect("trap"); assert_eq!( format!("{}", e.source().expect("trap message is stored in source")), "MyTrap" @@ -361,9 +366,9 @@ mod record_error { record e2 { line: u32, col: u32 } record-error: func(a: float64) -> result } - world result-playground { - import imports: imports - default export interface { + default world result-playground { + import imports: self.imports + export foo: interface { record e2 { line: u32, col: u32 } record-error: func(a: float64) -> result } @@ -411,11 +416,14 @@ mod record_error { (with "libc" (instance $libc)) )) (func $f_record_error - (export "record-error") (param "a" float64) (result (result float64 (error (record (field "line" u32) (field "col" u32))))) (canon lift (core func $i "core_record_error_export") (memory $libc "memory")) ) + + (instance (export "foo") + (export "record-error" (func $f_record_error)) + ) ) "# ), @@ -447,6 +455,7 @@ mod record_error { assert_eq!( results + .foo() .record_error(&mut store, 0.0) .expect("no trap") .expect("no error returned"), @@ -454,19 +463,24 @@ mod record_error { ); let e = results + .foo() .record_error(&mut store, 1.0) .expect("no trap") .err() .expect("error returned"); assert!(matches!( e, - record_error::E2 { + record_error::foo::E2 { line: 420, col: 1312 } )); - let e = results.record_error(&mut store, 2.0).err().expect("trap"); + let e = results + .foo() + .record_error(&mut store, 2.0) + .err() + .expect("trap"); assert_eq!( format!("{}", e.source().expect("trap message is stored in source")), "record_error: trap" @@ -486,9 +500,9 @@ mod variant_error { variant e3 { E1(e1), E2(e2) } variant-error: func(a: float64) -> result } - world result-playground { - import imports: imports - default export interface { + default world result-playground { + import imports: self.imports + export foo: interface { enum e1 { a, b, c } record e2 { line: u32, col: u32 } variant e3 { E1(e1), E2(e2) } @@ -538,11 +552,14 @@ mod variant_error { (with "libc" (instance $libc)) )) (func $f_variant_error - (export "variant-error") (param "a" float64) (result (result float64 (error (variant (case "E1" (enum "a" "b" "c")) (case "E2"(record (field "line" u32) (field "col" u32))))))) (canon lift (core func $i "core_variant_error_export") (memory $libc "memory")) ) + + (instance (export "foo") + (export "variant-error" (func $f_variant_error)) + ) ) "# ), @@ -574,6 +591,7 @@ mod variant_error { assert_eq!( results + .foo() .variant_error(&mut store, 0.0) .expect("no trap") .expect("no error returned"), @@ -581,19 +599,24 @@ mod variant_error { ); let e = results + .foo() .variant_error(&mut store, 1.0) .expect("no trap") .err() .expect("error returned"); assert!(matches!( e, - variant_error::E3::E2(variant_error::E2 { + variant_error::foo::E3::E2(variant_error::foo::E2 { line: 420, col: 1312 }) )); - let e = results.variant_error(&mut store, 2.0).err().expect("trap"); + let e = results + .foo() + .variant_error(&mut store, 2.0) + .err() + .expect("trap"); assert_eq!( format!("{}", e.source().expect("trap message is stored in source")), "variant_error: trap"