Skip to content

Commit 62a7381

Browse files
authored
Merge pull request #1092 from godot-rust/bugfix/wasm-registration
Remove `gensym`, fix Wasm class registration
2 parents 75a36ca + c5d76d9 commit 62a7381

File tree

10 files changed

+104
-57
lines changed

10 files changed

+104
-57
lines changed

.github/other/deny.toml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -182,18 +182,16 @@ allow = [
182182
# List of crates to deny
183183
# See also: https://github.com/RustSec/advisory-db/issues/173
184184
deny = [
185-
# unmaintained since Feb 20 + https://github.com/noamtashma/owning-ref-unsoundness
186-
{ name = "owning_ref" },
187-
188-
# unmaintained since Jul 20, RustSec refuses to acknowledge "because author says maintained"
189-
# https://togithub.com/rustsec/advisory-db/pull/891
190-
{ name = "im" },
191-
{ name = "im-rc" },
192-
193185
# Too heavy in default setup. Can be included for api-custom etc.
194186
# Note: rand indirectly depends on syn via rand_chacha -> ppv-lite86 -> zerocopy -> zerocopy-derive.
195-
{ name = "syn", wrappers = ["bindgen", "gensym", "zerocopy-derive"] },
187+
{ name = "syn", wrappers = ["bindgen"] },
196188
{ name = "serde", wrappers = ["godot-core", "serde_json"] },
189+
{ name = "rand" },
190+
191+
# Unmaintained since Jul 2020, RustSec refuses to acknowledge "because author says maintained".
192+
# https://github.com/rustsec/advisory-db/pull/891
193+
{ name = "im" },
194+
{ name = "im-rc" },
197195
]
198196

199197
# List of features to allow/deny

godot-ffi/Cargo.toml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,14 @@ api-4-3 = ["godot-bindings/api-4-3"]
3333
api-4-4 = ["godot-bindings/api-4-4"]
3434
# ]]
3535

36-
# TODO: get rid of gensym, which is implementable with proc-macros. Update cargo-deny.
37-
# gensym pulls in entire syn for 10 LoC. See https://github.com/regiontog/gensym/blob/master/src/lib.rs.
38-
# Might however require godot-ffi to depend on godot-macro, which is not ideal...
3936
[dependencies]
4037

4138
[target.'cfg(target_os = "linux")'.dependencies]
42-
libc = "0.2.153"
39+
libc = "0.2.171"
4340

4441
[target.'cfg(target_family = "wasm")'.dependencies]
45-
gensym = "0.1.1"
42+
# Only needed for WASM identifier generation.
43+
godot-macros = { path = "../godot-macros", version = "=0.2.4", features = ["experimental-wasm"] }
4644

4745
[build-dependencies]
4846
godot-bindings = { path = "../godot-bindings", version = "=0.2.4" }

godot-ffi/src/interface_init.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
1717
use crate as sys;
1818

19+
#[cfg(not(target_family = "wasm"))]
20+
use crate::toolbox::read_version_string;
21+
1922
// In WebAssembly, function references and data pointers live in different memory spaces, so trying to read the "memory"
2023
// at a function pointer (an index into a table) to heuristically determine which API we have (as is done below) won't work.
2124
#[cfg(target_family = "wasm")]
@@ -146,16 +149,3 @@ pub unsafe fn load_interface(
146149
) -> sys::GDExtensionInterface {
147150
sys::GDExtensionInterface::load(get_proc_address)
148151
}
149-
150-
fn read_version_string(version_ptr: &sys::GDExtensionGodotVersion) -> String {
151-
let char_ptr = version_ptr.string;
152-
153-
// SAFETY: `version_ptr` points to a layout-compatible version struct.
154-
let c_str = unsafe { std::ffi::CStr::from_ptr(char_ptr) };
155-
156-
String::from_utf8_lossy(c_str.to_bytes())
157-
.as_ref()
158-
.strip_prefix("Godot Engine ")
159-
.unwrap_or(&String::from_utf8_lossy(c_str.to_bytes()))
160-
.to_string()
161-
}

godot-ffi/src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,15 @@ mod toolbox;
6363

6464
#[doc(hidden)]
6565
#[cfg(target_family = "wasm")]
66-
pub use gensym::gensym;
66+
pub use godot_macros::wasm_declare_init_fn;
67+
68+
// No-op otherwise.
69+
#[doc(hidden)]
70+
#[cfg(not(target_family = "wasm"))]
71+
#[macro_export]
72+
macro_rules! wasm_declare_init_fn {
73+
() => {};
74+
}
6775

6876
pub use crate::godot_ffi::{GodotFfi, GodotNullableFfi, PrimitiveConversionError, PtrcallType};
6977

godot-ffi/src/plugins.rs

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,6 @@ macro_rules! plugin_registry {
2525
};
2626
}
2727

