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"