From 4a935277d950124f093062f8ed4208b14369f9b0 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Mon, 24 Oct 2022 11:18:45 -0700 Subject: [PATCH 1/3] wit-bindgen-rust: add optional prefix for calls from export macro. This commit adds an option for specifying a prefix to use for calls within the generated `export!` macro. It enables bindings to be generated into a submodule or crate and still have the exports to call the appropriate generated functions. --- crates/gen-guest-rust/src/lib.rs | 20 ++++++++++++++-- crates/gen-guest-rust/tests/codegen.rs | 32 ++++++++++++++++++++++++++ crates/guest-rust-macro/src/lib.rs | 12 +++++++++- 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/crates/gen-guest-rust/src/lib.rs b/crates/gen-guest-rust/src/lib.rs index 25dc8ba47..871e63bc8 100644 --- a/crates/gen-guest-rust/src/lib.rs +++ b/crates/gen-guest-rust/src/lib.rs @@ -47,6 +47,14 @@ pub struct Opts { /// validation if it doesn't already have a `&str`. #[cfg_attr(feature = "clap", arg(long))] pub raw_strings: bool, + + /// The prefix to use when calling functions from within the generated + /// `export!` macro. + /// + /// This enables the generated `export!` macro to reference code from + /// another mod/crate. + #[cfg_attr(feature = "clap", arg(long))] + pub macro_call_prefix: Option, } impl Opts { @@ -351,7 +359,11 @@ impl InterfaceGenerator<'_> { // Finish out the macro-generated export implementation. macro_src.push_str(" {\n"); - uwrite!(macro_src, "{module_name}::call_{name_snake}::<$t>("); + uwrite!( + macro_src, + "{prefix}{module_name}::call_{name_snake}::<$t>(", + prefix = self.gen.opts.macro_call_prefix.as_deref().unwrap_or("") + ); for param in params.iter() { uwrite!(macro_src, "{param},"); } @@ -401,7 +413,11 @@ impl InterfaceGenerator<'_> { macro_src.push_str(") {\n"); // Finish out the macro here - uwrite!(macro_src, "{module_name}::post_return_{name_snake}::<$t>("); + uwrite!( + macro_src, + "{prefix}{module_name}::post_return_{name_snake}::<$t>(", + prefix = self.gen.opts.macro_call_prefix.as_deref().unwrap_or("") + ); for param in params.iter() { uwrite!(macro_src, "{param},"); } diff --git a/crates/gen-guest-rust/tests/codegen.rs b/crates/gen-guest-rust/tests/codegen.rs index 6477fd759..69abf6ef0 100644 --- a/crates/gen-guest-rust/tests/codegen.rs +++ b/crates/gen-guest-rust/tests/codegen.rs @@ -120,3 +120,35 @@ mod raw_strings { let _t: Vec = cat::bar(); } } + +// This is a static compilation test to ensure that +// export bindings can go inside of another mod/crate +// and still compile. +mod prefix { + mod bindings { + wit_bindgen_guest_rust::generate!({ + export_str["exports1"]: " + foo: func(x: string) + bar: func() -> string + ", + name: "baz", + macro_call_prefix: "bindings::" + }); + + pub(crate) use export_baz; + } + + struct Component; + + impl bindings::exports1::Exports1 for Component { + fn foo(x: String) { + println!("foo: {}", x); + } + + fn bar() -> String { + "bar".to_string() + } + } + + bindings::export_baz!(Component); +} diff --git a/crates/guest-rust-macro/src/lib.rs b/crates/guest-rust-macro/src/lib.rs index 609f02e09..91ee402b2 100644 --- a/crates/guest-rust-macro/src/lib.rs +++ b/crates/guest-rust-macro/src/lib.rs @@ -1,5 +1,8 @@ use proc_macro::TokenStream; -use syn::parse::{Parse, ParseStream, Result}; +use syn::{ + parse::{Parse, ParseStream, Result}, + LitStr, Token, +}; use wit_bindgen_gen_guest_rust::Opts; #[proc_macro] @@ -11,12 +14,14 @@ mod kw { syn::custom_keyword!(unchecked); syn::custom_keyword!(no_std); syn::custom_keyword!(raw_strings); + syn::custom_keyword!(macro_call_prefix); } enum Opt { Unchecked, NoStd, RawStrings, + MacroCallPrefix(LitStr), } impl Parse for Opt { @@ -31,6 +36,10 @@ impl Parse for Opt { } else if l.peek(kw::raw_strings) { input.parse::()?; Ok(Opt::RawStrings) + } else if l.peek(kw::macro_call_prefix) { + input.parse::()?; + input.parse::()?; + Ok(Opt::MacroCallPrefix(input.parse()?)) } else { Err(l.error()) } @@ -43,6 +52,7 @@ impl wit_bindgen_rust_macro_shared::Configure for Opt { Opt::Unchecked => opts.unchecked = true, Opt::NoStd => opts.no_std = true, Opt::RawStrings => opts.raw_strings = true, + Opt::MacroCallPrefix(prefix) => opts.macro_call_prefix = Some(prefix.value()), } } } From 5892a4e68989ff21bf9a0b0e55641832d0e2f598 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Mon, 24 Oct 2022 12:54:10 -0700 Subject: [PATCH 2/3] wit-bindgen-rust: add option to control generated export macro name. This commit adds an option to control the export macro name. --- .gitignore | 1 + crates/gen-guest-rust/src/lib.rs | 18 ++++++++++++++---- crates/gen-guest-rust/tests/codegen.rs | 22 ++++++++++++++++++++++ crates/guest-rust-macro/src/lib.rs | 7 +++++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 3f7971eb8..0954a210e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ package-lock.json node_modules ace *.wasm +__pycache__ diff --git a/crates/gen-guest-rust/src/lib.rs b/crates/gen-guest-rust/src/lib.rs index 871e63bc8..21654a44f 100644 --- a/crates/gen-guest-rust/src/lib.rs +++ b/crates/gen-guest-rust/src/lib.rs @@ -55,6 +55,13 @@ pub struct Opts { /// another mod/crate. #[cfg_attr(feature = "clap", arg(long))] pub macro_call_prefix: Option, + + /// The name of the generated `export!` macro to use. + /// + /// If `None`, the name is derived from the name of the world in the + /// format `export_{world_name}!`. + #[cfg_attr(feature = "clap", arg(long))] + pub export_macro_name: Option, } impl Opts { @@ -119,7 +126,11 @@ impl WorldGenerator for RustWasm { fn finish(&mut self, name: &str, interfaces: &ComponentInterfaces, files: &mut Files) { if !self.exports.is_empty() { - let snake = name.to_snake_case(); + let macro_name = if let Some(name) = self.opts.export_macro_name.as_ref() { + name.to_snake_case() + } else { + format!("export_{}", name.to_snake_case()) + }; let macro_export = if self.opts.macro_export { "#[macro_export]" } else { @@ -131,9 +142,8 @@ impl WorldGenerator for RustWasm { /// Declares the export of the component's world for the /// given type. {macro_export} - macro_rules! export_{snake}(($t:ident) => {{ + macro_rules! {macro_name}(($t:ident) => {{ const _: () = {{ - " ); for src in self.exports.iter() { @@ -148,7 +158,7 @@ impl WorldGenerator for RustWasm { ); } - self.src.push_str("#[cfg(target_arch = \"wasm32\")]\n"); + self.src.push_str("\n#[cfg(target_arch = \"wasm32\")]\n"); // The custom section name here must start with "component-type" but // otherwise is attempted to be unique here to ensure that this doesn't get diff --git a/crates/gen-guest-rust/tests/codegen.rs b/crates/gen-guest-rust/tests/codegen.rs index 69abf6ef0..297a1259b 100644 --- a/crates/gen-guest-rust/tests/codegen.rs +++ b/crates/gen-guest-rust/tests/codegen.rs @@ -152,3 +152,25 @@ mod prefix { bindings::export_baz!(Component); } + +// This is a static compilation test to check that +// the export macro name can be overridden. +mod macro_name { + wit_bindgen_guest_rust::generate!({ + export_str["exports2"]: " + foo: func(x: string) + ", + name: "baz", + export_macro_name: "jam" + }); + + struct Component; + + impl exports2::Exports2 for Component { + fn foo(x: String) { + println!("foo: {}", x); + } + } + + jam!(Component); +} diff --git a/crates/guest-rust-macro/src/lib.rs b/crates/guest-rust-macro/src/lib.rs index 91ee402b2..8f01e45c5 100644 --- a/crates/guest-rust-macro/src/lib.rs +++ b/crates/guest-rust-macro/src/lib.rs @@ -15,6 +15,7 @@ mod kw { syn::custom_keyword!(no_std); syn::custom_keyword!(raw_strings); syn::custom_keyword!(macro_call_prefix); + syn::custom_keyword!(export_macro_name); } enum Opt { @@ -22,6 +23,7 @@ enum Opt { NoStd, RawStrings, MacroCallPrefix(LitStr), + ExportMacroName(LitStr), } impl Parse for Opt { @@ -40,6 +42,10 @@ impl Parse for Opt { input.parse::()?; input.parse::()?; Ok(Opt::MacroCallPrefix(input.parse()?)) + } else if l.peek(kw::export_macro_name) { + input.parse::()?; + input.parse::()?; + Ok(Opt::ExportMacroName(input.parse()?)) } else { Err(l.error()) } @@ -53,6 +59,7 @@ impl wit_bindgen_rust_macro_shared::Configure for Opt { Opt::NoStd => opts.no_std = true, Opt::RawStrings => opts.raw_strings = true, Opt::MacroCallPrefix(prefix) => opts.macro_call_prefix = Some(prefix.value()), + Opt::ExportMacroName(name) => opts.export_macro_name = Some(name.value()), } } } From 9d2ea88f986f4a883ba243449e3a070cac18958e Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Mon, 24 Oct 2022 13:08:05 -0700 Subject: [PATCH 3/3] wit-bindgen-rust: force a reference to the component type section. This commit changes the generated `export!` macro to ensure a reference to the object containing the component type section. It is necessary to do this to prevent the linker from GC'ing the section's static when building the bindings into a separate crate. --- crates/gen-guest-rust/src/lib.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/crates/gen-guest-rust/src/lib.rs b/crates/gen-guest-rust/src/lib.rs index 21654a44f..7a837617e 100644 --- a/crates/gen-guest-rust/src/lib.rs +++ b/crates/gen-guest-rust/src/lib.rs @@ -153,8 +153,19 @@ impl WorldGenerator for RustWasm { self.src, " }}; + + #[used] + #[doc(hidden)] + #[cfg(target_arch = \"wasm32\")] + static __FORCE_SECTION_REF: fn() = __force_section_ref; + #[doc(hidden)] + #[cfg(target_arch = \"wasm32\")] + fn __force_section_ref() {{ + {prefix}__link_section() + }} }}); - " + ", + prefix = self.opts.macro_call_prefix.as_deref().unwrap_or("") ); } @@ -185,6 +196,15 @@ impl WorldGenerator for RustWasm { )); self.src.push_str(&format!("{:?};\n", component_type)); + self.src.push_str( + " + #[inline(never)] + #[doc(hidden)] + #[cfg(target_arch = \"wasm32\")] + pub fn __link_section() {} + ", + ); + let mut src = mem::take(&mut self.src); if self.opts.rustfmt { let mut child = Command::new("rustfmt") @@ -340,8 +360,9 @@ impl InterfaceGenerator<'_> { uwrite!( macro_src, " + #[doc(hidden)] #[export_name = \"{export_name}\"] - unsafe extern \"C\" fn export_{iface_snake}_{name_snake}(\ + unsafe extern \"C\" fn __export_{iface_snake}_{name_snake}(\ ", ); @@ -408,8 +429,9 @@ impl InterfaceGenerator<'_> { uwrite!( macro_src, " + #[doc(hidden)] #[export_name = \"cabi_post_{export_name}\"] - pub unsafe extern \"C\" fn post_return_{iface_snake}_{name_snake}(\ + unsafe extern \"C\" fn __post_return_{iface_snake}_{name_snake}(\ " ); let mut params = Vec::new();