@@ -39,20 +39,24 @@ struct NamedMethodTable {
39
39
method_count : usize ,
40
40
}
41
41
42
+ #[ allow( dead_code) ] // for lazy feature
42
43
struct IndexedMethodTable {
43
44
table_name : Ident ,
44
45
imports : TokenStream ,
45
46
ctor_parameters : TokenStream ,
46
47
pre_init_code : TokenStream ,
47
48
fptr_type : TokenStream ,
48
49
method_inits : Vec < MethodInit > ,
50
+ lazy_key_type : TokenStream ,
51
+ lazy_method_init : TokenStream ,
49
52
named_accessors : Vec < AccessorMethod > ,
50
53
class_count : usize ,
51
54
method_count : usize ,
52
55
}
53
56
54
57
// ----------------------------------------------------------------------------------------------------------------------------------------------
55
58
59
+ #[ cfg_attr( feature = "codegen-lazy-fptrs" , allow( dead_code) ) ]
56
60
struct MethodInit {
57
61
method_init : TokenStream ,
58
62
index : usize ,
@@ -69,6 +73,7 @@ impl ToTokens for MethodInit {
69
73
struct AccessorMethod {
70
74
name : Ident ,
71
75
index : usize ,
76
+ lazy_key : TokenStream ,
72
77
}
73
78
74
79
// ----------------------------------------------------------------------------------------------------------------------------------------------
@@ -260,14 +265,17 @@ fn make_named_method_table(info: NamedMethodTable) -> TokenStream {
260
265
}
261
266
}
262
267
263
- fn make_indexed_method_table ( info : IndexedMethodTable ) -> TokenStream {
268
+ #[ cfg( not( feature = "codegen-lazy-fptrs" ) ) ]
269
+ fn make_method_table ( info : IndexedMethodTable ) -> TokenStream {
264
270
let IndexedMethodTable {
265
271
table_name,
266
272
imports,
267
273
ctor_parameters,
268
274
pre_init_code,
269
275
fptr_type,
270
276
mut method_inits,
277
+ lazy_key_type : _,
278
+ lazy_method_init : _,
271
279
named_accessors,
272
280
class_count,
273
281
method_count,
@@ -334,6 +342,79 @@ fn make_indexed_method_table(info: IndexedMethodTable) -> TokenStream {
334
342
}
335
343
}
336
344
345
+ #[ cfg( feature = "codegen-lazy-fptrs" ) ]
346
+ fn make_method_table ( info : IndexedMethodTable ) -> TokenStream {
347
+ let IndexedMethodTable {
348
+ table_name,
349
+ imports,
350
+ ctor_parameters : _,
351
+ pre_init_code : _,
352
+ fptr_type,
353
+ method_inits : _,
354
+ lazy_key_type,
355
+ lazy_method_init,
356
+ named_accessors,
357
+ class_count,
358
+ method_count,
359
+ } = info;
360
+
361
+ // Editor table can be empty, if the Godot binary is compiled without editor.
362
+ let unused_attr = ( method_count == 0 ) . then ( || quote ! { #[ allow( unused_variables) ] } ) ;
363
+ let named_method_api = make_named_accessors ( & named_accessors, & fptr_type) ;
364
+
365
+ // Assumes that inits already have a trailing comma.
366
+ // This is necessary because some generators emit multiple lines (statements) per element.
367
+ quote ! {
368
+ #imports
369
+ use crate :: StringCache ;
370
+ use std:: collections:: HashMap ;
371
+ use std:: cell:: RefCell ;
372
+
373
+ // Exists to be stored inside RefCell.
374
+ struct InnerTable {
375
+ // 'static because at this point, the interface and lifecycle tables are globally available.
376
+ string_cache: StringCache <' static >,
377
+ function_pointers: HashMap <#lazy_key_type, #fptr_type>,
378
+ }
379
+
380
+ // Note: get_method_bind and other function pointers could potentially be stored as fields in table, to avoid interface_fn!.
381
+ pub struct #table_name {
382
+ inner: RefCell <InnerTable >,
383
+ }
384
+
385
+ impl #table_name {
386
+ pub const CLASS_COUNT : usize = #class_count;
387
+ pub const METHOD_COUNT : usize = #method_count;
388
+
389
+ #unused_attr
390
+ pub fn load( ) -> Self {
391
+ // SAFETY: interface and lifecycle tables are initialized at this point, so we can get 'static references to them.
392
+ let ( interface, lifecycle_table) = unsafe {
393
+ ( crate :: get_interface( ) , crate :: method_table( ) )
394
+ } ;
395
+
396
+ Self {
397
+ inner: RefCell :: new( InnerTable {
398
+ string_cache: StringCache :: new( interface, lifecycle_table) ,
399
+ function_pointers: HashMap :: new( ) ,
400
+ } ) ,
401
+ }
402
+ }
403
+
404
+ #[ inline( always) ]
405
+ pub fn fptr_by_key( & self , key: #lazy_key_type) -> #fptr_type {
406
+ let mut guard = self . inner. borrow_mut( ) ;
407
+ let inner = & mut * guard;
408
+ * inner. function_pointers. entry( key. clone( ) ) . or_insert_with( || {
409
+ #lazy_method_init
410
+ } )
411
+ }
412
+
413
+ #named_method_api
414
+ }
415
+ }
416
+ }
417
+
337
418
pub ( crate ) fn generate_sys_builtin_methods_file (
338
419
api : & ExtensionApi ,
339
420
builtin_types : & BuiltinTypeMap ,
@@ -739,6 +820,18 @@ fn make_class_method_table(
739
820
pre_init_code : TokenStream :: new ( ) , // late-init, depends on class string names
740
821
fptr_type : quote ! { crate :: ClassMethodBind } ,
741
822
method_inits : vec ! [ ] ,
823
+ lazy_key_type : quote ! { crate :: lazy_keys:: ClassMethodKey } ,
824
+ lazy_method_init : quote ! {
825
+ let get_method_bind = crate :: interface_fn!( classdb_get_method_bind) ;
826
+ crate :: load_class_method(
827
+ get_method_bind,
828
+ & mut inner. string_cache,
829
+ None ,
830
+ key. class_name,
831
+ key. method_name,
832
+ key. hash
833
+ )
834
+ } ,
742
835
named_accessors : vec ! [ ] ,
743
836
class_count : 0 ,
744
837
method_count : 0 ,
@@ -775,18 +868,33 @@ fn make_class_method_table(
775
868
#( #class_sname_decls ) *
776
869
} ;
777
870
778
- make_indexed_method_table ( table)
871
+ make_method_table ( table)
779
872
}
780
873
781
874
/// For index-based method tables, have select methods exposed by name for internal use.
782
875
fn make_named_accessors ( accessors : & [ AccessorMethod ] , fptr : & TokenStream ) -> TokenStream {
783
876
let mut result_api = TokenStream :: new ( ) ;
784
877
785
- for AccessorMethod { name, index } in accessors {
786
- let code = quote ! {
787
- #[ inline( always) ]
788
- pub fn #name( & self ) -> #fptr {
789
- self . fptr_by_index( #index)
878
+ for accessor in accessors {
879
+ let AccessorMethod {
880
+ name,
881
+ index,
882
+ lazy_key,
883
+ } = accessor;
884
+
885
+ let code = if cfg ! ( feature = "codegen-lazy-fptrs" ) {
886
+ quote ! {
887
+ #[ inline( always) ]
888
+ pub fn #name( & self ) -> #fptr {
889
+ self . fptr_by_key( #lazy_key)
890
+ }
891
+ }
892
+ } else {
893
+ quote ! {
894
+ #[ inline( always) ]
895
+ pub fn #name( & self ) -> #fptr {
896
+ self . fptr_by_index( #index)
897
+ }
790
898
}
791
899
} ;
792
900
@@ -813,6 +921,18 @@ fn make_builtin_method_table(
813
921
} ,
814
922
fptr_type : quote ! { crate :: BuiltinMethodBind } ,
815
923
method_inits : vec ! [ ] ,
924
+ lazy_key_type : quote ! { crate :: lazy_keys:: BuiltinMethodKey } ,
925
+ lazy_method_init : quote ! {
926
+ let get_builtin_method = crate :: interface_fn!( variant_get_ptr_builtin_method) ;
927
+ crate :: load_builtin_method(
928
+ get_builtin_method,
929
+ & mut inner. string_cache,
930
+ key. variant_type. sys( ) ,
931
+ key. variant_type_str,
932
+ key. method_name,
933
+ key. hash
934
+ )
935
+ } ,
816
936
named_accessors : vec ! [ ] ,
817
937
class_count : 0 ,
818
938
method_count : 0 ,
@@ -828,7 +948,7 @@ fn make_builtin_method_table(
828
948
table. class_count += 1 ;
829
949
}
830
950
831
- make_indexed_method_table ( table)
951
+ make_method_table ( table)
832
952
}
833
953
834
954
fn populate_class_methods (
@@ -856,9 +976,20 @@ fn populate_class_methods(
856
976
857
977
// If requested, add a named accessor for this method.
858
978
if special_cases:: is_named_accessor_in_table ( class_ty, & method. name ) {
979
+ let class_name_str = class_ty. godot_ty . as_str ( ) ;
980
+ let method_name_str = method. name . as_str ( ) ;
981
+ let hash = method. hash . expect ( "hash present" ) ;
982
+
859
983
table. named_accessors . push ( AccessorMethod {
860
984
name : make_class_method_ptr_name ( class_ty, method) ,
861
985
index,
986
+ lazy_key : quote ! {
987
+ crate :: lazy_keys:: ClassMethodKey {
988
+ class_name: #class_name_str,
989
+ method_name: #method_name_str,
990
+ hash: #hash,
991
+ }
992
+ } ,
862
993
} ) ;
863
994
}
864
995
}
@@ -888,9 +1019,22 @@ fn populate_builtin_methods(
888
1019
889
1020
// If requested, add a named accessor for this method.
890
1021
if special_cases:: is_named_accessor_in_table ( & builtin_ty, & method. name ) {
1022
+ let variant_type = & builtin_name. sys_variant_type ;
1023
+ let variant_type_str = & builtin_name. json_builtin_name ;
1024
+ let method_name_str = method. name . as_str ( ) ;
1025
+ let hash = method. hash . expect ( "hash present" ) ;
1026
+
891
1027
table. named_accessors . push ( AccessorMethod {
892
1028
name : make_builtin_method_ptr_name ( & builtin_ty, method) ,
893
1029
index,
1030
+ lazy_key : quote ! {
1031
+ crate :: lazy_keys:: BuiltinMethodKey {
1032
+ variant_type: #variant_type,
1033
+ variant_type_str: #variant_type_str,
1034
+ method_name: #method_name_str,
1035
+ hash: #hash,
1036
+ }
1037
+ } ,
894
1038
} ) ;
895
1039
}
896
1040
}
@@ -911,8 +1055,9 @@ fn make_class_method_init(
911
1055
)
912
1056
} ) ;
913
1057
1058
+ // Could reuse lazy key, but less code like this -> faster parsing.
914
1059
quote ! {
915
- crate :: load_class_method( get_method_bind, string_names, #class_var, #class_name_str, #method_name_str, #hash) ,
1060
+ crate :: load_class_method( get_method_bind, string_names, Some ( #class_var) , #class_name_str, #method_name_str, #hash) ,
916
1061
}
917
1062
}
918
1063
@@ -933,8 +1078,19 @@ fn make_builtin_method_init(
933
1078
)
934
1079
} ) ;
935
1080
1081
+ // Could reuse lazy key, but less code like this -> faster parsing.
936
1082
quote ! {
937
- { let _ = #index; crate :: load_builtin_method( get_builtin_method, string_names, sys:: #variant_type, #variant_type_str, #method_name_str, #hash) } ,
1083
+ {
1084
+ let _ = #index;
1085
+ crate :: load_builtin_method(
1086
+ get_builtin_method,
1087
+ string_names,
1088
+ sys:: #variant_type,
1089
+ #variant_type_str,
1090
+ #method_name_str,
1091
+ #hash
1092
+ )
1093
+ } ,
938
1094
}
939
1095
}
940
1096
0 commit comments