summaryrefslogtreecommitdiffstats
path: root/core/object/class_db.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/object/class_db.cpp')
-rw-r--r--core/object/class_db.cpp215
1 files changed, 170 insertions, 45 deletions
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index fe4345aa0d..5c793a676f 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -76,6 +76,21 @@ class PlaceholderExtensionInstance {
StringName class_name;
HashMap<StringName, Variant> properties;
+ // Checks if a property is from a runtime class, and not a non-runtime base class.
+ bool is_runtime_property(const StringName &p_property_name) {
+ StringName current_class_name = class_name;
+
+ while (ClassDB::is_class_runtime(current_class_name)) {
+ if (ClassDB::has_property(current_class_name, p_property_name, true)) {
+ return true;
+ }
+
+ current_class_name = ClassDB::get_parent_class(current_class_name);
+ }
+
+ return false;
+ }
+
public:
PlaceholderExtensionInstance(const StringName &p_class_name) {
class_name = p_class_name;
@@ -83,27 +98,24 @@ public:
~PlaceholderExtensionInstance() {}
- void set(const StringName &p_name, const Variant &p_value) {
- bool is_default_valid = false;
- Variant default_value = ClassDB::class_get_default_property_value(class_name, p_name, &is_default_valid);
-
- // If there's a default value, then we know it's a valid property.
- if (is_default_valid) {
+ void set(const StringName &p_name, const Variant &p_value, bool &r_valid) {
+ r_valid = is_runtime_property(p_name);
+ if (r_valid) {
properties[p_name] = p_value;
}
}
- Variant get(const StringName &p_name) {
+ Variant get(const StringName &p_name, bool &r_valid) {
const Variant *value = properties.getptr(p_name);
Variant ret;
if (value) {
ret = *value;
+ r_valid = true;
} else {
- bool is_default_valid = false;
- Variant default_value = ClassDB::class_get_default_property_value(class_name, p_name, &is_default_valid);
- if (is_default_valid) {
- ret = default_value;
+ r_valid = is_runtime_property(p_name);
+ if (r_valid) {
+ ret = ClassDB::class_get_default_property_value(class_name, p_name);
}
}
@@ -115,10 +127,10 @@ public:
const StringName &name = *(StringName *)p_name;
const Variant &value = *(const Variant *)p_value;
- self->set(name, value);
+ bool valid = false;
+ self->set(name, value, valid);
- // We have to return true so Godot doesn't try to call the real setter function.
- return true;
+ return valid;
}
static GDExtensionBool placeholder_instance_get(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret) {
@@ -126,10 +138,10 @@ public:
const StringName &name = *(StringName *)p_name;
Variant *value = (Variant *)r_ret;
- *value = self->get(name);
+ bool valid = false;
+ *value = self->get(name, valid);
- // We have to return true so Godot doesn't try to call the real getter function.
- return true;
+ return valid;
}
static const GDExtensionPropertyInfo *placeholder_instance_get_property_list(GDExtensionClassInstancePtr p_instance, uint32_t *r_count) {
@@ -169,18 +181,18 @@ public:
return 0;
}
- static GDExtensionObjectPtr placeholder_class_create_instance(void *p_class_userdata) {
+ static GDExtensionObjectPtr placeholder_class_create_instance(void *p_class_userdata, bool p_notify_postinitialize) {
ClassDB::ClassInfo *ti = (ClassDB::ClassInfo *)p_class_userdata;
- // Find the closest native parent.
+ // Find the closest native parent, that isn't a runtime class.
ClassDB::ClassInfo *native_parent = ti->inherits_ptr;
- while (native_parent->gdextension) {
+ while (native_parent->gdextension || native_parent->is_runtime) {
native_parent = native_parent->inherits_ptr;
}
ERR_FAIL_NULL_V(native_parent->creation_func, nullptr);
// Construct a placeholder.
- Object *obj = native_parent->creation_func();
+ Object *obj = native_parent->creation_func(p_notify_postinitialize);
// ClassDB::set_object_extension_instance() won't be called for placeholders.
// We need need to make sure that all the things it would have done (even if
@@ -291,6 +303,29 @@ StringName ClassDB::get_parent_class_nocheck(const StringName &p_class) {
return ti->inherits;
}
+bool ClassDB::get_inheritance_chain_nocheck(const StringName &p_class, Vector<StringName> &r_result) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *start = classes.getptr(p_class);
+ if (!start) {
+ return false;
+ }
+
+ int classes_to_add = 0;
+ for (ClassInfo *ti = start; ti; ti = ti->inherits_ptr) {
+ classes_to_add++;
+ }
+
+ int64_t old_size = r_result.size();
+ r_result.resize(old_size + classes_to_add);
+ StringName *w = r_result.ptrw() + old_size;
+ for (ClassInfo *ti = start; ti; ti = ti->inherits_ptr) {
+ *w++ = ti->name;
+ }
+
+ return true;
+}
+
StringName ClassDB::get_compatibility_remapped_class(const StringName &p_class) {
if (classes.has(p_class)) {
return p_class;
@@ -490,12 +525,12 @@ StringName ClassDB::get_compatibility_class(const StringName &p_class) {
return StringName();
}
-Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require_real_class) {
+Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require_real_class, bool p_notify_postinitialize) {
ClassInfo *ti;
{
OBJTYPE_RLOCK;
ti = classes.getptr(p_class);
- if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) {
+ if (!_can_instantiate(ti)) {
if (compat_classes.has(p_class)) {
ti = classes.getptr(compat_classes[p_class]);
}
@@ -504,34 +539,78 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require
ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled.");
ERR_FAIL_NULL_V_MSG(ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated.");
}
+
#ifdef TOOLS_ENABLED
if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
ERR_PRINT("Class '" + String(p_class) + "' can only be instantiated by editor.");
return nullptr;
}
#endif
- if (ti->gdextension && ti->gdextension->create_instance) {
- ObjectGDExtension *extension = ti->gdextension;
-#ifdef TOOLS_ENABLED
- if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) {
- extension = get_placeholder_extension(ti->name);
- }
-#endif
- return (Object *)extension->create_instance(extension->class_userdata);
- } else {
+
#ifdef TOOLS_ENABLED
- if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) {
- if (!ti->inherits_ptr || !ti->inherits_ptr->creation_func) {
- ERR_PRINT(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name));
- } else {
- ObjectGDExtension *extension = get_placeholder_extension(ti->name);
- return (Object *)extension->create_instance(extension->class_userdata);
+ // Try to create placeholder.
+ if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) {
+ bool can_create_placeholder = false;
+ if (ti->gdextension) {
+ if (ti->gdextension->create_instance2) {
+ can_create_placeholder = true;
+ }
+#ifndef DISABLE_DEPRECATED
+ else if (ti->gdextension->create_instance) {
+ can_create_placeholder = true;
}
+#endif // DISABLE_DEPRECATED
+ } else if (!ti->inherits_ptr || !ti->inherits_ptr->creation_func) {
+ ERR_PRINT(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name));
+ } else {
+ can_create_placeholder = true;
}
-#endif
- return ti->creation_func();
+ if (can_create_placeholder) {
+ ObjectGDExtension *extension = get_placeholder_extension(ti->name);
+ return (Object *)extension->create_instance2(extension->class_userdata, p_notify_postinitialize);
+ }
+ }
+#endif // TOOLS_ENABLED
+
+ if (ti->gdextension && ti->gdextension->create_instance2) {
+ ObjectGDExtension *extension = ti->gdextension;
+ return (Object *)extension->create_instance2(extension->class_userdata, p_notify_postinitialize);
+ }
+#ifndef DISABLE_DEPRECATED
+ else if (ti->gdextension && ti->gdextension->create_instance) {
+ ObjectGDExtension *extension = ti->gdextension;
+ return (Object *)extension->create_instance(extension->class_userdata);
+ }
+#endif // DISABLE_DEPRECATED
+ else {
+ return ti->creation_func(p_notify_postinitialize);
+ }
+}
+
+bool ClassDB::_can_instantiate(ClassInfo *p_class_info) {
+ if (!p_class_info) {
+ return false;
}
+
+ if (p_class_info->disabled || !p_class_info->creation_func) {
+ return false;
+ }
+
+ if (!p_class_info->gdextension) {
+ return true;
+ }
+
+ if (p_class_info->gdextension->create_instance2) {
+ return true;
+ }
+
+#ifndef DISABLE_DEPRECATED
+ if (p_class_info->gdextension->create_instance) {
+ return true;
+ }
+#endif // DISABLE_DEPRECATED
+ return false;
}
Object *ClassDB::instantiate(const StringName &p_class) {
@@ -542,6 +621,10 @@ Object *ClassDB::instantiate_no_placeholders(const StringName &p_class) {
return _instantiate_internal(p_class, true);
}
+Object *ClassDB::instantiate_without_postinitialization(const StringName &p_class) {
+ return _instantiate_internal(p_class, true, false);
+}
+
#ifdef TOOLS_ENABLED
ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class) {
ObjectGDExtension *placeholder_extension = placeholder_extensions.getptr(p_class);
@@ -553,7 +636,7 @@ ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class)
{
OBJTYPE_RLOCK;
ti = classes.getptr(p_class);
- if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) {
+ if (!_can_instantiate(ti)) {
if (compat_classes.has(p_class)) {
ti = classes.getptr(compat_classes[p_class]);
}
@@ -614,7 +697,10 @@ ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class)
placeholder_extension->get_rid = &PlaceholderExtensionInstance::placeholder_instance_get_rid;
placeholder_extension->class_userdata = ti;
- placeholder_extension->create_instance = &PlaceholderExtensionInstance::placeholder_class_create_instance;
+#ifndef DISABLE_DEPRECATED
+ placeholder_extension->create_instance = nullptr;
+#endif // DISABLE_DEPRECATED
+ placeholder_extension->create_instance2 = &PlaceholderExtensionInstance::placeholder_class_create_instance;
placeholder_extension->free_instance = &PlaceholderExtensionInstance::placeholder_class_free_instance;
placeholder_extension->get_virtual = &PlaceholderExtensionInstance::placeholder_class_get_virtual;
placeholder_extension->get_virtual_call_data = nullptr;
@@ -631,7 +717,7 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName &
{
OBJTYPE_RLOCK;
ti = classes.getptr(p_class);
- if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) {
+ if (!_can_instantiate(ti)) {
if (compat_classes.has(p_class)) {
ti = classes.getptr(compat_classes[p_class]);
}
@@ -668,7 +754,33 @@ bool ClassDB::can_instantiate(const StringName &p_class) {
return false;
}
#endif
- return (!ti->disabled && ti->creation_func != nullptr && !(ti->gdextension && !ti->gdextension->create_instance));
+ return _can_instantiate(ti);
+}
+
+bool ClassDB::is_abstract(const StringName &p_class) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *ti = classes.getptr(p_class);
+ if (!ti) {
+ if (!ScriptServer::is_global_class(p_class)) {
+ ERR_FAIL_V_MSG(false, "Cannot get class '" + String(p_class) + "'.");
+ }
+ String path = ScriptServer::get_global_class_path(p_class);
+ Ref<Script> scr = ResourceLoader::load(path);
+ return scr.is_valid() && scr->is_valid() && scr->is_abstract();
+ }
+
+ if (ti->creation_func != nullptr) {
+ return false;
+ }
+ if (!ti->gdextension) {
+ return true;
+ }
+#ifndef DISABLE_DEPRECATED
+ return ti->gdextension->create_instance2 == nullptr && ti->gdextension->create_instance == nullptr;
+#else
+ return ti->gdextension->create_instance2 == nullptr;
+#endif // DISABLE_DEPRECATED
}
bool ClassDB::is_virtual(const StringName &p_class) {
@@ -688,7 +800,7 @@ bool ClassDB::is_virtual(const StringName &p_class) {
return false;
}
#endif
- return (!ti->disabled && ti->creation_func != nullptr && !(ti->gdextension && !ti->gdextension->create_instance) && ti->is_virtual);
+ return (_can_instantiate(ti) && ti->is_virtual);
}
void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) {
@@ -1952,6 +2064,14 @@ bool ClassDB::is_class_reloadable(const StringName &p_class) {
return ti->reloadable;
}
+bool ClassDB::is_class_runtime(const StringName &p_class) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *ti = classes.getptr(p_class);
+ ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'.");
+ return ti->is_runtime;
+}
+
void ClassDB::add_resource_base_extension(const StringName &p_extension, const StringName &p_class) {
if (resource_base_extensions.has(p_extension)) {
return;
@@ -2063,6 +2183,11 @@ void ClassDB::register_extension_class(ObjectGDExtension *p_extension) {
ClassInfo *parent = classes.getptr(p_extension->parent_class_name);
+#ifdef TOOLS_ENABLED
+ // @todo This is a limitation of the current implementation, but it should be possible to remove.
+ ERR_FAIL_COND_MSG(p_extension->is_runtime && parent->gdextension && !parent->is_runtime, "Extension runtime class " + String(p_extension->class_name) + " cannot descend from " + parent->name + " which isn't also a runtime class");
+#endif
+
ClassInfo c;
c.api = p_extension->editor_class ? API_EDITOR_EXTENSION : API_EXTENSION;
c.gdextension = p_extension;