28-
#[doc(hidden)]
29-
#[macro_export]
30-
// Following rustfmt::skip is no longer needed, but there are 2 workarounds good to know, thus preserved.
31-
// #[allow(clippy::deprecated_cfg_attr)]
32-
// #[cfg_attr(rustfmt, rustfmt::skip)]
33-
// cfg_attr: workaround for https://github.com/rust-lang/rust/pull/52234#issuecomment-976702997
34-
macro_rules! plugin_execute_pre_main_wasm {
35-
($gensym:ident,) => {
36-
// Rust presently requires that statics with a custom `#[link_section]` must be a simple
37-
// list of bytes on the Wasm target (with no extra levels of indirection such as references).
38-
//
39-
// As such, instead we export a function with a random name of predictable format to be used by the embedder.
40-
#[no_mangle]
41-
extern "C" fn $gensym() {
42-
__init();
43-
}
44-
};
45-
}
46-
4728
/// Executes a block of code before main, by utilising platform specific linker instructions.
4829
#[doc(hidden)]
4930
#[macro_export]
@@ -73,8 +54,7 @@ macro_rules! plugin_execute_pre_main {
7354
__inner_init
7455
};
7556

76-
#[cfg(target_family = "wasm")]
77-
$crate::gensym! { $crate::plugin_execute_pre_main_wasm!() }
57+
$crate::wasm_declare_init_fn!();
7858
};
7959
};
8060
}

godot-macros/Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ homepage = "https://godot-rust.github.io"
1212

1313
[features]
1414
api-custom = ["godot-bindings/api-custom"]
15-
register-docs = ["dep:markdown", "dep:litrs"]
1615
codegen-full = ["godot/__codegen-full"]
16+
experimental-wasm = ["dep:libc"]
17+
register-docs = ["dep:markdown", "dep:litrs"]
1718

1819
[lib]
1920
proc-macro = true
@@ -25,9 +26,12 @@ quote = "1.0.29"
2526
# Enabled by `docs`.
2627
markdown = { version = "=1.0.0-alpha.21", optional = true }
2728
litrs = { version = "0.4.1", optional = true }
28-
# Requires bugfixes from 0.6.1.
2929
venial = "0.6.1"
3030

31+
# Cannot use [target.'cfg(target_family = "wasm")'.dependencies], as proc-macro crates are always compiled for host platform, not target.
32+
# Thus solved via feature.
33+
libc = { version = "0.2.171", optional = true }
34+
3135
[build-dependencies]
3236
godot-bindings = { path = "../godot-bindings", version = "=0.2.4" } # emit_godot_version_cfg
3337

godot-macros/src/ffi_macros.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) godot-rust; Bromeon and contributors.
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
//! Macro implementations used by `godot-ffi` crate.
9+
10+
#![cfg(feature = "experimental-wasm")]
11+
12+
use crate::util::bail;
13+
use crate::ParseResult;
14+
use proc_macro2::TokenStream;
15+
use quote::{format_ident, quote};
16+
17+
pub(super) fn wasm_declare_init_fn(input: TokenStream) -> ParseResult<TokenStream> {
18+
if !input.is_empty() {
19+
return bail!(input, "macro expects no arguments");
20+
}
21+
22+
// Create sufficiently unique identifier without entire `uuid` (let alone `rand`) crate dependency.
23+
let a = unsafe { libc::rand() };
24+
let b = unsafe { libc::rand() };
25+
26+
// Rust presently requires that statics with a custom `#[link_section]` must be a simple
27+
// list of bytes on the Wasm target (with no extra levels of indirection such as references).
28+
//
29+
// As such, instead we export a function with a random name of known prefix to be used by the embedder.
30+
// This prefix is queried at load time, see godot-macros/src/gdextension.rs.
31+
let function_name = format_ident!("__godot_rust_registrant_{a}_{b}");
32+
33+
let code = quote! {
34+
#[cfg(target_family = "wasm")] // Strictly speaking not necessary, as this macro is only invoked for Wasm.
35+
#[no_mangle]
36+
extern "C" fn #function_name() {
37+
__init();
38+
}
39+
};
40+
41+
Ok(code)
42+
}

godot-macros/src/gdextension.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ pub fn attribute_gdextension(item: venial::Item) -> ParseResult<TokenStream> {
8787
console.log(`Patching Module with ${sym}`);
8888
Module[sym] = dso_exports[sym];
8989
}
90-
} else if (sym.startsWith("rust_gdext_registrant_")) {
90+
} else if (sym.startsWith("__godot_rust_registrant_")) {
9191
registrants.push(sym);
9292
}
9393
}

