From 3aa1d819b6b68248ab9646d42294968b10500483 Mon Sep 17 00:00:00 2001 From: Lili Zoey Date: Tue, 19 Sep 2023 19:06:13 +0200 Subject: [PATCH 1/2] - Remove GodotFuncMarshal - Rename VariantMetadata -> GodotMetadata - Add GodotCompatible, ToGodot, FromGodot - Add RawGd - Change everything so that To/FromGodot is the canonical way of passing values over the ffi-boundary - Change `GodotMetadata` to sealed `GodotType` - Implement `GodotType` for various types - Add `GodotFfiVariant` - Add tests for creating newtype wrappers around godot types - Fix to/from variant derives --- godot-codegen/src/central_generator.rs | 14 +- godot-codegen/src/class_generator.rs | 3 +- godot-codegen/src/util.rs | 19 +- godot-core/src/builtin/aabb.rs | 8 + godot-core/src/builtin/array.rs | 117 ++-- godot-core/src/builtin/basis.rs | 8 + godot-core/src/builtin/callable.rs | 13 +- godot-core/src/builtin/color.rs | 8 + godot-core/src/builtin/dictionary.rs | 42 +- godot-core/src/builtin/macros.rs | 4 + .../src/builtin/meta/godot_compat/impls.rs | 248 ++++++++ .../src/builtin/meta/godot_compat/mod.rs | 120 ++++ godot-core/src/builtin/meta/mod.rs | 147 ++++- godot-core/src/builtin/meta/return_marshal.rs | 14 +- godot-core/src/builtin/meta/signature.rs | 141 +++-- godot-core/src/builtin/others.rs | 10 + godot-core/src/builtin/packed_array.rs | 8 +- godot-core/src/builtin/plane.rs | 8 + godot-core/src/builtin/projection.rs | 8 + godot-core/src/builtin/quaternion.rs | 8 + godot-core/src/builtin/rect2.rs | 8 + godot-core/src/builtin/rect2i.rs | 8 +- godot-core/src/builtin/rid.rs | 8 + godot-core/src/builtin/string/godot_string.rs | 7 + godot-core/src/builtin/string/mod.rs | 31 +- godot-core/src/builtin/string/node_path.rs | 7 + godot-core/src/builtin/string/string_name.rs | 7 + godot-core/src/builtin/transform2d.rs | 8 + godot-core/src/builtin/transform3d.rs | 8 + godot-core/src/builtin/variant/impls.rs | 257 +++----- godot-core/src/builtin/variant/mod.rs | 17 +- .../src/builtin/variant/variant_traits.rs | 45 -- godot-core/src/builtin/vectors/vector2.rs | 7 + godot-core/src/builtin/vectors/vector2i.rs | 7 + godot-core/src/builtin/vectors/vector3.rs | 7 + godot-core/src/builtin/vectors/vector3i.rs | 7 + godot-core/src/builtin/vectors/vector4.rs | 7 + godot-core/src/builtin/vectors/vector4i.rs | 7 + godot-core/src/builtin/vectors/vector_axis.rs | 57 +- godot-core/src/engine.rs | 16 +- godot-core/src/obj/base.rs | 4 +- godot-core/src/obj/gd.rs | 513 +++------------ godot-core/src/obj/instance_id.rs | 35 +- godot-core/src/obj/mod.rs | 2 + godot-core/src/obj/raw.rs | 587 ++++++++++++++++++ godot-core/src/obj/traits.rs | 58 +- godot-ffi/src/godot_ffi.rs | 222 ++----- godot-ffi/src/lib.rs | 4 +- .../src/class/data_models/property.rs | 2 +- .../src/derive/derive_from_variant.rs | 72 +-- .../src/derive/derive_godot_compatible.rs | 29 + godot-macros/src/derive/derive_to_variant.rs | 6 +- godot-macros/src/derive/mod.rs | 2 + godot-macros/src/lib.rs | 25 +- godot/src/lib.rs | 9 +- itest/rust/build.rs | 65 +- .../builtin_tests/containers/callable_test.rs | 3 +- .../containers/dictionary_test.rs | 3 +- .../builtin_tests/containers/variant_test.rs | 26 +- .../src/builtin_tests/geometry/basis_test.rs | 3 +- .../src/builtin_tests/geometry/plane_test.rs | 15 +- itest/rust/src/common.rs | 6 +- itest/rust/src/framework/runner.rs | 3 +- itest/rust/src/object_tests/object_test.rs | 30 +- .../src/object_tests/virtual_methods_test.rs | 5 +- .../src/register_tests/derive_variant_test.rs | 34 +- .../src/register_tests/option_ffi_test.rs | 16 +- 67 files changed, 2060 insertions(+), 1193 deletions(-) create mode 100644 godot-core/src/builtin/meta/godot_compat/impls.rs create mode 100644 godot-core/src/builtin/meta/godot_compat/mod.rs create mode 100644 godot-core/src/obj/raw.rs create mode 100644 godot-macros/src/derive/derive_godot_compatible.rs diff --git a/godot-codegen/src/central_generator.rs b/godot-codegen/src/central_generator.rs index 81b285384..a75c15247 100644 --- a/godot-codegen/src/central_generator.rs +++ b/godot-codegen/src/central_generator.rs @@ -397,7 +397,7 @@ fn make_sys_code(central_items: &CentralItems) -> TokenStream { let [opaque_32bit, opaque_64bit] = opaque_types; quote! { - use crate::{ffi_methods, GDExtensionTypePtr, GDExtensionVariantOperator, GDExtensionVariantType, GodotFfi}; + use crate::{GDExtensionVariantOperator, GDExtensionVariantType}; #[cfg(target_pointer_width = "32")] pub mod types { @@ -443,12 +443,6 @@ fn make_sys_code(central_items: &CentralItems) -> TokenStream { } } - // SAFETY: - // This type is represented as `Self` in Godot, so `*mut Self` is sound. - unsafe impl GodotFfi for VariantType { - ffi_methods! { type GDExtensionTypePtr = *mut Self; .. } - } - // ---------------------------------------------------------------------------------------------------------------------------------------------- #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] @@ -475,12 +469,6 @@ fn make_sys_code(central_items: &CentralItems) -> TokenStream { self as _ } } - - // SAFETY: - // This type is represented as `Self` in Godot, so `*mut Self` is sound. - unsafe impl GodotFfi for VariantOperator { - ffi_methods! { type GDExtensionTypePtr = *mut Self; .. } - } } } diff --git a/godot-codegen/src/class_generator.rs b/godot-codegen/src/class_generator.rs index 30e6a503a..00230d960 100644 --- a/godot-codegen/src/class_generator.rs +++ b/godot-codegen/src/class_generator.rs @@ -1062,7 +1062,8 @@ fn make_special_builtin_methods(class_name: &TyName, _ctx: &Context) -> TokenStr if class_name.godot_ty == "Array" { quote! { pub fn from_outer_typed(outer: &Array) -> Self - where T: crate::builtin::meta::VariantMetadata + where + T: crate::builtin::meta::GodotType { Self { _outer_lifetime: std::marker::PhantomData, diff --git a/godot-codegen/src/util.rs b/godot-codegen/src/util.rs index 48e541bc9..c27007360 100644 --- a/godot-codegen/src/util.rs +++ b/godot-codegen/src/util.rs @@ -334,21 +334,22 @@ pub fn make_enum_definition(enum_: &Enum) -> TokenStream { } #index_enum_impl - impl sys::GodotFuncMarshal for #enum_name { + impl crate::builtin::meta::GodotCompatible for #enum_name { type Via = i64; - type FromViaError = sys::PrimitiveConversionError; - type IntoViaError = std::convert::Infallible; + } - fn try_from_via(via: Self::Via) -> std::result::Result { - let err = sys::PrimitiveConversionError::new(via); - let ord = i32::try_from(via).map_err(|_| err)?; - ::try_from_ord(ord).ok_or(err) + impl crate::builtin::meta::ToGodot for #enum_name { + fn to_godot(&self) -> Self::Via { + ::ord(*self) as i64 + } } - fn try_into_via(self) -> std::result::Result { - Ok(::ord(self).into()) + impl crate::builtin::meta::FromGodot for #enum_name { + fn try_from_godot(via: Self::Via) -> Option { + ::try_from_ord(i32::try_from(via).ok()?) } } + #bitfield_ops } } diff --git a/godot-core/src/builtin/aabb.rs b/godot-core/src/builtin/aabb.rs index 2ea50b374..b1ce96703 100644 --- a/godot-core/src/builtin/aabb.rs +++ b/godot-core/src/builtin/aabb.rs @@ -10,6 +10,8 @@ use sys::{ffi_methods, GodotFfi}; use crate::builtin::math::ApproxEq; use crate::builtin::{real, Plane, Vector3, Vector3Axis}; +use super::meta::impl_godot_as_self; + /// Axis-aligned bounding box in 3D space. /// /// `Aabb` consists of a position, a size, and several utility functions. It is typically used for @@ -396,9 +398,15 @@ impl std::fmt::Display for Aabb { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Aabb { + fn variant_type() -> sys::VariantType { + sys::VariantType::Aabb + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Aabb); + impl ApproxEq for Aabb { /// Returns `true` if the two `Aabb`s are approximately equal, by calling `is_equal_approx` on /// `position` and `size`. diff --git a/godot-core/src/builtin/array.rs b/godot-core/src/builtin/array.rs index a33369586..abd17d144 100644 --- a/godot-core/src/builtin/array.rs +++ b/godot-core/src/builtin/array.rs @@ -6,7 +6,6 @@ use godot_ffi as sys; -use crate::builtin::meta::VariantMetadata; use crate::builtin::*; use crate::obj::Share; use crate::property::{Export, ExportInfo, Property, TypeStringHint}; @@ -14,6 +13,8 @@ use std::fmt; use std::marker::PhantomData; use sys::{ffi_methods, interface_fn, GodotFfi}; +use super::meta::{FromGodot, GodotCompatible, GodotFfiVariant, GodotType, ToGodot}; + /// Godot's `Array` type. /// /// Unlike GDScript, all indices and sizes are unsigned, so negative indices are not supported. @@ -27,7 +28,7 @@ use sys::{ffi_methods, interface_fn, GodotFfi}; /// /// Godot also supports typed arrays, which are also just `Variant` arrays under the hood, but with /// runtime checks that no values of the wrong type are put into the array. We represent this as -/// `Array`, where the type `T` implements `VariantMetadata`, `FromVariant` and `ToVariant`. +/// `Array`, where the type `T` implements `VariantMetadata`, `FromGodot` and `ToGodot`. /// /// # Reference semantics /// @@ -47,11 +48,11 @@ use sys::{ffi_methods, interface_fn, GodotFfi}; /// concurrent modification on other threads (e.g. created through GDScript). // `T` must be restricted to `VariantMetadata` in the type, because `Drop` can only be implemented -// for `T: VariantMetadata` because `drop()` requires `sys_mut()`, which is on the `GodotFfi` +// for `T: GodotType` because `drop()` requires `sys_mut()`, which is on the `GodotFfi` // trait, whose `from_sys_init()` requires `Default`, which is only implemented for `T: // VariantMetadata`. Whew. This could be fixed by splitting up `GodotFfi` if desired. #[repr(C)] -pub struct Array { +pub struct Array { opaque: sys::types::OpaqueArray, _phantom: PhantomData, } @@ -72,7 +73,7 @@ impl_builtin_froms!(VariantArray; PackedVector3Array => array_from_packed_vector3_array, ); -impl Array { +impl Array { fn from_opaque(opaque: sys::types::OpaqueArray) -> Self { // Note: type is not yet checked at this point, because array has not yet been initialized! Self { @@ -231,13 +232,13 @@ impl Array { /// from variants may fail. /// In the current implementation, both cases will produce a panic rather than undefined /// behavior, but this should not be relied upon. - unsafe fn assume_type(self) -> Array { + unsafe fn assume_type(self) -> Array { // SAFETY: The memory layout of `TypedArray` does not depend on `T`. unsafe { std::mem::transmute(self) } } } -impl Array { +impl Array { /// Constructs an empty `Array`. pub fn new() -> Self { Self::default() @@ -364,7 +365,7 @@ impl Array { } } -impl Array { +impl Array { /// Returns an iterator over the elements of the `Array`. Note that this takes the array /// by reference but returns its elements by value, since they are internally converted from /// `Variant`. @@ -468,7 +469,7 @@ impl Array { } } -impl Array { +impl Array { /// Finds the index of an existing value in a sorted array using binary search. Equivalent of /// `bsearch` in GDScript. /// @@ -595,7 +596,11 @@ impl Array { // - `from_arg_ptr` // Arrays are properly initialized through a `from_sys` call, but the ref-count should be incremented // as that is the callee's responsibility. Which we do by calling `std::mem::forget(array.clone())`. -unsafe impl GodotFfi for Array { +unsafe impl GodotFfi for Array { + fn variant_type() -> sys::VariantType { + sys::VariantType::Array + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; fn from_sys; fn sys; @@ -616,7 +621,27 @@ unsafe impl GodotFfi for Array { } } -impl fmt::Debug for Array { +impl GodotCompatible for Array { + type Via = Self; +} + +impl ToGodot for Array { + fn to_godot(&self) -> Self::Via { + self.clone() + } + + fn into_godot(self) -> Self::Via { + self + } +} + +impl FromGodot for Array { + fn try_from_godot(via: Self::Via) -> Option { + Some(via) + } +} + +impl fmt::Debug for Array { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Going through `Variant` because there doesn't seem to be a direct way. write!(f, "{:?}", self.to_variant().stringify()) @@ -628,7 +653,7 @@ impl fmt::Debug for Array { /// /// To create a (mostly) independent copy instead, see [`Array::duplicate_shallow()`] and /// [`Array::duplicate_deep()`]. -impl Clone for Array { +impl Clone for Array { fn clone(&self) -> Self { // SAFETY: `self` is a valid array, since we have a reference that keeps it alive. let array = unsafe { @@ -645,19 +670,19 @@ impl Clone for Array { } } -impl Share for Array { +impl Share for Array { fn share(&self) -> Self { self.clone() } } -impl TypeStringHint for Array { +impl TypeStringHint for Array { fn type_string() -> String { format!("{}:{}", VariantType::Array as i32, T::type_string()) } } -impl Property for Array { +impl Property for Array { type Intermediate = Self; fn get_property(&self) -> Self::Intermediate { @@ -669,7 +694,7 @@ impl Property for Array { } } -impl Export for Array { +impl Export for Array { fn default_export_info() -> ExportInfo { ExportInfo { hint: crate::engine::global::PropertyHint::PROPERTY_HINT_TYPE_STRING, @@ -684,7 +709,7 @@ impl Export for Array { } } -impl Default for Array { +impl Default for Array { #[inline] fn default() -> Self { let mut array = unsafe { @@ -698,7 +723,7 @@ impl Default for Array { } } -impl Drop for Array { +impl Drop for Array { #[inline] fn drop(&mut self) { unsafe { @@ -708,17 +733,24 @@ impl Drop for Array { } } -impl VariantMetadata for Array { - fn variant_type() -> VariantType { - VariantType::Array +impl GodotType for Array { + type Ffi = Self; + + fn to_ffi(&self) -> Self::Ffi { + self.clone() } -} -// ---------------------------------------------------------------------------------------------------------------------------------------------- -// Conversion traits + fn into_ffi(self) -> Self::Ffi { + self + } -impl ToVariant for Array { - fn to_variant(&self) -> Variant { + fn try_from_ffi(ffi: Self::Ffi) -> Option { + Some(ffi) + } +} + +impl GodotFfiVariant for Array { + fn ffi_to_variant(&self) -> Variant { unsafe { Variant::from_var_sys_init(|variant_ptr| { let array_to_variant = sys::builtin_fn!(array_to_variant); @@ -726,10 +758,8 @@ impl ToVariant for Array { }) } } -} -impl FromVariant for Array { - fn try_from_variant(variant: &Variant) -> Result { + fn ffi_from_variant(variant: &Variant) -> Result { if variant.get_type() != Self::variant_type() { return Err(VariantConversionError::BadType); } @@ -745,15 +775,18 @@ impl FromVariant for Array { } } +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Conversion traits + /// Creates a `Array` from the given Rust array. -impl From<&[T; N]> for Array { +impl From<&[T; N]> for Array { fn from(arr: &[T; N]) -> Self { Self::from(&arr[..]) } } /// Creates a `Array` from the given slice. -impl From<&[T]> for Array { +impl From<&[T]> for Array { fn from(slice: &[T]) -> Self { let mut array = Self::new(); let len = slice.len(); @@ -775,7 +808,7 @@ impl From<&[T]> for Array { } /// Creates a `Array` from an iterator. -impl FromIterator for Array { +impl FromIterator for Array { fn from_iter>(iter: I) -> Self { let mut array = Self::new(); array.extend(iter); @@ -784,7 +817,7 @@ impl FromIterator for Array { } /// Extends a `Array` with the contents of an iterator. -impl Extend for Array { +impl Extend for Array { fn extend>(&mut self, iter: I) { // Unfortunately the GDExtension API does not offer the equivalent of `Vec::reserve`. // Otherwise we could use it to pre-allocate based on `iter.size_hint()`. @@ -798,7 +831,7 @@ impl Extend for Array { } /// Converts this array to a strongly typed Rust vector. -impl From<&Array> for Vec { +impl From<&Array> for Vec { fn from(array: &Array) -> Vec { let len = array.len(); let mut vec = Vec::with_capacity(len); @@ -816,12 +849,12 @@ impl From<&Array> for Vec { // ---------------------------------------------------------------------------------------------------------------------------------------------- -pub struct Iter<'a, T: VariantMetadata> { +pub struct Iter<'a, T: GodotType> { array: &'a Array, next_idx: usize, } -impl<'a, T: VariantMetadata + FromVariant> Iterator for Iter<'a, T> { +impl<'a, T: GodotType + FromGodot> Iterator for Iter<'a, T> { type Item = T; fn next(&mut self) -> Option { @@ -842,7 +875,7 @@ impl<'a, T: VariantMetadata + FromVariant> Iterator for Iter<'a, T> { } // TODO There's a macro for this, but it doesn't support generics yet; add support and use it -impl PartialEq for Array { +impl PartialEq for Array { #[inline] fn eq(&self, other: &Self) -> bool { unsafe { @@ -855,7 +888,7 @@ impl PartialEq for Array { } } -impl PartialOrd for Array { +impl PartialOrd for Array { #[inline] fn partial_cmp(&self, other: &Self) -> Option { let op_less = |lhs, rhs| unsafe { @@ -920,7 +953,7 @@ macro_rules! varray { // Note: use to_variant() and not Variant::from(), as that works with both references and values ($($elements:expr),* $(,)?) => { { - use $crate::builtin::ToVariant as _; + use $crate::builtin::meta::ToGodot as _; let mut array = $crate::builtin::VariantArray::default(); $( array.push($elements.to_variant()); @@ -945,10 +978,10 @@ struct TypeInfo { } impl TypeInfo { - fn of() -> Self { + fn of() -> Self { Self { - variant_type: T::variant_type(), - class_name: T::class_name().to_string_name(), + variant_type: ::Ffi::variant_type(), + class_name: T::Via::class_name().to_string_name(), } } diff --git a/godot-core/src/builtin/basis.rs b/godot-core/src/builtin/basis.rs index 1ab787921..8ada907c9 100644 --- a/godot-core/src/builtin/basis.rs +++ b/godot-core/src/builtin/basis.rs @@ -15,6 +15,8 @@ use std::cmp::Ordering; use std::fmt::Display; use std::ops::{Mul, MulAssign}; +use super::meta::impl_godot_as_self; + /// A 3x3 matrix, typically used as an orthogonal basis for [`Transform3D`](crate::builtin::Transform3D). /// /// Indexing into a `Basis` is done in row-major order. So `mat[1]` would return the first *row* and not @@ -594,9 +596,15 @@ impl Mul for Basis { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Basis { + fn variant_type() -> sys::VariantType { + sys::VariantType::Basis + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Basis); + /// The ordering used to interpret a set of euler angles as extrinsic /// rotations. #[derive(Copy, Clone, Eq, PartialEq, Debug)] diff --git a/godot-core/src/builtin/callable.rs b/godot-core/src/builtin/callable.rs index d18717480..bd6a028a3 100644 --- a/godot-core/src/builtin/callable.rs +++ b/godot-core/src/builtin/callable.rs @@ -6,7 +6,8 @@ use godot_ffi as sys; -use crate::builtin::{inner, StringName, ToVariant, Variant, VariantArray}; +use crate::builtin::meta::{impl_godot_as_self, GodotType, ToGodot}; +use crate::builtin::{inner, StringName, Variant, VariantArray}; use crate::engine::Object; use crate::obj::mem::Memory; use crate::obj::{Gd, GodotClass, InstanceId}; @@ -45,7 +46,7 @@ impl Callable { unsafe { sys::from_sys_init_or_init_default::(|self_ptr| { let ctor = sys::builtin_fn!(callable_from_object_method); - let args = [object.as_arg_ptr(), method.sys_const()]; + let args = [object.to_ffi().as_arg_ptr(), method.sys_const()]; ctor(self_ptr, args.as_ptr()); }) } @@ -190,7 +191,7 @@ impl Callable { // Increment refcount because we're getting a reference, and `InnerCallable::get_object` doesn't // increment the refcount. self.as_inner().get_object().map(|object| { - ::Mem::maybe_inc_ref(&object); + ::Mem::maybe_inc_ref(&object.raw); object }) } @@ -270,6 +271,10 @@ impl_builtin_traits! { // The `opaque` in `Callable` is just a pair of pointers, and requires no special initialization or cleanup // beyond what is done in `from_opaque` and `drop`. So using `*mut Opaque` is safe. unsafe impl GodotFfi for Callable { + fn variant_type() -> sys::VariantType { + sys::VariantType::Callable + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; .. } unsafe fn from_sys_init_default(init_fn: impl FnOnce(sys::GDExtensionTypePtr)) -> Self { @@ -279,6 +284,8 @@ unsafe impl GodotFfi for Callable { } } +impl_godot_as_self!(Callable); + impl fmt::Debug for Callable { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let method = self.method_name(); diff --git a/godot-core/src/builtin/color.rs b/godot-core/src/builtin/color.rs index 7c1827507..aff58cc6e 100644 --- a/godot-core/src/builtin/color.rs +++ b/godot-core/src/builtin/color.rs @@ -13,6 +13,8 @@ use sys::{ffi_methods, GodotFfi}; use std::ops; +use super::meta::impl_godot_as_self; + /// Color built-in type, in floating-point RGBA format. /// /// Channel values are _typically_ in the range of 0 to 1, but this is not a requirement, and @@ -315,9 +317,15 @@ impl Color { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Color { + fn variant_type() -> sys::VariantType { + sys::VariantType::Color + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Color); + impl ApproxEq for Color { fn approx_eq(&self, other: &Self) -> bool { // TODO(bromeon): re-implement in Rust diff --git a/godot-core/src/builtin/dictionary.rs b/godot-core/src/builtin/dictionary.rs index 6644b7837..25ff9c4d0 100644 --- a/godot-core/src/builtin/dictionary.rs +++ b/godot-core/src/builtin/dictionary.rs @@ -6,7 +6,8 @@ use godot_ffi as sys; -use crate::builtin::{inner, FromVariant, ToVariant, Variant}; +use crate::builtin::meta::{FromGodot, ToGodot}; +use crate::builtin::{inner, Variant}; use crate::obj::Share; use crate::property::{Export, ExportInfo, Property}; use std::fmt; @@ -15,6 +16,7 @@ use std::ptr::addr_of_mut; use sys::types::OpaqueDictionary; use sys::{ffi_methods, interface_fn, AsUninit, GodotFfi}; +use super::meta::impl_godot_as_self; use super::VariantArray; /// Godot's `Dictionary` type. @@ -74,7 +76,7 @@ impl Dictionary { /// /// _Godot equivalent: `erase`_ #[doc(alias = "erase")] - pub fn remove(&mut self, key: K) -> Option { + pub fn remove(&mut self, key: K) -> Option { let key = key.to_variant(); let old_value = self.get(key.clone()); self.as_inner().erase(key); @@ -90,7 +92,7 @@ impl Dictionary { /// /// _Godot equivalent: `find_key`_ #[doc(alias = "find_key")] - pub fn find_key_by_value(&self, value: V) -> Option { + pub fn find_key_by_value(&self, value: V) -> Option { let key = self.as_inner().find_key(value.to_variant()); if !key.is_nil() || self.contains_key(key.clone()) { @@ -104,7 +106,7 @@ impl Dictionary { /// /// Note that `NIL` values are returned as `Some(Variant::nil())`, while absent values are returned as `None`. /// If you want to treat both as `NIL`, use [`Self::get_or_nil`]. - pub fn get(&self, key: K) -> Option { + pub fn get(&self, key: K) -> Option { let key = key.to_variant(); if !self.contains_key(key.clone()) { return None; @@ -120,7 +122,7 @@ impl Dictionary { /// /// _Godot equivalent: `dict.get(key, null)`_ #[doc(alias = "get")] - pub fn get_or_nil(&self, key: K) -> Variant { + pub fn get_or_nil(&self, key: K) -> Variant { self.as_inner().get(key.to_variant(), Variant::nil()) } @@ -128,7 +130,7 @@ impl Dictionary { /// /// _Godot equivalent: `has`_ #[doc(alias = "has")] - pub fn contains_key(&self, key: K) -> bool { + pub fn contains_key(&self, key: K) -> bool { let key = key.to_variant(); self.as_inner().has(key) } @@ -189,7 +191,7 @@ impl Dictionary { /// Insert a value at the given key, returning the previous value for that key (if available). /// /// If you don't need the previous value, use [`Self::set`] instead. - pub fn insert(&mut self, key: K, value: V) -> Option { + pub fn insert(&mut self, key: K, value: V) -> Option { let key = key.to_variant(); let old_value = self.get(key.clone()); self.set(key, value); @@ -201,7 +203,7 @@ impl Dictionary { /// If you are interested in the previous value, use [`Self::insert`] instead. /// /// _Godot equivalent: `dict[key] = value`_ - pub fn set(&mut self, key: K, value: V) { + pub fn set(&mut self, key: K, value: V) { let key = key.to_variant(); // SAFETY: always returns a valid pointer to a value in the dictionary; either pre-existing or newly inserted. @@ -240,7 +242,7 @@ impl Dictionary { /// Get the pointer corresponding to the given key in the dictionary. /// /// If there exists no value at the given key, a `NIL` variant will be inserted for that key. - fn get_ptr_mut(&mut self, key: K) -> *mut Variant { + fn get_ptr_mut(&mut self, key: K) -> *mut Variant { let key = key.to_variant(); // SAFETY: accessing an unknown key _mutably_ creates that entry in the dictionary, with value `NIL`. @@ -266,6 +268,10 @@ impl Dictionary { // incremented as that is the callee's responsibility. Which we do by calling // `std::mem::forget(dictionary.clone())`. unsafe impl GodotFfi for Dictionary { + fn variant_type() -> sys::VariantType { + sys::VariantType::Dictionary + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; fn from_sys; fn from_sys_init; @@ -286,6 +292,8 @@ unsafe impl GodotFfi for Dictionary { } } +impl_godot_as_self!(Dictionary); + impl_builtin_traits! { for Dictionary { Default => dictionary_construct_default; @@ -351,8 +359,8 @@ impl Export for Dictionary { impl<'a, 'b, K, V, I> From for Dictionary where I: IntoIterator, - K: ToVariant + 'a, - V: ToVariant + 'b, + K: ToGodot + 'a, + V: ToGodot + 'b, { fn from(iterable: I) -> Self { iterable @@ -366,7 +374,7 @@ where /// /// Inserts all key-value pairs from the iterator into the dictionary. Previous values for keys appearing /// in `iter` will be overwritten. -impl Extend<(K, V)> for Dictionary { +impl Extend<(K, V)> for Dictionary { fn extend>(&mut self, iter: I) { for (k, v) in iter.into_iter() { self.set(k.to_variant(), v.to_variant()) @@ -374,7 +382,7 @@ impl Extend<(K, V)> for Dictionary { } } -impl FromIterator<(K, V)> for Dictionary { +impl FromIterator<(K, V)> for Dictionary { fn from_iter>(iter: I) -> Self { let mut dict = Dictionary::new(); dict.extend(iter); @@ -496,7 +504,7 @@ impl<'a> Iter<'a> { /// Creates an iterator that converts each `(Variant, Variant)` key-value pair into a `(K, V)` key-value /// pair, panicking upon conversion failure. - pub fn typed(self) -> TypedIter<'a, K, V> { + pub fn typed(self) -> TypedIter<'a, K, V> { TypedIter::from_untyped(self) } } @@ -527,7 +535,7 @@ impl<'a> Keys<'a> { /// Creates an iterator that will convert each `Variant` key into a key of type `K`, /// panicking upon failure to convert. - pub fn typed(self) -> TypedKeys<'a, K> { + pub fn typed(self) -> TypedKeys<'a, K> { TypedKeys::from_untyped(self) } @@ -569,7 +577,7 @@ impl<'a, K, V> TypedIter<'a, K, V> { } } -impl<'a, K: FromVariant, V: FromVariant> Iterator for TypedIter<'a, K, V> { +impl<'a, K: FromGodot, V: FromGodot> Iterator for TypedIter<'a, K, V> { type Item = (K, V); fn next(&mut self) -> Option { @@ -598,7 +606,7 @@ impl<'a, K> TypedKeys<'a, K> { } } -impl<'a, K: FromVariant> Iterator for TypedKeys<'a, K> { +impl<'a, K: FromGodot> Iterator for TypedKeys<'a, K> { type Item = K; fn next(&mut self) -> Option { diff --git a/godot-core/src/builtin/macros.rs b/godot-core/src/builtin/macros.rs index 2f8d52b63..d8cba3f94 100644 --- a/godot-core/src/builtin/macros.rs +++ b/godot-core/src/builtin/macros.rs @@ -141,6 +141,10 @@ macro_rules! impl_builtin_stub { // This is simply a wrapper around an `Opaque` value representing a value of the type. // So this is safe. unsafe impl GodotFfi for $Class { + fn variant_type() -> sys::VariantType { + sys::VariantType::$Class + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; .. } } }; diff --git a/godot-core/src/builtin/meta/godot_compat/impls.rs b/godot-core/src/builtin/meta/godot_compat/impls.rs new file mode 100644 index 000000000..23b98819a --- /dev/null +++ b/godot-core/src/builtin/meta/godot_compat/impls.rs @@ -0,0 +1,248 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use crate::builtin::meta::GodotType; + +use super::*; + +impl GodotCompatible for Option +where + Option: GodotType, +{ + type Via = Option; +} + +impl ToGodot for Option +where + Option: GodotType, +{ + fn to_godot(&self) -> Self::Via { + self.as_ref().map(ToGodot::to_godot) + } + + fn into_godot(self) -> Self::Via { + self.map(ToGodot::into_godot) + } +} + +impl FromGodot for Option +where + Option: GodotType, +{ + fn try_from_godot(via: Self::Via) -> Option { + match via { + Some(via) => T::try_from_godot(via).map(Some), + None => Some(None), + } + } + + fn from_godot(via: Self::Via) -> Self { + via.map(T::from_godot) + } +} + +impl GodotCompatible for sys::VariantType { + type Via = i64; +} + +impl ToGodot for sys::VariantType { + fn to_godot(&self) -> Self::Via { + *self as i64 + } + + fn into_godot(self) -> Self::Via { + self as i64 + } +} + +impl FromGodot for sys::VariantType { + fn try_from_godot(via: Self::Via) -> Option { + Some(Self::from_sys(via as sys::GDExtensionVariantType)) + } +} + +impl GodotCompatible for sys::VariantOperator { + type Via = i64; +} + +impl ToGodot for sys::VariantOperator { + fn to_godot(&self) -> Self::Via { + *self as i64 + } + + fn into_godot(self) -> Self::Via { + self as i64 + } +} + +impl FromGodot for sys::VariantOperator { + fn try_from_godot(via: Self::Via) -> Option { + Some(Self::from_sys(via as sys::GDExtensionVariantOperator)) + } +} + +impl GodotCompatible for *mut T { + type Via = i64; +} + +impl ToGodot for *mut T { + fn to_godot(&self) -> Self::Via { + *self as i64 + } +} + +impl FromGodot for *mut T { + fn try_from_godot(via: Self::Via) -> Option { + Some(via as Self) + } +} + +impl GodotCompatible for *const T { + type Via = i64; +} + +impl ToGodot for *const T { + fn to_godot(&self) -> Self::Via { + *self as i64 + } +} + +impl FromGodot for *const T { + fn try_from_godot(via: Self::Via) -> Option { + Some(via as Self) + } +} + +mod scalars { + use super::{impl_godot_as_self, FromGodot, GodotCompatible, ToGodot}; + use crate::builtin::meta::GodotType; + use godot_ffi as sys; + + macro_rules! impl_godot { + ($T:ty as $Via:ty $(, $param_metadata:expr)?) => { + impl GodotType for $T { + type Ffi = $Via; + + fn to_ffi(&self) -> Self::Ffi { + (*self).into() + } + + fn into_ffi(self) -> Self::Ffi { + self.into() + } + + fn try_from_ffi(ffi: Self::Ffi) -> Option { + Self::try_from(ffi).ok() + } + + $( + fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { + $param_metadata + } + )? + } + + impl GodotCompatible for $T { + type Via = $T; + } + + impl ToGodot for $T { + fn to_godot(&self) -> Self::Via { + *self + } + } + + impl FromGodot for $T { + fn try_from_godot(via: Self::Via) -> Option { + Some(via) + } + } + }; + ($T:ty as $Via:ty $(, $param_metadata:expr)?; lossy) => { + impl GodotType for $T { + type Ffi = $Via; + + fn to_ffi(&self) -> Self::Ffi { + *self as $Via + } + + fn into_ffi(self) -> Self::Ffi { + self as $Via + } + + fn try_from_ffi(ffi: Self::Ffi) -> Option { + Some(ffi as $T) + } + + $( + fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { + $param_metadata + } + )? + } + + impl GodotCompatible for $T { + type Via = $T; + } + + impl ToGodot for $T { + fn to_godot(&self) -> Self::Via { + *self + } + } + + impl FromGodot for $T { + fn try_from_godot(via: Self::Via) -> Option { + Some(via) + } + } + }; + } + + impl_godot_as_self!(bool); + impl_godot_as_self!(i64, sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64); + impl_godot_as_self!( + f64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE + ); + impl_godot_as_self!(()); + + impl_godot!( + i32 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32 + ); + impl_godot!( + i16 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16 + ); + impl_godot!( + i8 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8 + ); + impl_godot!( + u32 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32 + ); + impl_godot!( + u16 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16 + ); + impl_godot!( + u8 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8 + ); + + impl_godot!( + u64 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64; + lossy + ); + impl_godot!( + f32 as f64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT; + lossy + ); +} diff --git a/godot-core/src/builtin/meta/godot_compat/mod.rs b/godot-core/src/builtin/meta/godot_compat/mod.rs new file mode 100644 index 000000000..f1c03a276 --- /dev/null +++ b/godot-core/src/builtin/meta/godot_compat/mod.rs @@ -0,0 +1,120 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +mod impls; + +use godot_ffi as sys; + +use crate::builtin::{Variant, VariantConversionError}; + +use super::{GodotFfiVariant, GodotType}; + +/// Indicates that a type has some canonical Godot type that can represent it. +/// +/// The type specified here is what will be used to pass this type across to ffi-boundary to/from Godot. +/// Generally [`ToGodot`] needs to be implemented to pass a type to Godot, and [`FromGodot`] to receive this +/// type from Godot. +pub trait GodotCompatible { + /// The type used for ffi-passing. + type Via: GodotType; +} + +/// Defines the canonical conversion to Godot for a type. +/// +/// It is assumed that all the methods return equal values given equal inputs. Additionally it is assumed +/// that if [`FromGodot`] is implemented, converting to Godot and back again will return a value equal to the +/// starting value. +/// +/// Violating these assumptions is safe but will give unexpected results. +pub trait ToGodot: Sized + GodotCompatible { + /// Converts this type to the Godot type by reference, usually by cloning. + fn to_godot(&self) -> Self::Via; + + /// Converts this type to the Godot type. + fn into_godot(self) -> Self::Via { + self.to_godot() + } + + /// Converts this type to a [Variant]. + fn to_variant(&self) -> Variant { + self.to_godot().to_ffi().ffi_to_variant() + } +} + +/// Defines the canonical conversion from Godot for a type. +/// +/// It is assumed that all the methods return equal values given equal inputs. Additionally it is assumed +/// that if [`ToGodot`] is implemented, converting to Godot and back again will return a value equal to the +/// starting value. +/// +/// Violating these assumptions is safe but will give unexpected results. +pub trait FromGodot: Sized + GodotCompatible { + // TODO: better error + /// Performs the conversion. + fn try_from_godot(via: Self::Via) -> Option; + + /// ⚠️ Performs the conversion. + /// + /// # Panics + /// If the conversion fails. + fn from_godot(via: Self::Via) -> Self { + Self::try_from_godot(via).unwrap() + } + + /// Performs the conversion from a [`Variant`]. + fn try_from_variant(variant: &Variant) -> Result { + let ffi = ::Ffi::ffi_from_variant(variant)?; + let via = Self::Via::try_from_ffi(ffi).ok_or(VariantConversionError::BadValue)?; + Self::try_from_godot(via).ok_or(VariantConversionError::BadValue) + } + + /// ⚠️ Performs the conversion from a [`Variant`]. + /// + /// # Panics + /// If the conversion fails. + fn from_variant(variant: &Variant) -> Self { + Self::try_from_variant(variant).unwrap() + } +} + +pub(crate) fn into_ffi(t: T) -> ::Ffi { + let via = t.into_godot(); + via.into_ffi() +} + +pub(crate) fn try_from_ffi(ffi: ::Ffi) -> Option { + let via = ::try_from_ffi(ffi)?; + T::try_from_godot(via) +} + +macro_rules! impl_godot_as_self { + ($T:ty$(, $param_metadata:expr)?) => { + impl $crate::builtin::meta::GodotCompatible for $T { + type Via = $T; + } + + impl $crate::builtin::meta::ToGodot for $T { + #[inline] + fn to_godot(&self) -> Self::Via { + self.clone() + } + + #[inline] + fn into_godot(self) -> Self::Via { + self + } + } + + impl $crate::builtin::meta::FromGodot for $T { + #[inline] + fn try_from_godot(via: Self::Via) -> Option { + Some(via) + } + } + }; +} + +pub(crate) use impl_godot_as_self; diff --git a/godot-core/src/builtin/meta/mod.rs b/godot-core/src/builtin/meta/mod.rs index 8ae0d11c8..cce48934a 100644 --- a/godot-core/src/builtin/meta/mod.rs +++ b/godot-core/src/builtin/meta/mod.rs @@ -7,27 +7,120 @@ pub mod registration; mod class_name; +mod godot_compat; mod return_marshal; mod signature; pub use class_name::*; +pub use godot_compat::*; #[doc(hidden)] pub use return_marshal::*; #[doc(hidden)] pub use signature::*; use godot_ffi as sys; +use sys::{GodotFfi, GodotNullableFfi}; use crate::builtin::*; use crate::engine::global; use registration::method::MethodParamOrReturnInfo; -/// Stores meta-information about registered types or properties. +/// Conversion of GodotFfi-types into/from [`Variant`]. +pub trait GodotFfiVariant: Sized + GodotFfi { + fn ffi_to_variant(&self) -> Variant; + fn ffi_from_variant(variant: &Variant) -> Result; +} + +mod sealed { + // To ensure the user does not implement `GodotType` for their own types. + + use godot_ffi::GodotNullableFfi; + + use super::GodotType; + use crate::builtin::*; + use crate::obj::*; + + pub trait Sealed {} + + impl Sealed for Aabb {} + impl Sealed for Basis {} + impl Sealed for Callable {} + impl Sealed for Vector2 {} + impl Sealed for Vector3 {} + impl Sealed for Vector4 {} + impl Sealed for Vector2i {} + impl Sealed for Vector3i {} + impl Sealed for Vector4i {} + impl Sealed for Quaternion {} + impl Sealed for Color {} + impl Sealed for GodotString {} + impl Sealed for StringName {} + impl Sealed for NodePath {} + impl Sealed for PackedByteArray {} + impl Sealed for PackedInt32Array {} + impl Sealed for PackedInt64Array {} + impl Sealed for PackedFloat32Array {} + impl Sealed for PackedFloat64Array {} + impl Sealed for PackedStringArray {} + impl Sealed for PackedVector2Array {} + impl Sealed for PackedVector3Array {} + impl Sealed for PackedColorArray {} + impl Sealed for Plane {} + impl Sealed for Projection {} + impl Sealed for Rid {} + impl Sealed for Rect2 {} + impl Sealed for Rect2i {} + impl Sealed for Signal {} + impl Sealed for Transform2D {} + impl Sealed for Transform3D {} + impl Sealed for Dictionary {} + impl Sealed for bool {} + impl Sealed for i64 {} + impl Sealed for i32 {} + impl Sealed for i16 {} + impl Sealed for i8 {} + impl Sealed for u64 {} + impl Sealed for u32 {} + impl Sealed for u16 {} + impl Sealed for u8 {} + impl Sealed for f64 {} + impl Sealed for f32 {} + impl Sealed for () {} + impl Sealed for Variant {} + impl Sealed for Array {} + impl Sealed for RawGd {} + impl Sealed for Gd {} + impl Sealed for Option + where + T: GodotType, + T::Ffi: GodotNullableFfi, + { + } +} + +/// Types that can represent some Godot type. /// -/// Filling this information properly is important so that Godot can use ptrcalls instead of varcalls -/// (requires typed GDScript + sufficient information from the extension side) -pub trait VariantMetadata { - fn variant_type() -> VariantType; +/// This trait cannot be implemented for custom user types, for that you should see [`GodotCompatible`] +/// instead. +/// +/// Unlike [`GodotFfi`], types implementing this trait don't need to fully represent its corresponding Godot +/// type. For instance [`i32`] does not implement [`GodotFfi`] because it cannot represent all values of +/// Godot's `int` type, however it does implement `GodotType` because we can set the metadata of values with +/// this type to indicate that they are 32 bits large. +pub trait GodotType: GodotCompatible + ToGodot + FromGodot + sealed::Sealed { + type Ffi: GodotFfiVariant; + + fn to_ffi(&self) -> Self::Ffi; + fn into_ffi(self) -> Self::Ffi; + fn try_from_ffi(ffi: Self::Ffi) -> Option; + + fn from_ffi(ffi: Self::Ffi) -> Self { + Self::try_from_ffi(ffi).unwrap() + } + + fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { + Self::Ffi::default_param_metadata() + } fn class_name() -> ClassName { // If we use `ClassName::of::<()>()` then this type shows up as `(no base)` in documentation. @@ -36,7 +129,7 @@ pub trait VariantMetadata { fn property_info(property_name: &str) -> PropertyInfo { PropertyInfo { - variant_type: Self::variant_type(), + variant_type: Self::Ffi::variant_type(), class_name: Self::class_name(), property_name: StringName::from(property_name), hint: global::PropertyHint::PROPERTY_HINT_NONE, @@ -45,10 +138,6 @@ pub trait VariantMetadata { } } - fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_NONE - } - fn argument_info(property_name: &str) -> MethodParamOrReturnInfo { MethodParamOrReturnInfo::new(Self::property_info(property_name), Self::param_metadata()) } @@ -61,16 +150,44 @@ pub trait VariantMetadata { } } -impl VariantMetadata for Option { - fn variant_type() -> VariantType { - T::variant_type() +impl GodotType for Option +where + T: GodotType, + T::Ffi: GodotNullableFfi, +{ + type Ffi = T::Ffi; + + fn to_ffi(&self) -> Self::Ffi { + GodotNullableFfi::flatten_option(self.as_ref().map(|t| t.to_ffi())) } - fn class_name() -> ClassName { - T::class_name() + fn into_ffi(self) -> Self::Ffi { + GodotNullableFfi::flatten_option(self.map(|t| t.into_ffi())) + } + + fn try_from_ffi(ffi: Self::Ffi) -> Option { + if ffi.is_null() { + return Some(None); + } + + let t = GodotType::try_from_ffi(ffi); + t.map(Some) + } + + fn from_ffi(ffi: Self::Ffi) -> Self { + if ffi.is_null() { + return None; + } + + Some(GodotType::from_ffi(ffi)) } } +/// Stores meta-information about registered types or properties. +/// +/// Filling this information properly is important so that Godot can use ptrcalls instead of varcalls +/// (requires typed GDScript + sufficient information from the extension side) + // ---------------------------------------------------------------------------------------------------------------------------------------------- /// Rusty abstraction of `sys::GDExtensionPropertyInfo`. diff --git a/godot-core/src/builtin/meta/return_marshal.rs b/godot-core/src/builtin/meta/return_marshal.rs index ab44f7394..19a43c36c 100644 --- a/godot-core/src/builtin/meta/return_marshal.rs +++ b/godot-core/src/builtin/meta/return_marshal.rs @@ -7,6 +7,8 @@ use crate::obj::{Gd, GodotClass}; use crate::sys; +use super::{FromGodot, GodotType}; + /// Specifies how the return type is marshalled in a ptrcall. #[doc(hidden)] pub trait PtrcallReturn { @@ -35,15 +37,17 @@ pub struct PtrcallReturnT { _marker: std::marker::PhantomData, } -impl PtrcallReturn for PtrcallReturnT { +impl PtrcallReturn for PtrcallReturnT { type Ret = T; unsafe fn call(mut process_return_ptr: impl FnMut(sys::GDExtensionTypePtr)) -> Self::Ret { - let via = ::from_sys_init_default(|return_ptr| { - process_return_ptr(return_ptr) - }); + let ffi = + <::Ffi as sys::GodotFfi>::from_sys_init_default(|return_ptr| { + process_return_ptr(return_ptr) + }); - T::try_from_via(via).unwrap() + let via = T::Via::try_from_ffi(ffi).unwrap(); + T::from_godot(via) } } diff --git a/godot-core/src/builtin/meta/signature.rs b/godot-core/src/builtin/meta/signature.rs index 53f54973b..cd2e5c325 100644 --- a/godot-core/src/builtin/meta/signature.rs +++ b/godot-core/src/builtin/meta/signature.rs @@ -9,9 +9,12 @@ use std::fmt::Debug; use godot_ffi as sys; use sys::{BuiltinMethodBind, ClassMethodBind, UtilityFunctionBind}; +// TODO: +// separate arguments and return values, so that a type can be used in function arguments even if it doesn't +// implement `ToGodot`, and the other way around for return values. + use crate::builtin::meta::*; -//use crate::builtin::meta::MethodParamOrReturnInfo; -use crate::builtin::{FromVariant, ToVariant, Variant}; +use crate::builtin::Variant; use crate::obj::InstanceId; #[doc(hidden)] @@ -107,14 +110,17 @@ macro_rules! impl_varcall_signature_for_tuple { ( $PARAM_COUNT:literal; $R:ident - $(, $Pn:ident : $n:tt)* // $n cannot be literal if substituted as tuple index .0 + $(, ($pn:ident, $n:tt) : $Pn:ident)* // $n cannot be literal if substituted as tuple index .0 ) => { // R: FromVariantIndirect, Pn: ToVariant -> when calling engine APIs // R: ToVariant, Pn: #[allow(unused_variables)] impl<$R, $($Pn,)*> VarcallSignatureTuple for ($R, $($Pn,)*) - where $R: VariantMetadata + FromVariantIndirect + ToVariant + sys::GodotFuncMarshal + Debug, - $( $Pn: VariantMetadata + ToVariant + FromVariant + sys::GodotFuncMarshal + Debug, )* + where + $R: ToGodot + FromGodot + FromVariantIndirect + Debug, + $( + $Pn: ToGodot + FromGodot + Debug, + )* { const PARAM_COUNT: usize = $PARAM_COUNT; @@ -122,7 +128,7 @@ macro_rules! impl_varcall_signature_for_tuple { fn param_info(index: usize, param_name: &str) -> Option { match index { $( - $n => Some($Pn::argument_info(param_name)), + $n => Some($Pn::Via::argument_info(param_name)), )* _ => None, } @@ -130,14 +136,14 @@ macro_rules! impl_varcall_signature_for_tuple { #[inline] fn return_info() -> Option { - $R::return_info() + $R::Via::return_info() } #[inline] fn param_property_info(index: usize, param_name: &str) -> PropertyInfo { match index { $( - $n => $Pn::property_info(param_name), + $n => $Pn::Via::property_info(param_name), )* _ => unreachable!("property_info: unavailable for index {}", index), } @@ -166,21 +172,21 @@ macro_rules! impl_varcall_signature_for_tuple { method_name: &'static str, object_ptr: sys::GDExtensionObjectPtr, maybe_instance_id: Option, // if not static - args: Self::Params, + ($($pn,)*): Self::Params, varargs: &[Variant], ) -> Self::Ret { //$crate::out!("out_class_varcall: {method_name}"); // Note: varcalls are not safe from failing, if the happen through an object pointer -> validity check necessary. if let Some(instance_id) = maybe_instance_id { - crate::engine::ensure_object_alive(instance_id, object_ptr, method_name); + crate::engine::ensure_object_alive(Some(instance_id), object_ptr, method_name); } let class_fn = sys::interface_fn!(object_method_bind_call); let explicit_args = [ $( - <$Pn as ToVariant>::to_variant(&args.$n), + GodotFfiVariant::ffi_to_variant(&into_ffi($pn)), )* ]; @@ -208,13 +214,13 @@ macro_rules! impl_varcall_signature_for_tuple { #[inline] unsafe fn out_utility_ptrcall_varargs( utility_fn: UtilityFunctionBind, - args: Self::Params, + ($($pn,)*): Self::Params, varargs: &[Variant], ) -> Self::Ret { //$crate::out!("out_utility_ptrcall_varargs: {method_name}"); let explicit_args: [Variant; $PARAM_COUNT] = [ $( - <$Pn as ToVariant>::to_variant(&args.$n), + GodotFfiVariant::ffi_to_variant(&into_ffi($pn)), )* ]; @@ -244,12 +250,12 @@ macro_rules! impl_varcall_signature_for_tuple { macro_rules! impl_ptrcall_signature_for_tuple { ( $R:ident - $(, $Pn:ident : $n:tt)* // $n cannot be literal if substituted as tuple index .0 + $(, ($pn:ident, $n:tt) : $Pn:ident)* // $n cannot be literal if substituted as tuple index .0 ) => { #[allow(unused_variables)] impl<$R, $($Pn,)*> PtrcallSignatureTuple for ($R, $($Pn,)*) - where $R: sys::GodotFuncMarshal + Debug, - $( $Pn: sys::GodotFuncMarshal + Debug, )* + where $R: ToGodot + FromGodot + Debug, + $( $Pn: ToGodot + FromGodot + Debug, )* { type Params = ($($Pn,)*); type Ret = $R; @@ -280,11 +286,11 @@ macro_rules! impl_ptrcall_signature_for_tuple { method_name: &'static str, object_ptr: sys::GDExtensionObjectPtr, maybe_instance_id: Option, // if not static - args: Self::Params, + ($($pn,)*): Self::Params, ) -> Self::Ret { // $crate::out!("out_class_ptrcall: {method_name}"); if let Some(instance_id) = maybe_instance_id { - crate::engine::ensure_object_alive(instance_id, object_ptr, method_name); + crate::engine::ensure_object_alive(Some(instance_id), object_ptr, method_name); } let class_fn = sys::interface_fn!(object_method_bind_ptrcall); @@ -292,7 +298,7 @@ macro_rules! impl_ptrcall_signature_for_tuple { #[allow(clippy::let_unit_value)] let marshalled_args = ( $( - <$Pn as sys::GodotFuncMarshal>::try_into_via(args.$n).unwrap(), + into_ffi($pn), )* ); @@ -311,13 +317,13 @@ macro_rules! impl_ptrcall_signature_for_tuple { unsafe fn out_builtin_ptrcall>( builtin_fn: BuiltinMethodBind, type_ptr: sys::GDExtensionTypePtr, - args: Self::Params, + ($($pn,)*): Self::Params, ) -> Self::Ret { // $crate::out!("out_builtin_ptrcall: {method_name}"); #[allow(clippy::let_unit_value)] let marshalled_args = ( $( - <$Pn as sys::GodotFuncMarshal>::try_into_via(args.$n).unwrap(), + into_ffi($pn), )* ); @@ -335,13 +341,13 @@ macro_rules! impl_ptrcall_signature_for_tuple { #[inline] unsafe fn out_utility_ptrcall( utility_fn: UtilityFunctionBind, - args: Self::Params, + ($($pn,)*): Self::Params, ) -> Self::Ret { // $crate::out!("out_utility_ptrcall: {method_name}"); #[allow(clippy::let_unit_value)] let marshalled_args = ( $( - <$Pn as sys::GodotFuncMarshal>::try_into_via(args.$n).unwrap(), + into_ffi($pn), )* ); @@ -363,7 +369,7 @@ macro_rules! impl_ptrcall_signature_for_tuple { /// /// # Safety /// - It must be safe to dereference the pointer at `args_ptr.offset(N)` . -unsafe fn varcall_arg( +unsafe fn varcall_arg( args_ptr: *const sys::GDExtensionConstVariantPtr, method_name: &str, ) -> P { @@ -379,7 +385,7 @@ unsafe fn varcall_arg( /// - `ret` must be a pointer to an initialized `Variant`. /// - It must be safe to write a `Variant` once to `ret`. /// - It must be safe to write a `sys::GDExtensionCallError` once to `err`. -unsafe fn varcall_return( +unsafe fn varcall_return( ret_val: R, ret: sys::GDExtensionVariantPtr, err: *mut sys::GDExtensionCallError, @@ -413,13 +419,17 @@ pub(crate) unsafe fn varcall_return_checked( /// - It must be safe to dereference the address at `args_ptr.offset(N)` . /// - The pointer at `args_ptr.offset(N)` must follow the safety requirements as laid out in /// [`GodotFuncMarshal::try_from_arg`][sys::GodotFuncMarshal::try_from_arg]. -unsafe fn ptrcall_arg( +unsafe fn ptrcall_arg( args_ptr: *const sys::GDExtensionConstTypePtr, method_name: &str, call_type: sys::PtrcallType, ) -> P { - P::try_from_arg(sys::force_mut_ptr(*args_ptr.offset(N)), call_type) - .unwrap_or_else(|e| param_error::

(method_name, N as i32, &e)) + let ffi = ::Ffi::from_arg_ptr( + sys::force_mut_ptr(*args_ptr.offset(N)), + call_type, + ); + + try_from_ffi(ffi).unwrap_or_else(|| param_error::

(method_name, N as i32, &"TODO")) } /// Moves `ret_val` into `ret`. @@ -427,15 +437,14 @@ unsafe fn ptrcall_arg( /// # Safety /// `ret_val`, `ret`, and `call_type` must follow the safety requirements as laid out in /// [`GodotFuncMarshal::try_return`](sys::GodotFuncMarshal::try_return). -unsafe fn ptrcall_return( +unsafe fn ptrcall_return( ret_val: R, ret: sys::GDExtensionTypePtr, - method_name: &str, + _method_name: &str, call_type: sys::PtrcallType, ) { - ret_val - .try_return(ret, call_type) - .unwrap_or_else(|ret_val| return_error::(method_name, &ret_val)) + let val = into_ffi(ret_val); + val.move_return_ptr(ret, call_type); } fn param_error

