feat: updated engine

This commit is contained in:
Sara Gerretsen 2026-07-10 17:04:34 +02:00
parent cbe99774ff
commit f4cf6b3999
6607 changed files with 910135 additions and 430025 deletions

View file

@ -29,7 +29,9 @@
/**************************************************************************/
#include "object.h"
#include "object.compat.inc"
#include "core/config/engine.h"
#include "core/extension/gdextension_manager.h"
#include "core/io/resource.h"
#include "core/object/class_db.h"
@ -65,163 +67,45 @@ struct _ObjectDebugLock {
#endif
struct _ObjectSignalLock {
Mutex *mutex;
_ObjectSignalLock(const Object *const p_obj) {
mutex = p_obj->signal_mutex;
if (mutex) {
mutex->lock();
struct ObjectSignalLock {
Mutex *mutex1 = nullptr;
Mutex *mutex2 = nullptr;
ObjectSignalLock(const Object *p_obj1, const Object *p_obj2 = nullptr) {
mutex1 = p_obj1->signal_mutex;
if (p_obj2 != nullptr) {
mutex2 = p_obj2->signal_mutex;
if (mutex2 < mutex1) {
// We must always lock two locks in the same order.
SWAP(mutex1, mutex2);
} else if (unlikely(mutex2 == mutex1)) {
// No point locking the same lock twice.
mutex2 = nullptr;
}
}
if (mutex1) {
mutex1->lock();
}
if (mutex2) {
mutex2->lock();
}
}
~_ObjectSignalLock() {
if (mutex) {
mutex->unlock();
~ObjectSignalLock() {
if (mutex2) {
mutex2->unlock();
}
if (mutex1) {
mutex1->unlock();
}
}
};
#define OBJ_SIGNAL_LOCK _ObjectSignalLock _signal_lock(this);
PropertyInfo::operator Dictionary() const {
Dictionary d;
d["name"] = name;
d["class_name"] = class_name;
d["type"] = type;
d["hint"] = hint;
d["hint_string"] = hint_string;
d["usage"] = usage;
return d;
}
PropertyInfo PropertyInfo::from_dict(const Dictionary &p_dict) {
PropertyInfo pi;
if (p_dict.has("type")) {
pi.type = Variant::Type(int(p_dict["type"]));
}
if (p_dict.has("name")) {
pi.name = p_dict["name"];
}
if (p_dict.has("class_name")) {
pi.class_name = p_dict["class_name"];
}
if (p_dict.has("hint")) {
pi.hint = PropertyHint(int(p_dict["hint"]));
}
if (p_dict.has("hint_string")) {
pi.hint_string = p_dict["hint_string"];
}
if (p_dict.has("usage")) {
pi.usage = p_dict["usage"];
}
return pi;
}
TypedArray<Dictionary> convert_property_list(const List<PropertyInfo> *p_list) {
TypedArray<Dictionary> va;
for (const List<PropertyInfo>::Element *E = p_list->front(); E; E = E->next()) {
va.push_back(Dictionary(E->get()));
}
return va;
}
TypedArray<Dictionary> convert_property_list(const Vector<PropertyInfo> &p_vector) {
TypedArray<Dictionary> va;
for (const PropertyInfo &E : p_vector) {
va.push_back(Dictionary(E));
}
return va;
}
MethodInfo::operator Dictionary() const {
Dictionary d;
d["name"] = name;
d["args"] = convert_property_list(arguments);
Array da;
for (int i = 0; i < default_arguments.size(); i++) {
da.push_back(default_arguments[i]);
}
d["default_args"] = da;
d["flags"] = flags;
d["id"] = id;
Dictionary r = return_val;
d["return"] = r;
return d;
}
MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) {
MethodInfo mi;
if (p_dict.has("name")) {
mi.name = p_dict["name"];
}
Array args;
if (p_dict.has("args")) {
args = p_dict["args"];
}
for (const Variant &arg : args) {
Dictionary d = arg;
mi.arguments.push_back(PropertyInfo::from_dict(d));
}
Array defargs;
if (p_dict.has("default_args")) {
defargs = p_dict["default_args"];
}
for (const Variant &defarg : defargs) {
mi.default_arguments.push_back(defarg);
}
if (p_dict.has("return")) {
mi.return_val = PropertyInfo::from_dict(p_dict["return"]);
}
if (p_dict.has("flags")) {
mi.flags = p_dict["flags"];
}
return mi;
}
uint32_t MethodInfo::get_compatibility_hash() const {
bool has_return = (return_val.type != Variant::NIL) || (return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT);
uint32_t hash = hash_murmur3_one_32(has_return);
hash = hash_murmur3_one_32(arguments.size(), hash);
if (has_return) {
hash = hash_murmur3_one_32(return_val.type, hash);
if (return_val.class_name != StringName()) {
hash = hash_murmur3_one_32(return_val.class_name.hash(), hash);
}
}
for (const PropertyInfo &arg : arguments) {
hash = hash_murmur3_one_32(arg.type, hash);
if (arg.class_name != StringName()) {
hash = hash_murmur3_one_32(arg.class_name.hash(), hash);
}
}
hash = hash_murmur3_one_32(default_arguments.size(), hash);
for (const Variant &v : default_arguments) {
hash = hash_murmur3_one_32(v.hash(), hash);
}
hash = hash_murmur3_one_32(flags & METHOD_FLAG_CONST ? 1 : 0, hash);
hash = hash_murmur3_one_32(flags & METHOD_FLAG_VARARG ? 1 : 0, hash);
return hash_fmix32(hash);
}
Object::Connection::operator Variant() const {
Dictionary d;
d["signal"] = signal;
@ -234,18 +118,32 @@ void ObjectGDExtension::create_gdtype() {
ERR_FAIL_COND(gdtype);
gdtype = memnew(GDType(ClassDB::get_gdtype(parent_class_name), class_name));
gdtype->initialize();
}
void ObjectGDExtension::destroy_gdtype() {
ERR_FAIL_COND(!gdtype);
memdelete(const_cast<GDType *>(gdtype));
#ifdef TOOLS_ENABLED
if (!is_placeholder) {
#endif
memdelete(const_cast<GDType *>(gdtype));
#ifdef TOOLS_ENABLED
}
#endif
gdtype = nullptr;
}
ObjectGDExtension::~ObjectGDExtension() {
if (gdtype) {
memdelete(const_cast<GDType *>(gdtype));
#ifdef TOOLS_ENABLED
if (!is_placeholder) {
#endif
memdelete(const_cast<GDType *>(gdtype));
#ifdef TOOLS_ENABLED
}
#endif
}
}
@ -378,9 +276,9 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid
*r_valid = true;
}
return;
} else if (p_name.operator String().begins_with("metadata/")) {
} else if (p_name.string().begins_with("metadata/")) {
// Must exist, otherwise duplicate() will not work.
set_meta(p_name.operator String().replace_first("metadata/", ""), p_value);
set_meta(p_name.string().replace_first("metadata/", ""), p_value);
if (r_valid) {
*r_valid = true;
}
@ -616,24 +514,23 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
_get_property_listv(p_list, p_reversed);
if (!is_class("Script")) { // can still be set, but this is for user-friendliness
p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NEVER_DUPLICATE));
}
const uint32_t base_script_usage = is_class(Script::get_class_static()) ? PROPERTY_USAGE_NO_EDITOR : PROPERTY_USAGE_DEFAULT;
p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static(), base_script_usage | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_NEVER_DUPLICATE));
if (script_instance && !p_reversed) {
script_instance->get_property_list(p_list);
}
for (const KeyValue<StringName, Variant> &K : metadata) {
PropertyInfo pi = PropertyInfo(K.value.get_type(), "metadata/" + K.key.operator String());
PropertyInfo pi = PropertyInfo(K.value.get_type(), "metadata/" + K.key.string());
if (K.value.get_type() == Variant::OBJECT) {
pi.hint = PROPERTY_HINT_RESOURCE_TYPE;
Object *obj = K.value;
if (Object::cast_to<Script>(obj)) {
pi.hint_string = "Script";
pi.hint_string = Script::get_class_static();
pi.usage |= PROPERTY_USAGE_NEVER_DUPLICATE;
} else {
pi.hint_string = "Resource";
pi.hint_string = Resource::get_class_static();
}
}
p_list->push_back(pi);
@ -1136,7 +1033,7 @@ void Object::set_meta(const StringName &p_name, const Variant &p_value) {
if (E) {
E->value = p_value;
} else {
ERR_FAIL_COND_MSG(!p_name.operator String().is_valid_ascii_identifier(), vformat("Invalid metadata identifier: '%s'.", p_name));
ERR_FAIL_COND_MSG(!p_name.string().is_valid_unicode_identifier(), vformat("Invalid metadata identifier: '%s'.", p_name));
Variant *V = &metadata.insert(p_name, p_value)->value;
const String &sname = p_name;
@ -1208,9 +1105,9 @@ void Object::get_meta_list(List<StringName> *p_list) const {
void Object::add_user_signal(const MethodInfo &p_signal) {
ERR_FAIL_COND_MSG(p_signal.name.is_empty(), "Signal name cannot be empty.");
ERR_FAIL_COND_MSG(ClassDB::has_signal(get_class_name(), p_signal.name), vformat("User signal's name conflicts with a built-in signal of '%s'.", get_class_name()));
ERR_FAIL_COND_MSG(get_gdtype().get_signal_map(false).has(p_signal.name), vformat("User signal's name conflicts with a built-in signal of '%s'.", get_class_name()));
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), vformat("Trying to add already existing signal '%s'.", p_signal.name));
SignalData s;
@ -1219,7 +1116,7 @@ void Object::add_user_signal(const MethodInfo &p_signal) {
}
bool Object::_has_user_signal(const StringName &p_name) const {
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
if (!signal_map.has(p_name)) {
return false;
@ -1228,19 +1125,26 @@ bool Object::_has_user_signal(const StringName &p_name) const {
}
void Object::_remove_user_signal(const StringName &p_name) {
OBJ_SIGNAL_LOCK
HashMap<Callable, Object::SignalData::Slot> slots_to_disconnect;
SignalData *s = signal_map.getptr(p_name);
ERR_FAIL_NULL_MSG(s, "Provided signal does not exist.");
ERR_FAIL_COND_MSG(!s->removable, "Signal is not removable (not added with add_user_signal).");
for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) {
{
ObjectSignalLock signal_lock(this);
SignalData *s = signal_map.getptr(p_name);
ERR_FAIL_NULL_MSG(s, "Provided signal does not exist.");
ERR_FAIL_COND_MSG(!s->removable, "Signal is not removable (not added with add_user_signal).");
slots_to_disconnect = std::move(s->slot_map);
signal_map.erase(p_name);
}
for (const KeyValue<Callable, SignalData::Slot> &slot_kv : slots_to_disconnect) {
Object *target = slot_kv.key.get_object();
if (likely(target)) {
ObjectSignalLock signal_lock(target);
target->connections.erase(slot_kv.value.cE);
}
}
signal_map.erase(p_name);
}
Error Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
@ -1286,12 +1190,12 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
uint32_t slot_count = 0;
{
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
SignalData *s = signal_map.getptr(p_name);
if (!s) {
#ifdef DEBUG_ENABLED
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_name);
bool signal_is_valid = get_gdtype().get_signal_map(false).has(p_name);
//check in script
ERR_FAIL_COND_V_MSG(!signal_is_valid && script_instance && !script_instance->get_script()->has_script_signal(p_name), ERR_UNAVAILABLE, vformat("Can't emit non-existing signal \"%s\".", p_name));
#endif
@ -1313,19 +1217,19 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
}
DEV_ASSERT(slot_count == s->slot_map.size());
}
// Disconnect all one-shot connections before emitting to prevent recursion.
for (uint32_t i = 0; i < slot_count; ++i) {
bool disconnect = slot_flags[i] & CONNECT_ONE_SHOT;
// Disconnect all one-shot connections before emitting to prevent recursion.
for (uint32_t i = 0; i < slot_count; ++i) {
bool disconnect = slot_flags[i] & CONNECT_ONE_SHOT;
#ifdef TOOLS_ENABLED
if (disconnect && (slot_flags[i] & CONNECT_PERSIST) && Engine::get_singleton()->is_editor_hint()) {
// This signal was connected from the editor, and is being edited. Just don't disconnect for now.
disconnect = false;
}
if (disconnect && (slot_flags[i] & CONNECT_PERSIST) && Engine::get_singleton()->is_editor_hint()) {
// This signal was connected from the editor, and is being edited. Just don't disconnect for now.
disconnect = false;
}
#endif
if (disconnect) {
_disconnect(p_name, slot_callables[i]);
}
if (disconnect) {
_disconnect(p_name, slot_callables[i]);
}
}
@ -1441,12 +1345,16 @@ void Object::_reset_gdtype() const {
}
}
void Object::autorelease_gdtype(GDType **r_type) {
ClassDB::gdtype_autorelease_pool.push_back(r_type);
}
void Object::_add_user_signal(const String &p_name, const Array &p_args) {
// this version of add_user_signal is meant to be used from scripts or external apis
// without access to ADD_SIGNAL in bind_methods
// added events are per instance, as opposed to the other ones, which are global
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
MethodInfo mi;
mi.name = p_name;
@ -1500,6 +1408,8 @@ TypedArray<Dictionary> Object::_get_signal_connection_list(const StringName &p_s
}
TypedArray<Dictionary> Object::_get_incoming_connections() const {
ObjectSignalLock signal_lock(this);
TypedArray<Dictionary> ret;
for (const Object::Connection &connection : connections) {
ret.push_back(connection);
@ -1513,7 +1423,7 @@ bool Object::has_signal(const StringName &p_name) const {
return true;
}
if (ClassDB::has_signal(get_class_name(), p_name)) {
if (get_gdtype().get_signal_map(false).has(p_name)) {
return true;
}
@ -1525,7 +1435,7 @@ bool Object::has_signal(const StringName &p_name) const {
}
void Object::get_signal_list(List<MethodInfo> *p_signals) const {
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
if (script_instance) {
script_instance->get_script()->get_script_signal_list(p_signals);
@ -1543,7 +1453,7 @@ void Object::get_signal_list(List<MethodInfo> *p_signals) const {
}
void Object::get_all_signal_connections(List<Connection> *p_connections) const {
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
for (const KeyValue<StringName, SignalData> &E : signal_map) {
const SignalData *s = &E.value;
@ -1555,7 +1465,7 @@ void Object::get_all_signal_connections(List<Connection> *p_connections) const {
}
void Object::get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const {
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
const SignalData *s = signal_map.getptr(p_signal);
if (!s) {
@ -1568,7 +1478,7 @@ void Object::get_signal_connection_list(const StringName &p_signal, List<Connect
}
int Object::get_persistent_signal_connection_count() const {
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
int count = 0;
for (const KeyValue<StringName, SignalData> &E : signal_map) {
@ -1585,7 +1495,7 @@ int Object::get_persistent_signal_connection_count() const {
}
uint32_t Object::get_signal_connection_flags(const StringName &p_name, const Callable &p_callable) const {
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
const SignalData *signal_data = signal_map.getptr(p_name);
if (signal_data) {
const SignalData::Slot *slot = signal_data->slot_map.getptr(p_callable);
@ -1597,7 +1507,7 @@ uint32_t Object::get_signal_connection_flags(const StringName &p_name, const Cal
}
void Object::get_signals_connected_to_this(List<Connection> *p_connections) const {
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
for (const Connection &E : connections) {
p_connections->push_back(E);
@ -1606,20 +1516,21 @@ void Object::get_signals_connected_to_this(List<Connection> *p_connections) cons
Error Object::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) {
ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, vformat("Cannot connect to '%s': the provided callable is null.", p_signal));
OBJ_SIGNAL_LOCK
Object *target_object = p_callable.get_object();
ObjectSignalLock signal_lock(this, target_object);
if (p_callable.is_standard()) {
// FIXME: This branch should probably removed in favor of the `is_valid()` branch, but there exist some classes
// that call `connect()` before they are fully registered with ClassDB. Until all such classes can be found
// and registered soon enough this branch is needed to allow `connect()` to succeed.
ERR_FAIL_NULL_V_MSG(p_callable.get_object(), ERR_INVALID_PARAMETER, vformat("Cannot connect to '%s' to callable '%s': the callable object is null.", p_signal, p_callable));
ERR_FAIL_NULL_V_MSG(target_object, ERR_INVALID_PARAMETER, vformat("Cannot connect to '%s' to callable '%s': the callable object is null.", p_signal, p_callable));
} else {
ERR_FAIL_COND_V_MSG(!p_callable.is_valid(), ERR_INVALID_PARAMETER, vformat("Cannot connect to '%s': the provided callable is not valid: '%s'.", p_signal, p_callable));
}
SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
bool signal_is_valid = get_gdtype().get_signal_map(false).has(p_signal);
//check in script
if (!signal_is_valid && script_instance) {
if (script_instance->get_script()->has_script_signal(p_signal)) {
@ -1628,7 +1539,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
#ifdef TOOLS_ENABLED
else {
//allow connecting signals anyway if script is invalid, see issue #17070
if (!script_instance->get_script()->is_valid()) {
if (!script_instance->get_script()->is_script_valid()) {
signal_is_valid = true;
}
}
@ -1651,8 +1562,6 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
}
}
Object *target_object = p_callable.get_object();
SignalData::Slot slot;
Connection conn;
@ -1675,11 +1584,11 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
bool Object::is_connected(const StringName &p_signal, const Callable &p_callable) const {
ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, vformat("Cannot determine if connected to '%s': the provided callable is null.", p_signal)); // Should use `is_null`, see note in `connect` about the use of `is_valid`.
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
const SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
bool signal_is_valid = get_gdtype().get_signal_map(false).has(p_signal);
if (signal_is_valid) {
return false;
}
@ -1695,11 +1604,11 @@ bool Object::is_connected(const StringName &p_signal, const Callable &p_callable
}
bool Object::has_connections(const StringName &p_signal) const {
OBJ_SIGNAL_LOCK
ObjectSignalLock signal_lock(this);
const SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
bool signal_is_valid = get_gdtype().get_signal_map(false).has(p_signal);
if (signal_is_valid) {
return false;
}
@ -1720,11 +1629,12 @@ void Object::disconnect(const StringName &p_signal, const Callable &p_callable)
bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, vformat("Cannot disconnect from '%s': the provided callable is null.", p_signal)); // Should use `is_null`, see note in `connect` about the use of `is_valid`.
OBJ_SIGNAL_LOCK
Object *target_object = p_callable.get_object();
ObjectSignalLock signal_lock(this, target_object);
SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal) ||
bool signal_is_valid = get_gdtype().get_signal_map(false).has(p_signal) ||
(script_instance && script_instance->get_script()->has_script_signal(p_signal));
ERR_FAIL_COND_V_MSG(signal_is_valid, false, vformat("Attempt to disconnect a nonexistent connection from '%s'. Signal: '%s', callable: '%s'.", to_string(), p_signal, p_callable));
}
@ -1741,16 +1651,13 @@ bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable,
}
}
if (slot->cE) {
Object *target_object = p_callable.get_object();
if (target_object) {
target_object->connections.erase(slot->cE);
}
if (likely(target_object)) {
target_object->connections.erase(slot->cE);
}
s->slot_map.erase(*p_callable.get_base_comparator());
if (s->slot_map.is_empty() && ClassDB::has_signal(get_class_name(), p_signal)) {
if (s->slot_map.is_empty() && get_gdtype().get_signal_map(false).has(p_signal)) {
//not user signal, delete
signal_map.erase(p_signal);
}
@ -1793,10 +1700,18 @@ Variant Object::_get_indexed_bind(const NodePath &p_name) const {
void Object::initialize_class() {
static bool initialized = false;
if (initialized) {
if (likely(initialized)) {
return;
}
_add_class_to_classdb(get_gdtype_static(), nullptr);
static BinaryMutex __init_mutex;
MutexLock lock(__init_mutex);
if (initialized) {
// Initialized on another thread while we were waiting.
return;
}
_add_class_to_classdb(get_gdtype_static_mutable(), nullptr);
get_gdtype_static_mutable().initialize();
_bind_methods();
_bind_compatibility_methods();
initialized = true;
@ -1872,7 +1787,7 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) {
}
}
void Object::_add_class_to_classdb(const GDType &p_type, const GDType *p_inherits) {
void Object::_add_class_to_classdb(GDType &p_type, const GDType *p_inherits) {
ClassDB::_add_class(p_type, p_inherits);
}
@ -2073,11 +1988,15 @@ void Object::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_PREDELETE);
BIND_CONSTANT(NOTIFICATION_EXTENSION_RELOADED);
BIND_ENUM_CONSTANT(CONNECT_DEFERRED);
BIND_ENUM_CONSTANT(CONNECT_PERSIST);
BIND_ENUM_CONSTANT(CONNECT_ONE_SHOT);
BIND_ENUM_CONSTANT(CONNECT_REFERENCE_COUNTED);
BIND_ENUM_CONSTANT(CONNECT_APPEND_SOURCE_OBJECT);
BIND_BITFIELD_FLAG(CONNECT_DEFERRED);
BIND_BITFIELD_FLAG(CONNECT_PERSIST);
BIND_BITFIELD_FLAG(CONNECT_ONE_SHOT);
BIND_BITFIELD_FLAG(CONNECT_REFERENCE_COUNTED);
BIND_BITFIELD_FLAG(CONNECT_APPEND_SOURCE_OBJECT);
}
void Object::call_deferredp(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) {
MessageQueue::get_singleton()->push_callp(this, p_method, p_args, p_argcount);
}
void Object::set_deferred(const StringName &p_property, const Variant &p_value) {
@ -2189,13 +2108,8 @@ const GDType &Object::get_gdtype() const {
return *_gdtype_ptr;
}
bool Object::is_class(const String &p_class) const {
for (const StringName &name : get_gdtype().get_name_hierarchy()) {
if (name == p_class) {
return true;
}
}
return false;
bool Object::is_class(const StringName &p_class) const {
return get_gdtype().get_name_hierarchy().has(p_class);
}
const StringName &Object::get_class_name() const {
@ -2259,8 +2173,8 @@ void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindi
}
}
if (unlikely(!binding && p_callbacks)) {
uint32_t current_size = next_power_of_2(_instance_binding_count);
uint32_t new_size = next_power_of_2(_instance_binding_count + 1);
uint32_t current_size = Math::next_power_of_2(_instance_binding_count);
uint32_t new_size = Math::next_power_of_2(_instance_binding_count + 1);
if (current_size == 0 || new_size > current_size) {
_instance_bindings = (InstanceBinding *)memrealloc(_instance_bindings, new_size * sizeof(InstanceBinding));
@ -2403,56 +2317,36 @@ void Object::detach_from_objectdb() {
}
}
void Object::assign_type_static(GDType **type_ptr, const char *p_name, const GDType *super_type) {
static BinaryMutex _mutex;
MutexLock lock(_mutex);
GDType *type = *type_ptr;
if (type) {
// Assigned while we were waiting.
return;
}
type = memnew(GDType(super_type, StringName(p_name)));
*type_ptr = type;
ClassDB::gdtype_autorelease_pool.push_back(type_ptr);
}
Object::~Object() {
if (_emitting) {
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
ERR_PRINT(vformat("Object '%s' was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes.", to_string()));
}
{
OBJ_SIGNAL_LOCK
// Drop all connections to the signals of this object.
while (signal_map.size()) {
// Avoid regular iteration so erasing is safe.
KeyValue<StringName, SignalData> &E = *signal_map.begin();
SignalData *s = &E.value;
// Drop all connections to the signals of this object.
while (signal_map.size()) {
// Avoid regular iteration so erasing is safe.
KeyValue<StringName, SignalData> &E = *signal_map.begin();
SignalData *s = &E.value;
for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) {
Object *target = slot_kv.value.conn.callable.get_object();
if (likely(target)) {
target->connections.erase(slot_kv.value.cE);
}
for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) {
Object *target = slot_kv.value.conn.callable.get_object();
if (likely(target)) {
ObjectSignalLock signal_lock(this, target);
target->connections.erase(slot_kv.value.cE);
}
signal_map.erase(E.key);
}
// Disconnect signals that connect to this object.
while (connections.size()) {
Connection c = connections.front()->get();
Object *obj = c.callable.get_object();
bool disconnected = false;
if (likely(obj)) {
disconnected = c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true);
}
if (unlikely(!disconnected)) {
// If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here.
connections.pop_front();
}
signal_map.erase(E.key);
}
// Disconnect signals that connect to this object.
while (connections.size()) {
Connection c = connections.front()->get();
bool disconnected = c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true);
if (unlikely(!disconnected)) {
// If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here.
connections.pop_front();
}
}
@ -2638,7 +2532,7 @@ void ObjectDB::cleanup() {
spin_lock.lock();
if (slot_count > 0) {
WARN_PRINT("ObjectDB instances leaked at exit (run with --verbose for details).");
WARN_PRINT(vformat("%d ObjectDB %s leaked at exit (run with `--verbose` for details).", slot_count, slot_count == 1 ? "instance was" : "instances were"));
if (OS::get_singleton()->is_stdout_verbose()) {
// Ensure calling the native classes because if a leaked instance has a script
// that overrides any of those methods, it'd not be OK to call them at this point,