Skip to content

Commit 48f6e2d

Browse files
committed
Split function table initializations into one method per class
Reduces the number of local variables and expressions needed, which seems to cause problems for certain compilations (WASM or release mode in general), taking a long time and using a lot of RAM.
1 parent 8361797 commit 48f6e2d

File tree

1 file changed

+122
-44
lines changed

1 file changed

+122
-44
lines changed

godot-codegen/src/central_generator.rs

Lines changed: 122 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ struct IndexedMethodTable {
4646
ctor_parameters: TokenStream,
4747
pre_init_code: TokenStream,
4848
fptr_type: TokenStream,
49-
method_inits: Vec<MethodInit>,
49+
fetch_fptr_type: TokenStream,
50+
method_init_groups: Vec<MethodInitGroup>,
5051
lazy_key_type: TokenStream,
5152
lazy_method_init: TokenStream,
5253
named_accessors: Vec<AccessorMethod>,
@@ -70,6 +71,38 @@ impl ToTokens for MethodInit {
7071

7172
// ----------------------------------------------------------------------------------------------------------------------------------------------
7273

74+
#[cfg_attr(feature = "codegen-lazy-fptrs", allow(dead_code))]
75+
struct MethodInitGroup {
76+
class_name: Ident,
77+
class_var_init: Option<TokenStream>,
78+
method_inits: Vec<MethodInit>,
79+
}
80+
81+
impl MethodInitGroup {
82+
fn new(class_godot_name: &str, class_var: Ident, method_inits: Vec<MethodInit>) -> Self {
83+
Self {
84+
class_name: ident(class_godot_name),
85+
// Only create class variable if any methods have been added.
86+
class_var_init: if method_inits.is_empty() {
87+
None
88+
} else {
89+
let initializer_expr = util::make_sname_ptr(class_godot_name);
90+
Some(quote! {
91+
let #class_var = #initializer_expr;
92+
})
93+
},
94+
method_inits,
95+
}
96+
}
97+
98+
#[cfg(not(feature = "codegen-lazy-fptrs"))]
99+
fn function_name(&self) -> Ident {
100+
format_ident!("load_{}_methods", self.class_name)
101+
}
102+
}
103+
104+
// ----------------------------------------------------------------------------------------------------------------------------------------------
105+
73106
struct AccessorMethod {
74107
name: Ident,
75108
index: usize,
@@ -273,7 +306,8 @@ fn make_method_table(info: IndexedMethodTable) -> TokenStream {
273306
ctor_parameters,
274307
pre_init_code,
275308
fptr_type,
276-
mut method_inits,
309+
fetch_fptr_type,
310+
method_init_groups,
277311
lazy_key_type: _,
278312
lazy_method_init: _,
279313
named_accessors,
@@ -287,27 +321,59 @@ fn make_method_table(info: IndexedMethodTable) -> TokenStream {
287321

288322
// Make sure methods are complete and in order of index.
289323
assert_eq!(
290-
method_inits.len(),
324+
method_init_groups
325+
.iter()
326+
.map(|group| group.method_inits.len())
327+
.sum::<usize>(),
291328
method_count,
292329
"number of methods does not match count"
293330
);
294-
method_inits.sort_by_key(|init| init.index);
331+
// method_inits.sort_by_key(|init| init.index);
295332

296-
if let Some(last) = method_inits.last() {
333+
if let Some(last) = method_init_groups.last() {
297334
assert_eq!(
298-
last.index,
335+
last.method_inits.last().unwrap().index,
299336
method_count - 1,
300337
"last method should have highest index"
301338
);
302339
} else {
303340
assert_eq!(method_count, 0, "empty method table should have count 0");
304341
}
305342

343+
let method_load_inits = method_init_groups.iter().map(|group| {
344+
let func = group.function_name();
345+
quote! {
346+
#func(&mut function_pointers, string_names, fetch_fptr);
347+
}
348+
});
349+
350+
let method_load_decls = method_init_groups.iter().map(|group| {
351+
let func = group.function_name();
352+
let method_inits = &group.method_inits;
353+
let class_var_init = &group.class_var_init;
354+
355+
quote! {
356+
fn #func(
357+
function_pointers: &mut Vec<#fptr_type>,
358+
string_names: &mut crate::StringCache,
359+
fetch_fptr: FetchFn,
360+
) {
361+
#class_var_init
362+
363+
#(
364+
function_pointers.push(#method_inits);
365+
)*
366+
}
367+
}
368+
});
369+
306370
// Assumes that inits already have a trailing comma.
307371
// This is necessary because some generators emit multiple lines (statements) per element.
308372
quote! {
309373
#imports
310374

375+
type FetchFn = <#fetch_fptr_type as crate::Inner>::FnPtr;
376+
311377
pub struct #table_name {
312378
function_pointers: Vec<#fptr_type>,
313379
}
@@ -322,11 +388,10 @@ fn make_method_table(info: IndexedMethodTable) -> TokenStream {
322388
) -> Self {
323389
#pre_init_code
324390

325-
Self {
326-
function_pointers: vec![
327-
#( #method_inits )*
328-
]
329-
}
391+
let mut function_pointers = Vec::with_capacity(#method_count);
392+
#( #method_load_inits )*
393+
394+
Self { function_pointers }
330395
}
331396

332397
#[inline(always)]
@@ -339,6 +404,8 @@ fn make_method_table(info: IndexedMethodTable) -> TokenStream {
339404

340405
#named_method_api
341406
}
407+
408+
#( #method_load_decls )*
342409
}
343410
}
344411

@@ -350,7 +417,8 @@ fn make_method_table(info: IndexedMethodTable) -> TokenStream {
350417
ctor_parameters: _,
351418
pre_init_code: _,
352419
fptr_type,
353-
method_inits: _,
420+
fetch_fptr_type: _,
421+
method_init_groups: _,
354422
lazy_key_type,
355423
lazy_method_init,
356424
named_accessors,
@@ -819,7 +887,8 @@ fn make_class_method_table(
819887
},
820888
pre_init_code: TokenStream::new(), // late-init, depends on class string names
821889
fptr_type: quote! { crate::ClassMethodBind },
822-
method_inits: vec![],
890+
fetch_fptr_type: quote! { crate::GDExtensionInterfaceClassdbGetMethodBind },
891+
method_init_groups: vec![],
823892
lazy_key_type: quote! { crate::lazy_keys::ClassMethodKey },
824893
lazy_method_init: quote! {
825894
let get_method_bind = crate::interface_fn!(classdb_get_method_bind);
@@ -837,7 +906,6 @@ fn make_class_method_table(
837906
method_count: 0,
838907
};
839908

840-
let mut class_sname_decls = Vec::new();
841909
for class in api.classes.iter() {
842910
let class_ty = TyName::from_godot(&class.name);
843911
if special_cases::is_class_deleted(&class_ty)
@@ -847,25 +915,11 @@ fn make_class_method_table(
847915
continue;
848916
}
849917

850-
let class_var = format_ident!("sname_{}", &class.name);
851-
let initializer_expr = util::make_sname_ptr(&class.name);
852-
853-
let prev_method_count = table.method_count;
854-
populate_class_methods(&mut table, class, &class_ty, &class_var, ctx);
855-
if table.method_count > prev_method_count {
856-
// Only create class variable if any methods have been added.
857-
class_sname_decls.push(quote! {
858-
let #class_var = #initializer_expr;
859-
});
860-
}
861-
862-
table.class_count += 1;
918+
populate_class_methods(&mut table, class, &class_ty, ctx);
863919
}
864920

865921
table.pre_init_code = quote! {
866-
let get_method_bind = interface.classdb_get_method_bind.expect("classdb_get_method_bind absent");
867-
868-
#( #class_sname_decls )*
922+
let fetch_fptr = interface.classdb_get_method_bind.expect("classdb_get_method_bind absent");
869923
};
870924

871925
make_method_table(table)
@@ -916,16 +970,16 @@ fn make_builtin_method_table(
916970
string_names: &mut crate::StringCache,
917971
},
918972
pre_init_code: quote! {
919-
use crate as sys;
920-
let get_builtin_method = interface.variant_get_ptr_builtin_method.expect("variant_get_ptr_builtin_method absent");
973+
let fetch_fptr = interface.variant_get_ptr_builtin_method.expect("variant_get_ptr_builtin_method absent");
921974
},
922975
fptr_type: quote! { crate::BuiltinMethodBind },
923-
method_inits: vec![],
976+
fetch_fptr_type: quote! { crate::GDExtensionInterfaceVariantGetPtrBuiltinMethod },
977+
method_init_groups: vec![],
924978
lazy_key_type: quote! { crate::lazy_keys::BuiltinMethodKey },
925979
lazy_method_init: quote! {
926-
let get_builtin_method = crate::interface_fn!(variant_get_ptr_builtin_method);
980+
let fetch_fptr = crate::interface_fn!(variant_get_ptr_builtin_method);
927981
crate::load_builtin_method(
928-
get_builtin_method,
982+
fetch_fptr,
929983
&mut inner.string_cache,
930984
key.variant_type.sys(),
931985
key.variant_type_str,
@@ -945,7 +999,6 @@ fn make_builtin_method_table(
945999
};
9461000

9471001
populate_builtin_methods(&mut table, builtin, &builtin_type.type_names, ctx);
948-
table.class_count += 1;
9491002
}
9501003

9511004
make_method_table(table)
@@ -955,9 +1008,11 @@ fn populate_class_methods(
9551008
table: &mut IndexedMethodTable,
9561009
class: &Class,
9571010
class_ty: &TyName,
958-
class_var: &Ident,
9591011
ctx: &mut Context,
9601012
) {
1013+
let class_var = format_ident!("sname_{}", &class.name);
1014+
let mut method_inits = vec![];
1015+
9611016
for method in option_as_slice(&class.methods) {
9621017
if special_cases::is_deleted(class_ty, method, ctx) {
9631018
continue;
@@ -969,9 +1024,9 @@ fn populate_class_methods(
9691024
class_ty: class_ty.clone(),
9701025
method_name: method.name.clone(),
9711026
});
972-
let method_init = make_class_method_init(method, class_var, class_ty);
9731027

974-
table.method_inits.push(MethodInit { method_init, index });
1028+
let method_init = make_class_method_init(method, &class_var, class_ty);
1029+
method_inits.push(MethodInit { method_init, index });
9751030
table.method_count += 1;
9761031

9771032
// If requested, add a named accessor for this method.
@@ -993,6 +1048,13 @@ fn populate_class_methods(
9931048
});
9941049
}
9951050
}
1051+
1052+
table.method_init_groups.push(MethodInitGroup::new(
1053+
&class_ty.godot_ty,
1054+
class_var,
1055+
method_inits,
1056+
));
1057+
table.class_count += 1;
9961058
}
9971059

9981060
fn populate_builtin_methods(
@@ -1001,6 +1063,9 @@ fn populate_builtin_methods(
10011063
builtin_name: &TypeNames,
10021064
ctx: &mut Context,
10031065
) {
1066+
let class_var = format_ident!("sname_{}", &builtin_class.name);
1067+
let mut method_inits = vec![];
1068+
10041069
for method in option_as_slice(&builtin_class.methods) {
10051070
let builtin_ty = TyName::from_godot(&builtin_class.name);
10061071
if special_cases::is_builtin_deleted(&builtin_ty, method) {
@@ -1013,8 +1078,7 @@ fn populate_builtin_methods(
10131078
});
10141079

10151080
let method_init = make_builtin_method_init(method, builtin_name, index);
1016-
1017-
table.method_inits.push(MethodInit { method_init, index });
1081+
method_inits.push(MethodInit { method_init, index });
10181082
table.method_count += 1;
10191083

10201084
// If requested, add a named accessor for this method.
@@ -1038,6 +1102,13 @@ fn populate_builtin_methods(
10381102
});
10391103
}
10401104
}
1105+
1106+
table.method_init_groups.push(MethodInitGroup::new(
1107+
&builtin_class.name,
1108+
class_var,
1109+
method_inits,
1110+
));
1111+
table.class_count += 1;
10411112
}
10421113

10431114
fn make_class_method_init(
@@ -1057,7 +1128,14 @@ fn make_class_method_init(
10571128

10581129
// Could reuse lazy key, but less code like this -> faster parsing.
10591130
quote! {
1060-
crate::load_class_method(get_method_bind, string_names, Some(#class_var), #class_name_str, #method_name_str, #hash),
1131+
crate::load_class_method(
1132+
fetch_fptr,
1133+
string_names,
1134+
Some(#class_var),
1135+
#class_name_str,
1136+
#method_name_str,
1137+
#hash
1138+
),
10611139
}
10621140
}
10631141

@@ -1083,9 +1161,9 @@ fn make_builtin_method_init(
10831161
{
10841162
let _ = #index;
10851163
crate::load_builtin_method(
1086-
get_builtin_method,
1164+
fetch_fptr,
10871165
string_names,
1088-
sys::#variant_type,
1166+
crate::#variant_type,
10891167
#variant_type_str,
10901168
#method_name_str,
10911169
#hash

0 commit comments

Comments
 (0)