Skip to content

Commit e664454

Browse files
committed
Add WeakGd
1 parent e3485d2 commit e664454

File tree

7 files changed

+184
-28
lines changed

7 files changed

+184
-28
lines changed

godot-core/src/obj/base.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use std::mem::ManuallyDrop;
1515
use std::rc::Rc;
1616

1717
use crate::builtin::{Callable, Variant};
18+
use crate::obj::weak_gd::WeakGd;
1819
use crate::obj::{bounds, Gd, GodotClass, InstanceId};
1920
use crate::{classes, sys};
2021

@@ -166,6 +167,10 @@ impl<T: GodotClass> Base<T> {
166167
(*self.obj).clone()
167168
}
168169

170+
pub fn to_weak_gd(&self) -> WeakGd<T> {
171+
WeakGd::from_base(self)
172+
}
173+
169174
/// Returns a [`Gd`] referencing the base object, for exclusive use during object initialization.
170175
///
171176
/// Can be used during an initialization function [`I*::init()`][crate::classes::IObject::init] or [`Gd::from_init_fn()`].
@@ -328,6 +333,10 @@ impl<T: GodotClass> Base<T> {
328333

329334
(*self.obj).clone()
330335
}
336+
337+
pub(crate) fn is_instance_valid(&self) -> bool {
338+
self.obj.is_instance_valid()
339+
}
331340
}
332341

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

godot-core/src/obj/casts.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,6 @@ impl<T: GodotClass, U: GodotClass> CastSuccess<T, U> {
4747
}
4848
}
4949

