Skip to content

Commit 9fa6c09

Browse files
committed
Consider inheritance for virtual method hashes
1 parent 52edfe0 commit 9fa6c09

File tree

4 files changed

+46
-34
lines changed

4 files changed

+46
-34
lines changed

godot-codegen/src/context.rs

+5
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,11 @@ impl InheritanceTree {
347347
assert!(existing.is_none(), "Duplicate inheritance insert");
348348
}
349349

350+
#[allow(unused)] // Currently 4.4 gated, for virtual method hashes.
351+
pub fn direct_base(&self, derived_name: &TyName) -> Option<TyName> {
352+
self.derived_to_base.get(derived_name).cloned()
353+
}
354+
350355
/// Returns all base classes, without the class itself, in order from nearest to furthest (object).
351356
pub fn collect_all_bases(&self, derived_name: &TyName) -> Vec<TyName> {
352357
let mut upgoer = derived_name;

godot-codegen/src/generator/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ pub fn generate_sys_classes_file(
8787
// This allows Godot to fall back to an older compatibility function if one is not supported.
8888
#[cfg(since_api = "4.4")]
8989
{
90-
let code = virtual_hashes::make_virtual_hashes_file(api);
90+
let code = virtual_hashes::make_virtual_hashes_file(api, ctx);
9191
submit_fn(sys_gen_path.join("virtual_hashes.rs"), code);
9292
watch.record("generate_virtual_hashes_file");
9393
}

godot-codegen/src/generator/virtual_hashes.rs

+32-29
Original file line numberDiff line numberDiff line change
@@ -5,53 +5,56 @@
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
77

8+
use crate::context::Context;
89
use crate::models::domain::{Class, ClassLike, ExtensionApi, FnDirection, Function};
910
use proc_macro2::TokenStream;
1011
use quote::quote;
1112

12-
pub fn make_virtual_hashes_file(api: &ExtensionApi) -> TokenStream {
13-
make_virtual_hashes_for_all_classes(&api.classes)
13+
pub fn make_virtual_hashes_file(api: &ExtensionApi, ctx: &mut Context) -> TokenStream {
14+
make_virtual_hashes_for_all_classes(&api.classes, ctx)
1415
}
1516

16-
fn make_virtual_hashes_for_all_classes(all_classes: &[Class]) -> TokenStream {
17+
fn make_virtual_hashes_for_all_classes(all_classes: &[Class], ctx: &mut Context) -> TokenStream {
1718
let modules = all_classes
1819
.iter()
19-
.map(|class| make_virtual_hashes_for_class(class));
20+
.map(|class| make_virtual_hashes_for_class(class, ctx));
2021

2122
quote! {
22-
#![allow(non_snake_case, non_upper_case_globals)]
23+
#![allow(non_snake_case, non_upper_case_globals, unused_imports)]
2324

2425
#( #modules )*
2526
}
2627
}
2728

28-
fn make_virtual_hashes_for_class(class: &Class) -> TokenStream {
29-
let class_rust_name = &class.name().rust_ty;
29+
fn make_virtual_hashes_for_class(class: &Class, ctx: &mut Context) -> TokenStream {
30+
let class_name = class.name();
3031

31-
let constants: Vec<TokenStream> = class
32-
.methods
33-
.iter()
34-
.filter_map(|method| {
35-
let FnDirection::Virtual { hash } = method.direction() else {
36-
return None;
37-
};
38-
39-
let method_name = method.name_ident();
40-
let constant = quote! {
41-
pub const #method_name: u32 = #hash;
42-
};
43-
44-
Some(constant)
45-
})
46-
.collect();
47-
48-
// Don't generate mod SomeClass {} without contents.
49-
if constants.is_empty() {
50-
return TokenStream::new();
51-
}
32+
// Import all base class hashes via `use` statements.
33+
let use_base_class = if let Some(base_class) = ctx.inheritance_tree().direct_base(class_name) {
34+
quote! {
35+
pub use super::#base_class::*;
36+
}
37+
} else {
38+
TokenStream::new()
39+
};
40+
41+
let constants = class.methods.iter().filter_map(|method| {
42+
let FnDirection::Virtual { hash } = method.direction() else {
43+
return None;
44+
};
45+
46+
let method_name = method.name_ident();
47+
let constant = quote! {
48+
pub const #method_name: u32 = #hash;
49+
};
50+
51+
Some(constant)
52+
});
5253

54+
// Even if there are no virtual methods, we need to generate the module, to enable base class imports via `use`.
5355
quote! {
54-
pub mod #class_rust_name {
56+
pub mod #class_name {
57+
#use_base_class
5558
#( #constants )*
5659
}
5760
}

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

+8-4
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ pub fn transform_trait_impl(original_impl: venial::Impl) -> ParseResult<TokenStr
315315
// with known_virtual_hashes module could be changed to something like the following (GodotBase = nearest Godot base class):
316316
// __get_virtual_hash::<Self::GodotBase>("method")
317317
Some(quote! {
318-
if hash == ::godot::sys::known_virtual_hashes::#trait_base_class::#method_name_ident
318+
if hash == hashes::#method_name_ident
319319
})
320320
} else {
321321
None
@@ -370,10 +370,13 @@ pub fn transform_trait_impl(original_impl: venial::Impl) -> ParseResult<TokenStr
370370
let property_can_revert_fn = convert_to_match_expression_or_none(property_can_revert_fn);
371371

372372
// See also __default_virtual_call() codegen.
373-
let hash_param = if cfg!(since_api = "4.4") {
374-
quote! { hash: u32, }
373+
let (hash_param, hashes_use);
374+
if cfg!(since_api = "4.4") {
375+
hash_param = quote! { hash: u32, };
376+
hashes_use = quote! { use ::godot::sys::known_virtual_hashes::#trait_base_class as hashes; }
375377
} else {
376-
TokenStream::new()
378+
hash_param = TokenStream::new();
379+
hashes_use = TokenStream::new();
377380
};
378381

379382
let virtual_match_arms = overridden_virtuals
@@ -399,6 +402,7 @@ pub fn transform_trait_impl(original_impl: venial::Impl) -> ParseResult<TokenStr
399402
use ::godot::obj::UserClass as _;
400403
#tool_check
401404

405+
#hashes_use
402406
match name {
403407
#( #virtual_match_arms )*
404408
_ => None,

0 commit comments

Comments
 (0)