diff --git a/.github/workflows/minimal-ci.yml b/.github/workflows/minimal-ci.yml index db4d2eeb3..c630eec65 100644 --- a/.github/workflows/minimal-ci.yml +++ b/.github/workflows/minimal-ci.yml @@ -18,6 +18,8 @@ name: Minimal CI on: push: + branches: + - master pull_request: # branches: # - master diff --git a/gdext-class/src/obj/gd.rs b/gdext-class/src/obj/gd.rs new file mode 100644 index 000000000..d524c0cb2 --- /dev/null +++ b/gdext-class/src/obj/gd.rs @@ -0,0 +1,535 @@ +/* + * 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::marker::PhantomData; +use std::ops::{Deref, DerefMut}; +use std::ptr; + +use gdext_builtin::{FromVariant, ToVariant, Variant, VariantConversionError}; +use gdext_sys as sys; +use sys::types::OpaqueObject; +use sys::{ffi_methods, interface_fn, static_assert_eq_size, GodotFfi}; + +use crate::obj::{GdMut, GdRef, InstanceId}; +use crate::property_info::PropertyInfoBuilder; +use crate::storage::InstanceStorage; +use crate::traits::dom::Domain as _; +use crate::traits::mem::Memory as _; +use crate::traits::{cap, dom, mem, GodotClass, Inherits, Share}; +use crate::{api, callbacks, out, ClassName}; + +/// 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.) +/// or user-declared structs (`#[derive(GodotClass)]`). It does **not** hold built-in types (`Vector3`, `Color`, `i32`). +/// +/// This smart pointer behaves differently depending on `T`'s associated types, see [`GodotClass`] for their documentation. +/// In particular, the memory management strategy is fully dependent on `T`: +/// +/// * Objects of type `RefCounted` or inherited from it are **reference-counted**. This means that every time a smart pointer is +/// shared using [`Share::share()`], the reference counter is incremented, and every time one is dropped, it is decremented. +/// This ensures that the last reference (either in Rust or Godot) will deallocate the object and call `T`'s destructor. +/// +/// * Objects inheriting from `Object` which are not `RefCounted` (or inherited) are **manually-managed**. +/// Their destructor is not automatically called (unless they are part of the scene tree). Creating a `Gd` means that +/// you are responsible of explicitly deallocating such objects using [`Gd::free()`]. +/// +/// * For `T=Object`, the memory strategy is determined **dynamically**. Due to polymorphism, a `Gd` can point to either +/// reference-counted or manually-managed types at runtime. The behavior corresponds to one of the two previous points. +/// Note that if the dynamic type is also `Object`, the memory is manually-managed. +pub struct Gd { + // Note: `opaque` has the same layout as GDNativeObjectPtr == Object* in C++, i.e. the bytes represent a pointer + // To receive a GDNativeTypePtr == GDNativeObjectPtr* == Object**, we need to get the address of this + // Hence separate sys() for GDNativeTypePtr, and obj_sys() for GDNativeObjectPtr. + // The former is the standard FFI type, while the latter is used in object-specific GDExtension APIs. + // pub(crate) because accessed in traits::dom + pub(crate) opaque: OpaqueObject, + _marker: PhantomData<*const T>, +} + +// Size equality check (should additionally be covered by mem::transmute()) +static_assert_eq_size!( + sys::GDNativeObjectPtr, + sys::types::OpaqueObject, + "Godot FFI: pointer type `Object*` should have size advertised in JSON extension file" +); + +/// The methods in this impl block are only available for user-declared `T`, that is, +/// structs with `#[derive(GodotClass)]` but not Godot classes like `Node` or `RefCounted`. +impl Gd +where + T: GodotClass, +{ + /// Moves a user-created object into this smart pointer, submitting ownership to the Godot engine. + /// + /// This is only useful for types `T` which do not store their base objects (if they have a base, + /// you cannot construct them standalone). + pub fn new(user_object: T) -> Self { + /*let result = unsafe { + //let ptr = interface_fn!(classdb_construct_object)(class_name.c_str()); + let ptr = callbacks::create::(ptr::null_mut()); + Obj::from_obj_sys(ptr) + }; + + result.storage().initialize(user_object);*/ + + let object_ptr = callbacks::create_custom(move |_base| user_object); + let result = unsafe { Gd::from_obj_sys(object_ptr) }; + + T::Mem::maybe_init_ref(&result); + result + } + + /// Creates a default-constructed instance of `T` inside a smart pointer. + /// + /// This is equivalent to the GDScript expression `T.new()`. + pub fn new_default() -> Self + where + T: cap::GodotInit, + { + /*let class_name = ClassName::new::(); + let result = unsafe { + let ptr = interface_fn!(classdb_construct_object)(class_name.c_str()); + Obj::from_obj_sys(ptr) + }; + + result.storage().initialize_default(); + T::Mem::maybe_init_ref(&result); + result*/ + + let result = unsafe { + let object_ptr = callbacks::create::(ptr::null_mut()); + Gd::from_obj_sys(object_ptr) + }; + + T::Mem::maybe_init_ref(&result); + result + } + + // Note: possible names: write/read, hold/hold_mut, r/w, r/rw, ... + pub fn bind(&self) -> GdRef { + GdRef::from_cell(self.storage().get()) + } + + pub fn bind_mut(&mut self) -> GdMut { + GdMut::from_cell(self.storage().get_mut()) + } + + /// Storage object associated with the extension instance + pub(crate) fn storage(&self) -> &mut InstanceStorage { + let callbacks = crate::storage::nop_instance_callbacks(); + + unsafe { + let token = sys::get_library(); + 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::() + ); + crate::private::as_storage::(binding) + } + } +} + +/// The methods in this impl block are available for any `T`. +impl Gd { + /// Looks up the given instance ID and returns the associated object, if possible. + /// + /// If no such instance ID is registered, or if the dynamic type of the object behind that instance ID + /// is not compatible with `T`, then `None` is returned. + pub fn try_from_instance_id(instance_id: InstanceId) -> Option { + // SAFETY: Godot looks up ID in ObjectDB and returns null if not found + let ptr = unsafe { interface_fn!(object_get_instance_from_id)(instance_id.to_u64()) }; + + if ptr.is_null() { + None + } else { + // SAFETY: assumes that the returned GDNativeObjectPtr is convertible to Object* (i.e. C++ upcast doesn't modify the pointer) + let untyped = unsafe { Gd::::from_obj_sys(ptr).ready() }; + untyped.owned_cast::() + } + } + + /// Looks up the given instance ID and returns the associated object. + /// + /// # Panics + /// If no such instance ID is registered, or if the dynamic type of the object behind that instance ID + /// is not compatible with `T`. + #[cfg(feature = "convenience")] + pub fn from_instance_id(instance_id: InstanceId) -> Self { + Self::try_from_instance_id(instance_id).unwrap_or_else(|| { + panic!( + "Instance ID {} does not belong to a valid object of class '{}'", + instance_id, + T::CLASS_NAME + ) + }) + } + + fn from_opaque(opaque: OpaqueObject) -> Self { + Self { + opaque, + _marker: PhantomData, + } + } + + /// Returns the instance ID of this object, or `None` if the object is dead. + /// + pub fn instance_id_or_none(&self) -> Option { + // Note: bit 'id & (1 << 63)' determines if the instance is ref-counted + let id = unsafe { interface_fn!(object_get_instance_id)(self.obj_sys()) }; + InstanceId::try_from_u64(id) + } + + /// Returns the instance ID of this object (panics when dead). + /// + /// # Panics + /// If this object is no longer alive (registered in Godot's object database). + #[cfg(feature = "convenience")] + pub fn instance_id(&self) -> InstanceId { + self.instance_id_or_none().unwrap_or_else(|| { + panic!( + "failed to call instance_id() on destroyed object; \ + use instance_id_or_none() or keep your objects alive" + ) + }) + } + + /// Checks if this smart pointer points to a live object (read description!). + /// + /// Using this method is often indicative of bad design -- you should dispose of your pointers once an object is + /// destroyed. However, this method exists because GDScript offers it and there may be **rare** use cases. + /// + /// Do not use this method to check if you can safely access an object. Accessing dead objects is generally safe + /// and will panic in a defined manner. Encountering such panics is almost always a bug you should fix, and not a + /// runtime condition to check against. + pub fn is_instance_valid(&self) -> bool { + // TODO Is this really necessary, or is Godot's instance_id() guaranteed to return 0 for destroyed objects? + if let Some(id) = self.instance_id_or_none() { + api::utilities::is_instance_id_valid(id.to_i64()) + } else { + false + } + } + + /// Needed to initialize ref count -- must be explicitly invoked. + /// + /// Could be made part of FFI methods, but there are some edge cases where this is not intended. + pub(crate) fn ready(self) -> Self { + T::Mem::maybe_inc_ref(&self); + self + } + + /// **Upcast:** convert into a smart pointer to a base class. Always succeeds. + /// + /// Moves out of this value. If you want to create _another_ smart pointer instance, + /// use this idiom: + /// ```ignore + /// let obj: Gd = ...; + /// let base = obj.share().upcast::(); + /// ``` + pub fn upcast(self) -> Gd + where + Base: GodotClass, + T: Inherits, + { + self.owned_cast() + .expect("Upcast failed. This is a bug; please report it.") + } + + /// **Downcast:** try to convert into a smart pointer to a derived class. + /// + /// If `T`'s dynamic type is not `Derived` or one of its subclasses, `None` is returned + /// and the reference is dropped. Otherwise, `Some` is returned and the ownership is moved + /// to the returned value. + // TODO consider Result, Self> so that user can still use original object (e.g. to free if manual) + pub fn try_cast(self) -> Option> + where + Derived: GodotClass + Inherits, + { + self.owned_cast() + } + + /// **Downcast:** convert into a smart pointer to a derived class. Panics on error. + /// + /// # Panics + /// If the class' dynamic type is not `Derived` or one of its subclasses. Use [`Self::try_cast()`] if you want to check the result. + #[cfg(feature = "convenience")] + pub fn cast(self) -> Gd + where + Derived: GodotClass + Inherits, + { + self.owned_cast().unwrap_or_else(|| { + panic!( + "downcast from {from} to {to} failed; correct the code or use try_cast()", + from = T::CLASS_NAME, + to = Derived::CLASS_NAME + ) + }) + } + + fn owned_cast(self) -> Option> + where + U: GodotClass, + { + // 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::() }; + if result.is_some() { + // duplicated ref, one must be wiped + std::mem::forget(self); + } + + result + } + + // 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_name = ClassName::new::(); + let class_tag = interface_fn!(classdb_get_class_tag)(class_name.c_str()); + let cast_object_ptr = interface_fn!(object_cast_to)(self.obj_sys(), class_tag); + + if cast_object_ptr.is_null() { + None + } else { + Some(Gd::from_obj_sys(cast_object_ptr)) + } + } + + pub(crate) fn as_ref_counted(&self, apply: impl Fn(&mut api::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 api::Object) -> R) -> R { + // 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()); + // std::mem::forget(tmp); // no ownership transfer + // return_val + // } + + // Conversions from/to Godot C++ `Object*` pointers + ffi_methods! { + type sys::GDNativeObjectPtr = Opaque; + + fn from_obj_sys = from_sys; + fn from_obj_sys_init = from_sys_init; + fn obj_sys = sys; + fn write_obj_sys = write_sys; + } +} + +/// 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. + /// + /// # Panics + /// When the referred-to object has already been destroyed, or when this is invoked on an upcast `Gd` + /// that dynamically points to a reference-counted type. + pub fn free(self) { + // 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 +where + T: GodotClass, +{ + type Target = T; + + 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 GDNativeObjectPtr + // _marker: PhantomData, <- ZST + // } + // struct Node3D { + // object_ptr: sys::GDNativeObjectPtr, + // } + unsafe { std::mem::transmute::<&OpaqueObject, &T>(&self.opaque) } + } +} + +impl DerefMut for Gd +where + T: GodotClass, +{ + fn deref_mut(&mut self) -> &mut T { + // SAFETY: see Deref + unsafe { std::mem::transmute::<&mut OpaqueObject, &mut T>(&mut self.opaque) } + } +} + +impl GodotFfi for Gd { + ffi_methods! { type sys::GDNativeTypePtr = Opaque; .. } +} + +/// 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::()); + let is_last = 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 Share for Gd { + fn share(&self) -> Self { + out!("Gd::share"); + Self::from_opaque(self.opaque).ready() + } +} + +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Trait impls + +impl FromVariant for Gd { + fn try_from_variant(variant: &Variant) -> Result { + let result = unsafe { + let result = Self::from_sys_init(|self_ptr| { + let converter = sys::method_table().object_from_variant; + converter(self_ptr, variant.var_sys()); + }); + result.ready() + }; + + Ok(result) + } +} + +impl ToVariant for Gd { + fn to_variant(&self) -> Variant { + let variant = unsafe { + Variant::from_var_sys_init(|variant_ptr| { + let converter = sys::method_table().object_to_variant; + + // Note: this is a special case because of an inconsistency in Godot, where sometimes the equivalency is + // GDNativeTypePtr == Object** and sometimes GDNativeTypePtr == 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 *mut _); + }) + }; + + variant + } +} + +impl std::fmt::Debug for Gd { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // If you change this, don't forget to update Base::fmt() + if let Some(id) = self.instance_id_or_none() { + write!(f, "Gd{{ id: {} }}", id) + } else { + write!(f, "Gd{{ freed object }}") + } + } +} + +impl PropertyInfoBuilder for Gd { + fn variant_type() -> sys::GDNativeVariantType { + gdext_sys::GDNativeVariantType_GDNATIVE_VARIANT_TYPE_OBJECT + } + + fn property_info(name: &str) -> sys::GDNativePropertyInfo { + // Note: filling this information properly is important so that Godot can use ptrcalls instead of varcalls + // (requires typed GDScript + sufficient information from the extension side) + let reg = unsafe { sys::get_registry() }; + + let property_name = reg.c_string(name); + let class_name = reg.c_string(T::CLASS_NAME); + + sys::GDNativePropertyInfo { + type_: Self::variant_type(), + name: property_name, + class_name, + hint: 0, + hint_string: ptr::null_mut(), + usage: 7, // Default, TODO generate global enums + } + } +} diff --git a/godot-codegen/input/gdnative_interface.h b/godot-codegen/input/gdnative_interface.h index 8b980a45c..860f1de43 100644 --- a/godot-codegen/input/gdnative_interface.h +++ b/godot-codegen/input/gdnative_interface.h @@ -109,6 +109,7 @@ typedef enum { GDNATIVE_VARIANT_OP_LESS_EQUAL, GDNATIVE_VARIANT_OP_GREATER, GDNATIVE_VARIANT_OP_GREATER_EQUAL, + /* mathematic */ GDNATIVE_VARIANT_OP_ADD, GDNATIVE_VARIANT_OP_SUBTRACT, @@ -118,6 +119,7 @@ typedef enum { GDNATIVE_VARIANT_OP_POSITIVE, GDNATIVE_VARIANT_OP_MODULE, GDNATIVE_VARIANT_OP_POWER, + /* bitwise */ GDNATIVE_VARIANT_OP_SHIFT_LEFT, GDNATIVE_VARIANT_OP_SHIFT_RIGHT, @@ -125,11 +127,13 @@ typedef enum { GDNATIVE_VARIANT_OP_BIT_OR, GDNATIVE_VARIANT_OP_BIT_XOR, GDNATIVE_VARIANT_OP_BIT_NEGATE, + /* logic */ GDNATIVE_VARIANT_OP_AND, GDNATIVE_VARIANT_OP_OR, GDNATIVE_VARIANT_OP_XOR, GDNATIVE_VARIANT_OP_NOT, + /* containment */ GDNATIVE_VARIANT_OP_IN, GDNATIVE_VARIANT_OP_MAX @@ -152,11 +156,11 @@ typedef uint64_t GDObjectInstanceID; typedef enum { GDNATIVE_CALL_OK, GDNATIVE_CALL_ERROR_INVALID_METHOD, - GDNATIVE_CALL_ERROR_INVALID_ARGUMENT, /* expected is variant type */ - GDNATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, /* expected is number of arguments */ - GDNATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, /* expected is number of arguments */ + GDNATIVE_CALL_ERROR_INVALID_ARGUMENT, // Expected a different variant type. + GDNATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, // Expected lower number of arguments. + GDNATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, // Expected higher number of arguments. GDNATIVE_CALL_ERROR_INSTANCE_IS_NULL, - GDNATIVE_CALL_ERROR_METHOD_NOT_CONST, /* used for const call */ + GDNATIVE_CALL_ERROR_METHOD_NOT_CONST, // Used for const call. } GDNativeCallErrorType; typedef struct { @@ -202,22 +206,26 @@ typedef uint64_t (*GDNativeExtensionClassGetRID)(GDExtensionClassInstancePtr p_i typedef struct { GDNativeVariantType type; - const char *name; - const char *class_name; - uint32_t hint; // Bitfield of `PropertyHint` (defined in `extension_api.json`) - const char *hint_string; - uint32_t usage; // Bitfield of `PropertyUsageFlags` (defined in `extension_api.json`) + GDNativeStringNamePtr name; + GDNativeStringNamePtr class_name; + uint32_t hint; // Bitfield of `PropertyHint` (defined in `extension_api.json`). + GDNativeStringPtr hint_string; + uint32_t usage; // Bitfield of `PropertyUsageFlags` (defined in `extension_api.json`). } GDNativePropertyInfo; typedef struct { - const char *name; + GDNativeStringNamePtr name; GDNativePropertyInfo return_value; - uint32_t flags; // Bitfield of `GDNativeExtensionClassMethodFlags` + uint32_t flags; // Bitfield of `GDNativeExtensionClassMethodFlags`. int32_t id; - GDNativePropertyInfo *arguments; + + /* Arguments: `default_arguments` is an array of size `argument_count`. */ uint32_t argument_count; - GDNativeVariantPtr default_arguments; + GDNativePropertyInfo *arguments; + + /* Default arguments: `default_arguments` is an array of size `default_argument_count`. */ uint32_t default_argument_count; + GDNativeVariantPtr *default_arguments; } GDNativeMethodInfo; typedef const GDNativePropertyInfo *(*GDNativeExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count); @@ -225,13 +233,13 @@ typedef void (*GDNativeExtensionClassFreePropertyList)(GDExtensionClassInstanceP typedef GDNativeBool (*GDNativeExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name); typedef GDNativeBool (*GDNativeExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret); typedef void (*GDNativeExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what); -typedef void (*GDNativeExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDNativeStringPtr p_out); +typedef void (*GDNativeExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDNativeBool *r_is_valid, GDNativeStringPtr p_out); typedef void (*GDNativeExtensionClassReference)(GDExtensionClassInstancePtr p_instance); typedef void (*GDNativeExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance); typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); typedef GDNativeObjectPtr (*GDNativeExtensionClassCreateInstance)(void *p_userdata); typedef void (*GDNativeExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance); -typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, const char *p_name); +typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, const GDNativeStringNamePtr p_name); typedef struct { GDNativeBool is_virtual; @@ -246,11 +254,11 @@ typedef struct { GDNativeExtensionClassToString to_string_func; GDNativeExtensionClassReference reference_func; GDNativeExtensionClassUnreference unreference_func; - GDNativeExtensionClassCreateInstance create_instance_func; /* this one is mandatory */ - GDNativeExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ - GDNativeExtensionClassGetVirtual get_virtual_func; + GDNativeExtensionClassCreateInstance create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract. + GDNativeExtensionClassFreeInstance free_instance_func; // Destructor; mandatory. + GDNativeExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function. GDNativeExtensionClassGetRID get_rid_func; - void *class_userdata; + void *class_userdata; // Per-class user data, later accessible in instance bindings. } GDNativeExtensionClassCreationInfo; typedef void *GDNativeExtensionClassLibraryPtr; @@ -284,29 +292,33 @@ typedef enum { typedef void (*GDNativeExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); typedef void (*GDNativeExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); -/* passing -1 as argument in the following functions refers to the return type */ -typedef GDNativeVariantType (*GDNativeExtensionClassMethodGetArgumentType)(void *p_method_userdata, int32_t p_argument); -typedef void (*GDNativeExtensionClassMethodGetArgumentInfo)(void *p_method_userdata, int32_t p_argument, GDNativePropertyInfo *r_info); -typedef GDNativeExtensionClassMethodArgumentMetadata (*GDNativeExtensionClassMethodGetArgumentMetadata)(void *p_method_userdata, int32_t p_argument); - typedef struct { - const char *name; + GDNativeStringNamePtr name; void *method_userdata; GDNativeExtensionClassMethodCall call_func; GDNativeExtensionClassMethodPtrCall ptrcall_func; - uint32_t method_flags; // Bitfield of `GDNativeExtensionClassMethodFlags` - uint32_t argument_count; + uint32_t method_flags; // Bitfield of `GDNativeExtensionClassMethodFlags`. + + /* If `has_return_value` is false, `return_value_info` and `return_value_metadata` are ignored. */ GDNativeBool has_return_value; - GDNativeExtensionClassMethodGetArgumentType get_argument_type_func; - GDNativeExtensionClassMethodGetArgumentInfo get_argument_info_func; /* name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies. */ - GDNativeExtensionClassMethodGetArgumentMetadata get_argument_metadata_func; + GDNativePropertyInfo *return_value_info; + GDNativeExtensionClassMethodArgumentMetadata return_value_metadata; + + /* Arguments: `arguments_info` and `arguments_metadata` are array of size `argument_count`. + * Name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies. + */ + uint32_t argument_count; + GDNativePropertyInfo *arguments_info; + GDNativeExtensionClassMethodArgumentMetadata *arguments_metadata; + + /* Default arguments: `default_arguments` is an array of size `default_argument_count`. */ uint32_t default_argument_count; GDNativeVariantPtr *default_arguments; } GDNativeExtensionClassMethodInfo; /* SCRIPT INSTANCE EXTENSION */ -typedef void *GDNativeExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation +typedef void *GDNativeExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation. typedef GDNativeBool (*GDNativeExtensionScriptInstanceSet)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value); typedef GDNativeBool (*GDNativeExtensionScriptInstanceGet)(GDNativeExtensionScriptInstanceDataPtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret); @@ -328,7 +340,7 @@ typedef GDNativeBool (*GDNativeExtensionScriptInstanceHasMethod)(GDNativeExtensi typedef void (*GDNativeExtensionScriptInstanceCall)(GDNativeExtensionScriptInstanceDataPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); typedef void (*GDNativeExtensionScriptInstanceNotification)(GDNativeExtensionScriptInstanceDataPtr p_instance, int32_t p_what); -typedef const char *(*GDNativeExtensionScriptInstanceToString)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeBool *r_is_valid); +typedef void (*GDNativeExtensionScriptInstanceToString)(GDNativeExtensionScriptInstanceDataPtr p_instance, GDNativeBool *r_is_valid, GDNativeStringPtr r_out); typedef void (*GDNativeExtensionScriptInstanceRefCountIncremented)(GDNativeExtensionScriptInstanceDataPtr p_instance); typedef GDNativeBool (*GDNativeExtensionScriptInstanceRefCountDecremented)(GDNativeExtensionScriptInstanceDataPtr p_instance); @@ -349,7 +361,6 @@ typedef struct { GDNativeExtensionScriptInstanceGet get_func; GDNativeExtensionScriptInstanceGetPropertyList get_property_list_func; GDNativeExtensionScriptInstanceFreePropertyList free_property_list_func; - GDNativeExtensionScriptInstanceGetPropertyType get_property_type_func; GDNativeExtensionScriptInstancePropertyCanRevert property_can_revert_func; GDNativeExtensionScriptInstancePropertyGetRevert property_get_revert_func; @@ -359,6 +370,7 @@ typedef struct { GDNativeExtensionScriptInstanceGetMethodList get_method_list_func; GDNativeExtensionScriptInstanceFreeMethodList free_method_list_func; + GDNativeExtensionScriptInstanceGetPropertyType get_property_type_func; GDNativeExtensionScriptInstanceHasMethod has_method_func; @@ -392,6 +404,7 @@ typedef struct { const char *version_string; /* GODOT CORE */ + void *(*mem_alloc)(size_t p_bytes); void *(*mem_realloc)(void *p_ptr, size_t p_bytes); void (*mem_free)(void *p_ptr); @@ -400,7 +413,7 @@ typedef struct { void (*print_warning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line); void (*print_script_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line); - uint64_t (*get_native_struct_size)(const char *p_name); + uint64_t (*get_native_struct_size)(const GDNativeStringNamePtr p_name); /* GODOT VARIANT */ @@ -443,22 +456,21 @@ typedef struct { GDNativeVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDNativeVariantType p_type); GDNativeTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDNativeVariantType p_type); GDNativePtrOperatorEvaluator (*variant_get_ptr_operator_evaluator)(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b); - GDNativePtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDNativeVariantType p_type, const char *p_method, GDNativeInt p_hash); + GDNativePtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, GDNativeInt p_hash); GDNativePtrConstructor (*variant_get_ptr_constructor)(GDNativeVariantType p_type, int32_t p_constructor); GDNativePtrDestructor (*variant_get_ptr_destructor)(GDNativeVariantType p_type); void (*variant_construct)(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error); - GDNativePtrSetter (*variant_get_ptr_setter)(GDNativeVariantType p_type, const char *p_member); - GDNativePtrGetter (*variant_get_ptr_getter)(GDNativeVariantType p_type, const char *p_member); + GDNativePtrSetter (*variant_get_ptr_setter)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member); + GDNativePtrGetter (*variant_get_ptr_getter)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member); GDNativePtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDNativeVariantType p_type); GDNativePtrIndexedGetter (*variant_get_ptr_indexed_getter)(GDNativeVariantType p_type); GDNativePtrKeyedSetter (*variant_get_ptr_keyed_setter)(GDNativeVariantType p_type); GDNativePtrKeyedGetter (*variant_get_ptr_keyed_getter)(GDNativeVariantType p_type); GDNativePtrKeyedChecker (*variant_get_ptr_keyed_checker)(GDNativeVariantType p_type); - void (*variant_get_constant_value)(GDNativeVariantType p_type, const char *p_constant, GDNativeVariantPtr r_ret); - GDNativePtrUtilityFunction (*variant_get_ptr_utility_function)(const char *p_function, GDNativeInt p_hash); + void (*variant_get_constant_value)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_constant, GDNativeVariantPtr r_ret); + GDNativePtrUtilityFunction (*variant_get_ptr_utility_function)(const GDNativeStringNamePtr p_function, GDNativeInt p_hash); /* extra utilities */ - void (*string_new_with_latin1_chars)(GDNativeStringPtr r_dest, const char *p_contents); void (*string_new_with_utf8_chars)(GDNativeStringPtr r_dest, const char *p_contents); void (*string_new_with_utf16_chars)(GDNativeStringPtr r_dest, const char16_t *p_contents); @@ -469,6 +481,7 @@ typedef struct { void (*string_new_with_utf16_chars_and_len)(GDNativeStringPtr r_dest, const char16_t *p_contents, const GDNativeInt p_size); void (*string_new_with_utf32_chars_and_len)(GDNativeStringPtr r_dest, const char32_t *p_contents, const GDNativeInt p_size); void (*string_new_with_wide_chars_and_len)(GDNativeStringPtr r_dest, const wchar_t *p_contents, const GDNativeInt p_size); + /* Information about the following functions: * - The return value is the resulting encoded string length. * - The length returned is in characters, not in bytes. It also does not include a trailing zero. @@ -524,12 +537,12 @@ typedef struct { void (*object_method_bind_call)(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeVariantPtr *p_args, GDNativeInt p_arg_count, GDNativeVariantPtr r_ret, GDNativeCallError *r_error); void (*object_method_bind_ptrcall)(const GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); void (*object_destroy)(GDNativeObjectPtr p_o); - GDNativeObjectPtr (*global_get_singleton)(const char *p_name); + GDNativeObjectPtr (*global_get_singleton)(const GDNativeStringNamePtr p_name); void *(*object_get_instance_binding)(GDNativeObjectPtr p_o, void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks); void (*object_set_instance_binding)(GDNativeObjectPtr p_o, void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks); - void (*object_set_instance)(GDNativeObjectPtr p_o, const char *p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */ + void (*object_set_instance)(GDNativeObjectPtr p_o, const GDNativeStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */ GDNativeObjectPtr (*object_cast_to)(const GDNativeObjectPtr p_object, void *p_class_tag); GDNativeObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id); @@ -540,20 +553,22 @@ typedef struct { GDNativeScriptInstancePtr (*script_instance_create)(const GDNativeExtensionScriptInstanceInfo *p_info, GDNativeExtensionScriptInstanceDataPtr p_instance_data); /* CLASSDB */ - GDNativeObjectPtr (*classdb_construct_object)(const char *p_classname); /* The passed class must be a built-in godot class, or an already-registered extension class. In both case, object_set_instance should be called to fully initialize the object. */ - GDNativeMethodBindPtr (*classdb_get_method_bind)(const char *p_classname, const char *p_methodname, GDNativeInt p_hash); - void *(*classdb_get_class_tag)(const char *p_classname); + + GDNativeObjectPtr (*classdb_construct_object)(const GDNativeStringNamePtr p_classname); /* The passed class must be a built-in godot class, or an already-registered extension class. In both case, object_set_instance should be called to fully initialize the object. */ + GDNativeMethodBindPtr (*classdb_get_method_bind)(const GDNativeStringNamePtr p_classname, const GDNativeStringNamePtr p_methodname, GDNativeInt p_hash); + void *(*classdb_get_class_tag)(const GDNativeStringNamePtr p_classname); /* CLASSDB EXTENSION */ - void (*classdb_register_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs); - void (*classdb_register_extension_class_method)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info); - void (*classdb_register_extension_class_integer_constant)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, GDNativeInt p_constant_value, GDNativeBool p_is_bitfield); - void (*classdb_register_extension_class_property)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter); - void (*classdb_register_extension_class_property_group)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_group_name, const char *p_prefix); - void (*classdb_register_extension_class_property_subgroup)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_subgroup_name, const char *p_prefix); - void (*classdb_register_extension_class_signal)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count); - void (*classdb_unregister_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */ + /* Provided parameters for `classdb_register_extension_*` can be safely freed once the function returns. */ + void (*classdb_register_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs); + void (*classdb_register_extension_class_method)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info); + void (*classdb_register_extension_class_integer_constant)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_enum_name, const GDNativeStringNamePtr p_constant_name, GDNativeInt p_constant_value, GDNativeBool p_is_bitfield); + void (*classdb_register_extension_class_property)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativePropertyInfo *p_info, const GDNativeStringNamePtr p_setter, const GDNativeStringNamePtr p_getter); + void (*classdb_register_extension_class_property_group)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringPtr p_group_name, const GDNativeStringPtr p_prefix); + void (*classdb_register_extension_class_property_subgroup)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringPtr p_subgroup_name, const GDNativeStringPtr p_prefix); + void (*classdb_register_extension_class_signal)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name, const GDNativeStringNamePtr p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count); + void (*classdb_unregister_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const GDNativeStringNamePtr p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */ void (*get_library_path)(const GDNativeExtensionClassLibraryPtr p_library, GDNativeStringPtr r_path); @@ -581,9 +596,10 @@ typedef struct { } GDNativeInitialization; /* Define a C function prototype that implements the function below and expose it to dlopen() (or similar). - * It will be called on initialization. The name must be an unique one specified in the .gdextension config file. + * This is the entry point of the GDExtension library and will be called on initialization. + * It can be used to set up different init levels, which are called during various stages of initialization/shutdown. + * The function name must be a unique one specified in the .gdextension config file. */ - typedef GDNativeBool (*GDNativeInitializationFunction)(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization); #ifdef __cplusplus diff --git a/godot-codegen/src/central_generator.rs b/godot-codegen/src/central_generator.rs index 56f51bf0a..37efe3b7c 100644 --- a/godot-codegen/src/central_generator.rs +++ b/godot-codegen/src/central_generator.rs @@ -124,7 +124,7 @@ fn make_sys_code(central_items: &CentralItems) -> String { impl VariantType { #[doc(hidden)] - pub fn from_ord(enumerator: crate::GDNativeVariantType) -> Self { + pub fn from_sys(enumerator: crate::GDNativeVariantType) -> Self { // Annoying, but only stable alternative is transmute(), which dictates enum size match enumerator { 0 => Self::Nil, @@ -136,7 +136,7 @@ fn make_sys_code(central_items: &CentralItems) -> String { } #[doc(hidden)] - pub fn to_ord(self) -> crate::GDNativeVariantType { + pub fn sys(self) -> crate::GDNativeVariantType { self as _ } } @@ -155,7 +155,7 @@ fn make_sys_code(central_items: &CentralItems) -> String { impl VariantOperator { #[doc(hidden)] - pub fn from_ord(enumerator: crate::GDNativeVariantOperator) -> Self { + pub fn from_sys(enumerator: crate::GDNativeVariantOperator) -> Self { match enumerator { #( #variant_op_enumerators_ord => Self::#variant_op_enumerators_pascal, @@ -165,7 +165,7 @@ fn make_sys_code(central_items: &CentralItems) -> String { } #[doc(hidden)] - pub fn to_ord(self) -> crate::GDNativeVariantOperator { + pub fn sys(self) -> crate::GDNativeVariantOperator { self as _ } } diff --git a/godot-codegen/src/class_generator.rs b/godot-codegen/src/class_generator.rs index 1ea8ad638..d031fc726 100644 --- a/godot-codegen/src/class_generator.rs +++ b/godot-codegen/src/class_generator.rs @@ -6,12 +6,12 @@ //! Generates a file for each Godot class -use proc_macro2::TokenStream; +use proc_macro2::{Literal, TokenStream}; use quote::{format_ident, quote}; use std::path::{Path, PathBuf}; use crate::api_parser::*; -use crate::util::{c_str, ident, safe_ident, strlit, to_module_name, to_rust_type}; +use crate::util::{ident, safe_ident, strlit, to_module_name, to_rust_type}; use crate::{special_cases, util, Context, GeneratedClass, GeneratedModule, RustTy}; pub(crate) fn generate_class_files( @@ -59,7 +59,7 @@ pub(crate) fn generate_class_files( out_files.push(out_path); } -fn make_constructor(class: &Class, ctx: &Context, class_name_cstr: TokenStream) -> TokenStream { +fn make_constructor(class: &Class, ctx: &Context, class_name_str: &Literal) -> TokenStream { if ctx.is_singleton(&class.name) { // Note: we cannot return &'static mut Self, as this would be very easy to mutably alias. // &'static Self would be possible, but we would lose the whole mutability information (even if that @@ -69,7 +69,8 @@ fn make_constructor(class: &Class, ctx: &Context, class_name_cstr: TokenStream) quote! { pub fn singleton() -> Gd { unsafe { - let object_ptr = sys::interface_fn!(global_get_singleton)(#class_name_cstr); + let class_name = StringName::from(#class_name_str); + let object_ptr = sys::interface_fn!(global_get_singleton)(class_name.string_sys()); Gd::from_obj_sys(object_ptr) } } @@ -82,7 +83,8 @@ fn make_constructor(class: &Class, ctx: &Context, class_name_cstr: TokenStream) quote! { pub fn new() -> Gd { unsafe { - let object_ptr = sys::interface_fn!(classdb_construct_object)(#class_name_cstr); + let class_name = StringName::from(#class_name_str); + let object_ptr = sys::interface_fn!(classdb_construct_object)(class_name.string_sys()); //let instance = Self { object_ptr }; Gd::from_obj_sys(object_ptr) } @@ -94,7 +96,8 @@ fn make_constructor(class: &Class, ctx: &Context, class_name_cstr: TokenStream) #[must_use] pub fn new_alloc() -> Gd { unsafe { - let object_ptr = sys::interface_fn!(classdb_construct_object)(#class_name_cstr); + let class_name = StringName::from(#class_name_str); + let object_ptr = sys::interface_fn!(classdb_construct_object)(class_name.string_sys()); Gd::from_obj_sys(object_ptr) } } @@ -114,9 +117,8 @@ fn make_class(class: &Class, ctx: &mut Context) -> GeneratedClass { let name = ident(&class.name); let name_str = strlit(&class.name); - let name_cstr = c_str(&class.name); - let constructor = make_constructor(class, ctx, name_cstr); + let constructor = make_constructor(class, ctx, &name_str); let methods = make_methods(&class.methods, &class.name, ctx); let enums = make_enums(&class.enums, &class.name, ctx); @@ -356,7 +358,7 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context) let is_varcall = method.is_vararg; let (params, arg_exprs) = make_params(&method.arguments, is_varcall, ctx); - let method_name = special_cases::maybe_renamed(class_name, &method.name); + let method_name_str = special_cases::maybe_renamed(class_name, &method.name); /*if method.map_args(|args| args.is_empty()) { // Getters (i.e. 0 arguments) will be stripped of their `get_` prefix, to conform to Rust convention if let Some(remainder) = method_name.strip_prefix("get_") { @@ -367,10 +369,7 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context) } } }*/ - let method_name = safe_ident(method_name); - - let c_method_name = c_str(&method.name); - let c_class_name = c_str(class_name); + let method_name = safe_ident(method_name_str); let hash = method.hash; // TODO &mut safety @@ -393,7 +392,13 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context) quote! { #vis fn #method_name( #receiver #(, #params )*, varargs: &[Variant]) #return_decl { unsafe { - let method_bind = sys::interface_fn!(classdb_get_method_bind)(#c_class_name, #c_method_name, #hash); + let class_name = StringName::from(#class_name); + let method_name = StringName::from(#method_name_str); + let method_bind = sys::interface_fn!(classdb_get_method_bind)( + class_name.string_sys(), + method_name.string_sys(), + #hash + ); let call_fn = sys::interface_fn!(object_method_bind_call); let explicit_args = [ @@ -414,7 +419,13 @@ fn make_method_definition(method: &Method, class_name: &str, ctx: &mut Context) quote! { #vis fn #method_name( #receiver, #( #params ),* ) #return_decl { unsafe { - let method_bind = sys::interface_fn!(classdb_get_method_bind)(#c_class_name, #c_method_name, #hash); + let class_name = StringName::from(#class_name); + let method_name = StringName::from(#method_name_str); + let method_bind = sys::interface_fn!(classdb_get_method_bind)( + class_name.string_sys(), + method_name.string_sys(), + #hash + ); let call_fn = sys::interface_fn!(object_method_bind_ptrcall); let args = [ @@ -441,8 +452,8 @@ pub(crate) fn make_function_definition( let is_vararg = function.is_vararg; let (params, arg_exprs) = make_params(&function.arguments, is_vararg, ctx); - let function_name = safe_ident(&function.name); - let c_function_name = c_str(&function.name); + let function_name_str = &function.name; + let function_name = safe_ident(function_name_str); let hash = function.hash; let (return_decl, call) = make_utility_return(&function.return_type, ctx); @@ -450,7 +461,8 @@ pub(crate) fn make_function_definition( quote! { pub fn #function_name( #( #params ),* ) #return_decl { let result = unsafe { - let call_fn = sys::interface_fn!(variant_get_ptr_utility_function)(#c_function_name, #hash); + let function_name = StringName::from(#function_name_str); + let call_fn = sys::interface_fn!(variant_get_ptr_utility_function)(function_name.string_sys(), #hash); let call_fn = call_fn.unwrap_unchecked(); let args = [ diff --git a/godot-codegen/src/util.rs b/godot-codegen/src/util.rs index 1b2fbba26..f7d124912 100644 --- a/godot-codegen/src/util.rs +++ b/godot-codegen/src/util.rs @@ -218,14 +218,6 @@ pub fn safe_ident(s: &str) -> Ident { } } -// Code duplicated between here and godot-macros -pub fn c_str(s: &str) -> TokenStream { - let s = Literal::string(&format!("{}\0", s)); - quote! { - #s.as_ptr() as *const i8 - } -} - pub fn strlit(s: &str) -> Literal { Literal::string(s) } diff --git a/godot-core/src/builtin/macros.rs b/godot-core/src/builtin/macros.rs index 31e4a2d26..e7d9791d8 100644 --- a/godot-core/src/builtin/macros.rs +++ b/godot-core/src/builtin/macros.rs @@ -6,12 +6,16 @@ #![macro_use] -macro_rules! impl_basic_trait_as_sys { - ( Drop for $Type:ty => $gd_method:ident ) => { - impl Drop for $Type { +macro_rules! impl_builtin_traits_inner { + ( Default for $Type:ty => $gd_method:ident ) => { + impl Default for $Type { #[inline] - fn drop(&mut self) { - unsafe { (get_api().$gd_method)(self.sys_mut()) } + fn default() -> Self { + unsafe { + let mut gd_val = sys::$GdType::default(); + (sys::method_table().$gd_method)(&mut gd_val); + <$Type>::from_sys(gd_val) + } } } }; @@ -21,22 +25,23 @@ macro_rules! impl_basic_trait_as_sys { #[inline] fn clone(&self) -> Self { unsafe { - let mut result = sys::$GdType::default(); - (get_api().$gd_method)(&mut result, self.sys()); - <$Type>::from_sys(result) + Self::from_sys_init(|self_ptr| { + let ctor = sys::method_table().$gd_method; + let args = [self.sys()]; + ctor(self_ptr, args.as_ptr()); + }) } } } }; - ( Default for $Type:ty => $gd_method:ident ) => { - impl Default for $Type { + ( Drop for $Type:ty => $gd_method:ident ) => { + impl Drop for $Type { #[inline] - fn default() -> Self { + fn drop(&mut self) { unsafe { - let mut gd_val = sys::$GdType::default(); - (get_api().$gd_method)(&mut gd_val); - <$Type>::from_sys(gd_val) + let destructor = sys::method_table().$gd_method; + destructor(self.sys_mut()); } } } @@ -58,7 +63,7 @@ macro_rules! impl_basic_trait_as_sys { }; ( Eq for $Type:ty => $gd_method:ident ) => { - impl_basic_trait_as_sys!(PartialEq for $Type => $gd_method); + impl_builtin_traits_inner!(PartialEq for $Type => $gd_method); impl Eq for $Type {} }; @@ -86,7 +91,7 @@ macro_rules! impl_basic_trait_as_sys { }; ( Ord for $Type:ty => $gd_method:ident ) => { - impl_basic_trait_as_sys!(PartialOrd for $Type => $gd_method); + impl_builtin_traits_inner!(PartialOrd for $Type => $gd_method); impl Ord for $Type { #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering { @@ -96,16 +101,16 @@ macro_rules! impl_basic_trait_as_sys { }; } -macro_rules! impl_traits_as_sys { +macro_rules! impl_builtin_traits { ( for $Type:ty { $( $Trait:ident => $gd_method:ident; )* } ) => ( $( - impl_basic_trait_as_sys!( + impl_builtin_traits_inner! { $Trait for $Type => $gd_method - ); + } )* ) } diff --git a/godot-core/src/builtin/meta/class_name.rs b/godot-core/src/builtin/meta/class_name.rs new file mode 100644 index 000000000..9d70cbd24 --- /dev/null +++ b/godot-core/src/builtin/meta/class_name.rs @@ -0,0 +1,43 @@ +/* + * 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 godot_ffi as sys; + +use std::fmt::{Display, Formatter, Result as FmtResult}; + +use crate::builtin::*; +use crate::obj::GodotClass; + +/// Utility to construct class names known at compile time. +/// Cannot be a function since the backing string must be retained. +#[derive(Eq, PartialEq, Hash, Clone, Debug)] +pub struct ClassName { + backing: StringName, +} + +impl ClassName { + pub fn new() -> Self { + Self { + backing: StringName::from(T::CLASS_NAME), + } + } + + pub fn from_static(string: &'static str) -> Self { + Self { + backing: StringName::from(string), + } + } + + pub fn string_sys(&self) -> sys::GDNativeStringNamePtr { + self.backing.string_sys() + } +} + +impl Display for ClassName { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + self.backing.fmt(f) + } +} diff --git a/godot-core/src/builtin/meta/mod.rs b/godot-core/src/builtin/meta/mod.rs new file mode 100644 index 000000000..c0a2cb61d --- /dev/null +++ b/godot-core/src/builtin/meta/mod.rs @@ -0,0 +1,81 @@ +/* + * 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 class_name; +mod signature; + +pub use class_name::*; +pub use signature::*; + +use crate::builtin::*; +use crate::engine::global; + +use godot_ffi as sys; + +/// 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) +pub trait VariantMetadata { + fn variant_type() -> VariantType; + + fn property_info(property_name: &str) -> PropertyInfo { + PropertyInfo::new( + Self::variant_type(), + ClassName::new::<()>(), // FIXME Option or so + StringName::from(property_name), + ) + } + + fn param_metadata() -> sys::GDNativeExtensionClassMethodArgumentMetadata { + sys::GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_NONE + } +} + +// ---------------------------------------------------------------------------------------------------------------------------------------------- + +/// Rusty abstraction of sys::GDNativePropertyInfo +/// Keeps the actual allocated values (the sys equivalent only keeps pointers, which fall out of scope) +#[derive(Debug)] +pub struct PropertyInfo { + variant_type: VariantType, + class_name: ClassName, + property_name: StringName, + hint: global::PropertyHint, + hint_string: GodotString, + usage: global::PropertyUsageFlags, +} + +impl PropertyInfo { + pub fn new( + variant_type: VariantType, + class_name: ClassName, + property_name: StringName, + ) -> Self { + Self { + variant_type, + class_name, + property_name, + hint: global::PropertyHint::PROPERTY_HINT_NONE, + hint_string: GodotString::new(), + usage: global::PropertyUsageFlags::PROPERTY_USAGE_DEFAULT, + } + } + + /// Converts to the FFI type. Keep this object allocated while using that! + pub fn property_sys(&self) -> sys::GDNativePropertyInfo { + use crate::obj::EngineEnum as _; + + sys::GDNativePropertyInfo { + type_: self.variant_type.sys(), + name: self.property_name.string_sys(), + class_name: self.class_name.string_sys(), + hint: u32::try_from(self.hint.ord()).expect("hint.ord()"), + hint_string: self.hint_string.string_sys(), + usage: u32::try_from(self.usage.ord()).expect("usage.ord()"), + } + } +} diff --git a/godot-core/src/builtin/variant/variant_metadata.rs b/godot-core/src/builtin/meta/signature.rs similarity index 77% rename from godot-core/src/builtin/variant/variant_metadata.rs rename to godot-core/src/builtin/meta/signature.rs index 5f31ab613..2310f01f5 100644 --- a/godot-core/src/builtin/variant/variant_metadata.rs +++ b/godot-core/src/builtin/meta/signature.rs @@ -5,37 +5,16 @@ */ use godot_ffi as sys; +use godot_ffi::VariantType; use std::fmt::Debug; -pub trait VariantMetadata { - fn variant_type() -> sys::GDNativeVariantType; - - fn property_info(property_name: &str) -> sys::GDNativePropertyInfo { - let reg = unsafe { sys::get_registry() }; - sys::GDNativePropertyInfo { - type_: Self::variant_type(), - name: reg.c_string(property_name), - class_name: std::ptr::null_mut(), - hint: 0, - hint_string: std::ptr::null_mut(), - usage: 7, // Default, TODO generate global enums - } - } - - fn param_metadata() -> sys::GDNativeExtensionClassMethodArgumentMetadata { - sys::GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_NONE - } -} - -// ---------------------------------------------------------------------------------------------------------------------------------------------- - pub trait SignatureTuple { type Params; type Ret; - fn variant_type(index: i32) -> sys::GDNativeVariantType; + fn variant_type(index: i32) -> VariantType; + fn property_info(index: i32, param_name: &str) -> PropertyInfo; fn param_metadata(index: i32) -> sys::GDNativeExtensionClassMethodArgumentMetadata; - fn property_info(index: i32, param_name: &str) -> sys::GDNativePropertyInfo; fn varcall( instance_ptr: sys::GDExtensionClassInstancePtr, @@ -73,6 +52,7 @@ pub trait SignatureTuple { // } // } // +use crate::builtin::meta::*; use crate::builtin::{FromVariant, ToVariant, Variant}; use crate::obj::GodotClass; @@ -90,7 +70,7 @@ macro_rules! impl_signature_for_tuple { type Ret = $R; #[inline] - fn variant_type(index: i32) -> sys::GDNativeVariantType { + fn variant_type(index: i32) -> sys::VariantType { match index { -1 => $R::variant_type(), $( @@ -113,7 +93,7 @@ macro_rules! impl_signature_for_tuple { } #[inline] - fn property_info(index: i32, param_name: &str) -> sys::GDNativePropertyInfo { + fn property_info(index: i32, param_name: &str) -> PropertyInfo { match index { -1 => $R::property_info(param_name), $( @@ -207,34 +187,3 @@ impl_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6); impl_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7); impl_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8); impl_signature_for_tuple!(R, P0: 0, P1: 1, P2: 2, P3: 3, P4: 4, P5: 5, P6: 6, P7: 7, P8: 8, P9: 9); - -// Re-exported to crate::private -#[doc(hidden)] -pub mod func_callbacks { - use super::*; - - pub extern "C" fn get_type( - _method_data: *mut std::ffi::c_void, - n: i32, - ) -> sys::GDNativeVariantType { - S::variant_type(n) - } - - pub extern "C" fn get_info( - _method_data: *mut std::ffi::c_void, - n: i32, - ret: *mut sys::GDNativePropertyInfo, - ) { - // Return value is the first "argument" - let info = S::property_info(n, "TODO"); - unsafe { *ret = info }; - } - - pub extern "C" fn get_metadata( - _method_data: *mut std::ffi::c_void, - n: i32, - ) -> sys::GDNativeExtensionClassMethodArgumentMetadata { - // Return value is the first "argument" - S::param_metadata(n) - } -} diff --git a/godot-core/src/builtin/mod.rs b/godot-core/src/builtin/mod.rs index a0635c99a..9f8c83c4a 100644 --- a/godot-core/src/builtin/mod.rs +++ b/godot-core/src/builtin/mod.rs @@ -10,6 +10,7 @@ mod macros; mod arrays; mod color; +mod node_path; mod others; mod string; mod string_name; @@ -18,8 +19,11 @@ mod vector2; mod vector3; mod vector4; +pub mod meta; + pub use arrays::*; pub use color::*; +pub use node_path::*; pub use others::*; pub use string::*; pub use string_name::*; diff --git a/godot-core/src/builtin/node_path.rs b/godot-core/src/builtin/node_path.rs new file mode 100644 index 000000000..64cfaca31 --- /dev/null +++ b/godot-core/src/builtin/node_path.rs @@ -0,0 +1,68 @@ +/* + * 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::GodotString; +use godot_ffi as sys; +use godot_ffi::{ffi_methods, GodotFfi}; +use std::fmt::{Display, Formatter, Result as FmtResult}; + +pub struct NodePath { + opaque: sys::types::OpaqueNodePath, +} + +impl NodePath { + fn from_opaque(opaque: sys::types::OpaqueNodePath) -> Self { + Self { opaque } + } +} + +impl GodotFfi for NodePath { + ffi_methods! { type sys::GDNativeTypePtr = *mut Opaque; .. } +} + +impl From<&GodotString> for NodePath { + fn from(path: &GodotString) -> Self { + unsafe { + Self::from_sys_init(|self_ptr| { + let ctor = sys::method_table().node_path_from_string; + let args = [path.sys()]; + ctor(self_ptr, args.as_ptr()); + }) + } + } +} + +impl From<&NodePath> for GodotString { + fn from(path: &NodePath) -> Self { + unsafe { + Self::from_sys_init(|self_ptr| { + let ctor = sys::method_table().string_from_node_path; + let args = [path.sys()]; + ctor(self_ptr, args.as_ptr()); + }) + } + } +} + +impl From<&str> for NodePath { + fn from(path: &str) -> Self { + Self::from(&GodotString::from(path)) + } +} + +impl Display for NodePath { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + let string = GodotString::from(self); + ::fmt(&string, f) + } +} + +impl_builtin_traits! { + for NodePath { + Clone => node_path_construct_copy; + Drop => node_path_destroy; + } +} diff --git a/godot-core/src/builtin/others.rs b/godot-core/src/builtin/others.rs index d06932ece..e1cca4196 100644 --- a/godot-core/src/builtin/others.rs +++ b/godot-core/src/builtin/others.rs @@ -4,11 +4,9 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::fmt::{Display, Formatter, Result as FmtResult}; - // Stub for various other built-in classes, which are currently incomplete, but whose types // are required for codegen -use crate::builtin::{GodotString, StringName, Vector2}; +use crate::builtin::{StringName, Vector2}; use crate::obj::{Gd, GodotClass}; use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; @@ -24,7 +22,6 @@ impl_builtin_stub!(Basis, OpaqueBasis); impl_builtin_stub!(Transform2D, OpaqueTransform2D); impl_builtin_stub!(Transform3D, OpaqueTransform3D); impl_builtin_stub!(Projection, OpaqueProjection); -impl_builtin_stub!(NodePath, OpaqueNodePath); impl_builtin_stub!(RID, OpaqueRID); impl_builtin_stub!(Callable, OpaqueCallable); impl_builtin_stub!(Signal, OpaqueSignal); @@ -46,43 +43,6 @@ impl Rect2 { } } -impl From<&GodotString> for NodePath { - fn from(path: &GodotString) -> Self { - unsafe { - Self::from_sys_init(|self_ptr| { - let ctor = sys::method_table().node_path_from_string; - let args = [path.sys()]; - ctor(self_ptr, args.as_ptr()); - }) - } - } -} - -impl From<&NodePath> for GodotString { - fn from(path: &NodePath) -> Self { - unsafe { - Self::from_sys_init(|self_ptr| { - let ctor = sys::method_table().string_from_node_path; - let args = [path.sys()]; - ctor(self_ptr, args.as_ptr()); - }) - } - } -} - -impl From<&str> for NodePath { - fn from(path: &str) -> Self { - Self::from(&GodotString::from(path)) - } -} - -impl Display for NodePath { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - let string = GodotString::from(self); - ::fmt(&string, f) - } -} - impl Callable { pub fn from_object_method(object: Gd, method: S) -> Self where diff --git a/godot-core/src/builtin/string.rs b/godot-core/src/builtin/string.rs index c9cd7cb6d..cc9f4b43b 100644 --- a/godot-core/src/builtin/string.rs +++ b/godot-core/src/builtin/string.rs @@ -32,13 +32,6 @@ impl GodotString { fn string_sys = sys; fn write_string_sys = write_sys; } - - // #[doc(hidden)] - // pub fn leak_string_sys(self) -> sys::GDNativeStringPtr { - // let ptr = self.string_sys(); - // std::mem::forget(self); - // ptr - // } } impl GodotFfi for GodotString { @@ -76,15 +69,12 @@ impl Default for GodotString { } } -impl Clone for GodotString { - fn clone(&self) -> Self { - unsafe { - Self::from_sys_init(|self_ptr| { - let ctor = sys::method_table().string_construct_copy; - let args = [self.sys()]; - ctor(self_ptr, args.as_ptr()); - }) - } +impl_builtin_traits! { + for GodotString { + Clone => string_construct_copy; + Drop => string_destroy; + Eq => string_operator_equal; + Ord => string_operator_less; } } @@ -101,8 +91,15 @@ impl From for GodotString { } impl From<&str> for GodotString { - fn from(val: &str) -> Self { - GodotString::from_str(val).expect("From<&str>") + fn from(s: &str) -> Self { + let bytes = s.as_bytes(); + + unsafe { + Self::from_string_sys_init(|string_ptr| { + let ctor = interface_fn!(string_new_with_utf8_chars_and_len); + ctor(string_ptr, bytes.as_ptr() as *const i8, bytes.len() as i64); + }) + } } } @@ -131,16 +128,7 @@ impl FromStr for GodotString { type Err = Infallible; fn from_str(s: &str) -> Result { - let b = s.as_bytes(); - - let result = unsafe { - Self::from_string_sys_init(|string_ptr| { - let ctor = interface_fn!(string_new_with_utf8_chars_and_len); - ctor(string_ptr, b.as_ptr() as *const i8, b.len() as i64); - }) - }; - - Ok(result) + Ok(Self::from(s)) } } @@ -158,22 +146,6 @@ impl fmt::Debug for GodotString { } } -impl_traits_as_sys! { - for GodotString { - Eq => string_operator_equal; - Ord => string_operator_less; - } -} - -impl Drop for GodotString { - fn drop(&mut self) { - unsafe { - let destructor = sys::method_table().string_destroy; - destructor(self.sys_mut()); - } - } -} - // While this is a nice optimisation for ptrcalls, it's not easily possible // to pass in &GodotString when doing varcalls. /* diff --git a/godot-core/src/builtin/string_name.rs b/godot-core/src/builtin/string_name.rs index dbad9680e..b9107b7c5 100644 --- a/godot-core/src/builtin/string_name.rs +++ b/godot-core/src/builtin/string_name.rs @@ -9,6 +9,7 @@ use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::hash::{Hash, Hasher}; #[repr(C)] pub struct StringName { @@ -23,26 +24,12 @@ impl StringName { ffi_methods! { type sys::GDNativeStringNamePtr = *mut Opaque; + // Note: unlike from_sys, from_string_sys does not default-construct instance first. Typical usage in C++ is placement new. fn from_string_sys = from_sys; fn from_string_sys_init = from_sys_init; fn string_sys = sys; fn write_string_sys = write_sys; } - - #[doc(hidden)] - pub fn leak_string_sys(self) -> sys::GDNativeStringNamePtr { - let ptr = self.string_sys(); - std::mem::forget(self); - ptr - } -} - -impl Drop for StringName { - fn drop(&mut self) { - unsafe { - (sys::method_table().string_name_destroy)(self.sys()); - } - } } impl GodotFfi for StringName { @@ -63,6 +50,15 @@ impl GodotFfi for StringName { } } +impl_builtin_traits! { + for StringName { + Clone => string_name_construct_copy; + Drop => string_name_destroy; + Eq => string_name_operator_equal; + Ord => string_name_operator_less; + } +} + impl Default for StringName { fn default() -> Self { // Note: can't use from_sys_init(), as that calls the default constructor @@ -97,6 +93,15 @@ impl Debug for StringName { } } +impl Hash for StringName { + fn hash(&self, state: &mut H) { + // TODO use Godot hash via codegen + // C++: internal::gdn_interface->variant_get_ptr_builtin_method(GDNATIVE_VARIANT_TYPE_STRING_NAME, "hash", 171192809); + + self.to_string().hash(state) + } +} + impl From<&GodotString> for StringName { fn from(s: &GodotString) -> Self { unsafe { diff --git a/godot-core/src/builtin/variant/impls.rs b/godot-core/src/builtin/variant/impls.rs index 108bb9f11..43a737cc4 100644 --- a/godot-core/src/builtin/variant/impls.rs +++ b/godot-core/src/builtin/variant/impls.rs @@ -5,6 +5,7 @@ */ use super::*; +use crate::builtin::meta::VariantMetadata; use crate::builtin::*; use godot_ffi as sys; use sys::GodotFfi; @@ -59,8 +60,8 @@ macro_rules! impl_variant_traits { } impl VariantMetadata for $T { - fn variant_type() -> sys::GDNativeVariantType { - sys::$variant_type + fn variant_type() -> VariantType { + VariantType::$variant_type } $($extra)* @@ -84,8 +85,8 @@ macro_rules! impl_variant_traits_int { } impl VariantMetadata for $T { - fn variant_type() -> sys::GDNativeVariantType { - sys::GDNATIVE_VARIANT_TYPE_INT + fn variant_type() -> VariantType { + VariantType::Int } fn param_metadata() -> sys::GDNativeExtensionClassMethodArgumentMetadata { @@ -111,8 +112,8 @@ macro_rules! impl_variant_traits_float { } impl VariantMetadata for $T { - fn variant_type() -> sys::GDNativeVariantType { - sys::GDNATIVE_VARIANT_TYPE_FLOAT + fn variant_type() -> VariantType { + VariantType::Float } fn param_metadata() -> sys::GDNativeExtensionClassMethodArgumentMetadata { @@ -129,19 +130,18 @@ macro_rules! impl_variant_traits_float { mod impls { use super::*; - impl_variant_traits!(bool, bool_to_variant, bool_from_variant, GDNATIVE_VARIANT_TYPE_BOOL); - impl_variant_traits!(Vector2, vector2_to_variant, vector2_from_variant, GDNATIVE_VARIANT_TYPE_VECTOR2); - impl_variant_traits!(Vector3, vector3_to_variant, vector3_from_variant, GDNATIVE_VARIANT_TYPE_VECTOR3); - impl_variant_traits!(Vector4, vector4_to_variant, vector4_from_variant, GDNATIVE_VARIANT_TYPE_VECTOR4); - impl_variant_traits!(Vector2i, vector2i_to_variant, vector2i_from_variant, GDNATIVE_VARIANT_TYPE_VECTOR2I); - impl_variant_traits!(Vector3i, vector3i_to_variant, vector3i_from_variant, GDNATIVE_VARIANT_TYPE_VECTOR3I); - impl_variant_traits!(Color, color_to_variant, color_from_variant, GDNATIVE_VARIANT_TYPE_COLOR); - impl_variant_traits!(GodotString, string_to_variant, string_from_variant, GDNATIVE_VARIANT_TYPE_STRING); - impl_variant_traits!(StringName, string_name_to_variant, string_name_from_variant, GDNATIVE_VARIANT_TYPE_STRING_NAME); + impl_variant_traits!(bool, bool_to_variant, bool_from_variant, Bool); + 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!(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!(i64, int_to_variant, int_from_variant, GDNATIVE_VARIANT_TYPE_INT, - GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64); + impl_variant_traits!(i64, int_to_variant, int_from_variant, Int, GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64); impl_variant_traits_int!(i8, GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8); impl_variant_traits_int!(i16, GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16); impl_variant_traits_int!(i32, GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32); @@ -149,10 +149,9 @@ mod impls { impl_variant_traits_int!(u8, GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8); impl_variant_traits_int!(u16, GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16); impl_variant_traits_int!(u32, GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32); - // u64 is not supported, because it cannot be represented on GDScript side, and converting to i64 is error-prone. + // 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, GDNATIVE_VARIANT_TYPE_FLOAT, - GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE); + impl_variant_traits!(f64, float_to_variant, float_from_variant, Float, GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE); impl_variant_traits_float!(f32, GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT); } @@ -167,8 +166,8 @@ impl ToVariant for () { } impl VariantMetadata for () { - fn variant_type() -> sys::GDNativeVariantType { - sys::GDNATIVE_VARIANT_TYPE_NIL + fn variant_type() -> VariantType { + VariantType::Nil } } @@ -186,7 +185,7 @@ impl FromVariant for Variant { // Variant itself impl VariantMetadata for Variant { - fn variant_type() -> sys::GDNativeVariantType { - sys::GDNATIVE_VARIANT_TYPE_NIL + fn variant_type() -> VariantType { + VariantType::Nil // FIXME is this correct? what else to use? is this called at all? } } diff --git a/godot-core/src/builtin/variant/mod.rs b/godot-core/src/builtin/variant/mod.rs index 5897d4204..a70199c30 100644 --- a/godot-core/src/builtin/variant/mod.rs +++ b/godot-core/src/builtin/variant/mod.rs @@ -12,11 +12,9 @@ use sys::types::OpaqueVariant; use sys::{ffi_methods, interface_fn}; mod impls; -mod variant_metadata; mod variant_traits; pub use impls::*; -pub use variant_metadata::*; pub use variant_traits::*; pub use sys::{VariantOperator, VariantType}; @@ -71,13 +69,13 @@ impl Variant { // TODO test pub fn get_type(&self) -> VariantType { let ty_sys = unsafe { interface_fn!(variant_get_type)(self.var_sys()) }; - VariantType::from_ord(ty_sys) + VariantType::from_sys(ty_sys) } // TODO test #[allow(unused_mut)] pub fn evaluate(&self, rhs: &Variant, op: VariantOperator) -> Option { - let op_sys = op.to_ord(); + let op_sys = op.sys(); let mut is_valid = false as u8; let mut result = Variant::nil(); diff --git a/godot-core/src/engine.rs b/godot-core/src/engine.rs index d9974a0bd..9ecf5cda5 100644 --- a/godot-core/src/engine.rs +++ b/godot-core/src/engine.rs @@ -58,10 +58,11 @@ pub trait NodeExt { T: GodotClass + Inherits, { let path = path.into(); + let copy = path.clone(); // TODO avoid copy self.try_get_node_as(path).unwrap_or_else(|| { panic!( - "There is no node of type {ty} path `{path}`", + "There is no node of type {ty} path `{copy}`", ty = T::CLASS_NAME ) }) diff --git a/godot-core/src/lib.rs b/godot-core/src/lib.rs index 63439f5f4..1c5b35877 100644 --- a/godot-core/src/lib.rs +++ b/godot-core/src/lib.rs @@ -26,7 +26,6 @@ pub mod private { #[allow(non_camel_case_types)] pub trait You_forgot_the_attribute__godot_api {} - pub use crate::builtin::func_callbacks; pub use crate::engine::gen::classes::class_macros; pub use crate::registry::{callbacks, ClassPlugin, ErasedRegisterFn, PluginComponent}; pub use crate::storage::as_storage; diff --git a/godot-core/src/log.rs b/godot-core/src/log.rs index 41741dc74..56587da93 100644 --- a/godot-core/src/log.rs +++ b/godot-core/src/log.rs @@ -69,13 +69,14 @@ macro_rules! godot_print { pub use crate::{godot_error, godot_print, godot_script_error, godot_warn}; -use crate::builtin::Variant; +use crate::builtin::{StringName, Variant}; use crate::sys::{self, GodotFfi}; pub fn print(varargs: &[Variant]) { unsafe { + let method_name = StringName::from("print"); let call_fn = sys::interface_fn!(variant_get_ptr_utility_function)( - "print\0".as_ptr() as *const i8, + method_name.string_sys(), 2648703342i64, ); let call_fn = call_fn.unwrap_unchecked(); diff --git a/godot-core/src/macros.rs b/godot-core/src/macros.rs index 8574edeb8..2a60d0ffa 100644 --- a/godot-core/src/macros.rs +++ b/godot-core/src/macros.rs @@ -50,97 +50,136 @@ macro_rules! gdext_register_method_inner { ) => { unsafe { use $crate::sys; - use $crate::builtin::{Variant, SignatureTuple}; + use $crate::builtin::{Variant, StringName}; + use $crate::builtin::meta::*; const NUM_ARGS: usize = $crate::gdext_count_idents!($( $param, )*); - let method_info = sys::GDNativeExtensionClassMethodInfo { - name: concat!(stringify!($method_name), "\0").as_ptr() as *const i8, - method_userdata: std::ptr::null_mut(), - call_func: Some({ - unsafe extern "C" fn function( - _method_data: *mut std::ffi::c_void, - instance_ptr: sys::GDExtensionClassInstancePtr, - args: *const sys::GDNativeVariantPtr, - _arg_count: sys::GDNativeInt, - ret: sys::GDNativeVariantPtr, - err: *mut sys::GDNativeCallError, - ) { - let result = ::std::panic::catch_unwind(|| { - < ($($RetTy)+, $($ParamTy,)*) as SignatureTuple >::varcall::< $Class >( - instance_ptr, - args, - ret, - err, - |inst, params| { - let ( $($param,)* ) = params; - inst.$method_name( $( $param, )* ) - }, - stringify!($method_name), - ) - }); - - if let Err(e) = result { - $crate::log::godot_error!("Rust function panicked: {}", stringify!($method_name)); - $crate::private::print_panic(e); - - // Signal error and set return type to Nil - (*err).error = sys::GDNATIVE_CALL_ERROR_INVALID_METHOD; // no better fitting enum? - sys::interface_fn!(variant_new_nil)(ret); - } + type Sig = ( $($RetTy)+, $($ParamTy),* ); + + let varcall_func = { + unsafe extern "C" fn function( + _method_data: *mut std::ffi::c_void, + instance_ptr: sys::GDExtensionClassInstancePtr, + args: *const sys::GDNativeVariantPtr, + _arg_count: sys::GDNativeInt, + ret: sys::GDNativeVariantPtr, + err: *mut sys::GDNativeCallError, + ) { + let result = ::std::panic::catch_unwind(|| { + ::varcall::< $Class >( + instance_ptr, + args, + ret, + err, + |inst, params| { + let ( $($param,)* ) = params; + inst.$method_name( $( $param, )* ) + }, + stringify!($method_name), + ) + }); + + if let Err(e) = result { + $crate::log::godot_error!("Rust function panicked: {}", stringify!($method_name)); + $crate::private::print_panic(e); + + // Signal error and set return type to Nil + (*err).error = sys::GDNATIVE_CALL_ERROR_INVALID_METHOD; // no better fitting enum? + sys::interface_fn!(variant_new_nil)(ret); } + } - function - }), - ptrcall_func: Some({ - unsafe extern "C" fn function( - _method_data: *mut std::ffi::c_void, - instance_ptr: sys::GDExtensionClassInstancePtr, - args: *const sys::GDNativeTypePtr, - ret: sys::GDNativeTypePtr, - ) { - let result = ::std::panic::catch_unwind(|| { - < ($($RetTy)+, $($ParamTy,)*) as SignatureTuple >::ptrcall::< $Class >( - instance_ptr, - args, - ret, - |inst, params| { - let ( $($param,)* ) = params; - inst.$method_name( $( $param, )* ) - }, - stringify!($method_name), - ); - }); - - if let Err(e) = result { - $crate::log::godot_error!("Rust function panicked: {}", stringify!($method_name)); - $crate::private::print_panic(e); - - // TODO set return value to T::default()? - } + function + }; + + let ptrcall_func = { + unsafe extern "C" fn function( + _method_data: *mut std::ffi::c_void, + instance_ptr: sys::GDExtensionClassInstancePtr, + args: *const sys::GDNativeTypePtr, + ret: sys::GDNativeTypePtr, + ) { + let result = ::std::panic::catch_unwind(|| { + ::ptrcall::< $Class >( + instance_ptr, + args, + ret, + |inst, params| { + let ( $($param,)* ) = params; + inst.$method_name( $( $param, )* ) + }, + stringify!($method_name), + ); + }); + + if let Err(e) = result { + $crate::log::godot_error!("Rust function panicked: {}", stringify!($method_name)); + $crate::private::print_panic(e); + + // TODO set return value to T::default()? } + } + + function + }; + + // Return value meta-information + let has_return_value: bool = $crate::gdext_is_not_unit!($($RetTy)+); + let return_value_info = Sig::property_info(-1, ""); + let mut return_value_info_sys = return_value_info.property_sys(); + let return_value_metadata = Sig::param_metadata(-1); + + // Arguments meta-information + let argument_count = NUM_ARGS as u32; + let mut arguments_info: [PropertyInfo; NUM_ARGS] = { + let mut i = -1i32; + [$( + { + i += 1; + let prop = Sig::property_info(i, stringify!($param)); + //OnceArg::new(prop) + prop + }, + )*] + }; + let mut arguments_info_sys: [sys::GDNativePropertyInfo; NUM_ARGS] + = std::array::from_fn(|i| arguments_info[i].property_sys()); +// = std::array::from_fn(|i| arguments_info[i].once_sys()); + let mut arguments_metadata: [sys::GDNativeExtensionClassMethodArgumentMetadata; NUM_ARGS] + = std::array::from_fn(|i| Sig::param_metadata(i as i32)); + + let class_name = StringName::from(stringify!($Class)); + let method_name = StringName::from(stringify!($method_name)); - function - }), - method_flags: - sys::GDNATIVE_EXTENSION_METHOD_FLAGS_DEFAULT as u32, - argument_count: NUM_ARGS as u32, - has_return_value: $crate::gdext_is_not_unit!($($RetTy)+) as u8, - get_argument_type_func: Some($crate::private::func_callbacks::get_type::<( $($RetTy)+, $($ParamTy),* )>), - get_argument_info_func: Some($crate::private::func_callbacks::get_info::<( $($RetTy)+, $($ParamTy),* )>), - get_argument_metadata_func: Some($crate::private::func_callbacks::get_metadata::<( $($RetTy)+, $($ParamTy),* )>), + // println!("REG {class_name}::{method_name}"); + // println!(" ret {return_value_info:?}"); + + let method_info = sys::GDNativeExtensionClassMethodInfo { + name: method_name.string_sys(), + method_userdata: std::ptr::null_mut(), + call_func: Some(varcall_func), + ptrcall_func: Some(ptrcall_func), + method_flags: sys::GDNATIVE_EXTENSION_METHOD_FLAGS_DEFAULT as u32, + has_return_value: has_return_value as u8, + return_value_info: std::ptr::addr_of_mut!(return_value_info_sys), + return_value_metadata, + argument_count, + arguments_info: arguments_info_sys.as_mut_ptr(), + arguments_metadata: arguments_metadata.as_mut_ptr(), default_argument_count: 0, default_arguments: std::ptr::null_mut(), }; - let name = std::ffi::CStr::from_bytes_with_nul_unchecked(concat!(stringify!($Class), "\0").as_bytes()); - $crate::out!(" Register fn: {}::{}", stringify!($Class), stringify!($method_name)); sys::interface_fn!(classdb_register_extension_class_method)( sys::get_library(), - name.as_ptr(), + class_name.string_sys(), std::ptr::addr_of!(method_info), ); + + // std::mem::forget(class_name); + // std::mem::forget(method_name); } }; } diff --git a/godot-core/src/obj/gd.rs b/godot-core/src/obj/gd.rs index b44603528..fa7b7293e 100644 --- a/godot-core/src/obj/gd.rs +++ b/godot-core/src/obj/gd.rs @@ -10,17 +10,19 @@ use std::ops::{Deref, DerefMut}; use std::ptr; use godot_ffi as sys; +use godot_ffi::VariantType; use sys::types::OpaqueObject; use sys::{ffi_methods, interface_fn, static_assert_eq_size, GodotFfi}; -use crate::builtin::{FromVariant, ToVariant, Variant, VariantConversionError}; -use crate::builtin::{GodotString, VariantMetadata}; +use crate::builtin::meta::{ClassName, PropertyInfo, VariantMetadata}; +use crate::builtin::GodotString; +use crate::builtin::{FromVariant, StringName, ToVariant, Variant, VariantConversionError}; use crate::obj::dom::Domain as _; use crate::obj::mem::Memory as _; use crate::obj::{cap, dom, mem, GodotClass, Inherits, Share}; use crate::obj::{GdMut, GdRef, InstanceId}; use crate::storage::InstanceStorage; -use crate::{callbacks, engine, out, ClassName}; +use crate::{callbacks, engine, out}; /// Smart pointer to objects owned by the Godot engine. /// @@ -330,7 +332,7 @@ impl Gd { U: GodotClass, { let class_name = ClassName::new::(); - let class_tag = interface_fn!(classdb_get_class_tag)(class_name.c_str()); + 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); if cast_object_ptr.is_null() { @@ -593,25 +595,15 @@ impl Debug for Gd { } impl VariantMetadata for Gd { - fn variant_type() -> sys::GDNativeVariantType { - sys::GDNATIVE_VARIANT_TYPE_OBJECT + fn variant_type() -> VariantType { + VariantType::Object } - fn property_info(name: &str) -> sys::GDNativePropertyInfo { - // Note: filling this information properly is important so that Godot can use ptrcalls instead of varcalls - // (requires typed GDScript + sufficient information from the extension side) - let reg = unsafe { sys::get_registry() }; - - let property_name = reg.c_string(name); - let class_name = reg.c_string(T::CLASS_NAME); - - sys::GDNativePropertyInfo { - type_: Self::variant_type(), - name: property_name, - class_name, - hint: 0, - hint_string: ptr::null_mut(), - usage: 7, // Default, TODO generate global enums - } + fn property_info(property_name: &str) -> PropertyInfo { + PropertyInfo::new( + Self::variant_type(), + ClassName::new::(), + StringName::from(property_name), + ) } } diff --git a/godot-core/src/obj/instance_id.rs b/godot-core/src/obj/instance_id.rs index 72a91bc87..fbc565f5f 100644 --- a/godot-core/src/obj/instance_id.rs +++ b/godot-core/src/obj/instance_id.rs @@ -4,9 +4,10 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::builtin::{FromVariant, ToVariant, Variant, VariantConversionError, VariantMetadata}; +use crate::builtin::meta::VariantMetadata; +use crate::builtin::{FromVariant, ToVariant, Variant, VariantConversionError}; use godot_ffi as sys; -use godot_ffi::{ffi_methods, GodotFfi}; +use godot_ffi::{ffi_methods, GodotFfi, VariantType}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::num::NonZeroU64; @@ -118,8 +119,8 @@ impl ToVariant for Option { */ impl VariantMetadata for InstanceId { - fn variant_type() -> sys::GDNativeVariantType { - sys::GDNATIVE_VARIANT_TYPE_INT + fn variant_type() -> VariantType { + VariantType::Int } fn param_metadata() -> sys::GDNativeExtensionClassMethodArgumentMetadata { diff --git a/godot-core/src/registry.rs b/godot-core/src/registry.rs index 7a8578a5c..c07fa0d68 100644 --- a/godot-core/src/registry.rs +++ b/godot-core/src/registry.rs @@ -14,10 +14,12 @@ use godot_ffi as sys; use sys::interface_fn; use crate::bind::GodotExt; +use crate::builtin::meta::ClassName; +use crate::builtin::StringName; use crate::out; use std::any::Any; use std::collections::HashMap; -use std::fmt::{Display, Formatter}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; use std::ptr; #[derive(Debug)] @@ -35,8 +37,8 @@ pub struct ErasedRegisterFn { pub raw: fn(&mut dyn Any), } -impl std::fmt::Debug for ErasedRegisterFn { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +impl Debug for ErasedRegisterFn { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { write!(f, "0x{:0>16x}", self.raw as u64) } } @@ -83,15 +85,16 @@ pub enum PluginComponent { /// User-defined `to_string` function user_to_string_fn: Option< unsafe extern "C" fn( - instance: sys::GDExtensionClassInstancePtr, - out_string: sys::GDNativeStringPtr, + p_instance: sys::GDExtensionClassInstancePtr, + r_is_valid: *mut sys::GDNativeBool, + p_out: sys::GDNativeStringPtr, ), >, /// Callback for other virtuals get_virtual_fn: unsafe extern "C" fn( - _class_user_data: *mut std::ffi::c_void, - p_name: *const std::os::raw::c_char, + p_userdata: *mut std::os::raw::c_void, + p_name: sys::GDNativeStringNamePtr, ) -> sys::GDNativeExtensionClassCallVirtual, }, } @@ -217,17 +220,24 @@ fn fill_into(dst: &mut Option, src: Option) { fn register_class_raw(info: ClassRegistrationInfo) { // First register class... + + let class_name = info.class_name; + let parent_class_name = info + .parent_class_name + .expect("class defined (parent_class_name)"); + unsafe { interface_fn!(classdb_register_extension_class)( sys::get_library(), - info.class_name.c_str(), - info.parent_class_name - .expect("class defined (parent_class_name)") - .c_str(), + class_name.string_sys(), + parent_class_name.string_sys(), ptr::addr_of!(info.godot_params), ); } + // std::mem::forget(class_name); + // std::mem::forget(parent_class_name); + // ...then custom symbols //let mut class_builder = crate::builder::ClassBuilder::::new(); @@ -243,37 +253,6 @@ fn register_class_raw(info: ClassRegistrationInfo) { } } -/// Utility to convert `String` to C `const char*`. -/// Cannot be a function since the backing string must be retained. -#[derive(Eq, PartialEq, Hash, Clone, Debug)] -pub(crate) struct ClassName { - backing: String, -} - -impl ClassName { - pub fn new() -> Self { - Self { - backing: format!("{}\0", T::CLASS_NAME), - } - } - - fn from_static(string: &'static str) -> Self { - Self { - backing: format!("{}\0", string), - } - } - - pub fn c_str(&self) -> *const std::os::raw::c_char { - self.backing.as_ptr() as *const _ - } -} - -impl Display for ClassName { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", &self.backing[..self.backing.len() - 1]) - } -} - // Re-exported to crate::private pub mod callbacks { use super::*; @@ -297,7 +276,8 @@ pub mod callbacks { //out!("create callback: {}", class_name.backing); - let base_ptr = unsafe { interface_fn!(classdb_construct_object)(base_class_name.c_str()) }; + let base_ptr = + unsafe { interface_fn!(classdb_construct_object)(base_class_name.string_sys()) }; let base = unsafe { Base::from_sys(base_ptr) }; let user_instance = make_user_instance(base); @@ -307,7 +287,7 @@ pub mod callbacks { let binding_data_callbacks = crate::storage::nop_instance_callbacks(); unsafe { - interface_fn!(object_set_instance)(base_ptr, class_name.c_str(), instance_ptr); + interface_fn!(object_set_instance)(base_ptr, class_name.string_sys(), instance_ptr); interface_fn!(object_set_instance_binding)( base_ptr, sys::get_library(), @@ -316,6 +296,8 @@ pub mod callbacks { ); } + // std::mem::forget(class_name); + // std::mem::forget(base_class_name); base_ptr } @@ -330,16 +312,22 @@ pub mod callbacks { pub unsafe extern "C" fn get_virtual( _class_user_data: *mut std::ffi::c_void, - p_name: *const std::os::raw::c_char, + name: sys::GDNativeStringNamePtr, ) -> sys::GDNativeExtensionClassCallVirtual { - let name = std::ffi::CStr::from_ptr(p_name); - T::__virtual_call(name.to_str().expect("T::virtual_call")) + let method_name = StringName::from_string_sys(name); + let method_name = method_name.to_string(); + + T::__virtual_call(method_name.as_str()) } pub unsafe extern "C" fn to_string( instance: sys::GDExtensionClassInstancePtr, + _is_valid: *mut sys::GDNativeBool, out_string: sys::GDNativeStringPtr, ) { + // Note: to_string currently always succeeds, as it is only provided for classes that have a working implementation. + // is_valid output parameter thus not needed. + let storage = as_storage::(instance); let instance = storage.get(); let string = ::to_string(&*instance); diff --git a/godot-macros/src/godot_api.rs b/godot-macros/src/godot_api.rs index 11aeea550..d55d64e33 100644 --- a/godot-macros/src/godot_api.rs +++ b/godot-macros/src/godot_api.rs @@ -65,15 +65,12 @@ impl BoundAttr { fn transform_inherent_impl(mut decl: Impl) -> Result { let class_name = util::validate_impl(&decl, None, "godot_api")?; let class_name_str = class_name.to_string(); - let class_name_cstr = util::c_str(&class_name_str); //let register_fn = format_ident!("__godot_rust_register_{}", class_name_str); //#[allow(non_snake_case)] let (funcs, signals) = process_godot_fns(&mut decl)?; - let signal_cstrs = signals - .into_iter() - .map(|ident| util::c_str(ident.to_string().as_str())); + let signal_name_strs = signals.into_iter().map(|ident| ident.to_string()); let prv = quote! { ::godot::private }; @@ -88,12 +85,14 @@ fn transform_inherent_impl(mut decl: Impl) -> Result { )* unsafe { + let class_name = ::godot::builtin::StringName::from(#class_name_str); use ::godot::sys; #( + let signal_name = ::godot::builtin::StringName::new(#signal_name_strs); sys::interface_fn!(classdb_register_extension_class_signal)( sys::get_library(), - #class_name_cstr, - #signal_cstrs, + class_name.string_sys(), + signal_name.string_sys(), std::ptr::null(), // NULL only valid for zero parameters, in current impl; maybe better empty slice 0, ); diff --git a/godot-macros/src/util.rs b/godot-macros/src/util.rs index 7d6a48452..29a5e0da9 100644 --- a/godot-macros/src/util.rs +++ b/godot-macros/src/util.rs @@ -7,9 +7,9 @@ // Note: some code duplication with codegen crate use crate::ParseResult; -use proc_macro2::{Ident, Literal, Span, TokenStream, TokenTree}; +use proc_macro2::{Ident, Literal, Span, TokenTree}; +use quote::format_ident; use quote::spanned::Spanned; -use quote::{format_ident, quote}; use std::collections::HashMap; use venial::{Error, Function, Impl}; @@ -39,14 +39,6 @@ pub fn reduce_to_signature(function: &Function) -> Function { reduced } -// Code duplicated between here and godot-codegen -pub fn c_str(s: &str) -> TokenStream { - let s = Literal::string(&format!("{}\0", s)); - quote! { - #s.as_ptr() as *const i8 - } -} - // ---------------------------------------------------------------------------------------------------------------------------------------------- // Key-value parsing of proc attributes diff --git a/godot/Cargo.toml b/godot/Cargo.toml index 19e8c509c..b4222026b 100644 --- a/godot/Cargo.toml +++ b/godot/Cargo.toml @@ -6,7 +6,7 @@ rust-version = "1.63" [features] default = ["convenience"] -trace = [] +trace = ["godot-core/trace"] convenience = [] codegen-fmt = ["godot-core/codegen-fmt"] minimal = ["godot-core/minimal"] diff --git a/itest/rust/src/string_test.rs b/itest/rust/src/string_test.rs index 523085873..ed3889992 100644 --- a/itest/rust/src/string_test.rs +++ b/itest/rust/src/string_test.rs @@ -17,6 +17,9 @@ pub fn run() -> bool { ok &= string_clone(); ok &= string_name_conversion(); ok &= string_name_default_construct(); + ok &= string_name_eq_hash(); + ok &= string_name_ord(); + ok &= string_name_clone(); ok } @@ -76,3 +79,18 @@ fn string_name_default_construct() { assert_eq!(back, GodotString::new()); } + +#[itest] +fn string_name_eq_hash() { + // TODO +} + +#[itest] +fn string_name_ord() { + // TODO +} + +#[itest] +fn string_name_clone() { + // TODO +}