@@ -18,7 +18,7 @@ use crate::meta::{
1818use crate :: obj:: bounds:: { Declarer , DynMemory as _} ;
1919use crate :: obj:: casts:: CastSuccess ;
2020use crate :: obj:: rtti:: ObjectRtti ;
21- use crate :: obj:: { bounds, Bounds , GdDerefTarget , GdMut , GdRef , GodotClass , InstanceId } ;
21+ use crate :: obj:: { bounds, Bounds , Gd , GdDerefTarget , GdMut , GdRef , GodotClass , InstanceId } ;
2222use crate :: storage:: { InstanceCache , InstanceStorage , Storage } ;
2323use crate :: { classes, out} ;
2424
@@ -95,8 +95,7 @@ impl<T: GodotClass> RawGd<T> {
9595
9696 /// Returns `true` if the object is null.
9797 ///
98- /// This does not check if the object is dead, for that use
99- /// [`instance_id_or_none()`](Self::instance_id_or_none).
98+ /// This does not check if the object is dead. For that, use [`is_instance_valid()`](Self::is_instance_valid).
10099 pub ( crate ) fn is_null ( & self ) -> bool {
101100 self . obj . is_null ( ) || self . cached_rtti . is_none ( )
102101 }
@@ -148,10 +147,15 @@ impl<T: GodotClass> RawGd<T> {
148147 ///
149148 /// On success, you'll get a `CastSuccess<T, U>` instance, which holds a weak `RawGd<U>`. You can only extract that one by trading
150149 /// a strong `RawGd<T>` for it, to maintain the balance.
150+ ///
151+ /// This function is unreliable when invoked _during_ destruction (e.g. C++ `~RefCounted()` destructor). This can occur when debug-logging
152+ /// instances during cleanups. `Object::object_cast_to()` is a virtual function, but virtual dispatch during destructor doesn't work in C++.
151153 pub ( super ) fn ffi_cast < U > ( & self ) -> Result < CastSuccess < T , U > , ( ) >
152154 where
153155 U : GodotClass ,
154156 {
157+ //eprintln!("ffi_cast: {} (dyn {}) -> {}", T::class_name(), self.as_non_null().dynamic_class_string(), U::class_name());
158+
155159 // `self` may be null when we convert a null-variant into a `Option<Gd<T>>`, since we use `ffi_cast`
156160 // in the `ffi_from_variant` conversion function to ensure type-correctness. So the chain would be as follows:
157161 // - Variant::nil()
@@ -184,24 +188,57 @@ impl<T: GodotClass> RawGd<T> {
184188 Ok ( CastSuccess :: from_weak ( weak) )
185189 }
186190
191+ /// Executes a function, assuming that `self` inherits `RefCounted`.
192+ ///
193+ /// This function is unreliable when invoked _during_ destruction (e.g. C++ `~RefCounted()` destructor). This can occur when debug-logging
194+ /// instances during cleanups. `Object::object_cast_to()` is a virtual function, but virtual dispatch during destructor doesn't work in C++.
195+ ///
196+ /// # Panics
197+ /// If `self` does not inherit `RefCounted` or is null.
187198 pub ( crate ) fn with_ref_counted < R > ( & self , apply : impl Fn ( & mut classes:: RefCounted ) -> R ) -> R {
188199 // Note: this previously called Declarer::scoped_mut() - however, no need to go through bind() for changes in base RefCounted.
189200 // Any accesses to user objects (e.g. destruction if refc=0) would bind anyway.
201+ //
202+ // Might change implementation as follows -- but last time caused UB; investigate.
203+ // pub(crate) unsafe fn as_ref_counted_unchecked(&mut self) -> &mut classes::RefCounted {
204+ // self.as_target_mut()
205+ // }
206+
207+ let mut ref_counted = match self . ffi_cast :: < classes:: RefCounted > ( ) {
208+ Ok ( cast_success) => cast_success,
209+ Err ( ( ) ) if self . is_null ( ) => {
210+ panic ! ( "RawGd::with_ref_counted(): expected to inherit RefCounted, encountered null pointer" ) ;
211+ }
212+ Err ( ( ) ) => {
213+ // SAFETY: this branch implies non-null.
214+ let gd_ref = unsafe { self . as_non_null ( ) } ;
215+ let class = gd_ref. dynamic_class_string ( ) ;
216+
217+ // One way how this may panic is when invoked during destruction of a RefCounted object. The C++ `Object::object_cast_to()`
218+ // function is virtual but cannot be dynamically dispatched in a C++ destructor.
219+ panic ! ( "RawGd::with_ref_counted(): expected to inherit RefCounted, but encountered {class}" ) ;
220+ }
221+ } ;
190222
191- let mut cast_obj = self
192- . ffi_cast :: < classes:: RefCounted > ( )
193- . expect ( "object expected to inherit RefCounted" ) ;
223+ let return_val = apply ( ref_counted. as_dest_mut ( ) . as_target_mut ( ) ) ;
194224
195- // Using as_dest_mut() ensures that there is no refcount increment happening, i.e. any apply() function happens on *current* object.
196- // Apart from performance considerations, this is relevant when examining RefCounted::get_reference_count() -- otherwise we have an
197- // Observer effect, where reading the RefCounted object changes its reference count -- e.g. in Debug impl.
198- apply ( cast_obj. as_dest_mut ( ) . as_target_mut ( ) )
225+ // CastSuccess is forgotten when dropped, so no ownership transfer.
226+ return_val
199227 }
200228
201- // TODO replace the above with this -- last time caused UB; investigate.
202- // pub(crate) unsafe fn as_ref_counted_unchecked(&mut self) -> &mut classes::RefCounted {
203- // self.as_target_mut()
204- // }
229+ /// Enables outer `Gd` APIs or bypasses additional null checks, in cases where `RawGd` is guaranteed non-null.
230+ ///
231+ /// # Safety
232+ /// `self` must not be null.
233+ pub ( crate ) unsafe fn as_non_null ( & self ) -> & Gd < T > {
234+ debug_assert ! (
235+ !self . is_null( ) ,
236+ "RawGd::as_non_null() called on null pointer; this is UB"
237+ ) ;
238+
239+ // SAFETY: layout of Gd<T> is currently equivalent to RawGd<T>.
240+ unsafe { std:: mem:: transmute :: < & RawGd < T > , & Gd < T > > ( self ) }
241+ }
205242
206243 pub ( crate ) fn as_object_ref ( & self ) -> & classes:: Object {
207244 // SAFETY: Object is always a valid upcast target.
@@ -654,7 +691,7 @@ impl<T: GodotClass> Drop for RawGd<T> {
654691 fn drop ( & mut self ) {
655692 // No-op for manually managed objects
656693
657- out ! ( "RawGd::drop <{}>" , std :: any :: type_name :: < T > ( ) ) ;
694+ out ! ( "RawGd::drop: {self:?}" ) ;
658695
659696 // SAFETY: This `Gd` won't be dropped again after this.
660697 // If destruction is triggered by Godot, Storage already knows about it, no need to notify it
@@ -669,7 +706,7 @@ impl<T: GodotClass> Drop for RawGd<T> {
669706
670707impl < T : GodotClass > Clone for RawGd < T > {
671708 fn clone ( & self ) -> Self {
672- out ! ( "RawGd::clone" ) ;
709+ out ! ( "RawGd::clone: {self:?} (before clone) " ) ;
673710
674711 if self . is_null ( ) {
675712 Self :: null ( )
@@ -689,12 +726,7 @@ impl<T: GodotClass> Clone for RawGd<T> {
689726
690727impl < T : GodotClass > fmt:: Debug for RawGd < T > {
691728 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
692- if self . is_null ( ) {
693- return write ! ( f, "{} {{ null obj }}" , std:: any:: type_name:: <T >( ) ) ;
694- }
695-
696- let gd = super :: Gd :: from_ffi ( self . clone ( ) ) ;
697- write ! ( f, "{gd:?}" )
729+ classes:: debug_string_nullable ( self , f, "RawGd" )
698730 }
699731}
700732
0 commit comments