Skip to content

Commit

Permalink
Add support for typed arrays
Browse files Browse the repository at this point in the history
- Rename `Array` to `TypedArray<T>`
- Check/set runtime type of the underlying Godot `Array`
- Make all parameters and return values `T` instead of `Variant`
- Add `Array` as an alias for `TypedArray<Variant>`
- Add `array!` macro and use it in tests

See godot-rust#33 for design discussion
  • Loading branch information
ttencate committed Feb 9, 2023
1 parent b2f75f8 commit 897ad60
Show file tree
Hide file tree
Showing 17 changed files with 1,264 additions and 788 deletions.
5 changes: 5 additions & 0 deletions godot-codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ const SELECTED_CLASSES: &[&str] = &[
"Control",
"FileAccess",
"HTTPRequest",
"Image",
"ImageTextureLayered",
"Input",
"Label",
"MainLoop",
Expand All @@ -290,6 +292,9 @@ const SELECTED_CLASSES: &[&str] = &[
"SceneTree",
"Sprite2D",
"SpriteFrames",
"Texture",
"Texture2DArray",
"TextureLayered",
"Time",
"Timer",
];
861 changes: 861 additions & 0 deletions godot-core/src/builtin/array.rs

Large diffs are not rendered by default.

639 changes: 0 additions & 639 deletions godot-core/src/builtin/arrays.rs

This file was deleted.

4 changes: 4 additions & 0 deletions godot-core/src/builtin/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,13 @@ macro_rules! impl_builtin_traits_inner {
}
};

