Skip to content

Commit

Permalink
Consider inheritance for virtual method hashes
Browse files Browse the repository at this point in the history
  • Loading branch information
Bromeon committed Dec 26, 2024
1 parent 52edfe0 commit 9fa6c09
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 34 deletions.
5 changes: 5 additions & 0 deletions godot-codegen/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,11 @@ impl InheritanceTree {
assert!(existing.is_none(), "Duplicate inheritance insert");
}

#[allow(unused)] // Currently 4.4 gated, for virtual method hashes.
pub fn direct_base(&self, derived_name: &TyName) -> Option<TyName> {
self.derived_to_base.get(derived_name).cloned()
}

/// Returns all base classes, without the class itself, in order from nearest to furthest (object).
pub fn collect_all_bases(&self, derived_name: &TyName) -> Vec<TyName> {
let mut upgoer = derived_name;
Expand Down
2 changes: 1 addition & 1 deletion godot-codegen/src/generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ pub fn generate_sys_classes_file(
// This allows Godot to fall back to an older compatibility function if one is not supported.
#[cfg(since_api = "4.4")]
{
let code = virtual_hashes::make_virtual_hashes_file(api);
let code = virtual_hashes::make_virtual_hashes_file(api, ctx);
submit_fn(sys_gen_path.join("virtual_hashes.rs"), code);
watch.record("generate_virtual_hashes_file");
}
Expand Down
61 changes: 32 additions & 29 deletions godot-codegen/src/generator/virtual_hashes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,56 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use crate::context::Context;
use crate::models::domain::{Class, ClassLike, ExtensionApi, FnDirection, Function};
use proc_macro2::TokenStream;
use quote::quote;

pub fn make_virtual_hashes_file(api: &ExtensionApi) -> TokenStream {
make_virtual_hashes_for_all_classes(&api.classes)
pub fn make_virtual_hashes_file(api: &ExtensionApi, ctx: &mut Context) -> TokenStream {
make_virtual_hashes_for_all_classes(&api.classes, ctx)
}

fn make_virtual_hashes_for_all_classes(all_classes: &[Class]) -> TokenStream {
fn make_virtual_hashes_for_all_classes(all_classes: &[Class], ctx: &mut Context) -> TokenStream {
let modules = all_classes
.iter()
.map(|class| make_virtual_hashes_for_class(class));
.map(|class| make_virtual_hashes_for_class(class, ctx));

quote! {
#![allow(non_snake_case, non_upper_case_globals)]
#![allow(non_snake_case, non_upper_case_globals, unused_imports)]

#( #modules )*
}
}

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

let constants: Vec<TokenStream> = class
.methods
.iter()
.filter_map(|method| {
let FnDirection::Virtual { hash } = method.direction() else {
return None;
};

let method_name = method.name_ident();
let constant = quote! {
pub const #method_name: u32 = #hash;
};

Some(constant)
})
.collect();

// Don't generate mod SomeClass {} without contents.
if constants.is_empty() {
return TokenStream::new();
}
// Import all base class hashes via `use` statements.
let use_base_class = if let Some(base_class) = ctx.inheritance_tree().direct_base(class_name) {
quote! {
pub use super::#base_class::*;
}
} else {
TokenStream::new()
};

let constants = class.methods.iter().filter_map(|method| {
let FnDirection::Virtual { hash } = method.direction() else {
return None;
};

let method_name = method.name_ident();
let constant = quote! {
pub const #method_name: u32 = #hash;
};

Some(constant)
});

// Even if there are no virtual methods, we need to generate the module, to enable base class imports via `use`.
quote! {
pub mod #class_rust_name {
pub mod #class_name {
#use_base_class
#( #constants )*
}
}
Expand Down
12 changes: 8 additions & 4 deletions godot-macros/src/class/data_models/interface_trait_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ pub fn transform_trait_impl(original_impl: venial::Impl) -> ParseResult<TokenStr
// with known_virtual_hashes module could be changed to something like the following (GodotBase = nearest Godot base class):
// __get_virtual_hash::<Self::GodotBase>("method")
Some(quote! {
if hash == ::godot::sys::known_virtual_hashes::#trait_base_class::#method_name_ident
if hash == hashes::#method_name_ident
})
} else {
None
Expand Down Expand Up @@ -370,10 +370,13 @@ pub fn transform_trait_impl(original_impl: venial::Impl) -> ParseResult<TokenStr
let property_can_revert_fn = convert_to_match_expression_or_none(property_can_revert_fn);

// See also __default_virtual_call() codegen.
let hash_param = if cfg!(since_api = "4.4") {
quote! { hash: u32, }
let (hash_param, hashes_use);
if cfg!(since_api = "4.4") {
hash_param = quote! { hash: u32, };
hashes_use = quote! { use ::godot::sys::known_virtual_hashes::#trait_base_class as hashes; }
} else {
TokenStream::new()
hash_param = TokenStream::new();
hashes_use = TokenStream::new();
};

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

#hashes_use
match name {
#( #virtual_match_arms )*
_ => None,
Expand Down

0 comments on commit 9fa6c09

Please sign in to comment.