@@ -159,6 +159,7 @@ fn make_constructor(class: &Class, ctx: &Context) -> TokenStream {
159
159
fn make_class ( class : & Class , class_name : & TyName , ctx : & mut Context ) -> GeneratedClass {
160
160
// Strings
161
161
let godot_class_str = & class_name. godot_ty ;
162
+ let virtual_trait_str = class_name. virtual_trait_name ( ) ;
162
163
163
164
// Idents and tokens
164
165
let base = match class. inherits . as_ref ( ) {
@@ -174,6 +175,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
174
175
let enums = make_enums ( & class. enums , class_name, ctx) ;
175
176
let inherits_macro = format_ident ! ( "inherits_transitive_{}" , class_name. rust_ty) ;
176
177
let all_bases = ctx. inheritance_tree ( ) . collect_all_bases ( class_name) ;
178
+ let virtual_trait = make_virtual_methods_trait ( class, & all_bases, & virtual_trait_str, ctx) ;
177
179
178
180
let memory = if class_name. rust_ty == "Object" {
179
181
ident ( "DynamicRefCount" )
@@ -199,6 +201,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
199
201
pub struct #class_name {
200
202
object_ptr: sys:: GDExtensionObjectPtr ,
201
203
}
204
+ #virtual_trait
202
205
impl #class_name {
203
206
#constructor
204
207
#methods
@@ -323,12 +326,14 @@ fn make_module_file(classes_and_modules: Vec<GeneratedClassModule>) -> TokenStre
323
326
is_pub,
324
327
..
325
328
} = m;
329
+ let virtual_trait_name = ident ( & class_name. virtual_trait_name ( ) ) ;
326
330
327
331
let vis = is_pub. then_some ( quote ! { pub } ) ;
328
332
329
333
quote ! {
330
334
#vis mod #module_name;
331
335
pub use #module_name:: re_export:: #class_name;
336
+ pub use #module_name:: re_export:: #virtual_trait_name;
332
337
}
333
338
} ) ;
334
339
@@ -463,12 +468,15 @@ fn is_type_excluded(ty: &str, ctx: &mut Context) -> bool {
463
468
}
464
469
}
465
470
466
- fn is_method_excluded ( method : & ClassMethod , #[ allow( unused_variables) ] ctx : & mut Context ) -> bool {
471
+ fn is_method_excluded (
472
+ method : & ClassMethod ,
473
+ is_virtual_impl : bool ,
474
+ #[ allow( unused_variables) ] ctx : & mut Context ,
475
+ ) -> bool {
467
476
// Currently excluded:
468
477
//
469
- // * Private virtual methods designed for override; skip for now
470
- // E.g.: AudioEffectInstance::_process(const void*, AudioFrame*, int)
471
- // TODO decide what to do with them, overriding in a type-safe way?
478
+ // * Private virtual methods are only included in a virtual
479
+ // implementation.
472
480
//
473
481
// * Methods accepting pointers are often supplementary
474
482
// E.g.: TextServer::font_set_data_ptr() -- in addition to TextServer::font_set_data().
@@ -490,11 +498,14 @@ fn is_method_excluded(method: &ClassMethod, #[allow(unused_variables)] ctx: &mut
490
498
}
491
499
// -- end.
492
500
493
- method. name . starts_with ( '_' )
494
- || method
495
- . return_value
496
- . as_ref ( )
497
- . map_or ( false , |ret| ret. type_ . contains ( '*' ) )
501
+ if method. name . starts_with ( '_' ) && !is_virtual_impl {
502
+ return true ;
503
+ }
504
+
505
+ method
506
+ . return_value
507
+ . as_ref ( )
508
+ . map_or ( false , |ret| ret. type_ . contains ( '*' ) )
498
509
|| method
499
510
. arguments
500
511
. as_ref ( )
@@ -523,7 +534,8 @@ fn make_method_definition(
523
534
class_name : & TyName ,
524
535
ctx : & mut Context ,
525
536
) -> TokenStream {
526
- if is_method_excluded ( method, ctx) || special_cases:: is_deleted ( class_name, & method. name ) {
537
+ if is_method_excluded ( method, false , ctx) || special_cases:: is_deleted ( class_name, & method. name )
538
+ {
527
539
return TokenStream :: new ( ) ;
528
540
}
529
541
/*if method.map_args(|args| args.is_empty()) {
@@ -799,13 +811,7 @@ fn make_receiver(
799
811
is_const : bool ,
800
812
receiver_arg : TokenStream ,
801
813
) -> ( TokenStream , TokenStream ) {
802
- let receiver = if is_static {
803
- quote ! { }
804
- } else if is_const {
805
- quote ! { & self , }
806
- } else {
807
- quote ! { & mut self , }
808
- } ;
814
+ let receiver = make_receiver_self_param ( is_static, is_const) ;
809
815
810
816
let receiver_arg = if is_static {
811
817
quote ! { std:: ptr:: null_mut( ) }
@@ -816,6 +822,16 @@ fn make_receiver(
816
822
( receiver, receiver_arg)
817
823
}
818
824
825
+ fn make_receiver_self_param ( is_static : bool , is_const : bool ) -> TokenStream {
826
+ if is_static {
827
+ quote ! { }
828
+ } else if is_const {
829
+ quote ! { & self , }
830
+ } else {
831
+ quote ! { & mut self , }
832
+ }
833
+ }
834
+
819
835
fn make_params (
820
836
method_args : & Option < Vec < MethodArg > > ,
821
837
is_varcall : bool ,
@@ -930,3 +946,113 @@ fn make_return(
930
946
931
947
( return_decl, call)
932
948
}
949
+
950
+ fn make_virtual_methods_trait (
951
+ class : & Class ,
952
+ all_bases : & [ TyName ] ,
953
+ trait_name : & str ,
954
+ ctx : & mut Context ,
955
+ ) -> TokenStream {
956
+ let trait_name = ident ( trait_name) ;
957
+
958
+ let virtual_method_fns = make_all_virtual_methods ( class, all_bases, ctx) ;
959
+ let special_virtual_methods = special_virtual_methods ( ) ;
960
+
961
+ quote ! {
962
+ #[ allow( unused_variables) ]
963
+ #[ allow( clippy:: unimplemented) ]
964
+ pub trait #trait_name: crate :: private:: You_forgot_the_attribute__godot_api + crate :: obj:: GodotClass {
965
+ #( #virtual_method_fns ) *
966
+ #special_virtual_methods
967
+ }
968
+ }
969
+ }
970
+
971
+ fn special_virtual_methods ( ) -> TokenStream {
972
+ quote ! {
973
+ fn register_class( builder: & mut crate :: builder:: ClassBuilder <Self >) {
974
+ unimplemented!( )
975
+ }
976
+ fn init( base: crate :: obj:: Base <Self :: Base >) -> Self {
977
+ unimplemented!( )
978
+ }
979
+ fn to_string( & self ) -> crate :: builtin:: GodotString {
980
+ unimplemented!( )
981
+ }
982
+ }
983
+ }
984
+
985
+ fn make_virtual_method ( class_method : & ClassMethod , ctx : & mut Context ) -> TokenStream {
986
+ let method_name = ident ( virtual_method_name ( class_method) ) ;
987
+
988
+ // Virtual methods are never static.
989
+ assert ! ( !class_method. is_static) ;
990
+
991
+ let receiver = make_receiver_self_param ( false , class_method. is_const ) ;
992
+ let [ params, _, _, _] = make_params ( & class_method. arguments , class_method. is_vararg , ctx) ;
993
+
994
+ quote ! {
995
+ fn #method_name ( #receiver #( #params , ) * ) {
996
+ unimplemented!( )
997
+ }
998
+ }
999
+ }
1000
+
1001
+ fn make_all_virtual_methods (
1002
+ class : & Class ,
1003
+ all_bases : & [ TyName ] ,
1004
+ ctx : & mut Context ,
1005
+ ) -> Vec < TokenStream > {
1006
+ let mut all_virtuals = vec ! [ ] ;
1007
+ let mut extend_virtuals = |class| {
1008
+ all_virtuals. extend (
1009
+ get_methods_in_class ( class)
1010
+ . iter ( )
1011
+ . cloned ( )
1012
+ . filter ( |m| m. is_virtual ) ,
1013
+ ) ;
1014
+ } ;
1015
+
1016
+ // Get virtuals defined on the current class.
1017
+ extend_virtuals ( class) ;
1018
+ // Add virtuals from superclasses.
1019
+ for base in all_bases {
1020
+ let superclass = ctx. get_engine_class ( base) ;
1021
+ extend_virtuals ( superclass) ;
1022
+ }
1023
+ all_virtuals
1024
+ . into_iter ( )
1025
+ . filter_map ( |method| {
1026
+ if is_method_excluded ( & method, true , ctx) {
1027
+ None
1028
+ } else {
1029
+ Some ( make_virtual_method ( & method, ctx) )
1030
+ }
1031
+ } )
1032
+ . collect ( )
1033
+ }
1034
+
1035
+ fn get_methods_in_class ( class : & Class ) -> & [ ClassMethod ] {
1036
+ match & class. methods {
1037
+ None => & [ ] ,
1038
+ Some ( methods) => methods,
1039
+ }
1040
+ }
1041
+
1042
+ fn virtual_method_name ( class_method : & ClassMethod ) -> & str {
1043
+ // Matching the C++ convention, we remove the leading underscore
1044
+ // from virtual method names.
1045
+ let method_name = class_method
1046
+ . name
1047
+ . strip_prefix ( '_' )
1048
+ . unwrap_or ( & class_method. name ) ;
1049
+
1050
+ // As a special exception, a few classes define a virtual method
1051
+ // called "_init" (distinct from the constructor), so we rename
1052
+ // those to avoid a name conflict in our trait.
1053
+ if method_name == "init" {
1054
+ "init_ext"
1055
+ } else {
1056
+ method_name
1057
+ }
1058
+ }
0 commit comments