Skip to content

Commit

Permalink
Support resource maps in component::bindgen! (bytecodealliance#7069)
Browse files Browse the repository at this point in the history
* Support resource maps in `component::bindgen!`

This commit adds support to `component::bindgen!` to specify resource
types using the `with` key of the macro. This can be used to configure
the `T` of `Resource<T>` to use a preexisting type rather than
unconditionally generating a new empty enum to have a fresh type.

* Reenable tests
  • Loading branch information
alexcrichton authored and eduardomourar committed Sep 22, 2023
1 parent 2dcf09c commit 41f9f62
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 27 deletions.
82 changes: 82 additions & 0 deletions crates/component-macro/tests/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,85 @@ macro_rules! gentest {
}

component_macro_test_helpers::foreach!(gentest);

mod with_key_and_resources {
use anyhow::Result;
use wasmtime::component::Resource;

wasmtime::component::bindgen!({
inline: "
package demo:pkg
interface bar {
resource a
resource b
}
world foo {
resource a
resource b
import foo: interface {
resource a
resource b
}
import bar
}
",
with: {
"a": MyA,
"b": MyA,
"foo/a": MyA,
"foo/b": MyA,
"demo:pkg/bar/a": MyA,
"demo:pkg/bar/b": MyA,
},
});

pub struct MyA;

struct MyComponent;

impl FooImports for MyComponent {}

impl HostA for MyComponent {
fn drop(&mut self, _: Resource<MyA>) -> Result<()> {
loop {}
}
}

impl HostB for MyComponent {
fn drop(&mut self, _: Resource<MyA>) -> Result<()> {
loop {}
}
}

impl foo::Host for MyComponent {}

impl foo::HostA for MyComponent {
fn drop(&mut self, _: Resource<MyA>) -> Result<()> {
loop {}
}
}

impl foo::HostB for MyComponent {
fn drop(&mut self, _: Resource<MyA>) -> Result<()> {
loop {}
}
}

impl demo::pkg::bar::Host for MyComponent {}

impl demo::pkg::bar::HostA for MyComponent {
fn drop(&mut self, _: Resource<MyA>) -> Result<()> {
loop {}
}
}

impl demo::pkg::bar::HostB for MyComponent {
fn drop(&mut self, _: Resource<MyA>) -> Result<()> {
loop {}
}
}
}
18 changes: 11 additions & 7 deletions crates/wasmtime/src/component/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,16 +326,20 @@ pub(crate) use self::store::ComponentStoreData;
/// // Restrict the code generated to what's needed for the interface
/// // imports in the inlined WIT document fragment.
/// interfaces: "
/// import package.foo
/// import wasi:cli/command
/// ",
///
/// // Remap interface names to module names, imported from elsewhere.
/// // Using this option will prevent any code from being generated
/// // for the names mentioned in the mapping, assuming instead that the
/// // names mentioned come from a previous use of the `bindgen!` macro
/// // with `only_interfaces: true`.
/// // Remap imported interfaces or resources to types defined in Rust
/// // elsewhere. Using this option will prevent any code from being
/// // generated for interfaces mentioned here. Resources named here will
/// // not have a type generated to represent the resource.
/// //
/// // Interfaces mapped with this option should be previously generated
/// // with an invocation of this macro. Resources need to be mapped to a
/// // Rust type name.
/// with: {
/// "a": somewhere::else::a,
/// "wasi:random/random": some::other::wasi::random::random,
/// "wasi:filesystem/types/descriptor": MyDescriptorType,
/// },
/// });
/// ```
Expand Down
59 changes: 39 additions & 20 deletions crates/wit-bindgen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,16 +912,28 @@ impl<'a> InterfaceGenerator<'a> {
self.assert_type(id, &name);
}

fn type_resource(&mut self, id: TypeId, _name: &str, resource: &TypeDef, docs: &Docs) {
let camel = resource
.name
.as_ref()
.expect("resources are required to be named")
.to_upper_camel_case();
fn type_resource(&mut self, id: TypeId, name: &str, resource: &TypeDef, docs: &Docs) {
let camel = name.to_upper_camel_case();

if self.types_imported() {
self.rustdoc(docs);
uwriteln!(self.src, "pub enum {camel} {{}}");

let with_key = match self.current_interface {
Some((_, key, _)) => format!("{}/{name}", self.resolve.name_world_key(key)),
None => name.to_string(),
};
match self.gen.opts.with.get(&with_key) {
Some(path) => {
uwriteln!(
self.src,
"pub use {}{path} as {camel};",
self.path_to_root()
);
}
None => {
uwriteln!(self.src, "pub enum {camel} {{}}");
}
}

if self.gen.opts.async_.maybe_async() {
uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]")
Expand Down Expand Up @@ -1913,6 +1925,24 @@ impl<'a> InterfaceGenerator<'a> {
self.push_str("\n");
}
}

fn path_to_root(&self) -> String {
let mut path_to_root = String::new();
if let Some((_, key, is_export)) = self.current_interface {
match key {
WorldKey::Name(_) => {
path_to_root.push_str("super::");
}
WorldKey::Interface(_) => {
path_to_root.push_str("super::super::super::");
}
}
if is_export {
path_to_root.push_str("super::");
}
}
path_to_root
}
}

impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> {
Expand All @@ -1925,23 +1955,12 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> {
}

fn path_to_interface(&self, interface: InterfaceId) -> Option<String> {
let mut path_to_root = String::new();
if let Some((cur, key, is_export)) = self.current_interface {
if let Some((cur, _, _)) = self.current_interface {
if cur == interface {
return None;
}
match key {
WorldKey::Name(_) => {
path_to_root.push_str("super::");
}
WorldKey::Interface(_) => {
path_to_root.push_str("super::super::super::");
}
}
if is_export {
path_to_root.push_str("super::");
}
}
let mut path_to_root = self.path_to_root();
let InterfaceName { path, .. } = &self.gen.interface_names[&interface];
path_to_root.push_str(path);
Some(path_to_root)
Expand Down

0 comments on commit 41f9f62

Please sign in to comment.