Skip to content

Commit 8fa3f37

Browse files
feat(rust) add native link option
1 parent c36517b commit 8fa3f37

10 files changed

Lines changed: 427 additions & 65 deletions

File tree

crates/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod path;
1414
pub use path::name_package_module;
1515
mod async_;
1616
pub use async_::AsyncFilterSet;
17+
pub mod symbol_name;
1718

1819
#[derive(Default, Copy, Clone, PartialEq, Eq, Debug)]
1920
pub enum Direction {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use wit_bindgen_core::abi;
1+
use crate::abi;
22

33
fn hexdigit(v: u32) -> char {
44
if v < 10 {

crates/cpp/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ use std::{
99
process::{Command, Stdio},
1010
str::FromStr,
1111
};
12-
use symbol_name::{make_external_component, make_external_symbol};
1312
use wit_bindgen_c::to_c_ident;
1413
use wit_bindgen_core::{
1514
Files, InterfaceGenerator, Source, Types, WorldGenerator,
1615
abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType},
17-
name_package_module, uwrite, uwriteln,
16+
name_package_module,
17+
symbol_name::{make_external_component, make_external_symbol},
18+
uwrite, uwriteln,
1819
wit_parser::{
1920
Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, Param,
2021
Resolve, SizeAlign, Stability, Type, TypeDef, TypeDefKind, TypeId, TypeOwner, WorldId,
@@ -24,7 +25,6 @@ use wit_bindgen_core::{
2425
use wit_parser::TypeIdVisitor;
2526

2627
// mod wamr;
27-
mod symbol_name;
2828

2929
pub const RESOURCE_IMPORT_BASE_CLASS_NAME: &str = "ResourceImportBase";
3030
pub const RESOURCE_EXPORT_BASE_CLASS_NAME: &str = "ResourceExportBase";

crates/guest-rust/macro/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ impl Parse for Config {
175175
Opt::MergeStructurallyEqualTypes(enable) => {
176176
opts.merge_structurally_equal_types = Some(Some(enable.value()))
177177
}
178+
Opt::LinkNativeSymbols(enable) => {
179+
opts.link_native_symbols = enable.value();
180+
}
178181
}
179182
}
180183
} else {
@@ -332,6 +335,7 @@ mod kw {
332335
syn::custom_keyword!(debug);
333336
syn::custom_keyword!(enable_method_chaining);
334337
syn::custom_keyword!(merge_structurally_equal_types);
338+
syn::custom_keyword!(link_native_symbols);
335339
}
336340

337341
#[derive(Clone)]
@@ -416,6 +420,7 @@ enum Opt {
416420
Debug(syn::LitBool),
417421
EnableMethodChaining(syn::LitBool),
418422
MergeStructurallyEqualTypes(syn::LitBool),
423+
LinkNativeSymbols(syn::LitBool),
419424
}
420425

421426
impl Parse for Opt {
@@ -623,6 +628,10 @@ impl Parse for Opt {
623628
input.parse::<kw::merge_structurally_equal_types>()?;
624629
input.parse::<Token![:]>()?;
625630
Ok(Opt::MergeStructurallyEqualTypes(input.parse()?))
631+
} else if l.peek(kw::link_native_symbols) {
632+
input.parse::<kw::link_native_symbols>()?;
633+
input.parse::<Token![:]>()?;
634+
Ok(Opt::LinkNativeSymbols(input.parse()?))
626635
} else {
627636
Err(l.error())
628637
}

crates/guest-rust/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,24 @@ extern crate std;
891891
/// // structurally equal, which is useful when import and export the same
892892
/// // interface.
893893
/// merge_structurally_equal_types: true,
894+
///
895+
/// // Make the same generated bindings usable on a native (non-wasm)
896+
/// // target as well as on wasm32.
897+
/// //
898+
/// // Imports normally compile to `unreachable!()` off wasm32. With this
899+
/// // enabled each one instead calls through a function pointer that a host
900+
/// // installs at load time via a generated
901+
/// // `__wit_bindgen_register_*` symbol, and exports additionally get a
902+
/// // native symbol whose name encodes the characters a linker cannot
903+
/// // accept. Both targets still build from one source.
904+
/// //
905+
/// // The registration symbols are prefixed with a hex-encoded
906+
/// // `<package>/<world>` so that two `generate!` invocations in one crate
907+
/// // don't collide. Binding the *same* world twice in one linkage unit
908+
/// // still does; use `type_section_suffix` to tell them apart. See
909+
/// // `wit_bindgen_rust::Opts::link_native_symbols` for the full list of
910+
/// // symbols a host can expect.
911+
/// link_native_symbols: true,
894912
/// });
895913
/// ```
896914
///

crates/guest-rust/src/rt/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ pub fn maybe_link_cabi_realloc() {
153153
/// `cabi_realloc` module above. It's otherwise never explicitly called.
154154
///
155155
/// For more information about this see `./ci/rebuild-libwit-bindgen-cabi.sh`.
156-
#[cfg(any(target_env = "p1", target_env = ""))]
156+
#[cfg(any(target_env = "p1", target_env = "", not(target_arch = "wasm32")))]
157157
pub unsafe fn cabi_realloc(
158158
old_ptr: *mut u8,
159159
old_len: usize,

crates/rust/src/bindgen.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
6767
&rust_name,
6868
params,
6969
results,
70+
self.r#gen.r#gen.native_symbols(),
7071
));
7172
rust_name
7273
}

crates/rust/src/interface.rs

Lines changed: 94 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::fmt::Write as _;
1212
use std::mem;
1313
use wit_bindgen_core::abi::{self, AbiVariant, LiftLower};
1414
use wit_bindgen_core::{
15-
AnonymousTypeGenerator, Source, TypeInfo, dealias, uwrite, uwriteln, wit_parser::*,
15+
AnonymousTypeGenerator, Source, TypeInfo, dealias, symbol_name, uwrite, uwriteln, wit_parser::*,
1616
};
1717

1818
pub struct InterfaceGenerator<'a> {
@@ -212,13 +212,15 @@ impl<'i> InterfaceGenerator<'i> {
212212
"new",
213213
&[abi::WasmType::Pointer],
214214
&[abi::WasmType::I32],
215+
self.r#gen.native_symbols(),
215216
);
216217
let import_rep = crate::declare_import(
217218
&wasm_import_module,
218219
&format!("[resource-rep]{resource_name}"),
219220
"rep",
220221
&[abi::WasmType::I32],
221222
&[abi::WasmType::Pointer],
223+
self.r#gen.native_symbols(),
222224
);
223225
uwriteln!(
224226
self.src,
@@ -347,7 +349,6 @@ macro_rules! {macro_name} {{
347349
};
348350
self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*", async_);
349351
}
350-
let export_prefix = self.r#gen.opts.export_prefix.as_deref().unwrap_or("");
351352
for name in resources_to_drop {
352353
let module = match self.identifier {
353354
Identifier::Interface(_, key) => self.resolve.name_world_key(key),
@@ -356,23 +357,25 @@ macro_rules! {macro_name} {{
356357
}
357358
};
358359
let camel = name.to_upper_camel_case();
359-
uwriteln!(
360-
self.src,
361-
r#"
362-
const _: () = {{
363-
#[doc(hidden)]
364-
#[unsafe(export_name = "{export_prefix}{module}#[dtor]{name}")]
365-
#[allow(non_snake_case)]
366-
unsafe extern "C" fn dtor(rep: *mut u8) {{
367-
unsafe {{
368-
$($path_to_types)*::{camel}::dtor::<
369-
<$ty as $($path_to_types)*::Guest>::{camel}
370-
>(rep)
360+
for (cfg, symbol) in self.core_export_symbols(&format!("{module}#[dtor]{name}")) {
361+
uwriteln!(
362+
self.src,
363+
r#"
364+
const _: () = {{
365+
#[doc(hidden)]
366+
{cfg}#[unsafe(export_name = "{symbol}")]
367+
#[allow(non_snake_case)]
368+
unsafe extern "C" fn dtor(rep: *mut u8) {{
369+
unsafe {{
370+
$($path_to_types)*::{camel}::dtor::<
371+
<$ty as $($path_to_types)*::Guest>::{camel}
372+
>(rep)
373+
}}
371374
}}
372-
}}
373-
}};
374-
"#
375-
);
375+
}};
376+
"#
377+
);
378+
}
376379
}
377380
uwriteln!(self.src, "}};);");
378381
uwriteln!(self.src, "}}");
@@ -1019,6 +1022,7 @@ fn abi_layout(&mut self) -> ::core::alloc::Layout {{
10191022
"call",
10201023
&sig.params,
10211024
&sig.results,
1025+
self.r#gen.native_symbols(),
10221026
);
10231027
let mut args = String::new();
10241028
for i in 0..params_lower.len() {
@@ -1281,60 +1285,93 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
12811285
Identifier::World(_) => None,
12821286
Identifier::StreamOrFuturePayload => unreachable!(),
12831287
};
1284-
let export_prefix = self.r#gen.opts.export_prefix.as_deref().unwrap_or("");
12851288
let export_name = func.legacy_core_export_name(wasm_module_export_name.as_deref());
12861289
let export_name = if async_ {
12871290
format!("[async-lift]{export_name}")
12881291
} else {
12891292
export_name.to_string()
12901293
};
1291-
uwrite!(
1292-
self.src,
1293-
"\
1294-
#[unsafe(export_name = \"{export_prefix}{export_name}\")]
1295-
unsafe extern \"C\" fn export_{name_snake}\
1296-
",
1297-
);
12981294

1299-
let params = self.print_export_sig(func, async_);
1300-
self.push_str(" {\n");
1301-
uwriteln!(
1302-
self.src,
1303-
"unsafe {{ {path_to_self}::_export_{name_snake}_cabi::<{ty}>({}) }}",
1304-
params.join(", ")
1305-
);
1306-
self.push_str("}\n");
1307-
1308-
let export_prefix = self.r#gen.opts.export_prefix.as_deref().unwrap_or("");
1309-
if async_ {
1295+
for (cfg, symbol) in self.core_export_symbols(&export_name) {
13101296
uwrite!(
13111297
self.src,
13121298
"\
1313-
#[unsafe(export_name = \"{export_prefix}[callback]{export_name}\")]
1314-
unsafe extern \"C\" fn _callback_{name_snake}(event0: u32, event1: u32, event2: u32) -> u32 {{
1315-
unsafe {{
1316-
{path_to_self}::__callback_{name_snake}(event0, event1, event2)
1317-
}}
1318-
}}
1319-
"
1320-
);
1321-
} else if abi::guest_export_needs_post_return(self.resolve, func) {
1322-
uwrite!(
1323-
self.src,
1324-
"\
1325-
#[unsafe(export_name = \"{export_prefix}cabi_post_{export_name}\")]
1326-
unsafe extern \"C\" fn _post_return_{name_snake}\
1327-
"
1299+
{cfg}#[unsafe(export_name = \"{symbol}\")]
1300+
unsafe extern \"C\" fn export_{name_snake}\
1301+
",
13281302
);
1329-
let params = self.print_post_return_sig(func);
1330-
self.src.push_str("{\n");
1303+
let params = self.print_export_sig(func, async_);
1304+
self.push_str(" {\n");
13311305
uwriteln!(
13321306
self.src,
1333-
"unsafe {{ {path_to_self}::__post_return_{name_snake}::<{ty}>({}) }}",
1307+
"unsafe {{ {path_to_self}::_export_{name_snake}_cabi::<{ty}>({}) }}",
13341308
params.join(", ")
13351309
);
1336-
self.src.push_str("}\n");
1310+
self.push_str("}\n");
1311+
}
1312+
1313+
if async_ {
1314+
for (cfg, symbol) in self.core_export_symbols(&format!("[callback]{export_name}")) {
1315+
uwrite!(
1316+
self.src,
1317+
"\
1318+
{cfg}#[unsafe(export_name = \"{symbol}\")]
1319+
unsafe extern \"C\" fn _callback_{name_snake}(event0: u32, event1: u32, event2: u32) -> u32 {{
1320+
unsafe {{
1321+
{path_to_self}::__callback_{name_snake}(event0, event1, event2)
1322+
}}
1323+
}}
1324+
"
1325+
);
1326+
}
1327+
} else if abi::guest_export_needs_post_return(self.resolve, func) {
1328+
for (cfg, symbol) in self.core_export_symbols(&format!("cabi_post_{export_name}")) {
1329+
uwrite!(
1330+
self.src,
1331+
"\
1332+
{cfg}#[unsafe(export_name = \"{symbol}\")]
1333+
unsafe extern \"C\" fn _post_return_{name_snake}\
1334+
"
1335+
);
1336+
let params = self.print_post_return_sig(func);
1337+
self.src.push_str("{\n");
1338+
uwriteln!(
1339+
self.src,
1340+
"unsafe {{ {path_to_self}::__post_return_{name_snake}::<{ty}>({}) }}",
1341+
params.join(", ")
1342+
);
1343+
self.src.push_str("}\n");
1344+
}
1345+
}
1346+
}
1347+
1348+
/// Returns each copy of a core export named `export_name` that needs to be
1349+
/// emitted, as `(cfg, symbol)`: the `cfg` attribute to gate the copy with
1350+
/// and the symbol to export it as.
1351+
///
1352+
/// Normally there's just one copy: the canonical ABI name with no `cfg`.
1353+
/// With `link_native_symbols` enabled a second, hex-encoded copy is emitted
1354+
/// for native targets as well, because native linkers reject the `:`, `/`,
1355+
/// `#`, `[` and `]` characters that canonical names contain. Names that
1356+
/// survive encoding unchanged (`$root` exports, for instance) are emitted
1357+
/// once with no `cfg` rather than twice.
1358+
fn core_export_symbols(&self, export_name: &str) -> Vec<(&'static str, String)> {
1359+
let prefix = self.r#gen.opts.export_prefix.as_deref().unwrap_or("");
1360+
let wasm = format!("{prefix}{export_name}");
1361+
if self.r#gen.native_symbols().is_none() {
1362+
return vec![("", wasm)];
1363+
}
1364+
let native = format!(
1365+
"{prefix}{}",
1366+
symbol_name::make_external_component(export_name)
1367+
);
1368+
if native == wasm {
1369+
return vec![("", wasm)];
13371370
}
1371+
vec![
1372+
("#[cfg(target_arch = \"wasm32\")]\n", wasm),
1373+
("#[cfg(not(target_arch = \"wasm32\"))]\n", native),
1374+
]
13381375
}
13391376

13401377
fn print_export_sig(&mut self, func: &Function, async_: bool) -> Vec<String> {
@@ -2952,6 +2989,7 @@ impl<'a> {camel}Borrow<'a>{{
29522989
"drop",
29532990
&[abi::WasmType::I32],
29542991
&[],
2992+
self.r#gen.native_symbols(),
29552993
);
29562994
uwriteln!(
29572995
self.src,

0 commit comments

Comments
 (0)