diff options
Diffstat (limited to 'core/object')
-rw-r--r-- | core/object/SCsub | 5 | ||||
-rw-r--r-- | core/object/callable_method_pointer.h | 7 | ||||
-rw-r--r-- | core/object/class_db.cpp | 288 | ||||
-rw-r--r-- | core/object/class_db.h | 125 | ||||
-rw-r--r-- | core/object/make_virtuals.py | 161 | ||||
-rw-r--r-- | core/object/message_queue.cpp | 12 | ||||
-rw-r--r-- | core/object/method_bind.cpp | 29 | ||||
-rw-r--r-- | core/object/method_bind.h | 4 | ||||
-rw-r--r-- | core/object/object.cpp | 268 | ||||
-rw-r--r-- | core/object/object.h | 221 | ||||
-rw-r--r-- | core/object/object_id.h | 2 | ||||
-rw-r--r-- | core/object/ref_counted.cpp (renamed from core/object/reference.cpp) | 45 | ||||
-rw-r--r-- | core/object/ref_counted.h (renamed from core/object/reference.h) | 42 | ||||
-rw-r--r-- | core/object/script_language.cpp | 84 | ||||
-rw-r--r-- | core/object/script_language.h | 67 | ||||
-rw-r--r-- | core/object/undo_redo.cpp | 130 | ||||
-rw-r--r-- | core/object/undo_redo.h | 15 |
17 files changed, 1034 insertions, 471 deletions
diff --git a/core/object/SCsub b/core/object/SCsub index 5d429960e5..dc116aeb19 100644 --- a/core/object/SCsub +++ b/core/object/SCsub @@ -2,6 +2,11 @@ Import("env") +import make_virtuals +from platform_methods import run_in_subprocess + +env.CommandNoCache(["gdvirtual.gen.inc"], "make_virtuals.py", run_in_subprocess(make_virtuals.run)) + env_object = env.Clone() env_object.add_source_files(env.core_sources, "*.cpp") diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h index 115797a00c..8ba01be4e4 100644 --- a/core/object/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -32,7 +32,6 @@ #define CALLABLE_METHOD_POINTER_H #include "core/object/object.h" -#include "core/os/copymem.h" #include "core/templates/hashfuncs.h" #include "core/templates/simple_type.h" #include "core/variant/binder_common.h" @@ -98,7 +97,7 @@ public: } CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) { - zeromem(&data, sizeof(Data)); // Clear beforehand, may have padding bytes. + memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; #ifdef DEBUG_ENABLED data.object_id = p_instance->get_instance_id(); @@ -153,7 +152,7 @@ public: } CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) { - zeromem(&data, sizeof(Data)); // Clear beforehand, may have padding bytes. + memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; #ifdef DEBUG_ENABLED data.object_id = p_instance->get_instance_id(); @@ -208,7 +207,7 @@ public: } CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) { - zeromem(&data, sizeof(Data)); // Clear beforehand, may have padding bytes. + memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; #ifdef DEBUG_ENABLED data.object_id = p_instance->get_instance_id(); diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 6bc6d653d1..8ba46e49eb 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -359,9 +359,9 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { //must be alphabetically sorted for hash to compute names.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - ClassInfo *t = classes.getptr(E->get()); - ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E->get()) + "'."); + for (const StringName &E : names) { + ClassInfo *t = classes.getptr(E); + ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E) + "'."); if (t->api != p_api || !t->exposed) { continue; } @@ -388,8 +388,8 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { snames.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - MethodBind *mb = t->method_map[F->get()]; + for (const StringName &F : snames) { + MethodBind *mb = t->method_map[F]; hash = hash_djb2_one_64(mb->get_name().hash(), hash); hash = hash_djb2_one_64(mb->get_argument_count(), hash); hash = hash_djb2_one_64(mb->get_argument_type(-1), hash); //return @@ -426,9 +426,9 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { snames.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - hash = hash_djb2_one_64(F->get().hash(), hash); - hash = hash_djb2_one_64(t->constant_map[F->get()], hash); + for (const StringName &F : snames) { + hash = hash_djb2_one_64(F.hash(), hash); + hash = hash_djb2_one_64(t->constant_map[F], hash); } } @@ -444,9 +444,9 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { snames.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - MethodInfo &mi = t->signal_map[F->get()]; - hash = hash_djb2_one_64(F->get().hash(), hash); + for (const StringName &F : snames) { + MethodInfo &mi = t->signal_map[F]; + hash = hash_djb2_one_64(F.hash(), hash); for (int i = 0; i < mi.arguments.size(); i++) { hash = hash_djb2_one_64(mi.arguments[i].type, hash); } @@ -465,23 +465,23 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { snames.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - PropertySetGet *psg = t->property_setget.getptr(F->get()); + for (const StringName &F : snames) { + PropertySetGet *psg = t->property_setget.getptr(F); ERR_FAIL_COND_V(!psg, 0); - hash = hash_djb2_one_64(F->get().hash(), hash); + hash = hash_djb2_one_64(F.hash(), hash); hash = hash_djb2_one_64(psg->setter.hash(), hash); hash = hash_djb2_one_64(psg->getter.hash(), hash); } } //property list - for (List<PropertyInfo>::Element *F = t->property_list.front(); F; F = F->next()) { - hash = hash_djb2_one_64(F->get().name.hash(), hash); - hash = hash_djb2_one_64(F->get().type, hash); - hash = hash_djb2_one_64(F->get().hint, hash); - hash = hash_djb2_one_64(F->get().hint_string.hash(), hash); - hash = hash_djb2_one_64(F->get().usage, hash); + for (const PropertyInfo &F : t->property_list) { + hash = hash_djb2_one_64(F.name.hash(), hash); + hash = hash_djb2_one_64(F.type, hash); + hash = hash_djb2_one_64(F.hint, hash); + hash = hash_djb2_one_64(F.hint_string.hash(), hash); + hash = hash_djb2_one_64(F.usage, hash); } } @@ -501,12 +501,28 @@ void ClassDB::add_compatibility_class(const StringName &p_class, const StringNam compat_classes[p_class] = p_fallback; } -Object *ClassDB::instance(const StringName &p_class) { +thread_local bool initializing_with_extension = false; +thread_local ObjectNativeExtension *initializing_extension = nullptr; +thread_local GDExtensionClassInstancePtr initializing_extension_instance = nullptr; + +void ClassDB::instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance, Object *p_base) { + if (initializing_with_extension) { + *r_extension = initializing_extension; + *r_extension_instance = initializing_extension_instance; + initializing_with_extension = false; + initializing_extension->set_object_instance(*r_extension_instance, p_base); + } else { + *r_extension = nullptr; + *r_extension_instance = nullptr; + } +} + +Object *ClassDB::instantiate(const StringName &p_class) { ClassInfo *ti; { OBJTYPE_RLOCK; ti = classes.getptr(p_class); - if (!ti || ti->disabled || !ti->creation_func) { + if (!ti || ti->disabled || !ti->creation_func || (ti->native_extension && !ti->native_extension->create_instance)) { if (compat_classes.has(p_class)) { ti = classes.getptr(compat_classes[p_class]); } @@ -521,10 +537,24 @@ Object *ClassDB::instance(const StringName &p_class) { return nullptr; } #endif + if (ti->native_extension) { + initializing_with_extension = true; + initializing_extension = ti->native_extension; + initializing_extension_instance = ti->native_extension->create_instance(ti->native_extension->class_userdata); + } return ti->creation_func(); } -bool ClassDB::can_instance(const StringName &p_class) { +Object *ClassDB::construct_object(Object *(*p_create_func)(), ObjectNativeExtension *p_extension) { + if (p_extension) { + initializing_with_extension = true; + initializing_extension = p_extension; + initializing_extension_instance = p_extension->create_instance(p_extension->class_userdata); + } + return p_create_func(); +} + +bool ClassDB::can_instantiate(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); @@ -534,7 +564,7 @@ bool ClassDB::can_instance(const StringName &p_class) { return false; } #endif - return (!ti->disabled && ti->creation_func != nullptr); + return (!ti->disabled && ti->creation_func != nullptr && !(ti->native_extension && !ti->native_extension->create_instance)); } void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) { @@ -582,7 +612,7 @@ static MethodInfo info_from_bind(MethodBind *p_method) { } #endif -void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) { +void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); @@ -599,16 +629,16 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b #ifdef DEBUG_METHODS_ENABLED - for (List<MethodInfo>::Element *E = type->virtual_methods.front(); E; E = E->next()) { - p_methods->push_back(E->get()); + for (const MethodInfo &E : type->virtual_methods) { + p_methods->push_back(E); } - for (List<StringName>::Element *E = type->method_order.front(); E; E = E->next()) { - if (p_exclude_from_properties && type->methods_in_properties.has(E->get())) { + for (const StringName &E : type->method_order) { + if (p_exclude_from_properties && type->methods_in_properties.has(E)) { continue; } - MethodBind *method = type->method_map.get(E->get()); + MethodBind *method = type->method_map.get(E); MethodInfo minfo = info_from_bind(method); p_methods->push_back(minfo); @@ -635,7 +665,7 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b } } -bool ClassDB::get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, bool p_no_inheritance, bool p_exclude_from_properties) { +bool ClassDB::get_method_info(const StringName &p_class, const StringName &p_method, MethodInfo *r_info, bool p_no_inheritance, bool p_exclude_from_properties) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); @@ -686,7 +716,7 @@ bool ClassDB::get_method_info(StringName p_class, StringName p_method, MethodInf return false; } -MethodBind *ClassDB::get_method(StringName p_class, StringName p_name) { +MethodBind *ClassDB::get_method(const StringName &p_class, const StringName &p_name) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); @@ -743,8 +773,8 @@ void ClassDB::get_integer_constant_list(const StringName &p_class, List<String> while (type) { #ifdef DEBUG_METHODS_ENABLED - for (List<StringName>::Element *E = type->constant_order.front(); E; E = E->next()) { - p_constants->push_back(E->get()); + for (const StringName &E : type->constant_order) { + p_constants->push_back(E); } #else const StringName *K = nullptr; @@ -871,6 +901,32 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_ } } +void ClassDB::set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values) { + OBJTYPE_RLOCK; +#ifdef DEBUG_METHODS_ENABLED + ClassInfo *type = classes.getptr(p_class); + + ERR_FAIL_COND(!type); + + type->method_error_values[p_method] = p_values; +#endif +} + +Vector<Error> ClassDB::get_method_error_return_values(const StringName &p_class, const StringName &p_method) { +#ifdef DEBUG_METHODS_ENABLED + ClassInfo *type = classes.getptr(p_class); + + ERR_FAIL_COND_V(!type, Vector<Error>()); + + if (!type->method_error_values.has(p_method)) { + return Vector<Error>(); + } + return type->method_error_values[p_method]; +#else + return Vector<Error>(); +#endif +} + bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { OBJTYPE_RLOCK; @@ -890,7 +946,7 @@ bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool return false; } -void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { +void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); @@ -909,7 +965,7 @@ void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { type->signal_map[sname] = p_signal; } -void ClassDB::get_signal_list(StringName p_class, List<MethodInfo> *p_signals, bool p_no_inheritance) { +void ClassDB::get_signal_list(const StringName &p_class, List<MethodInfo> *p_signals, bool p_no_inheritance) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); @@ -931,7 +987,7 @@ void ClassDB::get_signal_list(StringName p_class, List<MethodInfo> *p_signals, b } } -bool ClassDB::has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance) { +bool ClassDB::has_signal(const StringName &p_class, const StringName &p_signal, bool p_no_inheritance) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; @@ -948,7 +1004,7 @@ bool ClassDB::has_signal(StringName p_class, StringName p_signal, bool p_no_inhe return false; } -bool ClassDB::get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal) { +bool ClassDB::get_signal(const StringName &p_class, const StringName &p_signal, MethodInfo *r_signal) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; @@ -965,7 +1021,7 @@ bool ClassDB::get_signal(StringName p_class, StringName p_signal, MethodInfo *r_ return false; } -void ClassDB::add_property_group(StringName p_class, const String &p_name, const String &p_prefix) { +void ClassDB::add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); @@ -973,7 +1029,7 @@ void ClassDB::add_property_group(StringName p_class, const String &p_name, const type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_GROUP)); } -void ClassDB::add_property_subgroup(StringName p_class, const String &p_name, const String &p_prefix) { +void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); @@ -981,11 +1037,23 @@ void ClassDB::add_property_subgroup(StringName p_class, const String &p_name, co type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_SUBGROUP)); } +void ClassDB::add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage) { + add_property(p_class, PropertyInfo(Variant::INT, p_count_property, PROPERTY_HINT_NONE, "", p_count_usage | PROPERTY_USAGE_ARRAY, vformat("%s,%s", p_label, p_array_element_prefix)), p_count_setter, p_count_getter); +} + +void ClassDB::add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix) { + OBJTYPE_WLOCK; + ClassInfo *type = classes.getptr(p_class); + ERR_FAIL_COND(!type); + + type->property_list.push_back(PropertyInfo(Variant::NIL, p_path, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, p_array_element_prefix)); +} + // NOTE: For implementation simplicity reasons, this method doesn't allow setters to have optional arguments at the end. -void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) { - lock->read_lock(); +void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) { + lock.read_lock(); ClassInfo *type = classes.getptr(p_class); - lock->read_unlock(); + lock.read_unlock(); ERR_FAIL_COND(!type); @@ -1040,26 +1108,41 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons type->property_setget[p_pinfo.name] = psg; } -void ClassDB::set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default) { +void ClassDB::set_property_default_value(const StringName &p_class, const StringName &p_name, const Variant &p_default) { if (!default_values.has(p_class)) { default_values[p_class] = HashMap<StringName, Variant>(); } default_values[p_class][p_name] = p_default; } -void ClassDB::get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance, const Object *p_validator) { +void ClassDB::add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property) { +#ifdef TOOLS_ENABLED + OBJTYPE_WLOCK; + ClassInfo *type = classes.getptr(p_class); + ERR_FAIL_COND(!type); + + ERR_FAIL_COND(!type->property_map.has(p_property)); + ERR_FAIL_COND(!type->property_map.has(p_linked_property)); + + PropertyInfo &pinfo = type->property_map[p_property]; + pinfo.linked_properties.push_back(p_linked_property); +#endif +} + +void ClassDB::get_property_list(const StringName &p_class, List<PropertyInfo> *p_list, bool p_no_inheritance, const Object *p_validator) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { - for (List<PropertyInfo>::Element *E = check->property_list.front(); E; E = E->next()) { + for (const PropertyInfo &pi : check->property_list) { if (p_validator) { - PropertyInfo pi = E->get(); - p_validator->_validate_property(pi); - p_list->push_back(pi); + // Making a copy as we may modify it. + PropertyInfo pi_mut = pi; + p_validator->_validate_property(pi_mut); + p_list->push_back(pi_mut); } else { - p_list->push_back(E->get()); + p_list->push_back(pi); } } @@ -1070,7 +1153,7 @@ void ClassDB::get_property_list(StringName p_class, List<PropertyInfo> *p_list, } } -bool ClassDB::get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) { +bool ClassDB::get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) { OBJTYPE_RLOCK; ClassInfo *check = classes.getptr(p_class); @@ -1095,6 +1178,8 @@ bool ClassDB::get_property_info(StringName p_class, StringName p_property, Prope } bool ClassDB::set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid) { + ERR_FAIL_NULL_V(p_object, false); + ClassInfo *type = classes.getptr(p_object->get_class_name()); ClassInfo *check = type; while (check) { @@ -1142,6 +1227,8 @@ bool ClassDB::set_property(Object *p_object, const StringName &p_property, const } bool ClassDB::get_property(Object *p_object, const StringName &p_property, Variant &r_value) { + ERR_FAIL_NULL_V(p_object, false); + ClassInfo *type = classes.getptr(p_object->get_class_name()); ClassInfo *check = type; while (check) { @@ -1234,7 +1321,7 @@ Variant::Type ClassDB::get_property_type(const StringName &p_class, const String return Variant::NIL; } -StringName ClassDB::get_property_setter(StringName p_class, const StringName &p_property) { +StringName ClassDB::get_property_setter(const StringName &p_class, const StringName &p_property) { ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { @@ -1249,7 +1336,7 @@ StringName ClassDB::get_property_setter(StringName p_class, const StringName &p_ return StringName(); } -StringName ClassDB::get_property_getter(StringName p_class, const StringName &p_property) { +StringName ClassDB::get_property_getter(const StringName &p_class, const StringName &p_property) { ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { @@ -1281,7 +1368,7 @@ bool ClassDB::has_property(const StringName &p_class, const StringName &p_proper return false; } -void ClassDB::set_method_flags(StringName p_class, StringName p_method, int p_flags) { +void ClassDB::set_method_flags(const StringName &p_class, const StringName &p_method, int p_flags) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; @@ -1290,7 +1377,7 @@ void ClassDB::set_method_flags(StringName p_class, StringName p_method, int p_fl check->method_map[p_method]->set_hint_flags(p_flags); } -bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inheritance) { +bool ClassDB::has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance) { ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { @@ -1306,6 +1393,24 @@ bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inhe return false; } +void ClassDB::bind_method_custom(const StringName &p_class, MethodBind *p_method) { + ClassInfo *type = classes.getptr(p_class); + if (!type) { + ERR_FAIL_MSG("Couldn't bind custom method '" + p_method->get_name() + "' for instance '" + p_class + "'."); + } + + if (type->method_map.has(p_method->get_name())) { + // overloading not supported + ERR_FAIL_MSG("Method already bound '" + p_class + "::" + p_method->get_name() + "'."); + } + +#ifdef DEBUG_METHODS_ENABLED + type->method_order.push_back(p_method->get_name()); +#endif + + type->method_map[p_method->get_name()] = p_method; +} + #ifdef DEBUG_METHODS_ENABLED MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount) { StringName mdname = method_name.name; @@ -1363,7 +1468,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c return p_bind; } -void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual) { +void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual, const Vector<String> &p_arg_names, bool p_object_core) { ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); OBJTYPE_WLOCK; @@ -1373,6 +1478,19 @@ void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_ if (p_virtual) { mi.flags |= METHOD_FLAG_VIRTUAL; } + if (p_object_core) { + mi.flags |= METHOD_FLAG_OBJECT_CORE; + } + if (p_arg_names.size()) { + if (p_arg_names.size() != mi.arguments.size()) { + WARN_PRINT("Mismatch argument name count for virtual function: " + String(p_class) + "::" + p_method.name); + } else { + for (int i = 0; i < p_arg_names.size(); i++) { + mi.arguments[i].name = p_arg_names[i]; + } + } + } + classes[p_class].virtual_methods.push_back(mi); classes[p_class].virtual_methods_map[p_method.name] = mi; @@ -1387,8 +1505,8 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { - for (List<MethodInfo>::Element *E = check->virtual_methods.front(); E; E = E->next()) { - p_methods->push_back(E->get()); + for (const MethodInfo &E : check->virtual_methods) { + p_methods->push_back(E); } if (p_no_inheritance) { @@ -1400,14 +1518,14 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p #endif } -void ClassDB::set_class_enabled(StringName p_class, bool p_enable) { +void ClassDB::set_class_enabled(const StringName &p_class, bool p_enable) { OBJTYPE_WLOCK; ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); classes[p_class].disabled = !p_enable; } -bool ClassDB::is_class_enabled(StringName p_class) { +bool ClassDB::is_class_enabled(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); @@ -1421,7 +1539,7 @@ bool ClassDB::is_class_enabled(StringName p_class) { return !ti->disabled; } -bool ClassDB::is_class_exposed(StringName p_class) { +bool ClassDB::is_class_exposed(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); @@ -1454,6 +1572,10 @@ void ClassDB::get_resource_base_extensions(List<String> *p_extensions) { } } +bool ClassDB::is_resource_extension(const StringName &p_extension) { + return resource_base_extensions.has(p_extension); +} + void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p_extensions) { const StringName *K = nullptr; @@ -1480,19 +1602,19 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con if (Engine::get_singleton()->has_singleton(p_class)) { c = Engine::get_singleton()->get_singleton_object(p_class); cleanup_c = false; - } else if (ClassDB::can_instance(p_class)) { - c = ClassDB::instance(p_class); + } else if (ClassDB::can_instantiate(p_class)) { + c = ClassDB::instantiate(p_class); cleanup_c = true; } if (c) { List<PropertyInfo> plist; c->get_property_list(&plist); - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (E->get().usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) { - if (!default_values[p_class].has(E->get().name)) { - Variant v = c->get(E->get().name); - default_values[p_class][E->get().name] = v; + for (const PropertyInfo &E : plist) { + if (E.usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) { + if (!default_values[p_class].has(E.name)) { + Variant v = c->get(E.name); + default_values[p_class][E.name] = v; } } } @@ -1541,12 +1663,34 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con return var; } -RWLock *ClassDB::lock = nullptr; +void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) { + GLOBAL_LOCK_FUNCTION; + + ERR_FAIL_COND_MSG(classes.has(p_extension->class_name), "Class already registered: " + String(p_extension->class_name)); + ERR_FAIL_COND_MSG(!classes.has(p_extension->parent_class_name), "Parent class name for extension class not found: " + String(p_extension->parent_class_name)); + + ClassInfo *parent = classes.getptr(p_extension->parent_class_name); + + ClassInfo c; + c.api = p_extension->editor_class ? API_EDITOR_EXTENSION : API_EXTENSION; + c.native_extension = p_extension; + c.name = p_extension->class_name; + c.creation_func = parent->creation_func; + c.inherits = parent->name; + c.class_ptr = parent->class_ptr; + c.inherits_ptr = parent; + c.exposed = true; -void ClassDB::init() { - lock = RWLock::create(); + classes[p_extension->class_name] = c; } +void ClassDB::unregister_extension_class(const StringName &p_class) { + ERR_FAIL_COND(!classes.has(p_class)); + classes.erase(p_class); +} + +RWLock ClassDB::lock; + void ClassDB::cleanup_defaults() { default_values.clear(); default_values_cached.clear(); @@ -1568,8 +1712,6 @@ void ClassDB::cleanup() { classes.clear(); resource_base_extensions.clear(); compat_classes.clear(); - - memdelete(lock); } // diff --git a/core/object/class_db.h b/core/object/class_db.h index 0591676b92..3a1cbf8559 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -97,6 +97,8 @@ public: enum APIType { API_CORE, API_EDITOR, + API_EXTENSION, + API_EDITOR_EXTENSION, API_NONE }; @@ -115,6 +117,8 @@ public: ClassInfo *inherits_ptr = nullptr; void *class_ptr = nullptr; + ObjectNativeExtension *native_extension = nullptr; + HashMap<StringName, MethodBind *> method_map; HashMap<StringName, int> constant_map; HashMap<StringName, List<StringName>> enum_map; @@ -128,6 +132,7 @@ public: List<MethodInfo> virtual_methods; Map<StringName, MethodInfo> virtual_methods_map; StringName category; + Map<StringName, Vector<Error>> method_error_values; #endif HashMap<StringName, PropertySetGet> property_setget; @@ -146,7 +151,7 @@ public: return memnew(T); } - static RWLock *lock; + static RWLock lock; static HashMap<StringName, ClassInfo> classes; static HashMap<StringName, StringName> resource_base_extensions; static HashMap<StringName, StringName> compat_classes; @@ -199,6 +204,9 @@ public: //nothing } + static void register_extension_class(ObjectNativeExtension *p_extension); + static void unregister_extension_class(const StringName &p_class); + template <class T> static Object *_create_ptr_func() { return T::create(); @@ -224,8 +232,11 @@ public: static StringName get_compatibility_remapped_class(const StringName &p_class); static bool class_exists(const StringName &p_class); static bool is_parent_class(const StringName &p_class, const StringName &p_inherits); - static bool can_instance(const StringName &p_class); - static Object *instance(const StringName &p_class); + static bool can_instantiate(const StringName &p_class); + static Object *instantiate(const StringName &p_class); + static Object *construct_object(Object *(*p_create_func)(), ObjectNativeExtension *p_extension); + static void instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance, Object *p_base); + static APIType get_api_type(const StringName &p_class); static uint64_t get_api_hash(APIType p_api); @@ -302,7 +313,7 @@ public: } template <class M> - static MethodBind *bind_vararg_method(uint32_t p_flags, StringName p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const Vector<Variant> &p_default_args = Vector<Variant>(), bool p_return_nil_is_variant = true) { + static MethodBind *bind_vararg_method(uint32_t p_flags, const StringName &p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const Vector<Variant> &p_default_args = Vector<Variant>(), bool p_return_nil_is_variant = true) { GLOBAL_LOCK_FUNCTION; MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant); @@ -321,7 +332,7 @@ public: if (type->method_map.has(p_name)) { memdelete(bind); - // overloading not supported + // Overloading not supported ERR_FAIL_V_MSG(nullptr, "Method already bound: " + instance_type + "::" + p_name + "."); } type->method_map[p_name] = bind; @@ -334,33 +345,38 @@ public: return bind; } - static void add_signal(StringName p_class, const MethodInfo &p_signal); - static bool has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance = false); - static bool get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal); - static void get_signal_list(StringName p_class, List<MethodInfo> *p_signals, bool p_no_inheritance = false); - - static void add_property_group(StringName p_class, const String &p_name, const String &p_prefix = ""); - static void add_property_subgroup(StringName p_class, const String &p_name, const String &p_prefix = ""); - static void add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1); - static void set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default); - static void get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr); - static bool get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr); + static void bind_method_custom(const StringName &p_class, MethodBind *p_method); + + static void add_signal(const StringName &p_class, const MethodInfo &p_signal); + static bool has_signal(const StringName &p_class, const StringName &p_signal, bool p_no_inheritance = false); + static bool get_signal(const StringName &p_class, const StringName &p_signal, MethodInfo *r_signal); + static void get_signal_list(const StringName &p_class, List<MethodInfo> *p_signals, bool p_no_inheritance = false); + + static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix = ""); + static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix = ""); + static void add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage = PROPERTY_USAGE_EDITOR); + static void add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix); + static void add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1); + static void set_property_default_value(const StringName &p_class, const StringName &p_name, const Variant &p_default); + static void add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property); + static void get_property_list(const StringName &p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr); + static bool get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr); static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = nullptr); static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value); static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false); static int get_property_index(const StringName &p_class, const StringName &p_property, bool *r_is_valid = nullptr); static Variant::Type get_property_type(const StringName &p_class, const StringName &p_property, bool *r_is_valid = nullptr); - static StringName get_property_setter(StringName p_class, const StringName &p_property); - static StringName get_property_getter(StringName p_class, const StringName &p_property); + static StringName get_property_setter(const StringName &p_class, const StringName &p_property); + static StringName get_property_getter(const StringName &p_class, const StringName &p_property); - static bool has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false); - static void set_method_flags(StringName p_class, StringName p_method, int p_flags); + static bool has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false); + static void set_method_flags(const StringName &p_class, const StringName &p_method, int p_flags); - static void get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false); - static bool get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false); - static MethodBind *get_method(StringName p_class, StringName p_name); + static void get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false); + static bool get_method_info(const StringName &p_class, const StringName &p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false); + static MethodBind *get_method(const StringName &p_class, const StringName &p_name); - static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true); + static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true, const Vector<String> &p_arg_names = Vector<String>(), bool p_object_core = false); static void get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false); static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int p_constant); @@ -373,21 +389,23 @@ public: static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false); static bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false); + static void set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values); + static Vector<Error> get_method_error_return_values(const StringName &p_class, const StringName &p_method); static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid = nullptr); static StringName get_category(const StringName &p_node); - static void set_class_enabled(StringName p_class, bool p_enable); - static bool is_class_enabled(StringName p_class); + static void set_class_enabled(const StringName &p_class, bool p_enable); + static bool is_class_enabled(const StringName &p_class); - static bool is_class_exposed(StringName p_class); + static bool is_class_exposed(const StringName &p_class); static void add_resource_base_extension(const StringName &p_extension, const StringName &p_class); static void get_resource_base_extensions(List<String> *p_extensions); static void get_extensions_for_type(const StringName &p_class, List<String> *p_extensions); + static bool is_resource_extension(const StringName &p_extension); static void add_compatibility_class(const StringName &p_class, const StringName &p_fallback); - static void init(); static void set_current_api(APIType p_api); static APIType get_current_api(); @@ -398,30 +416,55 @@ public: #ifdef DEBUG_METHODS_ENABLED #define BIND_CONSTANT(m_constant) \ - ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); + ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); #define BIND_ENUM_CONSTANT(m_constant) \ - ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant); - -#else + ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant); -#define BIND_CONSTANT(m_constant) \ - ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); +_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr) { +} -#define BIND_ENUM_CONSTANT(m_constant) \ - ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); +_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr, const Error &p_err) { + arr.push_back(p_err); +} -#endif +template <class... P> +_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr, const Error &p_err, P... p_args) { + arr.push_back(p_err); + errarray_add_str(arr, p_args...); +} -#ifdef TOOLS_ENABLED +template <class... P> +_FORCE_INLINE_ Vector<Error> errarray(P... p_args) { + Vector<Error> arr; + errarray_add_str(arr, p_args...); + return arr; +} -#define BIND_VMETHOD(m_method) \ - ClassDB::add_virtual_method(get_class_static(), m_method); +#define BIND_METHOD_ERR_RETURN_DOC(m_method, ...) \ + ::ClassDB::set_method_error_return_values(get_class_static(), m_method, errarray(__VA_ARGS__)); #else -#define BIND_VMETHOD(m_method) +#define BIND_CONSTANT(m_constant) \ + ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); + +#define BIND_ENUM_CONSTANT(m_constant) \ + ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); + +#define BIND_METHOD_ERR_RETURN_DOC(m_method, ...) #endif +#define GDREGISTER_CLASS(m_class) \ + if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \ + ::ClassDB::register_class<m_class>(); \ + } +#define GDREGISTER_VIRTUAL_CLASS(m_class) \ + if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \ + ::ClassDB::register_virtual_class<m_class>(); \ + } + +#include "core/disabled_classes.gen.h" + #endif // CLASS_DB_H diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py new file mode 100644 index 0000000000..86c2891e5d --- /dev/null +++ b/core/object/make_virtuals.py @@ -0,0 +1,161 @@ +proto = """ +#define GDVIRTUAL$VER($RET m_name $ARG) \\ +StringName _gdvirtual_##m_name##_sn = #m_name;\\ +GDNativeExtensionClassCallVirtual _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\ +_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\ + ScriptInstance *script_instance = ((Object*)(this))->get_script_instance();\\ + if (script_instance) {\\ + Callable::CallError ce; \\ + $CALLSIARGS\\ + $CALLSIBEGINscript_instance->call(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\ + if (ce.error == Callable::CallError::CALL_OK) {\\ + $CALLSIRET\\ + return true;\\ + } \\ + }\\ + if (_gdvirtual_##m_name) {\\ + $CALLPTRARGS\\ + $CALLPTRRETDEF\\ + _gdvirtual_##m_name(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\ + $CALLPTRRET\\ + return true;\\ + }\\ +\\ + return false;\\ +}\\ +_FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\ + ScriptInstance *script_instance = ((Object*)(this))->get_script_instance();\\ + if (script_instance) {\\ + return script_instance->has_method(_gdvirtual_##m_name##_sn);\\ + }\\ + if (_gdvirtual_##m_name) {\\ + return true;\\ + }\\ + return false;\\ +}\\ +\\ +_FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() { \\ + MethodInfo method_info;\\ + method_info.name = #m_name;\\ + method_info.flags = METHOD_FLAG_VIRTUAL;\\ + $FILL_METHOD_INFO\\ + return method_info;\\ +} + +""" + + +def generate_version(argcount, const=False, returns=False): + s = proto + sproto = str(argcount) + method_info = "" + if returns: + sproto += "R" + s = s.replace("$RET", "m_ret, ") + s = s.replace("$CALLPTRRETDEF", "PtrToArg<m_ret>::EncodeT ret;") + method_info += "\tmethod_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n" + else: + s = s.replace("$RET", "") + s = s.replace("$CALLPTRRETDEF", "") + + if const: + sproto += "C" + s = s.replace("$CONST", "const") + method_info += "\tmethod_info.flags|=METHOD_FLAG_CONST;\\\n" + else: + s = s.replace("$CONST", "") + + s = s.replace("$VER", sproto) + argtext = "" + callargtext = "" + callsiargs = "" + callsiargptrs = "" + callptrargsptr = "" + if argcount > 0: + argtext += ", " + callsiargs = "Variant vargs[" + str(argcount) + "]={" + callsiargptrs = "\t\tconst Variant *vargptrs[" + str(argcount) + "]={" + callptrargsptr = "\t\tconst GDNativeTypePtr argptrs[" + str(argcount) + "]={" + callptrargs = "" + for i in range(argcount): + if i > 0: + argtext += ", " + callargtext += ", " + callsiargs += ", " + callsiargptrs += ", " + callptrargs += "\t\t" + callptrargsptr += ", " + argtext += "m_type" + str(i + 1) + callargtext += "m_type" + str(i + 1) + " arg" + str(i + 1) + callsiargs += "Variant(arg" + str(i + 1) + ")" + callsiargptrs += "&vargs[" + str(i) + "]" + callptrargs += ( + "PtrToArg<m_type" + str(i + 1) + ">::EncodeT argval" + str(i + 1) + " = arg" + str(i + 1) + ";\\\n" + ) + callptrargsptr += "&argval" + str(i + 1) + method_info += "\tmethod_info.arguments.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::get_class_info());\\\n" + + if argcount: + callsiargs += "};\\\n" + callsiargptrs += "};\\\n" + s = s.replace("$CALLSIARGS", callsiargs + callsiargptrs) + s = s.replace("$CALLSIARGPASS", "(const Variant **)vargptrs," + str(argcount)) + callptrargsptr += "};\\\n" + s = s.replace("$CALLPTRARGS", callptrargs + callptrargsptr) + s = s.replace("$CALLPTRARGPASS", "(const GDNativeTypePtr*)argptrs") + else: + s = s.replace("$CALLSIARGS", "") + s = s.replace("$CALLSIARGPASS", "nullptr, 0") + s = s.replace("$CALLPTRARGS", "") + s = s.replace("$CALLPTRARGPASS", "nullptr") + + if returns: + if argcount > 0: + callargtext += "," + callargtext += " m_ret& r_ret" + s = s.replace("$CALLSIBEGIN", "Variant ret = ") + s = s.replace("$CALLSIRET", "r_ret = ret;") + s = s.replace("$CALLPTRRETPASS", "&ret") + s = s.replace("$CALLPTRRET", "r_ret = ret;") + else: + s = s.replace("$CALLSIBEGIN", "") + s = s.replace("$CALLSIRET", "") + s = s.replace("$CALLPTRRETPASS", "nullptr") + s = s.replace("$CALLPTRRET", "") + + s = s.replace("$ARG", argtext) + s = s.replace("$CALLARGS", callargtext) + s = s.replace("$FILL_METHOD_INFO", method_info) + + return s + + +def run(target, source, env): + + max_versions = 12 + + txt = """ +#ifndef GDVIRTUAL_GEN_H +#define GDVIRTUAL_GEN_H + + +""" + + for i in range(max_versions + 1): + + txt += "/* " + str(i) + " Arguments */\n\n" + txt += generate_version(i, False, False) + txt += generate_version(i, False, True) + txt += generate_version(i, True, False) + txt += generate_version(i, True, True) + + txt += "#endif" + + with open(target[0], "w") as f: + f.write(txt) + + +if __name__ == "__main__": + from platform_methods import subprocess_main + + subprocess_main(globals()) diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp index 4751c69f1e..736e940846 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -227,16 +227,16 @@ void MessageQueue::statistics() { print_line("TOTAL BYTES: " + itos(buffer_end)); print_line("NULL count: " + itos(null_count)); - for (Map<StringName, int>::Element *E = set_count.front(); E; E = E->next()) { - print_line("SET " + E->key() + ": " + itos(E->get())); + for (const KeyValue<StringName, int> &E : set_count) { + print_line("SET " + E.key + ": " + itos(E.value)); } - for (Map<Callable, int>::Element *E = call_count.front(); E; E = E->next()) { - print_line("CALL " + E->key() + ": " + itos(E->get())); + for (const KeyValue<Callable, int> &E : call_count) { + print_line("CALL " + E.key + ": " + itos(E.value)); } - for (Map<int, int>::Element *E = notify_count.front(); E; E = E->next()) { - print_line("NOTIFY " + itos(E->key()) + ": " + itos(E->get())); + for (const KeyValue<int, int> &E : notify_count) { + print_line("NOTIFY " + itos(E.key) + ": " + itos(E.value)); } } diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp index 9c5ed60708..c53104fe3f 100644 --- a/core/object/method_bind.cpp +++ b/core/object/method_bind.cpp @@ -34,6 +34,35 @@ #include "method_bind.h" +uint32_t MethodBind::get_hash() const { + uint32_t hash = hash_djb2_one_32(has_return() ? 1 : 0); + hash = hash_djb2_one_32(get_argument_count(), hash); + +#ifndef _MSC_VER +#warning This needs proper class name and argument type for hashing +#endif +#if 0 + + for (int i = (has_return() ? -1 : 0); i < get_argument_count(); i++) { + PropertyInfo pi = i == -1 ? get_return_info() : get_argument_info(i); + hash = hash_djb2_one_32(get_argument_type(i), hash); + if (pi.class_name != StringName()) { + hash = hash_djb2_one_32(pi.class_name.operator String().hash(), hash); + } + } +#endif + hash = hash_djb2_one_32(get_default_argument_count(), hash); + for (int i = 0; i < get_default_argument_count(); i++) { + Variant v = get_default_argument(i); + hash = hash_djb2_one_32(v.hash(), hash); + } + + hash = hash_djb2_one_32(is_const(), hash); + hash = hash_djb2_one_32(is_vararg(), hash); + + return hash; +} + #ifdef DEBUG_METHODS_ENABLED PropertyInfo MethodBind::get_argument_info(int p_argument) const { ERR_FAIL_INDEX_V(p_argument, get_argument_count(), PropertyInfo()); diff --git a/core/object/method_bind.h b/core/object/method_bind.h index 7cf4f8d4e8..b0b379873e 100644 --- a/core/object/method_bind.h +++ b/core/object/method_bind.h @@ -42,6 +42,8 @@ enum MethodFlags { METHOD_FLAG_VIRTUAL = 32, METHOD_FLAG_FROM_SCRIPT = 64, METHOD_FLAG_VARARG = 128, + METHOD_FLAG_STATIC = 256, + METHOD_FLAG_OBJECT_CORE = 512, METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL, }; @@ -134,6 +136,8 @@ public: void set_default_arguments(const Vector<Variant> &p_defargs); + uint32_t get_hash() const; + MethodBind(); virtual ~MethodBind(); }; diff --git a/core/object/object.cpp b/core/object/object.cpp index 171bc4dc2c..b5797a4633 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -385,6 +385,23 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid } } + if (_extension && _extension->set) { +// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#endif + if (_extension->set(_extension_instance, (const GDNativeStringNamePtr)&p_name, (const GDNativeVariantPtr)&p_value)) { + if (r_valid) { + *r_valid = true; + } + return; + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + } + //try built-in setgetter { if (ClassDB::set_property(this, p_name, p_value, r_valid)) { @@ -450,6 +467,23 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { return ret; } } + if (_extension && _extension->get) { +// C style pointer casts should never trigger a compiler warning because the risk is assumed by the user, so GCC should keep quiet about it. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#endif + + if (_extension->get(_extension_instance, (const GDNativeStringNamePtr)&p_name, (GDNativeVariantPtr)&ret)) { + if (r_valid) { + *r_valid = true; + } + return ret; + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + } //try built-in setgetter { @@ -596,7 +630,18 @@ 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 userfriendlyness + if (_extension && _extension->get_property_list) { + uint32_t pcount; + const GDNativePropertyInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount); + for (uint32_t i = 0; i < pcount; i++) { + p_list->push_back(PropertyInfo(Variant::Type(pinfo[i].type), pinfo[i].class_name, PropertyHint(pinfo[i].hint), pinfo[i].hint_string, pinfo[i].usage, pinfo[i].class_name)); + } + if (_extension->free_property_list) { + _extension->free_property_list(_extension_instance, pinfo); + } + } + + 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)); } if (!metadata.is_empty()) { @@ -740,7 +785,7 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; return Variant(); } - if (Object::cast_to<Reference>(this)) { + if (Object::cast_to<RefCounted>(this)) { r_error.argument = 0; r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; ERR_FAIL_V_MSG(Variant(), "Can't 'free' a reference."); @@ -761,6 +806,7 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a Variant ret; OBJ_DEBUG_LOCK + if (script_instance) { ret = script_instance->call(p_method, p_args, p_argcount, r_error); //force jumptable @@ -778,6 +824,8 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a } } + //extension does not need this, because all methods are registered in MethodBind + MethodBind *method = ClassDB::get_method(get_class_name(), p_method); if (method) { @@ -795,6 +843,10 @@ void Object::notification(int p_notification, bool p_reversed) { if (script_instance) { script_instance->notification(p_notification); } + + if (_extension && _extension->notification) { + _extension->notification(_extension_instance, p_notification); + } } String Object::to_string() { @@ -805,24 +857,12 @@ String Object::to_string() { return ret; } } + if (_extension && _extension->to_string) { + return _extension->to_string(_extension_instance); + } return "[" + get_class() + ":" + itos(get_instance_id()) + "]"; } -void Object::_changed_callback(Object *p_changed, const char *p_prop) { -} - -void Object::add_change_receptor(Object *p_receptor) { - change_receptors.insert(p_receptor); -} - -void Object::remove_change_receptor(Object *p_receptor) { - change_receptors.erase(p_receptor); -} - -void Object::property_list_changed_notify() { - _change_notify(); -} - 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()); @@ -847,7 +887,7 @@ void Object::set_script(const Variant &p_script) { Ref<Script> s = script; if (!s.is_null()) { - if (s->can_instance()) { + if (s->can_instantiate()) { OBJ_DEBUG_LOCK script_instance = s->instance_create(this); } else if (Engine::get_singleton()->is_editor_hint()) { @@ -856,7 +896,7 @@ void Object::set_script(const Variant &p_script) { } } - _change_notify(); //scripts may add variables, so refresh is desired + notify_property_list_changed(); //scripts may add variables, so refresh is desired emit_signal(CoreStringNames::get_singleton()->script_changed); } @@ -882,11 +922,11 @@ Variant Object::get_script() const { return script; } -bool Object::has_meta(const String &p_name) const { +bool Object::has_meta(const StringName &p_name) const { return metadata.has(p_name); } -void Object::set_meta(const String &p_name, const Variant &p_value) { +void Object::set_meta(const StringName &p_name, const Variant &p_value) { if (p_value.get_type() == Variant::NIL) { metadata.erase(p_name); return; @@ -895,12 +935,12 @@ void Object::set_meta(const String &p_name, const Variant &p_value) { metadata[p_name] = p_value; } -Variant Object::get_meta(const String &p_name) const { - ERR_FAIL_COND_V(!metadata.has(p_name), Variant()); +Variant Object::get_meta(const StringName &p_name) const { + ERR_FAIL_COND_V_MSG(!metadata.has(p_name), Variant(), "The object does not have any 'meta' values with the key '" + p_name + "'."); return metadata[p_name]; } -void Object::remove_meta(const String &p_name) { +void Object::remove_meta(const StringName &p_name) { metadata.erase(p_name); } @@ -924,23 +964,23 @@ Array Object::_get_method_list_bind() const { return ret; } -Vector<String> Object::_get_meta_list_bind() const { - Vector<String> _metaret; +Vector<StringName> Object::_get_meta_list_bind() const { + Vector<StringName> _metaret; List<Variant> keys; metadata.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - _metaret.push_back(E->get()); + for (const Variant &E : keys) { + _metaret.push_back(E); } return _metaret; } -void Object::get_meta_list(List<String> *p_list) const { +void Object::get_meta_list(List<StringName> *p_list) const { List<Variant> keys; metadata.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - p_list->push_back(E->get()); + for (const Variant &E : keys) { + p_list->push_back(E); } } @@ -1144,8 +1184,8 @@ Array Object::_get_signal_list() const { get_signal_list(&signal_list); Array ret; - for (List<MethodInfo>::Element *E = signal_list.front(); E; E = E->next()) { - ret.push_back(Dictionary(E->get())); + for (const MethodInfo &E : signal_list) { + ret.push_back(Dictionary(E)); } return ret; @@ -1157,8 +1197,7 @@ Array Object::_get_signal_connection_list(const String &p_signal) const { Array ret; - for (List<Connection>::Element *E = conns.front(); E; E = E->next()) { - Connection &c = E->get(); + for (const Connection &c : conns) { if (c.signal.get_name() == p_signal) { ret.push_back(c); } @@ -1257,16 +1296,16 @@ int Object::get_persistent_signal_connection_count() const { } void Object::get_signals_connected_to_this(List<Connection> *p_connections) const { - for (const List<Connection>::Element *E = connections.front(); E; E = E->next()) { - p_connections->push_back(E->get()); + for (const Connection &E : connections) { + p_connections->push_back(E); } } 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_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is null."); Object *target_object = p_callable.get_object(); - ERR_FAIL_COND_V(!target_object, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(!target_object, ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null."); SignalData *s = signal_map.getptr(p_signal); if (!s) { @@ -1324,7 +1363,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, co } bool Object::is_connected(const StringName &p_signal, const Callable &p_callable) const { - ERR_FAIL_COND_V(p_callable.is_null(), false); + ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot determine if connected to '" + p_signal + "': the provided callable is null."); const SignalData *s = signal_map.getptr(p_signal); if (!s) { bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal); @@ -1351,16 +1390,16 @@ void Object::disconnect(const StringName &p_signal, const Callable &p_callable) } void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) { - ERR_FAIL_COND(p_callable.is_null()); + ERR_FAIL_COND_MSG(p_callable.is_null(), "Cannot disconnect from '" + p_signal + "': the provided callable is null."); Object *target_object = p_callable.get_object(); - ERR_FAIL_COND(!target_object); + ERR_FAIL_COND_MSG(!target_object, "Cannot disconnect '" + p_signal + "' from callable '" + p_callable + "': the callable object is null."); SignalData *s = signal_map.getptr(p_signal); if (!s) { bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal) || (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal)); - ERR_FAIL_COND_MSG(signal_is_valid, "Attempt to disconnect a nonexistent connection from '" + to_string() + "'. signal: '" + p_signal + "', callable: '" + p_callable + "'."); + ERR_FAIL_COND_MSG(signal_is_valid, "Attempt to disconnect a nonexistent connection from '" + to_string() + "'. Signal: '" + p_signal + "', callable: '" + p_callable + "'."); } ERR_FAIL_COND_MSG(!s, vformat("Disconnecting nonexistent signal '%s' in %s.", p_signal, to_string())); @@ -1370,7 +1409,7 @@ void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, if (!p_force) { slot->reference_count--; // by default is zero, if it was not referenced it will go below it - if (slot->reference_count >= 0) { + if (slot->reference_count > 0) { return; } } @@ -1460,9 +1499,9 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) { List<Variant> keys; d.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - _clear_internal_resource_paths(E->get()); - _clear_internal_resource_paths(d[E->get()]); + for (const Variant &E : keys) { + _clear_internal_resource_paths(E); + _clear_internal_resource_paths(d[E]); } } break; default: { @@ -1491,11 +1530,15 @@ void Object::clear_internal_resource_paths() { get_property_list(&pinfo); - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - _clear_internal_resource_paths(get(E->get().name)); + for (const PropertyInfo &E : pinfo) { + _clear_internal_resource_paths(get(E.name)); } } +void Object::notify_property_list_changed() { + emit_signal(CoreStringNames::get_singleton()->property_list_changed); +} + void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("get_class"), &Object::get_class); ClassDB::bind_method(D_METHOD("is_class", "class"), &Object::is_class); @@ -1562,7 +1605,7 @@ void Object::_bind_methods() { 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); - ClassDB::bind_method(D_METHOD("property_list_changed_notify"), &Object::property_list_changed_notify); + ClassDB::bind_method(D_METHOD("notify_property_list_changed"), &Object::notify_property_list_changed); ClassDB::bind_method(D_METHOD("set_message_translation", "enable"), &Object::set_message_translation); ClassDB::bind_method(D_METHOD("can_translate_messages"), &Object::can_translate_messages); @@ -1574,23 +1617,27 @@ void Object::_bind_methods() { ClassDB::add_virtual_method("Object", MethodInfo("free"), false); ADD_SIGNAL(MethodInfo("script_changed")); + ADD_SIGNAL(MethodInfo("property_list_changed")); + +#define BIND_OBJ_CORE_METHOD(m_method) \ + ::ClassDB::add_virtual_method(get_class_static(), m_method, true, Vector<String>(), true); - BIND_VMETHOD(MethodInfo("_notification", PropertyInfo(Variant::INT, "what"))); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "_set", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value"))); + BIND_OBJ_CORE_METHOD(MethodInfo("_notification", PropertyInfo(Variant::INT, "what"))); + BIND_OBJ_CORE_METHOD(MethodInfo(Variant::BOOL, "_set", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value"))); #ifdef TOOLS_ENABLED 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); + BIND_OBJ_CORE_METHOD(miget); MethodInfo plget("_get_property_list"); plget.return_val.type = Variant::ARRAY; - BIND_VMETHOD(plget); + BIND_OBJ_CORE_METHOD(plget); #endif - BIND_VMETHOD(MethodInfo("_init")); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_to_string")); + BIND_OBJ_CORE_METHOD(MethodInfo("_init")); + BIND_OBJ_CORE_METHOD(MethodInfo(Variant::STRING, "_to_string")); BIND_CONSTANT(NOTIFICATION_POSTINITIALIZE); BIND_CONSTANT(NOTIFICATION_PREDELETE); @@ -1621,12 +1668,12 @@ void Object::get_translatable_strings(List<String> *p_strings) const { List<PropertyInfo> plist; get_property_list(&plist); - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (!(E->get().usage & PROPERTY_USAGE_INTERNATIONALIZED)) { + for (const PropertyInfo &E : plist) { + if (!(E.usage & PROPERTY_USAGE_INTERNATIONALIZED)) { continue; } - String text = get(E->get().name); + String text = get(E.name); if (text == "") { continue; @@ -1681,7 +1728,7 @@ Variant::Type Object::get_static_property_type_indexed(const Vector<StringName> for (int i = 1; i < p_path.size(); i++) { if (check.get_type() == Variant::OBJECT || check.get_type() == Variant::DICTIONARY || check.get_type() == Variant::ARRAY) { - // We cannot be sure about the type of properties this types can have + // We cannot be sure about the type of properties this type can have if (r_valid) { *r_valid = false; } @@ -1724,42 +1771,69 @@ uint32_t Object::get_edited_version() const { } #endif -void *Object::get_script_instance_binding(int p_script_language_index) { -#ifdef DEBUG_ENABLED - ERR_FAIL_INDEX_V(p_script_language_index, MAX_SCRIPT_INSTANCE_BINDINGS, nullptr); -#endif - - //it's up to the script language to make this thread safe, if the function is called twice due to threads being out of syncro - //just return the same pointer. - //if you want to put a big lock in the entire function and keep allocated pointers in a map or something, feel free to do it - //as it should not really affect performance much (won't be called too often), as in far most caes the condition below will be false afterwards +void Object::set_instance_binding(void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks) { + // This is only meant to be used on creation by the binder. + ERR_FAIL_COND(_instance_bindings != nullptr); + _instance_bindings = (InstanceBinding *)memalloc(sizeof(InstanceBinding)); + _instance_bindings[0].binding = p_binding; + _instance_bindings[0].free_callback = p_callbacks->free_callback; + _instance_bindings[0].reference_callback = p_callbacks->reference_callback; + _instance_bindings[0].token = p_token; + _instance_binding_count = 1; +} + +void *Object::get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks) { + void *binding = nullptr; + _instance_binding_mutex.lock(); + for (uint32_t i = 0; i < _instance_binding_count; i++) { + if (_instance_bindings[i].token == p_token) { + binding = _instance_bindings[i].binding; + break; + } + } + if (unlikely(!binding)) { + uint32_t current_size = next_power_of_2(_instance_binding_count); + uint32_t new_size = next_power_of_2(_instance_binding_count + 1); - if (!_script_instance_bindings[p_script_language_index]) { - void *script_data = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this); - if (script_data) { - atomic_increment(&instance_binding_count); - _script_instance_bindings[p_script_language_index] = script_data; + if (current_size == 0 || new_size > current_size) { + _instance_bindings = (InstanceBinding *)memrealloc(_instance_bindings, new_size * sizeof(InstanceBinding)); } + + _instance_bindings[_instance_binding_count].free_callback = p_callbacks->free_callback; + _instance_bindings[_instance_binding_count].reference_callback = p_callbacks->reference_callback; + _instance_bindings[_instance_binding_count].token = p_token; + + binding = p_callbacks->create_callback(p_token, this); + _instance_bindings[_instance_binding_count].binding = binding; + + _instance_binding_count++; } - return _script_instance_bindings[p_script_language_index]; -} + _instance_binding_mutex.unlock(); -bool Object::has_script_instance_binding(int p_script_language_index) { - return _script_instance_bindings[p_script_language_index] != nullptr; + return binding; } -void Object::set_script_instance_binding(int p_script_language_index, void *p_data) { -#ifdef DEBUG_ENABLED - CRASH_COND(_script_instance_bindings[p_script_language_index] != nullptr); -#endif - _script_instance_bindings[p_script_language_index] = p_data; +bool Object::has_instance_binding(void *p_token) { + bool found = false; + _instance_binding_mutex.lock(); + for (uint32_t i = 0; i < _instance_binding_count; i++) { + if (_instance_bindings[i].token == p_token) { + found = true; + break; + } + } + + _instance_binding_mutex.unlock(); + + return found; } void Object::_construct_object(bool p_reference) { type_is_reference = p_reference; _instance_id = ObjectDB::add_instance(this); - memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS); + + ClassDB::instance_get_native_extension_data(&_extension, &_extension_instance, this); #ifdef DEBUG_ENABLED _lock_index.init(1); @@ -1780,6 +1854,12 @@ Object::~Object() { } script_instance = nullptr; + if (_extension && _extension->free_instance) { + _extension->free_instance(_extension->class_userdata, _extension_instance); + _extension = nullptr; + _extension_instance = nullptr; + } + const StringName *S = nullptr; if (_emitting) { @@ -1811,12 +1891,13 @@ Object::~Object() { _instance_id = ObjectID(); _predelete_ok = 2; - if (!ScriptServer::are_languages_finished()) { - for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { - if (_script_instance_bindings[i]) { - ScriptServer::get_language(i)->free_instance_binding_data(_script_instance_bindings[i]); + if (_instance_bindings != nullptr) { + for (uint32_t i = 0; i < _instance_binding_count; i++) { + if (_instance_bindings[i].free_callback) { + _instance_bindings[i].free_callback(_instance_bindings[i].token, this, _instance_bindings[i].binding); } } + memfree(_instance_bindings); } } @@ -1834,7 +1915,6 @@ void ObjectDB::debug_objects(DebugFunc p_func) { for (uint32_t i = 0, count = slot_count; i < slot_max && count != 0; i++) { if (object_slots[i].validator) { p_func(object_slots[i].object); - count--; } } @@ -1863,7 +1943,7 @@ ObjectID ObjectDB::add_instance(Object *p_object) { 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].is_ref_counted = false; object_slots[i].next_free = i; object_slots[i].validator = 0; } @@ -1876,7 +1956,7 @@ ObjectID ObjectDB::add_instance(Object *p_object) { 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(); + object_slots[slot].is_ref_counted = p_object->is_ref_counted(); validator_counter = (validator_counter + 1) & OBJECTDB_VALIDATOR_MASK; if (unlikely(validator_counter == 0)) { validator_counter = 1; @@ -1887,7 +1967,7 @@ ObjectID ObjectDB::add_instance(Object *p_object) { id <<= OBJECTDB_SLOT_MAX_COUNT_BITS; id |= uint64_t(slot); - if (p_object->is_reference()) { + if (p_object->is_ref_counted()) { id |= OBJECTDB_REFERENCE_BIT; } @@ -1925,7 +2005,7 @@ void ObjectDB::remove_instance(Object *p_object) { 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].is_ref_counted = false; object_slots[slot].object = nullptr; spin_lock.unlock(); @@ -1960,7 +2040,7 @@ void ObjectDB::cleanup() { extra_info = " - Resource path: " + String(resource_get_path->call(obj, nullptr, 0, call_error)); } - uint64_t id = uint64_t(i) | (uint64_t(object_slots[i].validator) << OBJECTDB_VALIDATOR_BITS) | (object_slots[i].is_reference ? OBJECTDB_REFERENCE_BIT : 0); + uint64_t id = uint64_t(i) | (uint64_t(object_slots[i].validator) << OBJECTDB_VALIDATOR_BITS) | (object_slots[i].is_ref_counted ? OBJECTDB_REFERENCE_BIT : 0); print_line("Leaked instance: " + String(obj->get_class()) + ":" + itos(id) + extra_info); count--; diff --git a/core/object/object.h b/core/object/object.h index 7e460462cf..a44d921bff 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -31,24 +31,26 @@ #ifndef OBJECT_H #define OBJECT_H +#include "core/extension/gdnative_interface.h" #include "core/object/object_id.h" #include "core/os/rw_lock.h" #include "core/os/spin_lock.h" #include "core/templates/hash_map.h" #include "core/templates/list.h" #include "core/templates/map.h" +#include "core/templates/safe_refcount.h" #include "core/templates/set.h" #include "core/templates/vmap.h" #include "core/variant/callable_bind.h" #include "core/variant/variant.h" -#define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant() -#define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5 -#define VARIANT_ARG_DECLARE const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5 -#define VARIANT_ARG_MAX 5 -#define VARIANT_ARGPTRS const Variant *argptr[5] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4, &p_arg5 }; -#define VARIANT_ARGPTRS_PASS *argptr[0], *argptr[1], *argptr[2], *argptr[3], *argptr[4] -#define VARIANT_ARGS_FROM_ARRAY(m_arr) m_arr[0], m_arr[1], m_arr[2], m_arr[3], m_arr[4] +#define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant(), const Variant &p_arg6 = Variant(), const Variant &p_arg7 = Variant(), const Variant &p_arg8 = Variant() +#define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5, p_arg6, p_arg7, p_arg8 +#define VARIANT_ARG_DECLARE const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5, const Variant &p_arg6, const Variant &p_arg7, const Variant &p_arg8 +#define VARIANT_ARG_MAX 8 +#define VARIANT_ARGPTRS const Variant *argptr[8] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4, &p_arg5, &p_arg6, &p_arg7, &p_arg8 }; +#define VARIANT_ARGPTRS_PASS *argptr[0], *argptr[1], *argptr[2], *argptr[3], *argptr[4], *argptr[5], *argptr[6]], *argptr[7] +#define VARIANT_ARGS_FROM_ARRAY(m_arr) m_arr[0], m_arr[1], m_arr[2], m_arr[3], m_arr[4], m_arr[5], m_arr[6], m_arr[7] /** @author Juan Linietsky <reduzio@gmail.com> @@ -56,17 +58,19 @@ enum PropertyHint { PROPERTY_HINT_NONE, ///< no hint provided. - PROPERTY_HINT_RANGE, ///< hint_text = "min,max,step,slider; //slider is optional" - PROPERTY_HINT_EXP_RANGE, ///< hint_text = "min,max,step", exponential edit + PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_lesser][,noslider][,radians][,degrees][,exp][,suffix:<keyword>] range. PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc" + PROPERTY_HINT_ENUM_SUGGESTION, ///< hint_text= "val1,val2,val3,etc" PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout") PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer) PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer) PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags) PROPERTY_HINT_LAYERS_2D_RENDER, PROPERTY_HINT_LAYERS_2D_PHYSICS, + PROPERTY_HINT_LAYERS_2D_NAVIGATION, PROPERTY_HINT_LAYERS_3D_RENDER, PROPERTY_HINT_LAYERS_3D_PHYSICS, + PROPERTY_HINT_LAYERS_3D_NAVIGATION, PROPERTY_HINT_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc," PROPERTY_HINT_DIR, ///< a directory path must be passed PROPERTY_HINT_GLOBAL_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc," @@ -93,11 +97,13 @@ enum PropertyHint { PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog PROPERTY_HINT_INT_IS_OBJECTID, PROPERTY_HINT_ARRAY_TYPE, + PROPERTY_HINT_INT_IS_POINTER, PROPERTY_HINT_MAX, // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit }; enum PropertyUsageFlags { + PROPERTY_USAGE_NONE = 0, PROPERTY_USAGE_STORAGE = 1, PROPERTY_USAGE_EDITOR = 2, PROPERTY_USAGE_NETWORK = 4, @@ -125,28 +131,40 @@ enum PropertyUsageFlags { PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 25, // Used in inspector to increment property when keyed in animation player PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 26, // when loading, the resource for this property can be set at the end of loading PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 27, // For Object properties, instantiate them when creating in editor. + PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 28, //for project or editor settings, show when basic settings are selected + PROPERTY_USAGE_READ_ONLY = 1 << 29, // Mark a property as read-only in the inspector. + PROPERTY_USAGE_ARRAY = 1 << 30, // Used in the inspector to group properties as elements of an array. PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK, PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED, PROPERTY_USAGE_NOEDITOR = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_NETWORK, }; -#define ADD_SIGNAL(m_signal) ClassDB::add_signal(get_class_static(), m_signal) -#define ADD_PROPERTY(m_property, m_setter, m_getter) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter)) -#define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter), m_index) -#define ADD_PROPERTY_DEFAULT(m_property, m_default) ClassDB::set_property_default_value(get_class_static(), m_property, m_default) -#define ADD_GROUP(m_name, m_prefix) ClassDB::add_property_group(get_class_static(), m_name, m_prefix) -#define ADD_SUBGROUP(m_name, m_prefix) ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix) +#define ADD_SIGNAL(m_signal) ::ClassDB::add_signal(get_class_static(), m_signal) +#define ADD_PROPERTY(m_property, m_setter, m_getter) ::ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter)) +#define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) ::ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter), m_index) +#define ADD_PROPERTY_DEFAULT(m_property, m_default) ::ClassDB::set_property_default_value(get_class_static(), m_property, m_default) +#define ADD_GROUP(m_name, m_prefix) ::ClassDB::add_property_group(get_class_static(), m_name, m_prefix) +#define ADD_SUBGROUP(m_name, m_prefix) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix) +#define ADD_LINKED_PROPERTY(m_property, m_linked_property) ::ClassDB::add_linked_property(get_class_static(), m_property, m_linked_property) + +#define ADD_ARRAY_COUNT(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, _scs_create(m_count_property_setter), _scs_create(m_count_property_getter), m_prefix) +#define ADD_ARRAY_COUNT_WITH_USAGE_FLAGS(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix, m_property_usage_flags) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, _scs_create(m_count_property_setter), _scs_create(m_count_property_getter), m_prefix, m_property_usage_flags) +#define ADD_ARRAY(m_array_path, m_prefix) ClassDB::add_property_array(get_class_static(), m_array_path, m_prefix) struct PropertyInfo { Variant::Type type = Variant::NIL; String name; - StringName class_name; //for classes + StringName class_name; // For classes PropertyHint hint = PROPERTY_HINT_NONE; String hint_string; uint32_t usage = PROPERTY_USAGE_DEFAULT; - _FORCE_INLINE_ PropertyInfo added_usage(int p_fl) const { +#ifdef TOOLS_ENABLED + Vector<String> linked_properties; +#endif + + _FORCE_INLINE_ PropertyInfo added_usage(uint32_t p_fl) const { PropertyInfo pi = *this; pi.usage |= p_fl; return pi; @@ -158,7 +176,7 @@ struct PropertyInfo { PropertyInfo() {} - PropertyInfo(Variant::Type p_type, const String p_name, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = StringName()) : + PropertyInfo(const Variant::Type p_type, const String p_name, const PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", const uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = StringName()) : type(p_type), name(p_name), hint(p_hint), @@ -234,6 +252,52 @@ struct MethodInfo { ////else //return nullptr; +// API used to extend in GDNative and other C compatible compiled languages +class MethodBind; + +struct ObjectNativeExtension { + ObjectNativeExtension *parent = nullptr; + List<ObjectNativeExtension *> children; + StringName parent_class_name; + StringName class_name; + bool editor_class = false; + GDNativeExtensionClassSet set; + GDNativeExtensionClassGet get; + GDNativeExtensionClassGetPropertyList get_property_list; + GDNativeExtensionClassFreePropertyList free_property_list; + GDNativeExtensionClassNotification notification; + GDNativeExtensionClassToString to_string; + GDNativeExtensionClassReference reference; + GDNativeExtensionClassReference unreference; + + _FORCE_INLINE_ bool is_class(const String &p_class) const { + const ObjectNativeExtension *e = this; + while (e) { + if (p_class == e->class_name.operator String()) { + return true; + } + e = e->parent; + } + return false; + } + void *class_userdata = nullptr; + + GDNativeExtensionClassCreateInstance create_instance; + GDNativeExtensionClassFreeInstance free_instance; + GDNativeExtensionClassObjectInstance set_object_instance; + GDNativeExtensionClassGetVirtual get_virtual; +}; + +#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call(__VA_ARGS__) +#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call(__VA_ARGS__) +#ifdef DEBUG_METHODS_ENABLED +#define GDVIRTUAL_BIND(m_name, ...) ::ClassDB::add_virtual_method(get_class_static(), _gdvirtual_##m_name##_get_method_info(), true, sarray(__VA_ARGS__)); +#else +#define GDVIRTUAL_BIND(m_name, ...) +#endif +#define GDVIRTUAL_IS_OVERRIDDEN(m_name) _gdvirtual_##m_name##_overridden() +#define GDVIRTUAL_IS_OVERRIDDEN_PTR(m_obj, m_name) m_obj->_gdvirtual_##m_name##_overridden() + /* the following is an incomprehensible blob of hacks and workarounds to compensate for many of the fallencies in C++. As a plus, this macro pretty much alone defines the object model. */ @@ -254,13 +318,19 @@ private: private: \ void operator=(const m_class &p_rval) {} \ mutable StringName _class_name; \ - friend class ClassDB; \ + friend class ::ClassDB; \ \ public: \ virtual String get_class() const override { \ + if (_get_extension()) { \ + return _get_extension()->class_name.operator String(); \ + } \ return String(#m_class); \ } \ virtual const StringName *_get_class_namev() const override { \ + if (_get_extension()) { \ + return &_get_extension()->class_name; \ + } \ if (!_class_name) { \ _class_name = get_class_static(); \ } \ @@ -293,7 +363,12 @@ public: static String inherits_static() { \ return String(#m_inherits); \ } \ - virtual bool is_class(const String &p_class) const override { return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); } \ + 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); \ + } \ virtual bool is_class_ptr(void *p_ptr) const override { return (p_ptr == get_class_ptr_static()) ? true : m_inherits::is_class_ptr(p_ptr); } \ \ static void get_valid_parents_static(List<String> *p_parents) { \ @@ -316,7 +391,7 @@ public: return; \ } \ m_inherits::initialize_class(); \ - ClassDB::_add_class<m_class>(); \ + ::ClassDB::_add_class<m_class>(); \ if (m_class::_get_bind_methods() != m_inherits::_get_bind_methods()) { \ _bind_methods(); \ } \ @@ -359,13 +434,13 @@ protected: } \ p_list->push_back(PropertyInfo(Variant::NIL, get_class_static(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); \ if (!_is_gpl_reversed()) { \ - ClassDB::get_property_list(#m_class, p_list, true, this); \ + ::ClassDB::get_property_list(#m_class, p_list, true, this); \ } \ if (m_class::_get_get_property_list() != m_inherits::_get_get_property_list()) { \ _get_property_list(p_list); \ } \ if (_is_gpl_reversed()) { \ - ClassDB::get_property_list(#m_class, p_list, true, this); \ + ::ClassDB::get_property_list(#m_class, p_list, true, this); \ } \ if (p_reversed) { \ m_inherits::_get_property_listv(p_list, p_reversed); \ @@ -426,16 +501,15 @@ public: }; private: - enum { - MAX_SCRIPT_INSTANCE_BINDINGS = 8 - }; - #ifdef DEBUG_ENABLED friend struct _ObjectDebugLock; #endif friend bool predelete_handler(Object *); friend void postinitialize_handler(Object *); + ObjectNativeExtension *_extension = nullptr; + GDExtensionClassInstancePtr _extension_instance = nullptr; + struct SignalData { struct Slot { int reference_count = 0; @@ -454,7 +528,6 @@ private: #endif bool _block_signals = false; int _predelete_ok = 0; - Set<Object *> change_receptors; ObjectID _instance_id; bool _predelete(); void _postinitialize(); @@ -484,14 +557,40 @@ private: _FORCE_INLINE_ void _construct_object(bool p_reference); - friend class Reference; + friend class RefCounted; bool type_is_reference = false; - uint32_t instance_binding_count = 0; - void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS]; + + std::mutex _instance_binding_mutex; + struct InstanceBinding { + void *binding; + void *token; + GDNativeInstanceBindingFreeCallback free_callback = nullptr; + GDNativeInstanceBindingReferenceCallback reference_callback = nullptr; + }; + InstanceBinding *_instance_bindings = nullptr; + uint32_t _instance_binding_count = 0; Object(bool p_reference); protected: + _FORCE_INLINE_ bool _instance_binding_reference(bool p_reference) { + bool can_die = true; + if (_instance_bindings) { + _instance_binding_mutex.lock(); + for (uint32_t i = 0; i < _instance_binding_count; i++) { + if (_instance_bindings[i].reference_callback) { + if (!_instance_bindings[i].reference_callback(_instance_bindings[i].token, _instance_bindings[i].binding, p_reference)) { + can_die = false; + } + } + } + _instance_binding_mutex.unlock(); + } + return can_die; + } + friend class NativeExtensionMethodBind; + _ALWAYS_INLINE_ const ObjectNativeExtension *_get_extension() const { return _extension; } + _ALWAYS_INLINE_ GDExtensionClassInstancePtr _get_extension_instance() const { return _extension_instance; } virtual void _initialize_classv() { initialize_class(); } virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; }; virtual bool _getv(const StringName &p_name, Variant &r_property) const { return false; }; @@ -523,9 +622,6 @@ protected: static void get_valid_parents_static(List<String> *p_parents); static void _get_valid_parents_static(List<String> *p_parents); - void property_list_changed_notify(); - virtual void _changed_callback(Object *p_changed, const char *p_prop); - //Variant _call_bind(const StringName& p_name, const Variant& p_arg1 = Variant(), const Variant& p_arg2 = Variant(), const Variant& p_arg3 = Variant(), const Variant& p_arg4 = Variant()); //void _call_deferred_bind(const StringName& p_name, const Variant& p_arg1 = Variant(), const Variant& p_arg2 = Variant(), const Variant& p_arg3 = Variant(), const Variant& p_arg4 = Variant()); @@ -539,7 +635,7 @@ protected: return &_class_name; } - Vector<String> _get_meta_list_bind() const; + Vector<StringName> _get_meta_list_bind() const; Array _get_property_list_bind() const; Array _get_method_list_bind() const; @@ -555,16 +651,8 @@ public: //should be protected, but bug in clang++ _FORCE_INLINE_ static void register_custom_data_to_otdb() {} public: -#ifdef TOOLS_ENABLED - _FORCE_INLINE_ void _change_notify(const char *p_property = "") { - _edited = true; - for (Set<Object *>::Element *E = change_receptors.front(); E; E = E->next()) { - ((Object *)(E->get()))->_changed_callback(this, p_property); - } - } -#else - _FORCE_INLINE_ void _change_notify(const char *p_what = "") {} -#endif + void notify_property_list_changed(); + static void *get_class_ptr_static() { static int ptr; return &ptr; @@ -574,10 +662,6 @@ public: _FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_id; } - // this is used for editors - void add_change_receptor(Object *p_receptor); - void remove_change_receptor(Object *p_receptor); - template <class T> static T *cast_to(Object *p_object) { #ifndef NO_SAFE_CAST @@ -622,13 +706,25 @@ public: static String get_parent_class_static() { return String(); } static String get_category_static() { return String(); } - virtual String get_class() const { return "Object"; } + virtual String get_class() const { + if (_extension) + return _extension->class_name.operator String(); + return "Object"; + } virtual String get_save_class() const { return get_class(); } //class stored when saving - virtual bool is_class(const String &p_class) const { return (p_class == "Object"); } + virtual 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; } _FORCE_INLINE_ const StringName &get_class_name() const { + if (_extension) { + return _extension->class_name; + } if (!_class_ptr) { return *_get_class_namev(); } else { @@ -667,11 +763,11 @@ public: /* SCRIPT */ - bool has_meta(const String &p_name) const; - void set_meta(const String &p_name, const Variant &p_value); - void remove_meta(const String &p_name); - Variant get_meta(const String &p_name) const; - void get_meta_list(List<String> *p_list) const; + bool has_meta(const StringName &p_name) const; + void set_meta(const StringName &p_name, const Variant &p_value); + void remove_meta(const StringName &p_name); + Variant get_meta(const StringName &p_name) const; + void get_meta_list(List<StringName> *p_list) const; #ifdef TOOLS_ENABLED void set_edited(bool p_edited); @@ -728,14 +824,15 @@ public: #endif - //used by script languages to store binding data - void *get_script_instance_binding(int p_script_language_index); - bool has_script_instance_binding(int p_script_language_index); - void set_script_instance_binding(int p_script_language_index, void *p_data); + // Used by script languages to store binding data. + void *get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks); + // Used on creation by binding only. + void set_instance_binding(void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks); + bool has_instance_binding(void *p_token); void clear_internal_resource_paths(); - _ALWAYS_INLINE_ bool is_reference() const { return type_is_reference; } + _ALWAYS_INLINE_ bool is_ref_counted() const { return type_is_reference; } Object(); virtual ~Object(); @@ -755,7 +852,7 @@ class ObjectDB { struct ObjectSlot { //128 bits per slot uint64_t validator : OBJECTDB_VALIDATOR_BITS; uint64_t next_free : OBJECTDB_SLOT_MAX_COUNT_BITS; - uint64_t is_reference : 1; + uint64_t is_ref_counted : 1; Object *object; }; diff --git a/core/object/object_id.h b/core/object/object_id.h index 7f2496ad48..0666ec0855 100644 --- a/core/object/object_id.h +++ b/core/object/object_id.h @@ -42,7 +42,7 @@ class ObjectID { uint64_t id = 0; public: - _ALWAYS_INLINE_ bool is_reference() const { return (id & (uint64_t(1) << 63)) != 0; } + _ALWAYS_INLINE_ bool is_ref_counted() const { return (id & (uint64_t(1) << 63)) != 0; } _ALWAYS_INLINE_ bool is_valid() const { return id != 0; } _ALWAYS_INLINE_ bool is_null() const { return id == 0; } _ALWAYS_INLINE_ operator uint64_t() const { return id; } diff --git a/core/object/reference.cpp b/core/object/ref_counted.cpp index 71a52a9ba5..2833f774dc 100644 --- a/core/object/reference.cpp +++ b/core/object/ref_counted.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* reference.cpp */ +/* ref_counted.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "reference.h" +#include "ref_counted.h" #include "core/object/script_language.h" -bool Reference::init_ref() { +bool RefCounted::init_ref() { if (reference()) { if (!is_referenced() && refcount_init.unref()) { unreference(); // first referencing is already 1, so compensate for the ref above @@ -44,17 +44,17 @@ bool Reference::init_ref() { } } -void Reference::_bind_methods() { - ClassDB::bind_method(D_METHOD("init_ref"), &Reference::init_ref); - ClassDB::bind_method(D_METHOD("reference"), &Reference::reference); - ClassDB::bind_method(D_METHOD("unreference"), &Reference::unreference); +void RefCounted::_bind_methods() { + ClassDB::bind_method(D_METHOD("init_ref"), &RefCounted::init_ref); + ClassDB::bind_method(D_METHOD("reference"), &RefCounted::reference); + ClassDB::bind_method(D_METHOD("unreference"), &RefCounted::unreference); } -int Reference::reference_get_count() const { +int RefCounted::reference_get_count() const { return refcount.get(); } -bool Reference::reference() { +bool RefCounted::reference() { uint32_t rc_val = refcount.refval(); bool success = rc_val != 0; @@ -62,19 +62,17 @@ bool Reference::reference() { if (get_script_instance()) { get_script_instance()->refcount_incremented(); } - if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) { - for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { - if (_script_instance_bindings[i]) { - ScriptServer::get_language(i)->refcount_incremented_instance_binding(this); - } - } + if (_get_extension() && _get_extension()->reference) { + _get_extension()->reference(_get_extension_instance()); } + + _instance_binding_reference(true); } return success; } -bool Reference::unreference() { +bool RefCounted::unreference() { uint32_t rc_val = refcount.unrefval(); bool die = rc_val == 0; @@ -83,20 +81,17 @@ bool Reference::unreference() { bool script_ret = get_script_instance()->refcount_decremented(); die = die && script_ret; } - if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) { - for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { - if (_script_instance_bindings[i]) { - bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this); - die = die && script_ret; - } - } + if (_get_extension() && _get_extension()->unreference) { + _get_extension()->unreference(_get_extension_instance()); } + + die = die && _instance_binding_reference(false); } return die; } -Reference::Reference() : +RefCounted::RefCounted() : Object(true) { refcount.init(); refcount_init.init(); @@ -111,7 +106,7 @@ Variant WeakRef::get_ref() const { if (!obj) { return Variant(); } - Reference *r = cast_to<Reference>(obj); + RefCounted *r = cast_to<RefCounted>(obj); if (r) { return REF(r); } diff --git a/core/object/reference.h b/core/object/ref_counted.h index d02cb12069..f2dd2aa324 100644 --- a/core/object/reference.h +++ b/core/object/ref_counted.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* reference.h */ +/* ref_counted.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,14 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef REFERENCE_H -#define REFERENCE_H +#ifndef REF_COUNTED_H +#define REF_COUNTED_H #include "core/object/class_db.h" #include "core/templates/safe_refcount.h" -class Reference : public Object { - GDCLASS(Reference, Object); +class RefCounted : public Object { + GDCLASS(RefCounted, Object); SafeRefCount refcount; SafeRefCount refcount_init; @@ -49,8 +49,8 @@ public: bool unreference(); int reference_get_count() const; - Reference(); - ~Reference() {} + RefCounted(); + ~RefCounted() {} }; template <class T> @@ -78,7 +78,7 @@ class Ref { } } - //virtual Reference * get_reference() const { return reference; } + //virtual RefCounted * get_reference() const { return reference; } public: _FORCE_INLINE_ bool operator==(const T *p_ptr) const { return reference == p_ptr; @@ -130,7 +130,7 @@ public: template <class T_Other> void operator=(const Ref<T_Other> &p_from) { - Reference *refb = const_cast<Reference *>(static_cast<const Reference *>(p_from.ptr())); + RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr())); if (!refb) { unref(); return; @@ -179,7 +179,7 @@ public: template <class T_Other> Ref(const Ref<T_Other> &p_from) { - Reference *refb = const_cast<Reference *>(static_cast<const Reference *>(p_from.ptr())); + RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr())); if (!refb) { unref(); return; @@ -213,7 +213,7 @@ public: inline bool is_null() const { return reference == nullptr; } void unref() { - //TODO this should be moved to mutexes, since this engine does not really + // TODO: this should be moved to mutexes, since this engine does not really // do a lot of referencing on references and stuff // mutexes will avoid more crashes? @@ -223,7 +223,7 @@ public: reference = nullptr; } - void instance() { + void instantiate() { ref(memnew(T)); } @@ -234,10 +234,10 @@ public: } }; -typedef Ref<Reference> REF; +typedef Ref<RefCounted> REF; -class WeakRef : public Reference { - GDCLASS(WeakRef, Reference); +class WeakRef : public RefCounted { + GDCLASS(WeakRef, RefCounted); ObjectID ref; @@ -258,20 +258,22 @@ struct PtrToArg<Ref<T>> { return Ref<T>(const_cast<T *>(reinterpret_cast<const T *>(p_ptr))); } + typedef Ref<T> EncodeT; + _FORCE_INLINE_ static void encode(Ref<T> p_val, const void *p_ptr) { - *(Ref<Reference> *)p_ptr = p_val; + *(Ref<RefCounted> *)p_ptr = p_val; } }; template <class T> struct PtrToArg<const Ref<T> &> { + typedef Ref<T> EncodeT; + _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) { return Ref<T>((T *)p_ptr); } }; -#ifdef DEBUG_METHODS_ENABLED - template <class T> struct GetTypeInfo<Ref<T>> { static const Variant::Type VARIANT_TYPE = Variant::OBJECT; @@ -292,6 +294,4 @@ struct GetTypeInfo<const Ref<T> &> { } }; -#endif // DEBUG_METHODS_ENABLED - -#endif // REFERENCE_H +#endif // REF_COUNTED_H diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index c3f109a147..b0ce46ca2b 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -63,8 +63,8 @@ Array Script::_get_script_property_list() { Array ret; List<PropertyInfo> list; get_script_property_list(&list); - for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) { - ret.append(E->get().operator Dictionary()); + for (const PropertyInfo &E : list) { + ret.append(E.operator Dictionary()); } return ret; } @@ -73,8 +73,8 @@ Array Script::_get_script_method_list() { Array ret; List<MethodInfo> list; get_script_method_list(&list); - for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) { - ret.append(E->get().operator Dictionary()); + for (const MethodInfo &E : list) { + ret.append(E.operator Dictionary()); } return ret; } @@ -83,8 +83,8 @@ Array Script::_get_script_signal_list() { Array ret; List<MethodInfo> list; get_script_signal_list(&list); - for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) { - ret.append(E->get().operator Dictionary()); + for (const MethodInfo &E : list) { + ret.append(E.operator Dictionary()); } return ret; } @@ -93,14 +93,14 @@ Dictionary Script::_get_script_constant_map() { Dictionary ret; Map<StringName, Variant> map; get_constants(&map); - for (Map<StringName, Variant>::Element *E = map.front(); E; E = E->next()) { - ret[E->key()] = E->value(); + for (const KeyValue<StringName, Variant> &E : map) { + ret[E.key] = E.value; } return ret; } void Script::_bind_methods() { - ClassDB::bind_method(D_METHOD("can_instance"), &Script::can_instance); + ClassDB::bind_method(D_METHOD("can_instantiate"), &Script::can_instantiate); //ClassDB::bind_method(D_METHOD("instance_create","base_object"),&Script::instance_create); ClassDB::bind_method(D_METHOD("instance_has", "base_object"), &Script::instance_has); ClassDB::bind_method(D_METHOD("has_source_code"), &Script::has_source_code); @@ -120,7 +120,7 @@ void Script::_bind_methods() { ClassDB::bind_method(D_METHOD("is_tool"), &Script::is_tool); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_code", PROPERTY_HINT_NONE, "", 0), "set_source_code", "get_source_code"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_source_code", "get_source_code"); } void ScriptServer::set_scripting_enabled(bool p_enabled) { @@ -257,8 +257,8 @@ void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) { classes.push_back(*K); } classes.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *E = classes.front(); E; E = E->next()) { - r_global_classes->push_back(E->get()); + for (const StringName &E : classes) { + r_global_classes->push_back(E); } } @@ -266,15 +266,23 @@ void ScriptServer::save_global_classes() { List<StringName> gc; get_global_class_list(&gc); Array gcarr; - for (List<StringName>::Element *E = gc.front(); E; E = E->next()) { + for (const StringName &E : gc) { Dictionary d; - d["class"] = E->get(); - d["language"] = global_classes[E->get()].language; - d["path"] = global_classes[E->get()].path; - d["base"] = global_classes[E->get()].base; + d["class"] = E; + d["language"] = global_classes[E].language; + d["path"] = global_classes[E].path; + d["base"] = global_classes[E].base; gcarr.push_back(d); } + Array old; + if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { + old = ProjectSettings::get_singleton()->get("_global_script_classes"); + } + if ((!old.is_empty() || gcarr.is_empty()) && gcarr.hash() == old.hash()) { + return; + } + if (gcarr.is_empty()) { if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { ProjectSettings::get_singleton()->clear("_global_script_classes"); @@ -289,10 +297,10 @@ void ScriptServer::save_global_classes() { void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) { List<PropertyInfo> pinfo; get_property_list(&pinfo); - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - if (E->get().usage & PROPERTY_USAGE_STORAGE) { + for (const PropertyInfo &E : pinfo) { + if (E.usage & PROPERTY_USAGE_STORAGE) { Pair<StringName, Variant> p; - p.first = E->get().name; + p.first = E.name; if (get(p.first, p.second)) { state.push_back(p); } @@ -345,10 +353,10 @@ void ScriptLanguage::get_core_type_words(List<String> *p_core_type_words) const p_core_type_words->push_back("Vector3i"); p_core_type_words->push_back("Transform2D"); p_core_type_words->push_back("Plane"); - p_core_type_words->push_back("Quat"); + p_core_type_words->push_back("Quaternion"); p_core_type_words->push_back("AABB"); p_core_type_words->push_back("Basis"); - p_core_type_words->push_back("Transform"); + p_core_type_words->push_back("Transform3D"); p_core_type_words->push_back("Color"); p_core_type_words->push_back("StringName"); p_core_type_words->push_back("NodePath"); @@ -422,16 +430,16 @@ bool PlaceHolderScriptInstance::get(const StringName &p_name, Variant &r_ret) co void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const { if (script->is_placeholder_fallback_enabled()) { - for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { - p_properties->push_back(E->get()); + for (const PropertyInfo &E : properties) { + p_properties->push_back(E); } } else { - for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { - PropertyInfo pinfo = E->get(); + for (const PropertyInfo &E : properties) { + PropertyInfo pinfo = E; if (!values.has(pinfo.name)) { pinfo.usage |= PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE; } - p_properties->push_back(E->get()); + p_properties->push_back(E); } } } @@ -481,11 +489,11 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values) { Set<StringName> new_values; - for (const List<PropertyInfo>::Element *E = p_properties.front(); E; E = E->next()) { - StringName n = E->get().name; + for (const PropertyInfo &E : p_properties) { + StringName n = E.name; new_values.insert(n); - if (!values.has(n) || values[n].get_type() != E->get().type) { + if (!values.has(n) || values[n].get_type() != E.type) { if (p_values.has(n)) { values[n] = p_values[n]; } @@ -503,7 +511,7 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c Variant defval; if (script->get_property_default_value(E->key(), defval)) { //remove because it's the same as the default value - if (defval == E->get()) { + if (defval == E) { to_remove.push_back(E->key()); } } @@ -515,7 +523,7 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c } if (owner && owner->get_script_instance() == this) { - owner->_change_notify(); + owner->notify_property_list_changed(); } //change notify @@ -534,8 +542,8 @@ void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name, } bool found = false; - for (const List<PropertyInfo>::Element *F = properties.front(); F; F = F->next()) { - if (F->get().name == p_name) { + for (const PropertyInfo &F : properties) { + if (F.name == p_name) { found = true; break; } @@ -577,14 +585,6 @@ Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_nam return Variant(); } -uint16_t PlaceHolderScriptInstance::get_rpc_method_id(const StringName &p_method) const { - return UINT16_MAX; -} - -uint16_t PlaceHolderScriptInstance::get_rset_property_id(const StringName &p_method) const { - return UINT16_MAX; -} - PlaceHolderScriptInstance::PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner) : owner(p_owner), language(p_language), diff --git a/core/object/script_language.h b/core/object/script_language.h index f9898ccd0c..8d76cbf479 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -32,8 +32,8 @@ #define SCRIPT_LANGUAGE_H #include "core/doc_data.h" -#include "core/io/multiplayer_api.h" #include "core/io/resource.h" +#include "core/multiplayer/multiplayer.h" #include "core/templates/map.h" #include "core/templates/pair.h" @@ -41,21 +41,6 @@ class ScriptLanguage; typedef void (*ScriptEditRequestFunction)(const String &p_path); -struct ScriptNetData { - StringName name; - MultiplayerAPI::RPCMode mode; - bool operator==(ScriptNetData const &p_other) const { - return name == p_other.name; - } -}; - -struct SortNetData { - StringName::AlphCompare compare; - bool operator()(const ScriptNetData &p_a, const ScriptNetData &p_b) const { - return compare(p_a.name, p_b.name); - } -}; - class ScriptServer { enum { MAX_LANGUAGES = 16 @@ -130,7 +115,7 @@ protected: Dictionary _get_script_constant_map(); public: - virtual bool can_instance() const = 0; + virtual bool can_instantiate() const = 0; virtual Ref<Script> get_base_script() const = 0; //for script inheritance @@ -174,17 +159,7 @@ public: virtual bool is_placeholder_fallback_enabled() const { return false; } - virtual Vector<ScriptNetData> get_rpc_methods() const = 0; - virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0; - virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const = 0; - virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const = 0; - virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0; - - virtual Vector<ScriptNetData> get_rset_properties() const = 0; - virtual uint16_t get_rset_property_id(const StringName &p_property) const = 0; - virtual StringName get_rset_property(const uint16_t p_rset_property_id) const = 0; - virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const = 0; - virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0; Script() {} }; @@ -225,23 +200,15 @@ public: virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid); virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid); - virtual Vector<ScriptNetData> get_rpc_methods() const = 0; - virtual uint16_t get_rpc_method_id(const StringName &p_method) const = 0; - virtual StringName get_rpc_method(uint16_t p_id) const = 0; - virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const = 0; - virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0; - - virtual Vector<ScriptNetData> get_rset_properties() const = 0; - virtual uint16_t get_rset_property_id(const StringName &p_variable) const = 0; - virtual StringName get_rset_property(uint16_t p_id) const = 0; - virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const = 0; - virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const = 0; virtual ScriptLanguage *get_language() = 0; virtual ~ScriptInstance(); }; struct ScriptCodeCompletionOption { + /* Keep enum in Sync with: */ + /* /scene/gui/code_edit.h - CodeEdit::CodeCompletionKind */ enum Kind { KIND_CLASS, KIND_FUNCTION, @@ -301,14 +268,21 @@ public: String message; }; + struct ScriptError { + int line = -1; + int column = -1; + String message; + }; + void get_core_type_words(List<String> *p_core_type_words) const; virtual void get_reserved_words(List<String> *p_words) const = 0; + virtual bool is_control_flow_keyword(String p_string) const = 0; virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0; virtual void get_string_delimiters(List<String> *p_delimiters) const = 0; virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0; virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {} virtual bool is_using_templates() { return false; } - virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = nullptr, List<Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const = 0; + virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const = 0; virtual String validate_path(const String &p_path) const { return ""; } virtual Script *create_script() const = 0; virtual bool has_named_classes() const = 0; @@ -336,6 +310,7 @@ public: Ref<Script> script; String class_name; String class_member; + String class_path; int location; }; @@ -444,17 +419,7 @@ public: virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr); virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = nullptr); - virtual Vector<ScriptNetData> get_rpc_methods() const { return Vector<ScriptNetData>(); } - virtual uint16_t get_rpc_method_id(const StringName &p_method) const; - virtual StringName get_rpc_method(uint16_t p_id) const { return StringName(); } - virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const { return MultiplayerAPI::RPC_MODE_DISABLED; } - virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const { return MultiplayerAPI::RPC_MODE_DISABLED; } - - virtual Vector<ScriptNetData> get_rset_properties() const { return Vector<ScriptNetData>(); } - virtual uint16_t get_rset_property_id(const StringName &p_variable) const; - virtual StringName get_rset_property(uint16_t p_id) const { return StringName(); } - virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const { return MultiplayerAPI::RPC_MODE_DISABLED; } - virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const { return MultiplayerAPI::RPC_MODE_DISABLED; } + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const { return Vector<Multiplayer::RPCConfig>(); } PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner); ~PlaceHolderScriptInstance(); diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index c699820e75..b7d2bac96d 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -39,11 +39,15 @@ void UndoRedo::_discard_redo() { } for (int i = current_action + 1; i < actions.size(); i++) { - for (List<Operation>::Element *E = actions.write[i].do_ops.front(); E; E = E->next()) { - if (E->get().type == Operation::TYPE_REFERENCE) { - Object *obj = ObjectDB::get_instance(E->get().object); - if (obj) { - memdelete(obj); + for (Operation &E : actions.write[i].do_ops) { + if (E.type == Operation::TYPE_REFERENCE) { + if (E.ref.is_valid()) { + E.ref.unref(); + } else { + Object *obj = ObjectDB::get_instance(E.object); + if (obj) { + memdelete(obj); + } } } } @@ -53,6 +57,23 @@ void UndoRedo::_discard_redo() { actions.resize(current_action + 1); } +bool UndoRedo::_redo(bool p_execute) { + ERR_FAIL_COND_V(action_level > 0, false); + + if ((current_action + 1) >= actions.size()) { + return false; //nothing to redo + } + + current_action++; + if (p_execute) { + _process_operation_list(actions.write[current_action].do_ops.front()); + } + version++; + emit_signal(SNAME("version_changed")); + + return true; +} + void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { uint32_t ticks = OS::get_singleton()->get_ticks_msec(); @@ -105,8 +126,8 @@ void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIA ERR_FAIL_COND((current_action + 1) >= actions.size()); Operation do_op; do_op.object = p_object->get_instance_id(); - if (Object::cast_to<Reference>(p_object)) { - do_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + if (Object::cast_to<RefCounted>(p_object)) { + do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object)); } do_op.type = Operation::TYPE_METHOD; @@ -131,8 +152,8 @@ void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VAR Operation undo_op; undo_op.object = p_object->get_instance_id(); - if (Object::cast_to<Reference>(p_object)) { - undo_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + if (Object::cast_to<RefCounted>(p_object)) { + undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object)); } undo_op.type = Operation::TYPE_METHOD; @@ -150,8 +171,8 @@ void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, c ERR_FAIL_COND((current_action + 1) >= actions.size()); Operation do_op; do_op.object = p_object->get_instance_id(); - if (Object::cast_to<Reference>(p_object)) { - do_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + if (Object::cast_to<RefCounted>(p_object)) { + do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object)); } do_op.type = Operation::TYPE_PROPERTY; @@ -172,8 +193,8 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, Operation undo_op; undo_op.object = p_object->get_instance_id(); - if (Object::cast_to<Reference>(p_object)) { - undo_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + if (Object::cast_to<RefCounted>(p_object)) { + undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object)); } undo_op.type = Operation::TYPE_PROPERTY; @@ -188,8 +209,8 @@ void UndoRedo::add_do_reference(Object *p_object) { ERR_FAIL_COND((current_action + 1) >= actions.size()); Operation do_op; do_op.object = p_object->get_instance_id(); - if (Object::cast_to<Reference>(p_object)) { - do_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + if (Object::cast_to<RefCounted>(p_object)) { + do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object)); } do_op.type = Operation::TYPE_REFERENCE; @@ -208,8 +229,8 @@ void UndoRedo::add_undo_reference(Object *p_object) { Operation undo_op; undo_op.object = p_object->get_instance_id(); - if (Object::cast_to<Reference>(p_object)) { - undo_op.ref = Ref<Reference>(Object::cast_to<Reference>(p_object)); + if (Object::cast_to<RefCounted>(p_object)) { + undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object)); } undo_op.type = Operation::TYPE_REFERENCE; @@ -223,11 +244,15 @@ void UndoRedo::_pop_history_tail() { return; } - for (List<Operation>::Element *E = actions.write[0].undo_ops.front(); E; E = E->next()) { - if (E->get().type == Operation::TYPE_REFERENCE) { - Object *obj = ObjectDB::get_instance(E->get().object); - if (obj) { - memdelete(obj); + for (Operation &E : actions.write[0].undo_ops) { + if (E.type == Operation::TYPE_REFERENCE) { + if (E.ref.is_valid()) { + E.ref.unref(); + } else { + Object *obj = ObjectDB::get_instance(E.object); + if (obj) { + memdelete(obj); + } } } } @@ -242,7 +267,7 @@ bool UndoRedo::is_committing_action() const { return committing > 0; } -void UndoRedo::commit_action() { +void UndoRedo::commit_action(bool p_execute) { ERR_FAIL_COND(action_level <= 0); action_level--; if (action_level > 0) { @@ -255,8 +280,9 @@ void UndoRedo::commit_action() { } committing++; - redo(); // perform action + _redo(p_execute); // perform action committing--; + if (callback && actions.size() > 0) { callback(callback_ud, actions[actions.size() - 1].name); } @@ -323,19 +349,7 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { } bool UndoRedo::redo() { - ERR_FAIL_COND_V(action_level > 0, false); - - if ((current_action + 1) >= actions.size()) { - return false; //nothing to redo - } - - current_action++; - - _process_operation_list(actions.write[current_action].do_ops.front()); - version++; - emit_signal("version_changed"); - - return true; + return _redo(true); } bool UndoRedo::undo() { @@ -346,11 +360,29 @@ bool UndoRedo::undo() { _process_operation_list(actions.write[current_action].undo_ops.front()); current_action--; version--; - emit_signal("version_changed"); + emit_signal(SNAME("version_changed")); return true; } +int UndoRedo::get_history_count() { + ERR_FAIL_COND_V(action_level > 0, -1); + + return actions.size(); +} + +int UndoRedo::get_current_action() { + ERR_FAIL_COND_V(action_level > 0, -1); + + return current_action; +} + +String UndoRedo::get_action_name(int p_id) { + ERR_FAIL_INDEX_V(p_id, actions.size(), ""); + + return actions[p_id].name; +} + void UndoRedo::clear_history(bool p_increase_version) { ERR_FAIL_COND(action_level > 0); _discard_redo(); @@ -361,7 +393,7 @@ void UndoRedo::clear_history(bool p_increase_version) { if (p_increase_version) { version++; - emit_signal("version_changed"); + emit_signal(SNAME("version_changed")); } } @@ -373,11 +405,11 @@ String UndoRedo::get_current_action_name() const { return actions[current_action].name; } -bool UndoRedo::has_undo() { +bool UndoRedo::has_undo() const { return current_action >= 0; } -bool UndoRedo::has_redo() { +bool UndoRedo::has_redo() const { return (current_action + 1) < actions.size(); } @@ -436,8 +468,8 @@ Variant UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callabl v[i] = *p_args[i + 2]; } - static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5"); - add_do_method(object, method, v[0], v[1], v[2], v[3], v[4]); + static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8"); + add_do_method(object, method, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); return Variant(); } @@ -473,14 +505,14 @@ Variant UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Calla v[i] = *p_args[i + 2]; } - static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5"); - add_undo_method(object, method, v[0], v[1], v[2], v[3], v[4]); + static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8"); + add_undo_method(object, method, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); return Variant(); } void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode"), &UndoRedo::create_action, DEFVAL(MERGE_DISABLE)); - ClassDB::bind_method(D_METHOD("commit_action"), &UndoRedo::commit_action); + ClassDB::bind_method(D_METHOD("commit_action", "execute"), &UndoRedo::commit_action, DEFVAL(true)); ClassDB::bind_method(D_METHOD("is_committing_action"), &UndoRedo::is_committing_action); { @@ -505,8 +537,14 @@ void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("add_undo_property", "object", "property", "value"), &UndoRedo::add_undo_property); ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &UndoRedo::add_do_reference); ClassDB::bind_method(D_METHOD("add_undo_reference", "object"), &UndoRedo::add_undo_reference); + + ClassDB::bind_method(D_METHOD("get_history_count"), &UndoRedo::get_history_count); + ClassDB::bind_method(D_METHOD("get_current_action"), &UndoRedo::get_current_action); + ClassDB::bind_method(D_METHOD("get_action_name", "id"), &UndoRedo::get_action_name); ClassDB::bind_method(D_METHOD("clear_history", "increase_version"), &UndoRedo::clear_history, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_current_action_name"), &UndoRedo::get_current_action_name); + ClassDB::bind_method(D_METHOD("has_undo"), &UndoRedo::has_undo); ClassDB::bind_method(D_METHOD("has_redo"), &UndoRedo::has_redo); ClassDB::bind_method(D_METHOD("get_version"), &UndoRedo::get_version); diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h index 7b28b138c1..d1ce252d86 100644 --- a/core/object/undo_redo.h +++ b/core/object/undo_redo.h @@ -32,7 +32,7 @@ #define UNDO_REDO_H #include "core/object/class_db.h" -#include "core/object/reference.h" +#include "core/object/ref_counted.h" class UndoRedo : public Object { GDCLASS(UndoRedo, Object); @@ -61,7 +61,7 @@ private: }; Type type; - Ref<Reference> ref; + Ref<RefCounted> ref; ObjectID object; StringName name; Variant args[VARIANT_ARG_MAX]; @@ -84,6 +84,7 @@ private: void _pop_history_tail(); void _process_operation_list(List<Operation>::Element *E); void _discard_redo(); + bool _redo(bool p_execute); CommitNotifyCallback callback = nullptr; void *callback_ud = nullptr; @@ -109,15 +110,19 @@ public: void add_undo_reference(Object *p_object); bool is_committing_action() const; - void commit_action(); + void commit_action(bool p_execute = true); bool redo(); bool undo(); String get_current_action_name() const; + + int get_history_count(); + int get_current_action(); + String get_action_name(int p_id); void clear_history(bool p_increase_version = true); - bool has_undo(); - bool has_redo(); + bool has_undo() const; + bool has_redo() const; uint64_t get_version() const; |