Skip to content

Commit 31b0fc9

Browse files
committed
ToGodot for Gd/DynGd is again pass-by-ref
1 parent 369e9d3 commit 31b0fc9

File tree

7 files changed

+92
-39
lines changed

7 files changed

+92
-39
lines changed

godot-core/src/meta/args/as_arg.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::ffi::CStr;
1010
use crate::builtin::{GString, NodePath, StringName, Variant};
1111
use crate::meta::sealed::Sealed;
1212
use crate::meta::traits::GodotFfiVariant;
13-
use crate::meta::{CowArg, GodotType, ToGodot};
13+
use crate::meta::{CowArg, GodotType, ObjectArg, ToGodot};
1414
use crate::obj::{bounds, Bounds, DynGd, Gd, GodotClass, Inherits};
1515

1616
/// Implicit conversions for arguments passed to Godot APIs.
@@ -515,7 +515,39 @@ impl ArgPassing for ByRef {
515515
T::Via: GodotType,
516516
{
517517
// Use by-ref conversion if possible, avoiding unnecessary clones when passing to FFI.
518-
value.to_godot().to_ffi()
518+
GodotType::to_ffi(value.to_godot())
519+
}
520+
}
521+
522+
/// Pass arguments to Godot by object pointer (for objects only).
523+
///
524+
/// See [`ToGodot::Pass`].
525+
pub enum ByObject {}
526+
impl Sealed for ByObject {}
527+
impl ArgPassing for ByObject {
528+
type Output<'r, T: 'r> = &'r T;
529+
530+
type FfiOutput<'f, T>
531+
= ObjectArg
532+
where
533+
T: GodotType + 'f;
534+
535+
fn ref_to_owned_via<T>(value: &T) -> T::Via
536+
where
537+
T: ToGodot<Pass = Self>,
538+
T::Via: Clone,
539+
{
540+
// For ByObject types, do like ByRef: clone the reference to get owned value.
541+
value.to_godot().clone()
542+
}
543+
544+
fn ref_to_ffi<T>(value: &T) -> ObjectArg
545+
where
546+
T: ToGodot<Pass = Self>,
547+
T::Via: GodotType,
548+
{
549+
let obj_ref: &T::Via = value.to_godot(); // implements GodotType.
550+
unsafe { obj_ref.as_object_arg() }
519551
}
520552
}
521553

godot-core/src/meta/args/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod ref_arg;
1313
// ----------------------------------------------------------------------------------------------------------------------------------------------
1414
// Public APIs
1515

16-
pub use as_arg::{owned_into_arg, ref_to_arg, ArgPassing, AsArg, ByRef, ByValue, ToArg};
16+
pub use as_arg::{owned_into_arg, ref_to_arg, ArgPassing, AsArg, ByObject, ByRef, ByValue, ToArg};
1717
// ----------------------------------------------------------------------------------------------------------------------------------------------
1818
// Internal APIs
1919

godot-core/src/meta/args/object_arg.rs

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ use std::ptr;
99

1010
use godot_ffi::{ExtVariantType, GodotFfi, GodotNullableFfi, PtrcallType};
1111

12-
use crate::obj::{Gd, GodotClass, Inherits, RawGd};
12+
use crate::builtin::Variant;
13+
use crate::meta::error::ConvertError;
14+
use crate::meta::traits::GodotFfiVariant;
15+
use crate::obj::{Gd, GodotClass};
1316
use crate::{obj, sys};
1417

1518
/// View for object arguments passed to the Godot engine. Never owning; must be null or backed by `Gd<T>`.
@@ -34,29 +37,13 @@ impl ObjectArg {
3437
}
3538
}
3639

