77
88//! Runtime checks and inspection of Godot classes.
99
10- use crate :: builtin:: { GString , StringName , Variant } ;
10+ use crate :: builtin:: { GString , StringName , Variant , VariantType } ;
1111#[ cfg( debug_assertions) ]
1212use crate :: classes:: { ClassDb , Object } ;
1313use crate :: meta:: CallContext ;
@@ -30,11 +30,14 @@ pub(crate) fn debug_string<T: GodotClass>(
3030 }
3131}
3232
33+ #[ cfg( since_api = "4.4" ) ]
3334pub ( crate ) fn debug_string_variant (
3435 obj : & Variant ,
3536 f : & mut std:: fmt:: Formatter < ' _ > ,
3637 ty : & str ,
3738) -> std:: fmt:: Result {
39+ debug_assert_eq ! ( obj. get_type( ) , VariantType :: OBJECT ) ;
40+
3841 let id = obj
3942 . object_id_unchecked ( )
4043 . expect ( "Variant must be of type OBJECT" ) ;
@@ -58,6 +61,31 @@ pub(crate) fn debug_string_variant(
5861 }
5962}
6063
64+ // Polyfill for Godot < 4.4, where Variant::object_id_unchecked() is not available.
65+ #[ cfg( before_api = "4.4" ) ]
66+ pub ( crate ) fn debug_string_variant (
67+ obj : & Variant ,
68+ f : & mut std:: fmt:: Formatter < ' _ > ,
69+ ty : & str ,
70+ ) -> std:: fmt:: Result {
71+ debug_assert_eq ! ( obj. get_type( ) , VariantType :: OBJECT ) ;
72+
73+ match obj. try_to :: < Gd < crate :: classes:: Object > > ( ) {
74+ Ok ( obj) => {
75+ let id = obj. instance_id ( ) ; // Guaranteed valid, since conversion would have failed otherwise.
76+ let class = obj. dynamic_class_string ( ) ;
77+
78+ // Refcount is off-by-one due to now-created Gd<T> from conversion; correct by -1.
79+ let refcount = obj. maybe_refcount ( ) . map ( |rc| rc. saturating_sub ( 1 ) ) ;
80+
81+ debug_string_inner ( f, ty, id, class, refcount)
82+ }
83+ Err ( _) => {
84+ write ! ( f, "{ty} {{ freed obj }}" )
85+ }
86+ }
87+ }
88+
6189fn debug_string_inner (
6290 f : & mut std:: fmt:: Formatter < ' _ > ,
6391 ty : & str ,
0 commit comments