55 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
66 */
77
8+ #[ cfg( debug_assertions) ]
9+ use std:: cell:: Cell ;
10+ use std:: cell:: RefCell ;
11+ use std:: collections:: hash_map:: Entry ;
12+ use std:: collections:: HashMap ;
813use std:: fmt:: { Debug , Display , Formatter , Result as FmtResult } ;
914use std:: mem:: ManuallyDrop ;
15+ use std:: rc:: Rc ;
1016
11- use crate :: obj:: { Gd , GodotClass } ;
17+ use crate :: builtin:: { Callable , Variant } ;
18+ use crate :: obj:: { bounds, Gd , GodotClass , InstanceId } ;
1219use crate :: { classes, sys} ;
1320
21+ thread_local ! {
22+ /// Extra strong references for each instance ID, needed for [`Base::to_init_gd()`].
23+ ///
24+ /// At the moment, all Godot objects must be accessed from the main thread, because their deferred destruction (`Drop`) runs on the
25+ /// main thread, too. This may be relaxed in the future, and a `sys::Global` could be used instead of a `thread_local!`.
26+ static PENDING_STRONG_REFS : RefCell <HashMap <InstanceId , Gd <classes:: RefCounted >>> = RefCell :: new( HashMap :: new( ) ) ;
27+ }
28+
29+ /// Represents the initialization state of a `Base<T>` object.
30+ #[ cfg( debug_assertions) ]
31+ #[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
32+ enum InitState {
33+ /// Object is being constructed (inside `I*::init()` or `Gd::from_init_fn()`).
34+ ObjectConstructing ,
35+ /// Object construction is complete.
36+ ObjectInitialized ,
37+ /// `ScriptInstance` context - always considered initialized (bypasses lifecycle checks).
38+ Script ,
39+ }
40+
41+ #[ cfg( debug_assertions) ]
42+ macro_rules! base_from_obj {
43+ ( $obj: expr, $state: expr) => {
44+ Base :: from_obj( $obj, $state)
45+ } ;
46+ }
47+
48+ #[ cfg( not( debug_assertions) ) ]
49+ macro_rules! base_from_obj {
50+ ( $obj: expr, $state: expr) => {
51+ Base :: from_obj( $obj)
52+ } ;
53+ }
54+
55+ // ----------------------------------------------------------------------------------------------------------------------------------------------
56+
1457/// Restricted version of `Gd`, to hold the base instance inside a user's `GodotClass`.
1558///
1659/// Behaves similarly to [`Gd`][crate::obj::Gd], but is more constrained. Cannot be constructed by the user.
@@ -35,6 +78,12 @@ pub struct Base<T: GodotClass> {
3578 // 1. Gd<T> -- triggers InstanceStorage destruction
3679 // 2.
3780 obj : ManuallyDrop < Gd < T > > ,
81+
82+ /// Tracks the initialization state of this `Base<T>` in Debug mode.
83+ ///
84+ /// Rc allows to "copy-construct" the base from an existing one, while still affecting the user-instance through the original `Base<T>`.
85+ #[ cfg( debug_assertions) ]
86+ init_state : Rc < Cell < InitState > > ,
3887}
3988
4089impl < T : GodotClass > Base < T > {
@@ -47,7 +96,14 @@ impl<T: GodotClass> Base<T> {
4796 /// If `base` is destroyed while the returned `Base<T>` is in use, that constitutes a logic error, not a safety issue.
4897 pub ( crate ) unsafe fn from_base ( base : & Base < T > ) -> Base < T > {
4998 debug_assert ! ( base. obj. is_instance_valid( ) ) ;
50- Base :: from_obj ( Gd :: from_obj_sys_weak ( base. obj . obj_sys ( ) ) )
99+
100+ let obj = Gd :: from_obj_sys_weak ( base. obj . obj_sys ( ) ) ;
101+
102+ Self {
103+ obj : ManuallyDrop :: new ( obj) ,
104+ #[ cfg( debug_assertions) ]
105+ init_state : Rc :: clone ( & base. init_state ) ,
106+ }
51107 }
52108
53109 /// Create base from existing object (used in script instances).
@@ -57,9 +113,11 @@ impl<T: GodotClass> Base<T> {
57113 /// # Safety
58114 /// `gd` must be alive at the time of invocation. If it is destroyed while the returned `Base<T>` is in use, that constitutes a logic
59115 /// error, not a safety issue.
60- pub ( crate ) unsafe fn from_gd ( gd : & Gd < T > ) -> Self {
116+ pub ( crate ) unsafe fn from_script_gd ( gd : & Gd < T > ) -> Self {
61117 debug_assert ! ( gd. is_instance_valid( ) ) ;
62- Base :: from_obj ( Gd :: from_obj_sys_weak ( gd. obj_sys ( ) ) )
118+
119+ let obj = Gd :: from_obj_sys_weak ( gd. obj_sys ( ) ) ;
120+ base_from_obj ! ( obj, InitState :: Script )
63121 }
64122
65123 /// Create new base from raw Godot object.
@@ -72,17 +130,26 @@ impl<T: GodotClass> Base<T> {
72130 pub ( crate ) unsafe fn from_sys ( base_ptr : sys:: GDExtensionObjectPtr ) -> Self {
73131 assert ! ( !base_ptr. is_null( ) , "instance base is null pointer" ) ;
74132
75- // Initialize only as weak pointer (don't increment reference count)
133+ // Initialize only as weak pointer (don't increment reference count).
76134 let obj = Gd :: from_obj_sys_weak ( base_ptr) ;
77135
78136 // This obj does not contribute to the strong count, otherwise we create a reference cycle:
79137 // 1. RefCounted (dropped in GDScript)
80138 // 2. holds user T (via extension instance and storage)
81139 // 3. holds Base<T> RefCounted (last ref, dropped in T destructor, but T is never destroyed because this ref keeps storage alive)
82140 // Note that if late-init never happened on self, we have the same behavior (still a raw pointer instead of weak Gd)
83- Base :: from_obj ( obj)
141+ base_from_obj ! ( obj, InitState :: ObjectConstructing )
84142 }
85143
144+ #[ cfg( debug_assertions) ]
145+ fn from_obj ( obj : Gd < T > , init_state : InitState ) -> Self {
146+ Self {
147+ obj : ManuallyDrop :: new ( obj) ,
148+ init_state : Rc :: new ( Cell :: new ( init_state) ) ,
149+ }
150+ }
151+
152+ #[ cfg( not( debug_assertions) ) ]
86153 fn from_obj ( obj : Gd < T > ) -> Self {
87154 Self {
88155 obj : ManuallyDrop :: new ( obj) ,
@@ -94,10 +161,132 @@ impl<T: GodotClass> Base<T> {
94161 /// Using this method to call methods on the base field of a Rust object is discouraged, instead use the
95162 /// methods from [`WithBaseField`](super::WithBaseField) when possible.
96163 #[ doc( hidden) ]
164+ #[ deprecated = "Private API. Use `Base::to_init_gd()` or `WithBaseField::to_gd()` instead." ] // TODO(v0.4): remove.
97165 pub fn to_gd ( & self ) -> Gd < T > {
98166 ( * self . obj ) . clone ( )
99167 }
100168
169+ /// Returns a [`Gd`] referencing the base object, for exclusive use during object initialization.
170+ ///
171+ /// Can be used during an initialization function [`I*::init()`][crate::classes::IObject::init] or [`Gd::from_init_fn()`].
172+ ///
173+ /// The base pointer is only pointing to a base object; you cannot yet downcast it to the object being constructed.
174+ /// The instance ID is the same as the one the in-construction object will have.
175+ ///
176+ /// # Lifecycle for ref-counted classes
177+ /// If `T: Inherits<RefCounted>`, then the ref-counted object is not yet fully-initialized at the time of the `init` function running.
178+ /// Accessing the base object without further measures would be dangerous. Here, godot-rust employs a workaround: the `Base` object (which
179+ /// holds a weak pointer to the actual instance) is temporarily upgraded to a strong pointer, preventing use-after-free.
180+ ///
181+ /// This additional reference is automatically dropped at an implementation-defined point in time (which may change, and technically delay
182+ /// destruction of your object as soon as you use `Base::to_init_gd()`). Right now, this refcount-decrement is deferred to the next frame.
183+ ///
184+ /// For now, ref-counted bases can only use `to_init_gd()` on the main thread.
185+ ///
186+ /// # Panics (Debug)
187+ /// If called outside an initialization function, or for ref-counted objects on a non-main thread.
188+ #[ cfg( since_api = "4.2" ) ]
189+ pub fn to_init_gd ( & self ) -> Gd < T > {
190+ #[ cfg( debug_assertions) ] // debug_assert! still checks existence of symbols.
191+ assert ! (
192+ self . is_initializing( ) ,
193+ "Base::to_init_gd() can only be called during object initialization, inside I*::init() or Gd::from_init_fn()"
194+ ) ;
195+
196+ // For manually-managed objects, regular clone is fine.
197+ // Only static type matters, because this happens immediately after initialization, so T is both static and dynamic type.
198+ if !<T :: Memory as bounds:: Memory >:: IS_REF_COUNTED {
199+ return Gd :: clone ( & self . obj ) ;
200+ }
201+
202+ debug_assert ! (
203+ sys:: is_main_thread( ) ,
204+ "Base::to_init_gd() can only be called on the main thread for ref-counted objects (for now)"
205+ ) ;
206+
207+ // First time handing out a Gd<T>, we need to take measures to temporarily upgrade the Base's weak pointer to a strong one.
208+ // During the initialization phase (derived object being constructed), increment refcount by 1.
209+ let instance_id = self . obj . instance_id ( ) ;
210+ PENDING_STRONG_REFS . with ( |refs| {
211+ let mut pending_refs = refs. borrow_mut ( ) ;
212+ if let Entry :: Vacant ( e) = pending_refs. entry ( instance_id) {
213+ let strong_ref: Gd < T > = unsafe { Gd :: from_obj_sys ( self . obj . obj_sys ( ) ) } ;
214+
215+ // We know that T: Inherits<RefCounted> due to check above, but don't it as a static bound for Gd::upcast().
216+ // Thus fall back to low-level FFI cast on RawGd.
217+ let strong_ref_raw = strong_ref. raw ;
218+ let raw = strong_ref_raw
219+ . ffi_cast :: < classes:: RefCounted > ( )
220+ . expect ( "Base must be RefCounted" )
221+ . into_dest ( strong_ref_raw) ;
222+
223+ e. insert ( Gd { raw } ) ;
224+ }
225+ } ) ;
226+
227+ let name = format ! ( "Base<{}> deferred unref" , T :: class_name( ) ) ;
228+ let callable = Callable :: from_once_fn ( & name, move |_args| {
229+ Self :: drop_strong_ref ( instance_id) ;
230+ Ok ( Variant :: nil ( ) )
231+ } ) ;
232+
233+ // Use Callable::call_deferred() instead of Gd::apply_deferred(). The latter implicitly borrows &mut self,
234+ // causing a "destroyed while bind was active" panic.
235+ callable. call_deferred ( & [ ] ) ;
236+
237+ ( * self . obj ) . clone ( )
238+ }
239+
240+ /// Drops any extra strong references, possibly causing object destruction.
241+ fn drop_strong_ref ( instance_id : InstanceId ) {
242+ PENDING_STRONG_REFS . with ( |refs| {
243+ let mut pending_refs = refs. borrow_mut ( ) ;
244+ let strong_ref = pending_refs. remove ( & instance_id) ;
245+ assert ! (
246+ strong_ref. is_some( ) ,
247+ "Base unexpectedly had its strong ref rug-pulled"
248+ ) ;
249+
250+ // Triggers RawGd::drop() -> dec-ref -> possibly object destruction.
251+ } ) ;
252+ }
253+
254+ /// Finalizes the initialization of this `Base<T>` and returns whether
255+ pub ( crate ) fn mark_initialized ( & mut self ) {
256+ #[ cfg( debug_assertions) ]
257+ {
258+ assert_eq ! (
259+ self . init_state. get( ) ,
260+ InitState :: ObjectConstructing ,
261+ "Base<T> is already initialized, or holds a script instance"
262+ ) ;
263+
264+ self . init_state . set ( InitState :: ObjectInitialized ) ;
265+ }
266+
267+ // May return whether there is a "surplus" strong ref in the future, as self.extra_strong_ref.borrow().is_some().
268+ }
269+
270+ /// Returns a [`Gd`] referencing the base object, assuming the derived object is fully constructed.
271+ #[ doc( hidden) ]
272+ pub fn __fully_constructed_gd ( & self ) -> Gd < T > {
273+ #[ cfg( debug_assertions) ] // debug_assert! still checks existence of symbols.
274+ assert ! (
275+ !self . is_initializing( ) ,
276+ "WithBaseField::to_gd(), base(), base_mut() can only be called on fully-constructed objects, after I*::init() or Gd::from_init_fn()"
277+ ) ;
278+
279+ ( * self . obj ) . clone ( )
280+ }
281+
282+ /// Returns a [`Gd`] referencing the base object, for use in script contexts only.
283+ #[ doc( hidden) ]
284+ pub fn __script_gd ( & self ) -> Gd < T > {
285+ // Used internally by `SiMut::base()` and `SiMut::base_mut()` for script re-entrancy.
286+ // Could maybe add debug validation to ensure script context in the future.
287+ ( * self . obj ) . clone ( )
288+ }
289+
101290 // Currently only used in outbound virtual calls (for scripts); search for: base_field(self).obj_sys().
102291 #[ doc( hidden) ]
103292 pub fn obj_sys ( & self ) -> sys:: GDExtensionObjectPtr {
@@ -109,6 +298,36 @@ impl<T: GodotClass> Base<T> {
109298 pub ( crate ) fn debug_instance_id ( & self ) -> crate :: obj:: InstanceId {
110299 self . obj . instance_id ( )
111300 }
301+
302+ /// Returns a [`Gd`] referencing the base object, for use in script contexts only.
303+ pub ( crate ) fn to_script_gd ( & self ) -> Gd < T > {
304+ #[ cfg( debug_assertions) ]
305+ assert_eq ! (
306+ self . init_state. get( ) ,
307+ InitState :: Script ,
308+ "to_script_gd() can only be called on script-context Base objects"
309+ ) ;
310+
311+ ( * self . obj ) . clone ( )
312+ }
313+
314+ /// Returns `true` if this `Base<T>` is currently in the initializing state.
315+ #[ cfg( debug_assertions) ]
316+ fn is_initializing ( & self ) -> bool {
317+ self . init_state . get ( ) == InitState :: ObjectConstructing
318+ }
319+
320+ /// Returns a [`Gd`] referencing the base object, assuming the derived object is fully constructed.
321+ #[ doc( hidden) ]
322+ pub fn __constructed_gd ( & self ) -> Gd < T > {
323+ #[ cfg( debug_assertions) ] // debug_assert! still checks existence of symbols.
324+ assert ! (
325+ !self . is_initializing( ) ,
326+ "WithBaseField::to_gd(), base(), base_mut() can only be called on fully-constructed objects, after I*::init() or Gd::from_init_fn()"
327+ ) ;
328+
329+ ( * self . obj ) . clone ( )
330+ }
112331}
113332
114333impl < T : GodotClass > Debug for Base < T > {
0 commit comments