50-
/// Access shared reference to destination, without consuming object.
51-
#[cfg(debug_assertions)]
52-
pub fn as_dest_ref(&self) -> &RawGd<U> {
53-
self.check_validity();
54-
&self.dest
55-
}
56-
5750
/// Access exclusive reference to destination, without consuming object.
5851
pub fn as_dest_mut(&mut self) -> &mut RawGd<U> {
5952
self.check_validity();

godot-core/src/obj/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod on_editor;
2222
mod on_ready;
2323
mod raw_gd;
2424
mod traits;
25+
mod weak_gd;
2526

2627
pub(crate) mod rtti;
2728

@@ -35,6 +36,7 @@ pub use on_editor::*;
3536
pub use on_ready::*;
3637
pub use raw_gd::*;
3738
pub use traits::*;
39+
pub use weak_gd::*;
3840

3941
pub mod bounds;
4042
pub mod script;

godot-core/src/obj/raw_gd.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -358,27 +358,6 @@ impl<T: GodotClass> RawGd<T> {
358358
// Validation object identity.
359359
self.check_rtti("upcast_ref");
360360
debug_assert!(!self.is_null(), "cannot upcast null object refs");
361-
362-
// In Debug builds, go the long path via Godot FFI to verify the results are the same.
363-
#[cfg(debug_assertions)]
364-
{
365-
// SAFETY: we forget the object below and do not leave the function before.
366-
let ffi_dest = self.ffi_cast::<Base>().expect("failed FFI upcast");
367-
368-
// The ID check is not that expressive; we should do a complete comparison of the ObjectRtti, but currently the dynamic types can
369-
// be different (see comment in ObjectRtti struct). This at least checks that the transmuted object is not complete garbage.
370-
// We get direct_id from Self and not Base because the latter has no API with current bounds; but this equivalence is tested in Deref.
371-
let direct_id = self.instance_id_unchecked().expect("direct_id null");
372-
let ffi_id = ffi_dest
373-
.as_dest_ref()
374-
.instance_id_unchecked()
375-
.expect("ffi_id null");
376-
377-
assert_eq!(
378-
direct_id, ffi_id,
379-
"upcast_ref: direct and FFI IDs differ. This is a bug, please report to godot-rust maintainers."
380-
);
381-
}
382361
}
383362

384363
/// Verify that the object is non-null and alive. In Debug mode, additionally verify that it is of type `T` or derived.

godot-core/src/obj/weak_gd.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
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+
use std::fmt::{Debug, Formatter, Result as FmtResult};
9+
use std::mem::ManuallyDrop;
10+
use std::ops::{Deref, DerefMut};
11+
12+
use crate::classes;
13+
use crate::obj::{bounds, Base, Bounds, Gd, GdDerefTarget, GodotClass, Inherits, RawGd};
14+
15+
/// Weak pointer to objects owned by the Godot engine.
16+
/// `WeakGd<T>` doesn't guarantee the validation of the object and can hold a dead object.
17+
/// For `RefCounted`, it doesn't affect the reference count, which means it doesn't decrease reference count when dropped and doesn't prevent the `RefCounted` from being released.
18+
/// Can be used during initialization [`I*::init()`][crate::classes::IObject::init] or [`Gd::from_init_fn()`], and deconstruction [Drop].
19+
///
20+
/// # Panics
21+
/// If the weak pointer is invalid when dereferencing to call a Godot method.
22+
pub struct WeakGd<T: GodotClass> {
23+
gd: Option<ManuallyDrop<Gd<T>>>,
24+
}
25+
26+
impl<T: GodotClass> WeakGd<T> {
27+
fn from_raw_gd(val: &RawGd<T>) -> Self {
28+
Self {
29+
gd: if val.is_instance_valid() {
30+
unsafe { Some(ManuallyDrop::new(Gd::from_obj_sys_weak(val.obj_sys()))) }
31+
} else {
32+
None
33+
},
34+
}
35+
}
36+
37+
/// Create a weak pointer from a [Gd].
38+
pub fn from_gd(val: &Gd<T>) -> Self {
39+
Self::from_raw_gd(&val.raw)
40+
}
41+
42+
/// Create a weak pointer from a [Base].
43+
pub fn from_base(val: &Base<T>) -> Self {
44+
Self {
45+
gd: if val.is_instance_valid() {
46+
unsafe { Some(ManuallyDrop::new(Gd::from_obj_sys_weak(val.obj_sys()))) }
47+
} else {
48+
None
49+
},
50+
}
51+
}
52+
53+
fn ffi_cast<U: GodotClass>(self) -> Result<WeakGd<U>, Self> {
54+
if !self.is_instance_valid() {
55+
return Err(WeakGd { gd: None });
56+
}
57+
let WeakGd { gd } = self;
58+
let mut gd = gd.unwrap();
59+
let Gd { raw } = unsafe { ManuallyDrop::take(&mut gd) };
60+
let cast = raw.ffi_cast::<U>();
61+
62+
let raw = match cast {
63+
Ok(res) => Ok(ManuallyDrop::new(res.into_dest(raw))),
64+
Err(_) => Err(ManuallyDrop::new(raw)),
65+
};
66+
raw.map(|v| WeakGd::from_raw_gd(&v))
67+
.map_err(|e| WeakGd::from_raw_gd(&e))
68+
}
69+
70+
/// **Downcast:** try to convert into a weak pointer to a derived class, fails if this weak pointer is invalid.
71+
pub fn try_cast<Derived>(self) -> Result<WeakGd<Derived>, Self>
72+
where
73+
Derived: Inherits<T>,
74+
{
75+
self.ffi_cast()
76+
}
77+
78+
/// **Upcast:** try to convert into a weak pointer to a base class, fails if this weak pointer is invalid.
79+
pub fn try_upcast<Base>(self) -> Result<WeakGd<Base>, Self>
80+
where
81+
Base: GodotClass,
82+
T: Inherits<Base>,
83+
{
84+
self.ffi_cast()
85+
}
86+
87+
/// Checks if this weak pointer points to a live object.
88+
pub fn is_instance_valid(&self) -> bool {
89+
self.gd
90+
.as_ref()
91+
.map(|v| v.is_instance_valid())
92+
.unwrap_or(false)
93+
}
94+
}
95+
96+
impl<T: GodotClass> Deref for WeakGd<T>
97+
where
98+
GdDerefTarget<T>: Bounds<Declarer = bounds::DeclEngine>,
99+
{
100+
type Target = GdDerefTarget<T>;
101+
102+
fn deref(&self) -> &Self::Target {
103+
self.gd.as_ref().unwrap().deref()
104+
}
105+
}
106+
107+
impl<T: GodotClass> DerefMut for WeakGd<T>
108+
where
109+
GdDerefTarget<T>: Bounds<Declarer = bounds::DeclEngine>,
110+
{
111+
fn deref_mut(&mut self) -> &mut Self::Target {
112+
self.gd.as_mut().unwrap().deref_mut()
113+
}
114+
}
115+
116+
impl<T: GodotClass> Clone for WeakGd<T> {
117+
fn clone(&self) -> Self {
118+
if let Some(gd) = self.gd.as_ref() {
119+
Self::from_gd(gd)
120+
} else {
121+
Self { gd: None }
122+
}
123+
}
124+
}
125+
126+
impl<T: GodotClass> Debug for WeakGd<T> {
127+
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
128+
if self.is_instance_valid() {
129+
classes::debug_string(self.gd.as_ref().unwrap(), f, "WeakGd")
130+
} else {
131+
write!(f, "WeakGd {{ null }}")
132+
}
133+
}
134+
}

itest/rust/src/object_tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ mod base_init_test;
3535
mod validate_property_test;
3636
mod virtual_methods_niche_test;
3737
mod virtual_methods_test;
38+
mod weak_gd_test;
3839

3940
// Need to test this in the init level method.
4041
pub use init_level_test::initialize_init_level_test;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
use godot::obj::WeakGd;
9+
use godot::prelude::*;
10+
11+
use crate::framework::itest;
12+
13+
#[derive(GodotClass, Debug)]
14+
#[class(base=RefCounted)]
15+
struct RefcBasedDrop {
16+
pub base: Base<RefCounted>,
17+
}
18+
19+
#[godot_api]
20+
impl IRefCounted for RefcBasedDrop {
21+
fn init(base: Base<RefCounted>) -> Self {
22+
let mut obj: WeakGd<RefcBasedDrop> = base.to_weak_gd().try_cast().unwrap();
23+
obj.set_meta("meta", &"inited".to_variant());
24+
Self { base }
25+
}
26+
}
27+
28+
impl Drop for RefcBasedDrop {
29+
fn drop(&mut self) {
30+
let obj = self.base.to_weak_gd();
31+
assert_eq!(obj.get_meta("meta"), "inited".to_variant());
32+
}
33+
}
34+
35+
#[itest]
36+
fn weak_init_drop_refcounted() {
37+
RefcBasedDrop::new_gd();
38+
}

0 commit comments

Comments
 (0)