Skip to content

Commit 0626d94

Browse files
bors[bot]lilizoey
andauthored
Merge #249
249: Fix methods always taking a mutable receiver r=Bromeon a=lilizoey A lot of methods unnecessarily require a `&mut self` reference, but at least now methods actually request the kind of reference they're declared to take. closes #206 Co-authored-by: Lili Zoey <[email protected]>
2 parents def7a0f + c4d3d98 commit 0626d94

File tree

3 files changed

+163
-4
lines changed

3 files changed

+163
-4
lines changed

godot-core/src/builtin/meta/signature.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ pub trait SignatureTuple {
1818
fn param_metadata(index: i32) -> sys::GDExtensionClassMethodArgumentMetadata;
1919

2020
unsafe fn varcall<C: GodotClass>(
21+
instance_ptr: sys::GDExtensionClassInstancePtr,
22+
args_ptr: *const sys::GDExtensionConstVariantPtr,
23+
ret: sys::GDExtensionVariantPtr,
24+
err: *mut sys::GDExtensionCallError,
25+
func: fn(&C, Self::Params) -> Self::Ret,
26+
method_name: &str,
27+
);
28+
29+
unsafe fn varcall_mut<C: GodotClass>(
2130
instance_ptr: sys::GDExtensionClassInstancePtr,
2231
args_ptr: *const sys::GDExtensionConstVariantPtr,
2332
ret: sys::GDExtensionVariantPtr,
@@ -29,6 +38,17 @@ pub trait SignatureTuple {
2938
// Note: this method imposes extra bounds on GodotFfi, which may not be implemented for user types.
3039
// We could fall back to varcalls in such cases, and not require GodotFfi categorically.
3140
unsafe fn ptrcall<C: GodotClass>(
41+
instance_ptr: sys::GDExtensionClassInstancePtr,
42+
args_ptr: *const sys::GDExtensionConstTypePtr,
43+
ret: sys::GDExtensionTypePtr,
44+
func: fn(&C, Self::Params) -> Self::Ret,
45+
method_name: &str,
46+
call_type: sys::PtrcallType,
47+
);
48+
49+
// Note: this method imposes extra bounds on GodotFfi, which may not be implemented for user types.
50+
// We could fall back to varcalls in such cases, and not require GodotFfi categorically.
51+
unsafe fn ptrcall_mut<C: GodotClass>(
3252
instance_ptr: sys::GDExtensionClassInstancePtr,
3353
args_ptr: *const sys::GDExtensionConstTypePtr,
3454
ret: sys::GDExtensionTypePtr,
@@ -111,6 +131,38 @@ macro_rules! impl_signature_for_tuple {
111131
args_ptr: *const sys::GDExtensionConstVariantPtr,
112132
ret: sys::GDExtensionVariantPtr,
113133
err: *mut sys::GDExtensionCallError,
134+
func: fn(&C, Self::Params) -> Self::Ret,
135+
method_name: &str,
136+
) {
137+
$crate::out!("varcall: {}", method_name);
138+
139+
let storage = unsafe { crate::private::as_storage::<C>(instance_ptr) };
140+
let instance = storage.get();
141+
142+
let args = ( $(
143+
{
144+
let variant = unsafe { &*(*args_ptr.offset($n) as *mut Variant) }; // TODO from_var_sys
145+
let arg = <$Pn as FromVariant>::try_from_variant(variant)
146+
.unwrap_or_else(|e| param_error::<$Pn>(method_name, $n, variant));
147+
148+
arg
149+
},
150+
)* );
151+
152+
let ret_val = func(&*instance, args);
153+
let ret_variant = <$R as ToVariant>::to_variant(&ret_val); // TODO write_sys
154+
unsafe {
155+
*(ret as *mut Variant) = ret_variant;
156+
(*err).error = sys::GDEXTENSION_CALL_OK;
157+
}
158+
}
159+
160+
#[inline]
161+
unsafe fn varcall_mut<C : GodotClass>(
162+
instance_ptr: sys::GDExtensionClassInstancePtr,
163+
args_ptr: *const sys::GDExtensionConstVariantPtr,
164+
ret: sys::GDExtensionVariantPtr,
165+
err: *mut sys::GDExtensionCallError,
114166
func: fn(&mut C, Self::Params) -> Self::Ret,
115167
method_name: &str,
116168
) {
@@ -142,6 +194,38 @@ macro_rules! impl_signature_for_tuple {
142194
instance_ptr: sys::GDExtensionClassInstancePtr,
143195
args_ptr: *const sys::GDExtensionConstTypePtr,
144196
ret: sys::GDExtensionTypePtr,
197+
func: fn(&C, Self::Params) -> Self::Ret,
198+
method_name: &str,
199+
call_type: sys::PtrcallType,
200+
) {
201+
$crate::out!("ptrcall: {}", method_name);
202+
203+
let storage = unsafe { crate::private::as_storage::<C>(instance_ptr) };
204+
let instance = storage.get();
205+
206+
let args = ( $(
207+
unsafe {
208+
<$Pn as sys::GodotFuncMarshal>::try_from_arg(
209+
sys::force_mut_ptr(*args_ptr.offset($n)),
210+
call_type
211+
)
212+
}
213+
.unwrap_or_else(|e| param_error::<$Pn>(method_name, $n, &e)),
214+
)* );
215+
216+
let ret_val = func(&*instance, args);
217+
// SAFETY:
218+
// `ret` is always a pointer to an initialized value of type $R
219+
// TODO: double-check the above
220+
<$R as sys::GodotFuncMarshal>::try_return(ret_val, ret, call_type)
221+
.unwrap_or_else(|ret_val| return_error::<$R>(method_name, &ret_val));
222+
}
223+
224+
#[inline]
225+
unsafe fn ptrcall_mut<C : GodotClass>(
226+
instance_ptr: sys::GDExtensionClassInstancePtr,
227+
args_ptr: *const sys::GDExtensionConstTypePtr,
228+
ret: sys::GDExtensionTypePtr,
145229
func: fn(&mut C, Self::Params) -> Self::Ret,
146230
method_name: &str,
147231
call_type: sys::PtrcallType,

godot-core/src/macros.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ macro_rules! gdext_is_not_unit {
4141
#[macro_export]
4242
macro_rules! gdext_register_method_inner {
4343
(
44+
$ptrcall:ident,
45+
$varcall:ident,
4446
$Class:ty,
4547
$map_method:ident,
4648
fn $method_name:ident(
@@ -69,7 +71,7 @@ macro_rules! gdext_register_method_inner {
6971
let success = $crate::private::handle_panic(
7072
|| stringify!($method_name),
7173
|| {
72-
<Sig as SignatureTuple>::varcall::< $Class >(
74+
<Sig as SignatureTuple>::$varcall::< $Class >(
7375
instance_ptr,
7476
args,
7577
ret,
@@ -103,7 +105,7 @@ macro_rules! gdext_register_method_inner {
103105
let success = $crate::private::handle_panic(
104106
|| stringify!($method_name),
105107
|| {
106-
<Sig as SignatureTuple>::ptrcall::< $Class >(
108+
<Sig as SignatureTuple>::$ptrcall::< $Class >(
107109
instance_ptr,
108110
args,
109111
ret,
@@ -203,6 +205,8 @@ macro_rules! gdext_register_method {
203205
) -> $RetTy:ty
204206
) => {
205207
$crate::gdext_register_method_inner!(
208+
ptrcall_mut,
209+
varcall_mut,
206210
$Class,
207211
map_mut,
208212
fn $method_name(
@@ -223,6 +227,8 @@ macro_rules! gdext_register_method {
223227
) -> $RetTy:ty
224228
) => {
225229
$crate::gdext_register_method_inner!(
230+
ptrcall,
231+
varcall,
226232
$Class,
227233
map,
228234
fn $method_name(
@@ -279,6 +285,7 @@ macro_rules! gdext_register_method {
279285
#[macro_export]
280286
macro_rules! gdext_virtual_method_callback_inner {
281287
(
288+
$ptrcall:ident,
282289
$Class:ty,
283290
$map_method:ident,
284291
fn $method_name:ident(
@@ -294,6 +301,7 @@ macro_rules! gdext_virtual_method_callback_inner {
294301
ret: sys::GDExtensionTypePtr,
295302
) {
296303
$crate::gdext_ptrcall!(
304+
$ptrcall;
297305
instance_ptr, args, ret;
298306
$Class;
299307
fn $method_name( $( $arg : $Param, )* ) -> $Ret
@@ -320,6 +328,7 @@ macro_rules! gdext_virtual_method_callback {
320328
) -> $RetTy:ty
321329
) => {
322330
$crate::gdext_virtual_method_callback_inner!(
331+
ptrcall_mut,
323332
$Class,
324333
map_mut,
325334
fn $method_name(
@@ -338,6 +347,7 @@ macro_rules! gdext_virtual_method_callback {
338347
) -> $RetTy:ty
339348
) => {
340349
$crate::gdext_virtual_method_callback_inner!(
350+
ptrcall,
341351
$Class,
342352
map,
343353
fn $method_name(
@@ -388,14 +398,15 @@ macro_rules! gdext_virtual_method_callback {
388398
#[macro_export]
389399
macro_rules! gdext_ptrcall {
390400
(
401+
$ptrcall:ident;
391402
$instance_ptr:ident, $args_ptr:ident, $ret_ptr:ident;
392403
$Class:ty;
393404
fn $method_name:ident(
394405
$( $arg:ident : $ParamTy:ty, )*
395406
) -> $( $RetTy:tt )+
396407
) => {
397408
use $crate::builtin::meta::SignatureTuple;
398-
<($($RetTy)+, $($ParamTy,)*) as SignatureTuple>::ptrcall::<$Class>(
409+
<($($RetTy)+, $($ParamTy,)*) as SignatureTuple>::$ptrcall::<$Class>(
399410
$instance_ptr,
400411
$args_ptr,
401412
$ret_ptr,

itest/rust/src/object_test.rs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

7-
use std::cell::RefCell;
7+
use std::cell::{Cell, RefCell};
88
use std::mem;
99
use std::rc::Rc;
1010

@@ -762,3 +762,67 @@ pub mod object_test_gd {
762762
}
763763
}
764764
}
765+
766+
// ----------------------------------------------------------------------------------------------------------------------------------------------
767+
768+
#[derive(GodotClass)]
769+
#[class(init, base=Object)]
770+
struct DoubleUse {
771+
#[base]
772+
base: Base<Object>,
773+
774+
used: Cell<bool>,
775+
}
776+
777+
#[godot_api]
778+
impl DoubleUse {
779+
#[func]
780+
fn use_1(&self) {
781+
self.used.set(true);
782+
}
783+
}
784+
785+
#[derive(GodotClass)]
786+
#[class(init, base=Object)]
787+
struct SignalEmitter {
788+
#[base]
789+
base: Base<Object>,
790+
}
791+
792+
#[godot_api]
793+
impl SignalEmitter {
794+
#[signal]
795+
fn do_use();
796+
}
797+
798+
#[itest]
799+
/// Test that godot can call a method that takes `&self`, while there already exists an immutable reference
800+
/// to that type acquired through `bind`.
801+
///
802+
/// This test is not signal-specific, the original bug would happen whenever godot would call a method that
803+
/// takes `&self`. However this was the easiest way to test the bug i could find.
804+
fn double_use_reference() {
805+
let double_use: Gd<DoubleUse> = Gd::new_default();
806+
let emitter: Gd<SignalEmitter> = Gd::new_default();
807+
808+
emitter
809+
.share()
810+
.upcast::<Object>()
811+
.connect("do_use".into(), double_use.callable("use_1"), 0);
812+
813+
let guard = double_use.bind();
814+
815+
assert!(!guard.used.get());
816+
817+
emitter
818+
.share()
819+
.upcast::<Object>()
820+
.emit_signal("do_use".into(), &[]);
821+
822+
assert!(guard.used.get(), "use_1 was not called");
823+
824+
std::mem::drop(guard);
825+
826+
double_use.free();
827+
emitter.free();
828+
}

0 commit comments

Comments
 (0)