Add GDType and Object::_gdtype_ptr for first-class Object typing.

The type is currently bare-bones, but will be expanded in future PRs.
This commit is contained in:
Lukas Tenbrink 2025-05-02 01:57:34 +02:00
parent d705613db3
commit ac85d24e64
4 changed files with 151 additions and 47 deletions

42
core/object/gdtype.cpp Normal file
View file

@ -0,0 +1,42 @@
/**************************************************************************/
/* gdtype.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "gdtype.h"
GDType::GDType(const GDType *p_super_type, StringName p_name) :
super_type(p_super_type), name(std::move(p_name)) {
name_hierarchy.push_back(StringName(name, true));
if (super_type) {
for (const StringName &ancestor_name : super_type->name_hierarchy) {
name_hierarchy.push_back(StringName(ancestor_name, true));
}
}
}

50
core/object/gdtype.h Normal file
View file

@ -0,0 +1,50 @@
/**************************************************************************/
/* gdtype.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/string/string_name.h"
#include "core/templates/vector.h"
class GDType {
const GDType *super_type;
StringName name;
/// Contains all the class names in order:
/// `name` is the first element and `Object` is the last.
Vector<StringName> name_hierarchy;
public:
GDType(const GDType *p_super_type, StringName p_name);
const GDType *get_super_type() const { return super_type; }
const StringName &get_name() const { return name; }
const Vector<StringName> &get_name_hierarchy() const { return name_hierarchy; }
};

View file

@ -258,7 +258,7 @@ bool Object::_predelete() {
return false; return false;
} }
_class_name_ptr = nullptr; // Must restore, so constructors/destructors have proper class name access at each stage. _gdtype_ptr = nullptr; // Must restore, so constructors/destructors have proper class name access at each stage.
notification(NOTIFICATION_PREDELETE_CLEANUP, true); notification(NOTIFICATION_PREDELETE_CLEANUP, true);
// Destruction order starts with the most derived class, and progresses towards the base Object class: // Destruction order starts with the most derived class, and progresses towards the base Object class:
@ -301,7 +301,7 @@ void Object::cancel_free() {
void Object::_initialize() { void Object::_initialize() {
// Cache the class name in the object for quick reference. // Cache the class name in the object for quick reference.
_class_name_ptr = _get_class_namev(); _gdtype_ptr = &_get_typev();
_initialize_classv(); _initialize_classv();
} }
@ -2085,18 +2085,34 @@ uint32_t Object::get_edited_version() const {
} }
#endif #endif
const GDType &Object::get_gdtype() const {
if (unlikely(!_gdtype_ptr)) {
// While class is initializing / deinitializing, constructors and destructors
// need access to the proper type at the proper stage.
return _get_typev();
}
return *_gdtype_ptr;
}
bool Object::is_class(const String &p_class) const {
if (_extension && _extension->is_class(p_class)) {
return true;
}
for (const StringName &name : get_gdtype().get_name_hierarchy()) {
if (name == p_class) {
return true;
}
}
return false;
}
const StringName &Object::get_class_name() const { const StringName &Object::get_class_name() const {
if (_extension) { if (_extension) {
// Can't put inside the unlikely as constructor can run it. // Can't put inside the unlikely as constructor can run it.
return _extension->class_name; return _extension->class_name;
} }
if (unlikely(!_class_name_ptr)) { return get_gdtype().get_name();
// While class is initializing / deinitializing, constructors and destructors
// need access to the proper class at the proper stage.
return *_get_class_namev();
}
return *_class_name_ptr;
} }
StringName Object::get_class_name_for_extension(const GDExtension *p_library) const { StringName Object::get_class_name_for_extension(const GDExtension *p_library) const {
@ -2105,8 +2121,7 @@ StringName Object::get_class_name_for_extension(const GDExtension *p_library) co
// have to return the closest native parent's class name, so that it doesn't try to // have to return the closest native parent's class name, so that it doesn't try to
// use this like the real object. // use this like the real object.
if (unlikely(_extension && _extension->library == p_library && _extension->is_placeholder)) { if (unlikely(_extension && _extension->library == p_library && _extension->is_placeholder)) {
const StringName *class_name = _get_class_namev(); return get_class_name();
return *class_name;
} }
#endif #endif
@ -2116,13 +2131,13 @@ StringName Object::get_class_name_for_extension(const GDExtension *p_library) co
} }
// Extensions only have wrapper classes for classes exposed in ClassDB. // Extensions only have wrapper classes for classes exposed in ClassDB.
const StringName *class_name = _get_class_namev(); const StringName &class_name = get_class_name();
if (ClassDB::is_class_exposed(*class_name)) { if (ClassDB::is_class_exposed(class_name)) {
return *class_name; return class_name;
} }
// Find the nearest parent class that's exposed. // Find the nearest parent class that's exposed.
StringName parent_class = ClassDB::get_parent_class(*class_name); StringName parent_class = ClassDB::get_parent_class(class_name);
while (parent_class != StringName()) { while (parent_class != StringName()) {
if (ClassDB::is_class_exposed(parent_class)) { if (ClassDB::is_class_exposed(parent_class)) {
return parent_class; return parent_class;
@ -2296,14 +2311,16 @@ void Object::detach_from_objectdb() {
} }
} }
void Object::assign_class_name_static(const Span<char> &p_name, StringName &r_target) { void Object::assign_type_static(GDType **type_ptr, const char *p_name, const GDType *super_type) {
static BinaryMutex _mutex; static BinaryMutex _mutex;
MutexLock lock(_mutex); MutexLock lock(_mutex);
if (r_target) { GDType *type = *type_ptr;
// Already assigned while we were waiting for the mutex. if (type) {
// Assigned while we were waiting.
return; return;
} }
r_target = StringName(p_name.ptr(), true); type = memnew(GDType(super_type, StringName(p_name, true)));
*type_ptr = type;
} }
Object::~Object() { Object::~Object() {

View file

@ -31,6 +31,7 @@
#pragma once #pragma once
#include "core/extension/gdextension_interface.h" #include "core/extension/gdextension_interface.h"
#include "core/object/gdtype.h"
#include "core/object/message_queue.h" #include "core/object/message_queue.h"
#include "core/object/object_id.h" #include "core/object/object_id.h"
#include "core/os/rw_lock.h" #include "core/os/rw_lock.h"
@ -494,21 +495,18 @@ private:
friend class ::ClassDB; \ friend class ::ClassDB; \
\ \
public: \ public: \
virtual const StringName *_get_class_namev() const override { \ virtual const GDType &_get_typev() const override { \
return &get_class_static(); \ return get_gdtype_static(); \
} \
static const GDType &get_gdtype_static() { \
static GDType *_class_static; \
if (unlikely(!_class_static)) { \
assign_type_static(&_class_static, #m_class, &super_type::get_gdtype_static()); \
} \
return *_class_static; \
} \ } \
static const StringName &get_class_static() { \ static const StringName &get_class_static() { \
static StringName _class_name_static; \ return get_gdtype_static().get_name(); \
if (unlikely(!_class_name_static)) { \
assign_class_name_static(#m_class, _class_name_static); \
} \
return _class_name_static; \
} \
virtual bool is_class(const String &p_class) const override { \
if (_get_extension() && _get_extension()->is_class(p_class)) { \
return true; \
} \
return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); \
} \ } \
\ \
protected: \ protected: \
@ -668,7 +666,7 @@ private:
Variant script; // Reference does not exist yet, store it in a Variant. Variant script; // Reference does not exist yet, store it in a Variant.
HashMap<StringName, Variant> metadata; HashMap<StringName, Variant> metadata;
HashMap<StringName, Variant *> metadata_properties; HashMap<StringName, Variant *> metadata_properties;
mutable const StringName *_class_name_ptr = nullptr; mutable const GDType *_gdtype_ptr = nullptr;
void _add_user_signal(const String &p_name, const Array &p_args = Array()); void _add_user_signal(const String &p_name, const Array &p_args = Array());
bool _has_user_signal(const StringName &p_name) const; bool _has_user_signal(const StringName &p_name) const;
@ -774,9 +772,7 @@ protected:
Variant _call_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Variant _call_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Variant _call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Variant _call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
virtual const StringName *_get_class_namev() const { virtual const GDType &_get_typev() const { return get_gdtype_static(); }
return &get_class_static();
}
TypedArray<StringName> _get_meta_list_bind() const; TypedArray<StringName> _get_meta_list_bind() const;
TypedArray<Dictionary> _get_property_list_bind() const; TypedArray<Dictionary> _get_property_list_bind() const;
@ -842,26 +838,25 @@ public:
}; };
/* TYPE API */ /* TYPE API */
static void assign_class_name_static(const Span<char> &p_name, StringName &r_target); static void assign_type_static(GDType **type_ptr, const char *p_name, const GDType *super_type);
static const StringName &get_class_static() { static const GDType &get_gdtype_static() {
static StringName _class_name_static; static GDType *_class_static;
if (unlikely(!_class_name_static)) { if (unlikely(!_class_static)) {
assign_class_name_static("Object", _class_name_static); assign_type_static(&_class_static, "Object", nullptr);
} }
return _class_name_static; return *_class_static;
} }
const GDType &get_gdtype() const;
static const StringName &get_class_static() { return get_gdtype_static().get_name(); }
_FORCE_INLINE_ String get_class() const { return get_class_name(); } _FORCE_INLINE_ String get_class() const { return get_class_name(); }
virtual String get_save_class() const { return get_class(); } //class stored when saving virtual String get_save_class() const { return get_class(); } //class stored when saving
virtual bool is_class(const String &p_class) const { bool is_class(const String &p_class) const;
if (_extension && _extension->is_class(p_class)) {
return true;
}
return (p_class == "Object");
}
virtual bool is_class_ptr(void *p_ptr) const { return get_class_ptr_static() == p_ptr; } virtual bool is_class_ptr(void *p_ptr) const { return get_class_ptr_static() == p_ptr; }
template <typename T> template <typename T>