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