37-
/// Creates `ObjectArg` from a `RawGd`.
40+
/// Creates `ObjectArg` from an `Option<Gd>`.
3841
///
3942
/// # Safety
40-
/// The referenced `RawGd` must remain valid for the lifetime of this `ObjectArg`.
41-
pub unsafe fn from_raw_gd<T: GodotClass>(obj: &RawGd<T>) -> Self {
42-
// Runtime check is necessary, to ensure that object is still alive and has correct runtime type.
43-
if !obj.is_null() {
44-
obj.check_rtti("from_raw_gd");
45-
}
46-
47-
Self {
48-
object_ptr: obj.obj_sys(),
49-
}
50-
}
51-
52-
/// Creates `ObjectArg` from `Option<&Gd<U>>`, handling upcast to target type `T`.
53-
pub fn from_option_gd_ref<T, U>(opt: Option<&Gd<U>>) -> Self
54-
where
55-
T: GodotClass,
56-
U: GodotClass + Inherits<T>,
57-
{
58-
match opt {
59-
Some(gd) => unsafe { Self::from_gd(gd) },
43+
/// The referenced `Gd`, if not `None`, must remain valid for the lifetime of this `ObjectArg`.
44+
pub unsafe fn from_option_gd<T: GodotClass>(obj: Option<&Gd<T>>) -> Self {
45+
match obj {
46+
Some(gd) => Self::from_gd(gd),
6047
None => Self::null(),
6148
}
6249
}
@@ -72,11 +59,6 @@ impl ObjectArg {
7259
pub fn is_null(&self) -> bool {
7360
self.object_ptr.is_null()
7461
}
75-
76-
/// Returns the raw object pointer
77-
pub fn raw_ptr(&self) -> sys::GDExtensionObjectPtr {
78-
self.object_ptr
79-
}
8062
}
8163

8264
// #[derive(Clone)] doesn't seem to get bounds right.
@@ -130,6 +112,16 @@ unsafe impl GodotFfi for ObjectArg {
130112
}
131113
}
132114

115+
impl GodotFfiVariant for ObjectArg {
116+
fn ffi_to_variant(&self) -> Variant {
117+
obj::object_ffi_to_variant(self)
118+
}
119+
120+
fn ffi_from_variant(_variant: &Variant) -> Result<Self, ConvertError> {
121+
unreachable!("ObjectArg should only be passed *to* Godot, not *from*.")
122+
}
123+
}
124+
133125
impl GodotNullableFfi for ObjectArg {
134126
fn null() -> Self {
135127
Self::null()

godot-core/src/meta/godot_convert/impls.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ where
8383
fn godot_type_name() -> String {
8484
T::godot_type_name()
8585
}
86+
87+
// Only relevant for object types T.
88+
unsafe fn as_object_arg(&self) -> crate::meta::ObjectArg {
89+
match self {
90+
Some(inner) => unsafe { inner.as_object_arg() },
91+
None => crate::meta::ObjectArg::null(),
92+
}
93+
}
8694
}
8795

8896
impl<T: GodotConvert> GodotConvert for Option<T>

godot-core/src/meta/traits.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,25 @@ pub trait GodotType: GodotConvert<Via = Self> + sealed::Sealed + Sized + 'static
140140
fn qualifies_as_special_none(_from_variant: &Variant) -> bool {
141141
false
142142
}
143+
144+
/// Convert to `ObjectArg` for efficient object argument passing.
145+
///
146+
/// Implemented in `GodotType` because Rust has no specialization, and there's no good way to have trait bounds in `ByObject`, but not in
147+
/// other arg-passing strategies `ByValue`/`ByRef`.
148+
///
149+
/// # Panics
150+
/// If `Self` is not an object type (`Gd<T>`, `Option<Gd<T>>`). Note that `DynGd<T>` isn't directly implemented here, but uses `Gd<T>`'s
151+
/// impl on the FFI layer.
152+
///
153+
/// # Safety
154+
/// `self` must be kept alive while return value is in use. Might be addressed with lifetime in the future.
155+
#[doc(hidden)]
156+
unsafe fn as_object_arg(&self) -> crate::meta::ObjectArg {
157+
panic!(
158+
"as_object_arg() called for non-object type: {}",
159+
std::any::type_name::<Self>()
160+
)
161+
}
143162
}
144163

145164
// ----------------------------------------------------------------------------------------------------------------------------------------------

godot-core/src/obj/dyn_gd.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,9 +553,10 @@ where
553553
T: GodotClass,
554554
D: ?Sized,
555555
{
556+
// Delegate to Gd<T> passing strategy.
556557
type Pass = <Gd<T> as ToGodot>::Pass;
557558

558-
fn to_godot(&self) -> Self::Via {
559+
fn to_godot(&self) -> &Self::Via {
559560
self.obj.to_godot()
560561
}
561562

godot-core/src/obj/gd.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -895,15 +895,12 @@ impl<T: GodotClass> GodotConvert for Gd<T> {
895895
}
896896

897897
impl<T: GodotClass> ToGodot for Gd<T> {
898-
// FIXME(v0.4): ByRef/ByObject.
899-
type Pass = meta::ByValue;
898+
type Pass = meta::ByObject;
900899

901-
fn to_godot(&self) -> Self::Via {
902-
// For null objects created for FFI parameter passing, skip RTTI check.
903-
if !self.raw.is_null() {
904-
self.raw.check_rtti("to_godot");
905-
}
906-
self.clone()
900+
fn to_godot(&self) -> &Self {
901+
// Note: Gd<T> never null, so no need to check raw.is_null().
902+
self.raw.check_rtti("to_godot");
903+
self
907904
}
908905
}
909906

@@ -963,6 +960,10 @@ impl<T: GodotClass> GodotType for Gd<T> {
963960

964961
false
965962
}
963+
964+
unsafe fn as_object_arg(&self) -> meta::ObjectArg {
965+
meta::ObjectArg::from_gd(self)
966+
}
966967
}
967968

968969
impl<T: GodotClass> ArrayElement for Gd<T> {

0 commit comments

Comments
 (0)