(method_name: &str, index: i32, arg: &impl Debug) -> ! { @@ -445,7 +454,7 @@ fn param_error

(method_name: &str, index: i32, arg: &impl Debug) -> ! { ); } -fn return_error(method_name: &str, arg: &impl Debug) -> ! { +fn _return_error(method_name: &str, arg: &impl Debug) -> ! { let return_ty = std::any::type_name::(); panic!("{method_name}: return type {return_ty} is unable to store value {arg:?}",); } @@ -456,7 +465,7 @@ fn check_varcall_error( explicit_args: &[T], varargs: &[Variant], ) where - T: Debug + ToVariant, + T: Debug + ToGodot, { if err.error == sys::GDEXTENSION_CALL_OK { return; @@ -488,11 +497,7 @@ trait FromVariantIndirect { fn convert(variant: Variant) -> Self; } -impl FromVariantIndirect for () { - fn convert(_variant: Variant) -> Self {} -} - -impl FromVariantIndirect for T { +impl FromVariantIndirect for T { fn convert(variant: Variant) -> Self { T::from_variant(&variant) } @@ -503,33 +508,33 @@ impl FromVariantIndirect for T { // For example, RenderingServer::environment_set_volumetric_fog() has 14 parameters. We may need to extend this if the API adds more such methods. impl_varcall_signature_for_tuple!(0; R); -impl_varcall_signature_for_tuple!(1; R, P0: 0); -impl_varcall_signature_for_tuple!(2; R, P0: 0, P1: 1); -impl_varcall_signature_for_tuple!(3; R, P0: 0, P1: 1, P2: 2); -impl_varcall_signature_for_tuple!(4; R, P0: 0, P1: 1, P2: 2, P3: 3); -impl_varcall_signature_for_tuple!(5; R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4); -impl_varcall_signature_for_tuple!(6; R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5); -impl_varcall_signature_for_tuple!(7; R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6); -impl_varcall_signature_for_tuple!(8; R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7); -impl_varcall_signature_for_tuple!(9; R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8); -impl_varcall_signature_for_tuple!(10; R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8, P9: 9); -impl_varcall_signature_for_tuple!(11; R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8, P9: 9, P10: 10); -impl_varcall_signature_for_tuple!(12; R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8, P9: 9, P10: 10, P11: 11); -impl_varcall_signature_for_tuple!(13; R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8, P9: 9, P10: 10, P11: 11, P12: 12); -impl_varcall_signature_for_tuple!(14; R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8, P9: 9, P10: 10, P11: 11, P12: 12, P13: 13); +impl_varcall_signature_for_tuple!(1; R, (p0, 0): P0); +impl_varcall_signature_for_tuple!(2; R, (p0, 0): P0, (p1, 1): P1); +impl_varcall_signature_for_tuple!(3; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2); +impl_varcall_signature_for_tuple!(4; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3); +impl_varcall_signature_for_tuple!(5; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4); +impl_varcall_signature_for_tuple!(6; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5); +impl_varcall_signature_for_tuple!(7; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6); +impl_varcall_signature_for_tuple!(8; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7); +impl_varcall_signature_for_tuple!(9; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8); +impl_varcall_signature_for_tuple!(10; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9); +impl_varcall_signature_for_tuple!(11; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10); +impl_varcall_signature_for_tuple!(12; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11); +impl_varcall_signature_for_tuple!(13; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11, (p12, 12): P12); +impl_varcall_signature_for_tuple!(14; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11, (p12, 12): P12, (p13, 13): P13); impl_ptrcall_signature_for_tuple!(R); -impl_ptrcall_signature_for_tuple!(R, P0: 0); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8, P9: 9); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8, P9: 9, P10: 10); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8, P9: 9, P10: 10, P11: 11); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8, P9: 9, P10: 10, P11: 11, P12: 12); -impl_ptrcall_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8, P9: 9, P10: 10, P11: 11, P12: 12, P13: 13); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11, (p12, 12): P12); +impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11, (p12, 12): P12, (p13, 13): P13); diff --git a/godot-core/src/builtin/others.rs b/godot-core/src/builtin/others.rs index a35fe1ffd..f0e9a0512 100644 --- a/godot-core/src/builtin/others.rs +++ b/godot-core/src/builtin/others.rs @@ -9,6 +9,16 @@ use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; +use super::meta::impl_godot_as_self; + // TODO: Swap more inner math types with glam types // Note: ordered by enum ord in extension JSON impl_builtin_stub!(Signal, OpaqueSignal); + +impl_builtin_traits! { + for Signal { + Clone => signal_construct_copy; + } +} + +impl_godot_as_self!(Signal); diff --git a/godot-core/src/builtin/packed_array.rs b/godot-core/src/builtin/packed_array.rs index 276f4dad3..eace15494 100644 --- a/godot-core/src/builtin/packed_array.rs +++ b/godot-core/src/builtin/packed_array.rs @@ -6,6 +6,7 @@ use godot_ffi as sys; +use crate::builtin::meta::ToGodot; use crate::builtin::*; use std::fmt; use sys::types::*; @@ -439,6 +440,10 @@ macro_rules! impl_packed_array { } unsafe impl GodotFfi for $PackedArray { + fn variant_type() -> sys::VariantType { + sys::VariantType::$PackedArray + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; fn from_sys; fn sys; @@ -464,8 +469,9 @@ macro_rules! impl_packed_array { init_fn(result.sys_mut()); result } - } + + $crate::builtin::meta::impl_godot_as_self!($PackedArray); } } diff --git a/godot-core/src/builtin/plane.rs b/godot-core/src/builtin/plane.rs index 4931d6b87..46e11972e 100644 --- a/godot-core/src/builtin/plane.rs +++ b/godot-core/src/builtin/plane.rs @@ -12,6 +12,8 @@ use crate::builtin::{real, Vector3}; use std::ops::Neg; +use super::meta::impl_godot_as_self; + /// 3D plane in [Hessian normal form](https://mathworld.wolfram.com/HessianNormalForm.html). /// /// The Hessian form defines all points `point` which satisfy the equation @@ -249,9 +251,15 @@ impl Neg for Plane { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Plane { + fn variant_type() -> sys::VariantType { + sys::VariantType::Plane + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Plane); + impl ApproxEq for Plane { /// Finds whether the two planes are approximately equal. /// diff --git a/godot-core/src/builtin/projection.rs b/godot-core/src/builtin/projection.rs index bfa251fd7..7efcc3686 100644 --- a/godot-core/src/builtin/projection.rs +++ b/godot-core/src/builtin/projection.rs @@ -13,6 +13,8 @@ use crate::builtin::{real, Plane, RMat4, RealConv, Transform3D, Vector2, Vector4 use std::ops::Mul; +use super::meta::impl_godot_as_self; + /// A 4x4 matrix used for 3D projective transformations. It can represent /// transformations such as translation, rotation, scaling, shearing, and /// perspective division. It consists of four Vector4 columns. @@ -509,9 +511,15 @@ impl GlamConv for Projection { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Projection { + fn variant_type() -> sys::VariantType { + sys::VariantType::Projection + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Projection); + /// A projections clipping plane. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[repr(C)] diff --git a/godot-core/src/builtin/quaternion.rs b/godot-core/src/builtin/quaternion.rs index f966c497a..7afaab2f8 100644 --- a/godot-core/src/builtin/quaternion.rs +++ b/godot-core/src/builtin/quaternion.rs @@ -11,6 +11,8 @@ use crate::builtin::{inner, real, Basis, EulerOrder, RQuat, Vector3}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use super::meta::impl_godot_as_self; + #[derive(Copy, Clone, PartialEq, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[repr(C)] @@ -248,9 +250,15 @@ impl Mul for Quaternion { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Quaternion { + fn variant_type() -> sys::VariantType { + sys::VariantType::Quaternion + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Quaternion); + impl std::fmt::Display for Quaternion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.to_glam().fmt(f) diff --git a/godot-core/src/builtin/rect2.rs b/godot-core/src/builtin/rect2.rs index 4edc60901..a64f6b28c 100644 --- a/godot-core/src/builtin/rect2.rs +++ b/godot-core/src/builtin/rect2.rs @@ -10,6 +10,8 @@ use sys::{ffi_methods, GodotFfi}; use crate::builtin::math::ApproxEq; use crate::builtin::{real, Rect2i, RectSide, Vector2}; +use super::meta::impl_godot_as_self; + /// 2D axis-aligned bounding box. /// /// `Rect2` consists of a position, a size, and several utility functions. It is typically used for @@ -254,9 +256,15 @@ impl Rect2 { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Rect2 { + fn variant_type() -> sys::VariantType { + sys::VariantType::Rect2 + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Rect2); + impl ApproxEq for Rect2 { /// Returns if the two `Rect2`s are approximately equal, by comparing `position` and `size` separately. #[inline] diff --git a/godot-core/src/builtin/rect2i.rs b/godot-core/src/builtin/rect2i.rs index 2ed7b2566..23ad80973 100644 --- a/godot-core/src/builtin/rect2i.rs +++ b/godot-core/src/builtin/rect2i.rs @@ -8,7 +8,7 @@ use std::cmp; use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; -use super::{Rect2, RectSide, Vector2i}; +use super::{meta::impl_godot_as_self, Rect2, RectSide, Vector2i}; /// 2D axis-aligned integer bounding box. /// @@ -265,9 +265,15 @@ impl Rect2i { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Rect2i { + fn variant_type() -> sys::VariantType { + sys::VariantType::Rect2i + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Rect2i); + impl std::fmt::Display for Rect2i { /// Formats `Rect2i` to match Godot's string representation. /// diff --git a/godot-core/src/builtin/rid.rs b/godot-core/src/builtin/rid.rs index bdbdcf155..d57f488d2 100644 --- a/godot-core/src/builtin/rid.rs +++ b/godot-core/src/builtin/rid.rs @@ -9,6 +9,8 @@ use std::num::NonZeroU64; use godot_ffi as sys; use sys::{ffi_methods, static_assert, static_assert_eq_size, GodotFfi}; +use super::meta::impl_godot_as_self; + /// A RID ("resource ID") is an opaque handle that refers to a Godot `Resource`. /// /// RIDs do not grant access to the resource itself. Instead, they can be used in lower-level resource APIs @@ -116,5 +118,11 @@ impl std::fmt::Display for Rid { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Rid { + fn variant_type() -> sys::VariantType { + sys::VariantType::Rid + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } + +impl_godot_as_self!(Rid); diff --git a/godot-core/src/builtin/string/godot_string.rs b/godot-core/src/builtin/string/godot_string.rs index 2bc979973..25b9b878d 100644 --- a/godot-core/src/builtin/string/godot_string.rs +++ b/godot-core/src/builtin/string/godot_string.rs @@ -11,6 +11,7 @@ use sys::types::OpaqueString; use sys::{ffi_methods, interface_fn, GodotFfi}; use crate::builtin::inner; +use crate::builtin::meta::impl_godot_as_self; use super::string_chars::validate_unicode_scalar_sequence; use super::{NodePath, StringName}; @@ -110,6 +111,10 @@ impl GodotString { // incremented as that is the callee's responsibility. Which we do by calling // `std::mem::forget(string.clone())`. unsafe impl GodotFfi for GodotString { + fn variant_type() -> sys::VariantType { + sys::VariantType::String + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; fn from_sys; fn sys; @@ -130,6 +135,8 @@ unsafe impl GodotFfi for GodotString { } } +impl_godot_as_self!(GodotString); + impl_builtin_traits! { for GodotString { Default => string_construct_default; diff --git a/godot-core/src/builtin/string/mod.rs b/godot-core/src/builtin/string/mod.rs index 190be424c..47baaedbc 100644 --- a/godot-core/src/builtin/string/mod.rs +++ b/godot-core/src/builtin/string/mod.rs @@ -12,33 +12,34 @@ mod node_path; mod string_chars; mod string_name; -use godot_ffi::VariantType; pub use godot_string::*; pub use node_path::*; pub use string_name::*; -use super::{meta::VariantMetadata, FromVariant, ToVariant, Variant, VariantConversionError}; +use super::meta::{FromGodot, GodotCompatible, ToGodot}; -impl ToVariant for &str { - fn to_variant(&self) -> Variant { - GodotString::from(*self).to_variant() - } +impl GodotCompatible for &str { + type Via = GodotString; } -impl ToVariant for String { - fn to_variant(&self) -> Variant { - GodotString::from(self).to_variant() +impl ToGodot for &str { + fn to_godot(&self) -> Self::Via { + GodotString::from(*self) } } -impl FromVariant for String { - fn try_from_variant(variant: &Variant) -> Result { - Ok(GodotString::try_from_variant(variant)?.to_string()) +impl GodotCompatible for String { + type Via = GodotString; +} + +impl ToGodot for String { + fn to_godot(&self) -> Self::Via { + GodotString::from(self) } } -impl VariantMetadata for String { - fn variant_type() -> VariantType { - VariantType::String +impl FromGodot for String { + fn try_from_godot(via: Self::Via) -> Option { + Some(via.to_string()) } } diff --git a/godot-core/src/builtin/string/node_path.rs b/godot-core/src/builtin/string/node_path.rs index fdc970f0f..8a5d0beb3 100644 --- a/godot-core/src/builtin/string/node_path.rs +++ b/godot-core/src/builtin/string/node_path.rs @@ -10,6 +10,7 @@ use godot_ffi as sys; use godot_ffi::{ffi_methods, GDExtensionTypePtr, GodotFfi}; use crate::builtin::inner; +use crate::builtin::meta::impl_godot_as_self; use super::{GodotString, StringName}; @@ -48,6 +49,10 @@ impl NodePath { // incremented as that is the callee's responsibility. Which we do by calling // `std::mem::forget(node_path.clone())`. unsafe impl GodotFfi for NodePath { + fn variant_type() -> sys::VariantType { + sys::VariantType::NodePath + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; fn from_sys; fn sys; @@ -68,6 +73,8 @@ unsafe impl GodotFfi for NodePath { } } +impl_godot_as_self!(NodePath); + impl_builtin_traits! { for NodePath { Default => node_path_construct_default; diff --git a/godot-core/src/builtin/string/string_name.rs b/godot-core/src/builtin/string/string_name.rs index 3c4c7cc9f..a4d02af32 100644 --- a/godot-core/src/builtin/string/string_name.rs +++ b/godot-core/src/builtin/string/string_name.rs @@ -8,6 +8,7 @@ use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; use crate::builtin::inner; +use crate::builtin::meta::impl_godot_as_self; use crate::builtin::{GodotString, NodePath}; use std::fmt; @@ -116,6 +117,10 @@ impl StringName { // incremented as that is the callee's responsibility. Which we do by calling // `std::mem::forget(string_name.clone())`. unsafe impl GodotFfi for StringName { + fn variant_type() -> sys::VariantType { + sys::VariantType::StringName + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; fn from_sys; fn sys; @@ -136,6 +141,8 @@ unsafe impl GodotFfi for StringName { } } +impl_godot_as_self!(StringName); + impl_builtin_traits! { for StringName { Default => string_name_construct_default; diff --git a/godot-core/src/builtin/transform2d.rs b/godot-core/src/builtin/transform2d.rs index 192f90d6f..a89e15e1d 100644 --- a/godot-core/src/builtin/transform2d.rs +++ b/godot-core/src/builtin/transform2d.rs @@ -14,6 +14,8 @@ use crate::builtin::{real, RAffine2, RMat2, Rect2, Vector2}; use std::fmt::Display; use std::ops::{Mul, MulAssign}; +use super::meta::impl_godot_as_self; + /// Affine 2D transform (2x3 matrix). /// /// Represents transformations such as translation, rotation, or scaling. @@ -375,9 +377,15 @@ impl GlamConv for Transform2D { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Transform2D { + fn variant_type() -> sys::VariantType { + sys::VariantType::Transform2D + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Transform2D); + /// A 2x2 matrix, typically used as an orthogonal basis for [`Transform2D`]. /// /// Indexing into a `Basis2D` is done in a column-major order, meaning that diff --git a/godot-core/src/builtin/transform3d.rs b/godot-core/src/builtin/transform3d.rs index 02613724e..9448e6cf1 100644 --- a/godot-core/src/builtin/transform3d.rs +++ b/godot-core/src/builtin/transform3d.rs @@ -13,6 +13,8 @@ use crate::builtin::{real, Aabb, Basis, Plane, Projection, RAffine3, Vector3}; use std::fmt::Display; use std::ops::Mul; +use super::meta::impl_godot_as_self; + /// Affine 3D transform (3x4 matrix). /// /// Used for 3D linear transformations. Uses a basis + origin representation. @@ -382,9 +384,15 @@ impl GlamConv for Transform3D { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Transform3D { + fn variant_type() -> sys::VariantType { + sys::VariantType::Transform3D + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Transform3D); + #[cfg(test)] mod test { use super::*; diff --git a/godot-core/src/builtin/variant/impls.rs b/godot-core/src/builtin/variant/impls.rs index 2dcb93052..539697340 100644 --- a/godot-core/src/builtin/variant/impls.rs +++ b/godot-core/src/builtin/variant/impls.rs @@ -5,50 +5,25 @@ */ use super::*; -use crate::builtin::meta::registration::method::MethodParamOrReturnInfo; -use crate::builtin::meta::{PropertyInfo, VariantMetadata}; +use crate::builtin::meta::{GodotFfiVariant, GodotType, PropertyInfo}; use crate::builtin::*; use crate::engine::global; -use crate::obj::EngineEnum; use godot_ffi as sys; use sys::GodotFfi; // ---------------------------------------------------------------------------------------------------------------------------------------------- // Macro definitions -macro_rules! impl_variant_metadata { - ($T:ty, $variant_type:ident $( ; $($extra:tt)* )?) => { - impl VariantMetadata for $T { - fn variant_type() -> VariantType { - VariantType::$variant_type - } - - $($($extra)*)? - } - }; -} // Certain types need to be passed as initialized pointers in their from_variant implementations in 4.0. Because // 4.0 uses `*ptr = value` to return the type, and some types in c++ override `operator=` in c++ in a way // that requires the pointer the be initialized. But some other types will cause a memory leak in 4.1 if // initialized. // // Thus we can use `init` to indicate when it must be initialized in 4.0. -macro_rules! impl_variant_traits { - ($T:ty, $from_fn:ident, $to_fn:ident, $variant_type:ident) => { - impl_variant_traits!(@@ $T, $from_fn, $to_fn, $variant_type;); - }; - - ($T:ty, $from_fn:ident, $to_fn:ident, $variant_type:ident, $param_metadata:ident) => { - impl_variant_traits!(@@ $T, $from_fn, $to_fn, $variant_type; - fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { - sys::$param_metadata - } - ); - }; - - (@@ $T:ty, $from_fn:ident, $to_fn:ident, $variant_type:ident; $($extra:tt)*) => { - impl ToVariant for $T { - fn to_variant(&self) -> Variant { +macro_rules! impl_ffi_variant { + ($T:ty, $from_fn:ident, $to_fn:ident) => { + impl GodotFfiVariant for $T { + fn ffi_to_variant(&self) -> Variant { let variant = unsafe { Variant::from_var_sys_init(|variant_ptr| { let converter = sys::builtin_fn!($from_fn); @@ -58,13 +33,11 @@ macro_rules! impl_variant_traits { variant } - } - impl FromVariant for $T { - fn try_from_variant(variant: &Variant) -> Result { + fn ffi_from_variant(variant: &Variant) -> Result { // Type check -- at the moment, a strict match is required. if variant.get_type() != Self::variant_type() { - return Err(VariantConversionError::BadType) + return Err(VariantConversionError::BadType); } // For 4.0: @@ -86,59 +59,19 @@ macro_rules! impl_variant_traits { } } - impl_variant_metadata!($T, $variant_type; $($extra)*); - }; -} - -macro_rules! impl_variant_traits_int { - ($T:ty, $param_metadata:ident) => { - impl ToVariant for $T { - fn to_variant(&self) -> Variant { - i64::from(*self).to_variant() - } - } - - impl FromVariant for $T { - fn try_from_variant(v: &Variant) -> Result { - i64::try_from_variant(v) - .and_then(|i| <$T>::try_from(i).map_err(|_e| VariantConversionError::BadType)) - } - } - - impl VariantMetadata for $T { - fn variant_type() -> VariantType { - VariantType::Int - } + impl GodotType for $T { + type Ffi = Self; - fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { - sys::$param_metadata + fn to_ffi(&self) -> Self::Ffi { + self.clone() } - } - }; -} - -macro_rules! impl_variant_traits_float { - ($T:ty, $param_metadata:ident) => { - impl ToVariant for $T { - fn to_variant(&self) -> Variant { - let double = *self as f64; - f64::to_variant(&double) - } - } - - impl FromVariant for $T { - fn try_from_variant(v: &Variant) -> Result { - f64::try_from_variant(v).map(|double| double as $T) - } - } - impl VariantMetadata for $T { - fn variant_type() -> VariantType { - VariantType::Float + fn into_ffi(self) -> Self::Ffi { + self } - fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { - sys::$param_metadata + fn try_from_ffi(ffi: Self::Ffi) -> Option { + Some(ffi) } } }; @@ -152,91 +85,97 @@ macro_rules! impl_variant_traits_float { mod impls { use super::*; - impl_variant_traits!(Aabb, aabb_to_variant, aabb_from_variant, Aabb); - impl_variant_traits!(bool, bool_to_variant, bool_from_variant, Bool); - impl_variant_traits!(Basis, basis_to_variant, basis_from_variant, Basis); - impl_variant_traits!(Callable, callable_to_variant, callable_from_variant, Callable); - impl_variant_traits!(Vector2, vector2_to_variant, vector2_from_variant, Vector2); - impl_variant_traits!(Vector3, vector3_to_variant, vector3_from_variant, Vector3); - impl_variant_traits!(Vector4, vector4_to_variant, vector4_from_variant, Vector4); - impl_variant_traits!(Vector2i, vector2i_to_variant, vector2i_from_variant, Vector2i); - impl_variant_traits!(Vector3i, vector3i_to_variant, vector3i_from_variant, Vector3i); - impl_variant_traits!(Quaternion, quaternion_to_variant, quaternion_from_variant, Quaternion); - impl_variant_traits!(Color, color_to_variant, color_from_variant, Color); - impl_variant_traits!(GodotString, string_to_variant, string_from_variant, String); - impl_variant_traits!(StringName, string_name_to_variant, string_name_from_variant, StringName); - impl_variant_traits!(NodePath, node_path_to_variant, node_path_from_variant, NodePath); - // TODO use impl_variant_traits!, as soon as `Default` is available. Also consider auto-generating. - impl_variant_metadata!(Signal, /* signal_to_variant, signal_from_variant, */ Signal); - impl_variant_traits!(PackedByteArray, packed_byte_array_to_variant, packed_byte_array_from_variant, PackedByteArray); - impl_variant_traits!(PackedInt32Array, packed_int32_array_to_variant, packed_int32_array_from_variant, PackedInt32Array); - impl_variant_traits!(PackedInt64Array, packed_int64_array_to_variant, packed_int64_array_from_variant, PackedInt64Array); - impl_variant_traits!(PackedFloat32Array, packed_float32_array_to_variant, packed_float32_array_from_variant, PackedFloat32Array); - impl_variant_traits!(PackedFloat64Array, packed_float64_array_to_variant, packed_float64_array_from_variant, PackedFloat64Array); - impl_variant_traits!(PackedStringArray, packed_string_array_to_variant, packed_string_array_from_variant, PackedStringArray); - impl_variant_traits!(PackedVector2Array, packed_vector2_array_to_variant, packed_vector2_array_from_variant, PackedVector2Array); - impl_variant_traits!(PackedVector3Array, packed_vector3_array_to_variant, packed_vector3_array_from_variant, PackedVector3Array); - impl_variant_traits!(PackedColorArray, packed_color_array_to_variant, packed_color_array_from_variant, PackedColorArray); - impl_variant_traits!(Plane, plane_to_variant, plane_from_variant, Plane); - impl_variant_traits!(Projection, projection_to_variant, projection_from_variant, Projection); - impl_variant_traits!(Rid, rid_to_variant, rid_from_variant, Rid); - impl_variant_traits!(Rect2, rect2_to_variant, rect2_from_variant, Rect2); - impl_variant_traits!(Rect2i, rect2i_to_variant, rect2i_from_variant, Rect2i); - impl_variant_traits!(Transform2D, transform_2d_to_variant, transform_2d_from_variant, Transform2D); - impl_variant_traits!(Transform3D, transform_3d_to_variant, transform_3d_from_variant, Transform3D); - impl_variant_traits!(Dictionary, dictionary_to_variant, dictionary_from_variant, Dictionary); - - impl_variant_traits!(i64, int_to_variant, int_from_variant, Int, GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64); - impl_variant_traits_int!(i8, GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8); - impl_variant_traits_int!(i16, GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16); - impl_variant_traits_int!(i32, GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32); - - impl_variant_traits_int!(u8, GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8); - impl_variant_traits_int!(u16, GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16); - impl_variant_traits_int!(u32, GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32); - // u64 is not supported, because it cannot be represented on GDScript side, and implicitly converting to i64 is error-prone. - - impl_variant_traits!(f64, float_to_variant, float_from_variant, Float, GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE); - impl_variant_traits_float!(f32, GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT); + impl_ffi_variant!(Aabb, aabb_to_variant, aabb_from_variant); + impl_ffi_variant!(bool, bool_to_variant, bool_from_variant); + impl_ffi_variant!(Basis, basis_to_variant, basis_from_variant); + impl_ffi_variant!(Callable, callable_to_variant, callable_from_variant); + impl_ffi_variant!(Vector2, vector2_to_variant, vector2_from_variant); + impl_ffi_variant!(Vector3, vector3_to_variant, vector3_from_variant); + impl_ffi_variant!(Vector4, vector4_to_variant, vector4_from_variant); + impl_ffi_variant!(Vector2i, vector2i_to_variant, vector2i_from_variant); + impl_ffi_variant!(Vector3i, vector3i_to_variant, vector3i_from_variant); + impl_ffi_variant!(Vector4i, vector3i_to_variant, vector3i_from_variant); + impl_ffi_variant!(Quaternion, quaternion_to_variant, quaternion_from_variant); + impl_ffi_variant!(Color, color_to_variant, color_from_variant); + impl_ffi_variant!(GodotString, string_to_variant, string_from_variant); + impl_ffi_variant!(StringName, string_name_to_variant, string_name_from_variant); + impl_ffi_variant!(NodePath, node_path_to_variant, node_path_from_variant); + impl_ffi_variant!(PackedByteArray, packed_byte_array_to_variant, packed_byte_array_from_variant); + impl_ffi_variant!(PackedInt32Array, packed_int32_array_to_variant, packed_int32_array_from_variant); + impl_ffi_variant!(PackedInt64Array, packed_int64_array_to_variant, packed_int64_array_from_variant); + impl_ffi_variant!(PackedFloat32Array, packed_float32_array_to_variant, packed_float32_array_from_variant); + impl_ffi_variant!(PackedFloat64Array, packed_float64_array_to_variant, packed_float64_array_from_variant); + impl_ffi_variant!(PackedStringArray, packed_string_array_to_variant, packed_string_array_from_variant); + impl_ffi_variant!(PackedVector2Array, packed_vector2_array_to_variant, packed_vector2_array_from_variant); + impl_ffi_variant!(PackedVector3Array, packed_vector3_array_to_variant, packed_vector3_array_from_variant); + impl_ffi_variant!(PackedColorArray, packed_color_array_to_variant, packed_color_array_from_variant); + impl_ffi_variant!(Plane, plane_to_variant, plane_from_variant); + impl_ffi_variant!(Projection, projection_to_variant, projection_from_variant); + impl_ffi_variant!(Rid, rid_to_variant, rid_from_variant); + impl_ffi_variant!(Rect2, rect2_to_variant, rect2_from_variant); + impl_ffi_variant!(Rect2i, rect2i_to_variant, rect2i_from_variant); + impl_ffi_variant!(Signal, signal_to_variant, signal_from_variant); + impl_ffi_variant!(Transform2D, transform_2d_to_variant, transform_2d_from_variant); + impl_ffi_variant!(Transform3D, transform_3d_to_variant, transform_3d_from_variant); + impl_ffi_variant!(Dictionary, dictionary_to_variant, dictionary_from_variant); + impl_ffi_variant!(i64, int_to_variant, int_from_variant); + impl_ffi_variant!(f64, float_to_variant, float_from_variant); + } // ---------------------------------------------------------------------------------------------------------------------------------------------- // Explicit impls // Unit -impl ToVariant for () { - fn to_variant(&self) -> Variant { +impl GodotFfiVariant for () { + fn ffi_to_variant(&self) -> Variant { Variant::nil() } -} -impl VariantMetadata for () { - fn variant_type() -> VariantType { - VariantType::Nil + fn ffi_from_variant(variant: &Variant) -> Result { + if variant.is_nil() { + return Ok(()); + } + + Err(VariantConversionError::BadValue) } +} + +impl GodotType for () { + type Ffi = Self; - fn return_info() -> Option { - None + fn to_ffi(&self) -> Self::Ffi {} + + fn into_ffi(self) -> Self::Ffi {} + + fn try_from_ffi(_: Self::Ffi) -> Option { + Some(()) } } -impl ToVariant for Variant { - fn to_variant(&self) -> Variant { +impl GodotFfiVariant for Variant { + fn ffi_to_variant(&self) -> Variant { self.clone() } -} -impl FromVariant for Variant { - fn try_from_variant(variant: &Variant) -> Result { + fn ffi_from_variant(variant: &Variant) -> Result { Ok(variant.clone()) } } -// Variant itself -impl VariantMetadata for Variant { - fn variant_type() -> VariantType { - // Arrays use the `NIL` type to indicate that they are untyped. - VariantType::Nil +impl GodotType for Variant { + type Ffi = Variant; + + fn to_ffi(&self) -> Self::Ffi { + self.clone() + } + + fn into_ffi(self) -> Self::Ffi { + self + } + + fn try_from_ffi(ffi: Self::Ffi) -> Option { + Some(ffi) } fn property_info(property_name: &str) -> PropertyInfo { @@ -249,30 +188,4 @@ impl VariantMetadata for Variant { usage: global::PropertyUsageFlags::PROPERTY_USAGE_NIL_IS_VARIANT, } } - - fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8 - } -} - -impl ToVariant for T { - fn to_variant(&self) -> Variant { - ::to_variant(&self.ord()) - } -} - -impl FromVariant for T { - fn try_from_variant(variant: &Variant) -> Result { - ::try_from_variant(variant) - .and_then(|int| Self::try_from_ord(int).ok_or(VariantConversionError::BadType)) - } -} - -impl VariantMetadata for T { - fn variant_type() -> VariantType { - VariantType::Int - } - fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32 - } } diff --git a/godot-core/src/builtin/variant/mod.rs b/godot-core/src/builtin/variant/mod.rs index 221ae3043..04d96cf4d 100644 --- a/godot-core/src/builtin/variant/mod.rs +++ b/godot-core/src/builtin/variant/mod.rs @@ -17,6 +17,8 @@ pub use impls::*; pub use sys::{VariantOperator, VariantType}; pub use variant_traits::*; +use super::meta::{impl_godot_as_self, FromGodot, ToGodot}; + #[repr(C, align(8))] pub struct Variant { opaque: OpaqueVariant, @@ -31,7 +33,7 @@ impl Variant { /// Create a variant holding a non-nil value. /// /// Equivalent to `value.to_variant()`. - pub fn from(value: T) -> Self { + pub fn from(value: T) -> Self { value.to_variant() } @@ -41,14 +43,14 @@ impl Variant { /// /// # Panics /// When this variant holds a different type. - pub fn to(&self) -> T { + pub fn to(&self) -> T { T::from_variant(self) } /// Convert to type `T`, returning `Err` on failure. /// /// Equivalent to `T::try_from_variant(&self)`. - pub fn try_to(&self) -> Result { + pub fn try_to(&self) -> Result { T::try_from_variant(self) } @@ -259,6 +261,10 @@ impl Variant { // `from_opaque` properly initializes a dereferenced pointer to an `OpaqueVariant`. // `std::mem::swap` is sufficient for returning a value. unsafe impl GodotFfi for Variant { + fn variant_type() -> sys::VariantType { + sys::VariantType::Nil + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; .. } unsafe fn from_sys_init_default(init_fn: impl FnOnce(sys::GDExtensionTypePtr)) -> Self { @@ -268,6 +274,11 @@ unsafe impl GodotFfi for Variant { } } +impl_godot_as_self!( + Variant, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8 +); + impl Clone for Variant { fn clone(&self) -> Self { unsafe { diff --git a/godot-core/src/builtin/variant/variant_traits.rs b/godot-core/src/builtin/variant/variant_traits.rs index f7e77caf0..4269cc394 100644 --- a/godot-core/src/builtin/variant/variant_traits.rs +++ b/godot-core/src/builtin/variant/variant_traits.rs @@ -4,51 +4,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::builtin::Variant; - -/// Trait to enable conversions of types _from_ the [`Variant`] type. -pub trait FromVariant: Sized { - /// Tries to convert a `Variant` to `Self`, allowing to check the success or failure. - fn try_from_variant(variant: &Variant) -> Result; - - /// ⚠️ Converts from `Variant` to `Self`, panicking on error. - /// - /// This method should generally not be overridden by trait impls, even if conversions are infallible. - /// Implementing [`Self::try_from_variant`] suffices. - fn from_variant(variant: &Variant) -> Self { - Self::try_from_variant(variant).unwrap_or_else(|e| { - panic!( - "failed to convert from variant {:?} to {}; {:?}", - variant, - std::any::type_name::(), - e - ) - }) - } -} - -/// Trait to enable conversions of types _to_ the [`Variant`] type. -pub trait ToVariant { - /*fn try_to_variant(&self) -> Result; - - fn to_variant(&self) -> Variant { - Self::try_to_variant(self).unwrap_or_else(|e| { - panic!( - "failed to convert from {} to variant; {:?}", - std::any::type_name::(), - e - ) - }) - }*/ - - /// Infallible conversion from `Self` type to `Variant`. - /// - /// This method must not panic. If your conversion is fallible, this trait should not be used. - fn to_variant(&self) -> Variant; -} - -// ---------------------------------------------------------------------------------------------------------------------------------------------- - #[derive(Eq, PartialEq, Debug)] //pub struct VariantConversionError; pub enum VariantConversionError { diff --git a/godot-core/src/builtin/vectors/vector2.rs b/godot-core/src/builtin/vectors/vector2.rs index aaf126c2d..6eb883f22 100644 --- a/godot-core/src/builtin/vectors/vector2.rs +++ b/godot-core/src/builtin/vectors/vector2.rs @@ -9,6 +9,7 @@ use sys::{ffi_methods, GodotFfi}; use crate::builtin::inner; use crate::builtin::math::{FloatExt, GlamConv, GlamType}; +use crate::builtin::meta::impl_godot_as_self; use crate::builtin::vectors::Vector2Axis; use crate::builtin::{real, RAffine2, RVec2, Vector2i}; @@ -248,9 +249,15 @@ impl_from_tuple_for_vector2x!(Vector2, real); // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Vector2 { + fn variant_type() -> sys::VariantType { + sys::VariantType::Vector2 + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Vector2); + impl GlamConv for Vector2 { type Glam = RVec2; } diff --git a/godot-core/src/builtin/vectors/vector2i.rs b/godot-core/src/builtin/vectors/vector2i.rs index f53c913af..8000e4859 100644 --- a/godot-core/src/builtin/vectors/vector2i.rs +++ b/godot-core/src/builtin/vectors/vector2i.rs @@ -9,6 +9,7 @@ use std::cmp::Ordering; use sys::{ffi_methods, GodotFfi}; use crate::builtin::math::{FloatExt, GlamConv, GlamType}; +use crate::builtin::meta::impl_godot_as_self; use crate::builtin::{real, RVec2, Vector2, Vector2Axis}; use std::fmt; @@ -129,9 +130,15 @@ impl_from_tuple_for_vector2x!(Vector2i, i32); // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Vector2i { + fn variant_type() -> sys::VariantType { + sys::VariantType::Vector2i + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Vector2i); + impl GlamType for glam::IVec2 { type Mapped = Vector2i; diff --git a/godot-core/src/builtin/vectors/vector3.rs b/godot-core/src/builtin/vectors/vector3.rs index da1c9ad31..4ead3609c 100644 --- a/godot-core/src/builtin/vectors/vector3.rs +++ b/godot-core/src/builtin/vectors/vector3.rs @@ -8,6 +8,7 @@ use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; use crate::builtin::math::{FloatExt, GlamConv, GlamType}; +use crate::builtin::meta::impl_godot_as_self; use crate::builtin::vectors::Vector3Axis; use crate::builtin::{real, Basis, RVec3, Vector3i}; @@ -279,9 +280,15 @@ impl_from_tuple_for_vector3x!(Vector3, real); // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Vector3 { + fn variant_type() -> sys::VariantType { + sys::VariantType::Vector3 + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Vector3); + impl GlamType for RVec3 { type Mapped = Vector3; diff --git a/godot-core/src/builtin/vectors/vector3i.rs b/godot-core/src/builtin/vectors/vector3i.rs index 4dd37619b..e5c5463af 100644 --- a/godot-core/src/builtin/vectors/vector3i.rs +++ b/godot-core/src/builtin/vectors/vector3i.rs @@ -11,6 +11,7 @@ use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; use crate::builtin::math::{FloatExt, GlamConv, GlamType}; +use crate::builtin::meta::impl_godot_as_self; use crate::builtin::{real, RVec3, Vector3, Vector3Axis}; /// Vector used for 3D math using integer coordinates. @@ -160,9 +161,15 @@ impl_from_tuple_for_vector3x!(Vector3i, i32); // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Vector3i { + fn variant_type() -> sys::VariantType { + sys::VariantType::Vector3i + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Vector3i); + impl GlamType for glam::IVec3 { type Mapped = Vector3i; diff --git a/godot-core/src/builtin/vectors/vector4.rs b/godot-core/src/builtin/vectors/vector4.rs index 814b7a7c1..2ab3db12c 100644 --- a/godot-core/src/builtin/vectors/vector4.rs +++ b/godot-core/src/builtin/vectors/vector4.rs @@ -8,6 +8,7 @@ use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; use crate::builtin::math::{FloatExt, GlamConv, GlamType}; +use crate::builtin::meta::impl_godot_as_self; use crate::builtin::{real, RVec4, Vector4i}; use std::fmt; @@ -99,9 +100,15 @@ impl fmt::Display for Vector4 { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Vector4 { + fn variant_type() -> sys::VariantType { + sys::VariantType::Vector4 + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Vector4); + impl GlamType for RVec4 { type Mapped = Vector4; diff --git a/godot-core/src/builtin/vectors/vector4i.rs b/godot-core/src/builtin/vectors/vector4i.rs index daa864308..265a56584 100644 --- a/godot-core/src/builtin/vectors/vector4i.rs +++ b/godot-core/src/builtin/vectors/vector4i.rs @@ -8,6 +8,7 @@ use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; use crate::builtin::math::{FloatExt, GlamConv, GlamType}; +use crate::builtin::meta::impl_godot_as_self; use crate::builtin::{real, RVec4, Vector4, Vector4Axis}; use std::fmt; @@ -148,9 +149,15 @@ impl fmt::Display for Vector4i { // SAFETY: // This type is represented as `Self` in Godot, so `*mut Self` is sound. unsafe impl GodotFfi for Vector4i { + fn variant_type() -> sys::VariantType { + sys::VariantType::Vector4i + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } +impl_godot_as_self!(Vector4i); + impl GlamType for glam::IVec4 { type Mapped = Vector4i; diff --git a/godot-core/src/builtin/vectors/vector_axis.rs b/godot-core/src/builtin/vectors/vector_axis.rs index 2152a218d..64a99dca9 100644 --- a/godot-core/src/builtin/vectors/vector_axis.rs +++ b/godot-core/src/builtin/vectors/vector_axis.rs @@ -4,10 +4,9 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::builtin::meta::{FromGodot, GodotCompatible, ToGodot}; use crate::builtin::{real, Vector2, Vector2i, Vector3, Vector3i, Vector4, Vector4i}; use crate::obj::EngineEnum; -use godot_ffi as sys; -use sys::{ffi_methods, GodotFfi}; /// Access vector components in different order. /// @@ -98,10 +97,20 @@ impl EngineEnum for Vector2Axis { } } -// SAFETY: -// This type is represented as `Self` in Godot, so `*mut Self` is sound. -unsafe impl GodotFfi for Vector2Axis { - ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } +impl GodotCompatible for Vector2Axis { + type Via = i64; +} + +impl ToGodot for Vector2Axis { + fn to_godot(&self) -> Self::Via { + self.ord() as i64 + } +} + +impl FromGodot for Vector2Axis { + fn try_from_godot(via: Self::Via) -> Option { + Self::try_from_ord(via as i32) + } } // ---------------------------------------------------------------------------------------------------------------------------------------------- @@ -136,10 +145,20 @@ impl EngineEnum for Vector3Axis { } } -// SAFETY: -// This type is represented as `Self` in Godot, so `*mut Self` is sound. -unsafe impl GodotFfi for Vector3Axis { - ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } +impl GodotCompatible for Vector3Axis { + type Via = i64; +} + +impl ToGodot for Vector3Axis { + fn to_godot(&self) -> Self::Via { + self.ord() as i64 + } +} + +impl FromGodot for Vector3Axis { + fn try_from_godot(via: Self::Via) -> Option { + Self::try_from_ord(via as i32) + } } // ---------------------------------------------------------------------------------------------------------------------------------------------- @@ -177,10 +196,20 @@ impl EngineEnum for Vector4Axis { } } -// SAFETY: -// This type is represented as `Self` in Godot, so `*mut Self` is sound. -unsafe impl GodotFfi for Vector4Axis { - ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } +impl GodotCompatible for Vector4Axis { + type Via = i64; +} + +impl ToGodot for Vector4Axis { + fn to_godot(&self) -> Self::Via { + self.ord() as i64 + } +} + +impl FromGodot for Vector4Axis { + fn try_from_godot(via: Self::Via) -> Option { + Self::try_from_ord(via as i32) + } } // ---------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/godot-core/src/engine.rs b/godot-core/src/engine.rs index 50ce83d6e..142999aca 100644 --- a/godot-core/src/engine.rs +++ b/godot-core/src/engine.rs @@ -6,10 +6,12 @@ //! Godot engine classes and methods. +use godot_ffi::GodotNullableFfi; + // Re-exports of generated symbols use crate::builtin::{GodotString, NodePath}; use crate::obj::dom::EngineDomain; -use crate::obj::{Gd, GodotClass, Inherits, InstanceId}; +use crate::obj::{Gd, GodotClass, Inherits, InstanceId, RawGd}; pub use crate::gen::central::global; pub use crate::gen::classes::*; @@ -186,7 +188,7 @@ where // Utilities for crate pub(crate) fn debug_string( - ptr: &Gd, + ptr: &RawGd, f: &mut std::fmt::Formatter<'_>, ty: &str, ) -> std::fmt::Result { @@ -194,13 +196,15 @@ pub(crate) fn debug_string( let class: GodotString = ptr.as_object(|obj| Object::get_class(obj)); write!(f, "{ty} {{ id: {id}, class: {class} }}") + } else if ptr.is_null() { + write!(f, "{ty} {{ null }}") } else { write!(f, "{ty} {{ freed obj }}") } } pub(crate) fn display_string( - ptr: &Gd, + ptr: &RawGd, f: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { let string: GodotString = ptr.as_object(Object::to_string); @@ -214,10 +218,14 @@ pub(crate) fn object_ptr_from_id(instance_id: InstanceId) -> sys::GDExtensionObj } pub(crate) fn ensure_object_alive( - instance_id: InstanceId, + instance_id: Option, old_object_ptr: sys::GDExtensionObjectPtr, method_name: &'static str, ) { + let Some(instance_id) = instance_id else { + panic!("{method_name}: instance id is null") + }; + let new_object_ptr = object_ptr_from_id(instance_id); assert!( diff --git a/godot-core/src/obj/base.rs b/godot-core/src/obj/base.rs index a23c441ce..d7ac3f4b0 100644 --- a/godot-core/src/obj/base.rs +++ b/godot-core/src/obj/base.rs @@ -65,13 +65,13 @@ impl Base { impl Debug for Base { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - engine::debug_string(&self.obj, f, "Base") + engine::debug_string(&self.obj.raw, f, "Base") } } impl Display for Base { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - engine::display_string(&self.obj, f) + engine::display_string(&self.obj.raw, f) } } diff --git a/godot-core/src/obj/gd.rs b/godot-core/src/obj/gd.rs index 6160b1578..155ac15e3 100644 --- a/godot-core/src/obj/gd.rs +++ b/godot-core/src/obj/gd.rs @@ -4,30 +4,23 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::fmt; -use std::marker::PhantomData; +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::ops::{Deref, DerefMut}; use std::ptr; use godot_ffi as sys; -use sys::types::OpaqueObject; -use sys::{ - ffi_methods, interface_fn, static_assert_eq_size, GodotFfi, GodotNullablePtr, PtrcallType, -}; - -use crate::builtin::meta::{ClassName, VariantMetadata}; -use crate::builtin::{ - Callable, FromVariant, StringName, ToVariant, Variant, VariantConversionError, VariantType, -}; -use crate::obj::dom::Domain as _; -use crate::obj::mem::Memory as _; -use crate::obj::{ - cap, dom, mem, EngineEnum, GdMut, GdRef, GodotClass, Inherits, InstanceId, Share, -}; +use godot_ffi::VariantType; +use sys::{interface_fn, static_assert_eq_size, GodotNullableFfi}; + +use crate::builtin::meta::{FromGodot, GodotCompatible, GodotType, ToGodot}; +use crate::builtin::{Callable, StringName}; +use crate::obj::{cap, dom, mem, EngineEnum, GodotClass, Inherits, Share}; +use crate::obj::{GdMut, GdRef, InstanceId}; use crate::property::{Export, ExportInfo, Property, TypeStringHint}; -use crate::storage::InstanceStorage; use crate::{callbacks, engine, out}; +use super::RawGd; + /// Smart pointer to objects owned by the Godot engine. /// /// This smart pointer can only hold _objects_ in the Godot sense: instances of Godot classes (`Node`, `RefCounted`, etc.) @@ -59,11 +52,7 @@ pub struct Gd { // Hence separate sys() for GDExtensionTypePtr, and obj_sys() for GDExtensionObjectPtr. // The former is the standard FFI type, while the latter is used in object-specific GDExtension engines. // pub(crate) because accessed in obj::dom - pub(crate) opaque: OpaqueObject, - - // Last known instance ID -- this may no longer be valid! - cached_instance_id: std::cell::Cell, - _marker: PhantomData<*const T>, + pub(crate) raw: RawGd, } // Size equality check (should additionally be covered by mem::transmute()) @@ -143,8 +132,7 @@ where /// reference to the user instance. This can happen through re-entrancy (Rust -> GDScript -> Rust call). // Note: possible names: write/read, hold/hold_mut, r/w, r/rw, ... pub fn bind(&self) -> GdRef { - engine::ensure_object_alive(self.cached_instance_id.get(), self.obj_sys(), "bind"); - GdRef::from_cell(self.storage().get()) + self.raw.bind() } /// Hands out a guard for an exclusive borrow, through which the user instance can be read and written. @@ -159,19 +147,7 @@ where /// * If there is an ongoing function call from GDScript to Rust, which currently holds a `&T` or `&mut T` /// reference to the user instance. This can happen through re-entrancy (Rust -> GDScript -> Rust call). pub fn bind_mut(&mut self) -> GdMut { - engine::ensure_object_alive(self.cached_instance_id.get(), self.obj_sys(), "bind_mut"); - GdMut::from_cell(self.storage().get_mut()) - } - - /// Storage object associated with the extension instance. - pub(crate) fn storage(&self) -> &InstanceStorage { - // SAFETY: instance pointer belongs to this instance. We only get a shared reference, no exclusive access, so even - // calling this from multiple Gd pointers is safe. - // Potential issue is a concurrent free() in multi-threaded access; but that would need to be guarded against inside free(). - unsafe { - let binding = self.resolve_instance_ptr(); - crate::private::as_storage::(binding) - } + self.raw.bind_mut() } /// Storage object associated with the extension instance. @@ -230,34 +206,9 @@ impl Gd { }) } - fn from_opaque(opaque: OpaqueObject) -> Self { - let obj = Self { - opaque, - cached_instance_id: std::cell::Cell::new(InstanceId::from_i64(1)), // placeholder, so we can use obj_sys() - _marker: PhantomData, - }; - - // Initialize instance ID cache - let id = unsafe { interface_fn!(object_get_instance_id)(obj.obj_sys()) }; - let instance_id = InstanceId::try_from_u64(id) - .expect("Gd initialization failed; did you call share() on a dead instance?"); - obj.cached_instance_id.set(instance_id); - - obj - } - /// Returns the instance ID of this object, or `None` if the object is dead. pub fn instance_id_or_none(&self) -> Option { - let known_id = self.cached_instance_id.get(); - - // Refreshes the internal cached ID on every call, as we cannot be sure that the object has not been - // destroyed since last time. The only reliable way to find out is to call is_instance_id_valid(). - // Previously, we cached `None` for dead instances, but that introduced nullability for a rare corner-case optimization. - if engine::utilities::is_instance_id_valid(known_id.to_i64()) { - Some(known_id) - } else { - None - } + self.raw.instance_id_or_none() } /// ⚠️ Returns the instance ID of this object (panics when dead). @@ -278,7 +229,7 @@ impl Gd { /// This function does not check that the returned instance ID points to a valid instance! /// Unless performance is a problem, use [`instance_id()`][Self::instance_id] or [`instance_id_or_none()`][Self::instance_id_or_none] instead. pub fn instance_id_unchecked(&self) -> InstanceId { - self.cached_instance_id.get() + self.raw.cached_instance_id.get().unwrap() } /// Checks if this smart pointer points to a live object (read description!). @@ -291,7 +242,7 @@ impl Gd { /// runtime condition to check against. pub fn is_instance_valid(&self) -> bool { // This call refreshes the instance ID, and recognizes dead objects. - self.instance_id_or_none().is_some() + self.raw.is_instance_valid() } /// **Upcast:** convert into a smart pointer to a base class. Always succeeds. @@ -346,94 +297,15 @@ impl Gd { }) } - // See use-site for explanation. - fn is_cast_valid(&self) -> bool - where - U: GodotClass, - { - let as_obj = - unsafe { self.ffi_cast::() }.expect("Everything inherits object"); - let cast_is_valid = as_obj.is_class(U::class_name().to_godot_string()); - std::mem::forget(as_obj); - cast_is_valid - } - /// Returns `Ok(cast_obj)` on success, `Err(self)` on error fn owned_cast(self) -> Result, Self> where U: GodotClass, { - // Workaround for bug in Godot 4.0 that makes casts always succeed (https://github.com/godot-rust/gdext/issues/158). - // TODO once fixed in Godot, use #[cfg(before_api = "4.1")] - if !self.is_cast_valid::() { - return Err(self); - } - - // The unsafe { std::mem::transmute<&T, &Base>(self.inner()) } relies on the C++ static_cast class casts - // to return the same pointer, however in theory those may yield a different pointer (VTable offset, - // virtual inheritance etc.). It *seems* to work so far, but this is no indication it's not UB. - // - // The Deref/DerefMut impls for T implement an "implicit upcast" on the object (not Gd) level and - // rely on this (e.g. &Node3D -> &Node). - - let result = unsafe { self.ffi_cast::() }; - match result { - Some(cast_obj) => { - // duplicated ref, one must be wiped - std::mem::forget(self); - Ok(cast_obj) - } - None => Err(self), - } - } - - // Note: does not transfer ownership and is thus unsafe. Also operates on shared ref. - // Either the parameter or the return value *must* be forgotten (since reference counts are not updated). - unsafe fn ffi_cast(&self) -> Option> - where - U: GodotClass, - { - let class_tag = interface_fn!(classdb_get_class_tag)(U::class_name().string_sys()); - let cast_object_ptr = interface_fn!(object_cast_to)(self.obj_sys(), class_tag); - - // Create weak object, as ownership will be moved and reference-counter stays the same - sys::ptr_then(cast_object_ptr, |ptr| Gd::from_obj_sys_weak(ptr)) - } - - pub(crate) fn as_ref_counted(&self, apply: impl Fn(&mut engine::RefCounted) -> R) -> R { - debug_assert!( - self.is_instance_valid(), - "as_ref_counted() on freed instance; maybe forgot to increment reference count?" - ); - - let tmp = unsafe { self.ffi_cast::() }; - let mut tmp = tmp.expect("object expected to inherit RefCounted"); - let return_val = - ::Declarer::scoped_mut(&mut tmp, |obj| apply(obj)); - - std::mem::forget(tmp); // no ownership transfer - return_val - } - - pub(crate) fn as_object(&self, apply: impl Fn(&mut engine::Object) -> R) -> R { - // Note: no validity check; this could be called by to_string(), which can be called on dead instances - - let tmp = unsafe { self.ffi_cast::() }; - let mut tmp = tmp.expect("object expected to inherit Object; should never fail"); - // let return_val = apply(tmp.inner_mut()); - let return_val = - ::Declarer::scoped_mut(&mut tmp, |obj| apply(obj)); - - std::mem::forget(tmp); // no ownership transfer - return_val - } - - // Conversions from/to Godot C++ `Object*` pointers - ffi_methods! { - type sys::GDExtensionObjectPtr = Opaque; - - fn from_obj_sys_weak = from_sys; - fn obj_sys = sys; + self.raw + .owned_cast() + .map(Gd::from_ffi) + .map_err(Self::from_ffi) } /// Initializes this `Gd` from the object pointer as a **strong ref**, meaning @@ -443,17 +315,17 @@ impl Gd { /// should explicitly **not** be updated, [`Self::from_obj_sys_weak`] is available. #[doc(hidden)] pub unsafe fn from_obj_sys(ptr: sys::GDExtensionObjectPtr) -> Self { - // Initialize reference counter, if needed - Self::from_obj_sys_weak(ptr).with_inc_refcount() + Self::from_ffi(RawGd::from_obj_sys(ptr)) } - /// Returns `self` but with initialized ref-count. - fn with_inc_refcount(self) -> Self { - // Note: use init_ref and not inc_ref, since this might be the first reference increment. - // Godot expects RefCounted::init_ref to be called instead of RefCounted::reference in that case. - // init_ref also doesn't hurt (except 1 possibly unnecessary check). - T::Mem::maybe_init_ref(&self); - self + #[doc(hidden)] + pub unsafe fn from_obj_sys_weak(ptr: sys::GDExtensionObjectPtr) -> Self { + Self::from_ffi(RawGd::from_obj_sys_weak(ptr)) + } + + #[doc(hidden)] + pub fn obj_sys(&self) -> sys::GDExtensionObjectPtr { + self.raw.obj_sys() } /// Returns a callable referencing a method from this object named `method_name`. @@ -462,51 +334,6 @@ impl Gd { } } -/// _The methods in this impl block are only available for objects `T` that are manually managed, -/// i.e. anything that is not `RefCounted` or inherited from it._

-impl Gd -where - T: GodotClass, - M: mem::PossiblyManual + mem::Memory, -{ - /// Destroy the manually-managed Godot object. - /// - /// Consumes this smart pointer and renders all other `Gd` smart pointers (as well as any GDScript references) to the same object - /// immediately invalid. Using those `Gd` instances will lead to panics, but not undefined behavior. - /// - /// This operation is **safe** and effectively prevents double-free. - /// - /// Not calling `free()` on manually-managed instances causes memory leaks, unless their ownership is delegated, for - /// example to the node tree in case of nodes. - /// - /// # Panics - /// * When the referred-to object has already been destroyed. - /// * When this is invoked on an upcast `Gd` that dynamically points to a reference-counted type (i.e. operation not supported). - pub fn free(self) { - // TODO disallow for singletons, either only at runtime or both at compile time (new memory policy) and runtime - - // Runtime check in case of T=Object, no-op otherwise - let ref_counted = T::Mem::is_ref_counted(&self); - assert_ne!( - ref_counted, Some(true), - "called free() on Gd which points to a RefCounted dynamic type; free() only supported for manually managed types." - ); - - // If ref_counted returned None, that means the instance was destroyed - assert!( - ref_counted == Some(false) && self.is_instance_valid(), - "called free() on already destroyed object" - ); - - // This destroys the Storage instance, no need to run destructor again - unsafe { - interface_fn!(object_destroy)(self.obj_sys()); - } - - std::mem::forget(self); - } -} - impl Deref for Gd { // Target is always an engine class: // * if T is an engine class => T @@ -527,7 +354,7 @@ impl Deref for Gd { // struct Node3D { // object_ptr: sys::GDExtensionObjectPtr, // } - unsafe { std::mem::transmute::<&Self, &Self::Target>(self) } + self.raw.as_target() } } @@ -541,86 +368,10 @@ impl DerefMut for Gd { // same (i.e. `opaque` has the same value, but not address). // // The `&mut self` guarantees that no other base access can take place for *the same Gd instance* (access to other Gds is OK). - unsafe { std::mem::transmute::<&mut Self, &mut Self::Target>(self) } - } -} - -// SAFETY: -// - `move_return_ptr` -// When the `call_type` is `PtrcallType::Virtual`, and the current type is known to inherit from `RefCounted` -// then we use `ref_get_object`. Otherwise we use `Gd::from_obj_sys`. -// - `from_arg_ptr` -// When the `call_type` is `PtrcallType::Virtual`, and the current type is known to inherit from `RefCounted` -// then we use `ref_set_object`. Otherwise we use `std::ptr::write`. Finally we forget `self` as we pass -// ownership to the caller. -unsafe impl GodotFfi for Gd -where - T: GodotClass, -{ - ffi_methods! { type sys::GDExtensionTypePtr = Opaque; - fn from_sys; - fn from_sys_init; - fn sys; - } - - // For more context around `ref_get_object` and `ref_set_object`, see: - // https://github.com/godotengine/godot-cpp/issues/954 - - unsafe fn from_arg_ptr(ptr: sys::GDExtensionTypePtr, call_type: PtrcallType) -> Self { - let obj_ptr = if T::Mem::pass_as_ref(call_type) { - // ptr is `Ref*` - // See the docs for `PtrcallType::Virtual` for more info on `Ref`. - interface_fn!(ref_get_object)(ptr as sys::GDExtensionRefPtr) - } else if cfg!(since_api = "4.1") || matches!(call_type, PtrcallType::Virtual) { - // ptr is `T**` - *(ptr as *mut sys::GDExtensionObjectPtr) - } else { - // ptr is `T*` - ptr as sys::GDExtensionObjectPtr - }; - - // obj_ptr is `T*` - Self::from_obj_sys(obj_ptr) - } - - unsafe fn move_return_ptr(self, ptr: sys::GDExtensionTypePtr, call_type: PtrcallType) { - if T::Mem::pass_as_ref(call_type) { - interface_fn!(ref_set_object)(ptr as sys::GDExtensionRefPtr, self.obj_sys()) - } else { - ptr::write(ptr as *mut _, self.opaque) - } - // We've passed ownership to caller. - std::mem::forget(self); - } - - fn as_arg_ptr(&self) -> sys::GDExtensionConstTypePtr { - // We're passing a reference to the object to the callee. If the reference count needs to be - // incremented then the callee will do so. We do not need to prematurely do so. - // - // In Rust terms, if `T` is refcounted then we are effectively passing a `&Arc`, and the callee - // would need to call `.clone()` if desired. - - // In 4.0, argument pointers are passed to godot as `T*`, except for in virtual method calls. We - // can't perform virtual method calls currently, so they are always `T*`. - // - // In 4.1 argument pointers were standardized to always be `T**`. - #[cfg(before_api = "4.1")] - { - self.sys_const() - } - - #[cfg(since_api = "4.1")] - { - std::ptr::addr_of!(self.opaque) as sys::GDExtensionConstTypePtr - } + self.raw.as_target_mut() } } -// SAFETY: -// `Gd` will only contain types that inherit from `crate::engine::Object`. -// Godots `Object` in turn is known to be nullable and always a pointer. -unsafe impl GodotNullablePtr for Gd {} - impl Gd { /// Runs `init_fn` on the address of a pointer (initialized to null). If that pointer is still null after the `init_fn` call, /// then `None` will be returned; otherwise `Gd::from_obj_sys(ptr)`. @@ -640,76 +391,85 @@ impl Gd { // Note: see _call_native_mb_ret_obj() in godot-cpp, which does things quite different (e.g. querying the instance binding). // Initialize pointer with given function, return Some(ptr) on success and None otherwise - let object_ptr = raw_object_init(init_fn); + let object_ptr = super::raw_object_init(init_fn); // Do not increment ref-count; assumed to be return value from FFI. sys::ptr_then(object_ptr, |ptr| Gd::from_obj_sys_weak(ptr)) } } -/// Runs `init_fn` on the address of a pointer (initialized to null), then returns that pointer, possibly still null. -/// -/// # Safety -/// `init_fn` must be a function that correctly handles a _type pointer_ pointing to an _object pointer_. -#[doc(hidden)] -pub unsafe fn raw_object_init( - init_fn: impl FnOnce(sys::GDExtensionUninitializedTypePtr), -) -> sys::GDExtensionObjectPtr { - // return_ptr has type GDExtensionTypePtr = GDExtensionObjectPtr* = OpaqueObject* = Object** - // (in other words, the type-ptr contains the _address_ of an object-ptr). - let mut object_ptr: sys::GDExtensionObjectPtr = ptr::null_mut(); - let return_ptr: *mut sys::GDExtensionObjectPtr = ptr::addr_of_mut!(object_ptr); - - init_fn(return_ptr as sys::GDExtensionUninitializedTypePtr); - - // We don't need to know if Object** is null, but if Object* is null; return_ptr has the address of a local (never null). - object_ptr +/// _The methods in this impl block are only available for objects `T` that are manually managed, +/// i.e. anything that is not `RefCounted` or inherited from it._

+impl Gd +where + T: GodotClass, + M: mem::PossiblyManual + mem::Memory, +{ + /// Destroy the manually-managed Godot object. + /// + /// Consumes this smart pointer and renders all other `Gd` smart pointers (as well as any GDScript references) to the same object + /// immediately invalid. Using those `Gd` instances will lead to panics, but not undefined behavior. + /// + /// This operation is **safe** and effectively prevents double-free. + /// + /// Not calling `free()` on manually-managed instances causes memory leaks, unless their ownership is delegated, for + /// example to the node tree in case of nodes. + /// + /// # Panics + /// * When the referred-to object has already been destroyed. + /// * When this is invoked on an upcast `Gd` that dynamically points to a reference-counted type (i.e. operation not supported). + pub fn free(self) { + self.raw.free() + } } // ---------------------------------------------------------------------------------------------------------------------------------------------- // Trait impls -/// Destructor with semantics depending on memory strategy. -/// -/// * If this `Gd` smart pointer holds a reference-counted type, this will decrement the reference counter. -/// If this was the last remaining reference, dropping it will invoke `T`'s destructor. -/// -/// * If the held object is manually-managed, **nothing happens**. -/// To destroy manually-managed `Gd` pointers, you need to call [`Self::free()`]. -impl Drop for Gd { - fn drop(&mut self) { - // No-op for manually managed objects - - out!("Gd::drop <{}>", std::any::type_name::()); - // SAFETY: This `Gd` wont be dropped again after this. - let is_last = unsafe { T::Mem::maybe_dec_ref(self) }; // may drop - if is_last { - unsafe { - interface_fn!(object_destroy)(self.obj_sys()); - } - } +impl GodotCompatible for Gd { + type Via = Gd; +} - /*let st = self.storage(); - out!(" objd; self={:?}, val={:?}", st as *mut _, st.lifecycle); - //out!(" objd2; self={:?}, val={:?}", st as *mut _, st.lifecycle); - - // If destruction is triggered by Godot, Storage already knows about it, no need to notify it - if !self.storage().destroyed_by_godot() { - let is_last = T::Mem::maybe_dec_ref(&self); // may drop - if is_last { - //T::Declarer::destroy(self); - unsafe { - interface_fn!(object_destroy)(self.obj_sys()); - } - } - }*/ +impl ToGodot for Gd { + fn to_godot(&self) -> Self::Via { + self.clone() + } + + fn into_godot(self) -> Self::Via { + self + } +} + +impl FromGodot for Gd { + fn try_from_godot(via: Self::Via) -> Option { + Some(via) + } +} + +impl GodotType for Gd { + type Ffi = RawGd; + + fn to_ffi(&self) -> Self::Ffi { + self.raw.clone() + } + + fn into_ffi(self) -> Self::Ffi { + self.raw + } + + fn try_from_ffi(raw: Self::Ffi) -> Option { + if raw.is_null() { + None + } else { + Some(Self { raw }) + } } } impl Clone for Gd { fn clone(&self) -> Self { out!("Gd::clone"); - Self::from_opaque(self.opaque).with_inc_refcount() + Self::from_ffi(self.raw.clone()) } } @@ -770,73 +530,6 @@ impl Export for Gd { // Trait impls Property, Export and TypeStringHint for Option> are covered by blanket impl for Option -impl FromVariant for Gd { - fn try_from_variant(variant: &Variant) -> Result { - let result_or_none = unsafe { - // TODO(#234) replace Gd:: with Self when Godot stops allowing illegal conversions - // See https://github.com/godot-rust/gdext/issues/158 - - // TODO(uninit) - see if we can use from_sys_init() - use ::godot_ffi::AsUninit; - - Gd::::from_sys_init_opt(|self_ptr| { - let converter = sys::builtin_fn!(object_from_variant); - converter(self_ptr.as_uninit(), variant.var_sys()); - }) - }; - - // The conversion method `variant_to_object` does NOT increment the reference-count of the object; we need to do that manually. - // (This behaves differently in the opposite direction `object_to_variant`.) - result_or_none - .map(|obj| obj.with_inc_refcount()) - // TODO(#234) remove this cast when Godot stops allowing illegal conversions - // (See https://github.com/godot-rust/gdext/issues/158) - .and_then(|obj| obj.owned_cast().ok()) - .ok_or(VariantConversionError::VariantIsNil) - } -} - -impl FromVariant for Option> { - fn try_from_variant(variant: &Variant) -> Result { - if variant.is_nil() { - Ok(None) - } else { - Gd::try_from_variant(variant).map(Some) - } - } -} - -impl ToVariant for Gd { - fn to_variant(&self) -> Variant { - // The conversion method `object_to_variant` DOES increment the reference-count of the object; so nothing to do here. - // (This behaves differently in the opposite direction `variant_to_object`.) - - unsafe { - Variant::from_var_sys_init(|variant_ptr| { - let converter = sys::builtin_fn!(object_to_variant); - - // Note: this is a special case because of an inconsistency in Godot, where sometimes the equivalency is - // GDExtensionTypePtr == Object** and sometimes GDExtensionTypePtr == Object*. Here, it is the former, thus extra pointer. - // Reported at https://github.com/godotengine/godot/issues/61967 - let type_ptr = self.sys(); - converter( - variant_ptr, - ptr::addr_of!(type_ptr) as sys::GDExtensionTypePtr, - ); - }) - } - } -} - -impl ToVariant for Option> { - fn to_variant(&self) -> Variant { - match self { - Some(gd) => gd.to_variant(), - None => Variant::nil(), - } - } -} - impl PartialEq for Gd { /// ⚠️ Returns whether two `Gd` pointers point to the same object. /// @@ -850,25 +543,15 @@ impl PartialEq for Gd { impl Eq for Gd {} -impl fmt::Display for Gd { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - engine::display_string(self, f) +impl Display for Gd { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + engine::display_string(&self.raw, f) } } -impl fmt::Debug for Gd { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - engine::debug_string(self, f, "Gd") - } -} - -impl VariantMetadata for Gd { - fn variant_type() -> VariantType { - VariantType::Object - } - - fn class_name() -> ClassName { - T::class_name() +impl Debug for Gd { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + engine::debug_string(&self.raw, f, "Gd") } } diff --git a/godot-core/src/obj/instance_id.rs b/godot-core/src/obj/instance_id.rs index 6a5725451..b6e5cac30 100644 --- a/godot-core/src/obj/instance_id.rs +++ b/godot-core/src/obj/instance_id.rs @@ -4,10 +4,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::builtin::meta::VariantMetadata; -use crate::builtin::{FromVariant, ToVariant, Variant, VariantConversionError}; -use godot_ffi as sys; -use godot_ffi::{ffi_methods, GodotFfi, VariantType}; +use crate::builtin::meta::{FromGodot, GodotCompatible, ToGodot}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::num::NonZeroU64; @@ -77,23 +74,19 @@ impl Debug for InstanceId { } } -// SAFETY: -// This type is represented as `Self` in Godot, so `*mut Self` is sound. -unsafe impl GodotFfi for InstanceId { - ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } +impl GodotCompatible for InstanceId { + type Via = u64; } -impl FromVariant for InstanceId { - fn try_from_variant(variant: &Variant) -> Result { - i64::try_from_variant(variant) - .and_then(|i| InstanceId::try_from_i64(i).ok_or(VariantConversionError::BadValue)) +impl ToGodot for InstanceId { + fn to_godot(&self) -> Self::Via { + self.value.get() } } -impl ToVariant for InstanceId { - fn to_variant(&self) -> Variant { - let int = self.to_i64(); - int.to_variant() +impl FromGodot for InstanceId { + fn try_from_godot(via: Self::Via) -> Option { + Self::try_from_u64(via) } } @@ -121,13 +114,3 @@ impl ToVariant for Option { } } */ - -impl VariantMetadata for InstanceId { - fn variant_type() -> VariantType { - VariantType::Int - } - - fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64 - } -} diff --git a/godot-core/src/obj/mod.rs b/godot-core/src/obj/mod.rs index 59c356931..45cda1ed0 100644 --- a/godot-core/src/obj/mod.rs +++ b/godot-core/src/obj/mod.rs @@ -14,10 +14,12 @@ mod base; mod gd; mod guards; mod instance_id; +mod raw; mod traits; pub use base::*; pub use gd::*; pub use guards::*; pub use instance_id::*; +pub use raw::*; pub use traits::*; diff --git a/godot-core/src/obj/raw.rs b/godot-core/src/obj/raw.rs new file mode 100644 index 000000000..bd42a167d --- /dev/null +++ b/godot-core/src/obj/raw.rs @@ -0,0 +1,587 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use std::ptr; + +use godot_ffi as sys; +use sys::{interface_fn, GodotFfi, GodotNullableFfi, PtrcallType}; + +use crate::builtin::meta::{ + ClassName, FromGodot, GodotCompatible, GodotFfiVariant, GodotType, ToGodot, +}; +use crate::builtin::{Variant, VariantConversionError}; +use crate::obj::dom::Domain as _; +use crate::obj::mem::Memory as _; +use crate::obj::{dom, mem, GodotClass}; +use crate::obj::{GdMut, GdRef, InstanceId}; +use crate::storage::InstanceStorage; +use crate::{engine, out}; + +pub struct RawGd { + pub(super) obj: *mut T, + pub(super) cached_instance_id: std::cell::Cell>, +} + +impl RawGd { + pub(super) fn new_null() -> Self { + Self { + obj: ptr::null_mut(), + cached_instance_id: std::cell::Cell::new(None), + } + } + + pub(super) fn from_obj_sys_weak(obj: sys::GDExtensionObjectPtr) -> Self { + let mut instance_id = None; + if !obj.is_null() { + let id = + unsafe { interface_fn!(object_get_instance_id)(obj as sys::GDExtensionObjectPtr) }; + instance_id = InstanceId::try_from_u64(id); + } + + Self { + obj: obj as *mut T, + cached_instance_id: std::cell::Cell::new(instance_id), + } + } + + pub(super) fn from_obj_sys(obj: sys::GDExtensionObjectPtr) -> Self { + Self::from_obj_sys_weak(obj).with_inc_refcount() + } + + pub(crate) fn instance_id_or_none(&self) -> Option { + let known_id = match self.cached_instance_id.get() { + // Already dead + None => return None, + + // Possibly alive + Some(id) => id, + }; + + // Refreshes the internal cached ID on every call, as we cannot be sure that the object has not been + // destroyed since last time. The only reliable way to find out is to call is_instance_id_valid(). + if engine::utilities::is_instance_id_valid(known_id.to_i64()) { + Some(known_id) + } else { + self.cached_instance_id.set(None); + None + } + } + + pub(super) fn is_instance_valid(&self) -> bool { + // This call refreshes the instance ID, and recognizes dead objects. + self.instance_id_or_none().is_some() + } + + // Note: does not transfer ownership and is thus unsafe. Also operates on shared ref. + // Either the parameter or the return value *must* be forgotten (since reference counts are not updated). + pub(super) unsafe fn ffi_cast(&self) -> Option> + where + U: GodotClass, + { + if self.is_null() { + return Some(RawGd::new_null()); + } + + let class_tag = interface_fn!(classdb_get_class_tag)(U::class_name().string_sys()); + let cast_object_ptr = interface_fn!(object_cast_to)(self.obj_sys(), class_tag); + + // Create weak object, as ownership will be moved and reference-counter stays the same + sys::ptr_then(cast_object_ptr, |ptr| RawGd::from_obj_sys_weak(ptr)) + } + + // See use-site for explanation. + fn is_cast_valid(&self) -> bool + where + U: GodotClass, + { + if self.is_null() { + return true; + } + + let as_obj = + unsafe { self.ffi_cast::() }.expect("Everything inherits object"); + let cast_is_valid = as_obj + .as_target() + .is_class(U::class_name().to_godot_string()); + std::mem::forget(as_obj); + cast_is_valid + } + + /// Returns `Ok(cast_obj)` on success, `Err(self)` on error + pub(super) fn owned_cast(self) -> Result, Self> + where + U: GodotClass, + { + // Workaround for bug in Godot 4.0 that makes casts always succeed (https://github.com/godot-rust/gdext/issues/158). + // TODO once fixed in Godot, use #[cfg(before_api = "4.1")] + if !self.is_cast_valid::() { + return Err(self); + } + + // The unsafe { std::mem::transmute<&T, &Base>(self.inner()) } relies on the C++ static_cast class casts + // to return the same pointer, however in theory those may yield a different pointer (VTable offset, + // virtual inheritance etc.). It *seems* to work so far, but this is no indication it's not UB. + // + // The Deref/DerefMut impls for T implement an "implicit upcast" on the object (not Gd) level and + // rely on this (e.g. &Node3D -> &Node). + + let result = unsafe { self.ffi_cast::() }; + match result { + Some(cast_obj) => { + // duplicated ref, one must be wiped + std::mem::forget(self); + Ok(cast_obj) + } + None => Err(self), + } + } + + /// Returns `self` but with initialized ref-count. + fn with_inc_refcount(self) -> Self { + // Note: use init_ref and not inc_ref, since this might be the first reference increment. + // Godot expects RefCounted::init_ref to be called instead of RefCounted::reference in that case. + // init_ref also doesn't hurt (except 1 possibly unnecessary check). + if self.is_instance_valid() { + T::Mem::maybe_init_ref(&self); + } + self + } + + pub(crate) fn as_ref_counted(&self, apply: impl Fn(&mut engine::RefCounted) -> R) -> R { + debug_assert!( + self.is_instance_valid(), + "as_ref_counted() on freed instance; maybe forgot to increment reference count?" + ); + + let tmp = unsafe { self.ffi_cast::() }; + let mut tmp = tmp.expect("object expected to inherit RefCounted"); + let return_val = + ::Declarer::scoped_mut(&mut tmp, |obj| apply(obj)); + + std::mem::forget(tmp); // no ownership transfer + return_val + } + + pub(crate) fn as_object(&self, apply: impl Fn(&mut engine::Object) -> R) -> R { + // Note: no validity check; this could be called by to_string(), which can be called on dead instances + + let tmp = unsafe { self.ffi_cast::() }; + let mut tmp = tmp.expect("object expected to inherit Object; should never fail"); + // let return_val = apply(tmp.inner_mut()); + let return_val = + ::Declarer::scoped_mut(&mut tmp, |obj| apply(obj)); + + std::mem::forget(tmp); // no ownership transfer + return_val + } + + pub(super) fn as_target( + &self, + ) -> &<::Declarer as dom::Domain>::DerefTarget { + // SAFETY: + // + // This relies on `Gd.opaque` having the layout as `Node3D` (as an example), + // which also needs #[repr(transparent)]: + // + // struct Gd { + // opaque: OpaqueObject, // <- size of GDExtensionObjectPtr + // _marker: PhantomData, // <- ZST + // } + // struct Node3D { + // object_ptr: sys::GDExtensionObjectPtr, + // } + unsafe { + std::mem::transmute::< + &*mut T, + &<::Declarer as dom::Domain>::DerefTarget, + >(&self.obj) + } + } + + pub(super) fn as_target_mut( + &mut self, + ) -> &mut <::Declarer as dom::Domain>::DerefTarget { + // SAFETY: see also Deref + // + // The resulting `&mut T` is transmuted from `&mut OpaqueObject`, i.e. a *pointer* to the `opaque` field. + // `opaque` itself has a different *address* for each Gd instance, meaning that two simultaneous + // DerefMut borrows on two Gd instances will not alias, *even if* the underlying Godot object is the + // same (i.e. `opaque` has the same value, but not address). + // + // The `&mut self` guarantees that no other base access can take place for *the same Gd instance* (access to other Gds is OK). + unsafe { + std::mem::transmute::< + &mut *mut T, + &mut <::Declarer as dom::Domain>::DerefTarget, + >(&mut self.obj) + } + } + + pub(super) fn obj_sys(&self) -> sys::GDExtensionObjectPtr { + self.obj as sys::GDExtensionObjectPtr + } +} + +impl RawGd +where + T: GodotClass, +{ + /// Hands out a guard for a shared borrow, through which the user instance can be read. + /// + /// The pattern is very similar to interior mutability with standard [`RefCell`][std::cell::RefCell]. + /// You can either have multiple `GdRef` shared guards, or a single `GdMut` exclusive guard to a Rust + /// `GodotClass` instance, independently of how many `Gd` smart pointers point to it. There are runtime + /// checks to ensure that Rust safety rules (e.g. no `&` and `&mut` coexistence) are upheld. + /// + /// # Panics + /// * If another `Gd` smart pointer pointing to the same Rust instance has a live `GdMut` guard bound. + /// * If there is an ongoing function call from GDScript to Rust, which currently holds a `&mut T` + /// reference to the user instance. This can happen through re-entrancy (Rust -> GDScript -> Rust call). + // Note: possible names: write/read, hold/hold_mut, r/w, r/rw, ... + pub fn bind(&self) -> GdRef { + engine::ensure_object_alive(self.cached_instance_id.get(), self.obj_sys(), "bind"); + GdRef::from_cell(self.storage().get()) + } + + /// Hands out a guard for an exclusive borrow, through which the user instance can be read and written. + /// + /// The pattern is very similar to interior mutability with standard [`RefCell`][std::cell::RefCell]. + /// You can either have multiple `GdRef` shared guards, or a single `GdMut` exclusive guard to a Rust + /// `GodotClass` instance, independently of how many `Gd` smart pointers point to it. There are runtime + /// checks to ensure that Rust safety rules (e.g. no `&mut` aliasing) are upheld. + /// + /// # Panics + /// * If another `Gd` smart pointer pointing to the same Rust instance has a live `GdRef` or `GdMut` guard bound. + /// * If there is an ongoing function call from GDScript to Rust, which currently holds a `&T` or `&mut T` + /// reference to the user instance. This can happen through re-entrancy (Rust -> GDScript -> Rust call). + pub fn bind_mut(&mut self) -> GdMut { + engine::ensure_object_alive(self.cached_instance_id.get(), self.obj_sys(), "bind_mut"); + GdMut::from_cell(self.storage().get_mut()) + } + + /// Storage object associated with the extension instance. + pub(crate) fn storage(&self) -> &InstanceStorage { + // SAFETY: instance pointer belongs to this instance. We only get a shared reference, no exclusive access, so even + // calling this from multiple Gd pointers is safe. + // Potential issue is a concurrent free() in multi-threaded access; but that would need to be guarded against inside free(). + unsafe { + let binding = self.resolve_instance_ptr(); + crate::private::as_storage::(binding) + } + } + + /// Storage object associated with the extension instance. + // pub(crate) fn storage_mut(&mut self) -> &mut InstanceStorage { + // // SAFETY: + // unsafe { + // let binding = self.resolve_instance_ptr(); + // crate::private::as_storage_mut::(binding) + // } + // } + + unsafe fn resolve_instance_ptr(&self) -> sys::GDExtensionClassInstancePtr { + let callbacks = crate::storage::nop_instance_callbacks(); + let token = sys::get_library() as *mut std::ffi::c_void; + let binding = interface_fn!(object_get_instance_binding)(self.obj_sys(), token, &callbacks); + + debug_assert!( + !binding.is_null(), + "Class {} -- null instance; does the class have a Godot creator function?", + std::any::type_name::() + ); + binding as sys::GDExtensionClassInstancePtr + } +} + +// SAFETY: +// - `move_return_ptr` +// When the `call_type` is `PtrcallType::Virtual`, and the current type is known to inherit from `RefCounted` +// then we use `ref_get_object`. Otherwise we use `Gd::from_obj_sys`. +// - `from_arg_ptr` +// When the `call_type` is `PtrcallType::Virtual`, and the current type is known to inherit from `RefCounted` +// then we use `ref_set_object`. Otherwise we use `std::ptr::write`. Finally we forget `self` as we pass +// ownership to the caller. +unsafe impl GodotFfi for RawGd +where + T: GodotClass, +{ + fn variant_type() -> sys::VariantType { + sys::VariantType::Object + } + + unsafe fn from_sys(ptr: sys::GDExtensionTypePtr) -> Self { + Self::from_obj_sys_weak(ptr as sys::GDExtensionObjectPtr) + } + + unsafe fn from_sys_init(init: impl FnOnce(sys::GDExtensionUninitializedTypePtr)) -> Self { + let mut raw: std::mem::MaybeUninit = + std::mem::MaybeUninit::uninit(); + init(raw.as_mut_ptr() as sys::GDExtensionUninitializedTypePtr); + Self::from_obj_sys_weak(raw.assume_init()) + } + + fn sys(&self) -> sys::GDExtensionTypePtr { + self.obj as sys::GDExtensionTypePtr + } + + // For more context around `ref_get_object` and `ref_set_object`, see: + // https://github.com/godotengine/godot-cpp/issues/954 + + unsafe fn from_arg_ptr(ptr: sys::GDExtensionTypePtr, call_type: PtrcallType) -> Self { + if ptr.is_null() { + return Self::new_null(); + } + + let obj_ptr = if T::Mem::pass_as_ref(call_type) { + // ptr is `Ref*` + // See the docs for `PtrcallType::Virtual` for more info on `Ref`. + interface_fn!(ref_get_object)(ptr as sys::GDExtensionRefPtr) + } else if cfg!(since_api = "4.1") || matches!(call_type, PtrcallType::Virtual) { + // ptr is `T**` + *(ptr as *mut sys::GDExtensionObjectPtr) + } else { + // ptr is `T*` + ptr as sys::GDExtensionObjectPtr + }; + + // obj_ptr is `T*` + Self::from_obj_sys(obj_ptr) + } + + unsafe fn move_return_ptr(self, ptr: sys::GDExtensionTypePtr, call_type: PtrcallType) { + if T::Mem::pass_as_ref(call_type) { + interface_fn!(ref_set_object)(ptr as sys::GDExtensionRefPtr, self.obj_sys()) + } else { + ptr::write(ptr as *mut _, self.obj) + } + // We've passed ownership to caller. + std::mem::forget(self); + } + + fn as_arg_ptr(&self) -> sys::GDExtensionConstTypePtr { + // We're passing a reference to the object to the callee. If the reference count needs to be + // incremented then the callee will do so. We do not need to prematurely do so. + // + // In Rust terms, if `T` is refcounted then we are effectively passing a `&Arc`, and the callee + // would need to call `.clone()` if desired. + + // In 4.0, argument pointers are passed to godot as `T*`, except for in virtual method calls. We + // can't perform virtual method calls currently, so they are always `T*`. + // + // In 4.1 argument pointers were standardized to always be `T**`. + #[cfg(before_api = "4.1")] + { + self.sys_const() + } + + #[cfg(since_api = "4.1")] + { + std::ptr::addr_of!(self.obj) as sys::GDExtensionConstTypePtr + } + } +} + +impl GodotCompatible for RawGd { + type Via = Self; +} + +impl ToGodot for RawGd { + fn to_godot(&self) -> Self::Via { + self.clone() + } + + fn into_godot(self) -> Self::Via { + self + } +} + +impl FromGodot for RawGd { + fn try_from_godot(via: Self::Via) -> Option { + Some(via) + } +} + +/// _The methods in this impl block are only available for objects `T` that are manually managed, +/// i.e. anything that is not `RefCounted` or inherited from it._

+impl RawGd +where + T: GodotClass, + M: mem::PossiblyManual + mem::Memory, +{ + /// Destroy the manually-managed Godot object. + /// + /// Consumes this smart pointer and renders all other `Gd` smart pointers (as well as any GDScript references) to the same object + /// immediately invalid. Using those `Gd` instances will lead to panics, but not undefined behavior. + /// + /// This operation is **safe** and effectively prevents double-free. + /// + /// Not calling `free()` on manually-managed instances causes memory leaks, unless their ownership is delegated, for + /// example to the node tree in case of nodes. + /// + /// # Panics + /// * When the referred-to object has already been destroyed. + /// * When this is invoked on an upcast `Gd` that dynamically points to a reference-counted type (i.e. operation not supported). + pub fn free(self) { + // TODO disallow for singletons, either only at runtime or both at compile time (new memory policy) and runtime + + // Runtime check in case of T=Object, no-op otherwise + let ref_counted = T::Mem::is_ref_counted(&self); + assert_ne!( + ref_counted, Some(true), + "called free() on Gd which points to a RefCounted dynamic type; free() only supported for manually managed types." + ); + + // If ref_counted returned None, that means the instance was destroyed + assert!( + ref_counted == Some(false) && self.is_instance_valid(), + "called free() on already destroyed object" + ); + + // This destroys the Storage instance, no need to run destructor again + unsafe { + interface_fn!(object_destroy)(self.obj_sys()); + } + + std::mem::forget(self); + } +} + +impl GodotNullableFfi for RawGd { + fn flatten_option(opt: Option) -> Self { + match opt { + Some(raw) => raw, + None => Self::new_null(), + } + } + + fn is_null(&self) -> bool { + self.obj.is_null() + } +} + +/// Runs `init_fn` on the address of a pointer (initialized to null), then returns that pointer, possibly still null. +/// +/// # Safety +/// `init_fn` must be a function that correctly handles a _type pointer_ pointing to an _object pointer_. +#[doc(hidden)] +pub unsafe fn raw_object_init( + init_fn: impl FnOnce(sys::GDExtensionUninitializedTypePtr), +) -> sys::GDExtensionObjectPtr { + // return_ptr has type GDExtensionTypePtr = GDExtensionObjectPtr* = OpaqueObject* = Object** + // (in other words, the type-ptr contains the _address_ of an object-ptr). + let mut object_ptr: sys::GDExtensionObjectPtr = ptr::null_mut(); + let return_ptr: *mut sys::GDExtensionObjectPtr = ptr::addr_of_mut!(object_ptr); + + init_fn(return_ptr as sys::GDExtensionUninitializedTypePtr); + + // We don't need to know if Object** is null, but if Object* is null; return_ptr has the address of a local (never null). + object_ptr +} + +/// Destructor with semantics depending on memory strategy. +/// +/// * If this `Gd` smart pointer holds a reference-counted type, this will decrement the reference counter. +/// If this was the last remaining reference, dropping it will invoke `T`'s destructor. +/// +/// * If the held object is manually-managed, **nothing happens**. +/// To destroy manually-managed `Gd` pointers, you need to call [`Self::free()`]. +impl Drop for RawGd { + fn drop(&mut self) { + // No-op for manually managed objects + + out!("RawGd::drop <{}>", std::any::type_name::()); + // SAFETY: This `Gd` wont be dropped again after this. + let is_last = unsafe { T::Mem::maybe_dec_ref(self) }; // may drop + if is_last { + unsafe { + interface_fn!(object_destroy)(self.obj_sys()); + } + } + + /*let st = self.storage(); + out!(" objd; self={:?}, val={:?}", st as *mut _, st.lifecycle); + //out!(" objd2; self={:?}, val={:?}", st as *mut _, st.lifecycle); + + // If destruction is triggered by Godot, Storage already knows about it, no need to notify it + if !self.storage().destroyed_by_godot() { + let is_last = T::Mem::maybe_dec_ref(&self); // may drop + if is_last { + //T::Declarer::destroy(self); + unsafe { + interface_fn!(object_destroy)(self.obj_sys()); + } + } + }*/ + } +} + +impl Clone for RawGd { + fn clone(&self) -> Self { + out!("RawGd::clone"); + Self::from_obj_sys(self.obj as sys::GDExtensionObjectPtr) + } +} + +impl GodotType for RawGd { + type Ffi = Self; + + fn to_ffi(&self) -> Self::Ffi { + self.clone() + } + + fn into_ffi(self) -> Self::Ffi { + self + } + + fn try_from_ffi(ffi: Self::Ffi) -> Option { + Some(ffi) + } + + fn class_name() -> ClassName { + T::class_name() + } +} + +impl GodotFfiVariant for RawGd { + fn ffi_to_variant(&self) -> Variant { + // The conversion method `object_to_variant` DOES increment the reference-count of the object; so nothing to do here. + // (This behaves differently in the opposite direction `variant_to_object`.) + + unsafe { + Variant::from_var_sys_init(|variant_ptr| { + let converter = sys::builtin_fn!(object_to_variant); + + // Note: this is a special case because of an inconsistency in Godot, where sometimes the equivalency is + // GDExtensionTypePtr == Object** and sometimes GDExtensionTypePtr == Object*. Here, it is the former, thus extra pointer. + // Reported at https://github.com/godotengine/godot/issues/61967 + let type_ptr = self.sys(); + converter( + variant_ptr, + ptr::addr_of!(type_ptr) as sys::GDExtensionTypePtr, + ); + }) + } + } + + fn ffi_from_variant(variant: &Variant) -> Result { + let raw = unsafe { + // TODO(#234) replace Gd:: with Self when Godot stops allowing illegal conversions + // See https://github.com/godot-rust/gdext/issues/158 + + // TODO(uninit) - see if we can use from_sys_init() + + // raw_object_init? + RawGd::::from_sys_init(|self_ptr| { + let converter = sys::builtin_fn!(object_from_variant); + converter(self_ptr, variant.var_sys()); + }) + }; + + raw.with_inc_refcount() + .owned_cast() + .map_err(|_| VariantConversionError::BadType) + } +} diff --git a/godot-core/src/obj/traits.rs b/godot-core/src/obj/traits.rs index 750e88574..393570d91 100644 --- a/godot-core/src/obj/traits.rs +++ b/godot-core/src/obj/traits.rs @@ -264,15 +264,14 @@ mod private { pub mod dom { use super::private::Sealed; - use crate::obj::{Gd, GodotClass}; - use std::ops::DerefMut; + use crate::obj::{GodotClass, RawGd}; /// Trait that specifies who declares a given `GodotClass`. pub trait Domain: Sealed { type DerefTarget; #[doc(hidden)] - fn scoped_mut(obj: &mut Gd, closure: F) -> R + fn scoped_mut(obj: &mut RawGd, closure: F) -> R where T: GodotClass, F: FnOnce(&mut T) -> R; @@ -284,12 +283,12 @@ pub mod dom { impl Domain for EngineDomain { type DerefTarget = T; - fn scoped_mut(obj: &mut Gd, closure: F) -> R + fn scoped_mut(obj: &mut RawGd, closure: F) -> R where T: GodotClass, F: FnOnce(&mut T) -> R, { - closure(obj.deref_mut()) + closure(obj.as_target_mut()) } } @@ -299,13 +298,13 @@ pub mod dom { impl Domain for UserDomain { type DerefTarget = T::Base; - fn scoped_mut(obj: &mut Gd, closure: F) -> R + fn scoped_mut(obj: &mut RawGd, closure: F) -> R where T: GodotClass, F: FnOnce(&mut T) -> R, { let mut guard = obj.bind_mut(); - closure(guard.deref_mut()) + closure(&mut *guard) } } } @@ -313,21 +312,21 @@ pub mod dom { // ---------------------------------------------------------------------------------------------------------------------------------------------- pub mod mem { - use godot_ffi::PtrcallType; + use godot_ffi::{GodotNullableFfi, PtrcallType}; use super::private::Sealed; - use crate::obj::{Gd, GodotClass}; + use crate::obj::{GodotClass, RawGd}; use crate::out; /// Specifies the memory pub trait Memory: Sealed { /// Initialize reference counter #[doc(hidden)] - fn maybe_init_ref(obj: &Gd); + fn maybe_init_ref(obj: &RawGd); /// If ref-counted, then increment count #[doc(hidden)] - fn maybe_inc_ref(obj: &Gd); + fn maybe_inc_ref(obj: &RawGd); /// If ref-counted, then decrement count. Returns `true` if the count hit 0 and the object can be /// safely freed. @@ -343,11 +342,11 @@ pub mod mem { /// then the reference count must either be incremented before it hits 0, or some [`Gd`] referencing /// this object must be forgotten. #[doc(hidden)] - unsafe fn maybe_dec_ref(obj: &Gd) -> bool; + unsafe fn maybe_dec_ref(obj: &RawGd) -> bool; /// Check if ref-counted, return `None` if information is not available (dynamic and obj dead) #[doc(hidden)] - fn is_ref_counted(obj: &Gd) -> Option; + fn is_ref_counted(obj: &RawGd) -> Option; /// Returns `true` if argument and return pointers are passed as `Ref` pointers given this /// [`PtrcallType`]. @@ -367,24 +366,33 @@ pub mod mem { pub struct StaticRefCount {} impl Sealed for StaticRefCount {} impl Memory for StaticRefCount { - fn maybe_init_ref(obj: &Gd) { + fn maybe_init_ref(obj: &RawGd) { out!(" Stat::init <{}>", std::any::type_name::()); + if obj.is_null() { + return; + } obj.as_ref_counted(|refc| { let success = refc.init_ref(); assert!(success, "init_ref() failed"); }); } - fn maybe_inc_ref(obj: &Gd) { + fn maybe_inc_ref(obj: &RawGd) { out!(" Stat::inc <{}>", std::any::type_name::()); + if obj.is_null() { + return; + } obj.as_ref_counted(|refc| { let success = refc.reference(); assert!(success, "reference() failed"); }); } - unsafe fn maybe_dec_ref(obj: &Gd) -> bool { + unsafe fn maybe_dec_ref(obj: &RawGd) -> bool { out!(" Stat::dec <{}>", std::any::type_name::()); + if obj.is_null() { + return false; + } obj.as_ref_counted(|refc| { let is_last = refc.unreference(); out!(" +-- was last={is_last}"); @@ -392,7 +400,7 @@ pub mod mem { }) } - fn is_ref_counted(_obj: &Gd) -> Option { + fn is_ref_counted(_obj: &RawGd) -> Option { Some(true) } @@ -406,7 +414,7 @@ pub mod mem { pub struct DynamicRefCount {} impl Sealed for DynamicRefCount {} impl Memory for DynamicRefCount { - fn maybe_init_ref(obj: &Gd) { + fn maybe_init_ref(obj: &RawGd) { out!(" Dyn::init <{}>", std::any::type_name::()); if obj .instance_id_or_none() @@ -417,7 +425,7 @@ pub mod mem { } } - fn maybe_inc_ref(obj: &Gd) { + fn maybe_inc_ref(obj: &RawGd) { out!(" Dyn::inc <{}>", std::any::type_name::()); if obj .instance_id_or_none() @@ -428,7 +436,7 @@ pub mod mem { } } - unsafe fn maybe_dec_ref(obj: &Gd) -> bool { + unsafe fn maybe_dec_ref(obj: &RawGd) -> bool { out!(" Dyn::dec <{}>", std::any::type_name::()); if obj .instance_id_or_none() @@ -441,7 +449,7 @@ pub mod mem { } } - fn is_ref_counted(obj: &Gd) -> Option { + fn is_ref_counted(obj: &RawGd) -> Option { // Return `None` if obj is dead obj.instance_id_or_none().map(|id| id.is_ref_counted()) } @@ -454,12 +462,12 @@ pub mod mem { pub struct ManualMemory {} impl Sealed for ManualMemory {} impl Memory for ManualMemory { - fn maybe_init_ref(_obj: &Gd) {} - fn maybe_inc_ref(_obj: &Gd) {} - unsafe fn maybe_dec_ref(_obj: &Gd) -> bool { + fn maybe_init_ref(_obj: &RawGd) {} + fn maybe_inc_ref(_obj: &RawGd) {} + unsafe fn maybe_dec_ref(_obj: &RawGd) -> bool { false } - fn is_ref_counted(_obj: &Gd) -> Option { + fn is_ref_counted(_obj: &RawGd) -> Option { Some(false) } } diff --git a/godot-ffi/src/godot_ffi.rs b/godot-ffi/src/godot_ffi.rs index 36aa07f4e..b829939a9 100644 --- a/godot-ffi/src/godot_ffi.rs +++ b/godot-ffi/src/godot_ffi.rs @@ -5,10 +5,10 @@ */ use crate as sys; -use std::error::Error; use std::marker::PhantomData; -use std::ptr; +/// Types that can directly and fully represent some Godot type. +/// /// Adds methods to convert from and to Godot FFI pointers. /// See [crate::ffi_methods] for ergonomic implementation. /// @@ -18,6 +18,11 @@ use std::ptr; /// must properly initialize and clean up values given the [`PtrcallType`] provided by the caller. #[doc(hidden)] // shows up in implementors otherwise pub unsafe trait GodotFfi { + fn variant_type() -> sys::VariantType; + fn default_param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_NONE + } + /// Construct from Godot opaque pointer. /// /// # Safety @@ -129,94 +134,12 @@ pub unsafe fn from_sys_init_or_init_default( // ---------------------------------------------------------------------------------------------------------------------------------------------- -/// Marks a type as having a nullable counterpart in Godot. -/// -/// This trait primarily exists to implement GodotFfi for `Option>`, which is not possible -/// due to Rusts orphan rule. The rule also enforces better API design, though. `godot_ffi` should -/// not concern itself with the details of how Godot types work and merely defines the FFI abstraction. -/// By having a marker trait for nullable types, we can provide a generic implementation for -/// compatible types, without knowing their definition. -/// -/// # Safety +/// Types that can represent null-values. /// -/// The type has to have a pointer-sized counterpart in Godot, which needs to be nullable. -/// So far, this only applies to class types (Object hierarchy). -pub unsafe trait GodotNullablePtr: GodotFfi {} - -unsafe impl GodotFfi for Option -where - T: GodotNullablePtr, -{ - unsafe fn from_sys(ptr: sys::GDExtensionTypePtr) -> Self { - crate::ptr_then(ptr, |ptr| T::from_sys(ptr)) - } - - unsafe fn from_sys_init(init_fn: impl FnOnce(sys::GDExtensionUninitializedTypePtr)) -> Self { - let mut raw = std::mem::MaybeUninit::uninit(); - init_fn(raw.as_mut_ptr() as sys::GDExtensionUninitializedTypePtr); - - Self::from_sys(raw.assume_init()) - } - - fn sys(&self) -> sys::GDExtensionTypePtr { - match self { - Some(value) => value.sys(), - None => ptr::null_mut() as sys::GDExtensionTypePtr, - } - } - - #[cfg(before_api = "4.1")] - unsafe fn from_arg_ptr(ptr: sys::GDExtensionTypePtr, call_type: PtrcallType) -> Self { - match call_type { - PtrcallType::Standard => option_from_arg_single_ptr(ptr, call_type), - PtrcallType::Virtual => option_from_arg_double_ptr(ptr, call_type), - } - } - - #[cfg(since_api = "4.1")] - unsafe fn from_arg_ptr(ptr: sys::GDExtensionTypePtr, call_type: PtrcallType) -> Self { - option_from_arg_double_ptr(ptr, call_type) - } - - unsafe fn move_return_ptr(self, ptr: sys::GDExtensionTypePtr, call_type: PtrcallType) { - if let Some(value) = self { - value.move_return_ptr(ptr, call_type) - } - } -} - -/// Return an `Option` when `T` is a nullable pointer type and `ptr` is represented as `T**`. -/// -/// # Safety -/// `ptr` must either be null, a pointer to null, or a pointer to a pointer to a `T`. -unsafe fn option_from_arg_double_ptr( - ptr: sys::GDExtensionTypePtr, - call_type: PtrcallType, -) -> Option -where - T: GodotNullablePtr, -{ - if ptr.is_null() || (*(ptr as *mut sys::GDExtensionTypePtr)).is_null() { - None - } else { - Some(T::from_arg_ptr(ptr, call_type)) - } -} - -// 4.1 represents every object as `T**`. -#[cfg(before_api = "4.1")] -/// Return an `Option` when `T` is a nullable pointer type and `ptr` is represented as `T*`. -/// -/// # Safety -/// `ptr` must either be null, or a pointer to a `T`. -unsafe fn option_from_arg_single_ptr( - ptr: sys::GDExtensionTypePtr, - call_type: PtrcallType, -) -> Option -where - T: GodotNullablePtr, -{ - crate::ptr_then(ptr, |ptr| T::from_arg_ptr(ptr, call_type)) +/// Used to blanket implement various conversions over `Option`. +pub trait GodotNullableFfi: Sized + GodotFfi { + fn flatten_option(opt: Option) -> Self; + fn is_null(&self) -> bool; } // ---------------------------------------------------------------------------------------------------------------------------------------------- @@ -247,54 +170,6 @@ pub enum PtrcallType { Virtual, } -/// Trait implemented for all types that can be passed to and from Godot via function calls. -#[doc(hidden)] // shows up in implementors otherwise -pub trait GodotFuncMarshal: Sized { - /// The type used when passing a value to/from Godot. - type Via: GodotFfi; - /// Error type returned when a value of type `Via` fails to be converted into `Self`. - type FromViaError: Error; - /// Error type returned when a value of type `Self` fails to be converted into `Via`. - type IntoViaError: Error; - - /// Try to convert the value from [`Self::Via`] to [`Self`]. - fn try_from_via(via: Self::Via) -> Result; - - /// Try to convert the value from [`Self`] to [`Self::Via`]. - fn try_into_via(self) -> Result; - - /// Used for function arguments. On failure, the argument which can't be converted to Self is returned. - /// - /// # Safety - /// The value behind `ptr` must be the C FFI type that corresponds to [`Self::Via`]. - /// See also [`GodotFfi::from_arg_ptr`]. - unsafe fn try_from_arg( - ptr: sys::GDExtensionTypePtr, - call_type: PtrcallType, - ) -> Result { - let via = Self::Via::from_arg_ptr(ptr, call_type); - - Self::try_from_via(via) - } - - /// Used for function return values. On failure, `self` which can't be converted to Via is returned. - /// - /// # Safety - /// `dst` must point to an initialized value of type [`Self::Via`]. - /// See also [`GodotFfi::move_return_ptr`]. - unsafe fn try_return( - self, - dst: sys::GDExtensionTypePtr, - call_type: PtrcallType, - ) -> Result<(), Self::IntoViaError> { - let via = self.try_into_via()?; - - via.move_return_ptr(dst, call_type); - - Ok(()) - } -} - // ---------------------------------------------------------------------------------------------------------------------------------------------- // Macros to choose a certain implementation of `GodotFfi` trait for GDExtensionTypePtr; // or a free-standing `impl` for concrete sys pointers such as GDExtensionObjectPtr. @@ -539,10 +414,10 @@ where // ---------------------------------------------------------------------------------------------------------------------------------------------- // Implementation for common types (needs to be this crate due to orphan rule) mod scalars { - use super::{GodotFfi, GodotFuncMarshal, PrimitiveConversionError}; + use super::GodotFfi; use crate as sys; - use std::convert::Infallible; + /* macro_rules! impl_godot_marshalling { ($T:ty) => { // SAFETY: @@ -592,34 +467,44 @@ mod scalars { } }; } + */ + unsafe impl GodotFfi for bool { + fn variant_type() -> sys::VariantType { + sys::VariantType::Bool + } + + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } + } + + unsafe impl GodotFfi for i64 { + fn variant_type() -> sys::VariantType { + sys::VariantType::Int + } + + fn default_param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64 + } - // Directly implements GodotFfi + GodotFuncMarshal (through blanket impl) - impl_godot_marshalling!(bool); - impl_godot_marshalling!(i64); - impl_godot_marshalling!(f64); - - // Only implements GodotFuncMarshal - impl_godot_marshalling!(i32 as i64); - impl_godot_marshalling!(u32 as i64); - impl_godot_marshalling!(i16 as i64); - impl_godot_marshalling!(u16 as i64); - impl_godot_marshalling!(i8 as i64); - impl_godot_marshalling!(u8 as i64); - - // Some places Godot will pass along 64-bit numbers that are intended to be interpreted as unsigned - // integers despite Godot integers being signed by default. - impl_godot_marshalling!(u64 as i64; lossy); - impl_godot_marshalling!(f32 as f64; lossy); - - unsafe impl GodotFfi for *const T { ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } - unsafe impl GodotFfi for *mut T { + unsafe impl GodotFfi for f64 { + fn variant_type() -> sys::VariantType { + sys::VariantType::Float + } + + fn default_param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE + } + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } unsafe impl GodotFfi for () { + fn variant_type() -> sys::VariantType { + sys::VariantType::Nil + } + unsafe fn from_sys(_ptr: sys::GDExtensionTypePtr) -> Self { // Do nothing } @@ -651,23 +536,4 @@ mod scalars { // Do nothing } } - - impl GodotFuncMarshal for T - where - T: GodotFfi, - { - type Via = Self; - type FromViaError = Infallible; - type IntoViaError = Infallible; - - #[inline] - fn try_from_via(via: Self::Via) -> Result { - Ok(via) - } - - #[inline] - fn try_into_via(self) -> Result { - Ok(self) - } - } } diff --git a/godot-ffi/src/lib.rs b/godot-ffi/src/lib.rs index 230f63807..6d5a2327a 100644 --- a/godot-ffi/src/lib.rs +++ b/godot-ffi/src/lib.rs @@ -48,8 +48,8 @@ use std::ffi::CStr; pub use paste; pub use crate::godot_ffi::{ - from_sys_init_or_init_default, GodotFfi, GodotFuncMarshal, GodotNullablePtr, - PrimitiveConversionError, PtrcallType, + from_sys_init_or_init_default, GodotFfi, GodotNullableFfi, PrimitiveConversionError, + PtrcallType, }; // Method tables diff --git a/godot-macros/src/class/data_models/property.rs b/godot-macros/src/class/data_models/property.rs index d843d62a2..4b272e59c 100644 --- a/godot-macros/src/class/data_models/property.rs +++ b/godot-macros/src/class/data_models/property.rs @@ -174,7 +174,7 @@ pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream { }; export_tokens.push(quote! { - use ::godot::builtin::meta::VariantMetadata; + use ::godot::sys::GodotFfi; let (hint, hint_string) = #hint; let usage = #usage_flags; diff --git a/godot-macros/src/derive/derive_from_variant.rs b/godot-macros/src/derive/derive_from_variant.rs index 6421cac9c..bba211850 100644 --- a/godot-macros/src/derive/derive_from_variant.rs +++ b/godot-macros/src/derive/derive_from_variant.rs @@ -15,7 +15,7 @@ fn has_attr_skip(attributes: &[venial::Attribute]) -> bool { has_attr(attributes, "variant", "skip") } -pub fn derive_from_variant(decl: Declaration) -> ParseResult { +pub fn derive_from_godot(decl: Declaration) -> ParseResult { let DeclInfo { where_, generic_params, @@ -24,8 +24,8 @@ pub fn derive_from_variant(decl: Declaration) -> ParseResult { } = decl_get_info(&decl); let mut body = quote! { - let root = variant.try_to::<::godot::builtin::Dictionary>()?; - let root = root.get(#name_string).ok_or(::godot::builtin::VariantConversionError::BadType)?; + let root = variant.try_to::<::godot::builtin::Dictionary>().ok()?; + let root = root.get(#name_string)?; }; match decl { @@ -52,7 +52,7 @@ pub fn derive_from_variant(decl: Declaration) -> ParseResult { _ if has_attr_skip(&enum_v.attributes) => { quote! { if root == Variant::nil() { - return Ok(Self::default()); + return Some(Self::default()); } } } @@ -60,7 +60,7 @@ pub fn derive_from_variant(decl: Declaration) -> ParseResult { quote! { let child = root.try_to::(); if child == Ok(String::from(#variant_name_string)) { - return Ok(Self::#variant_name); + return Some(Self::#variant_name); } } } @@ -92,7 +92,7 @@ pub fn derive_from_variant(decl: Declaration) -> ParseResult { body = quote! { #body #matches - Err(::godot::builtin::VariantConversionError::MissingValue) + None }; } } @@ -103,10 +103,11 @@ pub fn derive_from_variant(decl: Declaration) -> ParseResult { let gen = generic_params.as_ref().map(|x| x.as_inline_args()); Ok(quote! { - impl #generic_params ::godot::builtin::FromVariant for #name #gen #where_ { - fn try_from_variant( - variant: &::godot::builtin::Variant - ) -> Result { + impl #generic_params ::godot::builtin::meta::FromGodot for #name #gen #where_ { + fn try_from_godot( + variant: ::godot::builtin::Variant + ) -> Option { + let variant = &variant; #body } } @@ -127,21 +128,20 @@ fn make_named_struct( } else { ( quote! { - let #ident = root.get(#string_ident) - .ok_or(::godot::builtin::VariantConversionError::MissingValue)?; + let #ident = root.get(#string_ident)?; }, - quote! { #ident: #ident.try_to()? }, + quote! { #ident: #ident.try_to().ok()? }, ) } }); let (set_idents, set_self): (Vec<_>, Vec<_>) = fields.unzip(); *body = quote! { #body - let root = root.try_to::<::godot::builtin::Dictionary>()?; + let root = root.try_to::<::godot::builtin::Dictionary>().ok()?; #( #set_idents )* - Ok(Self { #(#set_self,)* }) + Some(Self { #(#set_self,)* }) } } @@ -161,9 +161,9 @@ fn make_tuple_struct( } } else { quote! { - let #ident = root.pop_front() - .ok_or(::godot::builtin::VariantConversionError::MissingValue)? - .try_to::<#field_type>()?; + let #ident = root.pop_front()? + .try_to::<#field_type>() + .ok()?; } }, ) @@ -171,11 +171,11 @@ fn make_tuple_struct( let (idents, ident_set): (Vec<_>, Vec<_>) = ident_and_set.unzip(); *body = quote! { #body - let mut root = root.try_to::<::godot::builtin::VariantArray>()?; + let mut root = root.try_to::<::godot::builtin::VariantArray>().ok()?; #( #ident_set )* - Ok(Self( + Some(Self( #(#idents,)* )) }; @@ -183,12 +183,12 @@ fn make_tuple_struct( fn make_new_type_struct(body: &mut TokenStream, fields: venial::TupleStructFields) { *body = if has_attr_skip(&fields.fields.first().unwrap().0.attributes) { - quote! { Ok(Self::default()) } + quote! { Some(Self::default()) } } else { quote! { #body - let root = root.try_to()?; - Ok(Self(root)) + let root = root.try_to().ok()?; + Some(Self(root)) } } } @@ -196,7 +196,7 @@ fn make_new_type_struct(body: &mut TokenStream, fields: venial::TupleStructField fn make_unit_struct(body: &mut TokenStream) { *body = quote! { #body - return Ok(Self); + return Some(Self); } } @@ -209,7 +209,7 @@ fn make_enum_new_type( quote! { if let Ok(child) = root.try_to::<::godot::builtin::Dictionary>() { if let Some(variant) = child.get(#variant_name_string) { - return Ok(Self::#variant_name(variant.try_to::<#field_type>()?)); + return Some(Self::#variant_name(variant.try_to::<#field_type>().ok()?)); } } } @@ -225,7 +225,7 @@ fn make_enum_new_type_skipped( if let Ok(child) = root.try_to::<::godot::builtin::Dictionary>() { if let Some(v) = child.get(#variant_name_string) { if v.is_nil() { - return Ok(Self::#variant_name( + return Some(Self::#variant_name( <#field_type as Default>::default(), )); } @@ -248,9 +248,9 @@ fn make_enum_tuple( } } else { quote! { - let #ident = variant.pop_front() - .ok_or(::godot::builtin::VariantConversionError::MissingValue)? - .try_to::<#field_type>()?; + let #ident = variant.pop_front()? + .try_to::<#field_type>() + .ok()?; } }; (ident.to_token_stream(), set_ident) @@ -261,9 +261,9 @@ fn make_enum_tuple( let child = root.try_to::<::godot::builtin::Dictionary>(); if let Ok(child) = child { if let Some(variant) = child.get(#variant_name_string) { - let mut variant = variant.try_to::<::godot::builtin::VariantArray>()?; + let mut variant = variant.try_to::<::godot::builtin::VariantArray>().ok()?; #(#set_idents)* - return Ok(Self::#variant_name(#(#idents ,)*)); + return Some(Self::#variant_name(#(#idents ,)*)); } } } @@ -283,9 +283,9 @@ fn make_enum_named( } } else { quote! { - let #field_name = variant.get(#field_name_string) - .ok_or(::godot::builtin::VariantConversionError::MissingValue)? - .try_to::<#field_type>()?; + let #field_name = variant.get(#field_name_string)? + .try_to::<#field_type>() + .ok()?; } }; (field_name.to_token_stream(), set_field) @@ -295,11 +295,11 @@ fn make_enum_named( quote! { if let Ok(root) = root.try_to::<::godot::builtin::Dictionary>() { if let Some(variant) = root.get(#variant_name_string) { - let variant = variant.try_to::<::godot::builtin::Dictionary>()?; + let variant = variant.try_to::<::godot::builtin::Dictionary>().ok()?; #( #set_fields )* - return Ok(Self::#variant_name { + return Some(Self::#variant_name { #( #fields, )* }); } diff --git a/godot-macros/src/derive/derive_godot_compatible.rs b/godot-macros/src/derive/derive_godot_compatible.rs new file mode 100644 index 000000000..26cf0dc8c --- /dev/null +++ b/godot-macros/src/derive/derive_godot_compatible.rs @@ -0,0 +1,29 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use proc_macro2::TokenStream; +use quote::quote; +use venial::Declaration; + +use crate::util::{decl_get_info, DeclInfo}; +use crate::ParseResult; + +pub fn derive_godot_compatible(decl: Declaration) -> ParseResult { + let DeclInfo { + where_, + generic_params, + name, + .. + } = decl_get_info(&decl); + + let gen = generic_params.as_ref().map(|x| x.as_inline_args()); + + Ok(quote! { + impl #generic_params ::godot::builtin::meta::GodotCompatible for #name #gen #where_ { + type Via = ::godot::builtin::Variant; + } + }) +} diff --git a/godot-macros/src/derive/derive_to_variant.rs b/godot-macros/src/derive/derive_to_variant.rs index 3fb342072..baf18dab1 100644 --- a/godot-macros/src/derive/derive_to_variant.rs +++ b/godot-macros/src/derive/derive_to_variant.rs @@ -11,7 +11,7 @@ use venial::{Declaration, StructFields}; use crate::util::{decl_get_info, has_attr, DeclInfo}; use crate::ParseResult; -pub fn derive_to_variant(decl: Declaration) -> ParseResult { +pub fn derive_to_godot(decl: Declaration) -> ParseResult { let mut body = quote! { let mut root = ::godot::builtin::Dictionary::new(); }; @@ -91,9 +91,9 @@ pub fn derive_to_variant(decl: Declaration) -> ParseResult { }; Ok(quote! { - impl #generic_params ::godot::builtin::ToVariant for #name #gen #where_ { + impl #generic_params ::godot::builtin::meta::ToGodot for #name #gen #where_ { #allow_unreachable - fn to_variant(&self) -> ::godot::builtin::Variant { + fn to_godot(&self) -> ::godot::builtin::Variant { #body } } diff --git a/godot-macros/src/derive/mod.rs b/godot-macros/src/derive/mod.rs index 65869ab16..b49be621e 100644 --- a/godot-macros/src/derive/mod.rs +++ b/godot-macros/src/derive/mod.rs @@ -8,10 +8,12 @@ mod derive_export; mod derive_from_variant; +mod derive_godot_compatible; mod derive_property; mod derive_to_variant; pub(crate) use derive_export::*; pub(crate) use derive_from_variant::*; +pub(crate) use derive_godot_compatible::*; pub(crate) use derive_property::*; pub(crate) use derive_to_variant::*; diff --git a/godot-macros/src/lib.rs b/godot-macros/src/lib.rs index bc986fbcf..bf1d0a4af 100644 --- a/godot-macros/src/lib.rs +++ b/godot-macros/src/lib.rs @@ -442,13 +442,18 @@ pub fn godot_api(_meta: TokenStream, input: TokenStream) -> TokenStream { translate(input, class::attribute_godot_api) } -/// Derive macro for [ToVariant](../builtin/trait.ToVariant.html) on structs or enums. +#[proc_macro_derive(GodotCompatible)] +pub fn derive_godot_compatible(input: TokenStream) -> TokenStream { + translate(input, derive::derive_godot_compatible) +} + +/// Derive macro for [ToGodot](../builtin/meta/trait.ToGodot.html) on structs or enums. /// /// # Example /// /// ```no_run /// # use godot::prelude::*; -/// #[derive(FromVariant, ToVariant, PartialEq, Debug)] +/// #[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] /// struct StructNamed { /// field1: String, /// field2: i32, @@ -470,18 +475,18 @@ pub fn godot_api(_meta: TokenStream, input: TokenStream) -> TokenStream { /// ``` /// /// You can use the `#[skip]` attribute to ignore a field from being converted to `ToVariant`. -#[proc_macro_derive(ToVariant, attributes(variant))] -pub fn derive_to_variant(input: TokenStream) -> TokenStream { - translate(input, derive::derive_to_variant) +#[proc_macro_derive(ToGodot, attributes(variant))] +pub fn derive_to_godot(input: TokenStream) -> TokenStream { + translate(input, derive::derive_to_godot) } -/// Derive macro for [FromVariant](../builtin/trait.FromVariant.html) on structs or enums. +/// Derive macro for [FromGodot](../builtin/meta/trait.FromVariant.html) on structs or enums. /// /// # Example /// /// ```no_run /// # use godot::prelude::*; -/// #[derive(FromVariant, ToVariant, PartialEq, Debug)] +/// #[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] /// struct StructNamed { /// field1: String, /// field2: i32, @@ -504,9 +509,9 @@ pub fn derive_to_variant(input: TokenStream) -> TokenStream { /// /// You can use the skip attribute to ignore a field from the provided variant and use `Default::default()` /// to get it instead. -#[proc_macro_derive(FromVariant, attributes(variant))] -pub fn derive_from_variant(input: TokenStream) -> TokenStream { - translate(input, derive::derive_from_variant) +#[proc_macro_derive(FromGodot, attributes(variant))] +pub fn derive_from_godot(input: TokenStream) -> TokenStream { + translate(input, derive::derive_from_godot) } /// Derive macro for [Property](../bind/property/trait.Property.html) on enums. diff --git a/godot/src/lib.rs b/godot/src/lib.rs index b61b80f45..e9bc1b6e3 100644 --- a/godot/src/lib.rs +++ b/godot/src/lib.rs @@ -178,7 +178,9 @@ pub mod init { /// Export user-defined classes and methods to be called by the engine. pub mod bind { pub use godot_core::property; - pub use godot_macros::{godot_api, Export, FromVariant, GodotClass, Property, ToVariant}; + pub use godot_macros::{ + godot_api, Export, FromGodot, GodotClass, GodotCompatible, Property, ToGodot, + }; } /// Testing facilities (unstable). @@ -193,9 +195,12 @@ pub use godot_core::private; /// Often-imported symbols. pub mod prelude { pub use super::bind::property::{Export, Property, TypeStringHint}; - pub use super::bind::{godot_api, Export, FromVariant, GodotClass, Property, ToVariant}; + pub use super::bind::{ + godot_api, Export, FromGodot, GodotClass, GodotCompatible, Property, ToGodot, + }; pub use super::builtin::math::FloatExt as _; + pub use super::builtin::meta::{FromGodot, ToGodot}; pub use super::builtin::*; pub use super::builtin::{array, dict, varray}; // Re-export macros. pub use super::engine::{ diff --git a/itest/rust/build.rs b/itest/rust/build.rs index c5e39941b..94419db48 100644 --- a/itest/rust/build.rs +++ b/itest/rust/build.rs @@ -13,7 +13,7 @@ type IoResult = std::io::Result<()>; /// Push with GDScript expr in string macro_rules! pushs { - ($inputs:ident; $GDScriptTy:expr, $RustTy:ty, $gdscript_val:expr, $rust_val:expr) => { + ($inputs:ident; $GDScriptTy:expr, $RustTy:ty, $gdscript_val:expr, $rust_val:expr $(; $($extra:tt)*)?) => { $inputs.push(Input { ident: stringify!($RustTy) .to_ascii_lowercase() @@ -23,6 +23,7 @@ macro_rules! pushs { gdscript_val: $gdscript_val, rust_ty: quote! { $RustTy }, rust_val: quote! { $rust_val }, + extra: quote! { $($($extra)*)? }, }); }; } @@ -38,6 +39,42 @@ macro_rules! push { }; } +macro_rules! push_newtype { + ($inputs:ident; $GDScriptTy:expr, $name:ident($T:ty), $val:expr) => { + push_newtype!($inputs; $GDScriptTy, $name($T), $val, $name($val)); + }; + + ($inputs:ident; $GDScriptTy:expr, $name:ident($T:ty), $gdscript_val:expr, $rust_val:expr) => { + push_newtype!(@s $inputs; $GDScriptTy, $name($T), stringify!($gdscript_val), $rust_val); + }; + + (@s $inputs:ident; $GDScriptTy:expr, $name:ident($T:ty), $gdscript_val:expr, $rust_val:expr) => { + pushs!( + $inputs; $GDScriptTy, $name, $gdscript_val, $rust_val; + + #[derive(Debug, Clone, PartialEq)] + pub struct $name($T); + + impl godot::builtin::meta::GodotCompatible for $name { + type Via = $T; + } + + impl godot::builtin::meta::ToGodot for $name { + #[allow(clippy::clone_on_copy)] + fn to_godot(&self) -> Self::Via { + self.0.clone() + } + } + + impl godot::builtin::meta::FromGodot for $name { + fn try_from_godot(via: Self::Via) -> Option { + Some(Self(via)) + } + } + ); + } +} + // Edit this to change involved types fn collect_inputs() -> Vec { let mut inputs = vec![]; @@ -64,6 +101,27 @@ fn collect_inputs() -> Vec { push!(inputs; Vector3i, Vector3i, Vector3i(-1, -2147483648, 2147483647), Vector3i::new(-1, -2147483648, 2147483647)); push!(inputs; Callable, Callable, Callable(), Callable::invalid()); + push_newtype!(inputs; int, NewI64(i64), -922337203685477580); + push_newtype!(inputs; int, NewI32(i32), -2147483648); + push_newtype!(inputs; int, NewU32(u32), 4294967295); + push_newtype!(inputs; int, NewI16(i16), -32767); + push_newtype!(inputs; int, NewU16(u16), 65535); + push_newtype!(inputs; int, NewI8(i8), -128); + push_newtype!(inputs; int, NewU8(u8), 255); + push_newtype!(inputs; float, NewF32(f32), 12.5); + push_newtype!(inputs; float, NewF64(f64), 127.83156478); + push_newtype!(inputs; bool, NewBool(bool), true); + push_newtype!(inputs; Color, NewColor(Color), Color(0.7, 0.5, 0.3, 0.2), NewColor(Color::from_rgba(0.7, 0.5, 0.3, 0.2))); + push_newtype!(inputs; String, NewString(GodotString), "hello", NewString("hello".into())); + push_newtype!(inputs; StringName, NewStringName(StringName), &"hello", NewStringName("hello".into())); + push_newtype!(@s inputs; NodePath, NewNodePath(NodePath), r#"^"hello""#, NewNodePath("hello".into())); + push_newtype!(inputs; Vector2, NewVector2(Vector2), Vector2(12.5, -3.5), NewVector2(Vector2::new(12.5, -3.5))); + push_newtype!(inputs; Vector3, NewVector3(Vector3), Vector3(117.5, 100.0, -323.25), NewVector3(Vector3::new(117.5, 100.0, -323.25))); + push_newtype!(inputs; Vector4, NewVector4(Vector4), Vector4(-18.5, 24.75, -1.25, 777.875), NewVector4(Vector4::new(-18.5, 24.75, -1.25, 777.875))); + push_newtype!(inputs; Vector2i, NewVector2i(Vector2i), Vector2i(-2147483648, 2147483647), NewVector2i(Vector2i::new(-2147483648, 2147483647))); + push_newtype!(inputs; Vector3i, NewVector3i(Vector3i), Vector3i(-1, -2147483648, 2147483647), NewVector3i(Vector3i::new(-1, -2147483648, 2147483647))); + push_newtype!(inputs; Callable, NewCallable(Callable), Callable(), NewCallable(Callable::invalid())); + // Data structures // TODO enable below, when GDScript has typed array literals, or find a hack with eval/lambdas /*pushs!(inputs; Array[int], Array, @@ -93,9 +151,11 @@ fn collect_inputs() -> Vec { fn main() { let inputs = collect_inputs(); let methods = generate_rust_methods(&inputs); + let extras = inputs.iter().map(|input| &input.extra); let rust_tokens = quote::quote! { use godot::builtin::*; + use godot::builtin::meta::*; use godot::obj::InstanceId; use godot::engine::global::Error; @@ -108,6 +168,8 @@ fn main() { impl GenFfi { #(#methods)* } + + #(#extras)* }; let rust_output_dir = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen")); @@ -162,6 +224,7 @@ struct Input { gdscript_val: &'static str, rust_ty: TokenStream, rust_val: TokenStream, + extra: TokenStream, } fn generate_rust_methods(inputs: &[Input]) -> Vec { diff --git a/itest/rust/src/builtin_tests/containers/callable_test.rs b/itest/rust/src/builtin_tests/containers/callable_test.rs index 8de52b947..5efcc750d 100644 --- a/itest/rust/src/builtin_tests/containers/callable_test.rs +++ b/itest/rust/src/builtin_tests/containers/callable_test.rs @@ -6,7 +6,8 @@ use godot::bind::{godot_api, GodotClass}; use godot::builtin::inner::InnerCallable; -use godot::builtin::{varray, Callable, GodotString, StringName, ToVariant, Variant}; +use godot::builtin::meta::ToGodot; +use godot::builtin::{varray, Callable, GodotString, StringName, Variant}; use godot::engine::{Node2D, Object}; use godot::obj::Gd; diff --git a/itest/rust/src/builtin_tests/containers/dictionary_test.rs b/itest/rust/src/builtin_tests/containers/dictionary_test.rs index 8fe332647..acc8ce08c 100644 --- a/itest/rust/src/builtin_tests/containers/dictionary_test.rs +++ b/itest/rust/src/builtin_tests/containers/dictionary_test.rs @@ -6,7 +6,8 @@ use std::collections::{HashMap, HashSet}; -use godot::builtin::{dict, varray, Dictionary, FromVariant, ToVariant, Variant}; +use godot::builtin::meta::{FromGodot, ToGodot}; +use godot::builtin::{dict, varray, Dictionary, Variant}; use crate::framework::{expect_panic, itest}; diff --git a/itest/rust/src/builtin_tests/containers/variant_test.rs b/itest/rust/src/builtin_tests/containers/variant_test.rs index 3a9211f06..7b98f1a5a 100644 --- a/itest/rust/src/builtin_tests/containers/variant_test.rs +++ b/itest/rust/src/builtin_tests/containers/variant_test.rs @@ -7,10 +7,8 @@ use std::cmp::Ordering; use std::fmt::Display; -use godot::builtin::{ - dict, varray, FromVariant, GodotString, NodePath, StringName, ToVariant, Variant, Vector2, - Vector3, -}; +use godot::builtin::meta::{FromGodot, ToGodot}; +use godot::builtin::{dict, varray, GodotString, NodePath, StringName, Variant, Vector2, Vector3}; use godot::builtin::{ Basis, Dictionary, VariantArray, VariantConversionError, VariantOperator, VariantType, }; @@ -414,7 +412,7 @@ fn variant_hash_correct() { fn truncate_bad(original_value: i64) where - T: FromVariant + Display, + T: FromGodot + Display, { let variant = original_value.to_variant(); let result = T::try_from_variant(&variant); @@ -431,8 +429,8 @@ where fn equal(lhs: T, rhs: U, expected: bool) where - T: ToVariant, - U: ToVariant, + T: ToGodot, + U: ToGodot, { if expected { assert_eq!(lhs.to_variant(), rhs.to_variant()); @@ -443,9 +441,9 @@ where fn evaluate(op: VariantOperator, lhs: T, rhs: U, expected: E) where - T: ToVariant, - U: ToVariant, - E: ToVariant, + T: ToGodot, + U: ToGodot, + E: ToGodot, { let lhs = lhs.to_variant(); let rhs = rhs.to_variant(); @@ -456,8 +454,8 @@ where fn evaluate_fail(op: VariantOperator, lhs: T, rhs: U) where - T: ToVariant, - U: ToVariant, + T: ToGodot, + U: ToGodot, { let lhs = lhs.to_variant(); let rhs = rhs.to_variant(); @@ -467,8 +465,8 @@ where fn total_order(lhs: T, rhs: U, expected_order: Ordering) where - T: ToVariant, - U: ToVariant, + T: ToGodot, + U: ToGodot, { fn eval(v: Option) -> bool { v.expect("comparison is valid").to::() diff --git a/itest/rust/src/builtin_tests/geometry/basis_test.rs b/itest/rust/src/builtin_tests/geometry/basis_test.rs index a7761c0a0..87b24930d 100644 --- a/itest/rust/src/builtin_tests/geometry/basis_test.rs +++ b/itest/rust/src/builtin_tests/geometry/basis_test.rs @@ -6,7 +6,8 @@ use godot::builtin::inner::InnerBasis; use godot::builtin::math::assert_eq_approx; -use godot::builtin::{real, Basis, EulerOrder, RealConv, ToVariant, VariantOperator, Vector3}; +use godot::builtin::meta::ToGodot; +use godot::builtin::{real, Basis, EulerOrder, RealConv, VariantOperator, Vector3}; use crate::framework::itest; diff --git a/itest/rust/src/builtin_tests/geometry/plane_test.rs b/itest/rust/src/builtin_tests/geometry/plane_test.rs index 706f07998..da1aab8bf 100644 --- a/itest/rust/src/builtin_tests/geometry/plane_test.rs +++ b/itest/rust/src/builtin_tests/geometry/plane_test.rs @@ -8,7 +8,8 @@ use crate::framework::itest; use godot::builtin::inner::InnerPlane; use godot::builtin::math::{assert_eq_approx, ApproxEq}; -use godot::builtin::{real, Plane, RealConv, ToVariant, Vector3}; +use godot::builtin::meta::ToGodot; +use godot::builtin::{real, Plane, RealConv, Vector3}; use std::fmt::Debug; @@ -117,7 +118,7 @@ fn plane_intersect_segment() { "intersect_segment", a.intersect_segment(b, c) .as_ref() - .map(ToVariant::to_variant) + .map(ToGodot::to_variant) .unwrap_or_default(), inner_a.intersects_segment(b, c), ); @@ -130,7 +131,7 @@ fn plane_intersect_segment() { "intersect_segment", a.intersect_segment(b, c) .as_ref() - .map(ToVariant::to_variant) + .map(ToGodot::to_variant) .unwrap_or_default(), inner_a.intersects_segment(b, c), ); @@ -146,7 +147,7 @@ fn plane_intersect_ray() { "intersect_ray", a.intersect_ray(b, c) .as_ref() - .map(ToVariant::to_variant) + .map(ToGodot::to_variant) .unwrap_or_default(), inner_a.intersects_ray(b, c), ); @@ -157,7 +158,7 @@ fn plane_intersect_ray() { "intersect_ray", a.intersect_ray(b, c) .as_ref() - .map(ToVariant::to_variant) + .map(ToGodot::to_variant) .unwrap_or_default(), inner_a.intersects_ray(b, c), ); @@ -215,7 +216,7 @@ fn plane_intersect_3() { "intersect_3", a.intersect_3(&b, &c) .as_ref() - .map(ToVariant::to_variant) + .map(ToGodot::to_variant) .unwrap_or_default(), inner_a.intersect_3(b, c), ); @@ -228,7 +229,7 @@ fn plane_intersect_3() { "intersect_3", a.intersect_3(&b, &c) .as_ref() - .map(ToVariant::to_variant) + .map(ToGodot::to_variant) .unwrap_or_default(), inner_a.intersect_3(b, c), ); diff --git a/itest/rust/src/common.rs b/itest/rust/src/common.rs index 489ffccac..15d0616cb 100644 --- a/itest/rust/src/common.rs +++ b/itest/rust/src/common.rs @@ -6,13 +6,13 @@ use std::fmt::Debug; -use godot::builtin::{FromVariant, ToVariant}; +use godot::builtin::meta::{FromGodot, ToGodot}; pub fn roundtrip(value: T) where - T: FromVariant + ToVariant + PartialEq + Debug, + T: FromGodot + ToGodot + PartialEq + Debug, { - // TODO test other roundtrip (first FromVariant, then ToVariant) + // TODO test other roundtrip (first FromGodot, then ToGodot) // Some values can be represented in Variant, but not in T (e.g. Variant(0i64) -> Option -> Variant is lossy) let variant = value.to_variant(); diff --git a/itest/rust/src/framework/runner.rs b/itest/rust/src/framework/runner.rs index 878046afd..f78b49cbe 100644 --- a/itest/rust/src/framework/runner.rs +++ b/itest/rust/src/framework/runner.rs @@ -7,7 +7,8 @@ use std::time::{Duration, Instant}; use godot::bind::{godot_api, GodotClass}; -use godot::builtin::{Array, GodotString, ToVariant, Variant, VariantArray}; +use godot::builtin::meta::ToGodot; +use godot::builtin::{Array, GodotString, Variant, VariantArray}; use godot::engine::{Engine, Node, Os}; use godot::log::godot_error; use godot::obj::Gd; diff --git a/itest/rust/src/object_tests/object_test.rs b/itest/rust/src/object_tests/object_test.rs index 157812982..3ee73cc13 100644 --- a/itest/rust/src/object_tests/object_test.rs +++ b/itest/rust/src/object_tests/object_test.rs @@ -8,13 +8,13 @@ use std::cell::{Cell, RefCell}; use std::rc::Rc; use godot::bind::{godot_api, GodotClass}; -use godot::builtin::{ - FromVariant, GodotString, StringName, ToVariant, Variant, VariantConversionError, Vector3, -}; +use godot::builtin::meta::{FromGodot, ToGodot}; +use godot::builtin::{GodotString, StringName, Variant, VariantConversionError, Vector3}; use godot::engine::{ file_access, Area2D, Camera3D, FileAccess, Node, Node3D, Object, RefCounted, RefCountedVirtual, }; -use godot::obj::{Base, Gd, Inherits, InstanceId}; +use godot::obj::{Base, Gd, Inherits, InstanceId, RawGd}; +use godot::prelude::meta::GodotType; use godot::sys::{self, GodotFfi}; use crate::framework::{expect_panic, itest, TestContext}; @@ -76,10 +76,12 @@ fn object_user_roundtrip_return() { let obj: Gd = Gd::new(user); assert_eq!(obj.bind().value, value); - let ptr = obj.sys(); + let raw = obj.to_ffi(); + let ptr = raw.sys(); std::mem::forget(obj); - let obj2 = unsafe { Gd::::from_sys(ptr) }; + let raw2 = unsafe { RawGd::::from_sys(ptr) }; + let obj2 = Gd::from_ffi(raw2); assert_eq!(obj2.bind().value, value); } // drop @@ -90,12 +92,14 @@ fn object_user_roundtrip_write() { let obj: Gd = Gd::new(user); assert_eq!(obj.bind().value, value); + let raw = obj.to_ffi(); - let obj2 = unsafe { - Gd::::from_sys_init(|ptr| { - obj.move_return_ptr(sys::AsUninit::force_init(ptr), sys::PtrcallType::Standard) + let raw2 = unsafe { + RawGd::::from_sys_init(|ptr| { + raw.move_return_ptr(sys::AsUninit::force_init(ptr), sys::PtrcallType::Standard) }) }; + let obj2 = Gd::from_ffi(raw2); assert_eq!(obj2.bind().value, value); } // drop @@ -107,9 +111,11 @@ fn object_engine_roundtrip() { obj.set_position(pos); assert_eq!(obj.get_position(), pos); - let ptr = obj.sys(); + let raw = obj.to_ffi(); + let ptr = raw.sys(); - let obj2 = unsafe { Gd::::from_sys(ptr) }; + let raw2 = unsafe { RawGd::::from_sys(ptr) }; + let obj2 = Gd::from_ffi(raw2); assert_eq!(obj2.get_position(), pos); obj.free(); } @@ -386,7 +392,7 @@ fn object_engine_convert_variant_nil() { assert_eq!( Gd::::try_from_variant(&nil), - Err(VariantConversionError::VariantIsNil), + Err(VariantConversionError::BadValue), "try_from_variant(&nil)" ); diff --git a/itest/rust/src/object_tests/virtual_methods_test.rs b/itest/rust/src/object_tests/virtual_methods_test.rs index 5cfda5393..c3d40e4d3 100644 --- a/itest/rust/src/object_tests/virtual_methods_test.rs +++ b/itest/rust/src/object_tests/virtual_methods_test.rs @@ -9,10 +9,11 @@ use crate::framework::{itest, TestContext}; use godot::bind::{godot_api, GodotClass}; +use godot::builtin::meta::ToGodot; use godot::builtin::{ real, varray, Color, GodotString, PackedByteArray, PackedColorArray, PackedFloat32Array, PackedInt32Array, PackedStringArray, PackedVector2Array, PackedVector3Array, RealConv, - StringName, ToVariant, Variant, VariantArray, Vector2, Vector3, + StringName, Variant, VariantArray, Vector2, Vector3, }; use godot::engine::notify::NodeNotification; use godot::engine::resource_loader::CacheMode; @@ -484,7 +485,7 @@ impl CollisionObject2DTest { fn get_viewport(&self) -> Variant { self.viewport .as_ref() - .map(ToVariant::to_variant) + .map(ToGodot::to_variant) .unwrap_or(Variant::nil()) } } diff --git a/itest/rust/src/register_tests/derive_variant_test.rs b/itest/rust/src/register_tests/derive_variant_test.rs index 58e464123..6640074da 100644 --- a/itest/rust/src/register_tests/derive_variant_test.rs +++ b/itest/rust/src/register_tests/derive_variant_test.rs @@ -6,45 +6,45 @@ use std::fmt::Debug; -use godot::bind::FromVariant; -use godot::bind::ToVariant; -use godot::builtin::{dict, varray, FromVariant, ToVariant, Variant}; +use godot::bind::{FromGodot, GodotCompatible, ToGodot}; +use godot::builtin::meta::{FromGodot, ToGodot}; +use godot::builtin::{dict, varray, Variant}; use crate::common::roundtrip; use crate::framework::itest; // ---------------------------------------------------------------------------------------------------------------------------------------------- -// General FromVariant/ToVariant derive tests +// General FromGodot/ToGodot derive tests -#[derive(FromVariant, ToVariant, PartialEq, Debug)] +#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] struct StructUnit; -#[derive(FromVariant, ToVariant, PartialEq, Debug)] +#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] struct StructNewType(String); -#[derive(FromVariant, ToVariant, PartialEq, Debug)] +#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] struct StructTuple(String, i32); -#[derive(FromVariant, ToVariant, PartialEq, Debug)] +#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] struct StructNamed { field1: String, field2: i32, } -#[derive(FromVariant, ToVariant, PartialEq, Debug)] +#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] struct StructGenWhere(T) where - T: ToVariant + FromVariant; + T: ToGodot + FromGodot; trait Bound {} -#[derive(FromVariant, ToVariant, PartialEq, Debug)] -struct StructGenBound(T); +#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] +struct StructGenBound(T); -#[derive(FromVariant, ToVariant, PartialEq, Debug, Clone)] +#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug, Clone)] enum Uninhabited {} -#[derive(FromVariant, ToVariant, PartialEq, Debug, Clone)] +#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug, Clone)] enum Enum { Unit, OneTuple(i32), @@ -145,7 +145,7 @@ macro_rules! roundtrip_with_skip { }; } -#[derive(Debug, Default, Clone, PartialEq, ToVariant, FromVariant)] +#[derive(Debug, Default, Clone, PartialEq, ToGodot, FromGodot, GodotCompatible)] enum EnumWithSkip { #[variant(skip)] Skipped(String), @@ -209,10 +209,10 @@ roundtrip_with_skip!( // ---------------------------------------------------------------------------------------------------------------------------------------------- // Skipping of structs -#[derive(Debug, Default, PartialEq, ToVariant, FromVariant)] +#[derive(Debug, Default, PartialEq, ToGodot, FromGodot, GodotCompatible)] struct NewTypeStructWithSkip(#[variant(skip)] String); -#[derive(Debug, Default, PartialEq, ToVariant, FromVariant)] +#[derive(Debug, Default, PartialEq, ToGodot, FromGodot, GodotCompatible)] struct StructWithSkip { #[variant(skip)] skipped_field: String, diff --git a/itest/rust/src/register_tests/option_ffi_test.rs b/itest/rust/src/register_tests/option_ffi_test.rs index 0c4a822cb..a7d4351ec 100644 --- a/itest/rust/src/register_tests/option_ffi_test.rs +++ b/itest/rust/src/register_tests/option_ffi_test.rs @@ -4,6 +4,8 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use godot::obj::RawGd; +use godot::prelude::meta::GodotType; use godot::prelude::{godot_api, Gd, GodotClass, Node, Object, RefCounted}; use godot::sys::GodotFfi; @@ -12,9 +14,11 @@ use crate::framework::itest; #[itest] fn option_some_sys_conversion() { let v = Some(Object::new_alloc()); - let ptr = v.sys(); + let v_raw = v.to_ffi(); + let ptr = v_raw.sys(); - let v2 = unsafe { Option::>::from_sys(ptr) }; + let v2_raw = unsafe { RawGd::::from_sys(ptr) }; + let v2 = Option::>::from_ffi(v2_raw); assert_eq!(v2, v); // We're testing this behavior. @@ -24,10 +28,12 @@ fn option_some_sys_conversion() { #[itest] fn option_none_sys_conversion() { - let v = None; - let ptr = v.sys(); + let v: Option> = None; + let v_raw = v.to_ffi(); + let ptr = v_raw.sys(); - let v2 = unsafe { Option::>::from_sys(ptr) }; + let v2_raw = unsafe { RawGd::::from_sys(ptr) }; + let v2 = Option::>::from_ffi(v2_raw); assert_eq!(v2, v); } From ffe241a3a8141ce2a5963a24d9401f309aa17141 Mon Sep 17 00:00:00 2001 From: Lili Zoey Date: Mon, 2 Oct 2023 12:38:45 +0200 Subject: [PATCH 2/2] Various cleanup and changes to `GodotConvert` and related after discussion --- godot-codegen/src/util.rs | 8 +- godot-core/src/builtin/array.rs | 10 +- godot-core/src/builtin/callable.rs | 3 +- .../src/builtin/meta/godot_compat/impls.rs | 248 ------------- .../builtin/meta/godot_compatible/impls.rs | 252 +++++++++++++ .../{godot_compat => godot_compatible}/mod.rs | 16 +- godot-core/src/builtin/meta/mod.rs | 38 +- godot-core/src/builtin/meta/signature.rs | 4 +- godot-core/src/builtin/string/mod.rs | 6 +- godot-core/src/builtin/string/string_name.rs | 4 +- godot-core/src/builtin/variant/impls.rs | 4 + godot-core/src/builtin/variant/mod.rs | 5 +- godot-core/src/builtin/vectors/vector_axis.rs | 26 +- godot-core/src/engine.rs | 20 +- godot-core/src/obj/base.rs | 4 +- godot-core/src/obj/gd.rs | 140 ++++---- godot-core/src/obj/instance_id.rs | 29 +- godot-core/src/obj/mod.rs | 2 + godot-core/src/obj/raw.rs | 336 +++++++++--------- godot-core/src/obj/traits.rs | 18 +- godot-ffi/src/godot_ffi.rs | 3 + .../src/derive/derive_godot_compatible.rs | 2 +- godot-macros/src/lib.rs | 8 +- godot-macros/src/util/mod.rs | 4 +- godot/src/lib.rs | 4 +- itest/rust/build.rs | 2 +- itest/rust/src/object_tests/object_test.rs | 4 +- itest/rust/src/object_tests/property_test.rs | 2 +- .../src/register_tests/derive_variant_test.rs | 24 +- 29 files changed, 605 insertions(+), 621 deletions(-) delete mode 100644 godot-core/src/builtin/meta/godot_compat/impls.rs create mode 100644 godot-core/src/builtin/meta/godot_compatible/impls.rs rename godot-core/src/builtin/meta/{godot_compat => godot_compatible}/mod.rs (91%) diff --git a/godot-codegen/src/util.rs b/godot-codegen/src/util.rs index c27007360..6571fedf5 100644 --- a/godot-codegen/src/util.rs +++ b/godot-codegen/src/util.rs @@ -334,19 +334,19 @@ pub fn make_enum_definition(enum_: &Enum) -> TokenStream { } #index_enum_impl - impl crate::builtin::meta::GodotCompatible for #enum_name { - type Via = i64; + impl crate::builtin::meta::GodotConvert for #enum_name { + type Via = i32; } impl crate::builtin::meta::ToGodot for #enum_name { fn to_godot(&self) -> Self::Via { - ::ord(*self) as i64 + ::ord(*self) } } impl crate::builtin::meta::FromGodot for #enum_name { fn try_from_godot(via: Self::Via) -> Option { - ::try_from_ord(i32::try_from(via).ok()?) + ::try_from_ord(via) } } diff --git a/godot-core/src/builtin/array.rs b/godot-core/src/builtin/array.rs index abd17d144..dbf601cfa 100644 --- a/godot-core/src/builtin/array.rs +++ b/godot-core/src/builtin/array.rs @@ -13,7 +13,7 @@ use std::fmt; use std::marker::PhantomData; use sys::{ffi_methods, interface_fn, GodotFfi}; -use super::meta::{FromGodot, GodotCompatible, GodotFfiVariant, GodotType, ToGodot}; +use super::meta::{FromGodot, GodotConvert, GodotFfiVariant, GodotType, ToGodot}; /// Godot's `Array` type. /// @@ -28,7 +28,7 @@ use super::meta::{FromGodot, GodotCompatible, GodotFfiVariant, GodotType, ToGodo /// /// Godot also supports typed arrays, which are also just `Variant` arrays under the hood, but with /// runtime checks that no values of the wrong type are put into the array. We represent this as -/// `Array`, where the type `T` implements `VariantMetadata`, `FromGodot` and `ToGodot`. +/// `Array`, where the type `T` implements `GodotType`. /// /// # Reference semantics /// @@ -47,10 +47,10 @@ use super::meta::{FromGodot, GodotCompatible, GodotFfiVariant, GodotType, ToGodo /// concurrent modification on other threads (e.g. created through GDScript). -// `T` must be restricted to `VariantMetadata` in the type, because `Drop` can only be implemented +// `T` must be restricted to `GodotType` in the type, because `Drop` can only be implemented // for `T: GodotType` because `drop()` requires `sys_mut()`, which is on the `GodotFfi` // trait, whose `from_sys_init()` requires `Default`, which is only implemented for `T: -// VariantMetadata`. Whew. This could be fixed by splitting up `GodotFfi` if desired. +// GodotType`. Whew. This could be fixed by splitting up `GodotFfi` if desired. #[repr(C)] pub struct Array { opaque: sys::types::OpaqueArray, @@ -621,7 +621,7 @@ unsafe impl GodotFfi for Array { } } -impl GodotCompatible for Array { +impl GodotConvert for Array { type Via = Self; } diff --git a/godot-core/src/builtin/callable.rs b/godot-core/src/builtin/callable.rs index bd6a028a3..45a361ccf 100644 --- a/godot-core/src/builtin/callable.rs +++ b/godot-core/src/builtin/callable.rs @@ -46,7 +46,8 @@ impl Callable { unsafe { sys::from_sys_init_or_init_default::(|self_ptr| { let ctor = sys::builtin_fn!(callable_from_object_method); - let args = [object.to_ffi().as_arg_ptr(), method.sys_const()]; + let raw = object.to_ffi(); + let args = [raw.as_arg_ptr(), method.sys_const()]; ctor(self_ptr, args.as_ptr()); }) } diff --git a/godot-core/src/builtin/meta/godot_compat/impls.rs b/godot-core/src/builtin/meta/godot_compat/impls.rs deleted file mode 100644 index 23b98819a..000000000 --- a/godot-core/src/builtin/meta/godot_compat/impls.rs +++ /dev/null @@ -1,248 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -use crate::builtin::meta::GodotType; - -use super::*; - -impl GodotCompatible for Option -where - Option: GodotType, -{ - type Via = Option; -} - -impl ToGodot for Option -where - Option: GodotType, -{ - fn to_godot(&self) -> Self::Via { - self.as_ref().map(ToGodot::to_godot) - } - - fn into_godot(self) -> Self::Via { - self.map(ToGodot::into_godot) - } -} - -impl FromGodot for Option -where - Option: GodotType, -{ - fn try_from_godot(via: Self::Via) -> Option { - match via { - Some(via) => T::try_from_godot(via).map(Some), - None => Some(None), - } - } - - fn from_godot(via: Self::Via) -> Self { - via.map(T::from_godot) - } -} - -impl GodotCompatible for sys::VariantType { - type Via = i64; -} - -impl ToGodot for sys::VariantType { - fn to_godot(&self) -> Self::Via { - *self as i64 - } - - fn into_godot(self) -> Self::Via { - self as i64 - } -} - -impl FromGodot for sys::VariantType { - fn try_from_godot(via: Self::Via) -> Option { - Some(Self::from_sys(via as sys::GDExtensionVariantType)) - } -} - -impl GodotCompatible for sys::VariantOperator { - type Via = i64; -} - -impl ToGodot for sys::VariantOperator { - fn to_godot(&self) -> Self::Via { - *self as i64 - } - - fn into_godot(self) -> Self::Via { - self as i64 - } -} - -impl FromGodot for sys::VariantOperator { - fn try_from_godot(via: Self::Via) -> Option { - Some(Self::from_sys(via as sys::GDExtensionVariantOperator)) - } -} - -impl GodotCompatible for *mut T { - type Via = i64; -} - -impl ToGodot for *mut T { - fn to_godot(&self) -> Self::Via { - *self as i64 - } -} - -impl FromGodot for *mut T { - fn try_from_godot(via: Self::Via) -> Option { - Some(via as Self) - } -} - -impl GodotCompatible for *const T { - type Via = i64; -} - -impl ToGodot for *const T { - fn to_godot(&self) -> Self::Via { - *self as i64 - } -} - -impl FromGodot for *const T { - fn try_from_godot(via: Self::Via) -> Option { - Some(via as Self) - } -} - -mod scalars { - use super::{impl_godot_as_self, FromGodot, GodotCompatible, ToGodot}; - use crate::builtin::meta::GodotType; - use godot_ffi as sys; - - macro_rules! impl_godot { - ($T:ty as $Via:ty $(, $param_metadata:expr)?) => { - impl GodotType for $T { - type Ffi = $Via; - - fn to_ffi(&self) -> Self::Ffi { - (*self).into() - } - - fn into_ffi(self) -> Self::Ffi { - self.into() - } - - fn try_from_ffi(ffi: Self::Ffi) -> Option { - Self::try_from(ffi).ok() - } - - $( - fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { - $param_metadata - } - )? - } - - impl GodotCompatible for $T { - type Via = $T; - } - - impl ToGodot for $T { - fn to_godot(&self) -> Self::Via { - *self - } - } - - impl FromGodot for $T { - fn try_from_godot(via: Self::Via) -> Option { - Some(via) - } - } - }; - ($T:ty as $Via:ty $(, $param_metadata:expr)?; lossy) => { - impl GodotType for $T { - type Ffi = $Via; - - fn to_ffi(&self) -> Self::Ffi { - *self as $Via - } - - fn into_ffi(self) -> Self::Ffi { - self as $Via - } - - fn try_from_ffi(ffi: Self::Ffi) -> Option { - Some(ffi as $T) - } - - $( - fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { - $param_metadata - } - )? - } - - impl GodotCompatible for $T { - type Via = $T; - } - - impl ToGodot for $T { - fn to_godot(&self) -> Self::Via { - *self - } - } - - impl FromGodot for $T { - fn try_from_godot(via: Self::Via) -> Option { - Some(via) - } - } - }; - } - - impl_godot_as_self!(bool); - impl_godot_as_self!(i64, sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64); - impl_godot_as_self!( - f64, - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE - ); - impl_godot_as_self!(()); - - impl_godot!( - i32 as i64, - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32 - ); - impl_godot!( - i16 as i64, - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16 - ); - impl_godot!( - i8 as i64, - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8 - ); - impl_godot!( - u32 as i64, - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32 - ); - impl_godot!( - u16 as i64, - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16 - ); - impl_godot!( - u8 as i64, - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8 - ); - - impl_godot!( - u64 as i64, - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64; - lossy - ); - impl_godot!( - f32 as f64, - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT; - lossy - ); -} diff --git a/godot-core/src/builtin/meta/godot_compatible/impls.rs b/godot-core/src/builtin/meta/godot_compatible/impls.rs new file mode 100644 index 000000000..cdd827fc3 --- /dev/null +++ b/godot-core/src/builtin/meta/godot_compatible/impls.rs @@ -0,0 +1,252 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use crate::builtin::meta::{impl_godot_as_self, FromGodot, GodotConvert, GodotType, ToGodot}; +use godot_ffi as sys; + +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Option + +impl GodotConvert for Option +where + Option: GodotType, +{ + type Via = Option; +} + +impl ToGodot for Option +where + Option: GodotType, +{ + fn to_godot(&self) -> Self::Via { + self.as_ref().map(ToGodot::to_godot) + } + + fn into_godot(self) -> Self::Via { + self.map(ToGodot::into_godot) + } +} + +impl FromGodot for Option +where + Option: GodotType, +{ + fn try_from_godot(via: Self::Via) -> Option { + match via { + Some(via) => T::try_from_godot(via).map(Some), + None => Some(None), + } + } + + fn from_godot(via: Self::Via) -> Self { + via.map(T::from_godot) + } +} + +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Builtin Godot types + +impl GodotConvert for sys::VariantType { + type Via = i32; +} + +impl ToGodot for sys::VariantType { + fn to_godot(&self) -> Self::Via { + *self as i32 + } + + fn into_godot(self) -> Self::Via { + self as i32 + } +} + +impl FromGodot for sys::VariantType { + fn try_from_godot(via: Self::Via) -> Option { + Some(Self::from_sys(via as sys::GDExtensionVariantType)) + } +} + +impl GodotConvert for sys::VariantOperator { + type Via = i32; +} + +impl ToGodot for sys::VariantOperator { + fn to_godot(&self) -> Self::Via { + *self as i32 + } + + fn into_godot(self) -> Self::Via { + self as i32 + } +} + +impl FromGodot for sys::VariantOperator { + fn try_from_godot(via: Self::Via) -> Option { + Some(Self::from_sys(via as sys::GDExtensionVariantOperator)) + } +} + +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Pointers + +impl GodotConvert for *mut T { + type Via = i64; +} + +impl ToGodot for *mut T { + fn to_godot(&self) -> Self::Via { + *self as i64 + } +} + +impl FromGodot for *mut T { + fn try_from_godot(via: Self::Via) -> Option { + Some(via as Self) + } +} + +impl GodotConvert for *const T { + type Via = i64; +} + +impl ToGodot for *const T { + fn to_godot(&self) -> Self::Via { + *self as i64 + } +} + +impl FromGodot for *const T { + fn try_from_godot(via: Self::Via) -> Option { + Some(via as Self) + } +} + +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Scalars + +macro_rules! impl_godot_scalar { + ($T:ty as $Via:ty $(, $param_metadata:expr)?) => { + impl GodotType for $T { + type Ffi = $Via; + + fn to_ffi(&self) -> Self::Ffi { + (*self).into() + } + + fn into_ffi(self) -> Self::Ffi { + self.into() + } + + fn try_from_ffi(ffi: Self::Ffi) -> Option { + Self::try_from(ffi).ok() + } + + $( + fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { + $param_metadata + } + )? + } + + impl GodotConvert for $T { + type Via = $T; + } + + impl ToGodot for $T { + fn to_godot(&self) -> Self::Via { + *self + } + } + + impl FromGodot for $T { + fn try_from_godot(via: Self::Via) -> Option { + Some(via) + } + } + }; + + ($T:ty as $Via:ty $(, $param_metadata:expr)?; lossy) => { + impl GodotType for $T { + type Ffi = $Via; + + fn to_ffi(&self) -> Self::Ffi { + *self as $Via + } + + fn into_ffi(self) -> Self::Ffi { + self as $Via + } + + fn try_from_ffi(ffi: Self::Ffi) -> Option { + Some(ffi as $T) + } + + $( + fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { + $param_metadata + } + )? + } + + impl GodotConvert for $T { + type Via = $T; + } + + impl ToGodot for $T { + fn to_godot(&self) -> Self::Via { + *self + } + } + + impl FromGodot for $T { + fn try_from_godot(via: Self::Via) -> Option { + Some(via) + } + } + }; +} + +// `GodotType` for these three is implemented in `godot-core/src/builtin/variant/impls.rs`. +impl_godot_as_self!(bool); +impl_godot_as_self!(i64); +impl_godot_as_self!(f64); +impl_godot_as_self!(()); + +impl_godot_scalar!( + i32 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32 +); +impl_godot_scalar!( + i16 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16 +); +impl_godot_scalar!( + i8 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8 +); +impl_godot_scalar!( + u32 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32 +); +impl_godot_scalar!( + u16 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16 +); +impl_godot_scalar!( + u8 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8 +); + +impl_godot_scalar!( + u64 as i64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64; + lossy +); +impl_godot_scalar!( + f32 as f64, + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT; + lossy +); diff --git a/godot-core/src/builtin/meta/godot_compat/mod.rs b/godot-core/src/builtin/meta/godot_compatible/mod.rs similarity index 91% rename from godot-core/src/builtin/meta/godot_compat/mod.rs rename to godot-core/src/builtin/meta/godot_compatible/mod.rs index f1c03a276..674acb595 100644 --- a/godot-core/src/builtin/meta/godot_compat/mod.rs +++ b/godot-core/src/builtin/meta/godot_compatible/mod.rs @@ -6,8 +6,6 @@ mod impls; -use godot_ffi as sys; - use crate::builtin::{Variant, VariantConversionError}; use super::{GodotFfiVariant, GodotType}; @@ -17,7 +15,7 @@ use super::{GodotFfiVariant, GodotType}; /// The type specified here is what will be used to pass this type across to ffi-boundary to/from Godot. /// Generally [`ToGodot`] needs to be implemented to pass a type to Godot, and [`FromGodot`] to receive this /// type from Godot. -pub trait GodotCompatible { +pub trait GodotConvert { /// The type used for ffi-passing. type Via: GodotType; } @@ -29,11 +27,14 @@ pub trait GodotCompatible { /// starting value. /// /// Violating these assumptions is safe but will give unexpected results. -pub trait ToGodot: Sized + GodotCompatible { +pub trait ToGodot: Sized + GodotConvert { /// Converts this type to the Godot type by reference, usually by cloning. fn to_godot(&self) -> Self::Via; /// Converts this type to the Godot type. + /// + /// This can in some cases enable some optimizations, such as avoiding reference counting for + /// reference-counted values. fn into_godot(self) -> Self::Via { self.to_godot() } @@ -51,9 +52,10 @@ pub trait ToGodot: Sized + GodotCompatible { /// starting value. /// /// Violating these assumptions is safe but will give unexpected results. -pub trait FromGodot: Sized + GodotCompatible { +pub trait FromGodot: Sized + GodotConvert { // TODO: better error /// Performs the conversion. + #[must_use] fn try_from_godot(via: Self::Via) -> Option; /// ⚠️ Performs the conversion. @@ -91,8 +93,8 @@ pub(crate) fn try_from_ffi(ffi: ::Ffi) -> Opt } macro_rules! impl_godot_as_self { - ($T:ty$(, $param_metadata:expr)?) => { - impl $crate::builtin::meta::GodotCompatible for $T { + ($T:ty) => { + impl $crate::builtin::meta::GodotConvert for $T { type Via = $T; } diff --git a/godot-core/src/builtin/meta/mod.rs b/godot-core/src/builtin/meta/mod.rs index cce48934a..3dcbd615b 100644 --- a/godot-core/src/builtin/meta/mod.rs +++ b/godot-core/src/builtin/meta/mod.rs @@ -7,12 +7,12 @@ pub mod registration; mod class_name; -mod godot_compat; +mod godot_compatible; mod return_marshal; mod signature; pub use class_name::*; -pub use godot_compat::*; +pub use godot_compatible::*; #[doc(hidden)] pub use return_marshal::*; #[doc(hidden)] @@ -100,14 +100,17 @@ mod sealed { /// Types that can represent some Godot type. /// -/// This trait cannot be implemented for custom user types, for that you should see [`GodotCompatible`] -/// instead. +/// This trait cannot be implemented for custom user types, for that you should see [`GodotConvert`] +/// instead. A type implements `GodotType` when it can directly represent some primitive type exposed by +/// Godot. For instance, [`i64`] implements `GodotType`, since it can be directly represented by Godot's +/// `int` type. But [`VariantType`] does not implement `GodotType`. Since while it is an enum Godot uses, we +/// have no native way to indicate to Godot that a value should be one of the variants of `VariantType`. /// /// Unlike [`GodotFfi`], types implementing this trait don't need to fully represent its corresponding Godot /// type. For instance [`i32`] does not implement [`GodotFfi`] because it cannot represent all values of /// Godot's `int` type, however it does implement `GodotType` because we can set the metadata of values with /// this type to indicate that they are 32 bits large. -pub trait GodotType: GodotCompatible + ToGodot + FromGodot + sealed::Sealed { +pub trait GodotType: GodotConvert + ToGodot + FromGodot + sealed::Sealed { type Ffi: GodotFfiVariant; fn to_ffi(&self) -> Self::Ffi; @@ -181,12 +184,27 @@ where Some(GodotType::from_ffi(ffi)) } -} -/// Stores meta-information about registered types or properties. -/// -/// Filling this information properly is important so that Godot can use ptrcalls instead of varcalls -/// (requires typed GDScript + sufficient information from the extension side) + fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { + T::param_metadata() + } + + fn class_name() -> ClassName { + T::class_name() + } + + fn property_info(property_name: &str) -> PropertyInfo { + T::property_info(property_name) + } + + fn argument_info(property_name: &str) -> MethodParamOrReturnInfo { + T::argument_info(property_name) + } + + fn return_info() -> Option { + T::return_info() + } +} // ---------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/godot-core/src/builtin/meta/signature.rs b/godot-core/src/builtin/meta/signature.rs index cd2e5c325..1ccc06b2e 100644 --- a/godot-core/src/builtin/meta/signature.rs +++ b/godot-core/src/builtin/meta/signature.rs @@ -112,8 +112,6 @@ macro_rules! impl_varcall_signature_for_tuple { $R:ident $(, ($pn:ident, $n:tt) : $Pn:ident)* // $n cannot be literal if substituted as tuple index .0 ) => { - // R: FromVariantIndirect, Pn: ToVariant -> when calling engine APIs - // R: ToVariant, Pn: #[allow(unused_variables)] impl<$R, $($Pn,)*> VarcallSignatureTuple for ($R, $($Pn,)*) where @@ -400,7 +398,7 @@ unsafe fn varcall_return( /// # Safety /// See [`varcall_return`]. #[cfg(since_api = "4.2")] // unused before -pub(crate) unsafe fn varcall_return_checked( +pub(crate) unsafe fn varcall_return_checked( ret_val: Result, // TODO Err should be custom CallError enum ret: sys::GDExtensionVariantPtr, err: *mut sys::GDExtensionCallError, diff --git a/godot-core/src/builtin/string/mod.rs b/godot-core/src/builtin/string/mod.rs index 47baaedbc..a06064f23 100644 --- a/godot-core/src/builtin/string/mod.rs +++ b/godot-core/src/builtin/string/mod.rs @@ -16,9 +16,9 @@ pub use godot_string::*; pub use node_path::*; pub use string_name::*; -use super::meta::{FromGodot, GodotCompatible, ToGodot}; +use super::meta::{FromGodot, GodotConvert, ToGodot}; -impl GodotCompatible for &str { +impl GodotConvert for &str { type Via = GodotString; } @@ -28,7 +28,7 @@ impl ToGodot for &str { } } -impl GodotCompatible for String { +impl GodotConvert for String { type Via = GodotString; } diff --git a/godot-core/src/builtin/string/string_name.rs b/godot-core/src/builtin/string/string_name.rs index a4d02af32..eb16a9fba 100644 --- a/godot-core/src/builtin/string/string_name.rs +++ b/godot-core/src/builtin/string/string_name.rs @@ -4,6 +4,8 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::fmt; + use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; @@ -11,8 +13,6 @@ use crate::builtin::inner; use crate::builtin::meta::impl_godot_as_self; use crate::builtin::{GodotString, NodePath}; -use std::fmt; - /// A string optimized for unique names. /// /// StringNames are immutable strings designed for representing unique names. StringName ensures that only diff --git a/godot-core/src/builtin/variant/impls.rs b/godot-core/src/builtin/variant/impls.rs index 539697340..94c40f4c3 100644 --- a/godot-core/src/builtin/variant/impls.rs +++ b/godot-core/src/builtin/variant/impls.rs @@ -188,4 +188,8 @@ impl GodotType for Variant { usage: global::PropertyUsageFlags::PROPERTY_USAGE_NIL_IS_VARIANT, } } + + fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata { + sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8 + } } diff --git a/godot-core/src/builtin/variant/mod.rs b/godot-core/src/builtin/variant/mod.rs index 04d96cf4d..43b902307 100644 --- a/godot-core/src/builtin/variant/mod.rs +++ b/godot-core/src/builtin/variant/mod.rs @@ -274,10 +274,7 @@ unsafe impl GodotFfi for Variant { } } -impl_godot_as_self!( - Variant, - sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8 -); +impl_godot_as_self!(Variant); impl Clone for Variant { fn clone(&self) -> Self { diff --git a/godot-core/src/builtin/vectors/vector_axis.rs b/godot-core/src/builtin/vectors/vector_axis.rs index 64a99dca9..edb2580da 100644 --- a/godot-core/src/builtin/vectors/vector_axis.rs +++ b/godot-core/src/builtin/vectors/vector_axis.rs @@ -4,7 +4,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::builtin::meta::{FromGodot, GodotCompatible, ToGodot}; +use crate::builtin::meta::{FromGodot, GodotConvert, ToGodot}; use crate::builtin::{real, Vector2, Vector2i, Vector3, Vector3i, Vector4, Vector4i}; use crate::obj::EngineEnum; @@ -97,19 +97,19 @@ impl EngineEnum for Vector2Axis { } } -impl GodotCompatible for Vector2Axis { - type Via = i64; +impl GodotConvert for Vector2Axis { + type Via = i32; } impl ToGodot for Vector2Axis { fn to_godot(&self) -> Self::Via { - self.ord() as i64 + self.ord() } } impl FromGodot for Vector2Axis { fn try_from_godot(via: Self::Via) -> Option { - Self::try_from_ord(via as i32) + Self::try_from_ord(via) } } @@ -145,19 +145,19 @@ impl EngineEnum for Vector3Axis { } } -impl GodotCompatible for Vector3Axis { - type Via = i64; +impl GodotConvert for Vector3Axis { + type Via = i32; } impl ToGodot for Vector3Axis { fn to_godot(&self) -> Self::Via { - self.ord() as i64 + self.ord() } } impl FromGodot for Vector3Axis { fn try_from_godot(via: Self::Via) -> Option { - Self::try_from_ord(via as i32) + Self::try_from_ord(via) } } @@ -196,19 +196,19 @@ impl EngineEnum for Vector4Axis { } } -impl GodotCompatible for Vector4Axis { - type Via = i64; +impl GodotConvert for Vector4Axis { + type Via = i32; } impl ToGodot for Vector4Axis { fn to_godot(&self) -> Self::Via { - self.ord() as i64 + self.ord() } } impl FromGodot for Vector4Axis { fn try_from_godot(via: Self::Via) -> Option { - Self::try_from_ord(via as i32) + Self::try_from_ord(via) } } diff --git a/godot-core/src/engine.rs b/godot-core/src/engine.rs index 142999aca..036125075 100644 --- a/godot-core/src/engine.rs +++ b/godot-core/src/engine.rs @@ -6,12 +6,10 @@ //! Godot engine classes and methods. -use godot_ffi::GodotNullableFfi; - // Re-exports of generated symbols use crate::builtin::{GodotString, NodePath}; use crate::obj::dom::EngineDomain; -use crate::obj::{Gd, GodotClass, Inherits, InstanceId, RawGd}; +use crate::obj::{Gd, GodotClass, Inherits, InstanceId}; pub use crate::gen::central::global; pub use crate::gen::classes::*; @@ -188,27 +186,23 @@ where // Utilities for crate pub(crate) fn debug_string( - ptr: &RawGd, + obj: &Gd, f: &mut std::fmt::Formatter<'_>, ty: &str, ) -> std::fmt::Result { - if let Some(id) = ptr.instance_id_or_none() { - let class: GodotString = ptr.as_object(|obj| Object::get_class(obj)); - + if let Some(id) = obj.instance_id_or_none() { + let class: GodotString = obj.raw.as_object(|obj| Object::get_class(obj)); write!(f, "{ty} {{ id: {id}, class: {class} }}") - } else if ptr.is_null() { - write!(f, "{ty} {{ null }}") } else { write!(f, "{ty} {{ freed obj }}") } } pub(crate) fn display_string( - ptr: &RawGd, + obj: &Gd, f: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { - let string: GodotString = ptr.as_object(Object::to_string); - + let string: GodotString = obj.raw.as_object(Object::to_string); ::fmt(&string, f) } @@ -223,7 +217,7 @@ pub(crate) fn ensure_object_alive( method_name: &'static str, ) { let Some(instance_id) = instance_id else { - panic!("{method_name}: instance id is null") + panic!("{method_name}: cannot call method on null object") }; let new_object_ptr = object_ptr_from_id(instance_id); diff --git a/godot-core/src/obj/base.rs b/godot-core/src/obj/base.rs index d7ac3f4b0..a23c441ce 100644 --- a/godot-core/src/obj/base.rs +++ b/godot-core/src/obj/base.rs @@ -65,13 +65,13 @@ impl Base { impl Debug for Base { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - engine::debug_string(&self.obj.raw, f, "Base") + engine::debug_string(&self.obj, f, "Base") } } impl Display for Base { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - engine::display_string(&self.obj.raw, f) + engine::display_string(&self.obj, f) } } diff --git a/godot-core/src/obj/gd.rs b/godot-core/src/obj/gd.rs index 155ac15e3..16c99f853 100644 --- a/godot-core/src/obj/gd.rs +++ b/godot-core/src/obj/gd.rs @@ -10,11 +10,11 @@ use std::ptr; use godot_ffi as sys; use godot_ffi::VariantType; -use sys::{interface_fn, static_assert_eq_size, GodotNullableFfi}; +use sys::static_assert_eq_size; -use crate::builtin::meta::{FromGodot, GodotCompatible, GodotType, ToGodot}; +use crate::builtin::meta::{FromGodot, GodotConvert, GodotType, ToGodot}; use crate::builtin::{Callable, StringName}; -use crate::obj::{cap, dom, mem, EngineEnum, GodotClass, Inherits, Share}; +use crate::obj::{cap, dom, mem, EngineEnum, GdDerefTarget, GodotClass, Inherits, Share}; use crate::obj::{GdMut, GdRef, InstanceId}; use crate::property::{Export, ExportInfo, Property, TypeStringHint}; use crate::{callbacks, engine, out}; @@ -149,28 +149,6 @@ where pub fn bind_mut(&mut self) -> GdMut { self.raw.bind_mut() } - - /// Storage object associated with the extension instance. - // pub(crate) fn storage_mut(&mut self) -> &mut InstanceStorage { - // // SAFETY: - // unsafe { - // let binding = self.resolve_instance_ptr(); - // crate::private::as_storage_mut::(binding) - // } - // } - - unsafe fn resolve_instance_ptr(&self) -> sys::GDExtensionClassInstancePtr { - let callbacks = crate::storage::nop_instance_callbacks(); - let token = sys::get_library() as *mut std::ffi::c_void; - let binding = interface_fn!(object_get_instance_binding)(self.obj_sys(), token, &callbacks); - - debug_assert!( - !binding.is_null(), - "Class {} -- null instance; does the class have a Godot creator function?", - std::any::type_name::() - ); - binding as sys::GDExtensionClassInstancePtr - } } /// _The methods in this impl block are available for any `T`._

@@ -182,13 +160,9 @@ impl Gd { pub fn try_from_instance_id(instance_id: InstanceId) -> Option { let ptr = engine::object_ptr_from_id(instance_id); - if ptr.is_null() { - None - } else { - // SAFETY: assumes that the returned GDExtensionObjectPtr is convertible to Object* (i.e. C++ upcast doesn't modify the pointer) - let untyped = unsafe { Gd::::from_obj_sys(ptr) }; - untyped.owned_cast::().ok() - } + // SAFETY: assumes that the returned GDExtensionObjectPtr is convertible to Object* (i.e. C++ upcast doesn't modify the pointer) + let untyped = unsafe { Gd::::from_obj_sys_or_none(ptr)? }; + untyped.owned_cast::().ok() } /// ⚠️ Looks up the given instance ID and returns the associated object. @@ -206,9 +180,17 @@ impl Gd { }) } - /// Returns the instance ID of this object, or `None` if the object is dead. - pub fn instance_id_or_none(&self) -> Option { - self.raw.instance_id_or_none() + /// Returns the instance ID of this object, or `None` if the object is dead or null. + pub(crate) fn instance_id_or_none(&self) -> Option { + let known_id = self.instance_id_unchecked(); + + // Refreshes the internal cached ID on every call, as we cannot be sure that the object has not been + // destroyed since last time. The only reliable way to find out is to call is_instance_id_valid(). + if self.raw.is_instance_valid() { + Some(known_id) + } else { + None + } } /// ⚠️ Returns the instance ID of this object (panics when dead). @@ -227,9 +209,12 @@ impl Gd { /// ⚠️ Returns the last known, possibly invalid instance ID of this object. /// /// This function does not check that the returned instance ID points to a valid instance! - /// Unless performance is a problem, use [`instance_id()`][Self::instance_id] or [`instance_id_or_none()`][Self::instance_id_or_none] instead. + /// Unless performance is a problem, use [`instance_id()`][Self::instance_id] instead. pub fn instance_id_unchecked(&self) -> InstanceId { - self.raw.cached_instance_id.get().unwrap() + // SAFETY: + // A `Gd` can only be created from a non-null `RawGd`. Meaning `raw.instance_id_unchecked()` will + // always return `Some`. + unsafe { self.raw.instance_id_unchecked().unwrap_unchecked() } } /// Checks if this smart pointer points to a live object (read description!). @@ -242,7 +227,7 @@ impl Gd { /// runtime condition to check against. pub fn is_instance_valid(&self) -> bool { // This call refreshes the instance ID, and recognizes dead objects. - self.raw.is_instance_valid() + self.instance_id_or_none().is_some() } /// **Upcast:** convert into a smart pointer to a base class. Always succeeds. @@ -308,23 +293,33 @@ impl Gd { .map_err(Self::from_ffi) } + #[doc(hidden)] + pub(crate) unsafe fn from_obj_sys_or_none(ptr: sys::GDExtensionObjectPtr) -> Option { + Self::try_from_ffi(RawGd::from_obj_sys(ptr)) + } + /// Initializes this `Gd` from the object pointer as a **strong ref**, meaning /// it initializes/increments the reference counter and keeps the object alive. /// /// This is the default for most initializations from FFI. In cases where reference counter /// should explicitly **not** be updated, [`Self::from_obj_sys_weak`] is available. #[doc(hidden)] - pub unsafe fn from_obj_sys(ptr: sys::GDExtensionObjectPtr) -> Self { - Self::from_ffi(RawGd::from_obj_sys(ptr)) + pub(crate) unsafe fn from_obj_sys(ptr: sys::GDExtensionObjectPtr) -> Self { + Self::from_obj_sys_or_none(ptr).unwrap() } #[doc(hidden)] - pub unsafe fn from_obj_sys_weak(ptr: sys::GDExtensionObjectPtr) -> Self { - Self::from_ffi(RawGd::from_obj_sys_weak(ptr)) + pub(crate) unsafe fn from_obj_sys_weak_or_none(ptr: sys::GDExtensionObjectPtr) -> Option { + Self::try_from_ffi(RawGd::from_obj_sys_weak(ptr)) } #[doc(hidden)] - pub fn obj_sys(&self) -> sys::GDExtensionObjectPtr { + pub(crate) unsafe fn from_obj_sys_weak(ptr: sys::GDExtensionObjectPtr) -> Self { + Self::from_obj_sys_weak_or_none(ptr).unwrap() + } + + #[doc(hidden)] + pub(crate) fn obj_sys(&self) -> sys::GDExtensionObjectPtr { self.raw.obj_sys() } @@ -338,37 +333,16 @@ impl Deref for Gd { // Target is always an engine class: // * if T is an engine class => T // * if T is a user class => T::Base - type Target = <::Declarer as dom::Domain>::DerefTarget; + type Target = GdDerefTarget; fn deref(&self) -> &Self::Target { - // SAFETY: - // - // This relies on `Gd` having the layout as `Node3D` (as an example), - // which also needs #[repr(transparent)]: - // - // struct Gd { - // opaque: OpaqueObject, // <- size of GDExtensionObjectPtr - // cached_instance_id, // <- Cell is #[repr(transparent)] to its inner T - // _marker: PhantomData, // <- ZST - // } - // struct Node3D { - // object_ptr: sys::GDExtensionObjectPtr, - // } - self.raw.as_target() + self.raw.as_target().expect("`Gd` is never null") } } impl DerefMut for Gd { fn deref_mut(&mut self) -> &mut Self::Target { - // SAFETY: see also Deref - // - // The resulting `&mut T` is transmuted from `&mut OpaqueObject`, i.e. a *pointer* to the `opaque` field. - // `opaque` itself has a different *address* for each Gd instance, meaning that two simultaneous - // DerefMut borrows on two Gd instances will not alias, *even if* the underlying Godot object is the - // same (i.e. `opaque` has the same value, but not address). - // - // The `&mut self` guarantees that no other base access can take place for *the same Gd instance* (access to other Gds is OK). - self.raw.as_target_mut() + self.raw.as_target_mut().expect("`Gd` is never null") } } @@ -419,14 +393,34 @@ where /// * When the referred-to object has already been destroyed. /// * When this is invoked on an upcast `Gd` that dynamically points to a reference-counted type (i.e. operation not supported). pub fn free(self) { - self.raw.free() + // TODO disallow for singletons, either only at runtime or both at compile time (new memory policy) and runtime + + // Runtime check in case of T=Object, no-op otherwise + let ref_counted = T::Mem::is_ref_counted(&self.raw); + assert_ne!( + ref_counted, Some(true), + "called free() on Gd which points to a RefCounted dynamic type; free() only supported for manually managed types." + ); + + // If ref_counted returned None, that means the instance was destroyed + assert!( + ref_counted == Some(false) && self.is_instance_valid(), + "called free() on already destroyed object" + ); + + // This destroys the Storage instance, no need to run destructor again + unsafe { + sys::interface_fn!(object_destroy)(self.raw.obj_sys()); + } + + std::mem::forget(self); } } // ---------------------------------------------------------------------------------------------------------------------------------------------- // Trait impls -impl GodotCompatible for Gd { +impl GodotConvert for Gd { type Via = Gd; } @@ -464,6 +458,10 @@ impl GodotType for Gd { Some(Self { raw }) } } + + fn class_name() -> crate::builtin::meta::ClassName { + T::class_name() + } } impl Clone for Gd { @@ -545,13 +543,13 @@ impl Eq for Gd {} impl Display for Gd { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - engine::display_string(&self.raw, f) + engine::display_string(self, f) } } impl Debug for Gd { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - engine::debug_string(&self.raw, f, "Gd") + engine::debug_string(self, f, "Gd") } } diff --git a/godot-core/src/obj/instance_id.rs b/godot-core/src/obj/instance_id.rs index b6e5cac30..af2b38fe6 100644 --- a/godot-core/src/obj/instance_id.rs +++ b/godot-core/src/obj/instance_id.rs @@ -4,7 +4,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::builtin::meta::{FromGodot, GodotCompatible, ToGodot}; +use crate::builtin::meta::{FromGodot, GodotConvert, ToGodot}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::num::NonZeroU64; @@ -74,7 +74,7 @@ impl Debug for InstanceId { } } -impl GodotCompatible for InstanceId { +impl GodotConvert for InstanceId { type Via = u64; } @@ -89,28 +89,3 @@ impl FromGodot for InstanceId { Self::try_from_u64(via) } } - -/* -// Note: Option impl is only possible as long as `FromVariant` and `InstanceId` are in same crate. -// (Rust rationale: if upstream crate later adds blanket `impl FromVariant for Option`, this would collide) -impl FromVariant for Option { - fn try_from_variant(variant: &Variant) -> Result { - if variant.is_nil() { - Ok(None) - } else { - // Should 0 in variant be mapped to None, or cause an error like now? - i64::try_from_variant(variant).map(|i| InstanceId::try_from_i64(i)) - } - } -} - -impl ToVariant for Option { - fn to_variant(&self) -> Variant { - if let Some(id) = self { - id.to_variant() - } else { - 0i64.to_variant() - } - } -} -*/ diff --git a/godot-core/src/obj/mod.rs b/godot-core/src/obj/mod.rs index 45cda1ed0..e96bebd94 100644 --- a/godot-core/src/obj/mod.rs +++ b/godot-core/src/obj/mod.rs @@ -23,3 +23,5 @@ pub use guards::*; pub use instance_id::*; pub use raw::*; pub use traits::*; + +type GdDerefTarget = <::Declarer as dom::Domain>::DerefTarget; diff --git a/godot-core/src/obj/raw.rs b/godot-core/src/obj/raw.rs index bd42a167d..f7a369624 100644 --- a/godot-core/src/obj/raw.rs +++ b/godot-core/src/obj/raw.rs @@ -10,30 +10,45 @@ use godot_ffi as sys; use sys::{interface_fn, GodotFfi, GodotNullableFfi, PtrcallType}; use crate::builtin::meta::{ - ClassName, FromGodot, GodotCompatible, GodotFfiVariant, GodotType, ToGodot, + ClassName, FromGodot, GodotConvert, GodotFfiVariant, GodotType, ToGodot, }; use crate::builtin::{Variant, VariantConversionError}; use crate::obj::dom::Domain as _; use crate::obj::mem::Memory as _; -use crate::obj::{dom, mem, GodotClass}; +use crate::obj::GdDerefTarget; +use crate::obj::{dom, GodotClass}; use crate::obj::{GdMut, GdRef, InstanceId}; use crate::storage::InstanceStorage; use crate::{engine, out}; +/// Low-level bindings for object pointers in Godot. +/// +/// This should not be used directly, you should either use [`Gd`](super::Gd) or [`Option>`] +/// depending on whether you need a nullable object pointer or not. +#[repr(C)] +#[doc(hidden)] pub struct RawGd { pub(super) obj: *mut T, - pub(super) cached_instance_id: std::cell::Cell>, + // Must not be changed after initialization. + cached_instance_id: Option, } impl RawGd { - pub(super) fn new_null() -> Self { + /// Create a new object representing a null in Godot. + pub(super) fn null() -> Self { Self { obj: ptr::null_mut(), - cached_instance_id: std::cell::Cell::new(None), + cached_instance_id: None, } } - pub(super) fn from_obj_sys_weak(obj: sys::GDExtensionObjectPtr) -> Self { + /// Initializes this `RawGd` from the object pointer as a **weak ref**, meaning it does not + /// initialize/increment the reference counter. + /// + /// # Safety + /// + /// `obj` must be a valid object pointer or a null pointer. + pub(super) unsafe fn from_obj_sys_weak(obj: sys::GDExtensionObjectPtr) -> Self { let mut instance_id = None; if !obj.is_null() { let id = @@ -43,53 +58,48 @@ impl RawGd { Self { obj: obj as *mut T, - cached_instance_id: std::cell::Cell::new(instance_id), + cached_instance_id: instance_id, } } - pub(super) fn from_obj_sys(obj: sys::GDExtensionObjectPtr) -> Self { + /// Initializes this `RawGd` from the object pointer as a **strong ref**, meaning it initializes + /// /increments the reference counter and keeps the object alive. + /// + /// This is the default for most initializations from FFI. In cases where reference counter + /// should explicitly **not** be updated, [`from_obj_sys_weak()`](Self::from_obj_sys_weak) is available. + /// + /// # Safety + /// + /// `obj` must be a valid object pointer or a null pointer. + pub(super) unsafe fn from_obj_sys(obj: sys::GDExtensionObjectPtr) -> Self { Self::from_obj_sys_weak(obj).with_inc_refcount() } - pub(crate) fn instance_id_or_none(&self) -> Option { - let known_id = match self.cached_instance_id.get() { - // Already dead - None => return None, - - // Possibly alive - Some(id) => id, - }; - - // Refreshes the internal cached ID on every call, as we cannot be sure that the object has not been - // destroyed since last time. The only reliable way to find out is to call is_instance_id_valid(). - if engine::utilities::is_instance_id_valid(known_id.to_i64()) { - Some(known_id) - } else { - self.cached_instance_id.set(None); - None - } + /// Returns `self` but with initialized ref-count. + fn with_inc_refcount(self) -> Self { + // Note: use init_ref and not inc_ref, since this might be the first reference increment. + // Godot expects RefCounted::init_ref to be called instead of RefCounted::reference in that case. + // init_ref also doesn't hurt (except 1 possibly unnecessary check). + T::Mem::maybe_init_ref(&self); + self } - pub(super) fn is_instance_valid(&self) -> bool { - // This call refreshes the instance ID, and recognizes dead objects. - self.instance_id_or_none().is_some() + /// Returns `true` if the object is null. + /// + /// This does not check if the object is dead, for that use + /// [`instance_id_or_none()`](Self::instance_id_or_none). + pub(crate) fn is_null(&self) -> bool { + self.obj.is_null() || self.cached_instance_id.is_none() } - // Note: does not transfer ownership and is thus unsafe. Also operates on shared ref. - // Either the parameter or the return value *must* be forgotten (since reference counts are not updated). - pub(super) unsafe fn ffi_cast(&self) -> Option> - where - U: GodotClass, - { - if self.is_null() { - return Some(RawGd::new_null()); - } - - let class_tag = interface_fn!(classdb_get_class_tag)(U::class_name().string_sys()); - let cast_object_ptr = interface_fn!(object_cast_to)(self.obj_sys(), class_tag); + pub(crate) fn instance_id_unchecked(&self) -> Option { + self.cached_instance_id + } - // Create weak object, as ownership will be moved and reference-counter stays the same - sys::ptr_then(cast_object_ptr, |ptr| RawGd::from_obj_sys_weak(ptr)) + pub(crate) fn is_instance_valid(&self) -> bool { + self.cached_instance_id + .map(|id| engine::utilities::is_instance_id_valid(id.to_i64())) + .unwrap_or(false) } // See use-site for explanation. @@ -98,6 +108,7 @@ impl RawGd { U: GodotClass, { if self.is_null() { + // Null can be cast to anything. return true; } @@ -105,6 +116,7 @@ impl RawGd { unsafe { self.ffi_cast::() }.expect("Everything inherits object"); let cast_is_valid = as_obj .as_target() + .expect("object is not null") .is_class(U::class_name().to_godot_string()); std::mem::forget(as_obj); cast_is_valid @@ -139,23 +151,31 @@ impl RawGd { } } - /// Returns `self` but with initialized ref-count. - fn with_inc_refcount(self) -> Self { - // Note: use init_ref and not inc_ref, since this might be the first reference increment. - // Godot expects RefCounted::init_ref to be called instead of RefCounted::reference in that case. - // init_ref also doesn't hurt (except 1 possibly unnecessary check). - if self.is_instance_valid() { - T::Mem::maybe_init_ref(&self); + /// # Safety + /// Does not transfer ownership and is thus unsafe. Also operates on shared ref. Either the parameter or + /// the return value *must* be forgotten (since reference counts are not updated). + pub(super) unsafe fn ffi_cast(&self) -> Option> + where + U: GodotClass, + { + // `self` may be null when we convert a null-variant into a `Option>`. Since we use `ffi_cast` + // in the `ffi_from_variant` conversion function to ensure type-correctness. So a null-variant would + // be cast into a null `RawGd` which is then casted to a null `RawGd` which is then + // converted into a `None` `Option>`. + if self.is_null() { + // Null can be cast to anything. + // Forgetting a null doesn't do anything, since dropping a null also does nothing. + return Some(RawGd::null()); } - self + + let class_tag = interface_fn!(classdb_get_class_tag)(U::class_name().string_sys()); + let cast_object_ptr = interface_fn!(object_cast_to)(self.obj_sys(), class_tag); + + // Create weak object, as ownership will be moved and reference-counter stays the same + sys::ptr_then(cast_object_ptr, |ptr| RawGd::from_obj_sys_weak(ptr)) } pub(crate) fn as_ref_counted(&self, apply: impl Fn(&mut engine::RefCounted) -> R) -> R { - debug_assert!( - self.is_instance_valid(), - "as_ref_counted() on freed instance; maybe forgot to increment reference count?" - ); - let tmp = unsafe { self.ffi_cast::() }; let mut tmp = tmp.expect("object expected to inherit RefCounted"); let return_val = @@ -166,8 +186,6 @@ impl RawGd { } pub(crate) fn as_object(&self, apply: impl Fn(&mut engine::Object) -> R) -> R { - // Note: no validity check; this could be called by to_string(), which can be called on dead instances - let tmp = unsafe { self.ffi_cast::() }; let mut tmp = tmp.expect("object expected to inherit Object; should never fail"); // let return_val = apply(tmp.inner_mut()); @@ -178,46 +196,63 @@ impl RawGd { return_val } - pub(super) fn as_target( - &self, - ) -> &<::Declarer as dom::Domain>::DerefTarget { + // Target is always an engine class: + // * if T is an engine class => T + // * if T is a user class => T::Base + pub(super) fn as_target(&self) -> Option<&GdDerefTarget> { + if self.is_null() { + return None; + } + // SAFETY: + // Every engine object is a struct like // - // This relies on `Gd.opaque` having the layout as `Node3D` (as an example), - // which also needs #[repr(transparent)]: - // - // struct Gd { - // opaque: OpaqueObject, // <- size of GDExtensionObjectPtr - // _marker: PhantomData, // <- ZST - // } + // #[repr(C)] // struct Node3D { - // object_ptr: sys::GDExtensionObjectPtr, + // object_ptr: sys::GDExtensionObjectPtr, // <- pointer + // instance_id: InstanceId, // <- non-zero u64 // } - unsafe { + // + // and `RawGd` looks like + // + // #[repr(C)] + // pub struct RawGd { + // pub(super) obj: *mut T, // <- pointer + // cached_instance_id: Option, // <- u64 + // } + // + // So since self isn't null, that means `cached_instance_id` is not 0, and the two layouts are + // compatible. + let target = unsafe { std::mem::transmute::< - &*mut T, + &Self, &<::Declarer as dom::Domain>::DerefTarget, - >(&self.obj) - } + >(self) + }; + + Some(target) } - pub(super) fn as_target_mut( - &mut self, - ) -> &mut <::Declarer as dom::Domain>::DerefTarget { - // SAFETY: see also Deref - // - // The resulting `&mut T` is transmuted from `&mut OpaqueObject`, i.e. a *pointer* to the `opaque` field. - // `opaque` itself has a different *address* for each Gd instance, meaning that two simultaneous - // DerefMut borrows on two Gd instances will not alias, *even if* the underlying Godot object is the - // same (i.e. `opaque` has the same value, but not address). + // Target is always an engine class: + // * if T is an engine class => T + // * if T is a user class => T::Base + pub(super) fn as_target_mut(&mut self) -> Option<&mut GdDerefTarget> { + if self.is_null() { + return None; + } + + // SAFETY: see also `as_target()` // - // The `&mut self` guarantees that no other base access can take place for *the same Gd instance* (access to other Gds is OK). - unsafe { + // We have a mutable reference to self, and thus it's entirely safe to transmute that into a + // mutable reference to a compatible type. + let target = unsafe { std::mem::transmute::< - &mut *mut T, + &mut Self, &mut <::Declarer as dom::Domain>::DerefTarget, - >(&mut self.obj) - } + >(self) + }; + + Some(target) } pub(super) fn obj_sys(&self) -> sys::GDExtensionObjectPtr { @@ -231,58 +266,39 @@ where { /// Hands out a guard for a shared borrow, through which the user instance can be read. /// - /// The pattern is very similar to interior mutability with standard [`RefCell`][std::cell::RefCell]. - /// You can either have multiple `GdRef` shared guards, or a single `GdMut` exclusive guard to a Rust - /// `GodotClass` instance, independently of how many `Gd` smart pointers point to it. There are runtime - /// checks to ensure that Rust safety rules (e.g. no `&` and `&mut` coexistence) are upheld. - /// - /// # Panics - /// * If another `Gd` smart pointer pointing to the same Rust instance has a live `GdMut` guard bound. - /// * If there is an ongoing function call from GDScript to Rust, which currently holds a `&mut T` - /// reference to the user instance. This can happen through re-entrancy (Rust -> GDScript -> Rust call). + /// See [`crate::obj::Gd::bind()`] for a more in depth explanation. // Note: possible names: write/read, hold/hold_mut, r/w, r/rw, ... - pub fn bind(&self) -> GdRef { - engine::ensure_object_alive(self.cached_instance_id.get(), self.obj_sys(), "bind"); - GdRef::from_cell(self.storage().get()) + pub(crate) fn bind(&self) -> GdRef { + engine::ensure_object_alive(self.cached_instance_id, self.obj_sys(), "bind"); + GdRef::from_cell(self.storage().unwrap().get()) } /// Hands out a guard for an exclusive borrow, through which the user instance can be read and written. /// - /// The pattern is very similar to interior mutability with standard [`RefCell`][std::cell::RefCell]. - /// You can either have multiple `GdRef` shared guards, or a single `GdMut` exclusive guard to a Rust - /// `GodotClass` instance, independently of how many `Gd` smart pointers point to it. There are runtime - /// checks to ensure that Rust safety rules (e.g. no `&mut` aliasing) are upheld. - /// - /// # Panics - /// * If another `Gd` smart pointer pointing to the same Rust instance has a live `GdRef` or `GdMut` guard bound. - /// * If there is an ongoing function call from GDScript to Rust, which currently holds a `&T` or `&mut T` - /// reference to the user instance. This can happen through re-entrancy (Rust -> GDScript -> Rust call). - pub fn bind_mut(&mut self) -> GdMut { - engine::ensure_object_alive(self.cached_instance_id.get(), self.obj_sys(), "bind_mut"); - GdMut::from_cell(self.storage().get_mut()) + /// See [`crate::obj::Gd::bind_mut()`] for a more in depth explanation. + pub(crate) fn bind_mut(&mut self) -> GdMut { + engine::ensure_object_alive(self.cached_instance_id, self.obj_sys(), "bind_mut"); + GdMut::from_cell(self.storage().unwrap().get_mut()) } /// Storage object associated with the extension instance. - pub(crate) fn storage(&self) -> &InstanceStorage { + /// + /// Returns `None` if self is null. + pub(crate) fn storage(&self) -> Option<&InstanceStorage> { // SAFETY: instance pointer belongs to this instance. We only get a shared reference, no exclusive access, so even // calling this from multiple Gd pointers is safe. // Potential issue is a concurrent free() in multi-threaded access; but that would need to be guarded against inside free(). unsafe { let binding = self.resolve_instance_ptr(); - crate::private::as_storage::(binding) + sys::ptr_then(binding, |binding| crate::private::as_storage::(binding)) } } - /// Storage object associated with the extension instance. - // pub(crate) fn storage_mut(&mut self) -> &mut InstanceStorage { - // // SAFETY: - // unsafe { - // let binding = self.resolve_instance_ptr(); - // crate::private::as_storage_mut::(binding) - // } - // } - unsafe fn resolve_instance_ptr(&self) -> sys::GDExtensionClassInstancePtr { + if self.is_null() { + return ptr::null_mut(); + } + let callbacks = crate::storage::nop_instance_callbacks(); let token = sys::get_library() as *mut std::ffi::c_void; let binding = interface_fn!(object_get_instance_binding)(self.obj_sys(), token, &callbacks); @@ -317,10 +333,8 @@ where } unsafe fn from_sys_init(init: impl FnOnce(sys::GDExtensionUninitializedTypePtr)) -> Self { - let mut raw: std::mem::MaybeUninit = - std::mem::MaybeUninit::uninit(); - init(raw.as_mut_ptr() as sys::GDExtensionUninitializedTypePtr); - Self::from_obj_sys_weak(raw.assume_init()) + let obj = raw_object_init(init); + Self::from_obj_sys_weak(obj) } fn sys(&self) -> sys::GDExtensionTypePtr { @@ -332,7 +346,7 @@ where unsafe fn from_arg_ptr(ptr: sys::GDExtensionTypePtr, call_type: PtrcallType) -> Self { if ptr.is_null() { - return Self::new_null(); + return Self::null(); } let obj_ptr = if T::Mem::pass_as_ref(call_type) { @@ -384,7 +398,7 @@ where } } -impl GodotCompatible for RawGd { +impl GodotConvert for RawGd { type Via = Self; } @@ -404,61 +418,16 @@ impl FromGodot for RawGd { } } -/// _The methods in this impl block are only available for objects `T` that are manually managed, -/// i.e. anything that is not `RefCounted` or inherited from it._

-impl RawGd -where - T: GodotClass, - M: mem::PossiblyManual + mem::Memory, -{ - /// Destroy the manually-managed Godot object. - /// - /// Consumes this smart pointer and renders all other `Gd` smart pointers (as well as any GDScript references) to the same object - /// immediately invalid. Using those `Gd` instances will lead to panics, but not undefined behavior. - /// - /// This operation is **safe** and effectively prevents double-free. - /// - /// Not calling `free()` on manually-managed instances causes memory leaks, unless their ownership is delegated, for - /// example to the node tree in case of nodes. - /// - /// # Panics - /// * When the referred-to object has already been destroyed. - /// * When this is invoked on an upcast `Gd` that dynamically points to a reference-counted type (i.e. operation not supported). - pub fn free(self) { - // TODO disallow for singletons, either only at runtime or both at compile time (new memory policy) and runtime - - // Runtime check in case of T=Object, no-op otherwise - let ref_counted = T::Mem::is_ref_counted(&self); - assert_ne!( - ref_counted, Some(true), - "called free() on Gd which points to a RefCounted dynamic type; free() only supported for manually managed types." - ); - - // If ref_counted returned None, that means the instance was destroyed - assert!( - ref_counted == Some(false) && self.is_instance_valid(), - "called free() on already destroyed object" - ); - - // This destroys the Storage instance, no need to run destructor again - unsafe { - interface_fn!(object_destroy)(self.obj_sys()); - } - - std::mem::forget(self); - } -} - impl GodotNullableFfi for RawGd { fn flatten_option(opt: Option) -> Self { match opt { Some(raw) => raw, - None => Self::new_null(), + None => Self::null(), } } fn is_null(&self) -> bool { - self.obj.is_null() + Self::is_null(self) } } @@ -483,11 +452,11 @@ pub unsafe fn raw_object_init( /// Destructor with semantics depending on memory strategy. /// -/// * If this `Gd` smart pointer holds a reference-counted type, this will decrement the reference counter. +/// * If this `RawGd` smart pointer holds a reference-counted type, this will decrement the reference counter. /// If this was the last remaining reference, dropping it will invoke `T`'s destructor. /// /// * If the held object is manually-managed, **nothing happens**. -/// To destroy manually-managed `Gd` pointers, you need to call [`Self::free()`]. +/// To destroy manually-managed `RawGd` pointers, you need to call [`crate::obj::Gd::free()`]. impl Drop for RawGd { fn drop(&mut self) { // No-op for manually managed objects @@ -521,7 +490,11 @@ impl Drop for RawGd { impl Clone for RawGd { fn clone(&self) -> Self { out!("RawGd::clone"); - Self::from_obj_sys(self.obj as sys::GDExtensionObjectPtr) + if !self.is_null() { + unsafe { Self::from_obj_sys(self.obj as sys::GDExtensionObjectPtr) } + } else { + Self::null() + } } } @@ -585,3 +558,12 @@ impl GodotFfiVariant for RawGd { .map_err(|_| VariantConversionError::BadType) } } + +impl std::fmt::Debug for RawGd { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct(&format!("RawGd<{}>", std::any::type_name::())) + .field("obj", &self.obj) + .field("cached_instance_id", &self.cached_instance_id) + .finish() + } +} diff --git a/godot-core/src/obj/traits.rs b/godot-core/src/obj/traits.rs index 393570d91..1f7d1b667 100644 --- a/godot-core/src/obj/traits.rs +++ b/godot-core/src/obj/traits.rs @@ -288,7 +288,10 @@ pub mod dom { T: GodotClass, F: FnOnce(&mut T) -> R, { - closure(obj.as_target_mut()) + closure( + obj.as_target_mut() + .expect("scoped mut should not be called on a null object"), + ) } } @@ -312,7 +315,7 @@ pub mod dom { // ---------------------------------------------------------------------------------------------------------------------------------------------- pub mod mem { - use godot_ffi::{GodotNullableFfi, PtrcallType}; + use godot_ffi::PtrcallType; use super::private::Sealed; use crate::obj::{GodotClass, RawGd}; @@ -417,10 +420,11 @@ pub mod mem { fn maybe_init_ref(obj: &RawGd) { out!(" Dyn::init <{}>", std::any::type_name::()); if obj - .instance_id_or_none() + .instance_id_unchecked() .map(|id| id.is_ref_counted()) .unwrap_or(false) { + // Will call `RefCounted::init_ref()` which checks for liveness. StaticRefCount::maybe_init_ref(obj) } } @@ -428,10 +432,11 @@ pub mod mem { fn maybe_inc_ref(obj: &RawGd) { out!(" Dyn::inc <{}>", std::any::type_name::()); if obj - .instance_id_or_none() + .instance_id_unchecked() .map(|id| id.is_ref_counted()) .unwrap_or(false) { + // Will call `RefCounted::reference()` which checks for liveness. StaticRefCount::maybe_inc_ref(obj) } } @@ -439,10 +444,11 @@ pub mod mem { unsafe fn maybe_dec_ref(obj: &RawGd) -> bool { out!(" Dyn::dec <{}>", std::any::type_name::()); if obj - .instance_id_or_none() + .instance_id_unchecked() .map(|id| id.is_ref_counted()) .unwrap_or(false) { + // Will call `RefCounted::unreference()` which checks for liveness. StaticRefCount::maybe_dec_ref(obj) } else { false @@ -451,7 +457,7 @@ pub mod mem { fn is_ref_counted(obj: &RawGd) -> Option { // Return `None` if obj is dead - obj.instance_id_or_none().map(|id| id.is_ref_counted()) + obj.instance_id_unchecked().map(|id| id.is_ref_counted()) } } diff --git a/godot-ffi/src/godot_ffi.rs b/godot-ffi/src/godot_ffi.rs index b829939a9..a68ea76ba 100644 --- a/godot-ffi/src/godot_ffi.rs +++ b/godot-ffi/src/godot_ffi.rs @@ -137,6 +137,9 @@ pub unsafe fn from_sys_init_or_init_default( /// Types that can represent null-values. /// /// Used to blanket implement various conversions over `Option`. +/// +/// This is currently only implemented for `RawGd`. +// TODO: Consider implementing for `Variant`. pub trait GodotNullableFfi: Sized + GodotFfi { fn flatten_option(opt: Option) -> Self; fn is_null(&self) -> bool; diff --git a/godot-macros/src/derive/derive_godot_compatible.rs b/godot-macros/src/derive/derive_godot_compatible.rs index 26cf0dc8c..6b3491b0a 100644 --- a/godot-macros/src/derive/derive_godot_compatible.rs +++ b/godot-macros/src/derive/derive_godot_compatible.rs @@ -22,7 +22,7 @@ pub fn derive_godot_compatible(decl: Declaration) -> ParseResult { let gen = generic_params.as_ref().map(|x| x.as_inline_args()); Ok(quote! { - impl #generic_params ::godot::builtin::meta::GodotCompatible for #name #gen #where_ { + impl #generic_params ::godot::builtin::meta::GodotConvert for #name #gen #where_ { type Via = ::godot::builtin::Variant; } }) diff --git a/godot-macros/src/lib.rs b/godot-macros/src/lib.rs index bf1d0a4af..69f6362da 100644 --- a/godot-macros/src/lib.rs +++ b/godot-macros/src/lib.rs @@ -442,7 +442,7 @@ pub fn godot_api(_meta: TokenStream, input: TokenStream) -> TokenStream { translate(input, class::attribute_godot_api) } -#[proc_macro_derive(GodotCompatible)] +#[proc_macro_derive(GodotConvert)] pub fn derive_godot_compatible(input: TokenStream) -> TokenStream { translate(input, derive::derive_godot_compatible) } @@ -453,7 +453,7 @@ pub fn derive_godot_compatible(input: TokenStream) -> TokenStream { /// /// ```no_run /// # use godot::prelude::*; -/// #[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] +/// #[derive(FromGodot, ToGodot, GodotConvert, PartialEq, Debug)] /// struct StructNamed { /// field1: String, /// field2: i32, @@ -474,7 +474,7 @@ pub fn derive_godot_compatible(input: TokenStream) -> TokenStream { /// assert_eq!(obj.to_variant(), dict.to_variant()); /// ``` /// -/// You can use the `#[skip]` attribute to ignore a field from being converted to `ToVariant`. +/// You can use the `#[skip]` attribute to ignore a field from being converted to `ToGodot`. #[proc_macro_derive(ToGodot, attributes(variant))] pub fn derive_to_godot(input: TokenStream) -> TokenStream { translate(input, derive::derive_to_godot) @@ -486,7 +486,7 @@ pub fn derive_to_godot(input: TokenStream) -> TokenStream { /// /// ```no_run /// # use godot::prelude::*; -/// #[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] +/// #[derive(FromGodot, ToGodot, GodotConvert, PartialEq, Debug)] /// struct StructNamed { /// field1: String, /// field2: i32, diff --git a/godot-macros/src/util/mod.rs b/godot-macros/src/util/mod.rs index 22738be05..a65511a80 100644 --- a/godot-macros/src/util/mod.rs +++ b/godot-macros/src/util/mod.rs @@ -34,12 +34,12 @@ pub fn class_name_obj(class: &impl ToTokens) -> TokenStream { pub fn property_variant_type(property_type: &impl ToTokens) -> TokenStream { let property_type = property_type.to_token_stream(); - quote! {<<#property_type as ::godot::bind::property::Property>::Intermediate as ::godot::builtin::meta::VariantMetadata>::variant_type()} + quote! { <<<#property_type as ::godot::bind::property::Property>::Intermediate as ::godot::builtin::meta::GodotConvert>::Via as ::godot::builtin::meta::GodotType>::Ffi::variant_type() } } pub fn property_variant_class_name(property_type: &impl ToTokens) -> TokenStream { let property_type = property_type.to_token_stream(); - quote! {<<#property_type as ::godot::bind::property::Property>::Intermediate as ::godot::builtin::meta::VariantMetadata>::class_name()} + quote! { <<<#property_type as ::godot::bind::property::Property>::Intermediate as ::godot::builtin::meta::GodotConvert>::Via as ::godot::builtin::meta::GodotType>::class_name() } } pub fn bail_fn(msg: impl AsRef, tokens: T) -> ParseResult diff --git a/godot/src/lib.rs b/godot/src/lib.rs index e9bc1b6e3..3f08ef92f 100644 --- a/godot/src/lib.rs +++ b/godot/src/lib.rs @@ -179,7 +179,7 @@ pub mod init { pub mod bind { pub use godot_core::property; pub use godot_macros::{ - godot_api, Export, FromGodot, GodotClass, GodotCompatible, Property, ToGodot, + godot_api, Export, FromGodot, GodotClass, GodotConvert, Property, ToGodot, }; } @@ -196,7 +196,7 @@ pub use godot_core::private; pub mod prelude { pub use super::bind::property::{Export, Property, TypeStringHint}; pub use super::bind::{ - godot_api, Export, FromGodot, GodotClass, GodotCompatible, Property, ToGodot, + godot_api, Export, FromGodot, GodotClass, GodotConvert, Property, ToGodot, }; pub use super::builtin::math::FloatExt as _; diff --git a/itest/rust/build.rs b/itest/rust/build.rs index 94419db48..268c88ba2 100644 --- a/itest/rust/build.rs +++ b/itest/rust/build.rs @@ -55,7 +55,7 @@ macro_rules! push_newtype { #[derive(Debug, Clone, PartialEq)] pub struct $name($T); - impl godot::builtin::meta::GodotCompatible for $name { + impl godot::builtin::meta::GodotConvert for $name { type Via = $T; } diff --git a/itest/rust/src/object_tests/object_test.rs b/itest/rust/src/object_tests/object_test.rs index 3ee73cc13..2f91c713b 100644 --- a/itest/rust/src/object_tests/object_test.rs +++ b/itest/rust/src/object_tests/object_test.rs @@ -80,7 +80,7 @@ fn object_user_roundtrip_return() { let ptr = raw.sys(); std::mem::forget(obj); - let raw2 = unsafe { RawGd::::from_sys(ptr) }; + let raw2 = unsafe { RawGd::::from_sys(ptr) }; let obj2 = Gd::from_ffi(raw2); assert_eq!(obj2.bind().value, value); } // drop @@ -95,7 +95,7 @@ fn object_user_roundtrip_write() { let raw = obj.to_ffi(); let raw2 = unsafe { - RawGd::::from_sys_init(|ptr| { + RawGd::::from_sys_init(|ptr| { raw.move_return_ptr(sys::AsUninit::force_init(ptr), sys::PtrcallType::Standard) }) }; diff --git a/itest/rust/src/object_tests/property_test.rs b/itest/rust/src/object_tests/property_test.rs index 494807cfc..64866a1f0 100644 --- a/itest/rust/src/object_tests/property_test.rs +++ b/itest/rust/src/object_tests/property_test.rs @@ -449,6 +449,6 @@ fn export_resource() { class.free(); } -fn check_property(property: &Dictionary, key: &str, expected: impl ToVariant) { +fn check_property(property: &Dictionary, key: &str, expected: impl ToGodot) { assert_eq!(property.get_or_nil(key), expected.to_variant()); } diff --git a/itest/rust/src/register_tests/derive_variant_test.rs b/itest/rust/src/register_tests/derive_variant_test.rs index 6640074da..d567c8145 100644 --- a/itest/rust/src/register_tests/derive_variant_test.rs +++ b/itest/rust/src/register_tests/derive_variant_test.rs @@ -6,7 +6,7 @@ use std::fmt::Debug; -use godot::bind::{FromGodot, GodotCompatible, ToGodot}; +use godot::bind::{FromGodot, GodotConvert, ToGodot}; use godot::builtin::meta::{FromGodot, ToGodot}; use godot::builtin::{dict, varray, Variant}; @@ -16,35 +16,35 @@ use crate::framework::itest; // ---------------------------------------------------------------------------------------------------------------------------------------------- // General FromGodot/ToGodot derive tests -#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] +#[derive(FromGodot, ToGodot, GodotConvert, PartialEq, Debug)] struct StructUnit; -#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] +#[derive(FromGodot, ToGodot, GodotConvert, PartialEq, Debug)] struct StructNewType(String); -#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] +#[derive(FromGodot, ToGodot, GodotConvert, PartialEq, Debug)] struct StructTuple(String, i32); -#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] +#[derive(FromGodot, ToGodot, GodotConvert, PartialEq, Debug)] struct StructNamed { field1: String, field2: i32, } -#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] +#[derive(FromGodot, ToGodot, GodotConvert, PartialEq, Debug)] struct StructGenWhere(T) where T: ToGodot + FromGodot; trait Bound {} -#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug)] +#[derive(FromGodot, ToGodot, GodotConvert, PartialEq, Debug)] struct StructGenBound(T); -#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug, Clone)] +#[derive(FromGodot, ToGodot, GodotConvert, PartialEq, Debug, Clone)] enum Uninhabited {} -#[derive(FromGodot, ToGodot, GodotCompatible, PartialEq, Debug, Clone)] +#[derive(FromGodot, ToGodot, GodotConvert, PartialEq, Debug, Clone)] enum Enum { Unit, OneTuple(i32), @@ -145,7 +145,7 @@ macro_rules! roundtrip_with_skip { }; } -#[derive(Debug, Default, Clone, PartialEq, ToGodot, FromGodot, GodotCompatible)] +#[derive(Debug, Default, Clone, PartialEq, ToGodot, FromGodot, GodotConvert)] enum EnumWithSkip { #[variant(skip)] Skipped(String), @@ -209,10 +209,10 @@ roundtrip_with_skip!( // ---------------------------------------------------------------------------------------------------------------------------------------------- // Skipping of structs -#[derive(Debug, Default, PartialEq, ToGodot, FromGodot, GodotCompatible)] +#[derive(Debug, Default, PartialEq, ToGodot, FromGodot, GodotConvert)] struct NewTypeStructWithSkip(#[variant(skip)] String); -#[derive(Debug, Default, PartialEq, ToGodot, FromGodot, GodotCompatible)] +#[derive(Debug, Default, PartialEq, ToGodot, FromGodot, GodotConvert)] struct StructWithSkip { #[variant(skip)] skipped_field: String,