// TODO remove; use godot-core/src/builtin/variant/impls.rs instead (this one is only used for Callable)
( FromVariant for $Type:ty => $gd_method:ident ) => {
impl $crate::builtin::variant::FromVariant for $Type {
fn try_from_variant(variant: &$crate::builtin::Variant) -> Result<Self, $crate::builtin::variant::VariantConversionError> {
if variant.get_type() != <Self as $crate::builtin::meta::VariantMetadata>::variant_type() {
return Err($crate::builtin::variant::VariantConversionError)
}
let result = unsafe {
Self::from_sys_init(|self_ptr| {
let converter = sys::builtin_fn!($gd_method);
Expand Down
8 changes: 7 additions & 1 deletion godot-core/src/builtin/meta/class_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub struct ClassName {
}

impl ClassName {
pub fn new<T: GodotClass>() -> Self {
pub fn of<T: GodotClass>() -> Self {
Self {
backing: StringName::from(T::CLASS_NAME),
}
Expand All @@ -36,6 +36,12 @@ impl ClassName {
}
}

impl From<ClassName> for StringName {
fn from(class_name: ClassName) -> Self {
class_name.backing
}
}

impl Display for ClassName {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
self.backing.fmt(f)
Expand Down
6 changes: 5 additions & 1 deletion godot-core/src/builtin/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ use godot_ffi as sys;
pub trait VariantMetadata {
fn variant_type() -> VariantType;

fn class_name() -> ClassName {
ClassName::of::<()>() // FIXME Option or so
}

fn property_info(property_name: &str) -> PropertyInfo {
PropertyInfo::new(
Self::variant_type(),
ClassName::new::<()>(), // FIXME Option or so
Self::class_name(),
StringName::from(property_name),
)
}
Expand Down
7 changes: 4 additions & 3 deletions godot-core/src/builtin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
mod macros;
mod vector_macros;

mod arrays;
mod array;
mod color;
mod dictionary;
mod math;
Expand All @@ -54,9 +54,10 @@ mod vector4i;

pub mod meta;

pub use crate::dict;
// Re-export macros.
pub use crate::{array, dict};

pub use arrays::*;
pub use array::*;
pub use color::*;
pub use dictionary::*;
pub use math::*;
Expand Down
51 changes: 28 additions & 23 deletions godot-core/src/builtin/variant/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ 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)*)?
}
};
}

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;);
Expand Down Expand Up @@ -62,13 +74,7 @@ macro_rules! impl_variant_traits {
}
}

impl VariantMetadata for $T {
fn variant_type() -> VariantType {
VariantType::$variant_type
}

$($extra)*
}
impl_variant_metadata!($T, $variant_type; $($extra)*);
};
}

Expand Down Expand Up @@ -144,21 +150,19 @@ mod impls {
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 provide those, as soon as `Default` is available. Also consider auto-generating.
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!(Plane, plane_to_variant, plane_from_variant, Plane);
impl_variant_traits!(Quaternion, quaternion_to_variant, quaternion_from_variant, Quaternion);
impl_variant_traits!(Aabb, aabb_to_variant, aabb_from_variant, AABB);
impl_variant_traits!(Basis, basis_to_variant, basis_from_variant, Basis);
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!(Projection, projection_to_variant, projection_from_variant, Projection);
impl_variant_traits!(Rid, rid_to_variant, rid_from_variant, RID);
impl_variant_traits!(Callable, callable_to_variant, callable_from_variant, Callable);
impl_variant_traits!(Signal, signal_to_variant, signal_from_variant, Signal);
*/
impl_variant_traits!(Array, array_to_variant, array_from_variant, Array);
// TODO use impl_variant_traits!, as soon as `Default` is available. Also consider auto-generating.
impl_variant_metadata!(Rect2, /* rect2_to_variant, rect2_from_variant, */ Rect2);
impl_variant_metadata!(Rect2i, /* rect2i_to_variant, rect2i_from_variant, */ Rect2i);
impl_variant_metadata!(Plane, /* plane_to_variant, plane_from_variant, */ Plane);
impl_variant_metadata!(Quaternion, /* quaternion_to_variant, quaternion_from_variant, */ Quaternion);
impl_variant_metadata!(Aabb, /* aabb_to_variant, aabb_from_variant, */ Aabb);
impl_variant_metadata!(Basis, /* basis_to_variant, basis_from_variant, */ Basis);
impl_variant_metadata!(Transform2D, /* transform_2d_to_variant, transform_2d_from_variant, */ Transform2D);
impl_variant_metadata!(Transform3D, /* transform_3d_to_variant, transform_3d_from_variant, */ Transform3D);
impl_variant_metadata!(Projection, /* projection_to_variant, projection_from_variant, */ Projection);
impl_variant_metadata!(Rid, /* rid_to_variant, rid_from_variant, */ Rid);
impl_variant_metadata!(Callable, /* callable_to_variant, callable_from_variant, */ Callable);
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);
Expand Down Expand Up @@ -215,7 +219,8 @@ impl FromVariant for Variant {
// Variant itself
impl VariantMetadata for Variant {
fn variant_type() -> VariantType {
VariantType::Nil // FIXME is this correct? what else to use? is this called at all?
// Arrays use the `NIL` type to indicate that they are untyped.
VariantType::Nil
}
}

Expand Down
14 changes: 5 additions & 9 deletions godot-core/src/obj/gd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use godot_ffi::VariantType;
use sys::types::OpaqueObject;
use sys::{ffi_methods, interface_fn, static_assert_eq_size, GodotFfi};

use crate::builtin::meta::{ClassName, PropertyInfo, VariantMetadata};
use crate::builtin::{FromVariant, StringName, ToVariant, Variant, VariantConversionError};
use crate::builtin::meta::{ClassName, VariantMetadata};
use crate::builtin::{FromVariant, ToVariant, Variant, VariantConversionError};
use crate::obj::dom::Domain as _;
use crate::obj::mem::Memory as _;
use crate::obj::{cap, dom, mem, GodotClass, Inherits, Share};
Expand Down Expand Up @@ -332,7 +332,7 @@ impl<T: GodotClass> Gd<T> {
where
U: GodotClass,
{
let class_name = ClassName::new::<U>();
let class_name = ClassName::of::<U>();
let class_tag = interface_fn!(classdb_get_class_tag)(class_name.string_sys());
let cast_object_ptr = interface_fn!(object_cast_to)(self.obj_sys(), class_tag);

Expand Down Expand Up @@ -631,11 +631,7 @@ impl<T: GodotClass> VariantMetadata for Gd<T> {
VariantType::Object
}

fn property_info(property_name: &str) -> PropertyInfo {
PropertyInfo::new(
Self::variant_type(),
ClassName::new::<T>(),
StringName::from(property_name),
)
fn class_name() -> ClassName {
ClassName::of::<T>()
}
}
8 changes: 4 additions & 4 deletions godot-core/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ pub fn register_class<T: GodotExt + cap::GodotInit + cap::ImplementsGodotExt>()
// TODO: provide overloads with only some trait impls

out!("Manually register class {}", std::any::type_name::<T>());
let class_name = ClassName::new::<T>();
let class_name = ClassName::of::<T>();

let godot_params = sys::GDExtensionClassCreationInfo {
to_string_func: Some(callbacks::to_string::<T>),
Expand All @@ -133,7 +133,7 @@ pub fn register_class<T: GodotExt + cap::GodotInit + cap::ImplementsGodotExt>()

register_class_raw(ClassRegistrationInfo {
class_name,
parent_class_name: Some(ClassName::new::<T::Base>()),
parent_class_name: Some(ClassName::of::<T::Base>()),
generated_register_fn: None,
user_register_fn: Some(ErasedRegisterFn {
raw: callbacks::register_class_by_builder::<T>,
Expand Down Expand Up @@ -287,8 +287,8 @@ pub mod callbacks {
T: GodotClass,
F: FnOnce(Base<T::Base>) -> T,
{
let class_name = ClassName::new::<T>();
let base_class_name = ClassName::new::<T::Base>();
let class_name = ClassName::of::<T>();
let base_class_name = ClassName::of::<T::Base>();

//out!("create callback: {}", class_name.backing);

Expand Down
2 changes: 1 addition & 1 deletion godot-macros/src/derive_godot_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ fn make_exports_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
let class_name = ::godot::builtin::StringName::from(#class_name::CLASS_NAME);
let property_info = ::godot::builtin::meta::PropertyInfo::new(
#variant_type,
::godot::builtin::meta::ClassName::new::<#class_name>(),
::godot::builtin::meta::ClassName::of::<#class_name>(),
::godot::builtin::StringName::from(#name),
);
let property_info_sys = property_info.property_sys();
Expand Down
2 changes: 1 addition & 1 deletion godot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ pub use godot_core::private;
/// Often-imported symbols.
pub mod prelude {
pub use super::bind::{godot_api, GodotClass, GodotExt};
pub use super::builtin::dict; // Re-export macros.
pub use super::builtin::*;
pub use super::builtin::{array, dict}; // Re-export macros.
pub use super::engine::{
load, try_load, utilities, AudioStreamPlayer, Camera2D, Camera3D, Input, Node, Node2D,
Node3D, Object, PackedScene, RefCounted, Resource, SceneTree,
Expand Down
20 changes: 20 additions & 0 deletions itest/godot/ManualFfiTests.gd
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,23 @@ func test_export():

obj.free()
node.free()

func test_untyped_array_pass_to_user_func():
var obj = ArrayTest.new()
var array: Array = [42, "answer"]
assert_eq(obj.pass_untyped_array(array), 2)

func test_untyped_array_return_from_user_func():
var obj = ArrayTest.new()
var array: Array = obj.return_untyped_array()
assert_eq(array, [42, "answer"])

func test_typed_array_pass_to_user_func():
var obj = ArrayTest.new()
var array: Array[int] = [1, 2, 3]
assert_eq(obj.pass_typed_array(array), 6)

func test_typed_array_return_from_user_func():
var obj = ArrayTest.new()
var array: Array[int] = obj.return_typed_array(3)
assert_eq(array, [1, 2, 3])
2 changes: 1 addition & 1 deletion itest/godot/TestRunner.gd
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class _Test:
return ok

static func _suite_name(suite: Object) -> String:
var script := suite.get_script()
var script: Script = suite.get_script()
if script:
# Test suite written in GDScript.
return script.resource_path.get_file().get_basename()
Expand Down
Loading

0 comments on commit 897ad60

Please sign in to comment.