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
This commit is contained in:
Alex Crichton
2023-01-18 09:37:03 -06:00
committed by GitHub
parent 9b896d2a70
commit 247851234b
35 changed files with 1192 additions and 546 deletions

41
Cargo.lock generated
View File

@@ -3255,18 +3255,18 @@ checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
[[package]] [[package]]
name = "wasm-encoder" name = "wasm-encoder"
version = "0.20.0" version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05632e0a66a6ed8cca593c24223aabd6262f256c3693ad9822c315285f010614" checksum = "29ab2fe77b325731603297debb4573e002d06ae0aa1f4dc108585c81961e0609"
dependencies = [ dependencies = [
"leb128", "leb128",
] ]
[[package]] [[package]]
name = "wasm-mutate" name = "wasm-mutate"
version = "0.2.13" version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0e9666b36c1e7e88bf1e0d114c8a4fe7d4e32d3aa38c1e8e9d71a972b99764a" checksum = "c2f8b34ecab2aadb3a974fc96d38a37780793b1a44f9e681ed68f7e69757ca90"
dependencies = [ dependencies = [
"egg", "egg",
"log", "log",
@@ -3278,9 +3278,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-smith" name = "wasm-smith"
version = "0.11.10" version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ca3f5a24b691771929ac6adf330972e8ecbfe48dcf8809d7bf83216c124cfd" checksum = "00e76ca2ad5d10fdcd08d9af00ac02585a1e53a60020b0eda8874922f5d49bff"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
"flagset", "flagset",
@@ -3330,9 +3330,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.96.0" version = "0.97.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adde01ade41ab9a5d10ec8ed0bb954238cf8625b5cd5a13093d6de2ad9c2be1a" checksum = "b98123a0d2bacf9286239231b116cbd66c65d9b89793f7c9bba3a3ae7f1b15f3"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"url", "url",
@@ -3349,9 +3349,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmprinter" name = "wasmprinter"
version = "0.2.45" version = "0.2.46"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3045e1aa2cac847f4f94a1e25db9f084a947aeff47d9099fb9c5ccd16d335040" checksum = "595cca929e47a7bec3c941b5a8e133f51b17e6d9dd8c82ab97902196f5a07b42"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"wasmparser", "wasmparser",
@@ -3502,7 +3502,7 @@ dependencies = [
"wasmtime-wasi-crypto", "wasmtime-wasi-crypto",
"wasmtime-wasi-nn", "wasmtime-wasi-nn",
"wasmtime-wast", "wasmtime-wast",
"wast 50.0.0", "wast 51.0.0",
"wat", "wat",
"windows-sys", "windows-sys",
] ]
@@ -3523,6 +3523,7 @@ dependencies = [
name = "wasmtime-component-macro" name = "wasmtime-component-macro"
version = "6.0.0" version = "6.0.0"
dependencies = [ dependencies = [
"anyhow",
"component-macro-test-helpers", "component-macro-test-helpers",
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -3775,7 +3776,7 @@ dependencies = [
"anyhow", "anyhow",
"log", "log",
"wasmtime", "wasmtime",
"wast 50.0.0", "wast 51.0.0",
] ]
[[package]] [[package]]
@@ -3810,9 +3811,9 @@ dependencies = [
[[package]] [[package]]
name = "wast" name = "wast"
version = "50.0.0" version = "51.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2cbb59d4ac799842791fe7e806fa5dbbf6b5554d538e51cc8e176db6ff0ae34" checksum = "a1f621e6e9af96438d3e05f0699da5b1dae59f2df964a2982166aa9b03c5b599"
dependencies = [ dependencies = [
"leb128", "leb128",
"memchr", "memchr",
@@ -3822,11 +3823,11 @@ dependencies = [
[[package]] [[package]]
name = "wat" name = "wat"
version = "1.0.52" version = "1.0.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "584aaf7a1ecf4d383bbe1a25eeab0cbb8ff96acc6796707ff65cde48f4632f15" checksum = "5dd18c1168d7e8743d9b4f713c0203924f5dcc4a3983eb5e584de9614f9fccde"
dependencies = [ dependencies = [
"wast 50.0.0", "wast 51.0.0",
] ]
[[package]] [[package]]
@@ -4032,15 +4033,17 @@ dependencies = [
[[package]] [[package]]
name = "wit-parser" name = "wit-parser"
version = "0.3.1" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703eb1d2f89ff2c52d50f7ff002735e423cea75f0a5dc5c8a4626c4c47cd9ca6" checksum = "02cfa79275011530f37e0e164183c606bae1cdc466ea90bcd364d50605486a4d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"id-arena", "id-arena",
"indexmap", "indexmap",
"log",
"pulldown-cmark", "pulldown-cmark",
"unicode-xid", "unicode-xid",
"url",
] ]
[[package]] [[package]]

View File

@@ -158,14 +158,14 @@ winch-codegen = { path = "winch/codegen", version = "=0.4.0" }
target-lexicon = { version = "0.12.3", default-features = false, features = ["std"] } target-lexicon = { version = "0.12.3", default-features = false, features = ["std"] }
anyhow = "1.0.22" anyhow = "1.0.22"
wasmparser = "0.96.0" wasmparser = "0.97.0"
wat = "1.0.52" wat = "1.0.53"
wast = "50.0.0" wast = "51.0.0"
wasmprinter = "0.2.45" wasmprinter = "0.2.46"
wasm-encoder = "0.20.0" wasm-encoder = "0.21.0"
wasm-smith = "0.11.10" wasm-smith = "0.11.11"
wasm-mutate = "0.2.13" wasm-mutate = "0.2.14"
wit-parser = "0.3" wit-parser = "0.4"
windows-sys = "0.42.0" windows-sys = "0.42.0"
env_logger = "0.9" env_logger = "0.9"
rustix = "0.36.0" rustix = "0.36.0"

View File

@@ -16,6 +16,7 @@ test = false
doctest = false doctest = false
[dependencies] [dependencies]
anyhow = "1.0"
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"
syn = { version = "1.0", features = ["extra-traits"] } syn = { version = "1.0", features = ["extra-traits"] }

View File

@@ -3,14 +3,14 @@ use std::path::{Path, PathBuf};
use syn::parse::{Error, Parse, ParseStream, Result}; use syn::parse::{Error, Parse, ParseStream, Result};
use syn::punctuated::Punctuated; use syn::punctuated::Punctuated;
use syn::{braced, token, Ident, Token}; use syn::{braced, token, Ident, Token};
use wasmtime_wit_bindgen::Opts; use wasmtime_wit_bindgen::{Opts, TrappableError};
use wit_parser::{Document, World}; use wit_parser::{PackageId, Resolve, UnresolvedPackage, WorldId};
#[derive(Default)]
pub struct Config { pub struct Config {
opts: Opts, // ... opts: Opts,
world: World, resolve: Resolve,
files: Vec<String>, world: WorldId,
files: Vec<PathBuf>,
} }
pub fn expand(input: &Config) -> Result<TokenStream> { pub fn expand(input: &Config) -> Result<TokenStream> {
@@ -21,18 +21,14 @@ pub fn expand(input: &Config) -> Result<TokenStream> {
)); ));
} }
let src = input.opts.generate(&input.world); let src = input.opts.generate(&input.resolve, input.world);
let mut contents = src.parse::<TokenStream>().unwrap(); let mut contents = src.parse::<TokenStream>().unwrap();
// Include a dummy `include_str!` for any files we read so rustc knows that // Include a dummy `include_str!` for any files we read so rustc knows that
// we depend on the contents of those files. // we depend on the contents of those files.
let cwd = std::env::var("CARGO_MANIFEST_DIR").unwrap();
for file in input.files.iter() { for file in input.files.iter() {
contents.extend( contents.extend(
format!( format!("const _: &str = include_str!(r#\"{}\"#);\n", file.display())
"const _: &str = include_str!(r#\"{}\"#);\n",
Path::new(&cwd).join(file).display()
)
.parse::<TokenStream>() .parse::<TokenStream>()
.unwrap(), .unwrap(),
); );
@@ -41,73 +37,146 @@ pub fn expand(input: &Config) -> Result<TokenStream> {
Ok(contents) Ok(contents)
} }
enum Source {
Path(String),
Inline(String),
}
impl Parse for Config { impl Parse for Config {
fn parse(input: ParseStream<'_>) -> Result<Self> { fn parse(input: ParseStream<'_>) -> Result<Self> {
let call_site = Span::call_site(); let call_site = Span::call_site();
let mut opts = Opts::default();
let mut world = None; 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; let content;
syn::braced!(content in input); syn::braced!(content in input);
let fields = Punctuated::<Opt, Token![,]>::parse_terminated(&content)?; let fields = Punctuated::<Opt, Token![,]>::parse_terminated(&content)?;
let mut document = None;
for field in fields.into_pairs() { for field in fields.into_pairs() {
match field.into_value() { match field.into_value() {
Opt::Path(path) => { Opt::Path(s) => {
if world.is_some() { if source.is_some() {
return Err(Error::new(path.span(), "cannot specify second world")); 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) => { Opt::World(s) => {
if world.is_some() { if document.is_some() {
return Err(Error::new(span, "cannot specify second world")); 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::Inline(s) => {
Opt::Async(val) => ret.opts.async_ = val, if source.is_some() {
Opt::TrappableErrorType(val) => ret.opts.trappable_error_type = val, 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 { } else {
let s = input.parse::<syn::LitStr>()?; let document = input.parse::<syn::LitStr>()?;
world = Some(ret.parse(s)?); if input.parse::<Option<syn::token::In>>()?.is_some() {
source = Some(Source::Path(input.parse::<syn::LitStr>()?.value()));
} }
ret.world = world.ok_or_else(|| { parse_doc(&document.value(), &mut world)
Error::new( };
call_site, let (resolve, pkg, files) =
"must specify a `*.wit` file to generate bindings for", 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"))
})?; })?;
Ok(ret)
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_source(source: &Option<Source>) -> anyhow::Result<(Resolve, PackageId, Vec<PathBuf>)> {
fn parse(&mut self, path: syn::LitStr) -> Result<World> { let mut resolve = Resolve::default();
let span = path.span(); let mut files = Vec::new();
let path = path.value(); let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); let mut parse = |path: &Path| -> anyhow::Result<_> {
let path = manifest_dir.join(path); if path.is_dir() {
self.files.push(path.to_str().unwrap().to_string()); let (pkg, sources) = resolve.push_dir(&path)?;
World::parse_file(path).map_err(|e| Error::new(span, e)) 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>) -> String {
match s.find('.') {
Some(pos) => {
*world = Some(s[pos + 1..].to_string());
s[..pos].to_string()
}
None => s.to_string(),
} }
} }
mod kw { mod kw {
syn::custom_keyword!(path);
syn::custom_keyword!(inline); syn::custom_keyword!(inline);
syn::custom_keyword!(path);
syn::custom_keyword!(tracing); syn::custom_keyword!(tracing);
syn::custom_keyword!(trappable_error_type); syn::custom_keyword!(trappable_error_type);
syn::custom_keyword!(world);
} }
enum Opt { enum Opt {
World(syn::LitStr),
Path(syn::LitStr), Path(syn::LitStr),
Inline(Span, World), Inline(syn::LitStr),
Tracing(bool), Tracing(bool),
Async(bool), Async(bool),
TrappableErrorType(Vec<(String, String, String)>), TrappableErrorType(Vec<TrappableError>),
} }
impl Parse for Opt { impl Parse for Opt {
@@ -118,14 +187,13 @@ impl Parse for Opt {
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
Ok(Opt::Path(input.parse()?)) Ok(Opt::Path(input.parse()?))
} else if l.peek(kw::inline) { } else if l.peek(kw::inline) {
let span = input.parse::<kw::inline>()?.span; input.parse::<kw::inline>()?;
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
let s = input.parse::<syn::LitStr>()?; Ok(Opt::Inline(input.parse()?))
let world = Document::parse("<macro-input>".as_ref(), &s.value()) } else if l.peek(kw::world) {
.map_err(|e| Error::new(s.span(), e))? input.parse::<kw::world>()?;
.into_world() input.parse::<Token![:]>()?;
.map_err(|e| Error::new(s.span(), e))?; Ok(Opt::World(input.parse()?))
Ok(Opt::Inline(span, world))
} else if l.peek(kw::tracing) { } else if l.peek(kw::tracing) {
input.parse::<kw::tracing>()?; input.parse::<kw::tracing>()?;
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;
@@ -141,7 +209,16 @@ impl Parse for Opt {
let _lbrace = braced!(contents in input); let _lbrace = braced!(contents in input);
let fields: Punctuated<(String, String, String), Token![,]> = let fields: Punctuated<(String, String, String), Token![,]> =
contents.parse_terminated(trappable_error_field_parse)?; 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 { } else {
Err(l.error()) Err(l.error())
} }

View File

@@ -12,10 +12,10 @@ pub fn foreach(input: TokenStream) -> TokenStream {
let f = f.unwrap().path(); let f = f.unwrap().path();
if f.extension().and_then(|s| s.to_str()) == Some("wit") { if f.extension().and_then(|s| s.to_str()) == Some("wit") {
let name = f.file_stem().unwrap().to_str().unwrap(); 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(); let path = f.to_str().unwrap();
result.push(quote! { result.push(quote! {
#input!(#name #path); #input!(#ident #name #path);
}); });
} }
} }

View File

@@ -1,17 +1,19 @@
macro_rules! gentest { macro_rules! gentest {
($id:ident $path:tt) => { ($id:ident $name:tt $path:tt) => {
mod $id { mod $id {
mod normal { mod normal {
wasmtime::component::bindgen!($path); wasmtime::component::bindgen!($name in $path);
} }
mod async_ { mod async_ {
wasmtime::component::bindgen!({ wasmtime::component::bindgen!({
world: $name,
path: $path, path: $path,
async: true, async: true,
}); });
} }
mod tracing { mod tracing {
wasmtime::component::bindgen!({ wasmtime::component::bindgen!({
world: $name,
path: $path, path: $path,
tracing: true, tracing: true,
}); });

View File

@@ -5,8 +5,7 @@ interface chars {
return-char: func() -> char return-char: func() -> char
} }
world the-world { default world the-world {
import imports: chars import imports: self.chars
export exports: chars export exports: self.chars
default export chars
} }

View File

@@ -32,8 +32,7 @@ interface conventions {
%bool: func() %bool: func()
} }
world the-world { default world the-world {
import imports: conventions import imports: self.conventions
export exports: conventions export exports: self.conventions
default export conventions
} }

View File

@@ -1 +1 @@
world empty {} default world empty {}

View File

@@ -47,8 +47,7 @@ interface flegs {
roundtrip-flag64: func(x: flag64) -> flag64 roundtrip-flag64: func(x: flag64) -> flag64
} }
world the-flags { default world the-flags {
import import-flags: flegs import import-flags: self.flegs
export export-flags: flegs export export-flags: self.flegs
default export flegs
} }

View File

@@ -5,8 +5,7 @@ interface floats {
float64-result: func() -> float64 float64-result: func() -> float64
} }
world the-world { default world the-world {
import imports: floats import imports: self.floats
export exports: floats export exports: self.floats
default export floats
} }

View File

@@ -32,8 +32,7 @@ interface integers {
pair-ret: func() -> tuple<s64, u8> pair-ret: func() -> tuple<s64, u8>
} }
world the-world { default world the-world {
import imports: integers import imports: self.integers
export exports: integers export exports: self.integers
default export integers
} }

View File

@@ -77,8 +77,7 @@ interface lists {
load-store-everything: func(a: load-store-all-sizes) -> load-store-all-sizes load-store-everything: func(a: load-store-all-sizes) -> load-store-all-sizes
} }
world the-lists { default world the-lists {
import import-lists: lists import import-lists: self.lists
export export-lists: lists export export-lists: self.lists
default export lists
} }

View File

@@ -44,8 +44,7 @@ interface manyarg {
big-argument: func(x: big-struct) big-argument: func(x: big-struct)
} }
world the-world { default world the-world {
import imports: manyarg import imports: self.manyarg
export exports: manyarg export exports: self.manyarg
default export manyarg
} }

View File

@@ -6,8 +6,7 @@ interface multi-return {
mre: func() -> (a: u32, b: float32) mre: func() -> (a: u32, b: float32)
} }
world the-world { default world the-world {
import imports: multi-return import imports: self.multi-return
export exports: multi-return export exports: self.multi-return
default export multi-return
} }

View File

@@ -53,8 +53,7 @@ interface records {
typedef-inout: func(e: tuple-typedef2) -> s32 typedef-inout: func(e: tuple-typedef2) -> s32
} }
world the-world { default world the-world {
import imports: records import imports: self.records
export exports: records export exports: self.records
default export records
} }

View File

@@ -9,8 +9,7 @@ interface simple {
f6: func(a: u32, b: u32, c: u32) -> tuple<u32, u32, u32> f6: func(a: u32, b: u32, c: u32) -> tuple<u32, u32, u32>
} }
world the-world { default world the-world {
import imports: simple import imports: self.simple
export exports: simple export exports: self.simple
default export simple
} }

View File

@@ -1,13 +1,11 @@
interface simple-lists { interface simple-lists {
simple-list1: func(l: list<u32>) simple-list1: func(l: list<u32>)
simple-list2: func() -> list<u32> simple-list2: func() -> list<u32>
// TODO: reenable this when smw implements this simple-list3: func(a: list<u32>, b: list<u32>) -> tuple<list<u32>, list<u32>>
// simple-list3: func(a: list<u32>, b: list<u32>) -> tuple<list<u32>, list<u32>>
simple-list4: func(l: list<list<u32>>) -> list<list<u32>> simple-list4: func(l: list<list<u32>>) -> list<list<u32>>
} }
world my-world { default world my-world {
import imports: simple-lists import imports: self.simple-lists
export exports: simple-lists export exports: self.simple-lists
default export simple-lists
} }

View File

@@ -7,8 +7,7 @@ interface anon {
option-test: func() -> result<option<string>, error> option-test: func() -> result<option<string>, error>
} }
world the-world { default world the-world {
import imports: anon import imports: self.anon
export exports: anon export exports: self.anon
default export anon
} }

View File

@@ -1,5 +1,3 @@
world the-world { default world the-world {
default export interface { export y: func()
y: func()
}
} }

View File

@@ -1,4 +1,4 @@
world the-world { default world the-world {
export the-name: interface { export the-name: interface {
y: func() y: func()
} }

View File

@@ -1,4 +1,4 @@
world the-world { default world the-world {
import imports: interface { import imports: interface {
y: func() y: func()
} }

View File

@@ -4,8 +4,7 @@ interface strings {
c: func(a: string, b: string) -> string c: func(a: string, b: string) -> string
} }
world the-world { default world the-world {
import imports: strings import imports: self.strings
export exports: strings export exports: self.strings
default export strings
} }

View File

@@ -58,8 +58,7 @@ interface unions {
identify-distinguishable-num: func(num: distinguishable-num) -> u8 identify-distinguishable-num: func(num: distinguishable-num) -> u8
} }
world the-unions { default world the-unions {
import import-unions: unions import import-unions: self.unions
export export-unions: unions export export-unions: self.unions
default export unions
} }

View File

@@ -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
}
}

View File

@@ -139,8 +139,7 @@ interface variants {
return-named-result: func() -> (a: result<u8, my-errno>) return-named-result: func() -> (a: result<u8, my-errno>)
} }
world my-world { default world my-world {
import imports: variants import imports: self.variants
export exports: variants export exports: self.variants
default export variants
} }

View File

@@ -191,6 +191,9 @@ enum LocalInitializer<'data> {
AliasComponentExport(ComponentInstanceIndex, &'data str), AliasComponentExport(ComponentInstanceIndex, &'data str),
AliasModule(ClosedOverModule), AliasModule(ClosedOverModule),
AliasComponent(ClosedOverComponent), AliasComponent(ClosedOverComponent),
// export section
Export(ComponentItem),
} }
/// The "closure environment" of components themselves. /// 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 item = self.kind_to_item(export.kind, export.index);
let prev = self.result.exports.insert(export.name, item); let prev = self.result.exports.insert(export.name, item);
assert!(prev.is_none()); assert!(prev.is_none());
self.result
.initializers
.push(LocalInitializer::Export(item));
} }
} }

View File

@@ -731,6 +731,29 @@ impl<'a> Inliner<'a> {
AliasComponent(idx) => { AliasComponent(idx) => {
frame.components.push(frame.closed_over_component(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) Ok(None)

View File

@@ -20,7 +20,7 @@ pub use self::instance::{ExportInstance, Exports, Instance, InstancePre};
pub use self::linker::{Linker, LinkerInstance}; pub use self::linker::{Linker, LinkerInstance};
pub use self::types::Type; pub use self::types::Type;
pub use self::values::Val; 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 // These items are expected to be used by an eventual
// `#[derive(ComponentType)]`, they are not part of Wasmtime's API stability // `#[derive(ComponentType)]`, they are not part of Wasmtime's API stability
@@ -42,3 +42,249 @@ pub mod __internal {
} }
pub(crate) use self::store::ComponentStoreData; 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<String> {
/// 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<T>` 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<u8>) -> 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<u32> {
/// Ok(rand::thread_rng().gen())
/// }
///
/// fn sha256(&mut self, bytes: Vec<u8>) -> Result<String> {
/// // ...
/// }
/// }
///
/// 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<T, error-type>` into `Result<T, RustErrorType>` in Rust.
/// // The `RustErrorType` structure will have an automatically generated
/// // implementation of `From<ErrorType> 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;

View File

@@ -29,8 +29,14 @@ use source::Source;
struct Wasmtime { struct Wasmtime {
src: Source, src: Source,
opts: Opts, opts: Opts,
imports: Vec<String>, imports: Vec<Import>,
exports: Exports, exports: Exports,
types: Types,
}
enum Import {
Interface { snake: String },
Function { add_to_linker: String, sig: String },
} }
#[derive(Default)] #[derive(Default)]
@@ -50,40 +56,66 @@ pub struct Opts {
/// Whether or not to use async rust functions and traits. /// Whether or not to use async rust functions and traits.
pub async_: bool, pub async_: bool,
/// For a given wit interface and type name, generate a "trappable error type" /// A list of "trappable errors" which are used to replace the `E` in
/// of the following Rust type name /// `result<T, E>` found in WIT.
pub trappable_error_type: Vec<(String, String, String)>, pub trappable_error_type: Vec<TrappableError>,
}
#[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<String>,
/// The name, in Rust, of the error type to generate.
pub rust_name: String,
} }
impl Opts { impl Opts {
pub fn generate(&self, world: &World) -> String { pub fn generate(&self, resolve: &Resolve, world: WorldId) -> String {
let mut r = Wasmtime::default(); let mut r = Wasmtime::default();
r.opts = self.clone(); r.opts = self.clone();
r.generate(world) r.generate(resolve, world)
} }
} }
impl Wasmtime { 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() { for (name, import) in world.imports.iter() {
self.import(name, import); self.import(resolve, name, import);
} }
for (name, export) in world.exports.iter() { for (name, export) in world.exports.iter() {
self.export(name, export); self.export(resolve, name, export);
} }
if let Some(iface) = &world.default { self.finish(resolve, id)
self.export_default(&world.name, iface);
}
self.finish(world)
} }
fn import(&mut self, name: &str, iface: &Interface) { fn import(&mut self, resolve: &Resolve, name: &str, item: &WorldItem) {
let mut gen = InterfaceGenerator::new(self, iface, TypeMode::Owned);
gen.types();
gen.generate_trappable_error_types();
gen.generate_add_to_linker(name);
let snake = name.to_snake_case(); let snake = name.to_snake_case();
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);
let module = &gen.src[..]; let module = &gen.src[..];
uwriteln!( uwriteln!(
@@ -98,18 +130,34 @@ impl Wasmtime {
}} }}
" "
); );
Import::Interface { snake }
}
};
self.imports.push(snake); self.imports.push(import);
} }
fn export(&mut self, name: &str, iface: &Interface) { fn export(&mut self, resolve: &Resolve, name: &str, item: &WorldItem) {
let mut gen = InterfaceGenerator::new(self, iface, TypeMode::AllBorrowed("'a")); let snake = name.to_snake_case();
gen.types(); let mut gen = InterfaceGenerator::new(self, resolve, TypeMode::AllBorrowed("'a"));
gen.generate_trappable_error_types(); 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];
let camel = name.to_upper_camel_case(); let camel = name.to_upper_camel_case();
uwriteln!(gen.src, "pub struct {camel} {{"); uwriteln!(gen.src, "pub struct {camel} {{");
for func in iface.functions.iter() { for (_, func) in iface.functions.iter() {
uwriteln!( uwriteln!(
gen.src, gen.src,
"{}: wasmtime::component::Func,", "{}: wasmtime::component::Func,",
@@ -127,22 +175,23 @@ impl Wasmtime {
) -> anyhow::Result<{camel}> {{ ) -> anyhow::Result<{camel}> {{
" "
); );
let fields = gen.extract_typed_functions(); let mut fields = Vec::new();
for (name, getter) in fields.iter() { for (_, func) in iface.functions.iter() {
let (name, getter) = gen.extract_typed_function(func);
uwriteln!(gen.src, "let {name} = {getter};"); uwriteln!(gen.src, "let {name} = {getter};");
fields.push(name);
} }
uwriteln!(gen.src, "Ok({camel} {{"); uwriteln!(gen.src, "Ok({camel} {{");
for (name, _) in fields.iter() { for name in fields {
uwriteln!(gen.src, "{name},"); uwriteln!(gen.src, "{name},");
} }
uwriteln!(gen.src, "}})"); uwriteln!(gen.src, "}})");
uwriteln!(gen.src, "}}"); uwriteln!(gen.src, "}}");
for func in iface.functions.iter() { for (_, func) in iface.functions.iter() {
gen.define_rust_guest_export(Some(name), func); gen.define_rust_guest_export(Some(name), func);
} }
uwriteln!(gen.src, "}}"); uwriteln!(gen.src, "}}");
let snake = name.to_snake_case();
let module = &gen.src[..]; let module = &gen.src[..];
uwriteln!( uwriteln!(
@@ -166,11 +215,6 @@ impl Wasmtime {
)?\ )?\
" "
); );
let prev = self
.exports
.fields
.insert(snake.clone(), (format!("{snake}::{camel}"), getter));
assert!(prev.is_none());
self.exports.funcs.push(format!( self.exports.funcs.push(format!(
" "
pub fn {snake}(&self) -> &{snake}::{camel} {{ pub fn {snake}(&self) -> &{snake}::{camel} {{
@@ -178,35 +222,15 @@ impl Wasmtime {
}} }}
" "
)); ));
(format!("{snake}::{camel}"), getter)
} }
};
fn export_default(&mut self, _name: &str, iface: &Interface) { let prev = self.exports.fields.insert(snake.clone(), (ty, getter));
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()); assert!(prev.is_none());
} }
for func in iface.functions.iter() { fn finish(&mut self, resolve: &Resolve, world: WorldId) -> String {
let prev = mem::take(&mut gen.src); let camel = resolve.worlds[world].name.to_upper_camel_case();
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();
uwriteln!(self.src, "pub struct {camel} {{"); uwriteln!(self.src, "pub struct {camel} {{");
for (name, (ty, _)) in self.exports.fields.iter() { for (name, (ty, _)) in self.exports.fields.iter() {
uwriteln!(self.src, "{name}: {ty},"); uwriteln!(self.src, "{name}: {ty},");
@@ -219,13 +243,16 @@ impl Wasmtime {
("", "", "", "") ("", "", "", "")
}; };
self.toplevel_import_trait(resolve, world);
uwriteln!(self.src, "const _: () = {{"); uwriteln!(self.src, "const _: () = {{");
uwriteln!(self.src, "use wasmtime::component::__internal::anyhow;"); uwriteln!(self.src, "use wasmtime::component::__internal::anyhow;");
uwriteln!(self.src, "impl {camel} {{");
self.toplevel_add_to_linker(resolve, world);
uwriteln!( uwriteln!(
self.src, self.src,
" "
impl {camel} {{
/// Instantiates the provided `module` using the specified /// Instantiates the provided `module` using the specified
/// parameters, wrapping up the result in a structure that /// parameters, wrapping up the result in a structure that
/// translates between wasm and the host. /// translates between wasm and the host.
@@ -303,52 +330,138 @@ impl Wasmtime {
} }
impl Wasmtime { impl Wasmtime {
fn trappable_error_types<'a>( fn toplevel_import_trait(&mut self, resolve: &Resolve, world: WorldId) {
&'a self, let mut functions = Vec::new();
iface: &'a Interface, for import in self.imports.iter() {
) -> impl Iterator<Item = (&String, &TypeId, &String)> + 'a { match import {
self.opts Import::Interface { .. } => continue,
.trappable_error_type 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<T, U>(
linker: &mut wasmtime::component::Linker<T>,
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() .iter()
.filter(|(interface_name, _, _)| iface.name == *interface_name) .map(|n| format!("{n}::{}", n.to_upper_camel_case()))
.filter_map(|(_, wit_typename, rust_typename)| { .chain(if functions.is_empty() {
let wit_type = iface.type_lookup.get(wit_typename)?; None
Some((wit_typename, wit_type, rust_typename)) } 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<T, U>(
linker: &mut wasmtime::component::Linker<T>,
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> { struct InterfaceGenerator<'a> {
src: Source, src: Source,
gen: &'a mut Wasmtime, gen: &'a mut Wasmtime,
iface: &'a Interface, resolve: &'a Resolve,
default_param_mode: TypeMode, default_param_mode: TypeMode,
types: Types, current_interface: Option<InterfaceId>,
} }
impl<'a> InterfaceGenerator<'a> { impl<'a> InterfaceGenerator<'a> {
fn new( fn new(
gen: &'a mut Wasmtime, gen: &'a mut Wasmtime,
iface: &'a Interface, resolve: &'a Resolve,
default_param_mode: TypeMode, default_param_mode: TypeMode,
) -> InterfaceGenerator<'a> { ) -> InterfaceGenerator<'a> {
let mut types = Types::default();
types.analyze(iface);
InterfaceGenerator { InterfaceGenerator {
src: Source::default(), src: Source::default(),
gen, gen,
iface, resolve,
types,
default_param_mode, default_param_mode,
current_interface: None,
} }
} }
fn types(&mut self) { fn types(&mut self, id: InterfaceId) {
for (id, ty) in self.iface.types.iter() { for (name, id) in self.resolve.interfaces[id].types.iter() {
let name = match &ty.name { let id = *id;
Some(name) => name, let ty = &self.resolve.types[id];
None => continue,
};
match &ty.kind { match &ty.kind {
TypeDefKind::Record(record) => self.type_record(id, name, record, &ty.docs), TypeDefKind::Record(record) => self.type_record(id, name, record, &ty.docs),
TypeDefKind::Flags(flags) => self.type_flags(id, name, flags, &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::Type(t) => self.type_alias(id, name, t, &ty.docs),
TypeDefKind::Future(_) => todo!("generate for future"), TypeDefKind::Future(_) => todo!("generate for future"),
TypeDefKind::Stream(_) => todo!("generate for stream"), 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 // We fillin a special trappable error type in the case when a function has just one
// result, which is itself a `result<a, e>`, and the `e` is *not* a primitive // result, which is itself a `result<a, e>`, and the `e` is *not* a primitive
// (i.e. defined in std) type, and matches the typename given by the user. // (i.e. defined in std) type, and matches the typename given by the user.
let mut i = results.iter_types(); let mut i = results.iter_types();
if i.len() == 1 { let id = match i.next()? {
match i.next().unwrap() { Type::Id(id) => id,
Type::Id(id) => match &self.iface.types[*id].kind { _ => return None,
TypeDefKind::Result(r) => match r.err { };
Some(Type::Id(error_typeid)) => self if i.next().is_some() {
.gen return None;
.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 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 camel = name.to_upper_camel_case();
let owner = TypeOwner::Interface(id);
if self.gen.opts.async_ { if self.gen.opts.async_ {
uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]") 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 // Generate the `pub trait` which represents the host functionality for
// this import. // this import.
uwriteln!(self.src, "pub trait {camel}: Sized {{"); uwriteln!(self.src, "pub trait {camel}: Sized {{");
for func in self.iface.functions.iter() { for (_, func) in iface.functions.iter() {
self.rustdoc(&func.docs); self.generate_function_trait_sig(owner, func);
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<ok,err>` get special
// cased to use the host_wasmtime_rust::Error<err>, 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");
} }
uwriteln!(self.src, "}}"); uwriteln!(self.src, "}}");
@@ -885,10 +966,17 @@ impl<'a> InterfaceGenerator<'a> {
" "
); );
uwriteln!(self.src, "let mut inst = linker.instance(\"{name}\")?;"); uwriteln!(self.src, "let mut inst = linker.instance(\"{name}\")?;");
for func in self.iface.functions.iter() { for (_, func) in iface.functions.iter() {
self.generate_add_function_to_linker(owner, func, "inst");
}
uwriteln!(self.src, "Ok(())");
uwriteln!(self.src, "}}");
}
fn generate_add_function_to_linker(&mut self, owner: TypeOwner, func: &Function, linker: &str) {
uwrite!( uwrite!(
self.src, self.src,
"inst.{}(\"{}\", ", "{linker}.{}(\"{}\", ",
if self.gen.opts.async_ { if self.gen.opts.async_ {
"func_wrap_async" "func_wrap_async"
} else { } else {
@@ -896,14 +984,11 @@ impl<'a> InterfaceGenerator<'a> {
}, },
func.name func.name
); );
self.generate_guest_import_closure(func); self.generate_guest_import_closure(owner, func);
uwriteln!(self.src, ")?;") uwriteln!(self.src, ")?;")
} }
uwriteln!(self.src, "Ok(())");
uwriteln!(self.src, "}}");
}
fn generate_guest_import_closure(&mut self, func: &Function) { fn generate_guest_import_closure(&mut self, owner: TypeOwner, func: &Function) {
// Generate the closure that's passed to a `Linker`, the final piece of // Generate the closure that's passed to a `Linker`, the final piece of
// codegen here. // codegen here.
self.src self.src
@@ -936,7 +1021,15 @@ impl<'a> InterfaceGenerator<'a> {
); );
let _enter = span.enter(); let _enter = span.enter();
", ",
self.iface.name, func.name, match owner {
TypeOwner::Interface(id) => self.resolve.interfaces[id]
.name
.as_deref()
.unwrap_or("<no module>"),
TypeOwner::World(id) => &self.resolve.worlds[id].name,
TypeOwner::None => "<no owner>",
},
func.name,
)); ));
} }
@@ -952,7 +1045,10 @@ impl<'a> InterfaceGenerator<'a> {
uwrite!(self.src, ");\n"); 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!( uwrite!(
self.src, self.src,
"match r {{ "match r {{
@@ -977,10 +1073,51 @@ impl<'a> InterfaceGenerator<'a> {
} }
} }
fn extract_typed_functions(&mut self) -> Vec<(String, String)> { fn generate_function_trait_sig(&mut self, owner: TypeOwner, func: &Function) {
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(owner, &func.results) {
// Functions which have a single result `result<ok,err>` get special
// cased to use the host_wasmtime_rust::Error<err>, 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 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(); let snake = func.name.to_snake_case();
uwrite!(self.src, "*__exports.typed_func::<("); uwrite!(self.src, "*__exports.typed_func::<(");
for (_, ty) in func.params.iter() { for (_, ty) in func.params.iter() {
@@ -996,8 +1133,7 @@ impl<'a> InterfaceGenerator<'a> {
self.src.push_str(&func.name); self.src.push_str(&func.name);
self.src.push_str("\")?.func()"); self.src.push_str("\")?.func()");
ret.push((snake, mem::take(&mut self.src).to_string())); let ret = (snake, mem::take(&mut self.src).to_string());
}
self.src = prev; self.src = prev;
return ret; return ret;
} }
@@ -1097,17 +1233,49 @@ impl<'a> InterfaceGenerator<'a> {
self.src.push_str("}\n"); self.src.push_str("}\n");
} }
fn generate_trappable_error_types(&mut self) { fn trappable_error_types(
for (wit_typename, wit_type, trappable_type) in self.gen.trappable_error_types(&self.iface) &self,
{ owner: TypeOwner,
let info = self.info(*wit_type); ) -> impl Iterator<Item = (TypeId, String)> + '_ {
if self.lifetime_for(&info, TypeMode::Owned).is_some() { let resolve = self.resolve;
panic!( self.gen
"type {:?} in interface {:?} is not 'static", .opts
wit_typename, self.iface.name .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 abi_type = self.param_name(*wit_type); }
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::<Vec<_>>() {
let info = self.info(wit_type);
if self.lifetime_for(&info, TypeMode::Owned).is_some() {
panic!("wit error for {trappable_type} is not 'static")
}
let abi_type = self.param_name(wit_type);
uwriteln!( uwriteln!(
self.src, self.src,
@@ -1164,8 +1332,12 @@ impl<'a> InterfaceGenerator<'a> {
} }
impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> {
fn iface(&self) -> &'a Interface { fn resolve(&self) -> &'a Resolve {
self.iface self.resolve
}
fn current_interface(&self) -> Option<InterfaceId> {
self.current_interface
} }
fn default_param_mode(&self) -> TypeMode { fn default_param_mode(&self) -> TypeMode {
@@ -1177,6 +1349,6 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> {
} }
fn info(&self, ty: TypeId) -> TypeInfo { fn info(&self, ty: TypeId) -> TypeInfo {
self.types.get(ty) self.gen.types.get(ty)
} }
} }

View File

@@ -11,11 +11,12 @@ pub enum TypeMode {
} }
pub trait RustGenerator<'a> { pub trait RustGenerator<'a> {
fn iface(&self) -> &'a Interface; fn resolve(&self) -> &'a Resolve;
fn push_str(&mut self, s: &str); fn push_str(&mut self, s: &str);
fn info(&self, ty: TypeId) -> TypeInfo; fn info(&self, ty: TypeId) -> TypeInfo;
fn default_param_mode(&self) -> TypeMode; fn default_param_mode(&self) -> TypeMode;
fn current_interface(&self) -> Option<InterfaceId>;
fn print_ty(&mut self, ty: &Type, mode: TypeMode) { fn print_ty(&mut self, ty: &Type, mode: TypeMode) {
match ty { match ty {
@@ -56,25 +57,41 @@ pub trait RustGenerator<'a> {
fn print_tyid(&mut self, id: TypeId, mode: TypeMode) { fn print_tyid(&mut self, id: TypeId, mode: TypeMode) {
let info = self.info(id); let info = self.info(id);
let lt = self.lifetime_for(&info, mode); let lt = self.lifetime_for(&info, mode);
let ty = &self.iface().types[id]; let ty = &self.resolve().types[id];
if ty.name.is_some() { if ty.name.is_some() {
let name = if lt.is_some() { let name = if lt.is_some() {
self.param_name(id) self.param_name(id)
} else { } else {
self.result_name(id) 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); self.push_str(&name);
// If the type recursively owns data and it's a // If the type recursively owns data and it's a
// variant/record/list, then we need to place the // variant/record/list, then we need to place the
// lifetime parameter on the type as well. // 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); self.print_generics(lt);
} }
return; return;
fn needs_generics(iface: &Interface, ty: &TypeDefKind) -> bool { fn needs_generics(resolve: &Resolve, ty: &TypeDefKind) -> bool {
match ty { match ty {
TypeDefKind::Variant(_) TypeDefKind::Variant(_)
| TypeDefKind::Record(_) | TypeDefKind::Record(_)
@@ -87,9 +104,12 @@ pub trait RustGenerator<'a> {
| TypeDefKind::Enum(_) | TypeDefKind::Enum(_)
| TypeDefKind::Tuple(_) | TypeDefKind::Tuple(_)
| TypeDefKind::Union(_) => true, | 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(Type::String) => true,
TypeDefKind::Type(_) => false, TypeDefKind::Type(_) => false,
TypeDefKind::Unknown => unreachable!(),
} }
} }
} }
@@ -150,6 +170,7 @@ pub trait RustGenerator<'a> {
} }
TypeDefKind::Type(t) => self.print_ty(t, mode), 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::Char => out.push_str("Char"),
Type::String => out.push_str("String"), Type::String => out.push_str("String"),
Type::Id(id) => { Type::Id(id) => {
let ty = &self.iface().types[*id]; let ty = &self.resolve().types[*id];
match &ty.name { match &ty.name {
Some(name) => out.push_str(&name.to_upper_camel_case()), Some(name) => out.push_str(&name.to_upper_camel_case()),
None => match &ty.kind { None => match &ty.kind {
@@ -244,6 +265,7 @@ pub trait RustGenerator<'a> {
TypeDefKind::Variant(_) => out.push_str("Variant"), TypeDefKind::Variant(_) => out.push_str("Variant"),
TypeDefKind::Enum(_) => out.push_str("Enum"), TypeDefKind::Enum(_) => out.push_str("Enum"),
TypeDefKind::Union(_) => out.push_str("Union"), TypeDefKind::Union(_) => out.push_str("Union"),
TypeDefKind::Unknown => unreachable!(),
}, },
} }
} }
@@ -309,7 +331,7 @@ pub trait RustGenerator<'a> {
fn param_name(&self, ty: TypeId) -> String { fn param_name(&self, ty: TypeId) -> String {
let info = self.info(ty); let info = self.info(ty);
let name = self.iface().types[ty] let name = self.resolve().types[ty]
.name .name
.as_ref() .as_ref()
.unwrap() .unwrap()
@@ -323,7 +345,7 @@ pub trait RustGenerator<'a> {
fn result_name(&self, ty: TypeId) -> String { fn result_name(&self, ty: TypeId) -> String {
let info = self.info(ty); let info = self.info(ty);
let name = self.iface().types[ty] let name = self.resolve().types[ty]
.name .name
.as_ref() .as_ref()
.unwrap() .unwrap()

View File

@@ -34,14 +34,29 @@ impl std::ops::BitOrAssign for TypeInfo {
} }
impl Types { impl Types {
pub fn analyze(&mut self, iface: &Interface) { pub fn analyze(&mut self, resolve: &Resolve, world: WorldId) {
for (t, _) in iface.types.iter() { let world = &resolve.worlds[world];
self.type_id_info(iface, t); 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() { for (_, f) in iface.functions.iter() {
for (_, ty) in f.params.iter() { self.type_info_func(resolve, f);
}
}
}
}
}
fn type_info_func(&mut self, resolve: &Resolve, func: &Function) {
for (_, ty) in func.params.iter() {
self.set_param_result_ty( self.set_param_result_ty(
iface, resolve,
ty, ty,
TypeInfo { TypeInfo {
param: true, param: true,
@@ -49,9 +64,9 @@ impl Types {
}, },
); );
} }
for ty in f.results.iter_types() { for ty in func.results.iter_types() {
self.set_param_result_ty( self.set_param_result_ty(
iface, resolve,
ty, ty,
TypeInfo { TypeInfo {
result: true, result: true,
@@ -60,133 +75,136 @@ impl Types {
); );
} }
} }
}
pub fn get(&self, id: TypeId) -> TypeInfo { pub fn get(&self, id: TypeId) -> TypeInfo {
self.type_info[&id] 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) { if let Some(info) = self.type_info.get(&ty) {
return *info; return *info;
} }
let mut info = TypeInfo::default(); let mut info = TypeInfo::default();
match &iface.types[ty].kind { match &resolve.types[ty].kind {
TypeDefKind::Record(r) => { TypeDefKind::Record(r) => {
for field in r.fields.iter() { for field in r.fields.iter() {
info |= self.type_info(iface, &field.ty); info |= self.type_info(resolve, &field.ty);
} }
} }
TypeDefKind::Tuple(t) => { TypeDefKind::Tuple(t) => {
for ty in t.types.iter() { for ty in t.types.iter() {
info |= self.type_info(iface, ty); info |= self.type_info(resolve, ty);
} }
} }
TypeDefKind::Flags(_) => {} TypeDefKind::Flags(_) => {}
TypeDefKind::Enum(_) => {} TypeDefKind::Enum(_) => {}
TypeDefKind::Variant(v) => { TypeDefKind::Variant(v) => {
for case in v.cases.iter() { 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) => { TypeDefKind::List(ty) => {
info = self.type_info(iface, ty); info = self.type_info(resolve, ty);
info.has_list = true; info.has_list = true;
} }
TypeDefKind::Type(ty) => { TypeDefKind::Type(ty) => {
info = self.type_info(iface, ty); info = self.type_info(resolve, ty);
} }
TypeDefKind::Option(ty) => { TypeDefKind::Option(ty) => {
info = self.type_info(iface, ty); info = self.type_info(resolve, ty);
} }
TypeDefKind::Result(r) => { TypeDefKind::Result(r) => {
info = self.optional_type_info(iface, r.ok.as_ref()); info = self.optional_type_info(resolve, r.ok.as_ref());
info |= self.optional_type_info(iface, r.err.as_ref()); info |= self.optional_type_info(resolve, r.err.as_ref());
} }
TypeDefKind::Union(u) => { TypeDefKind::Union(u) => {
for case in u.cases.iter() { for case in u.cases.iter() {
info |= self.type_info(iface, &case.ty); info |= self.type_info(resolve, &case.ty);
} }
} }
TypeDefKind::Future(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) => { TypeDefKind::Stream(stream) => {
info = self.optional_type_info(iface, stream.element.as_ref()); info = self.optional_type_info(resolve, stream.element.as_ref());
info |= self.optional_type_info(iface, stream.end.as_ref()); info |= self.optional_type_info(resolve, stream.end.as_ref());
} }
TypeDefKind::Unknown => unreachable!(),
} }
self.type_info.insert(ty, info); self.type_info.insert(ty, info);
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(); let mut info = TypeInfo::default();
match ty { match ty {
Type::String => info.has_list = true, 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 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 { match ty {
Some(ty) => self.type_info(iface, ty), Some(ty) => self.type_info(resolve, ty),
None => TypeInfo::default(), None => TypeInfo::default(),
} }
} }
fn set_param_result_id(&mut self, iface: &Interface, ty: TypeId, info: TypeInfo) { fn set_param_result_id(&mut self, resolve: &Resolve, ty: TypeId, info: TypeInfo) {
match &iface.types[ty].kind { match &resolve.types[ty].kind {
TypeDefKind::Record(r) => { TypeDefKind::Record(r) => {
for field in r.fields.iter() { 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) => { TypeDefKind::Tuple(t) => {
for ty in t.types.iter() { 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::Flags(_) => {}
TypeDefKind::Enum(_) => {} TypeDefKind::Enum(_) => {}
TypeDefKind::Variant(v) => { TypeDefKind::Variant(v) => {
for case in v.cases.iter() { 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) => { 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) => { 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; let mut info2 = info;
info2.error = info.result; 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) => { TypeDefKind::Union(u) => {
for case in u.cases.iter() { 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::Future(ty) => {
self.set_param_result_optional_ty(resolve, ty.as_ref(), info)
}
TypeDefKind::Stream(stream) => { TypeDefKind::Stream(stream) => {
self.set_param_result_optional_ty(iface, stream.element.as_ref(), info); self.set_param_result_optional_ty(resolve, stream.element.as_ref(), info);
self.set_param_result_optional_ty(iface, stream.end.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 { match ty {
Type::Id(id) => { 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 cur = self.type_info.get_mut(id).unwrap();
let prev = *cur; let prev = *cur;
*cur |= info; *cur |= info;
if prev != *cur { 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( fn set_param_result_optional_ty(
&mut self, &mut self,
iface: &Interface, resolve: &Resolve,
ty: Option<&Type>, ty: Option<&Type>,
info: TypeInfo, info: TypeInfo,
) { ) {
match ty { match ty {
Some(ty) => self.set_param_result_ty(iface, ty, info), Some(ty) => self.set_param_result_ty(resolve, ty, info),
None => (), None => (),
} }
} }

View File

@@ -585,6 +585,12 @@ criteria = "safe-to-deploy"
version = "0.20.0" version = "0.20.0"
notes = "The Bytecode Alliance is the author of this crate." notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasm-encoder]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.21.0"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasm-encoder]] [[audits.wasm-encoder]]
who = "Alex Crichton <alex@alexcrichton.com>" who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -645,6 +651,12 @@ criteria = "safe-to-run"
version = "0.2.13" version = "0.2.13"
notes = "The Bytecode Alliance is the author of this crate." notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasm-mutate]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-run"
version = "0.2.14"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasm-smith]] [[audits.wasm-smith]]
who = "Alex Crichton <alex@alexcrichton.com>" who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -699,6 +711,12 @@ criteria = "safe-to-run"
version = "0.11.10" version = "0.11.10"
notes = "The Bytecode Alliance is the author of this crate." notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasm-smith]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-run"
version = "0.11.11"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasmi]] [[audits.wasmi]]
who = "Robin Freyler <robin.freyler@gmail.com>" who = "Robin Freyler <robin.freyler@gmail.com>"
criteria = "safe-to-run" criteria = "safe-to-run"
@@ -801,6 +819,12 @@ criteria = "safe-to-deploy"
version = "0.96.0" version = "0.96.0"
notes = "The Bytecode Alliance is the author of this crate." notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasmparser]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.97.0"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasmparser-nostd]] [[audits.wasmparser-nostd]]
who = "Alex Crichton <alex@alexcrichton.com>" who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-run" criteria = "safe-to-run"
@@ -866,6 +890,12 @@ criteria = "safe-to-deploy"
version = "0.2.45" version = "0.2.45"
notes = "The Bytecode Alliance is the author of this crate." notes = "The Bytecode Alliance is the author of this crate."
[[audits.wasmprinter]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.2.46"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wast]] [[audits.wast]]
who = "Alex Crichton <alex@alexcrichton.com>" who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -920,6 +950,12 @@ criteria = "safe-to-deploy"
version = "50.0.0" version = "50.0.0"
notes = "The Bytecode Alliance is the author of this crate." notes = "The Bytecode Alliance is the author of this crate."
[[audits.wast]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "51.0.0"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wat]] [[audits.wat]]
who = "Alex Crichton <alex@alexcrichton.com>" who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -956,6 +992,12 @@ criteria = "safe-to-deploy"
version = "1.0.52" version = "1.0.52"
notes = "The Bytecode Alliance is the author of this crate." notes = "The Bytecode Alliance is the author of this crate."
[[audits.wat]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "1.0.53"
notes = "The Bytecode Alliance is the author of this crate."
[[audits.wat]] [[audits.wat]]
who = "Alex Crichton <alex@alexcrichton.com>" who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@@ -1028,3 +1070,9 @@ criteria = "safe-to-deploy"
version = "0.3.1" version = "0.3.1"
notes = "The Bytecode Alliance is the author of this crate." notes = "The Bytecode Alliance is the author of this crate."
[[audits.wit-parser]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.4.0"
notes = "The Bytecode Alliance is the author of this crate."

View File

@@ -12,14 +12,12 @@ mod no_imports {
wasmtime::component::bindgen!({ wasmtime::component::bindgen!({
inline: " inline: "
world no-imports { default world no-imports {
export foo: interface { export foo: interface {
foo: func() foo: func()
} }
default export interface { export bar: func()
bar: func()
}
} }
", ",
}); });
@@ -59,14 +57,12 @@ mod one_import {
wasmtime::component::bindgen!({ wasmtime::component::bindgen!({
inline: " inline: "
world one-import { default world one-import {
import foo: interface { import foo: interface {
foo: func() foo: func()
} }
default export interface { export bar: func()
bar: func()
}
} }
", ",
}); });

View File

@@ -9,14 +9,12 @@ mod empty_error {
use super::*; use super::*;
wasmtime::component::bindgen!({ wasmtime::component::bindgen!({
inline: " inline: "
world result-playground { default world result-playground {
import imports: interface { import imports: interface {
empty-error: func(a: float64) -> result<float64> empty-error: func(a: float64) -> result<float64>
} }
default export interface { export empty-error: func(a: float64) -> result<float64>
empty-error: func(a: float64) -> result<float64>
}
}", }",
}); });
@@ -108,14 +106,12 @@ mod string_error {
use super::*; use super::*;
wasmtime::component::bindgen!({ wasmtime::component::bindgen!({
inline: " inline: "
world result-playground { default world result-playground {
import imports: interface { import imports: interface {
string-error: func(a: float64) -> result<float64, string> string-error: func(a: float64) -> result<float64, string>
} }
default export interface { export string-error: func(a: float64) -> result<float64, string>
string-error: func(a: float64) -> result<float64, string>
}
}", }",
}); });
@@ -223,9 +219,9 @@ mod enum_error {
enum e1 { a, b, c } enum e1 { a, b, c }
enum-error: func(a: float64) -> result<float64, e1> enum-error: func(a: float64) -> result<float64, e1>
} }
world result-playground { default world result-playground {
import imports: imports import imports: self.imports
default export interface { export foo: interface {
enum e1 { a, b, c } enum e1 { a, b, c }
enum-error: func(a: float64) -> result<float64, e1> enum-error: func(a: float64) -> result<float64, e1>
} }
@@ -273,11 +269,14 @@ mod enum_error {
(with "libc" (instance $libc)) (with "libc" (instance $libc))
)) ))
(func $f_enum_error (func $f_enum_error
(export "enum-error")
(param "a" float64) (param "a" float64)
(result (result float64 (error (enum "a" "b" "c")))) (result (result float64 (error (enum "a" "b" "c"))))
(canon lift (core func $i "core_enum_error_export") (memory $libc "memory")) (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!( assert_eq!(
results results
.foo()
.enum_error(&mut store, 0.0) .enum_error(&mut store, 0.0)
.expect("no trap") .expect("no trap")
.expect("no error returned"), .expect("no error returned"),
@@ -335,13 +335,18 @@ mod enum_error {
); );
let e = results let e = results
.foo()
.enum_error(&mut store, 1.0) .enum_error(&mut store, 1.0)
.expect("no trap") .expect("no trap")
.err() .err()
.expect("error returned"); .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!( assert_eq!(
format!("{}", e.source().expect("trap message is stored in source")), format!("{}", e.source().expect("trap message is stored in source")),
"MyTrap" "MyTrap"
@@ -361,9 +366,9 @@ mod record_error {
record e2 { line: u32, col: u32 } record e2 { line: u32, col: u32 }
record-error: func(a: float64) -> result<float64, e2> record-error: func(a: float64) -> result<float64, e2>
} }
world result-playground { default world result-playground {
import imports: imports import imports: self.imports
default export interface { export foo: interface {
record e2 { line: u32, col: u32 } record e2 { line: u32, col: u32 }
record-error: func(a: float64) -> result<float64, e2> record-error: func(a: float64) -> result<float64, e2>
} }
@@ -411,11 +416,14 @@ mod record_error {
(with "libc" (instance $libc)) (with "libc" (instance $libc))
)) ))
(func $f_record_error (func $f_record_error
(export "record-error")
(param "a" float64) (param "a" float64)
(result (result float64 (error (record (field "line" u32) (field "col" u32))))) (result (result float64 (error (record (field "line" u32) (field "col" u32)))))
(canon lift (core func $i "core_record_error_export") (memory $libc "memory")) (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!( assert_eq!(
results results
.foo()
.record_error(&mut store, 0.0) .record_error(&mut store, 0.0)
.expect("no trap") .expect("no trap")
.expect("no error returned"), .expect("no error returned"),
@@ -454,19 +463,24 @@ mod record_error {
); );
let e = results let e = results
.foo()
.record_error(&mut store, 1.0) .record_error(&mut store, 1.0)
.expect("no trap") .expect("no trap")
.err() .err()
.expect("error returned"); .expect("error returned");
assert!(matches!( assert!(matches!(
e, e,
record_error::E2 { record_error::foo::E2 {
line: 420, line: 420,
col: 1312 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!( assert_eq!(
format!("{}", e.source().expect("trap message is stored in source")), format!("{}", e.source().expect("trap message is stored in source")),
"record_error: trap" "record_error: trap"
@@ -486,9 +500,9 @@ mod variant_error {
variant e3 { E1(e1), E2(e2) } variant e3 { E1(e1), E2(e2) }
variant-error: func(a: float64) -> result<float64, e3> variant-error: func(a: float64) -> result<float64, e3>
} }
world result-playground { default world result-playground {
import imports: imports import imports: self.imports
default export interface { export foo: interface {
enum e1 { a, b, c } enum e1 { a, b, c }
record e2 { line: u32, col: u32 } record e2 { line: u32, col: u32 }
variant e3 { E1(e1), E2(e2) } variant e3 { E1(e1), E2(e2) }
@@ -538,11 +552,14 @@ mod variant_error {
(with "libc" (instance $libc)) (with "libc" (instance $libc))
)) ))
(func $f_variant_error (func $f_variant_error
(export "variant-error")
(param "a" float64) (param "a" float64)
(result (result float64 (error (variant (case "E1" (enum "a" "b" "c")) (case "E2"(record (field "line" u32) (field "col" u32))))))) (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")) (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!( assert_eq!(
results results
.foo()
.variant_error(&mut store, 0.0) .variant_error(&mut store, 0.0)
.expect("no trap") .expect("no trap")
.expect("no error returned"), .expect("no error returned"),
@@ -581,19 +599,24 @@ mod variant_error {
); );
let e = results let e = results
.foo()
.variant_error(&mut store, 1.0) .variant_error(&mut store, 1.0)
.expect("no trap") .expect("no trap")
.err() .err()
.expect("error returned"); .expect("error returned");
assert!(matches!( assert!(matches!(
e, e,
variant_error::E3::E2(variant_error::E2 { variant_error::foo::E3::E2(variant_error::foo::E2 {
line: 420, line: 420,
col: 1312 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!( assert_eq!(
format!("{}", e.source().expect("trap message is stored in source")), format!("{}", e.source().expect("trap message is stored in source")),
"variant_error: trap" "variant_error: trap"