Skip to content

Commit

Permalink
Class and builtin function tables now use giant arrays instead of nam…
Browse files Browse the repository at this point in the history
…ed fields

Should reduce the amount of generated code and thus compile time, while providing the same functionality.

This is not yet done for utility functions and builtin lifecycle methods, as those have a higher chance of being used via names,
and they are not that big in comparison.
  • Loading branch information
Bromeon committed Sep 11, 2023
1 parent b51b005 commit 1fc5a9b
Show file tree
Hide file tree
Showing 8 changed files with 568 additions and 196 deletions.
292 changes: 221 additions & 71 deletions godot-codegen/src/central_generator.rs

Large diffs are not rendered by default.

84 changes: 41 additions & 43 deletions godot-codegen/src/class_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ use quote::{format_ident, quote, ToTokens};
use std::path::Path;

use crate::api_parser::*;
use crate::central_generator::{collect_builtin_types, BuiltinTypeInfo};
use crate::central_generator::collect_builtin_types;
use crate::context::NotificationEnum;
use crate::util::{
ident, make_string_name, option_as_slice, parse_native_structures_format, safe_ident,
to_pascal_case, to_rust_expr, to_rust_type, to_rust_type_abi, to_snake_case,
NativeStructuresField,
to_pascal_case, to_rust_expr, to_rust_type, to_rust_type_abi, to_snake_case, ClassCodegenLevel,
MethodTableKey, NativeStructuresField,
};
use crate::{
codegen_special_cases, special_cases, util, Context, GeneratedBuiltin, GeneratedBuiltinModule,
Expand Down Expand Up @@ -280,27 +280,26 @@ pub(crate) fn generate_builtin_class_files(
let mut modules = vec![];
for class in api.builtin_classes.iter() {
let module_name = ModName::from_godot(&class.name);
let class_name = TyName::from_godot(&class.name);
let inner_class_name = TyName::from_godot(&format!("Inner{}", class.name));
let builtin_name = TyName::from_godot(&class.name);
let inner_builtin_name = TyName::from_godot(&format!("Inner{}", class.name));

if special_cases::is_builtin_type_deleted(&class_name) {
if special_cases::is_builtin_type_deleted(&builtin_name) {
continue;
}

let type_info = builtin_types_map
let _type_info = builtin_types_map
.get(&class.name)
.unwrap_or_else(|| panic!("builtin type not found: {}", class.name));

let generated_class =
make_builtin_class(class, &class_name, &inner_class_name, type_info, ctx);
let generated_class = make_builtin_class(class, &builtin_name, &inner_builtin_name, ctx);
let file_contents = generated_class.code;

let out_path = gen_path.join(format!("{}.rs", module_name.rust_mod));

submit_fn(out_path, file_contents);

modules.push(GeneratedBuiltinModule {
class_name: inner_class_name,
class_name: inner_builtin_name,
module_name,
});
}
Expand Down Expand Up @@ -494,17 +493,12 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
};

let constructor = make_constructor(class, ctx);
let get_method_table = util::get_api_level(class).table_global_getter();
let api_level = util::get_api_level(class);

let FnDefinitions {
functions: methods,
builders,
} = make_methods(
option_as_slice(&class.methods),
class_name,
&get_method_table,
ctx,
);
} = make_methods(option_as_slice(&class.methods), class_name, &api_level, ctx);

let enums = make_enums(option_as_slice(&class.enums), class_name, ctx);
let constants = make_constants(option_as_slice(&class.constants), class_name, ctx);
Expand Down Expand Up @@ -806,9 +800,8 @@ fn workaround_constant_collision(all_constants: &mut Vec<(Ident, i32)>) {

fn make_builtin_class(
class: &BuiltinClass,
class_name: &TyName,
builtin_name: &TyName,
inner_class_name: &TyName,
type_info: &BuiltinTypeInfo,
ctx: &mut Context,
) -> GeneratedBuiltin {
let outer_class = if let RustTy::BuiltinIdent(ident) = to_rust_type(&class.name, None, ctx) {
Expand All @@ -830,14 +823,13 @@ fn make_builtin_class(
builders,
} = make_builtin_methods(
option_as_slice(&class.methods),
class_name,
builtin_name,
inner_class_name,
type_info,
ctx,
);

let enums = make_enums(&class_enums, class_name, ctx);
let special_constructors = make_special_builtin_methods(class_name, ctx);
let enums = make_enums(&class_enums, builtin_name, ctx);
let special_constructors = make_special_builtin_methods(builtin_name, ctx);

// mod re_export needed, because class should not appear inside the file module, and we can't re-export private struct as pub
let code = quote! {
Expand Down Expand Up @@ -1017,26 +1009,27 @@ fn make_builtin_module_file(classes_and_modules: Vec<GeneratedBuiltinModule>) ->
fn make_methods(
methods: &[ClassMethod],
class_name: &TyName,
get_method_table: &Ident,
api_level: &ClassCodegenLevel,
ctx: &mut Context,
) -> FnDefinitions {
let definitions = methods
.iter()
.map(|method| make_method_definition(method, class_name, get_method_table, ctx));
let get_method_table = api_level.table_global_getter();

let definitions = methods.iter().map(|method| {
make_method_definition(method, class_name, api_level, &get_method_table, ctx)
});

FnDefinitions::expand(definitions)
}

fn make_builtin_methods(
methods: &[BuiltinClassMethod],
class_name: &TyName,
builtin_name: &TyName,
inner_class_name: &TyName,
type_info: &BuiltinTypeInfo,
ctx: &mut Context,
) -> FnDefinitions {
let definitions = methods.iter().map(|method| {
make_builtin_method_definition(method, class_name, inner_class_name, type_info, ctx)
});
let definitions = methods
.iter()
.map(|method| make_builtin_method_definition(method, builtin_name, inner_class_name, ctx));

FnDefinitions::expand(definitions)
}
Expand Down Expand Up @@ -1082,12 +1075,11 @@ fn make_special_builtin_methods(class_name: &TyName, _ctx: &Context) -> TokenStr
fn make_method_definition(
method: &ClassMethod,
class_name: &TyName,
api_level: &ClassCodegenLevel,
get_method_table: &Ident,
ctx: &mut Context,
) -> FnDefinition {
if codegen_special_cases::is_method_excluded(method, false, ctx)
|| special_cases::is_deleted(class_name, &method.name)
{
if special_cases::is_deleted(class_name, method, ctx) {
return FnDefinition::none();
}
/*if method.map_args(|args| args.is_empty()) {
Expand Down Expand Up @@ -1119,10 +1111,14 @@ fn make_method_definition(
quote! { sys::interface_fn!(object_method_bind_ptrcall) }
};

let fn_ptr = util::make_class_method_ptr_name(&class_name.godot_ty, method);
let table_index = ctx.get_table_index(&MethodTableKey::ClassMethod {
api_level: *api_level,
class_ty: class_name.clone(),
method_name: method.name.clone(),
});

let init_code = quote! {
let __method_bind = sys::#get_method_table().#fn_ptr;
let __method_bind = sys::#get_method_table().fptr_by_index(#table_index);
let __call_fn = #function_provider;
};

Expand Down Expand Up @@ -1156,12 +1152,11 @@ fn make_method_definition(

fn make_builtin_method_definition(
method: &BuiltinClassMethod,
class_name: &TyName,
builtin_name: &TyName,
inner_class_name: &TyName,
type_info: &BuiltinTypeInfo,
ctx: &mut Context,
) -> FnDefinition {
if codegen_special_cases::is_builtin_method_excluded(method) {
if special_cases::is_builtin_deleted(builtin_name, method) {
return FnDefinition::none();
}

Expand All @@ -1175,10 +1170,13 @@ fn make_builtin_method_definition(
let is_varcall = method.is_vararg;
let variant_ffi = is_varcall.then(VariantFfi::type_ptr);

let fn_ptr = util::make_builtin_method_ptr_name(&type_info.type_names, method);
let table_index = ctx.get_table_index(&MethodTableKey::BuiltinMethod {
builtin_ty: builtin_name.clone(),
method_name: method.name.clone(),
});

let init_code = quote! {
let __call_fn = sys::builtin_method_table().#fn_ptr;
let __call_fn = sys::builtin_method_table().fptr_by_index(#table_index);
};

let receiver = make_receiver(method.is_static, method.is_const, quote! { self.sys_ptr });
Expand All @@ -1191,7 +1189,7 @@ fn make_builtin_method_definition(
&FnSignature {
function_name: method_name_str,
surrounding_class: Some(inner_class_name),
is_private: special_cases::is_private(class_name, &method.name),
is_private: special_cases::is_private(builtin_name, &method.name),
is_virtual: false,
qualifier: FnQualifier::for_method(method.is_const, method.is_static),

Expand Down
152 changes: 122 additions & 30 deletions godot-codegen/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use crate::api_parser::Class;
use crate::{codegen_special_cases, util, ExtensionApi, GodotTy, RustTy, TyName};
use crate::api_parser::{BuiltinClass, BuiltinClassMethod, Class, ClassConstant, ClassMethod};
use crate::util::{option_as_slice, MethodTableKey};
use crate::{codegen_special_cases, special_cases, util, ExtensionApi, GodotTy, RustTy, TyName};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, ToTokens};
use std::collections::{HashMap, HashSet};
Expand All @@ -20,6 +21,8 @@ pub(crate) struct Context<'a> {
cached_rust_types: HashMap<GodotTy, RustTy>,
notifications_by_class: HashMap<TyName, Vec<(Ident, i32)>>,
notification_enum_names_by_class: HashMap<TyName, NotificationEnum>,
method_table_indices: HashMap<MethodTableKey, usize>,
method_table_next_index: HashMap<String, usize>,
}

impl<'a> Context<'a> {
Expand All @@ -34,6 +37,12 @@ impl<'a> Context<'a> {
for builtin in api.builtin_classes.iter() {
let ty_name = builtin.name.as_str();
ctx.builtin_types.insert(ty_name);

Self::populate_builtin_class_table_indices(
builtin,
option_as_slice(&builtin.methods),
&mut ctx,
);
}

for structure in api.native_structures.iter() {
Expand All @@ -60,31 +69,17 @@ impl<'a> Context<'a> {
}

// Populate notification constants (first, only for classes that declare them themselves).
if let Some(constants) = class.constants.as_ref() {
let mut has_notifications = false;

for constant in constants.iter() {
if let Some(rust_constant) = util::try_to_notification(constant) {
// First time
if !has_notifications {
ctx.notifications_by_class
.insert(class_name.clone(), Vec::new());

ctx.notification_enum_names_by_class.insert(
class_name.clone(),
NotificationEnum::for_own_class(&class_name),
);

has_notifications = true;
}

ctx.notifications_by_class
.get_mut(&class_name)
.expect("just inserted constants; must be present")
.push((rust_constant, constant.value));
}
}
}
Self::populate_notification_constants(
&class_name,
option_as_slice(&class.constants),
&mut ctx,
);
Self::populate_class_table_indices(
class,
&class_name,
option_as_slice(&class.methods),
&mut ctx,
);
}

// Populate remaining notification enum names, by copying the one to nearest base class that has at least 1 notification.
Expand Down Expand Up @@ -129,13 +124,110 @@ impl<'a> Context<'a> {
ctx
}

fn populate_notification_constants(
class_name: &TyName,
constants: &[ClassConstant],
ctx: &mut Context,
) {
let mut has_notifications = false;
for constant in constants.iter() {
if let Some(rust_constant) = util::try_to_notification(constant) {
// First time
if !has_notifications {
ctx.notifications_by_class
.insert(class_name.clone(), Vec::new());

ctx.notification_enum_names_by_class.insert(
class_name.clone(),
NotificationEnum::for_own_class(class_name),
);

has_notifications = true;
}

ctx.notifications_by_class
.get_mut(class_name)
.expect("just inserted constants; must be present")
.push((rust_constant, constant.value));
}
}
}

fn populate_class_table_indices(
class: &Class,
class_name: &TyName,
methods: &[ClassMethod],
ctx: &mut Context,
) {
if special_cases::is_class_deleted(class_name) {
return;
}

for method in methods.iter() {
if special_cases::is_deleted(class_name, method, ctx) {
continue;
}

let key = MethodTableKey::ClassMethod {
api_level: util::get_api_level(class),
class_ty: class_name.clone(),
method_name: method.name.clone(),
};

ctx.register_table_index(key);
}
}

fn populate_builtin_class_table_indices(
builtin: &BuiltinClass,
methods: &[BuiltinClassMethod],
ctx: &mut Context,
) {
let builtin_ty = TyName::from_godot(builtin.name.as_str());
if special_cases::is_builtin_type_deleted(&builtin_ty) {
return;
}

for method in methods.iter() {
if special_cases::is_builtin_deleted(&builtin_ty, method) {
continue;
}

let key = MethodTableKey::BuiltinMethod {
builtin_ty: builtin_ty.clone(),
method_name: method.name.clone(),
};

ctx.register_table_index(key);
}
}

pub fn get_engine_class(&self, class_name: &TyName) -> &Class {
self.engine_classes.get(class_name).unwrap()
}

// pub fn is_engine_class(&self, class_name: &str) -> bool {
// self.engine_classes.contains(class_name)
// }
// Private, because initialized in constructor. Ensures deterministic assignment.
fn register_table_index(&mut self, key: MethodTableKey) -> usize {
let key_category = key.category();

let next_index = self
.method_table_next_index
.entry(key_category)
.or_insert(0);

let prev = self.method_table_indices.insert(key, *next_index);
assert!(prev.is_none(), "table index already registered");

*next_index += 1;
*next_index
}

pub fn get_table_index(&self, key: &MethodTableKey) -> usize {
*self
.method_table_indices
.get(key)
.unwrap_or_else(|| panic!("did not register table index for key {:?}", key))
}

/// Checks if this is a builtin type (not `Object`).
///
Expand Down
Loading

0 comments on commit 1fc5a9b

Please sign in to comment.