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 ;
811use std:: fmt:: { Debug , Display , Formatter , Result as FmtResult } ;
912use std:: mem:: ManuallyDrop ;
13+ use std:: rc:: Rc ;
1014
11- use crate :: obj:: { Gd , GodotClass } ;
15+ use crate :: builtin:: { Callable , Variant } ;
16+ use crate :: obj:: { bounds, Gd , GodotClass } ;
1217use crate :: { classes, sys} ;
1318
19+ /// Represents the initialization state of a `Base<T>` object.
20+ #[ cfg( debug_assertions) ]
21+ #[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
22+ enum InitState {
23+ /// Object is being constructed (inside `I*::init()` or `Gd::from_init_fn()`).
24+ ObjectConstructing ,
25+ /// Object construction is complete.
26+ ObjectInitialized ,
27+ /// `ScriptInstance` context - always considered initialized (bypasses lifecycle checks).
28+ Script ,
29+ }
30+
31+ #[ cfg( debug_assertions) ]
32+ macro_rules! base_from_obj {
33+ ( $obj: expr, $state: expr) => {
34+ Base :: from_obj( $obj, $state)
35+ } ;
36+ }
37+
38+ #[ cfg( not( debug_assertions) ) ]
39+ macro_rules! base_from_obj {
40+ ( $obj: expr, $state: expr) => {
41+ Base :: from_obj( $obj)
42+ } ;
43+ }
44+
45+ // ----------------------------------------------------------------------------------------------------------------------------------------------
46+
1447/// Restricted version of `Gd`, to hold the base instance inside a user's `GodotClass`.
1548///
1649/// Behaves similarly to [`Gd`][crate::obj::Gd], but is more constrained. Cannot be constructed by the user.
@@ -35,6 +68,15 @@ pub struct Base<T: GodotClass> {
3568 // 1. Gd<T> -- triggers InstanceStorage destruction
3669 // 2.
3770 obj : ManuallyDrop < Gd < T > > ,
71+
72+ /// Additional strong ref, needed to prevent destruction if [`Self::to_init_gd()`] is called on ref-counted objects.
73+ extra_strong_ref : Rc < RefCell < Option < Gd < T > > > > ,
74+
75+ /// Tracks the initialization state of this `Base<T>` in Debug mode.
76+ ///
77+ /// Rc allows to "copy-construct" the base from an existing one, while still affecting the user-instance through the original `Base<T>`.
78+ #[ cfg( debug_assertions) ]
79+ init_state : Rc < Cell < InitState > > ,
3880}
3981
4082impl < T : GodotClass > Base < T > {
@@ -47,7 +89,15 @@ impl<T: GodotClass> Base<T> {
4789 /// If `base` is destroyed while the returned `Base<T>` is in use, that constitutes a logic error, not a safety issue.
4890 pub ( crate ) unsafe fn from_base ( base : & Base < T > ) -> Base < T > {
4991 debug_assert ! ( base. obj. is_instance_valid( ) ) ;
50- Base :: from_obj ( Gd :: from_obj_sys_weak ( base. obj . obj_sys ( ) ) )
92+
93+ let obj = Gd :: from_obj_sys_weak ( base. obj . obj_sys ( ) ) ;
94+
95+ Self {
96+ obj : ManuallyDrop :: new ( obj) ,
97+ extra_strong_ref : Rc :: clone ( & base. extra_strong_ref ) , // Before user init(), no handing out of Gd pointers occurs.
98+ #[ cfg( debug_assertions) ]
99+ init_state : Rc :: clone ( & base. init_state ) ,
100+ }
51101 }
52102
53103 /// Create base from existing object (used in script instances).
@@ -57,9 +107,11 @@ impl<T: GodotClass> Base<T> {
57107 /// # Safety
58108 /// `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
59109 /// error, not a safety issue.
60- pub ( crate ) unsafe fn from_gd ( gd : & Gd < T > ) -> Self {
110+ pub ( crate ) unsafe fn from_script_gd ( gd : & Gd < T > ) -> Self {
61111 debug_assert ! ( gd. is_instance_valid( ) ) ;
62- Base :: from_obj ( Gd :: from_obj_sys_weak ( gd. obj_sys ( ) ) )
112+
113+ let obj = Gd :: from_obj_sys_weak ( gd. obj_sys ( ) ) ;
114+ base_from_obj ! ( obj, InitState :: Script )
63115 }
64116
65117 /// Create new base from raw Godot object.
@@ -72,20 +124,31 @@ impl<T: GodotClass> Base<T> {
72124 pub ( crate ) unsafe fn from_sys ( base_ptr : sys:: GDExtensionObjectPtr ) -> Self {
73125 assert ! ( !base_ptr. is_null( ) , "instance base is null pointer" ) ;
74126
75- // Initialize only as weak pointer (don't increment reference count)
127+ // Initialize only as weak pointer (don't increment reference count).
76128 let obj = Gd :: from_obj_sys_weak ( base_ptr) ;
77129
78130 // This obj does not contribute to the strong count, otherwise we create a reference cycle:
79131 // 1. RefCounted (dropped in GDScript)
80132 // 2. holds user T (via extension instance and storage)
81133 // 3. holds Base<T> RefCounted (last ref, dropped in T destructor, but T is never destroyed because this ref keeps storage alive)
82134 // 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)
135+ base_from_obj ! ( obj, InitState :: ObjectConstructing )
84136 }
85137
138+ #[ cfg( debug_assertions) ]
139+ fn from_obj ( obj : Gd < T > , init_state : InitState ) -> Self {
140+ Self {
141+ obj : ManuallyDrop :: new ( obj) ,
142+ extra_strong_ref : Rc :: new ( RefCell :: new ( None ) ) ,
143+ init_state : Rc :: new ( Cell :: new ( init_state) ) ,
144+ }
145+ }
146+
147+ #[ cfg( not( debug_assertions) ) ]
86148 fn from_obj ( obj : Gd < T > ) -> Self {
87149 Self {
88150 obj : ManuallyDrop :: new ( obj) ,
151+ extra_strong_ref : Rc :: new ( RefCell :: new ( None ) ) ,
89152 }
90153 }
91154
@@ -94,10 +157,112 @@ impl<T: GodotClass> Base<T> {
94157 /// Using this method to call methods on the base field of a Rust object is discouraged, instead use the
95158 /// methods from [`WithBaseField`](super::WithBaseField) when possible.
96159 #[ doc( hidden) ]
160+ #[ deprecated = "Private API. Use `Base::to_init_gd()` or `WithBaseField::to_gd()` instead." ] // TODO(v0.4): remove.
97161 pub fn to_gd ( & self ) -> Gd < T > {
98162 ( * self . obj ) . clone ( )
99163 }
100164
165+ /// Returns a [`Gd`] referencing the base object, for exclusive use during object initialization.
166+ ///
167+ /// Can be used during an initialization function [`I*::init()`][crate::classes::IObject::init] or [`Gd::from_init_fn()`].
168+ ///
169+ /// The base pointer is only pointing to a base object; you cannot yet downcast it to the object being constructed.
170+ /// The instance ID is the same as the one the in-construction object will have.
171+ ///
172+ /// # Lifecycle for ref-counted classes
173+ /// If `T: Inherits<RefCounted>`, then the ref-counted object is not yet fully-initialized at the time of the `init` function running.
174+ /// Accessing the base object without further measures would be dangerous. Here, godot-rust employs a workaround: the `Base` object (which
175+ /// holds a weak pointer to the actual instance) is temporarily upgraded to a strong pointer, preventing use-after-free.
176+ ///
177+ /// This additional reference is automatically dropped at an implementation-defined point in time (which may change, and technically delay
178+ /// destruction of your object as soon as you use `Base::to_init_gd()`). Right now, this refcount-decrement is deferred to the next frame.
179+ ///
180+ /// For now, ref-counted bases can only use `to_init_gd()` on the main thread.
181+ ///
182+ /// # Panics (Debug)
183+ /// If called outside an initialization function, or for ref-counted objects on a non-main thread.
184+ #[ cfg( since_api = "4.2" ) ]
185+ pub fn to_init_gd ( & self ) -> Gd < T > {
186+ #[ cfg( debug_assertions) ] // debug_assert! still checks existence of symbols.
187+ assert ! (
188+ self . is_initializing( ) ,
189+ "Base::to_init_gd() can only be called during object initialization, inside I*::init() or Gd::from_init_fn()"
190+ ) ;
191+
192+ // For manually-managed objects, regular clone is fine.
193+ // Only static type matters, because this happens immediately after initialization, so T is both static and dynamic type.
194+ if !<T :: Memory as bounds:: Memory >:: IS_REF_COUNTED {
195+ return Gd :: clone ( & self . obj ) ;
196+ }
197+
198+ debug_assert ! (
199+ sys:: is_main_thread( ) ,
200+ "Base::to_init_gd() can only be called on the main thread for ref-counted objects (for now)"
201+ ) ;
202+
203+ // First time handing out a Gd<T>, we need to take measures to temporarily upgrade the Base's weak pointer to a strong one.
204+ // During the initialization phase (derived object being constructed), increment refcount by 1.
205+ if self . extra_strong_ref . borrow ( ) . is_none ( ) {
206+ let strong_ref = unsafe { Gd :: from_obj_sys ( self . obj . obj_sys ( ) ) } ;
207+ * self . extra_strong_ref . borrow_mut ( ) = Some ( strong_ref) ;
208+ }
209+
210+ // Can't use Gd::apply_deferred(), as that implicitly borrows &mut self, causing a "destroyed while bind was active" panic.
211+ let name = format ! ( "Base<{}> deferred unref" , T :: class_name( ) ) ;
212+ let rc = Rc :: clone ( & self . extra_strong_ref ) ;
213+ let callable = Callable :: from_once_fn ( & name, move |_args| {
214+ Self :: drop_strong_ref ( rc) ;
215+ Ok ( Variant :: nil ( ) )
216+ } ) ;
217+ callable. call_deferred ( & [ ] ) ;
218+
219+ ( * self . obj ) . clone ( )
220+ }
221+
222+ /// Drops any extra strong references, possibly causing object destruction.
223+ fn drop_strong_ref ( extra_strong_ref : Rc < RefCell < Option < Gd < T > > > > ) {
224+ let mut r = extra_strong_ref. borrow_mut ( ) ;
225+ assert ! ( r. is_some( ) ) ;
226+
227+ * r = None ; // Triggers RawGd::drop() -> dec-ref -> possibly object destruction.
228+ }
229+
230+ /// Finalizes the initialization of this `Base<T>` and returns whether
231+ pub ( crate ) fn mark_initialized ( & mut self ) -> bool {
232+ #[ cfg( debug_assertions) ]
233+ {
234+ assert_eq ! (
235+ self . init_state. get( ) ,
236+ InitState :: ObjectConstructing ,
237+ "Base<T> is already initialized, or holds a script instance"
238+ ) ;
239+
240+ self . init_state . set ( InitState :: ObjectInitialized ) ;
241+ }
242+
243+ self . extra_strong_ref . borrow ( ) . is_some ( )
244+ }
245+
246+ /// Returns a [`Gd`] referencing the base object, assuming the derived object is fully constructed.
247+ #[ doc( hidden) ]
248+ pub fn __fully_constructed_gd ( & self ) -> Gd < T > {
249+ #[ cfg( debug_assertions) ] // debug_assert! still checks existence of symbols.
250+ assert ! (
251+ !self . is_initializing( ) ,
252+ "WithBaseField::to_gd(), base(), base_mut() can only be called on fully-constructed objects, after I*::init() or Gd::from_init_fn()"
253+ ) ;
254+
255+ ( * self . obj ) . clone ( )
256+ }
257+
258+ /// Returns a [`Gd`] referencing the base object, for use in script contexts only.
259+ #[ doc( hidden) ]
260+ pub fn __script_gd ( & self ) -> Gd < T > {
261+ // Used internally by `SiMut::base()` and `SiMut::base_mut()` for script re-entrancy.
262+ // Could maybe add debug validation to ensure script context in the future.
263+ ( * self . obj ) . clone ( )
264+ }
265+
101266 // Currently only used in outbound virtual calls (for scripts); search for: base_field(self).obj_sys().
102267 #[ doc( hidden) ]
103268 pub fn obj_sys ( & self ) -> sys:: GDExtensionObjectPtr {
@@ -109,6 +274,36 @@ impl<T: GodotClass> Base<T> {
109274 pub ( crate ) fn debug_instance_id ( & self ) -> crate :: obj:: InstanceId {
110275 self . obj . instance_id ( )
111276 }
277+
278+ /// Returns a [`Gd`] referencing the base object, for use in script contexts only.
279+ pub ( crate ) fn to_script_gd ( & self ) -> Gd < T > {
280+ #[ cfg( debug_assertions) ]
281+ assert_eq ! (
282+ self . init_state. get( ) ,
283+ InitState :: Script ,
284+ "to_script_gd() can only be called on script-context Base objects"
285+ ) ;
286+
287+ ( * self . obj ) . clone ( )
288+ }
289+
290+ /// Returns `true` if this `Base<T>` is currently in the initializing state.
291+ #[ cfg( debug_assertions) ]
292+ fn is_initializing ( & self ) -> bool {
293+ self . init_state . get ( ) == InitState :: ObjectConstructing
294+ }
295+
296+ /// Returns a [`Gd`] referencing the base object, assuming the derived object is fully constructed.
297+ #[ doc( hidden) ]
298+ pub fn __constructed_gd ( & self ) -> Gd < T > {
299+ #[ cfg( debug_assertions) ] // debug_assert! still checks existence of symbols.
300+ assert ! (
301+ !self . is_initializing( ) ,
302+ "WithBaseField::to_gd(), base(), base_mut() can only be called on fully-constructed objects, after I*::init() or Gd::from_init_fn()"
303+ ) ;
304+
305+ ( * self . obj ) . clone ( )
306+ }
112307}
113308
114309impl < T : GodotClass > Debug for Base < T > {
0 commit comments