@@ -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()) {
@@ -768,13 +780,7 @@ fn make_receiver(
768
780
is_const : bool ,
769
781
receiver_arg : TokenStream ,
770
782
) -> ( TokenStream , TokenStream ) {
771
- let receiver = if is_static {
772
- quote ! { }
773
- } else if is_const {
774
- quote ! { & self , }
775
- } else {
776
- quote ! { & mut self , }
777
- } ;
783
+ let receiver = make_receiver_self_param ( is_static, is_const) ;
778
784
779
785
let receiver_arg = if is_static {
780
786
quote ! { std:: ptr:: null_mut( ) }
@@ -785,6 +791,16 @@ fn make_receiver(
785
791
( receiver, receiver_arg)
786
792
}
787
793
794
+ fn make_receiver_self_param ( is_static : bool , is_const : bool ) -> TokenStream {
795
+ if is_static {
796
+ quote ! { }
797
+ } else if is_const {
798
+ quote ! { & self , }
799
+ } else {
800
+ quote ! { & mut self , }
801
+ }
802
+ }
803
+
788
804
fn make_params (
789
805
method_args : & Option < Vec < MethodArg > > ,
790
806
is_varcall : bool ,
@@ -891,3 +907,113 @@ fn make_return(
891
907
892
908
( return_decl, call)
893
909
}
910
+
911
+ fn make_virtual_methods_trait (
912
+ class : & Class ,
913
+ all_bases : & [ TyName ] ,
914
+ trait_name : & str ,
915
+ ctx : & mut Context ,
916
+ ) -> TokenStream {
917
+ let trait_name = ident ( trait_name) ;
918
+
919
+ let virtual_method_fns = make_all_virtual_methods ( class, all_bases, ctx) ;
920
+ let special_virtual_methods = special_virtual_methods ( ) ;
921
+
922
+ quote ! {
923
+ #[ allow( unused_variables) ]
924
+ #[ allow( clippy:: unimplemented) ]
925
+ pub trait #trait_name: crate :: private:: You_forgot_the_attribute__godot_api + crate :: obj:: GodotClass {
926
+ #( #virtual_method_fns ) *
927
+ #special_virtual_methods
928
+ }
929
+ }
930
+ }
931
+
932
+ fn special_virtual_methods ( ) -> TokenStream {
933
+ quote ! {
934
+ fn register_class( builder: & mut crate :: builder:: ClassBuilder <Self >) {
935
+ unimplemented!( )
936
+ }
937
+ fn init( base: crate :: obj:: Base <Self :: Base >) -> Self {
938
+ unimplemented!( )
939
+ }
940
+ fn to_string( & self ) -> crate :: builtin:: GodotString {
941
+ unimplemented!( )
942
+ }
943
+ }
944
+ }
945
+
946
+ fn make_virtual_method ( class_method : & ClassMethod , ctx : & mut Context ) -> TokenStream {
947
+ let method_name = ident ( virtual_method_name ( class_method) ) ;
948
+
949
+ // Virtual methods are never static.
950
+ assert ! ( !class_method. is_static) ;
951
+
952
+ let receiver = make_receiver_self_param ( false , class_method. is_const ) ;
953
+ let ( params, _) = make_params ( & class_method. arguments , class_method. is_vararg , ctx) ;
954
+
955
+ quote ! {
956
+ fn #method_name ( #receiver #( #params , ) * ) {
957
+ unimplemented!( )
958
+ }
959
+ }
960
+ }
961
+
962
+ fn make_all_virtual_methods (
963
+ class : & Class ,
964
+ all_bases : & [ TyName ] ,
965
+ ctx : & mut Context ,
966
+ ) -> Vec < TokenStream > {
967
+ let mut all_virtuals = vec ! [ ] ;
968
+ let mut extend_virtuals = |class| {
969
+ all_virtuals. extend (
970
+ get_methods_in_class ( class)
971
+ . iter ( )
972
+ . cloned ( )
973
+ . filter ( |m| m. is_virtual ) ,
974
+ ) ;
975
+ } ;
976
+
977
+ // Get virtuals defined on the current class.
978
+ extend_virtuals ( class) ;
979
+ // Add virtuals from superclasses.
980
+ for base in all_bases {
981
+ let superclass = ctx. get_engine_class ( base) ;
982
+ extend_virtuals ( superclass) ;
983
+ }
984
+ all_virtuals
985
+ . into_iter ( )
986
+ . filter_map ( |method| {
987
+ if is_method_excluded ( & method, true , ctx) {
988
+ None
989
+ } else {
990
+ Some ( make_virtual_method ( & method, ctx) )
991
+ }
992
+ } )
993
+ . collect ( )
994
+ }
995
+
996
+ fn get_methods_in_class ( class : & Class ) -> & [ ClassMethod ] {
997
+ match & class. methods {
998
+ None => & [ ] ,
999
+ Some ( methods) => methods,
1000
+ }
1001
+ }
1002
+
1003
+ fn virtual_method_name ( class_method : & ClassMethod ) -> & str {
1004
+ // Matching the C++ convention, we remove the leading underscore
1005
+ // from virtual method names.
1006
+ let method_name = class_method
1007
+ . name
1008
+ . strip_prefix ( '_' )
1009
+ . unwrap_or ( & class_method. name ) ;
1010
+
1011
+ // As a special exception, a few classes define a virtual method
1012
+ // called "_init" (distinct from the constructor), so we rename
1013
+ // those to avoid a name conflict in our trait.
1014
+ if method_name == "init" {
1015
+ "init_ext"
1016
+ } else {
1017
+ method_name
1018
+ }
1019
+ }
0 commit comments