Skip to content

Commit c3e7023

Browse files
authored
Merge pull request #396 from mhoff12358/mhoff12358/gd_as_self_argument
Allow godot-visible functions to receive a Gd instead of self.
2 parents 0932417 + c28de15 commit c3e7023

File tree

8 files changed

+173
-21
lines changed

8 files changed

+173
-21
lines changed

godot-core/src/obj/base.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ pub struct Base<T: GodotClass> {
3535
}
3636

3737
impl<T: GodotClass> Base<T> {
38+
/// # Safety
39+
/// The returned Base is a weak pointer, so holding it will not keep the object alive. It must not be accessed after the object is destroyed.
40+
pub(crate) unsafe fn from_base(base: &Base<T>) -> Base<T> {
41+
Base::from_obj(Gd::from_obj_sys_weak(base.obj_sys()))
42+
}
43+
3844
// Note: not &mut self, to only borrow one field and not the entire struct
3945
pub(crate) unsafe fn from_sys(base_ptr: sys::GDExtensionObjectPtr) -> Self {
4046
assert!(!base_ptr.is_null(), "instance base is null pointer");

godot-core/src/registry.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,9 @@ pub mod callbacks {
347347
unsafe { interface_fn!(classdb_construct_object)(base_class_name.string_sys()) };
348348

349349
let base = unsafe { Base::from_sys(base_ptr) };
350-
let user_instance = make_user_instance(base);
350+
let user_instance = make_user_instance(unsafe { Base::from_base(&base) });
351351

352-
let instance = InstanceStorage::<T>::construct(user_instance);
352+
let instance = InstanceStorage::<T>::construct(user_instance, base);
353353
let instance_ptr = instance.into_raw();
354354
let instance_ptr = instance_ptr as sys::GDExtensionClassInstancePtr;
355355

godot-core/src/storage.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ mod single_threaded {
2929
use std::any::type_name;
3030
use std::cell;
3131

32-
use crate::obj::GodotClass;
32+
use crate::obj::{Base, Gd, GodotClass, Inherits};
3333
use crate::out;
3434

3535
use super::Lifecycle;
3636

3737
/// Manages storage and lifecycle of user's extension class instances.
3838
pub struct InstanceStorage<T: GodotClass> {
3939
user_instance: cell::RefCell<T>,
40+
base: Base<T::Base>,
4041

4142
// Declared after `user_instance`, is dropped last
4243
pub lifecycle: cell::Cell<Lifecycle>,
@@ -45,11 +46,12 @@ mod single_threaded {
4546

4647
/// For all Godot extension classes
4748
impl<T: GodotClass> InstanceStorage<T> {
48-
pub fn construct(user_instance: T) -> Self {
49+
pub fn construct(user_instance: T, base: Base<T::Base>) -> Self {
4950
out!(" Storage::construct <{}>", type_name::<T>());
5051

5152
Self {
5253
user_instance: cell::RefCell::new(user_instance),
54+
base,
5355
lifecycle: cell::Cell::new(Lifecycle::Alive),
5456
godot_ref_count: cell::Cell::new(1),
5557
}
@@ -101,6 +103,13 @@ mod single_threaded {
101103
})
102104
}
103105

106+
pub fn get_gd(&self) -> Gd<T>
107+
where
108+
T: Inherits<<T as GodotClass>::Base>,
109+
{
110+
self.base.clone().cast()
111+
}
112+
104113
pub(super) fn godot_ref_count(&self) -> u32 {
105114
self.godot_ref_count.get()
106115
}
@@ -113,7 +122,7 @@ mod multi_threaded {
113122
use std::sync;
114123
use std::sync::atomic::{AtomicU32, Ordering};
115124

116-
use crate::obj::GodotClass;
125+
use crate::obj::{Base, Gd, GodotClass, Inherits, Share};
117126
use crate::out;
118127

119128
use super::Lifecycle;
@@ -146,6 +155,7 @@ mod multi_threaded {
146155
/// Manages storage and lifecycle of user's extension class instances.
147156
pub struct InstanceStorage<T: GodotClass> {
148157
user_instance: sync::RwLock<T>,
158+
base: Base<T::Base>,
149159

150160
// Declared after `user_instance`, is dropped last
151161
pub lifecycle: AtomicLifecycle,
@@ -154,11 +164,12 @@ mod multi_threaded {
154164

155165
/// For all Godot extension classes
156166
impl<T: GodotClass> InstanceStorage<T> {
157-
pub fn construct(user_instance: T) -> Self {
167+
pub fn construct(user_instance: T, base: Base<T::Base>) -> Self {
158168
out!(" Storage::construct <{}>", type_name::<T>());
159169

160170
Self {
161171
user_instance: sync::RwLock::new(user_instance),
172+
base,
162173
lifecycle: AtomicLifecycle::new(Lifecycle::Alive),
163174
godot_ref_count: AtomicU32::new(1),
164175
}
@@ -206,6 +217,13 @@ mod multi_threaded {
206217
})
207218
}
208219

220+
pub fn get_gd(&self) -> Gd<T>
221+
where
222+
T: Inherits<<T as GodotClass>::Base>,
223+
{
224+
self.base.clone().cast()
225+
}
226+
209227
pub(super) fn godot_ref_count(&self) -> u32 {
210228
self.godot_ref_count.load(Ordering::Relaxed)
211229
}

godot-macros/src/class/data_models/field_var.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ impl GetterSetterImpl {
195195
FuncDefinition {
196196
func: signature,
197197
rename: None,
198+
has_gd_self: false,
198199
},
199200
);
200201

godot-macros/src/class/data_models/func.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub struct FuncDefinition {
1414
pub func: venial::Function,
1515
/// The name the function will be exposed as in Godot. If `None`, the Rust function name is used.
1616
pub rename: Option<String>,
17+
pub has_gd_self: bool,
1718
}
1819

1920
/// Returns a C function which acts as the callback when a virtual method of this instance is invoked.
@@ -24,7 +25,7 @@ pub fn make_virtual_method_callback(
2425
class_name: &Ident,
2526
method_signature: &venial::Function,
2627
) -> TokenStream {
27-
let signature_info = get_signature_info(method_signature);
28+
let signature_info = get_signature_info(method_signature, false);
2829
let method_name = &method_signature.name;
2930

3031
let wrapped_method = make_forwarding_closure(class_name, &signature_info);
@@ -54,7 +55,7 @@ pub fn make_method_registration(
5455
class_name: &Ident,
5556
func_definition: FuncDefinition,
5657
) -> TokenStream {
57-
let signature_info = get_signature_info(&func_definition.func);
58+
let signature_info = get_signature_info(&func_definition.func, func_definition.has_gd_self);
5859
let sig_tuple =
5960
util::make_signature_tuple_type(&signature_info.ret_type, &signature_info.param_types);
6061

@@ -127,6 +128,7 @@ pub fn make_method_registration(
127128
enum ReceiverType {
128129
Ref,
129130
Mut,
131+
GdSelf,
130132
Static,
131133
}
132134

@@ -167,6 +169,18 @@ fn make_forwarding_closure(class_name: &Ident, signature_info: &SignatureInfo) -
167169
}
168170
}
169171
}
172+
ReceiverType::GdSelf => {
173+
quote! {
174+
|instance_ptr, params| {
175+
let ( #(#params,)* ) = params;
176+
177+
let storage =
178+
unsafe { ::godot::private::as_storage::<#class_name>(instance_ptr) };
179+
180+
<#class_name>::#method_name(storage.get_gd(), #(#params),*)
181+
}
182+
}
183+
}
170184
ReceiverType::Static => {
171185
quote! {
172186
|_, params| {
@@ -178,9 +192,13 @@ fn make_forwarding_closure(class_name: &Ident, signature_info: &SignatureInfo) -
178192
}
179193
}
180194

181-
fn get_signature_info(signature: &venial::Function) -> SignatureInfo {
195+
fn get_signature_info(signature: &venial::Function, has_gd_self: bool) -> SignatureInfo {
182196
let method_name = signature.name.clone();
183-
let mut receiver_type = ReceiverType::Static;
197+
let mut receiver_type = if has_gd_self {
198+
ReceiverType::GdSelf
199+
} else {
200+
ReceiverType::Static
201+
};
184202
let mut param_idents: Vec<Ident> = Vec::new();
185203
let mut param_types = Vec::new();
186204
let ret_type = match &signature.return_ty {
@@ -192,6 +210,11 @@ fn get_signature_info(signature: &venial::Function) -> SignatureInfo {
192210
for (arg, _) in &signature.params.inner {
193211
match arg {
194212
venial::FnParam::Receiver(recv) => {
213+
if receiver_type == ReceiverType::GdSelf {
214+
// This shouldn't happen, as when has_gd_self is true the first function parameter should have been removed.
215+
// And the first parameter should be the only one that can be a Receiver.
216+
panic!("has_gd_self is true for a signature starting with a Receiver param.");
217+
}
195218
receiver_type = if recv.tk_mut.is_some() {
196219
ReceiverType::Mut
197220
} else if recv.tk_ref.is_some() {
@@ -228,7 +251,7 @@ fn get_signature_info(signature: &venial::Function) -> SignatureInfo {
228251

229252
fn make_method_flags(method_type: ReceiverType) -> TokenStream {
230253
match method_type {
231-
ReceiverType::Ref | ReceiverType::Mut => {
254+
ReceiverType::Ref | ReceiverType::Mut | ReceiverType::GdSelf => {
232255
quote! { ::godot::engine::global::MethodFlags::METHOD_FLAGS_DEFAULT }
233256
}
234257
ReceiverType::Static => {

godot-macros/src/class/godot_api.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ pub fn attribute_godot_api(input_decl: Declaration) -> Result<TokenStream, Error
4747

4848
/// Attribute for user-declared function
4949
enum BoundAttrType {
50-
Func { rename: Option<String> },
50+
Func {
51+
rename: Option<String>,
52+
has_gd_self: bool,
53+
},
5154
Signal(AttributeValue),
5255
Const(AttributeValue),
5356
}
@@ -230,11 +233,25 @@ fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<FuncDefinition>, Vec<Functi
230233
return attr.bail("generic fn parameters are not supported", method);
231234
}
232235

233-
match attr.ty {
234-
BoundAttrType::Func { rename } => {
236+
match &attr.ty {
237+
BoundAttrType::Func {
238+
rename,
239+
has_gd_self,
240+
} => {
235241
// Signatures are the same thing without body
236-
let sig = util::reduce_to_signature(method);
237-
func_definitions.push(FuncDefinition { func: sig, rename });
242+
let mut sig = util::reduce_to_signature(method);
243+
if *has_gd_self {
244+
if sig.params.is_empty() {
245+
return attr.bail("with attribute key `gd_self`, the method must have a first parameter of type Gd<Self>", method);
246+
} else {
247+
sig.params.inner.remove(0);
248+
}
249+
}
250+
func_definitions.push(FuncDefinition {
251+
func: sig,
252+
rename: rename.clone(),
253+
has_gd_self: *has_gd_self,
254+
});
238255
}
239256
BoundAttrType::Signal(ref _attr_val) => {
240257
if method.return_ty.is_some() {
@@ -317,11 +334,15 @@ where
317334
let mut parser = KvParser::parse(attributes, "func")?.unwrap();
318335

319336
let rename = parser.handle_expr("rename")?.map(|ts| ts.to_string());
337+
let has_gd_self = parser.handle_alone("gd_self")?;
320338

321339
Some(BoundAttr {
322340
attr_name: attr_name.clone(),
323341
index,
324-
ty: BoundAttrType::Func { rename },
342+
ty: BoundAttrType::Func {
343+
rename,
344+
has_gd_self,
345+
},
325346
})
326347
}
327348
name if name == "signal" => {

itest/godot/ManualFfiTests.gd

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,3 +304,25 @@ func test_func_rename():
304304
assert_eq(func_rename.has_method("renamed_static"), false)
305305
assert_eq(func_rename.has_method("spell_static"), true)
306306
assert_eq(func_rename.spell_static(), "static")
307+
308+
var gd_self_reference: GdSelfReference
309+
func update_self_reference(value):
310+
gd_self_reference.update_internal(value)
311+
312+
# Todo: Once there is a way to assert for a SCRIPT ERROR failure this can be reenabled.
313+
"""
314+
func test_gd_self_reference_fails():
315+
# Create the gd_self_reference and connect its signal to a gdscript method that calls back into it.
316+
gd_self_reference = GdSelfReference.new()
317+
gd_self_reference.update_internal_signal.connect(update_self_reference)
318+
319+
# The returned value will still be 0 because update_internal can't be called in update_self_reference due to a borrowing issue.
320+
assert_eq(gd_self_reference.fail_to_update_internal_value_due_to_conflicting_borrow(10), 0)
321+
"""
322+
323+
func test_gd_self_reference_succeeds():
324+
# Create the gd_self_reference and connect its signal to a gdscript method that calls back into it.
325+
gd_self_reference = GdSelfReference.new()
326+
gd_self_reference.update_internal_signal.connect(update_self_reference)
327+
328+
assert_eq(gd_self_reference.succeed_at_updating_internal_value(10), 10)

itest/rust/src/register_tests/func_test.rs

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use godot::prelude::*;
88

99
#[derive(GodotClass)]
10-
#[class(base=RefCounted)]
10+
#[class(init, base=RefCounted)]
1111
struct FuncRename;
1212

1313
#[godot_api]
@@ -40,9 +40,70 @@ impl FuncRename {
4040
}
4141
}
4242

43+
#[derive(GodotClass)]
44+
#[class(base=RefCounted)]
45+
struct GdSelfReference {
46+
internal_value: i32,
47+
48+
#[base]
49+
base: Base<RefCounted>,
50+
}
51+
52+
#[godot_api]
53+
impl GdSelfReference {
54+
// A signal that will be looped back to update_internal through gdscript.
55+
#[signal]
56+
fn update_internal_signal(new_internal: i32);
57+
58+
#[func]
59+
fn update_internal(&mut self, new_value: i32) {
60+
self.internal_value = new_value;
61+
}
62+
63+
#[func]
64+
fn fail_to_update_internal_value_due_to_conflicting_borrow(
65+
&mut self,
66+
new_internal: i32,
67+
) -> i32 {
68+
// Since a self reference is held while the signal is emitted, when
69+
// GDScript tries to call update_internal(), there will be a failure due
70+
// to the double borrow and self.internal_value won't be changed.
71+
self.base.emit_signal(
72+
"update_internal_signal".into(),
73+
&[new_internal.to_variant()],
74+
);
75+
self.internal_value
76+
}
77+
78+
#[func(gd_self)]
79+
fn succeed_at_updating_internal_value(mut this: Gd<Self>, new_internal: i32) -> i32 {
80+
// Since this isn't bound while the signal is emitted, GDScript will succeed at calling
81+
// update_internal() and self.internal_value will be changed.
82+
this.emit_signal(
83+
"update_internal_signal".into(),
84+
&[new_internal.to_variant()],
85+
);
86+
return this.bind().internal_value;
87+
}
88+
89+
#[func(gd_self)]
90+
fn takes_gd_as_equivalent(mut this: Gd<GdSelfReference>) -> bool {
91+
this.bind_mut();
92+
true
93+
}
94+
95+
#[func(gd_self)]
96+
fn takes_gd_as_self_no_return_type(this: Gd<GdSelfReference>) {
97+
this.bind();
98+
}
99+
}
100+
43101
#[godot_api]
44-
impl RefCountedVirtual for FuncRename {
45-
fn init(_base: Base<Self::Base>) -> Self {
46-
Self
102+
impl RefCountedVirtual for GdSelfReference {
103+
fn init(base: Base<Self::Base>) -> Self {
104+
Self {
105+
internal_value: 0,
106+
base,
107+
}
47108
}
48109
}

0 commit comments

Comments
 (0)