godot_core/obj/base.rs
1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8#[cfg(debug_assertions)] #[cfg_attr(published_docs, doc(cfg(debug_assertions)))]
9use std::cell::Cell;
10use std::cell::RefCell;
11use std::collections::hash_map::Entry;
12use std::collections::HashMap;
13use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
14use std::mem::ManuallyDrop;
15use std::rc::Rc;
16
17use crate::builtin::Callable;
18use crate::obj::{bounds, Gd, GodotClass, InstanceId, PassiveGd};
19use crate::{classes, sys};
20
21thread_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)] #[cfg_attr(published_docs, doc(cfg(debug_assertions)))]
31#[derive(Debug, Copy, Clone, PartialEq, Eq)]
32enum 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)] #[cfg_attr(published_docs, doc(cfg(debug_assertions)))]
42macro_rules! base_from_obj {
43 ($obj:expr, $state:expr) => {
44 Base::from_obj($obj, $state)
45 };
46}
47
48#[cfg(not(debug_assertions))] #[cfg_attr(published_docs, doc(cfg(not(debug_assertions))))]
49macro_rules! base_from_obj {
50 ($obj:expr, $state:expr) => {
51 Base::from_obj($obj)
52 };
53}
54
55// ----------------------------------------------------------------------------------------------------------------------------------------------
56
57/// Restricted version of `Gd`, to hold the base instance inside a user's `GodotClass`.
58///
59/// Behaves similarly to [`Gd`][crate::obj::Gd], but is more constrained. Cannot be constructed by the user.
60pub struct Base<T: GodotClass> {
61 // Like `Gd`, it's theoretically possible that Base is destroyed while there are still other Gd pointers to the underlying object. This is
62 // safe, may however lead to unintended behavior. The base_test.rs file checks some of these scenarios.
63
64 // Internal smart pointer is never dropped. It thus acts like a weak pointer and is needed to break reference cycles between Gd<T>
65 // and the user instance owned by InstanceStorage.
66 //
67 // There is no data apart from the opaque bytes, so no memory or resources to deallocate.
68 // When triggered by Godot/GDScript, the destruction order is as follows:
69 // 1. Most-derived Godot class (C++)
70 // ...
71 // 2. RefCounted (C++)
72 // 3. Object (C++) -- this triggers InstanceStorage destruction
73 // 4. Base<T>
74 // 5. User struct (GodotClass implementation)
75 // 6. InstanceStorage
76 //
77 // When triggered by Rust (Gd::drop on last strong ref), it's as follows:
78 // 1. Gd<T> -- triggers InstanceStorage destruction
79 // 2.
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)] #[cfg_attr(published_docs, doc(cfg(debug_assertions)))]
86 init_state: Rc<Cell<InitState>>,
87}
88
89impl<T: GodotClass> Base<T> {
90 /// "Copy constructor": allows to share a `Base<T>` weak pointer.
91 ///
92 /// The return value is a weak pointer, so it will not keep the instance alive.
93 ///
94 /// # Safety
95 /// `base` must be alive at the time of invocation, i.e. user `init()` (which could technically destroy it) must not have run yet.
96 /// If `base` is destroyed while the returned `Base<T>` is in use, that constitutes a logic error, not a safety issue.
97 pub(crate) unsafe fn from_base(base: &Base<T>) -> Base<T> {
98 debug_assert!(base.obj.is_instance_valid());
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)] #[cfg_attr(published_docs, doc(cfg(debug_assertions)))]
105 init_state: Rc::clone(&base.init_state),
106 }
107 }
108
109 /// Create base from existing object (used in script instances).
110 ///
111 /// The return value is a weak pointer, so it will not keep the instance alive.
112 ///
113 /// # Safety
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
115 /// error, not a safety issue.
116 pub(crate) unsafe fn from_script_gd(gd: &Gd<T>) -> Self {
117 debug_assert!(gd.is_instance_valid());
118
119 let obj = Gd::from_obj_sys_weak(gd.obj_sys());
120 base_from_obj!(obj, InitState::Script)
121 }
122
123 /// Create new base from raw Godot object.
124 ///
125 /// The return value is a weak pointer, so it will not keep the instance alive.
126 ///
127 /// # Safety
128 /// `base_ptr` must point to a valid, live object at the time of invocation. If it is destroyed while the returned `Base<T>` is in use,
129 /// that constitutes a logic error, not a safety issue.
130 pub(crate) unsafe fn from_sys(base_ptr: sys::GDExtensionObjectPtr) -> Self {
131 assert!(!base_ptr.is_null(), "instance base is null pointer");
132
133 // Initialize only as weak pointer (don't increment reference count).
134 let obj = Gd::from_obj_sys_weak(base_ptr);
135
136 // This obj does not contribute to the strong count, otherwise we create a reference cycle:
137 // 1. RefCounted (dropped in GDScript)
138 // 2. holds user T (via extension instance and storage)
139 // 3. holds Base<T> RefCounted (last ref, dropped in T destructor, but T is never destroyed because this ref keeps storage alive)
140 // Note that if late-init never happened on self, we have the same behavior (still a raw pointer instead of weak Gd)
141 base_from_obj!(obj, InitState::ObjectConstructing)
142 }
143
144 #[cfg(debug_assertions)] #[cfg_attr(published_docs, doc(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))] #[cfg_attr(published_docs, doc(cfg(not(debug_assertions))))]
153 fn from_obj(obj: Gd<T>) -> Self {
154 Self {
155 obj: ManuallyDrop::new(obj),
156 }
157 }
158
159 /// Returns a [`Gd`] referencing the base object, for exclusive use during object initialization and `NOTIFICATION_POSTINITIALIZE`.
160 ///
161 /// Can be used during an initialization function [`I*::init()`][crate::classes::IObject::init] or [`Gd::from_init_fn()`], or [`POSTINITIALIZE`][crate::classes::notify::ObjectNotification::POSTINITIALIZE].
162 ///
163 /// The base pointer is only pointing to a base object; you cannot yet downcast it to the object being constructed.
164 /// The instance ID is the same as the one the in-construction object will have.
165 ///
166 /// # Lifecycle for ref-counted classes
167 /// If `T: Inherits<RefCounted>`, then the ref-counted object is not yet fully-initialized at the time of the `init` function and [`POSTINITIALIZE`][crate::classes::notify::ObjectNotification::POSTINITIALIZE] running.
168 /// Accessing the base object without further measures would be dangerous. Here, godot-rust employs a workaround: the `Base` object (which
169 /// holds a weak pointer to the actual instance) is temporarily upgraded to a strong pointer, preventing use-after-free.
170 ///
171 /// This additional reference is automatically dropped at an implementation-defined point in time (which may change, and technically delay
172 /// destruction of your object as soon as you use `Base::to_init_gd()`). Right now, this refcount-decrement is deferred to the next frame.
173 ///
174 /// For now, ref-counted bases can only use `to_init_gd()` on the main thread.
175 ///
176 /// # Panics (Debug)
177 /// If called outside an initialization function, or for ref-counted objects on a non-main thread.
178 pub fn to_init_gd(&self) -> Gd<T> {
179 #[cfg(debug_assertions)] // debug_assert! still checks existence of symbols.
180 assert!(
181 self.is_initializing(),
182 "Base::to_init_gd() can only be called during object initialization, inside I*::init() or Gd::from_init_fn()"
183 );
184
185 // For manually-managed objects, regular clone is fine.
186 // Only static type matters, because this happens immediately after initialization, so T is both static and dynamic type.
187 if !<T::Memory as bounds::Memory>::IS_REF_COUNTED {
188 return Gd::clone(&self.obj);
189 }
190
191 debug_assert!(
192 sys::is_main_thread(),
193 "Base::to_init_gd() can only be called on the main thread for ref-counted objects (for now)"
194 );
195
196 // First time handing out a Gd<T>, we need to take measures to temporarily upgrade the Base's weak pointer to a strong one.
197 // During the initialization phase (derived object being constructed), increment refcount by 1.
198 let instance_id = self.obj.instance_id();
199 PENDING_STRONG_REFS.with(|refs| {
200 let mut pending_refs = refs.borrow_mut();
201 if let Entry::Vacant(e) = pending_refs.entry(instance_id) {
202 let strong_ref: Gd<T> = unsafe { Gd::from_obj_sys(self.obj.obj_sys()) };
203
204 // We know that T: Inherits<RefCounted> due to check above, but don't it as a static bound for Gd::upcast().
205 // Thus fall back to low-level FFI cast on RawGd.
206 let strong_ref_raw = strong_ref.raw;
207 let raw = strong_ref_raw
208 .ffi_cast::<classes::RefCounted>()
209 .expect("Base must be RefCounted")
210 .into_dest(strong_ref_raw);
211
212 e.insert(Gd { raw });
213 }
214 });
215
216 let name = format!("Base<{}> deferred unref", T::class_id());
217 let callable = Callable::from_once_fn(&name, move |_args| {
218 Self::drop_strong_ref(instance_id);
219 });
220
221 // Use Callable::call_deferred() instead of Gd::apply_deferred(). The latter implicitly borrows &mut self,
222 // causing a "destroyed while bind was active" panic.
223 callable.call_deferred(&[]);
224
225 (*self.obj).clone()
226 }
227
228 /// Drops any extra strong references, possibly causing object destruction.
229 fn drop_strong_ref(instance_id: InstanceId) {
230 PENDING_STRONG_REFS.with(|refs| {
231 let mut pending_refs = refs.borrow_mut();
232 let strong_ref = pending_refs.remove(&instance_id);
233 assert!(
234 strong_ref.is_some(),
235 "Base unexpectedly had its strong ref rug-pulled"
236 );
237
238 // Editor creates instances of given class for various purposes (getting class docs, default values...)
239 // and frees them instantly before our callable can be executed.
240 // Perform "weak" drop instead of "strong" one iff our instance is no longer valid.
241 if !instance_id.lookup_validity() {
242 strong_ref.unwrap().drop_weak();
243 }
244
245 // Triggers RawGd::drop() -> dec-ref -> possibly object destruction.
246 });
247 }
248
249 /// Finalizes the initialization of this `Base<T>` and returns whether
250 pub(crate) fn mark_initialized(&mut self) {
251 #[cfg(debug_assertions)]
252 {
253 assert_eq!(
254 self.init_state.get(),
255 InitState::ObjectConstructing,
256 "Base<T> is already initialized, or holds a script instance"
257 );
258
259 self.init_state.set(InitState::ObjectInitialized);
260 }
261
262 // May return whether there is a "surplus" strong ref in the future, as self.extra_strong_ref.borrow().is_some().
263 }
264
265 /// Returns a [`Gd`] referencing the base object, assuming the derived object is fully constructed.
266 #[doc(hidden)]
267 pub fn __fully_constructed_gd(&self) -> Gd<T> {
268 #[cfg(debug_assertions)] // debug_assert! still checks existence of symbols.
269 assert!(
270 !self.is_initializing(),
271 "WithBaseField::to_gd(), base(), base_mut() can only be called on fully-constructed objects, after I*::init() or Gd::from_init_fn()"
272 );
273
274 (*self.obj).clone()
275 }
276
277 /// Returns a [`Gd`] referencing the base object, for use in script contexts only.
278 #[doc(hidden)]
279 pub fn __script_gd(&self) -> Gd<T> {
280 // Used internally by `SiMut::base()` and `SiMut::base_mut()` for script re-entrancy.
281 // Could maybe add debug validation to ensure script context in the future.
282 (*self.obj).clone()
283 }
284
285 // Currently only used in outbound virtual calls (for scripts); search for: base_field(self).obj_sys().
286 #[doc(hidden)]
287 pub fn obj_sys(&self) -> sys::GDExtensionObjectPtr {
288 self.obj.obj_sys()
289 }
290
291 // Internal use only, do not make public.
292 #[cfg(feature = "debug-log")] #[cfg_attr(published_docs, doc(cfg(feature = "debug-log")))]
293 pub(crate) fn debug_instance_id(&self) -> crate::obj::InstanceId {
294 self.obj.instance_id()
295 }
296
297 /// Returns a passive reference to the base object, for use in script contexts only.
298 pub(crate) fn to_script_passive(&self) -> PassiveGd<T> {
299 #[cfg(debug_assertions)] #[cfg_attr(published_docs, doc(cfg(debug_assertions)))]
300 assert_eq!(
301 self.init_state.get(),
302 InitState::Script,
303 "to_script_passive() can only be called on script-context Base objects"
304 );
305
306 // SAFETY: the object remains valid for script contexts as per the assertion above.
307 unsafe { PassiveGd::from_strong_ref(&self.obj) }
308 }
309
310 /// Returns `true` if this `Base<T>` is currently in the initializing state.
311 #[cfg(debug_assertions)] #[cfg_attr(published_docs, doc(cfg(debug_assertions)))]
312 fn is_initializing(&self) -> bool {
313 self.init_state.get() == InitState::ObjectConstructing
314 }
315
316 /// Returns a [`Gd`] referencing the base object, assuming the derived object is fully constructed.
317 #[doc(hidden)]
318 pub fn __constructed_gd(&self) -> Gd<T> {
319 #[cfg(debug_assertions)] // debug_assert! still checks existence of symbols.
320 assert!(
321 !self.is_initializing(),
322 "WithBaseField::to_gd(), base(), base_mut() can only be called on fully-constructed objects, after I*::init() or Gd::from_init_fn()"
323 );
324
325 (*self.obj).clone()
326 }
327
328 /// Returns a [`PassiveGd`] referencing the base object, assuming the derived object is fully constructed.
329 ///
330 /// Unlike [`Self::__constructed_gd()`], this does not increment the reference count for ref-counted `T`s.
331 /// The returned weak reference is safe to use only as long as the associated instance remains alive.
332 ///
333 /// # Safety
334 /// Caller must ensure that the underlying object remains valid for the entire lifetime of the returned `PassiveGd`.
335 pub(crate) unsafe fn constructed_passive(&self) -> PassiveGd<T> {
336 #[cfg(debug_assertions)] // debug_assert! still checks existence of symbols.
337 assert!(
338 !self.is_initializing(),
339 "WithBaseField::base(), base_mut() can only be called on fully-constructed objects, after I*::init() or Gd::from_init_fn()"
340 );
341
342 // SAFETY: object pointer is valid and remains valid as long as self is alive (per safety precondition of this fn).
343 unsafe { PassiveGd::from_obj_sys(self.obj.obj_sys()) }
344 }
345}
346
347impl<T: GodotClass> Debug for Base<T> {
348 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
349 classes::debug_string(&self.obj, f, "Base")
350 }
351}
352
353impl<T: GodotClass> Display for Base<T> {
354 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
355 classes::display_string(&self.obj, f)
356 }
357}