summaryrefslogtreecommitdiff
path: root/core/object.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/object.cpp')
-rw-r--r--core/object.cpp456
1 files changed, 248 insertions, 208 deletions
diff --git a/core/object.cpp b/core/object.cpp
index dc1dc2c41f..140ee811fe 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -335,10 +335,8 @@ MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const Pr
Object::Connection::operator Variant() const {
Dictionary d;
- d["source"] = source;
d["signal"] = signal;
- d["target"] = target;
- d["method"] = method;
+ d["callable"] = callable;
d["flags"] = flags;
d["binds"] = binds;
return d;
@@ -346,34 +344,19 @@ Object::Connection::operator Variant() const {
bool Object::Connection::operator<(const Connection &p_conn) const {
- if (source == p_conn.source) {
-
- if (signal == p_conn.signal) {
-
- if (target == p_conn.target) {
-
- return method < p_conn.method;
- } else {
-
- return target < p_conn.target;
- }
- } else
- return signal < p_conn.signal;
+ if (signal == p_conn.signal) {
+ return callable < p_conn.callable;
} else {
- return source < p_conn.source;
+ return signal < p_conn.signal;
}
}
Object::Connection::Connection(const Variant &p_variant) {
Dictionary d = p_variant;
- if (d.has("source"))
- source = d["source"];
if (d.has("signal"))
signal = d["signal"];
- if (d.has("target"))
- target = d["target"];
- if (d.has("method"))
- method = d["method"];
+ if (d.has("callable"))
+ callable = d["callable"];
if (d.has("flags"))
flags = d["flags"];
if (d.has("binds"))
@@ -655,18 +638,18 @@ void Object::get_method_list(List<MethodInfo> *p_list) const {
}
}
-Variant Object::_call_bind(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
+Variant Object::_call_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
- r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 0;
return Variant();
}
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
- r_error.expected = Variant::STRING;
+ r_error.expected = Variant::STRING_NAME;
return Variant();
}
@@ -675,22 +658,22 @@ Variant Object::_call_bind(const Variant **p_args, int p_argcount, Variant::Call
return call(method, &p_args[1], p_argcount - 1, r_error);
}
-Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
+Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
- r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 0;
return Variant();
}
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
- r_error.expected = Variant::STRING;
+ r_error.expected = Variant::STRING_NAME;
return Variant();
}
- r_error.error = Variant::CallError::CALL_OK;
+ r_error.error = Callable::CallError::CALL_OK;
StringName method = *p_args[0];
@@ -700,29 +683,29 @@ Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Vari
}
#ifdef DEBUG_ENABLED
-static void _test_call_error(const StringName &p_func, const Variant::CallError &error) {
+static void _test_call_error(const StringName &p_func, const Callable::CallError &error) {
switch (error.error) {
- case Variant::CallError::CALL_OK:
- case Variant::CallError::CALL_ERROR_INVALID_METHOD:
+ case Callable::CallError::CALL_OK:
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD:
break;
- case Variant::CallError::CALL_ERROR_INVALID_ARGUMENT: {
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
- ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(error.expected) + ".");
+ ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(Variant::Type(error.expected)) + ".");
break;
}
- case Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too many arguments, expected " + itos(error.argument) + ".");
break;
}
- case Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too few arguments, expected " + itos(error.argument) + ".");
break;
}
- case Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break;
}
}
@@ -749,7 +732,7 @@ void Object::call_multilevel(const StringName &p_method, const Variant **p_args,
//Variant ret;
OBJ_DEBUG_LOCK
- Variant::CallError error;
+ Callable::CallError error;
if (script_instance) {
script_instance->call_multilevel(p_method, p_args, p_argcount);
@@ -769,7 +752,7 @@ void Object::call_multilevel_reversed(const StringName &p_method, const Variant
MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
- Variant::CallError error;
+ Callable::CallError error;
OBJ_DEBUG_LOCK
if (method) {
@@ -823,9 +806,9 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) {
}
}
- Variant::CallError ce;
+ Callable::CallError ce;
Variant ret = call(p_method, argptrs, p_args.size(), ce);
- if (ce.error != Variant::CallError::CALL_OK) {
+ if (ce.error != Callable::CallError::CALL_OK) {
ERR_FAIL_V_MSG(Variant(), "Error calling method from 'callv': " + Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce) + ".");
}
return ret;
@@ -842,7 +825,7 @@ Variant Object::call(const StringName &p_name, VARIANT_ARG_DECLARE) {
argc++;
}
- Variant::CallError error;
+ Callable::CallError error;
Variant ret = call(p_name, argptr, argc, error);
return ret;
@@ -859,38 +842,38 @@ void Object::call_multilevel(const StringName &p_name, VARIANT_ARG_DECLARE) {
argc++;
}
- //Variant::CallError error;
+ //Callable::CallError error;
call_multilevel(p_name, argptr, argc);
}
-Variant Object::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
+Variant Object::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- r_error.error = Variant::CallError::CALL_OK;
+ r_error.error = Callable::CallError::CALL_OK;
if (p_method == CoreStringNames::get_singleton()->_free) {
//free must be here, before anything, always ready
#ifdef DEBUG_ENABLED
if (p_argcount != 0) {
r_error.argument = 0;
- r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
return Variant();
}
if (Object::cast_to<Reference>(this)) {
r_error.argument = 0;
- r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
ERR_FAIL_V_MSG(Variant(), "Can't 'free' a reference.");
}
if (_lock_index.get() > 1) {
r_error.argument = 0;
- r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
ERR_FAIL_V_MSG(Variant(), "Object is locked and can't be freed.");
}
#endif
//must be here, must be before everything,
memdelete(this);
- r_error.error = Variant::CallError::CALL_OK;
+ r_error.error = Callable::CallError::CALL_OK;
return Variant();
}
@@ -901,15 +884,15 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
//force jumptable
switch (r_error.error) {
- case Variant::CallError::CALL_OK:
+ case Callable::CallError::CALL_OK:
return ret;
- case Variant::CallError::CALL_ERROR_INVALID_METHOD:
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD:
break;
- case Variant::CallError::CALL_ERROR_INVALID_ARGUMENT:
- case Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
- case Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
return ret;
- case Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
+ case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
}
}
}
@@ -917,10 +900,9 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
if (method) {
-
ret = method->call(this, p_args, p_argcount, r_error);
} else {
- r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
}
return ret;
@@ -968,7 +950,7 @@ void Object::cancel_delete() {
_predelete_ok = true;
}
-void Object::set_script_and_instance(const RefPtr &p_script, ScriptInstance *p_instance) {
+void Object::set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance) {
//this function is not meant to be used in any of these ways
ERR_FAIL_COND(p_script.is_null());
@@ -979,7 +961,7 @@ void Object::set_script_and_instance(const RefPtr &p_script, ScriptInstance *p_i
script_instance = p_instance;
}
-void Object::set_script(const RefPtr &p_script) {
+void Object::set_script(const Variant &p_script) {
if (script == p_script)
return;
@@ -990,7 +972,7 @@ void Object::set_script(const RefPtr &p_script) {
}
script = p_script;
- Ref<Script> s(script);
+ Ref<Script> s = script;
if (!s.is_null()) {
if (s->can_instance()) {
@@ -1017,12 +999,12 @@ void Object::set_script_instance(ScriptInstance *p_instance) {
script_instance = p_instance;
if (p_instance)
- script = p_instance->get_script().get_ref_ptr();
+ script = p_instance->get_script();
else
- script = RefPtr();
+ script = Variant();
}
-RefPtr Object::get_script() const {
+Variant Object::get_script() const {
return script;
}
@@ -1075,9 +1057,9 @@ Array Object::_get_method_list_bind() const {
return ret;
}
-PoolVector<String> Object::_get_meta_list_bind() const {
+Vector<String> Object::_get_meta_list_bind() const {
- PoolVector<String> _metaret;
+ Vector<String> _metaret;
List<Variant> keys;
metadata.get_key_list(&keys);
@@ -1103,7 +1085,7 @@ void Object::add_user_signal(const MethodInfo &p_signal) {
ERR_FAIL_COND_MSG(p_signal.name == "", "Signal name cannot be empty.");
ERR_FAIL_COND_MSG(ClassDB::has_signal(get_class_name(), p_signal.name), "User signal's name conflicts with a built-in signal of '" + get_class_name() + "'.");
ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), "Trying to add already existing signal '" + p_signal.name + "'.");
- Signal s;
+ SignalData s;
s.user = p_signal;
signal_map[p_signal.name] = s;
}
@@ -1118,23 +1100,22 @@ bool Object::_has_user_signal(const StringName &p_name) const {
struct _ObjectSignalDisconnectData {
StringName signal;
- Object *target;
- StringName method;
+ Callable callable;
};
-Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
+Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
ERR_FAIL_COND_V(p_argcount < 1, Variant());
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
- r_error.expected = Variant::STRING;
- ERR_FAIL_COND_V(p_args[0]->get_type() != Variant::STRING, Variant());
+ r_error.expected = Variant::STRING_NAME;
+ ERR_FAIL_COND_V(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING, Variant());
}
- r_error.error = Variant::CallError::CALL_OK;
+ r_error.error = Callable::CallError::CALL_OK;
StringName signal = *p_args[0];
@@ -1155,7 +1136,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
if (_block_signals)
return ERR_CANT_ACQUIRE_RESOURCE; //no emit, signals blocked
- Signal *s = signal_map.getptr(p_name);
+ SignalData *s = signal_map.getptr(p_name);
if (!s) {
#ifdef DEBUG_ENABLED
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_name);
@@ -1171,7 +1152,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
//copy on write will ensure that disconnecting the signal or even deleting the object will not affect the signal calling.
//this happens automatically and will not change the performance of calling.
//awesome, isn't it?
- VMap<Signal::Target, Signal::Slot> slot_map = s->slot_map;
+ VMap<Callable, SignalData::Slot> slot_map = s->slot_map;
int ssize = slot_map.size();
@@ -1185,7 +1166,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
const Connection &c = slot_map.getv(i).conn;
- Object *target = ObjectDB::get_instance(slot_map.getk(i)._id);
+ Object *target = c.callable.get_object();
if (!target) {
// Target might have been deleted during signal callback, this is expected and OK.
continue;
@@ -1210,22 +1191,23 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
}
if (c.flags & CONNECT_DEFERRED) {
- MessageQueue::get_singleton()->push_call(target->get_instance_id(), c.method, args, argc, true);
+ MessageQueue::get_singleton()->push_callable(c.callable, args, argc, true);
} else {
- Variant::CallError ce;
+ Callable::CallError ce;
_emitting = true;
- target->call(c.method, args, argc, ce);
+ Variant ret;
+ c.callable.call(args, argc, ret, ce);
_emitting = false;
- if (ce.error != Variant::CallError::CALL_OK) {
+ if (ce.error != Callable::CallError::CALL_OK) {
#ifdef DEBUG_ENABLED
if (c.flags & CONNECT_PERSIST && Engine::get_singleton()->is_editor_hint() && (script.is_null() || !Ref<Script>(script)->is_tool()))
continue;
#endif
- if (ce.error == Variant::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
+ if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
//most likely object is not initialized yet, do not throw error.
} else {
- ERR_PRINT("Error calling method from signal '" + String(p_name) + "': " + Variant::get_call_error_text(target, c.method, args, argc, ce) + ".");
+ ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(c.callable, args, argc, ce) + ".");
err = ERR_METHOD_NOT_FOUND;
}
}
@@ -1242,8 +1224,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
_ObjectSignalDisconnectData dd;
dd.signal = p_name;
- dd.target = target;
- dd.method = c.method;
+ dd.callable = c.callable;
disconnect_data.push_back(dd);
}
}
@@ -1251,7 +1232,8 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
while (!disconnect_data.empty()) {
const _ObjectSignalDisconnectData &dd = disconnect_data.front()->get();
- disconnect(dd.signal, dd.target, dd.method);
+
+ _disconnect(dd.signal, dd.callable);
disconnect_data.pop_front();
}
@@ -1323,15 +1305,8 @@ Array Object::_get_signal_connection_list(const String &p_signal) const {
for (List<Connection>::Element *E = conns.front(); E; E = E->next()) {
Connection &c = E->get();
- if (c.signal == p_signal) {
- Dictionary rc;
- rc["signal"] = c.signal;
- rc["method"] = c.method;
- rc["source"] = c.source;
- rc["target"] = c.target;
- rc["binds"] = c.binds;
- rc["flags"] = c.flags;
- ret.push_back(rc);
+ if (c.signal.get_name() == p_signal) {
+ ret.push_back(c);
}
}
@@ -1343,11 +1318,7 @@ Array Object::_get_incoming_connections() const {
Array ret;
int connections_amount = connections.size();
for (int idx_conn = 0; idx_conn < connections_amount; idx_conn++) {
- Dictionary conn_data;
- conn_data["source"] = connections[idx_conn].source;
- conn_data["signal_name"] = connections[idx_conn].signal;
- conn_data["method_name"] = connections[idx_conn].method;
- ret.push_back(conn_data);
+ ret.push_back(connections[idx_conn]);
}
return ret;
@@ -1381,7 +1352,7 @@ void Object::get_all_signal_connections(List<Connection> *p_connections) const {
while ((S = signal_map.next(S))) {
- const Signal *s = &signal_map[*S];
+ const SignalData *s = &signal_map[*S];
for (int i = 0; i < s->slot_map.size(); i++) {
@@ -1392,7 +1363,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 {
- const Signal *s = signal_map.getptr(p_signal);
+ const SignalData *s = signal_map.getptr(p_signal);
if (!s)
return; //nothing
@@ -1407,7 +1378,7 @@ int Object::get_persistent_signal_connection_count() const {
while ((S = signal_map.next(S))) {
- const Signal *s = &signal_map[*S];
+ const SignalData *s = &signal_map[*S];
for (int i = 0; i < s->slot_map.size(); i++) {
if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST) {
@@ -1426,11 +1397,18 @@ void Object::get_signals_connected_to_this(List<Connection> *p_connections) cons
}
}
-Error Object::connect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, const Vector<Variant> &p_binds, uint32_t p_flags) {
+Error Object::connect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, const Vector<Variant> &p_binds, uint32_t p_flags) {
+
+ return connect(p_signal, Callable(p_to_object, p_to_method), p_binds, p_flags);
+}
+Error Object::connect(const StringName &p_signal, const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) {
+
+ ERR_FAIL_COND_V(p_callable.is_null(), ERR_INVALID_PARAMETER);
- ERR_FAIL_NULL_V(p_to_object, ERR_INVALID_PARAMETER);
+ Object *target_object = p_callable.get_object();
+ ERR_FAIL_COND_V(!target_object, ERR_INVALID_PARAMETER);
- Signal *s = signal_map.getptr(p_signal);
+ SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
//check in script
@@ -1449,33 +1427,32 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str
#endif
}
- ERR_FAIL_COND_V_MSG(!signal_is_valid, ERR_INVALID_PARAMETER, "In Object of type '" + String(get_class()) + "': Attempt to connect nonexistent signal '" + p_signal + "' to method '" + p_to_object->get_class() + "." + p_to_method + "'.");
+ ERR_FAIL_COND_V_MSG(!signal_is_valid, ERR_INVALID_PARAMETER, "In Object of type '" + String(get_class()) + "': Attempt to connect nonexistent signal '" + p_signal + "' to callable '" + p_callable + "'.");
- signal_map[p_signal] = Signal();
+ signal_map[p_signal] = SignalData();
s = &signal_map[p_signal];
}
- Signal::Target target(p_to_object->get_instance_id(), p_to_method);
+ Callable target = p_callable;
+
if (s->slot_map.has(target)) {
if (p_flags & CONNECT_REFERENCE_COUNTED) {
s->slot_map[target].reference_count++;
return OK;
} else {
- ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object.");
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given callable '" + p_callable + "' in that object.");
}
}
- Signal::Slot slot;
+ SignalData::Slot slot;
Connection conn;
- conn.source = this;
- conn.target = p_to_object;
- conn.method = p_to_method;
- conn.signal = p_signal;
+ conn.callable = target;
+ conn.signal = ::Signal(this, p_signal);
conn.flags = p_flags;
conn.binds = p_binds;
slot.conn = conn;
- slot.cE = p_to_object->connections.push_back(conn);
+ slot.cE = target_object->connections.push_back(conn);
if (p_flags & CONNECT_REFERENCE_COUNTED) {
slot.reference_count = 1;
}
@@ -1485,10 +1462,15 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str
return OK;
}
-bool Object::is_connected(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) const {
+bool Object::is_connected_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) const {
+
+ return is_connected(p_signal, Callable(p_to_object, p_to_method));
+}
+
+bool Object::is_connected(const StringName &p_signal, const Callable &p_callable) const {
- ERR_FAIL_NULL_V(p_to_object, false);
- const Signal *s = signal_map.getptr(p_signal);
+ ERR_FAIL_COND_V(p_callable.is_null(), false);
+ const SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
if (signal_is_valid)
@@ -1500,28 +1482,35 @@ bool Object::is_connected(const StringName &p_signal, Object *p_to_object, const
ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + ".");
}
- Signal::Target target(p_to_object->get_instance_id(), p_to_method);
+ Callable target = p_callable;
return s->slot_map.has(target);
//const Map<Signal::Target,Signal::Slot>::Element *E = s->slot_map.find(target);
//return (E!=NULL);
}
-void Object::disconnect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) {
+void Object::disconnect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) {
- _disconnect(p_signal, p_to_object, p_to_method);
+ _disconnect(p_signal, Callable(p_to_object, p_to_method));
}
-void Object::_disconnect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, bool p_force) {
- ERR_FAIL_NULL(p_to_object);
- Signal *s = signal_map.getptr(p_signal);
- ERR_FAIL_COND_MSG(!s, vformat("Nonexistent signal '%s' in %s.", p_signal, to_string()));
+void Object::disconnect(const StringName &p_signal, const Callable &p_callable) {
+ _disconnect(p_signal, p_callable);
+}
- Signal::Target target(p_to_object->get_instance_id(), p_to_method);
+void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
- ERR_FAIL_COND_MSG(!s->slot_map.has(target), "Disconnecting nonexistent signal '" + p_signal + "', slot: " + itos(target._id) + ":" + target.method + ".");
+ ERR_FAIL_COND(p_callable.is_null());
- Signal::Slot *slot = &s->slot_map[target];
+ Object *target_object = p_callable.get_object();
+ ERR_FAIL_COND(!target_object);
+
+ SignalData *s = signal_map.getptr(p_signal);
+ ERR_FAIL_COND_MSG(!s, vformat("Nonexistent signal '%s' in %s.", p_signal, to_string()));
+
+ ERR_FAIL_COND_MSG(!s->slot_map.has(p_callable), "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + ".");
+
+ SignalData::Slot *slot = &s->slot_map[p_callable];
if (!p_force) {
slot->reference_count--; // by default is zero, if it was not referenced it will go below it
@@ -1530,8 +1519,8 @@ void Object::_disconnect(const StringName &p_signal, Object *p_to_object, const
}
}
- p_to_object->connections.erase(slot->cE);
- s->slot_map.erase(target);
+ target_object->connections.erase(slot->cE);
+ s->slot_map.erase(p_callable);
if (s->slot_map.empty() && ClassDB::has_signal(get_class_name(), p_signal)) {
//not user signal, delete
@@ -1680,7 +1669,7 @@ void Object::_bind_methods() {
{
MethodInfo mi;
mi.name = "emit_signal";
- mi.arguments.push_back(PropertyInfo(Variant::STRING, "signal"));
+ mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "signal"));
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "emit_signal", &Object::_emit_signal, mi, varray(), false);
}
@@ -1688,7 +1677,7 @@ void Object::_bind_methods() {
{
MethodInfo mi;
mi.name = "call";
- mi.arguments.push_back(PropertyInfo(Variant::STRING, "method"));
+ mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call", &Object::_call_bind, mi);
}
@@ -1696,7 +1685,7 @@ void Object::_bind_methods() {
{
MethodInfo mi;
mi.name = "call_deferred";
- mi.arguments.push_back(PropertyInfo(Variant::STRING, "method"));
+ mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_deferred", &Object::_call_deferred_bind, mi, varray(), false);
}
@@ -1711,9 +1700,9 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_signal_connection_list", "signal"), &Object::_get_signal_connection_list);
ClassDB::bind_method(D_METHOD("get_incoming_connections"), &Object::_get_incoming_connections);
- ClassDB::bind_method(D_METHOD("connect", "signal", "target", "method", "binds", "flags"), &Object::connect, DEFVAL(Array()), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("disconnect", "signal", "target", "method"), &Object::disconnect);
- ClassDB::bind_method(D_METHOD("is_connected", "signal", "target", "method"), &Object::is_connected);
+ ClassDB::bind_method(D_METHOD("connect", "signal", "callable", "binds", "flags"), &Object::connect, DEFVAL(Array()), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("disconnect", "signal", "callable"), &Object::disconnect);
+ ClassDB::bind_method(D_METHOD("is_connected", "signal", "callable"), &Object::is_connected);
ClassDB::bind_method(D_METHOD("set_block_signals", "enable"), &Object::set_block_signals);
ClassDB::bind_method(D_METHOD("is_blocking_signals"), &Object::is_blocking_signals);
@@ -1730,9 +1719,9 @@ void Object::_bind_methods() {
ADD_SIGNAL(MethodInfo("script_changed"));
BIND_VMETHOD(MethodInfo("_notification", PropertyInfo(Variant::INT, "what")));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_set", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value")));
+ BIND_VMETHOD(MethodInfo(Variant::BOOL, "_set", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value")));
#ifdef TOOLS_ENABLED
- MethodInfo miget("_get", PropertyInfo(Variant::STRING, "property"));
+ MethodInfo miget("_get", PropertyInfo(Variant::STRING_NAME, "property"));
miget.return_val.name = "Variant";
miget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
BIND_VMETHOD(miget);
@@ -1830,7 +1819,7 @@ Variant::Type Object::get_static_property_type_indexed(const Vector<StringName>
return Variant::NIL;
}
- Variant::CallError ce;
+ Callable::CallError ce;
Variant check = Variant::construct(t, NULL, 0, ce);
for (int i = 1; i < p_path.size(); i++) {
@@ -1911,8 +1900,8 @@ void Object::set_script_instance_binding(int p_script_language_index, void *p_da
_script_instance_bindings[p_script_language_index] = p_data;
}
-Object::Object() {
-
+void Object::_construct_object(bool p_reference) {
+ type_is_reference = p_reference;
_class_ptr = NULL;
_block_signals = false;
_predelete_ok = 0;
@@ -1933,6 +1922,14 @@ Object::Object() {
_lock_index.init(1);
#endif
}
+Object::Object(bool p_reference) {
+ _construct_object(p_reference);
+}
+
+Object::Object() {
+
+ _construct_object(false);
+}
Object::~Object() {
@@ -1949,15 +1946,15 @@ Object::~Object() {
while ((S = signal_map.next(NULL))) {
- Signal *s = &signal_map[*S];
+ SignalData *s = &signal_map[*S];
//brute force disconnect for performance
int slot_count = s->slot_map.size();
- const VMap<Signal::Target, Signal::Slot>::Pair *slot_list = s->slot_map.get_array();
+ const VMap<Callable, SignalData::Slot>::Pair *slot_list = s->slot_map.get_array();
for (int i = 0; i < slot_count; i++) {
- slot_list[i].value.conn.target->connections.erase(slot_list[i].value.cE);
+ slot_list[i].value.conn.callable.get_object()->connections.erase(slot_list[i].value.cE);
}
signal_map.erase(*S);
@@ -1967,7 +1964,7 @@ Object::~Object() {
while (connections.size()) {
Connection c = connections.front()->get();
- c.source->_disconnect(c.signal, c.target, c.method, true);
+ c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true);
}
ObjectDB::remove_instance(this);
@@ -1993,96 +1990,139 @@ void postinitialize_handler(Object *p_object) {
p_object->_postinitialize();
}
-HashMap<ObjectID, Object *> ObjectDB::instances;
-uint64_t ObjectDB::instance_counter = 1;
-HashMap<Object *, ObjectID, ObjectDB::ObjectPtrHash> ObjectDB::instance_checks;
-ObjectID ObjectDB::add_instance(Object *p_object) {
-
- ERR_FAIL_COND_V(p_object->get_instance_id().is_valid(), ObjectID());
-
- rw_lock->write_lock();
- ObjectID instance_id = ObjectID(++instance_counter);
- instances[instance_id] = p_object;
- instance_checks[p_object] = instance_id;
-
- rw_lock->write_unlock();
+void ObjectDB::debug_objects(DebugFunc p_func) {
- return instance_id;
+ spin_lock.lock();
+ for (uint32_t i = 0; i < slot_count; i++) {
+ uint32_t slot = object_slots[i].next_free;
+ p_func(object_slots[slot].object);
+ }
+ spin_lock.unlock();
}
-void ObjectDB::remove_instance(Object *p_object) {
+void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+}
- rw_lock->write_lock();
+SpinLock ObjectDB::spin_lock;
+uint32_t ObjectDB::slot_count = 0;
+uint32_t ObjectDB::slot_max = 0;
+ObjectDB::ObjectSlot *ObjectDB::object_slots = nullptr;
+uint64_t ObjectDB::validator_counter = 0;
- instances.erase(p_object->get_instance_id());
- instance_checks.erase(p_object);
+int ObjectDB::get_object_count() {
- rw_lock->write_unlock();
+ return slot_count;
}
-Object *ObjectDB::get_instance(ObjectID p_instance_id) {
- rw_lock->read_lock();
- Object **obj = instances.getptr(p_instance_id);
- rw_lock->read_unlock();
+ObjectID ObjectDB::add_instance(Object *p_object) {
- if (!obj)
- return NULL;
- return *obj;
-}
+ spin_lock.lock();
+ if (unlikely(slot_count == slot_max)) {
-void ObjectDB::debug_objects(DebugFunc p_func) {
+ CRASH_COND(slot_count == (1 << OBJECTDB_SLOT_MAX_COUNT_BITS));
- rw_lock->read_lock();
+ uint32_t new_slot_max = slot_max > 0 ? slot_max * 2 : 1;
+ object_slots = (ObjectSlot *)memrealloc(object_slots, sizeof(ObjectSlot) * new_slot_max);
+ for (uint32_t i = slot_max; i < new_slot_max; i++) {
+ object_slots[i].object = nullptr;
+ object_slots[i].is_reference = false;
+ object_slots[i].next_free = i;
+ object_slots[i].validator = 0;
+ }
+ slot_max = new_slot_max;
+ }
+
+ uint32_t slot = object_slots[slot_count].next_free;
+ if (object_slots[slot].object != nullptr) {
+ spin_lock.unlock();
+ ERR_FAIL_COND_V(object_slots[slot].object != nullptr, ObjectID());
+ }
+ object_slots[slot].object = p_object;
+ object_slots[slot].is_reference = p_object->is_reference();
+ validator_counter = (validator_counter + 1) & OBJECTDB_VALIDATOR_MASK;
+ if (unlikely(validator_counter == 0)) {
+ validator_counter = 1;
+ }
+ object_slots[slot].validator = validator_counter;
- const ObjectID *K = NULL;
- while ((K = instances.next(K))) {
+ uint64_t id = validator_counter;
+ id <<= OBJECTDB_SLOT_MAX_COUNT_BITS;
+ id |= uint64_t(slot);
- p_func(instances[*K]);
+ if (p_object->is_reference()) {
+ id |= OBJECTDB_REFERENCE_BIT;
}
- rw_lock->read_unlock();
-}
+ slot_count++;
-void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+ spin_lock.unlock();
+
+ return ObjectID(id);
}
-int ObjectDB::get_object_count() {
+void ObjectDB::remove_instance(Object *p_object) {
+ uint64_t t = p_object->get_instance_id();
+ uint32_t slot = t & OBJECTDB_SLOT_MAX_COUNT_MASK; //slot is always valid on valid object
- rw_lock->read_lock();
- int count = instances.size();
- rw_lock->read_unlock();
+ spin_lock.lock();
- return count;
-}
+#ifdef DEBUG_ENABLED
+
+ if (object_slots[slot].object != p_object) {
+ spin_lock.unlock();
+ ERR_FAIL_COND(object_slots[slot].object != p_object);
+ }
+ {
+ uint64_t validator = (t >> OBJECTDB_SLOT_MAX_COUNT_BITS) & OBJECTDB_VALIDATOR_MASK;
+ if (object_slots[slot].validator != validator) {
+ spin_lock.unlock();
+ ERR_FAIL_COND(object_slots[slot].validator != validator);
+ }
+ }
+
+#endif
+ //decrease slot count
+ slot_count--;
+ //set the free slot properly
+ object_slots[slot_count].next_free = slot;
+ //invalidate, so checks against it fail
+ object_slots[slot].validator = 0;
+ object_slots[slot].is_reference = false;
+ object_slots[slot].object = nullptr;
-RWLock *ObjectDB::rw_lock = NULL;
+ spin_lock.unlock();
+}
void ObjectDB::setup() {
- rw_lock = RWLock::create();
+ //nothing to do now
}
void ObjectDB::cleanup() {
- rw_lock->write_lock();
- if (instances.size()) {
+ if (slot_count > 0) {
+ spin_lock.lock();
WARN_PRINT("ObjectDB Instances still exist!");
if (OS::get_singleton()->is_stdout_verbose()) {
- const ObjectID *K = NULL;
- while ((K = instances.next(K))) {
+ for (uint32_t i = 0; i < slot_count; i++) {
+ uint32_t slot = object_slots[i].next_free;
+ Object *obj = object_slots[slot].object;
String node_name;
- if (instances[*K]->is_class("Node"))
- node_name = " - Node name: " + String(instances[*K]->call("get_name"));
- if (instances[*K]->is_class("Resource"))
- node_name = " - Resource name: " + String(instances[*K]->call("get_name")) + " Path: " + String(instances[*K]->call("get_path"));
- print_line("Leaked instance: " + String(instances[*K]->get_class()) + ":" + itos(*K) + node_name);
+ if (obj->is_class("Node"))
+ node_name = " - Node name: " + String(obj->call("get_name"));
+ if (obj->is_class("Resource"))
+ node_name = " - Resource name: " + String(obj->call("get_name")) + " Path: " + String(obj->call("get_path"));
+
+ uint64_t id = uint64_t(slot) | (uint64_t(object_slots[slot].validator) << OBJECTDB_VALIDATOR_BITS) | (object_slots[slot].is_reference ? OBJECTDB_REFERENCE_BIT : 0);
+ print_line("Leaked instance: " + String(obj->get_class()) + ":" + itos(id) + node_name);
}
}
+ spin_lock.unlock();
+ }
+
+ if (object_slots) {
+ memfree(object_slots);
}
- instances.clear();
- instance_checks.clear();
- rw_lock->write_unlock();
- memdelete(rw_lock);
}