Skip to content

Commit 960dae6

Browse files
committed
Class and builtin function tables now use giant arrays instead of named 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.
1 parent b51b005 commit 960dae6

File tree

8 files changed

+568
-196
lines changed

8 files changed

+568
-196
lines changed

godot-codegen/src/central_generator.rs

Lines changed: 221 additions & 71 deletions
Large diffs are not rendered by default.

godot-codegen/src/class_generator.rs

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ use quote::{format_ident, quote, ToTokens};
1111
use std::path::Path;
1212

1313
use crate::api_parser::*;
14-
use crate::central_generator::{collect_builtin_types, BuiltinTypeInfo};
14+
use crate::central_generator::collect_builtin_types;
1515
use crate::context::NotificationEnum;
1616
use crate::util::{
1717
ident, make_string_name, option_as_slice, parse_native_structures_format, safe_ident,
18-
to_pascal_case, to_rust_expr, to_rust_type, to_rust_type_abi, to_snake_case,
19-
NativeStructuresField,
18+
to_pascal_case, to_rust_expr, to_rust_type, to_rust_type_abi, to_snake_case, ClassCodegenLevel,
19+
MethodTableKey, NativeStructuresField,
2020
};
2121
use crate::{
2222
codegen_special_cases, special_cases, util, Context, GeneratedBuiltin, GeneratedBuiltinModule,
@@ -280,27 +280,26 @@ pub(crate) fn generate_builtin_class_files(
280280
let mut modules = vec![];
281281
for class in api.builtin_classes.iter() {
282282
let module_name = ModName::from_godot(&class.name);
283-
let class_name = TyName::from_godot(&class.name);
284-
let inner_class_name = TyName::from_godot(&format!("Inner{}", class.name));
283+
let builtin_name = TyName::from_godot(&class.name);
284+
let inner_builtin_name = TyName::from_godot(&format!("Inner{}", class.name));
285285

286-
if special_cases::is_builtin_type_deleted(&class_name) {
286+
if special_cases::is_builtin_type_deleted(&builtin_name) {
287287
continue;
288288
}
289289

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

294-
let generated_class =
295-
make_builtin_class(class, &class_name, &inner_class_name, type_info, ctx);
294+
let generated_class = make_builtin_class(class, &builtin_name, &inner_builtin_name, ctx);
296295
let file_contents = generated_class.code;
297296

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

300299
submit_fn(out_path, file_contents);
301300

302301
modules.push(GeneratedBuiltinModule {
303-
class_name: inner_class_name,
302+
class_name: inner_builtin_name,
304303
module_name,
305304
});
306305
}
@@ -494,17 +493,12 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
494493
};
495494

496495
let constructor = make_constructor(class, ctx);
497-
let get_method_table = util::get_api_level(class).table_global_getter();
496+
let api_level = util::get_api_level(class);
498497

499498
let FnDefinitions {
500499
functions: methods,
501500
builders,
502-
} = make_methods(
503-
option_as_slice(&class.methods),
504-
class_name,
505-
&get_method_table,
506-
ctx,
507-
);
501+
} = make_methods(option_as_slice(&class.methods), class_name, &api_level, ctx);
508502

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

807801
fn make_builtin_class(
808802
class: &BuiltinClass,
809-
class_name: &TyName,
803+
builtin_name: &TyName,
810804
inner_class_name: &TyName,
811-
type_info: &BuiltinTypeInfo,
812805
ctx: &mut Context,
813806
) -> GeneratedBuiltin {
814807
let outer_class = if let RustTy::BuiltinIdent(ident) = to_rust_type(&class.name, None, ctx) {
@@ -830,14 +823,13 @@ fn make_builtin_class(
830823
builders,
831824
} = make_builtin_methods(
832825
option_as_slice(&class.methods),
833-
class_name,
826+
builtin_name,
834827
inner_class_name,
835-
type_info,
836828
ctx,
837829
);
838830

839-
let enums = make_enums(&class_enums, class_name, ctx);
840-
let special_constructors = make_special_builtin_methods(class_name, ctx);
831+
let enums = make_enums(&class_enums, builtin_name, ctx);
832+
let special_constructors = make_special_builtin_methods(builtin_name, ctx);
841833

842834
// mod re_export needed, because class should not appear inside the file module, and we can't re-export private struct as pub
843835
let code = quote! {
@@ -1017,26 +1009,27 @@ fn make_builtin_module_file(classes_and_modules: Vec<GeneratedBuiltinModule>) ->
10171009
fn make_methods(
10181010
methods: &[ClassMethod],
10191011
class_name: &TyName,
1020-
get_method_table: &Ident,
1012+
api_level: &ClassCodegenLevel,
10211013
ctx: &mut Context,
10221014
) -> FnDefinitions {
1023-
let definitions = methods
1024-
.iter()
1025-
.map(|method| make_method_definition(method, class_name, get_method_table, ctx));
1015+
let get_method_table = api_level.table_global_getter();
1016+
1017+
let definitions = methods.iter().map(|method| {
1018+
make_method_definition(method, class_name, api_level, &get_method_table, ctx)
1019+
});
10261020

10271021
FnDefinitions::expand(definitions)
10281022
}
10291023

10301024
fn make_builtin_methods(
10311025
methods: &[BuiltinClassMethod],
1032-
class_name: &TyName,
1026+
builtin_name: &TyName,
10331027
inner_class_name: &TyName,
1034-
type_info: &BuiltinTypeInfo,
10351028
ctx: &mut Context,
10361029
) -> FnDefinitions {
1037-
let definitions = methods.iter().map(|method| {
1038-
make_builtin_method_definition(method, class_name, inner_class_name, type_info, ctx)
1039-
});
1030+
let definitions = methods
1031+
.iter()
1032+
.map(|method| make_builtin_method_definition(method, builtin_name, inner_class_name, ctx));
10401033

10411034
FnDefinitions::expand(definitions)
10421035
}
@@ -1082,12 +1075,11 @@ fn make_special_builtin_methods(class_name: &TyName, _ctx: &Context) -> TokenStr
10821075
fn make_method_definition(
10831076
method: &ClassMethod,
10841077
class_name: &TyName,
1078+
api_level: &ClassCodegenLevel,
10851079
get_method_table: &Ident,
10861080
ctx: &mut Context,
10871081
) -> FnDefinition {
1088-
if codegen_special_cases::is_method_excluded(method, false, ctx)
1089-
|| special_cases::is_deleted(class_name, &method.name)
1090-
{
1082+
if special_cases::is_deleted(class_name, method, ctx) {
10911083
return FnDefinition::none();
10921084
}
10931085
/*if method.map_args(|args| args.is_empty()) {
@@ -1119,10 +1111,14 @@ fn make_method_definition(
11191111
quote! { sys::interface_fn!(object_method_bind_ptrcall) }
11201112
};
11211113

1122-
let fn_ptr = util::make_class_method_ptr_name(&class_name.godot_ty, method);
1114+
let table_index = ctx.get_table_index(&MethodTableKey::ClassMethod {
1115+
api_level: *api_level,
1116+
class_ty: class_name.clone(),
1117+
method_name: method.name.clone(),
1118+
});
11231119

11241120
let init_code = quote! {
1125-
let __method_bind = sys::#get_method_table().#fn_ptr;
1121+
let __method_bind = sys::#get_method_table().fptr_by_index(#table_index);
11261122
let __call_fn = #function_provider;
11271123
};
11281124

@@ -1156,12 +1152,11 @@ fn make_method_definition(
11561152

11571153
fn make_builtin_method_definition(
11581154
method: &BuiltinClassMethod,
1159-
class_name: &TyName,
1155+
builtin_name: &TyName,
11601156
inner_class_name: &TyName,
1161-
type_info: &BuiltinTypeInfo,
11621157
ctx: &mut Context,
11631158
) -> FnDefinition {
1164-
if codegen_special_cases::is_builtin_method_excluded(method) {
1159+
if special_cases::is_builtin_deleted(builtin_name, method) {
11651160
return FnDefinition::none();
11661161
}
11671162

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

1178-
let fn_ptr = util::make_builtin_method_ptr_name(&type_info.type_names, method);
1173+
let table_index = ctx.get_table_index(&MethodTableKey::BuiltinMethod {
1174+
builtin_ty: builtin_name.clone(),
1175+
method_name: method.name.clone(),
1176+
});
11791177

11801178
let init_code = quote! {
1181-
let __call_fn = sys::builtin_method_table().#fn_ptr;
1179+
let __call_fn = sys::builtin_method_table().fptr_by_index(#table_index);
11821180
};
11831181

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

godot-codegen/src/context.rs

Lines changed: 122 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

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

2528
impl<'a> Context<'a> {
@@ -34,6 +37,12 @@ impl<'a> Context<'a> {
3437
for builtin in api.builtin_classes.iter() {
3538
let ty_name = builtin.name.as_str();
3639
ctx.builtin_types.insert(ty_name);
40+
41+
Self::populate_builtin_class_table_indices(
42+
builtin,
43+
option_as_slice(&builtin.methods),
44+
&mut ctx,
45+
);
3746
}
3847

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

6271
// Populate notification constants (first, only for classes that declare them themselves).
63-
if let Some(constants) = class.constants.as_ref() {
64-
let mut has_notifications = false;
65-
66-
for constant in constants.iter() {
67-
if let Some(rust_constant) = util::try_to_notification(constant) {
68-
// First time
69-
if !has_notifications {
70-
ctx.notifications_by_class
71-
.insert(class_name.clone(), Vec::new());
72-
73-
ctx.notification_enum_names_by_class.insert(
74-
class_name.clone(),
75-
NotificationEnum::for_own_class(&class_name),
76-
);
77-
78-
has_notifications = true;
79-
}
80-
81-
ctx.notifications_by_class
82-
.get_mut(&class_name)
83-
.expect("just inserted constants; must be present")
84-
.push((rust_constant, constant.value));
85-
}
86-
}
87-
}
72+
Self::populate_notification_constants(
73+
&class_name,
74+
option_as_slice(&class.constants),
75+
&mut ctx,
76+
);
77+
Self::populate_class_table_indices(
78+
class,
79+
&class_name,
80+
option_as_slice(&class.methods),
81+
&mut ctx,
82+
);
8883
}
8984

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

127+
fn populate_notification_constants(
128+
class_name: &TyName,
129+
constants: &[ClassConstant],
130+
ctx: &mut Context,
131+
) {
132+
let mut has_notifications = false;
133+
for constant in constants.iter() {
134+
if let Some(rust_constant) = util::try_to_notification(constant) {
135+
// First time
136+
if !has_notifications {
137+
ctx.notifications_by_class
138+
.insert(class_name.clone(), Vec::new());
139+
140+
ctx.notification_enum_names_by_class.insert(
141+
class_name.clone(),
142+
NotificationEnum::for_own_class(class_name),
143+
);
144+
145+
has_notifications = true;
146+
}
147+
148+
ctx.notifications_by_class
149+
.get_mut(class_name)
150+
.expect("just inserted constants; must be present")
151+
.push((rust_constant, constant.value));
152+
}
153+
}
154+
}
155+
156+
fn populate_class_table_indices(
157+
class: &Class,
158+
class_name: &TyName,
159+
methods: &[ClassMethod],
160+
ctx: &mut Context,
161+
) {
162+
if special_cases::is_class_deleted(class_name) {
163+
return;
164+
}
165+
166+
for method in methods.iter() {
167+
if special_cases::is_deleted(class_name, method, ctx) {
168+
continue;
169+
}
170+
171+
let key = MethodTableKey::ClassMethod {
172+
api_level: util::get_api_level(class),
173+
class_ty: class_name.clone(),
174+
method_name: method.name.clone(),
175+
};
176+
177+
ctx.register_table_index(key);
178+
}
179+
}
180+
181+
fn populate_builtin_class_table_indices(
182+
builtin: &BuiltinClass,
183+
methods: &[BuiltinClassMethod],
184+
ctx: &mut Context,
185+
) {
186+
let builtin_ty = TyName::from_godot(builtin.name.as_str());
187+
if special_cases::is_builtin_type_deleted(&builtin_ty) {
188+
return;
189+
}
190+
191+
for method in methods.iter() {
192+
if special_cases::is_builtin_deleted(&builtin_ty, method) {
193+
continue;
194+
}
195+
196+
let key = MethodTableKey::BuiltinMethod {
197+
builtin_ty: builtin_ty.clone(),
198+
method_name: method.name.clone(),
199+
};
200+
201+
ctx.register_table_index(key);
202+
}
203+
}
204+
132205
pub fn get_engine_class(&self, class_name: &TyName) -> &Class {
133206
self.engine_classes.get(class_name).unwrap()
134207
}
135208

136-
// pub fn is_engine_class(&self, class_name: &str) -> bool {
137-
// self.engine_classes.contains(class_name)
138-
// }
209+
// Private, because initialized in constructor. Ensures deterministic assignment.
210+
fn register_table_index(&mut self, key: MethodTableKey) -> usize {
211+
let key_category = key.category();
212+
213+
let next_index = self
214+
.method_table_next_index
215+
.entry(key_category)
216+
.or_insert(0);
217+
218+
let prev = self.method_table_indices.insert(key, *next_index);
219+
assert!(prev.is_none(), "table index already registered");
220+
221+
*next_index += 1;
222+
*next_index
223+
}
224+
225+
pub fn get_table_index(&self, key: &MethodTableKey) -> usize {
226+
*self
227+
.method_table_indices
228+
.get(key)
229+
.unwrap_or_else(|| panic!("did not register table index for key {:?}", key))
230+
}
139231

140232
/// Checks if this is a builtin type (not `Object`).
141233
///

0 commit comments

Comments
 (0)