Skip to content

Commit bbb5135

Browse files
committed
base() + base_mut() now use weak references
1 parent 5ee7c97 commit bbb5135

File tree

4 files changed

+56
-14
lines changed

4 files changed

+56
-14
lines changed

godot-core/src/obj/base.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,8 @@ impl<T: GodotClass> Base<T> {
314314
"to_script_gd() can only be called on script-context Base objects"
315315
);
316316

317-
(*self.obj).clone()
317+
// SAFETY: ScriptBase{Ref,Mut} also drop weakly.
318+
unsafe { (*self.obj).clone_weak() }
318319
}
319320

320321
/// Returns `true` if this `Base<T>` is currently in the initializing state.
@@ -334,6 +335,22 @@ impl<T: GodotClass> Base<T> {
334335

335336
(*self.obj).clone()
336337
}
338+
339+
/// Returns a weak [`Gd`] referencing the base object, assuming the derived object is fully constructed.
340+
///
341+
/// Unlike `__constructed_gd()`, this does not increment the reference count for ref-counted `T`s.
342+
/// The returned weak reference is safe to use only as long as the associated instance remains alive.
343+
#[doc(hidden)]
344+
pub fn __constructed_gd_weak(&self) -> Gd<T> {
345+
#[cfg(debug_assertions)] // debug_assert! still checks existence of symbols.
346+
assert!(
347+
!self.is_initializing(),
348+
"WithBaseField::base(), base_mut() can only be called on fully-constructed objects, after I*::init() or Gd::from_init_fn()"
349+
);
350+
351+
// Create weak reference from the same object pointer without cloning (incrementing refcount).
352+
unsafe { Gd::from_obj_sys_weak(self.obj.obj_sys()) }
353+
}
337354
}
338355

339356
impl<T: GodotClass> Debug for Base<T> {

godot-core/src/obj/gd.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,15 @@ impl<T: GodotClass> Gd<T> {
335335
Some(rc.map(|i| i as usize))
336336
}
337337

338+
/// Create a non-owning pointer from this.
339+
///
340+
/// # Safety
341+
/// Must be destroyed with [`drop_weak()`][Self::drop_weak]; regular `Drop` will cause use-after-free.
342+
pub(crate) unsafe fn clone_weak(&self) -> Self {
343+
// SAFETY: delegated to caller.
344+
unsafe { Gd::from_obj_sys_weak(self.obj_sys()) }
345+
}
346+
338347
/// Drop without decrementing ref-counter.
339348
///
340349
/// Needed in situations where the instance should effectively be forgotten, but without leaking other associated data.

godot-core/src/obj/guards.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -199,24 +199,32 @@ macro_rules! make_base_ref {
199199
#[doc = concat!("This can be used to call methods on the base object of a ", $object_name, " that takes `&self` as the receiver.\n\n")]
200200
#[doc = concat!("See [`", stringify!($doc_type), "::base()`](", stringify!($doc_path), "::base()) for usage.")]
201201
pub struct $ident<'a, T: $bound> {
202-
gd: Gd<T::Base>,
202+
// Weak reference to the base object. Safe because 'a lifetime keeps the object (strong ref) alive.
203+
// Option because Gd::drop_weak() takes ownership, thus can't use ManuallyDrop.
204+
weak_gd: Option<Gd<T::Base>>,
203205
_instance: &'a T,
204206
}
205207

206208
impl<'a, T: $bound> $ident<'a, T> {
207-
pub(crate) fn new(gd: Gd<T::Base>, instance: &'a T) -> Self {
209+
pub(crate) fn new(weak_gd: Gd<T::Base>, instance: &'a T) -> Self {
208210
Self {
209-
gd,
211+
weak_gd: Some(weak_gd),
210212
_instance: instance,
211213
}
212214
}
213215
}
214216

217+
impl<'a, T: $bound> Drop for $ident<'a, T> {
218+
fn drop(&mut self) {
219+
self.weak_gd.take().unwrap().drop_weak();
220+
}
221+
}
222+
215223
impl<T: $bound> Deref for $ident<'_, T> {
216224
type Target = Gd<T::Base>;
217225

218226
fn deref(&self) -> &Gd<T::Base> {
219-
&self.gd
227+
self.weak_gd.as_ref().unwrap()
220228
}
221229
}
222230
};
@@ -232,33 +240,41 @@ macro_rules! make_base_mut {
232240
///
233241
#[doc = concat!("See [`", stringify!($doc_type), "::base_mut()`](", stringify!($doc_path), "::base_mut()) for usage.\n")]
234242
pub struct $ident<'a, T: $bound> {
235-
gd: Gd<T::Base>,
243+
// Weak reference to the base object. Safe because 'a lifetime keeps the object (strong ref) alive.
244+
// Option because Gd::drop_weak() takes ownership, thus can't use ManuallyDrop.
245+
weak_gd: Option<Gd<T::Base>>,
236246
_inaccessible_guard: InaccessibleGuard<'a, T>,
237247
}
238248

239249
impl<'a, T: $bound> $ident<'a, T> {
240250
pub(crate) fn new(
241-
gd: Gd<T::Base>,
251+
weak_gd: Gd<T::Base>,
242252
inaccessible_guard: InaccessibleGuard<'a, T>,
243253
) -> Self {
244254
Self {
245-
gd,
255+
weak_gd: Some(weak_gd),
246256
_inaccessible_guard: inaccessible_guard,
247257
}
248258
}
249259
}
250260

261+
impl<'a, T: $bound> Drop for $ident<'a, T> {
262+
fn drop(&mut self) {
263+
self.weak_gd.take().unwrap().drop_weak();
264+
}
265+
}
266+
251267
impl<T: $bound> Deref for $ident<'_, T> {
252268
type Target = Gd<T::Base>;
253269

254270
fn deref(&self) -> &Gd<T::Base> {
255-
&self.gd
271+
self.weak_gd.as_ref().unwrap()
256272
}
257273
}
258274

259275
impl<T: $bound> DerefMut for $ident<'_, T> {
260276
fn deref_mut(&mut self) -> &mut Gd<T::Base> {
261-
&mut self.gd
277+
self.weak_gd.as_mut().unwrap()
262278
}
263279
}
264280
};

godot-core/src/obj/traits.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,9 +422,9 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
422422
///
423423
/// For this, use [`base_mut()`](WithBaseField::base_mut()) instead.
424424
fn base(&self) -> BaseRef<'_, Self> {
425-
let gd = self.base_field().__constructed_gd();
425+
let weak_gd = self.base_field().__constructed_gd_weak();
426426

427-
BaseRef::new(gd, self)
427+
BaseRef::new(weak_gd, self)
428428
}
429429

430430
/// Returns a mutable reference suitable for calling engine methods on this object.
@@ -493,7 +493,7 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
493493
/// ```
494494
#[allow(clippy::let_unit_value)]
495495
fn base_mut(&mut self) -> BaseMut<'_, Self> {
496-
let base_gd = self.base_field().__constructed_gd();
496+
let weak_gd = self.base_field().__constructed_gd_weak();
497497

498498
let gd = self.to_gd();
499499
// SAFETY:
@@ -514,7 +514,7 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
514514

515515
let guard = storage.get_inaccessible(self);
516516

517-
BaseMut::new(base_gd, guard)
517+
BaseMut::new(weak_gd, guard)
518518
}
519519
}
520520

0 commit comments

Comments
 (0)