1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/*
* Copyright (c) godot-rust; Bromeon and contributors.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::mem::ManuallyDrop;
use crate::obj::base_init::{InitState, InitTracker};
use crate::obj::{Gd, GodotClass, PassiveGd};
use crate::{classes, sys};
macro_rules! base_from_obj {
($obj:expr_2021, $state:expr_2021) => {
Base::from_obj($obj, $state)
};
}
// ----------------------------------------------------------------------------------------------------------------------------------------------
/// Restricted version of `Gd`, to hold the base instance inside a user's `GodotClass`.
///
/// Behaves similarly to [`Gd`][crate::obj::Gd], but is more constrained. Cannot be constructed by the user.
pub struct Base<T: GodotClass> {
// Like `Gd`, it's theoretically possible that Base is destroyed while there are still other Gd pointers to the underlying object. This is
// safe, may however lead to unintended behavior. The base_test.rs file checks some of these scenarios.
// Internal smart pointer is never dropped. It thus acts like a weak pointer and is needed to break reference cycles between Gd<T>
// and the user instance owned by InstanceStorage.
//
// There is no data apart from the opaque bytes, so no memory or resources to deallocate.
// When triggered by Godot/GDScript, the destruction order is as follows:
// 1. Most-derived Godot class (C++)
// ...
// 2. RefCounted (C++)
// 3. Object (C++) -- this triggers InstanceStorage destruction
// 4. Base<T>
// 5. User struct (GodotClass implementation)
// 6. InstanceStorage
//
// When triggered by Rust (Gd::drop on last strong ref), it's as follows:
// 1. Gd<T> -- triggers InstanceStorage destruction
// 2.
pub(super) obj: ManuallyDrop<Gd<T>>,
pub(super) init_state: InitTracker,
}
impl<T: GodotClass> Base<T> {
/// "Copy constructor": allows to share a `Base<T>` weak pointer.
///
/// The return value is a weak pointer, so it will not keep the instance alive.
///
/// # Safety
/// `base` must be alive at the time of invocation, i.e. user `init()` (which could technically destroy it) must not have run yet.
/// If `base` is destroyed while the returned `Base<T>` is in use, that constitutes a logic error, not a safety issue.
pub(crate) unsafe fn from_base(base: &Base<T>) -> Base<T> {
sys::balanced_assert!(
Base::is_valid(base),
"Cannot construct Base; was object freed during initialization?"
);
// SAFETY: See method docs.
let obj = unsafe { Gd::from_obj_sys_weak(base.obj.obj_sys()) };
Self {
obj: ManuallyDrop::new(obj),
init_state: base.init_state.clone(),
}
}
/// Checks if this base points to a live object.
pub(crate) fn is_valid(base: &Base<T>) -> bool {
base.obj.is_instance_valid()
}
/// Workaround for `base` being unitialized during object initialization and `NOTIFICATION_POSTINITIALIZE`
/// for Godot versions before 4.7.
///
/// # Behaviour after Godot 4.7
///
/// Since Godot 4.7 initialization layer receives fully-constructed object to work with – therefore in Godot 4.7 and later
/// this method simply returns a clone of a given instance.
///
/// Use this method if you want to support Godot versions older than 4.7.
///
/// # Behaviour before Godot 4.7
///
/// Returns a [`Gd`] referencing the base object, for exclusive use during object initialization and `NOTIFICATION_POSTINITIALIZE`.
///
/// 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].
///
/// The base pointer is only pointing to a base object; you cannot yet downcast it to the object being constructed.
/// The instance ID is the same as the one the in-construction object will have.
///
/// ## Lifecycle for ref-counted classes
///
/// 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.
/// Accessing the base object without further measures would be dangerous. Here, godot-rust employs a workaround: the `Base` object (which
/// holds a weak pointer to the actual instance) is temporarily upgraded to a strong pointer, preventing use-after-free.
///
/// This additional reference is automatically dropped at an implementation-defined point in time (which may change, and technically delay
/// destruction of your object as soon as you use `Base::to_init_gd()`). Right now, this refcount-decrement is deferred to the next frame.
///
/// Ref-counted bases can only use `to_init_gd()` on the main thread.
///
/// ## Panics (Debug)
/// In Godot before 4.7, if called outside an initialization function, or for ref-counted objects on a non-main thread.
pub fn to_init_gd(&self) -> Gd<T> {
self.to_init_gd_inner()
}
/// Create base from existing object (used in script instances).
///
/// The return value is a weak pointer, so it will not keep the instance alive.
///
/// # Safety
/// `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
/// error, not a safety issue.
pub(crate) unsafe fn from_script_gd(gd: &Gd<T>) -> Self {
sys::balanced_assert!(gd.is_instance_valid());
// SAFETY: pointer is valid and remains alive while in use.
let obj = unsafe { Gd::from_obj_sys_weak(gd.obj_sys()) };
base_from_obj!(obj, InitState::Script)
}
/// Create new base from raw Godot object.
///
/// The return value is a weak pointer, so it will not keep the instance alive.
///
/// # Safety
/// `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,
/// that constitutes a logic error, not a safety issue.
pub(crate) unsafe fn from_sys(base_ptr: sys::GDExtensionObjectPtr) -> Self {
sys::balanced_assert!(!base_ptr.is_null(), "instance base is null pointer");
// Initialize only as weak pointer (don't increment reference count).
// SAFETY: pointer is valid and remains alive while in use.
let obj = unsafe { Gd::from_obj_sys_weak(base_ptr) };
// This obj does not contribute to the strong count, otherwise we create a reference cycle:
// 1. RefCounted (dropped in GDScript)
// 2. holds user T (via extension instance and storage)
// 3. holds Base<T> RefCounted (last ref, dropped in T destructor, but T is never destroyed because this ref keeps storage alive)
// Note that if late-init never happened on self, we have the same behavior (still a raw pointer instead of weak Gd)
base_from_obj!(obj, InitState::ObjectConstructing)
}
fn from_obj(obj: Gd<T>, init_state: InitState) -> Self {
Self {
obj: ManuallyDrop::new(obj),
init_state: InitTracker::new(init_state),
}
}
/// Returns a [`Gd`] referencing the base object, for use in script contexts only.
#[doc(hidden)]
pub fn __script_gd(&self) -> Gd<T> {
// Used internally by `SiMut::base()` and `SiMut::base_mut()` for script re-entrancy.
// Could maybe add debug validation to ensure script context in the future.
(*self.obj).clone()
}
// Currently only used in outbound virtual calls (for scripts); search for: base_field(self).obj_sys().
#[doc(hidden)]
pub fn obj_sys(&self) -> sys::GDExtensionObjectPtr {
self.obj.obj_sys()
}
// Internal use only, do not make public.
#[cfg(feature = "debug-log")] #[cfg_attr(published_docs, doc(cfg(feature = "debug-log")))]
pub(crate) fn debug_instance_id(&self) -> crate::obj::InstanceId {
self.obj.instance_id()
}
/// Returns a passive reference to the base object, for use in script contexts only.
pub(crate) fn to_script_passive(&self) -> PassiveGd<T> {
self.init_state.assert_script();
// SAFETY: the object remains valid for script contexts as per the assertion above.
unsafe { PassiveGd::from_strong_ref(&self.obj) }
}
/// Returns a [`Gd`] referencing the base object, assuming the derived object is fully constructed.
#[doc(hidden)]
pub fn __constructed_gd(&self) -> Gd<T> {
self.init_state.assert_constructed();
(*self.obj).clone()
}
/// Returns a [`PassiveGd`] referencing the base object, assuming the derived object is fully constructed.
///
/// Unlike [`Self::__constructed_gd()`], this does not increment the reference count for ref-counted `T`s.
/// The returned weak reference is safe to use only as long as the associated instance remains alive.
///
/// # Safety
/// Caller must ensure that the underlying object remains valid for the entire lifetime of the returned `PassiveGd`.
pub(crate) unsafe fn constructed_passive(&self) -> PassiveGd<T> {
self.init_state.assert_constructed();
// SAFETY: object pointer is valid and remains valid as long as self is alive (per safety precondition of this fn).
unsafe { PassiveGd::from_obj_sys(self.obj.obj_sys()) }
}
}
impl<T: GodotClass> Debug for Base<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
classes::debug_string(&self.obj, f, "Base")
}
}
impl<T: GodotClass> Display for Base<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
classes::display_string(&self.obj, f)
}
}