godot-macros/src/lib.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod class;
1515
mod derive;
1616
#[cfg(all(feature = "register-docs", since_api = "4.3"))]
1717
mod docs;
18+
mod ffi_macros;
1819
mod gdextension;
1920
mod itest;
2021
mod util;
@@ -774,9 +775,6 @@ pub fn derive_godot_class(input: TokenStream) -> TokenStream {
774775
/// method, you can access all declared signals in `self.signals().some_signal()` or `gd.signals().some_signal()`. The returned object is
775776
/// of type [`TypedSignal`], which provides further APIs for emitting and connecting, among others.
776777
///
777-
/// Visibility of signals **must not exceed class visibility**. If your class is private (as above) and you declare your signal as `pub fn`,
778-
/// you will get a compile error "can't leak private type".
779-
///
780778
/// A detailed explanation with examples is available in the [book chapter _Registering signals_](https://godot-rust.github.io/book/register/signals.html).
781779
///
782780
/// [`WithSignals`]: ../obj/trait.WithSignals.html
@@ -1051,9 +1049,21 @@ pub fn gdextension(meta: TokenStream, input: TokenStream) -> TokenStream {
10511049
}
10521050

10531051
// ----------------------------------------------------------------------------------------------------------------------------------------------
1052+
// Used by godot-ffi
1053+
1054+
/// Creates an initialization block for Wasm.
1055+
#[proc_macro]
1056+
#[cfg(feature = "experimental-wasm")]
1057+
pub fn wasm_declare_init_fn(input: TokenStream) -> TokenStream {
1058+
translate_functional(input, ffi_macros::wasm_declare_init_fn)
1059+
}
1060+
1061+
// ----------------------------------------------------------------------------------------------------------------------------------------------
1062+
// Implementation
10541063

10551064
type ParseResult<T> = Result<T, venial::Error>;
10561065

1066+
/// For `#[derive(...)]` derive macros.
10571067
fn translate<F>(input: TokenStream, transform: F) -> TokenStream
10581068
where
10591069
F: FnOnce(venial::Item) -> ParseResult<TokenStream2>,
@@ -1067,6 +1077,7 @@ where
10671077
TokenStream::from(result2)
10681078
}
10691079

1080+
/// For `#[proc_macro_attribute]` procedural macros.
10701081
fn translate_meta<F>(
10711082
self_name: &str,
10721083
meta: TokenStream,
@@ -1087,6 +1098,18 @@ where
10871098
TokenStream::from(result2)
10881099
}
10891100

1101+
/// For `#[proc_macro]` function-style macros.
1102+
#[cfg(feature = "experimental-wasm")]
1103+
fn translate_functional<F>(input: TokenStream, transform: F) -> TokenStream
1104+
where
1105+
F: FnOnce(TokenStream2) -> ParseResult<TokenStream2>,
1106+
{
1107+
let input2 = TokenStream2::from(input);
1108+
let result2 = transform(input2).unwrap_or_else(|e| e.to_compile_error());
1109+
1110+
TokenStream::from(result2)
1111+
}
1112+
10901113
/// Returns the index of the key in `keys` (if any) that is present.
10911114
fn handle_mutually_exclusive_keys(
10921115
parser: &mut KvParser,

godot/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,18 @@
104104
//! Support for WebAssembly exports is still a work-in-progress and is not yet well tested. This feature is in place for users
105105
//! to explicitly opt in to any instabilities or rough edges that may result.
106106
//!
107-
//! By default, Wasm threads are enabled and require the flag `"-C", "link-args=-sUSE_PTHREADS=1"` in the `wasm32-unknown-unknown` target.
107+
//! Please read [Export to Web](https://godot-rust.github.io/book/toolchain/export-web.html) in the book.
108+
//!
109+
//! By default, Wasm threads are enabled and require the flag `"-C", "link-args=-pthread"` in the `wasm32-unknown-unknown` target.
108110
//! This must be kept in sync with Godot's Web export settings (threading support enabled). To disable it, use **additionally* the feature
109111
//! `experimental-wasm-nothreads`.<br><br>
110112
//!
113+
//! It is recommended to use this feature in combination with `lazy-function-tables` to reduce the size of the generated Wasm binary.
114+
//!
111115
//! * **`experimental-wasm-nothreads`**
112116
//!
113117
//! Requires the `experimental-wasm` feature. Disables threading support for WebAssembly exports. This needs to be kept in sync with
114-
//! Godot's Web export setting (threading support disabled), and must _not_ use the `"-C", "link-args=-sUSE_PTHREADS=1"` flag in the
118+
//! Godot's Web export setting (threading support disabled), and must _not_ use the `"-C", "link-args=-pthread"` flag in the
115119
//! `wasm32-unknown-unknown` target.<br><br>
116120
//!
117121
//! * **`codegen-rustfmt`**

0 commit comments

Comments
 (0)