diff --git a/core/object/gdtype.cpp b/core/object/gdtype.cpp new file mode 100644 index 00000000000..25ce7aef3c0 --- /dev/null +++ b/core/object/gdtype.cpp @@ -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)); + } + } +} diff --git a/core/object/gdtype.h b/core/object/gdtype.h new file mode 100644 index 00000000000..a4384459f1b --- /dev/null +++ b/core/object/gdtype.h @@ -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 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 &get_name_hierarchy() const { return name_hierarchy; } +}; diff --git a/core/object/object.cpp b/core/object/object.cpp index a8bea5a9b31..4ff35a28b64 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -258,7 +258,7 @@ bool Object::_predelete() { 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); // 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() { // Cache the class name in the object for quick reference. - _class_name_ptr = _get_class_namev(); + _gdtype_ptr = &_get_typev(); _initialize_classv(); } @@ -2085,18 +2085,34 @@ uint32_t Object::get_edited_version() const { } #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 { if (_extension) { // Can't put inside the unlikely as constructor can run it. return _extension->class_name; } - if (unlikely(!_class_name_ptr)) { - // 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; + return get_gdtype().get_name(); } 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 // use this like the real object. if (unlikely(_extension && _extension->library == p_library && _extension->is_placeholder)) { - const StringName *class_name = _get_class_namev(); - return *class_name; + return get_class_name(); } #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. - const StringName *class_name = _get_class_namev(); - if (ClassDB::is_class_exposed(*class_name)) { - return *class_name; + const StringName &class_name = get_class_name(); + if (ClassDB::is_class_exposed(class_name)) { + return class_name; } // 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()) { if (ClassDB::is_class_exposed(parent_class)) { return parent_class; @@ -2296,14 +2311,16 @@ void Object::detach_from_objectdb() { } } -void Object::assign_class_name_static(const Span &p_name, StringName &r_target) { +void Object::assign_type_static(GDType **type_ptr, const char *p_name, const GDType *super_type) { static BinaryMutex _mutex; MutexLock lock(_mutex); - if (r_target) { - // Already assigned while we were waiting for the mutex. + GDType *type = *type_ptr; + if (type) { + // Assigned while we were waiting. return; } - r_target = StringName(p_name.ptr(), true); + type = memnew(GDType(super_type, StringName(p_name, true))); + *type_ptr = type; } Object::~Object() { diff --git a/core/object/object.h b/core/object/object.h index 12cb512679a..bae803b6484 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -31,6 +31,7 @@ #pragma once #include "core/extension/gdextension_interface.h" +#include "core/object/gdtype.h" #include "core/object/message_queue.h" #include "core/object/object_id.h" #include "core/os/rw_lock.h" @@ -494,21 +495,18 @@ private: friend class ::ClassDB; \ \ public: \ - virtual const StringName *_get_class_namev() const override { \ - return &get_class_static(); \ + virtual const GDType &_get_typev() const override { \ + 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 StringName _class_name_static; \ - 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); \ + return get_gdtype_static().get_name(); \ } \ \ protected: \ @@ -668,7 +666,7 @@ private: Variant script; // Reference does not exist yet, store it in a Variant. HashMap metadata; HashMap 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()); 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_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - virtual const StringName *_get_class_namev() const { - return &get_class_static(); - } + virtual const GDType &_get_typev() const { return get_gdtype_static(); } TypedArray _get_meta_list_bind() const; TypedArray _get_property_list_bind() const; @@ -842,26 +838,25 @@ public: }; /* TYPE API */ - static void assign_class_name_static(const Span &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 StringName _class_name_static; - if (unlikely(!_class_name_static)) { - assign_class_name_static("Object", _class_name_static); + static const GDType &get_gdtype_static() { + static GDType *_class_static; + if (unlikely(!_class_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(); } virtual String get_save_class() const { return get_class(); } //class stored when saving - virtual bool is_class(const String &p_class) const { - if (_extension && _extension->is_class(p_class)) { - return true; - } - return (p_class == "Object"); - } + bool is_class(const String &p_class) const; virtual bool is_class_ptr(void *p_ptr) const { return get_class_ptr_static() == p_ptr; } template