Skip to content

Commit 214f508

Browse files
authored
Merge pull request #408 from godot-rust/qol/indexed-method-tables
Array-based function pointer tables
2 parents b51b005 + 960dae6 commit 214f508

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)