Skip to content

Commit bb39acd

Browse files
authored
Merge pull request #1173 from godot-rust/bugfix/virtual-dispatch-renamed
Virtual dispatch: fix incorrect matching of renamed methods
2 parents 6e0f41c + 6861b60 commit bb39acd

File tree

5 files changed

+43
-55
lines changed

5 files changed

+43
-55
lines changed

godot-codegen/src/generator/mod.rs

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,9 @@ pub mod native_structures;
2727
pub mod notifications;
2828
pub mod signals;
2929
pub mod utility_functions;
30+
pub mod virtual_definition_consts;
3031
pub mod virtual_traits;
3132

32-
#[cfg(since_api = "4.4")]
33-
pub mod virtual_hashes;
34-
3533
// ----------------------------------------------------------------------------------------------------------------------------------------------
3634

3735
// Some file generation functions are in specific modules:
@@ -43,11 +41,6 @@ pub mod virtual_hashes;
4341
pub fn generate_sys_module_file(sys_gen_path: &Path, submit_fn: &mut SubmitFn) {
4442
// Don't delegate #[cfg] to generated code; causes issues in release CI, reproducible with:
4543
// cargo clippy --features godot/experimental-godot-api,godot/codegen-rustfmt,godot/serde
46-
let virtual_hashes_mod = if cfg!(since_api = "4.4") {
47-
quote! { pub mod virtual_hashes; }
48-
} else {
49-
quote! {}
50-
};
5144

5245
let code = quote! {
5346
pub mod table_builtins;
@@ -56,11 +49,11 @@ pub fn generate_sys_module_file(sys_gen_path: &Path, submit_fn: &mut SubmitFn) {
5649
pub mod table_scene_classes;
5750
pub mod table_editor_classes;
5851
pub mod table_utilities;
59-
#virtual_hashes_mod
6052

6153
pub mod central;
6254
pub mod gdextension_interface;
6355
pub mod interface;
56+
pub mod virtual_consts;
6457
};
6558

6659
submit_fn(sys_gen_path.join("mod.rs"), code);
@@ -93,12 +86,9 @@ pub fn generate_sys_classes_file(
9386

9487
// From 4.4 onward, generate table that maps all virtual methods to their known hashes.
9588
// This allows Godot to fall back to an older compatibility function if one is not supported.
96-
#[cfg(since_api = "4.4")]
97-
{
98-
let code = virtual_hashes::make_virtual_hashes_file(api, ctx);
99-
submit_fn(sys_gen_path.join("virtual_hashes.rs"), code);
100-
watch.record("generate_virtual_hashes_file");
101-
}
89+
let code = virtual_definition_consts::make_virtual_consts_file(api, ctx);
90+
submit_fn(sys_gen_path.join("virtual_consts.rs"), code);
91+
watch.record("generate_virtual_consts_file");
10292
}
10393

10494
pub fn generate_sys_utilities_file(

godot-codegen/src/generator/virtual_hashes.rs renamed to godot-codegen/src/generator/virtual_definition_consts.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::models::domain::{Class, ClassLike, ExtensionApi, FnDirection, Functio
1010
use proc_macro2::TokenStream;
1111
use quote::quote;
1212

13-
pub fn make_virtual_hashes_file(api: &ExtensionApi, ctx: &mut Context) -> TokenStream {
13+
pub fn make_virtual_consts_file(api: &ExtensionApi, ctx: &mut Context) -> TokenStream {
1414
make_virtual_hashes_for_all_classes(&api.classes, ctx)
1515
}
1616

@@ -39,13 +39,24 @@ fn make_virtual_hashes_for_class(class: &Class, ctx: &mut Context) -> TokenStrea
3939
};
4040

4141
let constants = class.methods.iter().filter_map(|method| {
42-
let FnDirection::Virtual { hash } = method.direction() else {
42+
let FnDirection::Virtual {
43+
#[cfg(since_api = "4.4")]
44+
hash,
45+
} = method.direction()
46+
else {
4347
return None;
4448
};
4549

46-
let method_name = method.name_ident();
50+
let rust_name = method.name_ident();
51+
let godot_name_str = method.godot_name();
52+
53+
#[cfg(since_api = "4.4")]
54+
let constant = quote! {
55+
pub const #rust_name: (&'static str, u32) = (#godot_name_str, #hash);
56+
};
57+
#[cfg(before_api = "4.4")]
4758
let constant = quote! {
48-
pub const #method_name: u32 = #hash;
59+
pub const #rust_name: &'static str = #godot_name_str;
4960
};
5061

5162
Some(constant)

godot-ffi/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ pub use gen::table_editor_classes::*;
8282
pub use gen::table_scene_classes::*;
8383
pub use gen::table_servers_classes::*;
8484
pub use gen::table_utilities::*;
85-
#[cfg(since_api = "4.4")]
86-
pub use gen::virtual_hashes as known_virtual_hashes;
85+
pub use gen::virtual_consts as godot_virtual_consts;
8786

8887
// Other
8988
pub use extras::*;

godot-macros/src/class/data_models/interface_trait_impl.rs

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,13 @@ pub fn transform_trait_impl(mut original_impl: venial::Impl) -> ParseResult<Toke
102102
&& !decls
103103
.overridden_virtuals
104104
.iter()
105-
.any(|v| v.method_name == "_ready")
105+
.any(|v| v.rust_method_name == "_ready")
106106
{
107107
let match_arm = OverriddenVirtualFn {
108108
cfg_attrs: vec![],
109-
method_name: "_ready".to_string(),
110-
// Can't use `hashes::ready` here, as the base class might not be `Node` (see above why such a branch is still added).
111-
#[cfg(since_api = "4.4")]
112-
hash_constant: quote! { ::godot::sys::known_virtual_hashes::Node::ready },
109+
rust_method_name: "_ready".to_string(),
110+
// Can't use `virtuals::ready` here, as the base class might not be `Node` (see above why such a branch is still added).
111+
godot_name_hash_constant: quote! { ::godot::sys::godot_virtual_consts::Node::ready },
113112
signature_info: SignatureInfo::fn_ready(),
114113
before_kind: BeforeKind::OnlyBefore,
115114
interface_trait: None,
@@ -136,16 +135,12 @@ pub fn transform_trait_impl(mut original_impl: venial::Impl) -> ParseResult<Toke
136135
};
137136

138137
// See also __default_virtual_call() codegen.
139-
let (hash_param, hashes_use, match_expr);
138+
let (hash_param, match_expr);
140139
if cfg!(since_api = "4.4") {
141140
hash_param = quote! { hash: u32, };
142-
hashes_use = quote! {
143-
use ::godot::sys::known_virtual_hashes::#trait_base_class as hashes;
144-
};
145141
match_expr = quote! { (name, hash) };
146142
} else {
147143
hash_param = TokenStream::new();
148-
hashes_use = TokenStream::new();
149144
match_expr = quote! { name };
150145
};
151146

@@ -164,9 +159,9 @@ pub fn transform_trait_impl(mut original_impl: venial::Impl) -> ParseResult<Toke
164159
fn __virtual_call(name: &str, #hash_param) -> ::godot::sys::GDExtensionClassCallVirtual {
165160
//println!("virtual_call: {}.{}", std::any::type_name::<Self>(), name);
166161
use ::godot::obj::UserClass as _;
162+
use ::godot::sys::godot_virtual_consts::#trait_base_class as virtuals;
167163
#tool_check
168164

169-
#hashes_use
170165
match #match_expr {
171166
#( #virtual_match_arms )*
172167
_ => None,
@@ -459,7 +454,6 @@ fn handle_regular_virtual_fn<'a>(
459454
cfg_attrs: Vec<&'a venial::Attribute>,
460455
decls: &mut IDecls<'a>,
461456
) -> Option<(venial::Punctuated<venial::FnParam>, Group)> {
462-
#[cfg(since_api = "4.4")]
463457
let method_name_ident = original_method.name.clone();
464458
let method = util::reduce_to_signature(original_method);
465459

@@ -518,12 +512,11 @@ fn handle_regular_virtual_fn<'a>(
518512
// each distinct method.
519513
decls.overridden_virtuals.push(OverriddenVirtualFn {
520514
cfg_attrs,
521-
method_name: virtual_method_name,
515+
rust_method_name: virtual_method_name,
522516
// If ever the `I*` verbatim validation is relaxed (it won't work with use-renames or other weird edge cases), the approach
523-
// with known_virtual_hashes module could be changed to something like the following (GodotBase = nearest Godot base class):
517+
// with godot_virtual_consts module could be changed to something like the following (GodotBase = nearest Godot base class):
524518
// __get_virtual_hash::<Self::GodotBase>("method")
525-
#[cfg(since_api = "4.4")]
526-
hash_constant: quote! { hashes::#method_name_ident },
519+
godot_name_hash_constant: quote! { virtuals::#method_name_ident },
527520
signature_info,
528521
before_kind,
529522
interface_trait: Some(trait_path.clone()),
@@ -573,9 +566,11 @@ fn make_inactive_class_check(_return_value: TokenStream) -> TokenStream {
573566

574567
struct OverriddenVirtualFn<'a> {
575568
cfg_attrs: Vec<&'a venial::Attribute>,
576-
method_name: String,
577-
#[cfg(since_api = "4.4")]
578-
hash_constant: TokenStream,
569+
rust_method_name: String,
570+
/// Path to a pre-defined constant storing a `("_virtual_func", 123456789)` tuple with name and hash of the virtual function.
571+
///
572+
/// Before Godot 4.4, this just stores the name `"_virtual_func"`.
573+
godot_name_hash_constant: TokenStream,
579574
signature_info: SignatureInfo,
580575
before_kind: BeforeKind,
581576
interface_trait: Option<venial::TypeExpr>,
@@ -584,16 +579,7 @@ struct OverriddenVirtualFn<'a> {
584579
impl OverriddenVirtualFn<'_> {
585580
fn make_match_arm(&self, class_name: &Ident) -> TokenStream {
586581
let cfg_attrs = self.cfg_attrs.iter();
587-
let method_name_str = self.method_name.as_str();
588-
589-
#[cfg(since_api = "4.4")]
590-
let pattern = {
591-
let hash_constant = &self.hash_constant;
592-
quote! { (#method_name_str, #hash_constant) }
593-
};
594-
595-
#[cfg(before_api = "4.4")]
596-
let pattern = method_name_str;
582+
let godot_name_hash_constant = &self.godot_name_hash_constant;
597583

598584
// Lazily generate code for the actual work (calling user function).
599585
let method_callback = make_virtual_callback(
@@ -605,7 +591,7 @@ impl OverriddenVirtualFn<'_> {
605591

606592
quote! {
607593
#(#cfg_attrs)*
608-
#pattern => #method_callback,
594+
#godot_name_hash_constant => #method_callback,
609595
}
610596
}
611597
}

godot-macros/src/class/derive_godot_class.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -400,13 +400,15 @@ fn make_user_class_impl(
400400
// See also __virtual_call() codegen.
401401
// This doesn't explicitly check if the base class inherits from Node (and thus has `_ready`), but the derive-macro already does
402402
// this for the `OnReady` field declaration.
403-
let (hash_param, hash_check);
403+
let (hash_param, matches_ready_hash);
404404
if cfg!(since_api = "4.4") {
405405
hash_param = quote! { hash: u32, };
406-
hash_check = quote! { && hash == ::godot::sys::known_virtual_hashes::Node::ready };
406+
matches_ready_hash = quote! {
407+
(name, hash) == ::godot::sys::godot_virtual_consts::Node::ready
408+
};
407409
} else {
408410
hash_param = TokenStream::new();
409-
hash_check = TokenStream::new();
411+
matches_ready_hash = quote! { name == "_ready" }
410412
}
411413

412414
let default_virtual_fn = quote! {
@@ -417,7 +419,7 @@ fn make_user_class_impl(
417419
use ::godot::obj::UserClass as _;
418420
#tool_check
419421

420-
if name == "_ready" #hash_check {
422+
if #matches_ready_hash {
421423
#callback
422424
} else {
423425
None

0 commit comments

Comments
 (0)