diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index b03c173dd..ed0e16d81 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -16,6 +16,7 @@ mod async_; pub use async_::AsyncFilterSet; mod chainable_method; pub use chainable_method::{ChainableMethodFilterSet, ChainingMode}; +pub mod symbol_name; #[derive(Default, Copy, Clone, PartialEq, Eq, Debug)] pub enum Direction { diff --git a/crates/cpp/src/symbol_name.rs b/crates/core/src/symbol_name.rs similarity index 98% rename from crates/cpp/src/symbol_name.rs rename to crates/core/src/symbol_name.rs index 4b71d1005..1798eb749 100644 --- a/crates/cpp/src/symbol_name.rs +++ b/crates/core/src/symbol_name.rs @@ -1,4 +1,4 @@ -use wit_bindgen_core::abi; +use crate::abi; fn hexdigit(v: u32) -> char { if v < 10 { diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 2d7b30099..e0277893e 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -9,12 +9,13 @@ use std::{ process::{Command, Stdio}, str::FromStr, }; -use symbol_name::{make_external_component, make_external_symbol}; use wit_bindgen_c::to_c_ident; use wit_bindgen_core::{ Files, InterfaceGenerator, Source, Types, WorldGenerator, abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType}, - name_package_module, uwrite, uwriteln, + name_package_module, + symbol_name::{make_external_component, make_external_symbol}, + uwrite, uwriteln, wit_parser::{ Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, Param, Resolve, SizeAlign, Stability, Type, TypeDef, TypeDefKind, TypeId, TypeOwner, WorldId, @@ -24,7 +25,6 @@ use wit_bindgen_core::{ use wit_parser::TypeIdVisitor; // mod wamr; -mod symbol_name; pub const RESOURCE_IMPORT_BASE_CLASS_NAME: &str = "ResourceImportBase"; pub const RESOURCE_EXPORT_BASE_CLASS_NAME: &str = "ResourceExportBase"; diff --git a/crates/guest-rust/src/lib.rs b/crates/guest-rust/src/lib.rs index 3efde07ad..9a0773440 100644 --- a/crates/guest-rust/src/lib.rs +++ b/crates/guest-rust/src/lib.rs @@ -894,6 +894,72 @@ extern crate std; /// }); /// ``` /// +/// ## Native (non-WebAssembly) targets +/// +/// Generated bindings also compile for native targets, which is useful for +/// testing component code without a wasm runtime or for building it as a +/// `cdylib` plugin. Native linkers don't accept the `:`, `/`, `#`, `[` and +/// `]` characters that canonical ABI symbol names use, so on native targets +/// symbols are hex-encoded with the scheme in +/// `wit_bindgen_core::symbol_name` (the same one the C++ generator uses). +/// +/// Imports are not resolved by the native linker. Each import calls through +/// a function pointer that starts out null and is looked up on first use +/// from a host-installed resolver: after loading the library a host calls +/// the exported `__wit_bindgen_set_import_resolver` function with a callback +/// that maps an import's core module and function names to a function +/// pointer with the import's core signature. Its signature is +/// +/// ```c +/// void __wit_bindgen_set_import_resolver( +/// void *(*resolver)(void *ctx, const char *import), +/// void *ctx); +/// ``` +/// +/// where `import` is the import's core module and function name joined by +/// `#` as one NUL-terminated string, e.g. `my:pkg/iface@1.0.0#[method]res.frob` +/// (see `wit_bindgen::rt::ImportResolver`). Passing a null `resolver` +/// uninstalls the current one. +/// +/// This means everything links whether or not a host is present, the import +/// shims define no global symbols at all, and a host only needs to implement +/// the imports it cares about — calling an import the resolver doesn't +/// provide aborts with a message naming it. +/// +/// The resolver should be installed once, before any export is called: each +/// import caches the pointer it was given, so a later +/// `__wit_bindgen_set_import_resolver` call won't be seen by imports that +/// have already been resolved. The resolver is also shared by every +/// `generate!` invocation linked into the library and is keyed only by core +/// module and function name, so two worlds importing the same core name +/// necessarily get the same implementation. +/// +/// Exports, including post-return functions, async callbacks, and resource +/// destructors, are exported under their hex-encoded core export names. A +/// `__wit_bindgen_cabi_realloc` function is also exported so hosts can +/// allocate guest-owned memory when lowering arguments, as the canonical ABI +/// requires. That symbol and `__wit_bindgen_set_import_resolver` are defined +/// once in the `wit-bindgen` runtime crate rather than per world. +/// +/// Each world additionally exports a marker function +/// `const char *__wit_bindgen_world_(void)`, where `` is the +/// hex-encoded fully qualified world name including its package version and +/// `type_section_suffix`, e.g. `my:pkg@1.0.0/my-world`. Since `dlsym` can't +/// tell a host whether the library it opened implements the world it expects +/// — and the canonical ABI lowering of a world changes with the world, so +/// guessing wrong corrupts memory instead of failing cleanly — a host should +/// look this symbol up before installing a resolver or calling an export, and +/// treat a missing symbol as the wrong plugin. Calling it returns the world +/// name as a NUL-terminated string, for the resulting error message. +/// +/// This marker is keyed the same way as the `component-type` custom section a +/// wasm build emits, so the rules are the ones a wasm build already imposes. +/// Binding the same world twice needs a `type_section_suffix` to tell the +/// markers apart (plus `export_prefix` for the core export names). Two +/// *different* worlds sharing one fully qualified name is not a supported +/// configuration at all: here the linker rejects the duplicate marker, and on +/// wasm `wit-component` refuses to merge the two packages. +/// /// [WIT package]: https://component-model.bytecodealliance.org/design/packages.html #[cfg(feature = "macros")] pub use wit_bindgen_rust_macro::generate; diff --git a/crates/guest-rust/src/rt/mod.rs b/crates/guest-rust/src/rt/mod.rs index b9dbe2946..c6360d38c 100644 --- a/crates/guest-rust/src/rt/mod.rs +++ b/crates/guest-rust/src/rt/mod.rs @@ -153,7 +153,7 @@ pub fn maybe_link_cabi_realloc() { /// `cabi_realloc` module above. It's otherwise never explicitly called. /// /// For more information about this see `./ci/rebuild-libwit-bindgen-cabi.sh`. -#[cfg(any(target_env = "p1", target_env = ""))] +#[cfg(any(target_env = "p1", target_env = "", not(target_arch = "wasm32")))] pub unsafe fn cabi_realloc( old_ptr: *mut u8, old_len: usize, @@ -192,6 +192,105 @@ pub unsafe fn cabi_realloc( return ptr; } +/// Native (non-wasm) import resolution. +/// +/// On native targets imports aren't resolved by the linker. Each generated +/// import shim instead asks a host-installed resolver for its implementation +/// the first time it's called, identifying the import by its core module and +/// function name as plain strings. The host installs the resolver once per +/// loaded library through `__wit_bindgen_set_import_resolver`, so the import +/// shims define no global symbols at all and any number of `generate!` +/// invocations can share one resolver. (Export symbols and world markers are +/// a separate matter: those are global, so binding the same world twice in +/// one binary needs `export_prefix` and `type_section_suffix` respectively to +/// avoid duplicate symbols.) +/// +/// Because each shim caches the pointer the resolver handed it, installing a +/// resolver a second time has no effect on imports that have already been +/// called. Hosts are expected to install one before calling any export. +#[cfg(not(target_arch = "wasm32"))] +mod native_imports { + use core::ffi::{CStr, c_char}; + use core::sync::atomic::{AtomicPtr, Ordering}; + + /// A host-provided callback returning the implementation of the import + /// named by `import`, or null if the host doesn't implement it. The + /// returned pointer must be a function with the import's core signature. + /// `ctx` is the value passed alongside the resolver, returned to the host + /// on every call. + /// + /// `import` is the import's canonical ABI core module and function name + /// joined by `#`, as a single NUL-terminated string — for example + /// `my:pkg/iface@1.0.0#[method]res.frob`, or `$root#some-func` for a + /// function imported at the top level of a world. (Note the package + /// version trails the interface name here, unlike in a world name.) It + /// points into the guest's static data and stays valid for as long as the + /// library is loaded, so a host may use it as a lookup key without + /// copying it. + pub type ImportResolver = unsafe extern "C" fn(ctx: *mut (), import: *const c_char) -> *mut (); + + static RESOLVER: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut()); + static RESOLVER_CTX: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut()); + + /// Installs the import resolver for this linkage unit. Hosts call this + /// (typically via `dlsym`) after loading the library and before calling + /// any export. + /// + /// Passing `None` for `resolver` uninstalls the current one. That only + /// affects imports which haven't been resolved yet; imports already + /// called keep the pointer they cached. + #[unsafe(no_mangle)] + pub unsafe extern "C" fn __wit_bindgen_set_import_resolver( + resolver: Option, + ctx: *mut (), + ) { + let resolver = match resolver { + Some(resolver) => resolver as *mut (), + None => core::ptr::null_mut(), + }; + RESOLVER_CTX.store(ctx, Ordering::Relaxed); + // The release store of the resolver publishes the context above. + RESOLVER.store(resolver, Ordering::Release); + } + + /// Called by generated import shims on their first invocation. + /// + /// `import` is the `module#name` string described on [`ImportResolver`]. + pub fn resolve_import(import: &CStr) -> *mut () { + let display = import.to_str().unwrap_or(""); + let resolver = RESOLVER.load(Ordering::Acquire); + assert!( + !resolver.is_null(), + "import `{display}` was called before the host installed an \ + import resolver via `__wit_bindgen_set_import_resolver`" + ); + let ctx = RESOLVER_CTX.load(Ordering::Relaxed); + let resolver: ImportResolver = unsafe { core::mem::transmute(resolver) }; + let ptr = unsafe { resolver(ctx, import.as_ptr()) }; + assert!( + !ptr.is_null(), + "the host's import resolver provided no implementation for \ + import `{display}`" + ); + ptr + } + + /// The guest allocator, exported so hosts can allocate guest-owned + /// memory when lowering data, as the canonical ABI requires. + #[unsafe(no_mangle)] + pub unsafe extern "C" fn __wit_bindgen_cabi_realloc( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, + ) -> *mut u8 { + unsafe { crate::rt::cabi_realloc(old_ptr, old_len, align, new_len) } + } +} + +#[cfg(not(target_arch = "wasm32"))] +pub use native_imports::{ImportResolver, resolve_import}; + /// Provide a hook for generated export functions to run static constructors at /// most once. /// diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 70aeb67a5..efd10db19 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -67,6 +67,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { &rust_name, params, results, + self.r#gen.r#gen.runtime_path(), )); rust_name } diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 628142d19..d52c733ed 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -12,7 +12,7 @@ use std::fmt::Write as _; use std::mem; use wit_bindgen_core::abi::{self, AbiVariant, LiftLower}; use wit_bindgen_core::{ - AnonymousTypeGenerator, ChainingMode, Source, TypeInfo, dealias, uwrite, uwriteln, + AnonymousTypeGenerator, ChainingMode, Source, TypeInfo, dealias, symbol_name, uwrite, uwriteln, wit_parser::*, }; @@ -218,6 +218,7 @@ impl<'i> InterfaceGenerator<'i> { "new", &[abi::WasmType::Pointer], &[abi::WasmType::I32], + self.r#gen.runtime_path(), ); let import_rep = crate::declare_import( &wasm_import_module, @@ -225,6 +226,7 @@ impl<'i> InterfaceGenerator<'i> { "rep", &[abi::WasmType::I32], &[abi::WasmType::Pointer], + self.r#gen.runtime_path(), ); uwriteln!( self.src, @@ -353,7 +355,6 @@ macro_rules! {macro_name} {{ }; self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*", async_); } - let export_prefix = self.r#gen.opts.export_prefix.as_deref().unwrap_or(""); for name in resources_to_drop { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), @@ -362,13 +363,13 @@ macro_rules! {macro_name} {{ } }; let camel = name.to_upper_camel_case(); + let attrs = self.core_export_attrs(&format!("{module}#[dtor]{name}")); uwriteln!( self.src, r#" const _: () = {{ #[doc(hidden)] - #[unsafe(export_name = "{export_prefix}{module}#[dtor]{name}")] - #[allow(non_snake_case)] + {attrs}#[allow(non_snake_case)] unsafe extern "C" fn dtor(rep: *mut u8) {{ unsafe {{ $($path_to_types)*::{camel}::dtor::< @@ -1036,6 +1037,7 @@ fn abi_layout(&mut self) -> ::core::alloc::Layout {{ "call", &sig.params, &sig.results, + self.r#gen.runtime_path(), ); let mut args = String::new(); for i in 0..params_lower.len() { @@ -1298,21 +1300,20 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8) Identifier::World(_) => None, Identifier::StreamOrFuturePayload => unreachable!(), }; - let export_prefix = self.r#gen.opts.export_prefix.as_deref().unwrap_or(""); let export_name = func.legacy_core_export_name(wasm_module_export_name.as_deref()); let export_name = if async_ { format!("[async-lift]{export_name}") } else { export_name.to_string() }; + + let attrs = self.core_export_attrs(&export_name); uwrite!( self.src, "\ - #[unsafe(export_name = \"{export_prefix}{export_name}\")] - unsafe extern \"C\" fn export_{name_snake}\ + {attrs}unsafe extern \"C\" fn export_{name_snake}\ ", ); - let params = self.print_export_sig(func, async_); self.push_str(" {\n"); uwriteln!( @@ -1322,13 +1323,12 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8) ); self.push_str("}\n"); - let export_prefix = self.r#gen.opts.export_prefix.as_deref().unwrap_or(""); if async_ { + let attrs = self.core_export_attrs(&format!("[callback]{export_name}")); uwrite!( self.src, "\ - #[unsafe(export_name = \"{export_prefix}[callback]{export_name}\")] - unsafe extern \"C\" fn _callback_{name_snake}(event0: u32, event1: u32, event2: u32) -> u32 {{ + {attrs}unsafe extern \"C\" fn _callback_{name_snake}(event0: u32, event1: u32, event2: u32) -> u32 {{ unsafe {{ {path_to_self}::__callback_{name_snake}(event0, event1, event2) }} @@ -1336,11 +1336,11 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8) " ); } else if abi::guest_export_needs_post_return(self.resolve, func) { + let attrs = self.core_export_attrs(&format!("cabi_post_{export_name}")); uwrite!( self.src, "\ - #[unsafe(export_name = \"{export_prefix}cabi_post_{export_name}\")] - unsafe extern \"C\" fn _post_return_{name_snake}\ + {attrs}unsafe extern \"C\" fn _post_return_{name_snake}\ " ); let params = self.print_post_return_sig(func); @@ -1354,6 +1354,24 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8) } } + /// Returns the `export_name` attributes for a core export named + /// `export_name`. + /// + /// Has to exist due to the fact that native names cannot contain + /// special characters that wasm32 can like '/'. + /// + /// `cfg_attr` conditions are mutually exclusive, so exactly one attribute + /// applies on any target (for names that survive encoding unchanged, such + /// as `$root` exports, both carry the same string). + fn core_export_attrs(&self, export_name: &str) -> String { + let prefix = self.r#gen.opts.export_prefix.as_deref().unwrap_or(""); + let native = symbol_name::make_external_component(export_name); + format!( + "#[cfg_attr(target_arch = \"wasm32\", unsafe(export_name = \"{prefix}{export_name}\"))]\n\ + #[cfg_attr(not(target_arch = \"wasm32\"), unsafe(export_name = \"{prefix}{native}\"))]\n" + ) + } + fn print_export_sig(&mut self, func: &Function, async_: bool) -> Vec { self.src.push_str("("); let variant = if async_ { @@ -2987,6 +3005,7 @@ impl<'a> {camel}Borrow<'a>{{ "drop", &[abi::WasmType::I32], &[], + self.r#gen.runtime_path(), ); uwriteln!( self.src, diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 33bf3806a..0e9dd5bba 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -11,7 +11,8 @@ use std::str::FromStr; use wit_bindgen_core::abi::{Bitcast, WasmType}; use wit_bindgen_core::{ AsyncFilterSet, ChainableMethodFilterSet, ChainingMode, Files, InterfaceGenerator as _, Source, - Types, WorldGenerator, dealias, name_package_module, uwrite, uwriteln, wit_parser::*, + Types, WorldGenerator, dealias, name_package_module, symbol_name, uwrite, uwriteln, + wit_parser::*, }; mod bindgen; @@ -549,6 +550,46 @@ impl RustWasm { Ok(remapped) } + /// Emits this world's native marker symbol. + /// + /// Native bindings are loaded by `dlsym`, which gives a host no way to + /// tell whether the library it opened implements the WIT world it expects + /// or something else — and the canonical ABI lowering of a world changes + /// with the world, so guessing wrong corrupts memory rather than failing + /// cleanly. So each world exports a marker function named for the world, + /// which a host looks up before calling anything else, treating a missing + /// symbol as the wrong plugin. Calling it returns the world name as a + /// NUL-terminated string, for the error message. + /// + /// The symbol is keyed on the world's name and `type_section_suffix`, + /// exactly like the `component-type` custom section is on wasm, so the + /// rules match what a wasm build already imposes: binding the same world + /// twice needs a `type_section_suffix` to tell the two apart, and two + /// different worlds sharing a fully qualified name is not a supported + /// configuration in the first place — on wasm `wit-component` refuses to + /// merge them, and here the linker refuses to define the symbol twice. + fn finish_native_world_marker(&mut self, resolve: &Resolve, world: WorldId) { + let world = &resolve.worlds[world]; + let pkg = world + .package + .map(|p| resolve.packages[p].name.to_string()) + .unwrap_or_default(); + let name = format!("{pkg}/{}", world.name); + let suffix = self.opts.type_section_suffix.as_deref().unwrap_or(""); + let symbol = symbol_name::make_external_component(&format!("{name}{suffix}")); + uwriteln!( + self.src, + r#" +#[cfg(not(target_arch = "wasm32"))] +#[unsafe(no_mangle)] +#[allow(non_snake_case)] +pub extern "C" fn __wit_bindgen_world_{symbol}() -> *const ::core::ffi::c_char {{ + c"{name}".as_ptr() +}} +"# + ); + } + fn finish_runtime_module(&mut self) { if !self.rt_module.is_empty() { // As above, disable rustfmt, as we use prettyplease. @@ -1501,6 +1542,7 @@ impl WorldGenerator for RustWasm { let exports = mem::take(&mut self.export_modules); self.emit_modules(exports); + self.finish_native_world_marker(resolve, world); self.finish_runtime_module(); self.finish_export_macro(resolve, world); @@ -1875,12 +1917,26 @@ fn wasm_type(ty: WasmType) -> &'static str { } } +/// Declares the core import `wasm_import_module`/`wasm_import_name` as a +/// function named `rust_name`, usable on both `wasm32` and native targets. +/// +/// On `wasm32` this is an ordinary linker-resolved wasm import. On native +/// targets the import instead calls through a function pointer looked up on +/// first use from the host's import resolver (see `rt::resolve_import` and +/// the exported `__wit_bindgen_set_import_resolver`), identified by its core +/// module and function names as plain strings. Everything links whether or +/// not a host is present — the shim defines no global symbols, so import +/// shims never collide between `generate!` invocations — and calling an +/// import with no resolver (or one the host doesn't implement) aborts with a +/// message naming the import, just as the old `unreachable!()` stubs +/// aborted. fn declare_import( wasm_import_module: &str, wasm_import_name: &str, rust_name: &str, params: &[WasmType], results: &[WasmType], + rt: &str, ) -> String { let mut sig = "(".to_owned(); for param in params.iter() { @@ -1894,18 +1950,44 @@ fn declare_import( sig.push_str(" -> "); sig.push_str(wasm_type(*result)); } + + let named_params: Vec = params + .iter() + .enumerate() + .map(|(i, ty)| format!("arg{i}: {}", wasm_type(*ty))) + .collect(); + let ret_sig = results + .first() + .map(|r| format!(" -> {}", wasm_type(*r))) + .unwrap_or_default(); + let call_args = (0..params.len()) + .map(|i| format!("arg{i}")) + .collect::>() + .join(", "); + let named_params_str = named_params.join(", "); + format!( - " - #[cfg(target_arch = \"wasm32\")] - #[link(wasm_import_module = \"{wasm_import_module}\")] - unsafe extern \"C\" {{ - #[link_name = \"{wasm_import_name}\"] + r#" + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "{wasm_import_module}")] + unsafe extern "C" {{ + #[link_name = "{wasm_import_name}"] fn {rust_name}{sig}; }} - #[cfg(not(target_arch = \"wasm32\"))] - unsafe extern \"C\" fn {rust_name}{sig} {{ unreachable!() }} - " + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn {rust_name}({named_params_str}){ret_sig} {{ + static CACHE: ::core::sync::atomic::AtomicPtr<()> = + ::core::sync::atomic::AtomicPtr::new(::core::ptr::null_mut()); + let mut ptr = CACHE.load(::core::sync::atomic::Ordering::Acquire); + if ptr.is_null() {{ + ptr = {rt}::resolve_import(c"{wasm_import_module}#{wasm_import_name}"); + CACHE.store(ptr, ::core::sync::atomic::Ordering::Release); + }} + let f: unsafe extern "C" fn{sig} = unsafe {{ ::core::mem::transmute(ptr) }}; + unsafe {{ f({call_args}) }} + }} + "#, ) } diff --git a/crates/rust/tests/codegen.rs b/crates/rust/tests/codegen.rs index 5cc96cf42..28898f30e 100644 --- a/crates/rust/tests/codegen.rs +++ b/crates/rust/tests/codegen.rs @@ -24,8 +24,13 @@ mod multiple_paths { #[allow(unused, reason = "testing codegen, not functionality")] mod inline_and_path { wit_bindgen::generate!({ + // NB: this package is deliberately not `test:paths` like + // `multiple_paths` above. Two structurally different worlds under one + // fully qualified name is not a supported configuration: on wasm + // `wit-component` refuses to merge the two `component-type` sections, + // and natively the two world markers collide at link time. inline: r#" - package test:paths; + package test:inline-and-path-root; world test { import test:inline-and-path/bar; @@ -251,7 +256,11 @@ mod borrowing_method_chaining { } "#, generate_all, - chainable_methods: ["&all"] + chainable_methods: ["&all"], + // `method_chaining` above binds this same world; natively the world + // marker symbols would collide without a suffix, just like the + // `component-type` sections would in a wasm component build. + type_section_suffix: "-borrowing", }); } @@ -422,3 +431,151 @@ mod versioned_selectors { assert!(Alpha { x: 1 } < Alpha { x: 2 }); } } + +// These two modules deliberately call `export!`: the native export symbols +// are generated inside the `__export_*_cabi!` macro, so without expanding it +// none of that code is ever compiled. (The native import shims compile in +// every module above as a side effect of being default behavior.) +#[allow(unused, reason = "testing codegen, not functionality")] +mod native_symbols { + wit_bindgen::generate!({ + inline: r#" + package test:native; + + interface operations { + resource thing { + constructor(x: u32); + get: func() -> u32; + } + add: func(a: u32, b: u32) -> u32; + describe: func(value: u32) -> string; + } + + world test { + import operations; + export operations; + } + "#, + generate_all, + }); + + struct Component; + + impl exports::test::native::operations::Guest for Component { + type Thing = MyThing; + + fn add(a: u32, b: u32) -> u32 { + a + b + } + + fn describe(value: u32) -> String { + value.to_string() + } + } + + struct MyThing(u32); + + impl exports::test::native::operations::GuestThing for MyThing { + fn new(x: u32) -> Self { + MyThing(x) + } + + fn get(&self) -> u32 { + self.0 + } + } + + export!(Component); +} + +#[allow(unused, reason = "testing codegen, not functionality")] +mod native_symbols_async { + wit_bindgen::generate!({ + inline: r#" + package test:native-async; + + interface operations { + describe: func(value: u32) -> string; + } + + world test { + import operations; + export operations; + } + "#, + generate_all, + async: true, + }); + + struct Component; + + impl exports::test::native_async::operations::Guest for Component { + async fn describe(value: u32) -> String { + value.to_string() + } + } + + export!(Component); +} + +// Import shims are not namespaced by world (they define no symbols at all), +// so two worlds importing the same interface link fine side by side. +#[allow(unused, reason = "testing codegen, not functionality")] +mod native_symbols_shared_one { + wit_bindgen::generate!({ + inline: r#" + package test:native-shared; + interface operations { add: func(a: u32, b: u32) -> u32; } + world one { import operations; } + "#, + generate_all, + }); +} + +#[allow(unused, reason = "testing codegen, not functionality")] +mod native_symbols_shared_two { + wit_bindgen::generate!({ + inline: r#" + package test:native-shared; + interface operations { add: func(a: u32, b: u32) -> u32; } + world two { import operations; } + "#, + generate_all, + }); +} + +// The other two kinds of native symbol are global, so binding the *same* world +// twice needs both knobs: `type_section_suffix` for the world marker (the same +// option a wasm build needs to keep the two `component-type` sections apart) +// and `export_prefix` for the core export names. Drop the suffix and this +// fails with `symbol `__wit_bindgen_world_...` is already defined`; drop the +// prefix and it fails with `symbol `run` is already defined`. +macro_rules! native_symbols_same_world { + ($module:ident, $tag:literal) => { + #[allow(unused, reason = "testing codegen, not functionality")] + mod $module { + wit_bindgen::generate!({ + inline: r#" + package test:native-same-world; + world w { export run: func() -> u32; } + "#, + generate_all, + type_section_suffix: $tag, + export_prefix: $tag, + }); + + struct Component; + + impl Guest for Component { + fn run() -> u32 { + 0 + } + } + + export!(Component); + } + }; +} + +native_symbols_same_world!(native_symbols_same_world_a, "a_"); +native_symbols_same_world!(native_symbols_same_world_b, "b_");