5
5
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
6
*/
7
7
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 ;
8
13
use std:: fmt:: { Debug , Display , Formatter , Result as FmtResult } ;
9
14
use std:: mem:: ManuallyDrop ;
15
+ use std:: rc:: Rc ;
10
16
11
- use crate :: obj:: { Gd , GodotClass } ;
17
+ use crate :: builtin:: { Callable , Variant } ;
18
+ use crate :: obj:: { bounds, Gd , GodotClass , InstanceId } ;
12
19
use crate :: { classes, sys} ;
13
20
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
+
14
57
/// Restricted version of `Gd`, to hold the base instance inside a user's `GodotClass`.
15
58
///
16
59
/// 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> {
35
78
// 1. Gd<T> -- triggers InstanceStorage destruction
36
79
// 2.
37
80
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 > > ,
38
87
}
39
88
40
89
impl < T : GodotClass > Base < T > {
@@ -47,7 +96,14 @@ impl<T: GodotClass> Base<T> {
47
96
/// If `base` is destroyed while the returned `Base<T>` is in use, that constitutes a logic error, not a safety issue.
48
97
pub ( crate ) unsafe fn from_base ( base : & Base < T > ) -> Base < T > {
49
98
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
+ }
51
107
}
52
108
53
109
/// Create base from existing object (used in script instances).
@@ -57,9 +113,11 @@ impl<T: GodotClass> Base<T> {
57
113
/// # Safety
58
114
/// `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
59
115
/// 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 {
61
117
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 )
63
121
}
64
122
65
123
/// Create new base from raw Godot object.
@@ -72,17 +130,26 @@ impl<T: GodotClass> Base<T> {
72
130
pub ( crate ) unsafe fn from_sys ( base_ptr : sys:: GDExtensionObjectPtr ) -> Self {
73
131
assert ! ( !base_ptr. is_null( ) , "instance base is null pointer" ) ;
74
132
75
- // Initialize only as weak pointer (don't increment reference count)
133
+ // Initialize only as weak pointer (don't increment reference count).
76
134
let obj = Gd :: from_obj_sys_weak ( base_ptr) ;
77
135
78
136
// This obj does not contribute to the strong count, otherwise we create a reference cycle:
79
137
// 1. RefCounted (dropped in GDScript)
80
138
// 2. holds user T (via extension instance and storage)
81
139
// 3. holds Base<T> RefCounted (last ref, dropped in T destructor, but T is never destroyed because this ref keeps storage alive)
82
140
// 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 )
84
142
}
85
143
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) ) ]
86
153
fn from_obj ( obj : Gd < T > ) -> Self {
87
154
Self {
88
155
obj : ManuallyDrop :: new ( obj) ,
@@ -94,10 +161,132 @@ impl<T: GodotClass> Base<T> {
94
161
/// Using this method to call methods on the base field of a Rust object is discouraged, instead use the
95
162
/// methods from [`WithBaseField`](super::WithBaseField) when possible.
96
163
#[ doc( hidden) ]
164
+ #[ deprecated = "Private API. Use `Base::to_init_gd()` or `WithBaseField::to_gd()` instead." ] // TODO(v0.4): remove.
97
165
pub fn to_gd ( & self ) -> Gd < T > {
98
166
( * self . obj ) . clone ( )
99
167
}
100
168
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
+
101
290
// Currently only used in outbound virtual calls (for scripts); search for: base_field(self).obj_sys().
102
291
#[ doc( hidden) ]
103
292
pub fn obj_sys ( & self ) -> sys:: GDExtensionObjectPtr {
@@ -109,6 +298,36 @@ impl<T: GodotClass> Base<T> {
109
298
pub ( crate ) fn debug_instance_id ( & self ) -> crate :: obj:: InstanceId {
110
299
self . obj . instance_id ( )
111
300
}
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
+ }
112
331
}
113
332
114
333
impl < T : GodotClass > Debug for Base < T > {
0 commit comments