diff options
Diffstat (limited to 'core/object')
23 files changed, 3925 insertions, 1605 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.cpp b/core/object/callable_method_pointer.cpp index 2bfaef744d..81f8ab6be2 100644 --- a/core/object/callable_method_pointer.cpp +++ b/core/object/callable_method_pointer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -85,9 +85,9 @@ void CallableCustomMethodPointerBase::_setup(uint32_t *p_base_ptr, uint32_t p_pt // Precompute hash. for (uint32_t i = 0; i < comp_size; i++) { if (i == 0) { - h = hash_djb2_one_32(comp_ptr[i]); + h = hash_murmur3_one_32(comp_ptr[i]); } else { - h = hash_djb2_one_32(comp_ptr[i], h); + h = hash_murmur3_one_32(comp_ptr[i], h); } } } diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h index 115797a00c..f2a440b49a 100644 --- a/core/object/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,14 +32,13 @@ #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" #include "core/variant/callable.h" class CallableCustomMethodPointerBase : public CallableCustom { - uint32_t *comp_ptr; + uint32_t *comp_ptr = nullptr; uint32_t comp_size; uint32_t h; #ifdef DEBUG_METHODS_ENABLED @@ -52,6 +51,14 @@ protected: void _setup(uint32_t *p_base_ptr, uint32_t p_ptr_size); public: + virtual StringName get_method() const { +#ifdef DEBUG_METHODS_ENABLED + return StringName(text); +#else + return StringName(); +#endif + } + #ifdef DEBUG_METHODS_ENABLED void set_text(const char *p_text) { text = p_text; @@ -98,7 +105,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 +160,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 +215,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(); @@ -238,4 +245,86 @@ Callable create_custom_callable_function_pointer(T *p_instance, #define callable_mp(I, M) create_custom_callable_function_pointer(I, M) #endif +// STATIC VERSIONS + +template <class... P> +class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase { + struct Data { + void (*method)(P...); + } data; + +public: + virtual ObjectID get_object() const { + return ObjectID(); + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { + call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); + r_return_value = Variant(); + } + + CallableCustomStaticMethodPointer(void (*p_method)(P...)) { + memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); + } +}; + +template <class T, class... P> +Callable create_custom_callable_static_function_pointer( +#ifdef DEBUG_METHODS_ENABLED + const char *p_func_text, +#endif + void (*p_method)(P...)) { + typedef CallableCustomStaticMethodPointer<P...> CCMP; // Messes with memnew otherwise. + CCMP *ccmp = memnew(CCMP(p_method)); +#ifdef DEBUG_METHODS_ENABLED + ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. +#endif + return Callable(ccmp); +} + +template <class R, class... P> +class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase { + struct Data { + R(*method) + (P...); + } data; + +public: + virtual ObjectID get_object() const { + return ObjectID(); + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { + call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); + } + + CallableCustomStaticMethodPointerRet(R (*p_method)(P...)) { + memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); + } +}; + +template <class R, class... P> +Callable create_custom_callable_static_function_pointer( +#ifdef DEBUG_METHODS_ENABLED + const char *p_func_text, +#endif + R (*p_method)(P...)) { + typedef CallableCustomStaticMethodPointerRet<R, P...> CCMP; // Messes with memnew otherwise. + CCMP *ccmp = memnew(CCMP(p_method)); +#ifdef DEBUG_METHODS_ENABLED + ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. +#endif + return Callable(ccmp); +} + +#ifdef DEBUG_METHODS_ENABLED +#define callable_mp_static(M) create_custom_callable_static_function_pointer(#M, M) +#else +#define callable_mp_static(M) create_custom_callable_static_function_pointer(M) +#endif + #endif // CALLABLE_METHOD_POINTER_H diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 375ad8fae1..d67315f20d 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,190 +39,13 @@ #ifdef DEBUG_METHODS_ENABLED -MethodDefinition D_METHOD(const char *p_name) { +MethodDefinition D_METHODP(const char *p_name, const char *const **p_args, uint32_t p_argcount) { MethodDefinition md; md.name = StaticCString::create(p_name); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.push_back(StaticCString::create(p_arg1)); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(2); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(3); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(4); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(5); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(6); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(7); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(8); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(9); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - md.args.write[8] = StaticCString::create(p_arg9); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(10); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - md.args.write[8] = StaticCString::create(p_arg9); - md.args.write[9] = StaticCString::create(p_arg10); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(11); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - md.args.write[8] = StaticCString::create(p_arg9); - md.args.write[9] = StaticCString::create(p_arg10); - md.args.write[10] = StaticCString::create(p_arg11); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(12); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - md.args.write[8] = StaticCString::create(p_arg9); - md.args.write[9] = StaticCString::create(p_arg10); - md.args.write[10] = StaticCString::create(p_arg11); - md.args.write[11] = StaticCString::create(p_arg12); - return md; -} - -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12, const char *p_arg13) { - MethodDefinition md; - md.name = StaticCString::create(p_name); - md.args.resize(13); - md.args.write[0] = StaticCString::create(p_arg1); - md.args.write[1] = StaticCString::create(p_arg2); - md.args.write[2] = StaticCString::create(p_arg3); - md.args.write[3] = StaticCString::create(p_arg4); - md.args.write[4] = StaticCString::create(p_arg5); - md.args.write[5] = StaticCString::create(p_arg6); - md.args.write[6] = StaticCString::create(p_arg7); - md.args.write[7] = StaticCString::create(p_arg8); - md.args.write[8] = StaticCString::create(p_arg9); - md.args.write[9] = StaticCString::create(p_arg10); - md.args.write[10] = StaticCString::create(p_arg11); - md.args.write[11] = StaticCString::create(p_arg12); - md.args.write[12] = StaticCString::create(p_arg13); + md.args.resize(p_argcount); + for (uint32_t i = 0; i < p_argcount; i++) { + md.args.write[i] = StaticCString::create(*p_args[i]); + } return md; } @@ -267,10 +90,8 @@ bool ClassDB::is_parent_class(const StringName &p_class, const StringName &p_inh void ClassDB::get_class_list(List<StringName> *p_classes) { OBJTYPE_RLOCK; - const StringName *k = nullptr; - - while ((k = classes.next(k))) { - p_classes->push_back(*k); + for (const KeyValue<StringName, ClassInfo> &E : classes) { + p_classes->push_back(E.key); } p_classes->sort(); @@ -279,11 +100,9 @@ void ClassDB::get_class_list(List<StringName> *p_classes) { void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { OBJTYPE_RLOCK; - const StringName *k = nullptr; - - while ((k = classes.next(k))) { - if (*k != p_class && _is_parent_class(*k, p_class)) { - p_classes->push_back(*k); + for (const KeyValue<StringName, ClassInfo> &E : classes) { + if (E.key != p_class && _is_parent_class(E.key, p_class)) { + p_classes->push_back(E.key); } } } @@ -291,11 +110,9 @@ void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringNa void ClassDB::get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { OBJTYPE_RLOCK; - const StringName *k = nullptr; - - while ((k = classes.next(k))) { - if (*k != p_class && _get_parent_class(*k) == p_class) { - p_classes->push_back(*k); + for (const KeyValue<StringName, ClassInfo> &E : classes) { + if (E.key != p_class && _get_parent_class(E.key) == p_class) { + p_classes->push_back(E.key); } } } @@ -347,35 +164,28 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { OBJTYPE_RLOCK; #ifdef DEBUG_METHODS_ENABLED - uint64_t hash = hash_djb2_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG)); - - List<StringName> names; + uint64_t hash = hash_murmur3_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG)); - const StringName *k = nullptr; + List<StringName> class_list; + ClassDB::get_class_list(&class_list); + // Must be alphabetically sorted for hash to compute. + class_list.sort_custom<StringName::AlphCompare>(); - while ((k = classes.next(k))) { - names.push_back(*k); - } - //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 : class_list) { + 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; } - hash = hash_djb2_one_64(t->name.hash(), hash); - hash = hash_djb2_one_64(t->inherits.hash(), hash); + hash = hash_murmur3_one_64(t->name.hash(), hash); + hash = hash_murmur3_one_64(t->inherits.hash(), hash); { //methods List<StringName> snames; - k = nullptr; - - while ((k = t->method_map.next(k))) { - String name = k->operator String(); + for (const KeyValue<StringName, MethodBind *> &F : t->method_map) { + String name = F.key.operator String(); ERR_CONTINUE(name.is_empty()); @@ -383,34 +193,34 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { continue; // Ignore non-virtual methods that start with an underscore } - snames.push_back(*k); + snames.push_back(F.key); } snames.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - MethodBind *mb = t->method_map[F->get()]; - 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 + for (const StringName &F : snames) { + MethodBind *mb = t->method_map[F]; + hash = hash_murmur3_one_64(mb->get_name().hash(), hash); + hash = hash_murmur3_one_64(mb->get_argument_count(), hash); + hash = hash_murmur3_one_64(mb->get_argument_type(-1), hash); //return for (int i = 0; i < mb->get_argument_count(); i++) { const PropertyInfo info = mb->get_argument_info(i); - hash = hash_djb2_one_64(info.type, hash); - hash = hash_djb2_one_64(info.name.hash(), hash); - hash = hash_djb2_one_64(info.hint, hash); - hash = hash_djb2_one_64(info.hint_string.hash(), hash); + hash = hash_murmur3_one_64(info.type, hash); + hash = hash_murmur3_one_64(info.name.hash(), hash); + hash = hash_murmur3_one_64(info.hint, hash); + hash = hash_murmur3_one_64(info.hint_string.hash(), hash); } - hash = hash_djb2_one_64(mb->get_default_argument_count(), hash); + hash = hash_murmur3_one_64(mb->get_default_argument_count(), hash); for (int i = 0; i < mb->get_default_argument_count(); i++) { //hash should not change, i hope for tis Variant da = mb->get_default_argument(i); - hash = hash_djb2_one_64(da.hash(), hash); + hash = hash_murmur3_one_64(da.hash(), hash); } - hash = hash_djb2_one_64(mb->get_hint_flags(), hash); + hash = hash_murmur3_one_64(mb->get_hint_flags(), hash); } } @@ -418,17 +228,15 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { List<StringName> snames; - k = nullptr; - - while ((k = t->constant_map.next(k))) { - snames.push_back(*k); + for (const KeyValue<StringName, int64_t> &F : t->constant_map) { + snames.push_back(F.key); } 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_murmur3_one_64(F.hash(), hash); + hash = hash_murmur3_one_64(t->constant_map[F], hash); } } @@ -436,19 +244,17 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { List<StringName> snames; - k = nullptr; - - while ((k = t->signal_map.next(k))) { - snames.push_back(*k); + for (const KeyValue<StringName, MethodInfo> &F : t->signal_map) { + snames.push_back(F.key); } 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_murmur3_one_64(F.hash(), hash); for (int i = 0; i < mi.arguments.size(); i++) { - hash = hash_djb2_one_64(mi.arguments[i].type, hash); + hash = hash_murmur3_one_64(mi.arguments[i].type, hash); } } } @@ -457,35 +263,33 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { List<StringName> snames; - k = nullptr; - - while ((k = t->property_setget.next(k))) { - snames.push_back(*k); + for (const KeyValue<StringName, PropertySetGet> &F : t->property_setget) { + snames.push_back(F.key); } 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(psg->setter.hash(), hash); - hash = hash_djb2_one_64(psg->getter.hash(), hash); + hash = hash_murmur3_one_64(F.hash(), hash); + hash = hash_murmur3_one_64(psg->setter.hash(), hash); + hash = hash_murmur3_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_murmur3_one_64(F.name.hash(), hash); + hash = hash_murmur3_one_64(F.type, hash); + hash = hash_murmur3_one_64(F.hint, hash); + hash = hash_murmur3_one_64(F.hint_string.hash(), hash); + hash = hash_murmur3_one_64(F.usage, hash); } } - return hash; + return hash_fmix32(hash); #else return 0; #endif @@ -501,19 +305,26 @@ void ClassDB::add_compatibility_class(const StringName &p_class, const StringNam compat_classes[p_class] = p_fallback; } -Object *ClassDB::instance(const StringName &p_class) { +StringName ClassDB::get_compatibility_class(const StringName &p_class) { + if (compat_classes.has(p_class)) { + return compat_classes[p_class]; + } + return StringName(); +} + +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]); } } ERR_FAIL_COND_V_MSG(!ti, nullptr, "Cannot get class '" + String(p_class) + "'."); ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled."); - ERR_FAIL_COND_V(!ti->creation_func, nullptr); + ERR_FAIL_COND_V_MSG(!ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated."); } #ifdef TOOLS_ENABLED if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { @@ -521,10 +332,34 @@ Object *ClassDB::instance(const StringName &p_class) { return nullptr; } #endif - return ti->creation_func(); + if (ti->native_extension && ti->native_extension->create_instance) { + return (Object *)ti->native_extension->create_instance(ti->native_extension->class_userdata); + } else { + return ti->creation_func(); + } } -bool ClassDB::can_instance(const StringName &p_class) { +void ClassDB::set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance) { + ERR_FAIL_COND(!p_object); + ClassInfo *ti; + { + OBJTYPE_RLOCK; + ti = classes.getptr(p_class); + 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]); + } + } + ERR_FAIL_COND_MSG(!ti, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_COND_MSG(ti->disabled, "Class '" + String(p_class) + "' is disabled."); + ERR_FAIL_COND_MSG(!ti->native_extension, "Class '" + String(p_class) + "' has no native extension."); + } + + p_object->_extension = ti->native_extension; + p_object->_extension_instance = p_instance; +} + +bool ClassDB::can_instantiate(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); @@ -534,7 +369,20 @@ 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)); +} + +bool ClassDB::is_virtual(const StringName &p_class) { + OBJTYPE_RLOCK; + + ClassInfo *ti = classes.getptr(p_class); + ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'."); +#ifdef TOOLS_ENABLED + if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { + return false; + } +#endif + return (!ti->disabled && ti->creation_func != nullptr && !(ti->native_extension && !ti->native_extension->create_instance) && ti->is_virtual); } void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) { @@ -559,7 +407,6 @@ void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherit } } -#ifdef DEBUG_METHODS_ENABLED static MethodInfo info_from_bind(MethodBind *p_method) { MethodInfo minfo; minfo.name = p_method->get_name(); @@ -580,9 +427,8 @@ static MethodInfo info_from_bind(MethodBind *p_method) { return minfo; } -#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 +445,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); @@ -616,13 +462,10 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b #else - const StringName *K = nullptr; - - while ((K = type->method_map.next(K))) { - MethodBind *m = type->method_map[*K]; - MethodInfo mi; - mi.name = m->get_name(); - p_methods->push_back(mi); + for (KeyValue<StringName, MethodBind *> &E : type->method_map) { + MethodBind *m = E.value; + MethodInfo minfo = info_from_bind(m); + p_methods->push_back(minfo); } #endif @@ -635,7 +478,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); @@ -668,9 +511,8 @@ bool ClassDB::get_method_info(StringName p_class, StringName p_method, MethodInf if (type->method_map.has(p_method)) { if (r_info) { MethodBind *m = type->method_map[p_method]; - MethodInfo mi; - mi.name = m->get_name(); - *r_info = mi; + MethodInfo minfo = info_from_bind(m); + *r_info = minfo; } return true; } @@ -686,7 +528,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); @@ -701,7 +543,7 @@ MethodBind *ClassDB::get_method(StringName p_class, StringName p_name) { return nullptr; } -void ClassDB::bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int p_constant) { +void ClassDB::bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant, bool p_is_bitfield) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); @@ -715,18 +557,20 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName type->constant_map[p_name] = p_constant; String enum_name = p_enum; - if (enum_name != String()) { - if (enum_name.find(".") != -1) { + if (!enum_name.is_empty()) { + if (enum_name.contains(".")) { enum_name = enum_name.get_slicec('.', 1); } - List<StringName> *constants_list = type->enum_map.getptr(enum_name); + ClassInfo::EnumInfo *constants_list = type->enum_map.getptr(enum_name); if (constants_list) { - constants_list->push_back(p_name); + constants_list->constants.push_back(p_name); + constants_list->is_bitfield = p_is_bitfield; } else { - List<StringName> new_list; - new_list.push_back(p_name); + ClassInfo::EnumInfo new_list; + new_list.is_bitfield = p_is_bitfield; + new_list.constants.push_back(p_name); type->enum_map[enum_name] = new_list; } } @@ -743,14 +587,13 @@ 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; - while ((K = type->constant_map.next(K))) { - p_constants->push_back(*K); + for (const KeyValue<StringName, int64_t> &E : type->constant_map) { + p_constants->push_back(E.key); } #endif @@ -762,13 +605,13 @@ void ClassDB::get_integer_constant_list(const StringName &p_class, List<String> } } -int ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success) { +int64_t ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); while (type) { - int *constant = type->constant_map.getptr(p_name); + int64_t *constant = type->constant_map.getptr(p_name); if (constant) { if (p_success) { *p_success = true; @@ -811,12 +654,11 @@ StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const S ClassInfo *type = classes.getptr(p_class); while (type) { - const StringName *k = nullptr; - while ((k = type->enum_map.next(k))) { - List<StringName> &constants_list = type->enum_map.get(*k); + for (KeyValue<StringName, ClassInfo::EnumInfo> &E : type->enum_map) { + List<StringName> &constants_list = E.value.constants; const List<StringName>::Element *found = constants_list.find(p_name); if (found) { - return *k; + return E.key; } } @@ -836,9 +678,8 @@ void ClassDB::get_enum_list(const StringName &p_class, List<StringName> *p_enums ClassInfo *type = classes.getptr(p_class); while (type) { - const StringName *k = nullptr; - while ((k = type->enum_map.next(k))) { - p_enums->push_back(*k); + for (KeyValue<StringName, ClassInfo::EnumInfo> &E : type->enum_map) { + p_enums->push_back(E.key); } if (p_no_inheritance) { @@ -855,10 +696,10 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_ ClassInfo *type = classes.getptr(p_class); while (type) { - const List<StringName> *constants = type->enum_map.getptr(p_enum); + const ClassInfo::EnumInfo *constants = type->enum_map.getptr(p_enum); if (constants) { - for (const List<StringName>::Element *E = constants->front(); E; E = E->next()) { + for (const List<StringName>::Element *E = constants->constants.front(); E; E = E->next()) { p_constants->push_back(E->get()); } } @@ -871,6 +712,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 +757,26 @@ 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) { +bool ClassDB::is_enum_bitfield(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { + OBJTYPE_RLOCK; + + ClassInfo *type = classes.getptr(p_class); + + while (type) { + if (type->enum_map.has(p_name) && type->enum_map[p_name].is_bitfield) { + return true; + } + if (p_no_inheritance) { + return false; + } + + type = type->inherits_ptr; + } + + return false; +} + +void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); @@ -909,7 +795,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); @@ -918,9 +804,8 @@ void ClassDB::get_signal_list(StringName p_class, List<MethodInfo> *p_signals, b ClassInfo *check = type; while (check) { - const StringName *S = nullptr; - while ((S = check->signal_map.next(S))) { - p_signals->push_back(check->signal_map[*S]); + for (KeyValue<StringName, MethodInfo> &E : check->signal_map) { + p_signals->push_back(E.value); } if (p_no_inheritance) { @@ -931,7 +816,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 +833,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,24 +850,46 @@ 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, int p_indent_depth) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); - type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_GROUP)); + String prefix = p_prefix; + if (p_indent_depth > 0) { + prefix = vformat("%s,%d", p_prefix, p_indent_depth); + } + + type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, 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, int p_indent_depth) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); - type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_SUBGROUP)); + String prefix = p_prefix; + if (p_indent_depth > 0) { + prefix = vformat("%s,%d", p_prefix, p_indent_depth); + } + + type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, 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) { +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(); @@ -1040,26 +947,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 +992,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 +1017,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) { @@ -1116,7 +1040,7 @@ bool ClassDB::set_property(Object *p_object, const StringName &p_property, const if (psg->_setptr) { psg->_setptr->call(p_object, arg, 2, ce); } else { - p_object->call(psg->setter, arg, 2, ce); + p_object->callp(psg->setter, arg, 2, ce); } } else { @@ -1124,7 +1048,7 @@ bool ClassDB::set_property(Object *p_object, const StringName &p_property, const if (psg->_setptr) { psg->_setptr->call(p_object, arg, 1, ce); } else { - p_object->call(psg->setter, arg, 1, ce); + p_object->callp(psg->setter, arg, 1, ce); } } @@ -1142,6 +1066,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) { @@ -1155,20 +1081,20 @@ bool ClassDB::get_property(Object *p_object, const StringName &p_property, Varia Variant index = psg->index; const Variant *arg[1] = { &index }; Callable::CallError ce; - r_value = p_object->call(psg->getter, arg, 1, ce); + r_value = p_object->callp(psg->getter, arg, 1, ce); } else { Callable::CallError ce; if (psg->_getptr) { r_value = psg->_getptr->call(p_object, nullptr, 0, ce); } else { - r_value = p_object->call(psg->getter, nullptr, 0, ce); + r_value = p_object->callp(psg->getter, nullptr, 0, ce); } } return true; } - const int *c = check->constant_map.getptr(p_property); //constants count + const int64_t *c = check->constant_map.getptr(p_property); //constants count if (c) { r_value = *c; return true; @@ -1234,7 +1160,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 +1175,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 +1207,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 +1216,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 +1232,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 +1307,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 +1317,24 @@ 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_object_core) { + if (p_arg_names.size() != mi.arguments.size()) { + WARN_PRINT("Mismatch argument name count for virtual method: " + 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]; + } + } + } + + if (classes[p_class].virtual_methods_map.has(p_method.name)) { + // overloading not supported + ERR_FAIL_MSG("Virtual method already bound '" + String(p_class) + "::" + p_method.name + "'."); + } classes[p_class].virtual_methods.push_back(mi); classes[p_class].virtual_methods_map[p_method.name] = mi; @@ -1387,8 +1349,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 +1362,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 +1383,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); @@ -1429,15 +1391,6 @@ bool ClassDB::is_class_exposed(StringName p_class) { return ti->exposed; } -StringName ClassDB::get_category(const StringName &p_node) { - ERR_FAIL_COND_V(!classes.has(p_node), StringName()); -#ifdef DEBUG_ENABLED - return classes[p_node].category; -#else - return StringName(); -#endif -} - void ClassDB::add_resource_base_extension(const StringName &p_extension, const StringName &p_class) { if (resource_base_extensions.has(p_extension)) { return; @@ -1447,26 +1400,25 @@ void ClassDB::add_resource_base_extension(const StringName &p_extension, const S } void ClassDB::get_resource_base_extensions(List<String> *p_extensions) { - const StringName *K = nullptr; - - while ((K = resource_base_extensions.next(K))) { - p_extensions->push_back(*K); + for (const KeyValue<StringName, StringName> &E : resource_base_extensions) { + p_extensions->push_back(E.key); } } -void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p_extensions) { - const StringName *K = nullptr; +bool ClassDB::is_resource_extension(const StringName &p_extension) { + return resource_base_extensions.has(p_extension); +} - while ((K = resource_base_extensions.next(K))) { - StringName cmp = resource_base_extensions[*K]; - if (is_parent_class(p_class, cmp) || is_parent_class(cmp, p_class)) { - p_extensions->push_back(*K); +void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p_extensions) { + for (const KeyValue<StringName, StringName> &E : resource_base_extensions) { + if (is_parent_class(p_class, E.value) || is_parent_class(E.value, p_class)) { + p_extensions->push_back(E.key); } } } HashMap<StringName, HashMap<StringName, Variant>> ClassDB::default_values; -Set<StringName> ClassDB::default_values_cached; +HashSet<StringName> ClassDB::default_values_cached; Variant ClassDB::class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid) { if (!default_values_cached.has(p_class)) { @@ -1480,19 +1432,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) && !ClassDB::is_virtual(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; } } } @@ -1529,7 +1481,8 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con // Some properties may have an instantiated Object as default value, // (like Path2D's `curve` used to have), but that's not a good practice. // Instead, those properties should use PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT - // to be auto-instantiated when created in the editor. + // to be auto-instantiated when created in the editor with the following method: + // EditorNode::get_editor_data().instantiate_object_properties(obj); if (var.get_type() == Variant::OBJECT) { Object *obj = var.get_validated_object(); if (obj) { @@ -1541,6 +1494,56 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con return var; } +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; + + 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); +} + +HashMap<StringName, ClassDB::NativeStruct> ClassDB::native_structs; +void ClassDB::register_native_struct(const StringName &p_name, const String &p_code, uint64_t p_current_size) { + NativeStruct ns; + ns.ccode = p_code; + ns.struct_size = p_current_size; + native_structs[p_name] = ns; +} + +void ClassDB::get_native_struct_list(List<StringName> *r_names) { + for (const KeyValue<StringName, NativeStruct> &E : native_structs) { + r_names->push_back(E.key); + } +} + +String ClassDB::get_native_struct_code(const StringName &p_name) { + ERR_FAIL_COND_V(!native_structs.has(p_name), String()); + return native_structs[p_name].ccode; +} + +uint64_t ClassDB::get_native_struct_size(const StringName &p_name) { + ERR_FAIL_COND_V(!native_structs.has(p_name), 0); + return native_structs[p_name].struct_size; +} + RWLock ClassDB::lock; void ClassDB::cleanup_defaults() { @@ -1551,19 +1554,17 @@ void ClassDB::cleanup_defaults() { void ClassDB::cleanup() { //OBJTYPE_LOCK; hah not here - const StringName *k = nullptr; - - while ((k = classes.next(k))) { - ClassInfo &ti = classes[*k]; + for (KeyValue<StringName, ClassInfo> &E : classes) { + ClassInfo &ti = E.value; - const StringName *m = nullptr; - while ((m = ti.method_map.next(m))) { - memdelete(ti.method_map[*m]); + for (KeyValue<StringName, MethodBind *> &F : ti.method_map) { + memdelete(F.value); } } classes.clear(); resource_base_extensions.clear(); compat_classes.clear(); + native_structs.clear(); } // diff --git a/core/object/class_db.h b/core/object/class_db.h index 6fd5748dbb..8b6a260d86 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,13 +35,10 @@ #include "core/object/object.h" #include "core/string/print_string.h" -/** To bind more then 6 parameters include this: - * - */ - // Makes callable_mp readily available in all classes connecting signals. // Needs to come after method_bind and object have been included. #include "core/object/callable_method_pointer.h" +#include "core/templates/hash_set.h" #define DEFVAL(m_defval) (m_defval) @@ -57,29 +54,17 @@ struct MethodDefinition { name(p_name) {} }; -MethodDefinition D_METHOD(const char *p_name); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12); -MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_arg2, const char *p_arg3, const char *p_arg4, const char *p_arg5, const char *p_arg6, const char *p_arg7, const char *p_arg8, const char *p_arg9, const char *p_arg10, const char *p_arg11, const char *p_arg12, const char *p_arg13); - -#else +MethodDefinition D_METHODP(const char *p_name, const char *const **p_args, uint32_t p_argcount); -//#define NO_VARIADIC_MACROS - -#ifdef NO_VARIADIC_MACROS +template <typename... VarArgs> +MethodDefinition D_METHOD(const char *p_name, const VarArgs... p_args) { + const char *args[sizeof...(p_args) + 1] = { p_args..., nullptr }; // +1 makes sure zero sized arrays are also supported. + const char *const *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } -static _FORCE_INLINE_ const char *D_METHOD(const char *m_name, ...) { - return m_name; + return D_METHODP(p_name, sizeof...(p_args) == 0 ? nullptr : (const char *const **)argptrs, sizeof...(p_args)); } #else @@ -90,13 +75,13 @@ static _FORCE_INLINE_ const char *D_METHOD(const char *m_name, ...) { #endif -#endif - class ClassDB { public: enum APIType { API_CORE, API_EDITOR, + API_EXTENSION, + API_EDITOR_EXTENSION, API_NONE }; @@ -105,8 +90,8 @@ public: int index; StringName setter; StringName getter; - MethodBind *_setptr; - MethodBind *_getptr; + MethodBind *_setptr = nullptr; + MethodBind *_getptr = nullptr; Variant::Type type; }; @@ -115,19 +100,26 @@ 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; + HashMap<StringName, int64_t> constant_map; + struct EnumInfo { + List<StringName> constants; + bool is_bitfield = false; + }; + + HashMap<StringName, EnumInfo> enum_map; HashMap<StringName, MethodInfo> signal_map; List<PropertyInfo> property_list; HashMap<StringName, PropertyInfo> property_map; #ifdef DEBUG_METHODS_ENABLED List<StringName> constant_order; List<StringName> method_order; - Set<StringName> methods_in_properties; + HashSet<StringName> methods_in_properties; List<MethodInfo> virtual_methods; - Map<StringName, MethodInfo> virtual_methods_map; - StringName category; + HashMap<StringName, MethodInfo> virtual_methods_map; + HashMap<StringName, Vector<Error>> method_error_values; #endif HashMap<StringName, PropertySetGet> property_setget; @@ -135,6 +127,7 @@ public: StringName name; bool disabled = false; bool exposed = false; + bool is_virtual = false; Object *(*creation_func)() = nullptr; ClassInfo() {} @@ -162,7 +155,14 @@ public: static void _add_class2(const StringName &p_class, const StringName &p_inherits); static HashMap<StringName, HashMap<StringName, Variant>> default_values; - static Set<StringName> default_values_cached; + static HashSet<StringName> default_values_cached; + + // Native structs, used by binder + struct NativeStruct { + String ccode; // C code to create the native struct, fields separated by ; Arrays accepted (even containing other structs), also function pointers. All types must be Godot types. + uint64_t struct_size; // local size of struct, for comparison + }; + static HashMap<StringName, NativeStruct> native_structs; private: // Non-locking variants of get_parent_class and is_parent_class. @@ -177,28 +177,34 @@ public: } template <class T> - static void register_class() { + static void register_class(bool p_virtual = false) { GLOBAL_LOCK_FUNCTION; T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_COND(!t); t->creation_func = &creator<T>; t->exposed = true; + t->is_virtual = p_virtual; t->class_ptr = T::get_class_ptr_static(); + t->api = current_api; T::register_custom_data_to_otdb(); } template <class T> - static void register_virtual_class() { + static void register_abstract_class() { GLOBAL_LOCK_FUNCTION; T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_COND(!t); t->exposed = true; t->class_ptr = T::get_class_ptr_static(); + t->api = current_api; //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(); @@ -213,6 +219,7 @@ public: t->creation_func = &_create_ptr_func<T>; t->exposed = true; t->class_ptr = T::get_class_ptr_static(); + t->api = current_api; T::register_custom_data_to_otdb(); } @@ -224,85 +231,40 @@ 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 bool is_virtual(const StringName &p_class); + static Object *instantiate(const StringName &p_class); + static void set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance); + static APIType get_api_type(const StringName &p_class); static uint64_t get_api_hash(APIType p_api); - template <class N, class M> - static MethodBind *bind_method(N p_method_name, M p_method) { - MethodBind *bind = create_method_bind(p_method); - - return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, nullptr, 0); //use static function, much smaller binary usage - } - - template <class N, class M> - static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1) { - MethodBind *bind = create_method_bind(p_method); - const Variant *ptr[1] = { &p_def1 }; - - return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 1); - } - - template <class N, class M> - static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2) { - MethodBind *bind = create_method_bind(p_method); - const Variant *ptr[2] = { &p_def1, &p_def2 }; - - return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 2); - } - - template <class N, class M> - static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3) { - MethodBind *bind = create_method_bind(p_method); - const Variant *ptr[3] = { &p_def1, &p_def2, &p_def3 }; - - return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 3); - } - - template <class N, class M> - static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4) { - MethodBind *bind = create_method_bind(p_method); - const Variant *ptr[4] = { &p_def1, &p_def2, &p_def3, &p_def4 }; - - return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 4); - } - - template <class N, class M> - static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5) { - MethodBind *bind = create_method_bind(p_method); - const Variant *ptr[5] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5 }; - - return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 5); - } - - template <class N, class M> - static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5, const Variant &p_def6) { - MethodBind *bind = create_method_bind(p_method); - const Variant *ptr[6] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5, &p_def6 }; - - return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 6); - } - - template <class N, class M> - static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5, const Variant &p_def6, const Variant &p_def7) { + template <class N, class M, typename... VarArgs> + static MethodBind *bind_method(N p_method_name, M p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } MethodBind *bind = create_method_bind(p_method); - const Variant *ptr[7] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5, &p_def6, &p_def7 }; - - return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 7); + return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } - template <class N, class M> - static MethodBind *bind_method(N p_method_name, M p_method, const Variant &p_def1, const Variant &p_def2, const Variant &p_def3, const Variant &p_def4, const Variant &p_def5, const Variant &p_def6, const Variant &p_def7, const Variant &p_def8) { - MethodBind *bind = create_method_bind(p_method); - const Variant *ptr[8] = { &p_def1, &p_def2, &p_def3, &p_def4, &p_def5, &p_def6, &p_def7, &p_def8 }; - - return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, ptr, 8); + template <class N, class M, typename... VarArgs> + static MethodBind *bind_static_method(const StringName &p_class, N p_method_name, M p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + MethodBind *bind = create_static_method_bind(p_method); + bind->set_instance_class(p_class); + return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } 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 +283,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,93 +296,144 @@ 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 = "", int p_indent_depth = 0); + static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix = "", int p_indent_depth = 0); + 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_DEFAULT); + 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); + static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant, bool p_is_bitfield = false); static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false); - static int get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success = nullptr); + static int64_t get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success = nullptr); static bool has_integer_constant(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false); static StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false); + static StringName get_integer_constant_bitfield(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false); static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false); 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 bool is_enum_bitfield(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 StringName get_compatibility_class(const StringName &p_class); static void set_current_api(APIType p_api); static APIType get_current_api(); static void cleanup_defaults(); static void cleanup(); + + static void register_native_struct(const StringName &p_name, const String &p_code, uint64_t p_current_size); + static void get_native_struct_list(List<StringName> *r_names); + static String get_native_struct_code(const StringName &p_name); + static uint64_t get_native_struct_size(const StringName &p_name); // Used for asserting }; #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); + ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant); + +#define BIND_BITFIELD_FLAG(m_constant) \ + ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, true); + +_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr) { +} + +_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr, const Error &p_err) { + arr.push_back(p_err); +} + +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...); +} + +template <class... P> +_FORCE_INLINE_ Vector<Error> errarray(P... p_args) { + Vector<Error> arr; + errarray_add_str(arr, p_args...); + return arr; +} + +#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_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(), StringName(), #m_constant, m_constant); + ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); -#endif +#define BIND_BITFIELD_FLAG(m_constant) \ + ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant, true); -#ifdef TOOLS_ENABLED +#define BIND_METHOD_ERR_RETURN_DOC(m_method, ...) -#define BIND_VMETHOD(m_method) \ - ClassDB::add_virtual_method(get_class_static(), m_method); +#endif -#else +#define GDREGISTER_CLASS(m_class) \ + if (m_class::_class_is_enabled) { \ + ::ClassDB::register_class<m_class>(); \ + } +#define GDREGISTER_VIRTUAL_CLASS(m_class) \ + if (m_class::_class_is_enabled) { \ + ::ClassDB::register_class<m_class>(true); \ + } +#define GDREGISTER_ABSTRACT_CLASS(m_class) \ + if (m_class::_class_is_enabled) { \ + ::ClassDB::register_abstract_class<m_class>(); \ + } -#define BIND_VMETHOD(m_method) +#define GDREGISTER_NATIVE_STRUCT(m_class, m_code) ClassDB::register_native_struct(#m_class, m_code, sizeof(m_class)) -#endif +#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..c18d70d9f6 --- /dev/null +++ b/core/object/make_virtuals.py @@ -0,0 +1,178 @@ +proto = """ +#define GDVIRTUAL$VER($RET m_name $ARG) \\ +StringName _gdvirtual_##m_name##_sn = #m_name;\\ +mutable bool _gdvirtual_##m_name##_initialized = false;\\ +mutable GDNativeExtensionClassCallVirtual _gdvirtual_##m_name = nullptr;\\ +template<bool required>\\ +_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->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\ + if (ce.error == Callable::CallError::CALL_OK) {\\ + $CALLSIRET\\ + return true;\\ + } \\ + }\\ + if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ + _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\ + _gdvirtual_##m_name##_initialized = true;\\ + }\\ + if (_gdvirtual_##m_name) {\\ + $CALLPTRARGS\\ + $CALLPTRRETDEF\\ + _gdvirtual_##m_name(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\ + $CALLPTRRET\\ + return true;\\ + }\\ + \\ + if (required) {\\ + ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\ + $RVOID\\ + }\\ +\\ + 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 (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ + _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, #m_name) : (GDNativeExtensionClassCallVirtual) nullptr;\\ + _gdvirtual_##m_name##_initialized = true;\\ + }\\ + 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("$RVOID", "(void)r_ret;") # If required, may lead to uninitialized errors + 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("$RVOID", "") + 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 = VariantCaster<m_ret>::cast(ret);") + s = s.replace("$CALLPTRRETPASS", "&ret") + s = s.replace("$CALLPTRRET", "r_ret = (m_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..fa1945cf79 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/core_string_names.h" +#include "core/object/class_db.h" #include "core/object/script_language.h" MessageQueue *MessageQueue::singleton = nullptr; @@ -40,23 +41,8 @@ MessageQueue *MessageQueue::get_singleton() { return singleton; } -Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) { - return push_callable(Callable(p_id, p_method), p_args, p_argcount, p_show_error); -} - -Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS; - - int argc = 0; - - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) { - break; - } - argc++; - } - - return push_call(p_id, p_method, argptr, argc, false); +Error MessageQueue::push_callp(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) { + return push_callablep(Callable(p_id, p_method), p_args, p_argcount, p_show_error); } Error MessageQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value) { @@ -113,8 +99,8 @@ Error MessageQueue::push_notification(ObjectID p_id, int p_notification) { return OK; } -Error MessageQueue::push_call(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) { - return push_call(p_object->get_instance_id(), p_method, VARIANT_ARG_PASS); +Error MessageQueue::push_callp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) { + return push_callp(p_object->get_instance_id(), p_method, p_args, p_argcount, p_show_error); } Error MessageQueue::push_notification(Object *p_object, int p_notification) { @@ -125,7 +111,7 @@ Error MessageQueue::push_set(Object *p_object, const StringName &p_prop, const V return push_set(p_object->get_instance_id(), p_prop, p_value); } -Error MessageQueue::push_callable(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error) { +Error MessageQueue::push_callablep(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error) { _THREAD_SAFE_METHOD_ int room_needed = sizeof(Message) + sizeof(Variant) * p_argcount; @@ -155,25 +141,10 @@ Error MessageQueue::push_callable(const Callable &p_callable, const Variant **p_ return OK; } -Error MessageQueue::push_callable(const Callable &p_callable, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS; - - int argc = 0; - - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) { - break; - } - argc++; - } - - return push_callable(p_callable, argptr, argc); -} - void MessageQueue::statistics() { - Map<StringName, int> set_count; - Map<int, int> notify_count; - Map<Callable, int> call_count; + HashMap<StringName, int> set_count; + HashMap<int, int> notify_count; + HashMap<Callable, int> call_count; int null_count = 0; uint32_t read_pos = 0; @@ -227,16 +198,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/message_queue.h b/core/object/message_queue.h index 1b8def62d3..2219cdb8f6 100644 --- a/core/object/message_queue.h +++ b/core/object/message_queue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,8 +31,11 @@ #ifndef MESSAGE_QUEUE_H #define MESSAGE_QUEUE_H -#include "core/object/class_db.h" +#include "core/object/object_id.h" #include "core/os/thread_safe.h" +#include "core/variant/variant.h" + +class Object; class MessageQueue { _THREAD_SAFE_CLASS_ @@ -59,10 +62,10 @@ class MessageQueue { }; }; - uint8_t *buffer; + uint8_t *buffer = nullptr; uint32_t buffer_end = 0; uint32_t buffer_max_used = 0; - uint32_t buffer_size; + uint32_t buffer_size = 0; void _call_function(const Callable &p_callable, const Variant *p_args, int p_argcount, bool p_show_error); @@ -73,14 +76,42 @@ class MessageQueue { public: static MessageQueue *get_singleton(); - Error push_call(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false); - Error push_call(ObjectID p_id, const StringName &p_method, VARIANT_ARG_LIST); + Error push_callp(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false); + template <typename... VarArgs> + Error push_call(ObjectID p_id, const StringName &p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + return push_callp(p_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); + } + Error push_notification(ObjectID p_id, int p_notification); Error push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value); - Error push_callable(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error = false); - Error push_callable(const Callable &p_callable, VARIANT_ARG_LIST); + Error push_callablep(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error = false); + + template <typename... VarArgs> + Error push_callable(const Callable &p_callable, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + return push_callablep(p_callable, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); + } + + Error push_callp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false); + template <typename... VarArgs> + Error push_call(Object *p_object, const StringName &p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + return push_callp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); + } - Error push_call(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST); Error push_notification(Object *p_object, int p_notification); Error push_set(Object *p_object, const StringName &p_prop, const Variant &p_value); diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp index 9c5ed60708..a4474ea53b 100644 --- a/core/object/method_bind.cpp +++ b/core/object/method_bind.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,12 +34,37 @@ #include "method_bind.h" -#ifdef DEBUG_METHODS_ENABLED +uint32_t MethodBind::get_hash() const { + uint32_t hash = hash_murmur3_one_32(has_return() ? 1 : 0); + hash = hash_murmur3_one_32(get_argument_count(), hash); + + 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_murmur3_one_32(get_argument_type(i), hash); + if (pi.class_name != StringName()) { + hash = hash_murmur3_one_32(pi.class_name.operator String().hash(), hash); + } + } + + hash = hash_murmur3_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_murmur3_one_32(v.hash(), hash); + } + + hash = hash_murmur3_one_32(is_const(), hash); + hash = hash_murmur3_one_32(is_vararg(), hash); + + return hash_fmix32(hash); +} + PropertyInfo MethodBind::get_argument_info(int p_argument) const { ERR_FAIL_INDEX_V(p_argument, get_argument_count(), PropertyInfo()); PropertyInfo info = _gen_argument_type_info(p_argument); +#ifdef DEBUG_METHODS_ENABLED info.name = p_argument < arg_names.size() ? String(arg_names[p_argument]) : String("arg" + itos(p_argument)); +#endif return info; } @@ -47,11 +72,14 @@ PropertyInfo MethodBind::get_return_info() const { return _gen_argument_type_info(-1); } -#endif void MethodBind::_set_const(bool p_const) { _const = p_const; } +void MethodBind::_set_static(bool p_static) { + _static = p_static; +} + void MethodBind::_set_returns(bool p_returns) { _returns = p_returns; } @@ -80,7 +108,6 @@ void MethodBind::set_default_arguments(const Vector<Variant> &p_defargs) { default_argument_count = default_arguments.size(); } -#ifdef DEBUG_METHODS_ENABLED void MethodBind::_generate_argument_types(int p_count) { set_argument_count(p_count); @@ -94,17 +121,13 @@ void MethodBind::_generate_argument_types(int p_count) { argument_types = argt; } -#endif - MethodBind::MethodBind() { static int last_id = 0; method_id = last_id++; } MethodBind::~MethodBind() { -#ifdef DEBUG_METHODS_ENABLED if (argument_types) { memdelete_arr(argument_types); } -#endif } diff --git a/core/object/method_bind.h b/core/object/method_bind.h index 7cf4f8d4e8..d60550c899 100644 --- a/core/object/method_bind.h +++ b/core/object/method_bind.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,18 +33,6 @@ #include "core/variant/binder_common.h" -enum MethodFlags { - METHOD_FLAG_NORMAL = 1, - METHOD_FLAG_EDITOR = 2, - METHOD_FLAG_NOSCRIPT = 4, - METHOD_FLAG_CONST = 8, - METHOD_FLAG_REVERSE = 16, // used for events - METHOD_FLAG_VIRTUAL = 32, - METHOD_FLAG_FROM_SCRIPT = 64, - METHOD_FLAG_VARARG = 128, - METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL, -}; - VARIANT_ENUM_CAST(MethodFlags) // some helpers @@ -58,22 +46,22 @@ class MethodBind { int default_argument_count = 0; int argument_count = 0; + bool _static = false; bool _const = false; bool _returns = false; protected: -#ifdef DEBUG_METHODS_ENABLED Variant::Type *argument_types = nullptr; +#ifdef DEBUG_METHODS_ENABLED Vector<StringName> arg_names; #endif void _set_const(bool p_const); + void _set_static(bool p_static); void _set_returns(bool p_returns); -#ifdef DEBUG_METHODS_ENABLED virtual Variant::Type _gen_argument_type(int p_arg) const = 0; virtual PropertyInfo _gen_argument_type_info(int p_arg) const = 0; void _generate_argument_types(int p_count); -#endif void set_argument_count(int p_count) { argument_count = p_count; } public: @@ -100,7 +88,6 @@ public: } } -#ifdef DEBUG_METHODS_ENABLED _FORCE_INLINE_ Variant::Type get_argument_type(int p_argument) const { ERR_FAIL_COND_V(p_argument < -1 || p_argument > argument_count, Variant::NIL); return argument_types[p_argument + 1]; @@ -109,6 +96,7 @@ public: PropertyInfo get_argument_info(int p_argument) const; PropertyInfo get_return_info() const; +#ifdef DEBUG_METHODS_ENABLED void set_argument_names(const Vector<StringName> &p_names); // Set by ClassDB, can't be inferred otherwise. Vector<StringName> get_argument_names() const; @@ -116,7 +104,7 @@ public: #endif void set_hint_flags(uint32_t p_hint) { hint_flags = p_hint; } - uint32_t get_hint_flags() const { return hint_flags | (is_const() ? METHOD_FLAG_CONST : 0) | (is_vararg() ? METHOD_FLAG_VARARG : 0); } + uint32_t get_hint_flags() const { return hint_flags | (is_const() ? METHOD_FLAG_CONST : 0) | (is_vararg() ? METHOD_FLAG_VARARG : 0) | (is_static() ? METHOD_FLAG_STATIC : 0); } _FORCE_INLINE_ StringName get_instance_class() const { return instance_class; } _FORCE_INLINE_ void set_instance_class(const StringName &p_class) { instance_class = p_class; } @@ -129,98 +117,150 @@ public: void set_name(const StringName &p_name); _FORCE_INLINE_ int get_method_id() const { return method_id; } _FORCE_INLINE_ bool is_const() const { return _const; } + _FORCE_INLINE_ bool is_static() const { return _static; } _FORCE_INLINE_ bool has_return() const { return _returns; } virtual bool is_vararg() const { return false; } void set_default_arguments(const Vector<Variant> &p_defargs); + uint32_t get_hash() const; + MethodBind(); virtual ~MethodBind(); }; -template <class T> -class MethodBindVarArg : public MethodBind { -public: - typedef Variant (T::*NativeCall)(const Variant **, int, Callable::CallError &); - +// MethodBindVarArg base CRTP +template <class Derived, class T, class R, bool should_returns> +class MethodBindVarArgBase : public MethodBind { protected: - NativeCall call_method = nullptr; -#ifdef DEBUG_METHODS_ENABLED - MethodInfo arguments; -#endif + R(T::*method) + (const Variant **, int, Callable::CallError &); + MethodInfo method_info; public: -#ifdef DEBUG_METHODS_ENABLED - virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { if (p_arg < 0) { - return arguments.return_val; - } else if (p_arg < arguments.arguments.size()) { - return arguments.arguments[p_arg]; + return _gen_return_type_info(); + } else if (p_arg < method_info.arguments.size()) { + return method_info.arguments[p_arg]; } else { return PropertyInfo(Variant::NIL, "arg_" + itos(p_arg), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT); } } - virtual Variant::Type _gen_argument_type(int p_arg) const { + virtual Variant::Type _gen_argument_type(int p_arg) const override { return _gen_argument_type_info(p_arg).type; } - virtual GodotTypeInfo::Metadata get_argument_meta(int) const { +#ifdef DEBUG_METHODS_ENABLED + virtual GodotTypeInfo::Metadata get_argument_meta(int) const override { return GodotTypeInfo::METADATA_NONE; } -#else - virtual Variant::Type _gen_argument_type(int p_arg) const { - return Variant::NIL; - } #endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - T *instance = static_cast<T *>(p_object); - return (instance->*call_method)(p_args, p_arg_count, r_error); + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { + ERR_FAIL(); // Can't call. } - void set_method_info(const MethodInfo &p_info, bool p_return_nil_is_variant) { - set_argument_count(p_info.arguments.size()); + virtual bool is_const() const { return false; } + + virtual bool is_vararg() const override { return true; } + + MethodBindVarArgBase( + R (T::*p_method)(const Variant **, int, Callable::CallError &), + const MethodInfo &p_method_info, + bool p_return_nil_is_variant) : + method(p_method), method_info(p_method_info) { + set_argument_count(method_info.arguments.size()); + Variant::Type *at = memnew_arr(Variant::Type, method_info.arguments.size() + 1); + at[0] = _gen_return_type_info().type; + if (method_info.arguments.size()) { #ifdef DEBUG_METHODS_ENABLED - Variant::Type *at = memnew_arr(Variant::Type, p_info.arguments.size() + 1); - at[0] = p_info.return_val.type; - if (p_info.arguments.size()) { Vector<StringName> names; - names.resize(p_info.arguments.size()); - for (int i = 0; i < p_info.arguments.size(); i++) { - at[i + 1] = p_info.arguments[i].type; - names.write[i] = p_info.arguments[i].name; + names.resize(method_info.arguments.size()); +#endif + for (int i = 0; i < method_info.arguments.size(); i++) { + at[i + 1] = method_info.arguments[i].type; +#ifdef DEBUG_METHODS_ENABLED + names.write[i] = method_info.arguments[i].name; +#endif } +#ifdef DEBUG_METHODS_ENABLED set_argument_names(names); +#endif } argument_types = at; - arguments = p_info; if (p_return_nil_is_variant) { - arguments.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + method_info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; } -#endif + + _set_returns(should_returns); } - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { - ERR_FAIL(); // Can't call. +private: + PropertyInfo _gen_return_type_info() const { + return Derived::_gen_return_type_info_impl(); } +}; - void set_method(NativeCall p_method) { call_method = p_method; } - virtual bool is_const() const { return false; } +// variadic, no return +template <class T> +class MethodBindVarArgT : public MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false> { + friend class MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false>; - virtual bool is_vararg() const { return true; } +public: + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { + (static_cast<T *>(p_object)->*MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false>::method)(p_args, p_arg_count, r_error); + return {}; + } - MethodBindVarArg() { - _set_returns(true); + MethodBindVarArgT( + void (T::*p_method)(const Variant **, int, Callable::CallError &), + const MethodInfo &p_method_info, + bool p_return_nil_is_variant) : + MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false>(p_method, p_method_info, p_return_nil_is_variant) { + } + +private: + static PropertyInfo _gen_return_type_info_impl() { + return {}; } }; template <class T> -MethodBind *create_vararg_method_bind(Variant (T::*p_method)(const Variant **, int, Callable::CallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) { - MethodBindVarArg<T> *a = memnew((MethodBindVarArg<T>)); - a->set_method(p_method); - a->set_method_info(p_info, p_return_nil_is_variant); +MethodBind *create_vararg_method_bind(void (T::*p_method)(const Variant **, int, Callable::CallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) { + MethodBind *a = memnew((MethodBindVarArgT<T>)(p_method, p_info, p_return_nil_is_variant)); + a->set_instance_class(T::get_class_static()); + return a; +} + +// variadic, return +template <class T, class R> +class MethodBindVarArgTR : public MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true> { + friend class MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true>; + +public: + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { + return (static_cast<T *>(p_object)->*MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true>::method)(p_args, p_arg_count, r_error); + } + + MethodBindVarArgTR( + R (T::*p_method)(const Variant **, int, Callable::CallError &), + const MethodInfo &p_info, + bool p_return_nil_is_variant) : + MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true>(p_method, p_info, p_return_nil_is_variant) { + } + +private: + static PropertyInfo _gen_return_type_info_impl() { + return GetTypeInfo<R>::get_class_info(); + } +}; + +template <class T, class R> +MethodBind *create_vararg_method_bind(R (T::*p_method)(const Variant **, int, Callable::CallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) { + MethodBind *a = memnew((MethodBindVarArgTR<T, R>)(p_method, p_info, p_return_nil_is_variant)); a->set_instance_class(T::get_class_static()); return a; } @@ -244,13 +284,12 @@ class MethodBindT : public MethodBind { void (MB_T::*method)(P...); protected: -#ifdef DEBUG_METHODS_ENABLED // GCC raises warnings in the case P = {} as the comparison is always false... #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlogical-op" #endif - virtual Variant::Type _gen_argument_type(int p_arg) const { + virtual Variant::Type _gen_argument_type(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { return call_get_argument_type<P...>(p_arg); } else { @@ -261,42 +300,39 @@ protected: #pragma GCC diagnostic pop #endif - virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { PropertyInfo pi; call_get_argument_type_info<P...>(p_arg, pi); return pi; } -#endif public: #ifdef DEBUG_METHODS_ENABLED - virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const override { return call_get_argument_metadata<P...>(p_arg); } #endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { #ifdef TYPED_METHOD_BIND call_with_variant_args_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); #else - call_with_variant_args_dv((MB_T *)(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); + call_with_variant_args_dv(reinterpret_cast<MB_T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); #endif return Variant(); } - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { #ifdef TYPED_METHOD_BIND call_with_ptr_args<T, P...>(static_cast<T *>(p_object), method, p_args); #else - call_with_ptr_args<MB_T, P...>((MB_T *)(p_object), method, p_args); + call_with_ptr_args<MB_T, P...>(reinterpret_cast<MB_T *>(p_object), method, p_args); #endif } MethodBindT(void (MB_T::*p_method)(P...)) { method = p_method; -#ifdef DEBUG_METHODS_ENABLED _generate_argument_types(sizeof...(P)); -#endif set_argument_count(sizeof...(P)); } }; @@ -312,7 +348,7 @@ MethodBind *create_method_bind(void (T::*p_method)(P...)) { return a; } -// no return, not const +// no return, const #ifdef TYPED_METHOD_BIND template <class T, class... P> @@ -323,13 +359,12 @@ class MethodBindTC : public MethodBind { void (MB_T::*method)(P...) const; protected: -#ifdef DEBUG_METHODS_ENABLED // GCC raises warnings in the case P = {} as the comparison is always false... #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlogical-op" #endif - virtual Variant::Type _gen_argument_type(int p_arg) const { + virtual Variant::Type _gen_argument_type(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { return call_get_argument_type<P...>(p_arg); } else { @@ -340,43 +375,40 @@ protected: #pragma GCC diagnostic pop #endif - virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { PropertyInfo pi; call_get_argument_type_info<P...>(p_arg, pi); return pi; } -#endif public: #ifdef DEBUG_METHODS_ENABLED - virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const override { return call_get_argument_metadata<P...>(p_arg); } #endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { #ifdef TYPED_METHOD_BIND call_with_variant_argsc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); #else - call_with_variant_argsc_dv((MB_T *)(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); + call_with_variant_argsc_dv(reinterpret_cast<MB_T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); #endif return Variant(); } - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { #ifdef TYPED_METHOD_BIND call_with_ptr_argsc<T, P...>(static_cast<T *>(p_object), method, p_args); #else - call_with_ptr_argsc<MB_T, P...>((MB_T *)(p_object), method, p_args); + call_with_ptr_argsc<MB_T, P...>(reinterpret_cast<MB_T *>(p_object), method, p_args); #endif } MethodBindTC(void (MB_T::*p_method)(P...) const) { method = p_method; _set_const(true); -#ifdef DEBUG_METHODS_ENABLED _generate_argument_types(sizeof...(P)); -#endif set_argument_count(sizeof...(P)); } }; @@ -404,13 +436,12 @@ class MethodBindTR : public MethodBind { (P...); protected: -#ifdef DEBUG_METHODS_ENABLED // GCC raises warnings in the case P = {} as the comparison is always false... #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlogical-op" #endif - virtual Variant::Type _gen_argument_type(int p_arg) const { + virtual Variant::Type _gen_argument_type(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { return call_get_argument_type<P...>(p_arg); } else { @@ -418,7 +449,7 @@ protected: } } - virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { PropertyInfo pi; call_get_argument_type_info<P...>(p_arg, pi); @@ -430,11 +461,10 @@ protected: #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif -#endif public: #ifdef DEBUG_METHODS_ENABLED - virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const override { if (p_arg >= 0) { return call_get_argument_metadata<P...>(p_arg); } else { @@ -443,30 +473,28 @@ public: } #endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { Variant ret; #ifdef TYPED_METHOD_BIND call_with_variant_args_ret_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); #else - call_with_variant_args_ret_dv((MB_T *)p_object, method, p_args, p_arg_count, ret, r_error, get_default_arguments()); + call_with_variant_args_ret_dv(reinterpret_cast<MB_T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); #endif return ret; } - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { #ifdef TYPED_METHOD_BIND call_with_ptr_args_ret<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret); #else - call_with_ptr_args_ret<MB_T, R, P...>((MB_T *)(p_object), method, p_args, r_ret); + call_with_ptr_args_ret<MB_T, R, P...>(reinterpret_cast<MB_T *>(p_object), method, p_args, r_ret); #endif } MethodBindTR(R (MB_T::*p_method)(P...)) { method = p_method; _set_returns(true); -#ifdef DEBUG_METHODS_ENABLED _generate_argument_types(sizeof...(P)); -#endif set_argument_count(sizeof...(P)); } }; @@ -495,13 +523,12 @@ class MethodBindTRC : public MethodBind { (P...) const; protected: -#ifdef DEBUG_METHODS_ENABLED // GCC raises warnings in the case P = {} as the comparison is always false... #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlogical-op" #endif - virtual Variant::Type _gen_argument_type(int p_arg) const { + virtual Variant::Type _gen_argument_type(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { return call_get_argument_type<P...>(p_arg); } else { @@ -509,7 +536,7 @@ protected: } } - virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { PropertyInfo pi; call_get_argument_type_info<P...>(p_arg, pi); @@ -521,11 +548,10 @@ protected: #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif -#endif public: #ifdef DEBUG_METHODS_ENABLED - virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const override { if (p_arg >= 0) { return call_get_argument_metadata<P...>(p_arg); } else { @@ -534,21 +560,21 @@ public: } #endif - virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { Variant ret; #ifdef TYPED_METHOD_BIND call_with_variant_args_retc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); #else - call_with_variant_args_retc_dv((MB_T *)(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); + call_with_variant_args_retc_dv(reinterpret_cast<MB_T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); #endif return ret; } - virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { #ifdef TYPED_METHOD_BIND call_with_ptr_args_retc<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret); #else - call_with_ptr_args_retc<MB_T, R, P...>((MB_T *)(p_object), method, p_args, r_ret); + call_with_ptr_args_retc<MB_T, R, P...>(reinterpret_cast<MB_T *>(p_object), method, p_args, r_ret); #endif } @@ -556,9 +582,7 @@ public: method = p_method; _set_returns(true); _set_const(true); -#ifdef DEBUG_METHODS_ENABLED _generate_argument_types(sizeof...(P)); -#endif set_argument_count(sizeof...(P)); } }; @@ -574,4 +598,139 @@ MethodBind *create_method_bind(R (T::*p_method)(P...) const) { return a; } +/* STATIC BINDS */ + +// no return + +template <class... P> +class MethodBindTS : public MethodBind { + void (*function)(P...); + +protected: +// GCC raises warnings in the case P = {} as the comparison is always false... +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wlogical-op" +#endif + virtual Variant::Type _gen_argument_type(int p_arg) const override { + if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { + return call_get_argument_type<P...>(p_arg); + } else { + return Variant::NIL; + } + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { + PropertyInfo pi; + call_get_argument_type_info<P...>(p_arg, pi); + return pi; + } + +public: +#ifdef DEBUG_METHODS_ENABLED + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const override { + return call_get_argument_metadata<P...>(p_arg); + } + +#endif + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { + (void)p_object; // unused + call_with_variant_args_static_dv(function, p_args, p_arg_count, r_error, get_default_arguments()); + return Variant(); + } + + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { + (void)p_object; + (void)r_ret; + call_with_ptr_args_static_method(function, p_args); + } + + MethodBindTS(void (*p_function)(P...)) { + function = p_function; + _generate_argument_types(sizeof...(P)); + set_argument_count(sizeof...(P)); + _set_static(true); + } +}; + +template <class... P> +MethodBind *create_static_method_bind(void (*p_method)(P...)) { + MethodBind *a = memnew((MethodBindTS<P...>)(p_method)); + return a; +} + +// return + +template <class R, class... P> +class MethodBindTRS : public MethodBind { + R(*function) + (P...); + +protected: +// GCC raises warnings in the case P = {} as the comparison is always false... +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wlogical-op" +#endif + virtual Variant::Type _gen_argument_type(int p_arg) const override { + if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { + return call_get_argument_type<P...>(p_arg); + } else { + return GetTypeInfo<R>::VARIANT_TYPE; + } + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + virtual PropertyInfo _gen_argument_type_info(int p_arg) const override { + if (p_arg >= 0 && p_arg < (int)sizeof...(P)) { + PropertyInfo pi; + call_get_argument_type_info<P...>(p_arg, pi); + return pi; + } else { + return GetTypeInfo<R>::get_class_info(); + } + } + +public: +#ifdef DEBUG_METHODS_ENABLED + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const override { + if (p_arg >= 0) { + return call_get_argument_metadata<P...>(p_arg); + } else { + return GetTypeInfo<R>::METADATA; + } + } + +#endif + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) override { + Variant ret; + call_with_variant_args_static_ret_dv(function, p_args, p_arg_count, ret, r_error, get_default_arguments()); + return ret; + } + + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) override { + (void)p_object; + call_with_ptr_args_static_method_ret(function, p_args, r_ret); + } + + MethodBindTRS(R (*p_function)(P...)) { + function = p_function; + _generate_argument_types(sizeof...(P)); + set_argument_count(sizeof...(P)); + _set_static(true); + _set_returns(true); + } +}; + +template <class R, class... P> +MethodBind *create_static_method_bind(R (*p_method)(P...)) { + MethodBind *a = memnew((MethodBindTRS<R, P...>)(p_method)); + return a; +} + #endif // METHOD_BIND_H diff --git a/core/object/object.cpp b/core/object/object.cpp index 1a9cce49d8..5f2287c9d3 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -161,161 +161,6 @@ MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) { return mi; } -MethodInfo::MethodInfo() : - flags(METHOD_FLAG_NORMAL) {} - -MethodInfo::MethodInfo(const String &p_name) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { -} - -MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { - arguments.push_back(p_param1); -} - -MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); -} - -MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); -} - -MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); -} - -MethodInfo::MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); - arguments.push_back(p_param5); -} - -MethodInfo::MethodInfo(Variant::Type ret) : - flags(METHOD_FLAG_NORMAL) { - return_val.type = ret; -} - -MethodInfo::MethodInfo(Variant::Type ret, const String &p_name) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { - return_val.type = ret; -} - -MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { - return_val.type = ret; - arguments.push_back(p_param1); -} - -MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { - return_val.type = ret; - arguments.push_back(p_param1); - arguments.push_back(p_param2); -} - -MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { - return_val.type = ret; - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); -} - -MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { - return_val.type = ret; - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); -} - -MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) : - name(p_name), - flags(METHOD_FLAG_NORMAL) { - return_val.type = ret; - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); - arguments.push_back(p_param5); -} - -MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name) : - name(p_name), - return_val(p_ret), - flags(METHOD_FLAG_NORMAL) { -} - -MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1) : - name(p_name), - return_val(p_ret), - flags(METHOD_FLAG_NORMAL) { - arguments.push_back(p_param1); -} - -MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2) : - name(p_name), - return_val(p_ret), - flags(METHOD_FLAG_NORMAL) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); -} - -MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3) : - name(p_name), - return_val(p_ret), - flags(METHOD_FLAG_NORMAL) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); -} - -MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4) : - name(p_name), - return_val(p_ret), - flags(METHOD_FLAG_NORMAL) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); -} - -MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5) : - name(p_name), - return_val(p_ret), - flags(METHOD_FLAG_NORMAL) { - arguments.push_back(p_param1); - arguments.push_back(p_param2); - arguments.push_back(p_param3); - arguments.push_back(p_param4); - arguments.push_back(p_param5); -} - Object::Connection::operator Variant() const { Dictionary d; d["signal"] = signal; @@ -385,13 +230,26 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid } } - //try built-in setgetter + 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 setter. { if (ClassDB::set_property(this, p_name, p_value, r_valid)) { - /* - if (r_valid) - *r_valid=true; - */ return; } } @@ -403,22 +261,22 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid } return; - } else if (p_name == CoreStringNames::get_singleton()->_meta) { - //set_meta(p_name,p_value); - metadata = p_value.duplicate(); - if (r_valid) { - *r_valid = true; - } - return; - } - - //something inside the object... :| - bool success = _setv(p_name, p_value); - if (success) { - if (r_valid) { - *r_valid = true; + } else { + Variant **V = metadata_properties.getptr(p_name); + if (V) { + **V = p_value; + if (r_valid) { + *r_valid = true; + } + return; + } else if (p_name.operator String().begins_with("metadata/")) { + // Must exist, otherwise duplicate() will not work. + set_meta(p_name.operator String().replace_first("metadata/", ""), p_value); + if (r_valid) { + *r_valid = true; + } + return; } - return; } #ifdef TOOLS_ENABLED @@ -434,6 +292,15 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid } #endif + // Something inside the object... :| + bool success = _setv(p_name, p_value); + if (success) { + if (r_valid) { + *r_valid = true; + } + return; + } + if (r_valid) { *r_valid = false; } @@ -450,8 +317,25 @@ 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 + // Try built-in getter. { if (ClassDB::get_property(const_cast<Object *>(this), p_name, ret)) { if (r_valid) { @@ -467,24 +351,18 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { *r_valid = true; } return ret; + } + + const Variant *const *V = metadata_properties.getptr(p_name); - } else if (p_name == CoreStringNames::get_singleton()->_meta) { - ret = metadata; + if (V) { + ret = **V; if (r_valid) { *r_valid = true; } return ret; } else { - //something inside the object... :| - bool success = _getv(p_name, ret); - if (success) { - if (r_valid) { - *r_valid = true; - } - return ret; - } - #ifdef TOOLS_ENABLED if (script_instance) { bool valid; @@ -497,6 +375,14 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { } } #endif + // Something inside the object... :| + bool success = _getv(p_name, ret); + if (success) { + if (r_valid) { + *r_valid = true; + } + return ret; + } if (r_valid) { *r_valid = false; @@ -594,18 +480,45 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons script_instance->get_property_list(p_list); } + if (_extension) { + const ObjectNativeExtension *current_extension = _extension; + while (current_extension) { + p_list->push_back(PropertyInfo(Variant::NIL, current_extension->class_name, PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); + ClassDB::get_property_list(current_extension->class_name, p_list, true, this); + current_extension = current_extension->parent; + } + } + + 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(pinfo[i])); + } + if (_extension->free_property_list) { + _extension->free_property_list(_extension_instance, pinfo); + } + } + _get_property_listv(p_list, p_reversed); - if (!is_class("Script")) { // can still be set, but this is for userfriendlyness + 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()) { - p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - } + if (script_instance && !p_reversed) { p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); script_instance->get_property_list(p_list); } + + for (const KeyValue<StringName, Variant> &K : metadata) { + PropertyInfo pi = PropertyInfo(K.value.get_type(), "metadata/" + K.key.operator String()); + if (K.value.get_type() == Variant::OBJECT) { + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "Resource"; + } + p_list->push_back(pi); + } } void Object::_validate_property(PropertyInfo &property) const { @@ -634,7 +547,7 @@ Variant Object::_call_bind(const Variant **p_args, int p_argcount, Callable::Cal StringName method = *p_args[0]; - return call(method, &p_args[1], p_argcount - 1, r_error); + return callp(method, &p_args[1], p_argcount - 1, r_error); } Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { @@ -655,7 +568,7 @@ Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Call StringName method = *p_args[0]; - MessageQueue::get_singleton()->push_call(get_instance_id(), method, &p_args[1], p_argcount - 1, true); + MessageQueue::get_singleton()->push_callp(get_instance_id(), method, &p_args[1], p_argcount - 1, true); return Variant(); } @@ -705,31 +618,14 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) { } Callable::CallError ce; - Variant ret = call(p_method, argptrs, p_args.size(), ce); + Variant ret = callp(p_method, argptrs, p_args.size(), ce); if (ce.error != Callable::CallError::CALL_OK) { ERR_FAIL_V_MSG(Variant(), "Error calling method from 'callv': " + Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce) + "."); } return ret; } -Variant Object::call(const StringName &p_name, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS; - - int argc = 0; - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) { - break; - } - argc++; - } - - Callable::CallError error; - - Variant ret = call(p_name, argptr, argc, error); - return ret; -} - -Variant Object::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { r_error.error = Callable::CallError::CALL_OK; if (p_method == CoreStringNames::get_singleton()->_free) { @@ -740,7 +636,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,8 +657,9 @@ 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); + ret = script_instance->callp(p_method, p_args, p_argcount, r_error); //force jumptable switch (r_error.error) { case Callable::CallError::CALL_OK: @@ -772,12 +669,15 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: + case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST: return ret; case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: { } } } + //extension does not need this, because all methods are registered in MethodBind + MethodBind *method = ClassDB::get_method(get_class_name(), p_method); if (method) { @@ -789,12 +689,64 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a return ret; } +Variant Object::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + + if (p_method == CoreStringNames::get_singleton()->_free) { + // Free is not const, so fail. + r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST; + return Variant(); + } + + Variant ret; + OBJ_DEBUG_LOCK + + if (script_instance) { + ret = script_instance->call_const(p_method, p_args, p_argcount, r_error); + //force jumptable + switch (r_error.error) { + case Callable::CallError::CALL_OK: + return ret; + case Callable::CallError::CALL_ERROR_INVALID_METHOD: + break; + case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST: + break; + case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: + case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: + case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: + return ret; + case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: { + } + } + } + + //extension does not need this, because all methods are registered in MethodBind + + MethodBind *method = ClassDB::get_method(get_class_name(), p_method); + + if (method) { + if (!method->is_const()) { + r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST; + return ret; + } + ret = method->call(this, p_args, p_argcount, r_error); + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + } + + return ret; +} + void Object::notification(int p_notification, bool p_reversed) { _notificationv(p_notification, 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,6 +757,11 @@ String Object::to_string() { return ret; } } + if (_extension && _extension->to_string) { + String ret; + _extension->to_string(_extension_instance, &ret); + return ret; + } return "[" + get_class() + ":" + itos(get_instance_id()) + "]"; } @@ -832,7 +789,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()) { @@ -867,26 +824,44 @@ 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); + if (metadata.has(p_name)) { + metadata.erase(p_name); + metadata_properties.erase("metadata/" + p_name.operator String()); + notify_property_list_changed(); + } return; } - metadata[p_name] = p_value; + HashMap<StringName, Variant>::Iterator E = metadata.find(p_name); + if (E) { + E->value = p_value; + } else { + ERR_FAIL_COND(!p_name.operator String().is_valid_identifier()); + Variant *V = &metadata.insert(p_name, p_value)->value; + metadata_properties["metadata/" + p_name.operator String()] = V; + notify_property_list_changed(); + } } -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 Variant &p_default) const { + if (!metadata.has(p_name)) { + if (p_default != Variant()) { + return p_default; + } else { + ERR_FAIL_V_MSG(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) { - metadata.erase(p_name); +void Object::remove_meta(const StringName &p_name) { + set_meta(p_name, Variant()); } Array Object::_get_property_list_bind() const { @@ -909,28 +884,24 @@ 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 KeyValue<StringName, Variant> &K : metadata) { + _metaret.push_back(K.key); } return _metaret; } -void Object::get_meta_list(List<String> *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()); +void Object::get_meta_list(List<StringName> *p_list) const { + for (const KeyValue<StringName, Variant> &K : metadata) { + p_list->push_back(K.key); } } void Object::add_user_signal(const MethodInfo &p_signal) { - ERR_FAIL_COND_MSG(p_signal.name == "", "Signal name cannot be empty."); + ERR_FAIL_COND_MSG(p_signal.name.is_empty(), "Signal name cannot be empty."); ERR_FAIL_COND_MSG(ClassDB::has_signal(get_class_name(), p_signal.name), "User signal's name conflicts with a built-in signal of '" + get_class_name() + "'."); ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), "Trying to add already existing signal '" + p_signal.name + "'."); SignalData s; @@ -950,15 +921,15 @@ struct _ObjectSignalDisconnectData { Callable callable; }; -Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +Error Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - ERR_FAIL_COND_V(p_argcount < 1, Variant()); + ERR_FAIL_COND_V(p_argcount < 1, Error::ERR_INVALID_PARAMETER); if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; r_error.expected = Variant::STRING_NAME; - ERR_FAIL_COND_V(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING, Variant()); + ERR_FAIL_COND_V(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING, Error::ERR_INVALID_PARAMETER); } r_error.error = Callable::CallError::CALL_OK; @@ -972,12 +943,10 @@ Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::C args = &p_args[1]; } - emit_signal(signal, args, argc); - - return Variant(); + return emit_signalp(signal, args, argc); } -Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int p_argcount) { +Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount) { if (_block_signals) { return ERR_CANT_ACQUIRE_RESOURCE; //no emit, signals blocked } @@ -1036,7 +1005,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int } if (c.flags & CONNECT_DEFERRED) { - MessageQueue::get_singleton()->push_callable(c.callable, args, argc, true); + MessageQueue::get_singleton()->push_callablep(c.callable, args, argc, true); } else { Callable::CallError ce; _emitting = true; @@ -1084,21 +1053,6 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int return err; } -Error Object::emit_signal(const StringName &p_name, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS; - - int argc = 0; - - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) { - break; - } - argc++; - } - - return emit_signal(p_name, argptr, argc); -} - void Object::_add_user_signal(const String &p_name, const Array &p_args) { // this version of add_user_signal is meant to be used from scripts or external apis // without access to ADD_SIGNAL in bind_methods @@ -1129,21 +1083,20 @@ 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; } -Array Object::_get_signal_connection_list(const String &p_signal) const { +Array Object::_get_signal_connection_list(const StringName &p_signal) const { List<Connection> conns; get_all_signal_connections(&conns); 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); } @@ -1191,21 +1144,18 @@ void Object::get_signal_list(List<MethodInfo> *p_signals) const { ClassDB::get_signal_list(get_class_name(), p_signals); //find maybe usersignals? - const StringName *S = nullptr; - while ((S = signal_map.next(S))) { - if (signal_map[*S].user.name != "") { + for (const KeyValue<StringName, SignalData> &E : signal_map) { + if (!E.value.user.name.is_empty()) { //user signal - p_signals->push_back(signal_map[*S].user); + p_signals->push_back(E.value.user); } } } void Object::get_all_signal_connections(List<Connection> *p_connections) const { - const StringName *S = nullptr; - - while ((S = signal_map.next(S))) { - const SignalData *s = &signal_map[*S]; + for (const KeyValue<StringName, SignalData> &E : signal_map) { + const SignalData *s = &E.value; for (int i = 0; i < s->slot_map.size(); i++) { p_connections->push_back(s->slot_map.getv(i).conn); @@ -1226,10 +1176,9 @@ void Object::get_signal_connection_list(const StringName &p_signal, List<Connect int Object::get_persistent_signal_connection_count() const { int count = 0; - const StringName *S = nullptr; - while ((S = signal_map.next(S))) { - const SignalData *s = &signal_map[*S]; + for (const KeyValue<StringName, SignalData> &E : signal_map) { + const SignalData *s = &E.value; for (int i = 0; i < s->slot_map.size(); i++) { if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST) { @@ -1242,16 +1191,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) { @@ -1309,7 +1258,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); @@ -1327,8 +1276,6 @@ bool Object::is_connected(const StringName &p_signal, const Callable &p_callable Callable target = p_callable; return s->slot_map.has(*target.get_base_comparator()); - //const Map<Signal::Target,Signal::Slot>::Element *E = s->slot_map.find(target); - //return (E!=nullptr ); } void Object::disconnect(const StringName &p_signal, const Callable &p_callable) { @@ -1336,26 +1283,26 @@ 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 + "'."); + (!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(!s, vformat("Disconnecting nonexistent signal '%s' in %s.", p_signal, to_string())); ERR_FAIL_COND_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + "."); - SignalData::Slot *slot = &s->slot_map[p_callable]; + SignalData::Slot *slot = &s->slot_map[*p_callable.get_base_comparator()]; 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; } } @@ -1369,11 +1316,11 @@ void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, } } -void Object::_set_bind(const String &p_set, const Variant &p_value) { +void Object::_set_bind(const StringName &p_set, const Variant &p_value) { set(p_set, p_value); } -Variant Object::_get_bind(const String &p_name) const { +Variant Object::_get_bind(const StringName &p_name) const { return get(p_name); } @@ -1416,12 +1363,12 @@ String Object::tr_n(const StringName &p_message, const StringName &p_message_plu void Object::_clear_internal_resource_paths(const Variant &p_var) { switch (p_var.get_type()) { case Variant::OBJECT: { - RES r = p_var; + Ref<Resource> r = p_var; if (!r.is_valid()) { return; } - if (!r->get_path().begins_with("res://") || r->get_path().find("::") == -1) { + if (!r->is_built_in()) { return; //not an internal resource } @@ -1445,9 +1392,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: { @@ -1476,8 +1423,8 @@ 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)); } } @@ -1503,7 +1450,7 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("set_meta", "name", "value"), &Object::set_meta); ClassDB::bind_method(D_METHOD("remove_meta", "name"), &Object::remove_meta); - ClassDB::bind_method(D_METHOD("get_meta", "name"), &Object::get_meta); + ClassDB::bind_method(D_METHOD("get_meta", "name", "default"), &Object::get_meta, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("has_meta", "name"), &Object::has_meta); ClassDB::bind_method(D_METHOD("get_meta_list"), &Object::_get_meta_list_bind); @@ -1565,22 +1512,25 @@ void Object::_bind_methods() { ADD_SIGNAL(MethodInfo("script_changed")); ADD_SIGNAL(MethodInfo("property_list_changed")); - BIND_VMETHOD(MethodInfo("_notification", PropertyInfo(Variant::INT, "what"))); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "_set", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value"))); +#define BIND_OBJ_CORE_METHOD(m_method) \ + ::ClassDB::add_virtual_method(get_class_static(), m_method, true, Vector<String>(), true); + + 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); @@ -1591,10 +1541,6 @@ void Object::_bind_methods() { BIND_ENUM_CONSTANT(CONNECT_REFERENCE_COUNTED); } -void Object::call_deferred(const StringName &p_method, VARIANT_ARG_DECLARE) { - MessageQueue::get_singleton()->push_call(this, p_method, VARIANT_ARG_PASS); -} - void Object::set_deferred(const StringName &p_property, const Variant &p_value) { MessageQueue::get_singleton()->push_set(this, p_property, p_value); } @@ -1611,14 +1557,14 @@ 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 == "") { + if (text.is_empty()) { continue; } @@ -1671,7 +1617,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; } @@ -1714,42 +1660,67 @@ 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) { - instance_binding_count.increment(); - _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); #ifdef DEBUG_ENABLED _lock_index.init(1); @@ -1764,21 +1735,34 @@ Object::Object() { _construct_object(false); } +void Object::detach_from_objectdb() { + if (_instance_id != ObjectID()) { + ObjectDB::remove_instance(this); + _instance_id = ObjectID(); + } +} + Object::~Object() { if (script_instance) { memdelete(script_instance); } script_instance = nullptr; - const StringName *S = nullptr; + if (_extension && _extension->free_instance) { + _extension->free_instance(_extension->class_userdata, _extension_instance); + _extension = nullptr; + _extension_instance = nullptr; + } if (_emitting) { //@todo this may need to actually reach the debugger prioritarily somehow because it may crash before ERR_PRINT("Object " + to_string() + " was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes."); } - while ((S = signal_map.next(nullptr))) { - SignalData *s = &signal_map[*S]; + while (signal_map.size()) { + // Avoid regular iteration so erasing is safe. + KeyValue<StringName, SignalData> &E = *signal_map.begin(); + SignalData *s = &E.value; //brute force disconnect for performance int slot_count = s->slot_map.size(); @@ -1788,7 +1772,7 @@ Object::~Object() { slot_list[i].value.conn.callable.get_object()->connections.erase(slot_list[i].value.cE); } - signal_map.erase(*S); + signal_map.erase(E.key); } //signals from nodes that connect to this node @@ -1797,16 +1781,19 @@ Object::~Object() { c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true); } - ObjectDB::remove_instance(this); - _instance_id = ObjectID(); + if (_instance_id != ObjectID()) { + ObjectDB::remove_instance(this); + _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); } } @@ -1824,7 +1811,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--; } } @@ -1853,7 +1839,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; } @@ -1866,7 +1852,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; @@ -1877,7 +1863,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; } @@ -1915,7 +1901,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(); @@ -1950,7 +1936,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 029478873d..17f75a4e1d 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,49 +31,41 @@ #ifndef OBJECT_H #define OBJECT_H +#include "core/extension/gdnative_interface.h" +#include "core/object/message_queue.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/hash_set.h" #include "core/templates/list.h" -#include "core/templates/map.h" +#include "core/templates/rb_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] - -/** -@author Juan Linietsky <reduzio@gmail.com> -*/ - 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][,no_slider][,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_LINK, 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," PROPERTY_HINT_GLOBAL_DIR, ///< a directory path must be passed PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines + PROPERTY_HINT_EXPRESSION, ///< used for string properties that can contain multiple lines PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color PROPERTY_HINT_IMAGE_COMPRESS_LOSSY, @@ -92,63 +84,81 @@ enum PropertyHint { PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send PROPERTY_HINT_NODE_PATH_VALID_TYPES, 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_GLOBAL_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_LOCALE_ID, + PROPERTY_HINT_LOCALIZABLE_STRING, + PROPERTY_HINT_NODE_TYPE, ///< a node object type PROPERTY_HINT_MAX, // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit }; enum PropertyUsageFlags { - PROPERTY_USAGE_STORAGE = 1, - PROPERTY_USAGE_EDITOR = 2, - PROPERTY_USAGE_NETWORK = 4, - PROPERTY_USAGE_EDITOR_HELPER = 8, - PROPERTY_USAGE_CHECKABLE = 16, //used for editing global variables - PROPERTY_USAGE_CHECKED = 32, //used for editing global variables - PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings - PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor - PROPERTY_USAGE_CATEGORY = 256, - PROPERTY_USAGE_SUBGROUP = 512, - PROPERTY_USAGE_NO_INSTANCE_STATE = 2048, - PROPERTY_USAGE_RESTART_IF_CHANGED = 4096, - PROPERTY_USAGE_SCRIPT_VARIABLE = 8192, - PROPERTY_USAGE_STORE_IF_NULL = 16384, - PROPERTY_USAGE_ANIMATE_AS_TRIGGER = 32768, - PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED = 65536, - PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 17, - PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 18, - PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 19, - PROPERTY_USAGE_INTERNAL = 1 << 20, - PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 21, // If the object is duplicated also this property will be duplicated - PROPERTY_USAGE_HIGH_END_GFX = 1 << 22, - PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 23, - PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 24, - 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_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, + PROPERTY_USAGE_NONE = 0, + PROPERTY_USAGE_STORAGE = 1 << 1, + PROPERTY_USAGE_EDITOR = 1 << 2, + PROPERTY_USAGE_CHECKABLE = 1 << 3, // Used for editing global variables. + PROPERTY_USAGE_CHECKED = 1 << 4, // Used for editing global variables. + PROPERTY_USAGE_INTERNATIONALIZED = 1 << 5, // Hint for internationalized strings. + PROPERTY_USAGE_GROUP = 1 << 6, // Used for grouping props in the editor. + PROPERTY_USAGE_CATEGORY = 1 << 7, + PROPERTY_USAGE_SUBGROUP = 1 << 8, + PROPERTY_USAGE_CLASS_IS_BITFIELD = 1 << 9, + PROPERTY_USAGE_NO_INSTANCE_STATE = 1 << 10, + PROPERTY_USAGE_RESTART_IF_CHANGED = 1 << 11, + PROPERTY_USAGE_SCRIPT_VARIABLE = 1 << 12, + PROPERTY_USAGE_STORE_IF_NULL = 1 << 13, + PROPERTY_USAGE_ANIMATE_AS_TRIGGER = 1 << 14, + PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED = 1 << 15, + PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 16, + PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 17, + PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 18, + PROPERTY_USAGE_INTERNAL = 1 << 19, + PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 20, // If the object is duplicated also this property will be duplicated. + PROPERTY_USAGE_HIGH_END_GFX = 1 << 21, + PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 22, + PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 23, + PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 24, // Used in inspector to increment property when keyed in animation player. + PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 25, // when loading, the resource for this property can be set at the end of loading. + PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 26, // For Object properties, instantiate them when creating in editor. + PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 27, //for project or editor settings, show when basic settings are selected. + PROPERTY_USAGE_READ_ONLY = 1 << 28, // Mark a property as read-only in the inspector. + PROPERTY_USAGE_ARRAY = 1 << 29, // Used in the inspector to group properties as elements of an array. + + PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR, + PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNATIONALIZED, + PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE, }; -#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_GROUP_INDENT(m_name, m_prefix, m_depth) ::ClassDB::add_property_group(get_class_static(), m_name, m_prefix, m_depth) +#define ADD_SUBGROUP(m_name, m_prefix) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix) +#define ADD_SUBGROUP_INDENT(m_name, m_prefix, m_depth) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix, m_depth) +#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; @@ -160,7 +170,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), @@ -177,6 +187,14 @@ struct PropertyInfo { type(Variant::OBJECT), class_name(p_class_name) {} + explicit PropertyInfo(const GDNativePropertyInfo &pinfo) : + type((Variant::Type)pinfo.type), + name(pinfo.name), + class_name(pinfo.class_name), // can be null + hint((PropertyHint)pinfo.hint), + hint_string(pinfo.hint_string), // can be null + usage(pinfo.usage) {} + bool operator==(const PropertyInfo &p_info) const { return ((type == p_info.type) && (name == p_info.name) && @@ -193,10 +211,21 @@ struct PropertyInfo { Array convert_property_list(const List<PropertyInfo> *p_list); +enum MethodFlags { + METHOD_FLAG_NORMAL = 1, + METHOD_FLAG_EDITOR = 2, + METHOD_FLAG_CONST = 4, + METHOD_FLAG_VIRTUAL = 8, + METHOD_FLAG_VARARG = 16, + METHOD_FLAG_STATIC = 32, + METHOD_FLAG_OBJECT_CORE = 64, + METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL, +}; + struct MethodInfo { String name; PropertyInfo return_val; - uint32_t flags; // NOLINT - prevent clang-tidy to assign method_bind.h constant here, it should stay in .cpp. + uint32_t flags = METHOD_FLAGS_DEFAULT; int id = 0; List<PropertyInfo> arguments; Vector<Variant> default_arguments; @@ -208,37 +237,107 @@ struct MethodInfo { static MethodInfo from_dict(const Dictionary &p_dict); - MethodInfo(); - MethodInfo(const String &p_name); - MethodInfo(const String &p_name, const PropertyInfo &p_param1); - MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2); - MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3); - MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4); - MethodInfo(const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5); - MethodInfo(Variant::Type ret); - MethodInfo(Variant::Type ret, const String &p_name); - MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1); - MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2); - MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3); - MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4); - MethodInfo(Variant::Type ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5); - MethodInfo(const PropertyInfo &p_ret, const String &p_name); - MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1); - MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2); - MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3); - MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4); - MethodInfo(const PropertyInfo &p_ret, const String &p_name, const PropertyInfo &p_param1, const PropertyInfo &p_param2, const PropertyInfo &p_param3, const PropertyInfo &p_param4, const PropertyInfo &p_param5); + MethodInfo() {} + + void _push_params(const PropertyInfo &p_param) { + arguments.push_back(p_param); + } + + template <typename... VarArgs> + void _push_params(const PropertyInfo &p_param, VarArgs... p_params) { + arguments.push_back(p_param); + _push_params(p_params...); + } + + MethodInfo(const String &p_name) { name = p_name; } + + template <typename... VarArgs> + MethodInfo(const String &p_name, VarArgs... p_params) { + name = p_name; + _push_params(p_params...); + } + + MethodInfo(Variant::Type ret) { return_val.type = ret; } + MethodInfo(Variant::Type ret, const String &p_name) { + return_val.type = ret; + name = p_name; + } + + template <typename... VarArgs> + MethodInfo(Variant::Type ret, const String &p_name, VarArgs... p_params) { + name = p_name; + return_val.type = ret; + _push_params(p_params...); + } + + MethodInfo(const PropertyInfo &p_ret, const String &p_name) { + return_val = p_ret; + name = p_name; + } + + template <typename... VarArgs> + MethodInfo(const PropertyInfo &p_ret, const String &p_name, VarArgs... p_params) { + return_val = p_ret; + name = p_name; + _push_params(p_params...); + } }; -// old cast_to -//if ( is_type(T::get_class_static()) ) -//return static_cast<T*>(this); -////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; + GDNativeExtensionClassGetRID get_rid; + + _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; + GDNativeExtensionClassGetVirtual get_virtual; +}; + +#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call<false>(__VA_ARGS__) +#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<false>(__VA_ARGS__) + +#define GDVIRTUAL_REQUIRED_CALL(m_name, ...) _gdvirtual_##m_name##_call<true>(__VA_ARGS__) +#define GDVIRTUAL_REQUIRED_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<true>(__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. -*/ + * The following is an incomprehensible blob of hacks and workarounds to + * compensate for many of the fallacies in C++. As a plus, this macro pretty + * much alone defines the object model. + */ #define REVERSE_GET_PROPERTY_LIST \ public: \ @@ -256,13 +355,20 @@ private: private: \ void operator=(const m_class &p_rval) {} \ mutable StringName _class_name; \ - friend class ClassDB; \ + friend class ::ClassDB; \ \ public: \ + static constexpr bool _class_is_enabled = !bool(GD_IS_DEFINED(ClassDB_Disable_##m_class)) && m_inherits::_class_is_enabled; \ 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(); \ } \ @@ -282,20 +388,12 @@ public: m_inherits::get_inheritance_list_static(p_inheritance_list); \ p_inheritance_list->push_back(String(#m_class)); \ } \ - static String get_category_static() { \ - String category = m_inherits::get_category_static(); \ - if (_get_category != m_inherits::_get_category) { \ - if (category != "") { \ - category += "/"; \ - } \ - category += _get_category(); \ + virtual bool is_class(const String &p_class) const override { \ + if (_get_extension() && _get_extension()->is_class(p_class)) { \ + return true; \ } \ - return category; \ + return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); \ } \ - 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_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) { \ @@ -318,7 +416,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(); \ } \ @@ -330,7 +428,7 @@ protected: initialize_class(); \ } \ _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &) const { \ - return (bool (Object::*)(const StringName &, Variant &) const) & m_class::_get; \ + return (bool(Object::*)(const StringName &, Variant &) const) & m_class::_get; \ } \ virtual bool _getv(const StringName &p_name, Variant &r_ret) const override { \ if (m_class::_get_get() != m_inherits::_get_get()) { \ @@ -341,7 +439,7 @@ protected: return m_inherits::_getv(p_name, r_ret); \ } \ _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { \ - return (bool (Object::*)(const StringName &, const Variant &)) & m_class::_set; \ + return (bool(Object::*)(const StringName &, const Variant &)) & m_class::_set; \ } \ virtual bool _setv(const StringName &p_name, const Variant &p_property) override { \ if (m_inherits::_setv(p_name, p_property)) { \ @@ -353,7 +451,7 @@ protected: return false; \ } \ _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> * p_list) const { \ - return (void (Object::*)(List<PropertyInfo> *) const) & m_class::_get_property_list; \ + return (void(Object::*)(List<PropertyInfo> *) const) & m_class::_get_property_list; \ } \ virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const override { \ if (!p_reversed) { \ @@ -361,20 +459,20 @@ 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); \ } \ } \ _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { \ - return (void (Object::*)(int)) & m_class::_notification; \ + return (void(Object::*)(int)) & m_class::_notification; \ } \ virtual void _notificationv(int p_notification, bool p_reversed) override { \ if (!p_reversed) { \ @@ -390,12 +488,6 @@ protected: \ private: -#define OBJ_CATEGORY(m_category) \ -protected: \ - _FORCE_INLINE_ static String _get_category() { return m_category; } \ - \ -private: - #define OBJ_SAVE_TYPE(m_class) \ public: \ virtual String get_save_class() const override { return #m_class; } \ @@ -428,16 +520,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; @@ -464,42 +555,69 @@ private: #ifdef TOOLS_ENABLED bool _edited = false; uint32_t _edited_version = 0; - Set<String> editor_section_folding; + HashSet<String> editor_section_folding; #endif ScriptInstance *script_instance = nullptr; - Variant script; //reference does not yet exist, store it in a - Dictionary metadata; + Variant script; // Reference does not exist yet, store it in a Variant. + HashMap<StringName, Variant> metadata; + HashMap<StringName, Variant *> metadata_properties; mutable StringName _class_name; mutable const StringName *_class_ptr = nullptr; void _add_user_signal(const String &p_name, const Array &p_args = Array()); bool _has_user_signal(const StringName &p_name) const; - Variant _emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + Error _emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Array _get_signal_list() const; - Array _get_signal_connection_list(const String &p_signal) const; + Array _get_signal_connection_list(const StringName &p_signal) const; Array _get_incoming_connections() const; - void _set_bind(const String &p_set, const Variant &p_value); - Variant _get_bind(const String &p_name) const; + void _set_bind(const StringName &p_set, const Variant &p_value); + Variant _get_bind(const StringName &p_name) const; void _set_indexed_bind(const NodePath &p_name, const Variant &p_value); Variant _get_indexed_bind(const NodePath &p_name) const; _FORCE_INLINE_ void _construct_object(bool p_reference); - friend class Reference; + friend class RefCounted; bool type_is_reference = false; - SafeNumeric<uint32_t> instance_binding_count; - void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS]; + + std::mutex _instance_binding_mutex; + struct InstanceBinding { + void *binding = nullptr; + void *token = nullptr; + 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; }; virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const {}; virtual void _notificationv(int p_notification, bool p_reversed) {} - static String _get_category() { return ""; } static void _bind_methods(); bool _set(const StringName &p_name, const Variant &p_property) { return false; }; bool _get(const StringName &p_name, Variant &r_property) const { return false; }; @@ -524,9 +642,6 @@ protected: static void get_valid_parents_static(List<String> *p_parents); static void _get_valid_parents_static(List<String> *p_parents); - //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()); - Variant _call_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Variant _call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); @@ -537,7 +652,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; @@ -548,11 +663,13 @@ protected: void _disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force = false); -public: //should be protected, but bug in clang++ +public: // Should be protected, but bug in clang++. static void initialize_class(); _FORCE_INLINE_ static void register_custom_data_to_otdb() {} public: + static constexpr bool _class_is_enabled = true; + void notify_property_list_changed(); static void *get_class_ptr_static() { @@ -562,6 +679,7 @@ public: bool _is_gpl_reversed() const { return false; } + void detach_from_objectdb(); _FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_id; } template <class T> @@ -606,15 +724,27 @@ public: static String get_class_static() { return "Object"; } 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 { @@ -623,8 +753,6 @@ public: } /* IAPI */ - //void set(const String& p_name, const Variant& p_value); - //Variant get(const String& p_name) const; void set(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr); Variant get(const StringName &p_name, bool *r_valid = nullptr) const; @@ -636,13 +764,24 @@ public: bool has_method(const StringName &p_method) const; void get_method_list(List<MethodInfo> *p_list) const; Variant callv(const StringName &p_method, const Array &p_args); - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); - Variant call(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper + virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + + template <typename... VarArgs> + Variant call(const StringName &p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + Callable::CallError cerr; + return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr); + } void notification(int p_notification, bool p_reversed = false); virtual String to_string(); - //used mainly by script, get and set all INCLUDING string + // Used mainly by script, get and set all INCLUDING string. virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const; virtual void setvar(const Variant &p_key, const Variant &p_value, bool *r_valid = nullptr); @@ -651,28 +790,38 @@ public: void set_script(const Variant &p_script); Variant get_script() const; - /* 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 Variant &p_default = Variant()) const; + void get_meta_list(List<StringName> *p_list) const; #ifdef TOOLS_ENABLED void set_edited(bool p_edited); bool is_edited() const; - uint32_t get_edited_version() const; //this function is used to check when something changed beyond a point, it's used mainly for generating previews + // This function is used to check when something changed beyond a point, it's used mainly for generating previews. + uint32_t get_edited_version() const; #endif void set_script_instance(ScriptInstance *p_instance); _FORCE_INLINE_ ScriptInstance *get_script_instance() const { return script_instance; } - void set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance); //some script languages can't control instance creation, so this function eases the process + // Some script languages can't control instance creation, so this function eases the process. + void set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance); void add_user_signal(const MethodInfo &p_signal); - Error emit_signal(const StringName &p_name, VARIANT_ARG_LIST); - Error emit_signal(const StringName &p_name, const Variant **p_args, int p_argcount); + + template <typename... VarArgs> + Error emit_signal(const StringName &p_name, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + return emit_signalp(p_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); + } + + Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount); bool has_signal(const StringName &p_name) const; void get_signal_list(List<MethodInfo> *p_signals) const; void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const; @@ -684,7 +833,11 @@ public: void disconnect(const StringName &p_signal, const Callable &p_callable); bool is_connected(const StringName &p_signal, const Callable &p_callable) const; - void call_deferred(const StringName &p_method, VARIANT_ARG_LIST); + template <typename... VarArgs> + void call_deferred(const StringName &p_name, VarArgs... p_args) { + MessageQueue::get_singleton()->push_call(this, p_name, p_args...); + } + void set_deferred(const StringName &p_property, const Variant &p_value); void set_block_signals(bool p_block); @@ -697,10 +850,11 @@ public: virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; - String tr(const StringName &p_message, const StringName &p_context = "") const; // translate message (internationalization) + // Translate message (internationalization). + String tr(const StringName &p_message, const StringName &p_context = "") const; String tr_n(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; - bool _is_queued_for_deletion = false; // set to true by SceneTree::queue_delete() + bool _is_queued_for_deletion = false; // Set to true by SceneTree::queue_delete(). bool is_queued_for_deletion() const; _FORCE_INLINE_ void set_message_translation(bool p_enable) { _can_translate = p_enable; } @@ -709,19 +863,20 @@ public: #ifdef TOOLS_ENABLED void editor_set_section_unfold(const String &p_section, bool p_unfolded); bool editor_is_section_unfolded(const String &p_section); - const Set<String> &editor_get_section_folding() const { return editor_section_folding; } + const HashSet<String> &editor_get_section_folding() const { return editor_section_folding; } void editor_clear_section_folding() { editor_section_folding.clear(); } #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(); @@ -731,18 +886,18 @@ bool predelete_handler(Object *p_object); void postinitialize_handler(Object *p_object); class ObjectDB { -//this needs to add up to 63, 1 bit is for reference +// This needs to add up to 63, 1 bit is for reference. #define OBJECTDB_VALIDATOR_BITS 39 #define OBJECTDB_VALIDATOR_MASK ((uint64_t(1) << OBJECTDB_VALIDATOR_BITS) - 1) #define OBJECTDB_SLOT_MAX_COUNT_BITS 24 #define OBJECTDB_SLOT_MAX_COUNT_MASK ((uint64_t(1) << OBJECTDB_SLOT_MAX_COUNT_BITS) - 1) #define OBJECTDB_REFERENCE_BIT (uint64_t(1) << (OBJECTDB_SLOT_MAX_COUNT_BITS + OBJECTDB_VALIDATOR_BITS)) - struct ObjectSlot { //128 bits per slot + 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; - Object *object; + uint64_t is_ref_counted : 1; + Object *object = nullptr; }; static SpinLock spin_lock; @@ -768,7 +923,7 @@ public: uint64_t id = p_instance_id; uint32_t slot = id & OBJECTDB_SLOT_MAX_COUNT_MASK; - ERR_FAIL_COND_V(slot >= slot_max, nullptr); //this should never happen unless RID is corrupted + ERR_FAIL_COND_V(slot >= slot_max, nullptr); // This should never happen unless RID is corrupted. spin_lock.lock(); diff --git a/core/object/object_id.h b/core/object/object_id.h index 7f2496ad48..a36997630f 100644 --- a/core/object/object_id.h +++ b/core/object/object_id.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -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 22e4e8a336..726e2c012c 100644 --- a/core/object/reference.cpp +++ b/core/object/ref_counted.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* reference.cpp */ +/* ref_counted.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -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.get() > 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.get() > 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,9 +106,9 @@ 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); + return Ref<RefCounted>(r); } return obj; @@ -123,7 +118,7 @@ void WeakRef::set_obj(Object *p_object) { ref = p_object ? p_object->get_instance_id() : ObjectID(); } -void WeakRef::set_ref(const REF &p_ref) { +void WeakRef::set_ref(const Ref<RefCounted> &p_ref) { ref = p_ref.is_valid() ? p_ref->get_instance_id() : ObjectID(); } diff --git a/core/object/reference.h b/core/object/ref_counted.h index d02cb12069..bd06a84bd8 100644 --- a/core/object/reference.h +++ b/core/object/ref_counted.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* reference.h */ +/* ref_counted.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -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,8 @@ public: } }; -typedef Ref<Reference> REF; - -class WeakRef : public Reference { - GDCLASS(WeakRef, Reference); +class WeakRef : public RefCounted { + GDCLASS(WeakRef, RefCounted); ObjectID ref; @@ -247,7 +245,7 @@ protected: public: Variant get_ref() const; void set_obj(Object *p_object); - void set_ref(const REF &p_ref); + void set_ref(const Ref<RefCounted> &p_ref); WeakRef() {} }; @@ -258,20 +256,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; + *(const_cast<Ref<RefCounted> *>(reinterpret_cast<const 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 +292,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 42fb0a0caf..226fd8b791 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -46,10 +46,12 @@ bool ScriptServer::languages_finished = false; ScriptEditRequestFunction ScriptServer::edit_request_func = nullptr; void Script::_notification(int p_what) { - if (p_what == NOTIFICATION_POSTINITIALIZE) { - if (EngineDebugger::is_active()) { - EngineDebugger::get_script_debugger()->set_break_language(get_language()); - } + switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: { + if (EngineDebugger::is_active()) { + EngineDebugger::get_script_debugger()->set_break_language(get_language()); + } + } break; } } @@ -63,8 +65,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 +75,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,24 +85,24 @@ 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; } Dictionary Script::_get_script_constant_map() { Dictionary ret; - Map<StringName, Variant> map; + HashMap<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 +122,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) { @@ -142,7 +144,7 @@ void ScriptServer::register_language(ScriptLanguage *p_language) { _languages[_language_count++] = p_language; } -void ScriptServer::unregister_language(ScriptLanguage *p_language) { +void ScriptServer::unregister_language(const ScriptLanguage *p_language) { for (int i = 0; i < _language_count; i++) { if (_languages[i] == p_language) { _language_count--; @@ -251,14 +253,13 @@ StringName ScriptServer::get_global_class_native_base(const String &p_class) { } void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) { - const StringName *K = nullptr; List<StringName> classes; - while ((K = global_classes.next(K))) { - classes.push_back(*K); + for (const KeyValue<StringName, GlobalScriptClass> &E : global_classes) { + classes.push_back(E.key); } 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,12 +267,12 @@ 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); } @@ -294,13 +295,18 @@ void ScriptServer::save_global_classes() { } //////////////////// + +Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + return callp(p_method, p_args, p_argcount, r_error); +} + 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); } @@ -308,20 +314,6 @@ void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) } } -Variant ScriptInstance::call(const StringName &p_method, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS; - int argc = 0; - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) { - break; - } - argc++; - } - - Callable::CallError error; - return call(p_method, argptr, argc, error); -} - void ScriptInstance::property_set_fallback(const StringName &, const Variant &, bool *r_valid) { if (r_valid) { *r_valid = false; @@ -352,11 +344,14 @@ void ScriptLanguage::get_core_type_words(List<String> *p_core_type_words) const p_core_type_words->push_back("Vector3"); p_core_type_words->push_back("Vector3i"); p_core_type_words->push_back("Transform2D"); + p_core_type_words->push_back("Vector4"); + p_core_type_words->push_back("Vector4i"); 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("Projection"); p_core_type_words->push_back("Color"); p_core_type_words->push_back("StringName"); p_core_type_words->push_back("NodePath"); @@ -430,16 +425,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); } } } @@ -487,13 +482,13 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { return false; } -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; +void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const HashMap<StringName, Variant> &p_values) { + HashSet<StringName> new_values; + 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,16 +498,16 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c properties = p_properties; List<StringName> to_remove; - for (Map<StringName, Variant>::Element *E = values.front(); E; E = E->next()) { - if (!new_values.has(E->key())) { - to_remove.push_back(E->key()); + for (KeyValue<StringName, Variant> &E : values) { + if (!new_values.has(E.key)) { + to_remove.push_back(E.key); } Variant defval; - if (script->get_property_default_value(E->key(), defval)) { + if (script->get_property_default_value(E.key, defval)) { //remove because it's the same as the default value - if (defval == E->get()) { - to_remove.push_back(E->key()); + if (defval == E.value) { + to_remove.push_back(E.key); } } } @@ -533,23 +528,23 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid) { if (script->is_placeholder_fallback_enabled()) { - Map<StringName, Variant>::Element *E = values.find(p_name); + HashMap<StringName, Variant>::Iterator E = values.find(p_name); if (E) { - E->value() = p_value; + E->value = p_value; } else { values.insert(p_name, p_value); } 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; } } if (!found) { - properties.push_back(PropertyInfo(p_value.get_type(), p_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE)); + properties.push_back(PropertyInfo(p_value.get_type(), p_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE)); } } @@ -560,13 +555,13 @@ void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name, Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_name, bool *r_valid) { if (script->is_placeholder_fallback_enabled()) { - const Map<StringName, Variant>::Element *E = values.find(p_name); + HashMap<StringName, Variant>::ConstIterator E = values.find(p_name); if (E) { if (r_valid) { *r_valid = true; } - return E->value(); + return E->value; } E = constants.find(p_name); @@ -574,7 +569,7 @@ Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_nam if (r_valid) { *r_valid = true; } - return E->value(); + return E->value; } } @@ -585,14 +580,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..c9f8a4f828 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,30 +32,14 @@ #define SCRIPT_LANGUAGE_H #include "core/doc_data.h" -#include "core/io/multiplayer_api.h" #include "core/io/resource.h" -#include "core/templates/map.h" #include "core/templates/pair.h" +#include "core/templates/rb_map.h" 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 @@ -83,7 +67,7 @@ public: _FORCE_INLINE_ static int get_language_count() { return _language_count; } static ScriptLanguage *get_language(int p_idx); static void register_language(ScriptLanguage *p_language); - static void unregister_language(ScriptLanguage *p_language); + static void unregister_language(const ScriptLanguage *p_language); static void set_reload_scripts_on_save(bool p_enable); static bool is_reload_scripts_on_save_enabled(); @@ -130,7 +114,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 @@ -147,7 +131,7 @@ public: virtual Error reload(bool p_keep_state = false) = 0; #ifdef TOOLS_ENABLED - virtual const Vector<DocData::ClassDoc> &get_documentation() const = 0; + virtual Vector<DocData::ClassDoc> get_documentation() const = 0; #endif // TOOLS_ENABLED virtual bool has_method(const StringName &p_method) const = 0; @@ -169,22 +153,12 @@ public: virtual int get_member_line(const StringName &p_member) const { return -1; } - virtual void get_constants(Map<StringName, Variant> *p_constants) {} - virtual void get_members(Set<StringName> *p_constants) {} + virtual void get_constants(HashMap<StringName, Variant> *p_constants) {} + virtual void get_members(HashSet<StringName> *p_constants) {} 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 Variant get_rpc_config() const = 0; Script() {} }; @@ -201,8 +175,21 @@ public: virtual void get_method_list(List<MethodInfo> *p_list) const = 0; virtual bool has_method(const StringName &p_method) const = 0; - virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST); - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0; + + virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0; + + template <typename... VarArgs> + Variant call(const StringName &p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + Callable::CallError cerr; + return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr); + } + + virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); // implement if language supports const functions virtual void notification(int p_notification) = 0; virtual String to_string(bool *r_valid) { if (r_valid) { @@ -225,51 +212,12 @@ 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 Variant get_rpc_config() const { return get_script()->get_rpc_config(); } virtual ScriptLanguage *get_language() = 0; virtual ~ScriptInstance(); }; -struct ScriptCodeCompletionOption { - enum Kind { - KIND_CLASS, - KIND_FUNCTION, - KIND_SIGNAL, - KIND_VARIABLE, - KIND_MEMBER, - KIND_ENUM, - KIND_CONSTANT, - KIND_NODE_PATH, - KIND_FILE_PATH, - KIND_PLAIN_TEXT, - }; - Kind kind = KIND_PLAIN_TEXT; - String display; - String insert_text; - Color font_color; - RES icon; - Variant default_value; - - ScriptCodeCompletionOption() {} - - ScriptCodeCompletionOption(const String &p_text, Kind p_kind) { - display = p_text; - insert_text = p_text; - kind = p_kind; - } -}; - class ScriptCodeCompletionCache { static ScriptCodeCompletionCache *singleton; @@ -281,7 +229,8 @@ public: virtual ~ScriptCodeCompletionCache() {} }; -class ScriptLanguage { +class ScriptLanguage : public Object { + GDCLASS(ScriptLanguage, Object) public: virtual String get_name() const = 0; @@ -301,14 +250,40 @@ public: String message; }; + struct ScriptError { + int line = -1; + int column = -1; + String message; + }; + + enum TemplateLocation { + TEMPLATE_BUILT_IN, + TEMPLATE_EDITOR, + TEMPLATE_PROJECT + }; + + struct ScriptTemplate { + String inherit = "Object"; + String name; + String description; + String content; + int id = 0; + TemplateLocation origin = TemplateLocation::TEMPLATE_BUILT_IN; + + String get_hash() const { + return itos(origin) + inherit + name; + } + }; + 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 Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { return Ref<Script>(); } + virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) { return Vector<ScriptTemplate>(); } 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, HashSet<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; @@ -320,22 +295,70 @@ public: virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; } virtual bool overrides_external_editor() { return false; } - virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; } + /* Keep enum in Sync with: */ + /* /scene/gui/code_edit.h - CodeEdit::CodeCompletionKind */ + enum CodeCompletionKind { + CODE_COMPLETION_KIND_CLASS, + CODE_COMPLETION_KIND_FUNCTION, + CODE_COMPLETION_KIND_SIGNAL, + CODE_COMPLETION_KIND_VARIABLE, + CODE_COMPLETION_KIND_MEMBER, + CODE_COMPLETION_KIND_ENUM, + CODE_COMPLETION_KIND_CONSTANT, + CODE_COMPLETION_KIND_NODE_PATH, + CODE_COMPLETION_KIND_FILE_PATH, + CODE_COMPLETION_KIND_PLAIN_TEXT, + CODE_COMPLETION_KIND_MAX + }; + + enum CodeCompletionLocation { + LOCATION_LOCAL = 0, + LOCATION_PARENT_MASK = 1 << 8, + LOCATION_OTHER_USER_CODE = 1 << 9, + LOCATION_OTHER = 1 << 10, + }; + + struct CodeCompletionOption { + CodeCompletionKind kind = CODE_COMPLETION_KIND_PLAIN_TEXT; + String display; + String insert_text; + Color font_color; + Ref<Resource> icon; + Variant default_value; + Vector<Pair<int, int>> matches; + int location = LOCATION_OTHER; + + CodeCompletionOption() {} + + CodeCompletionOption(const String &p_text, CodeCompletionKind p_kind, int p_location = LOCATION_OTHER) { + display = p_text; + insert_text = p_text; + kind = p_kind; + location = p_location; + } + }; + + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<CodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; } + + enum LookupResultType { + LOOKUP_RESULT_SCRIPT_LOCATION, + LOOKUP_RESULT_CLASS, + LOOKUP_RESULT_CLASS_CONSTANT, + LOOKUP_RESULT_CLASS_PROPERTY, + LOOKUP_RESULT_CLASS_METHOD, + LOOKUP_RESULT_CLASS_SIGNAL, + LOOKUP_RESULT_CLASS_ENUM, + LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE, + LOOKUP_RESULT_CLASS_ANNOTATION, + LOOKUP_RESULT_MAX + }; struct LookupResult { - enum Type { - RESULT_SCRIPT_LOCATION, - RESULT_CLASS, - RESULT_CLASS_CONSTANT, - RESULT_CLASS_PROPERTY, - RESULT_CLASS_METHOD, - RESULT_CLASS_ENUM, - RESULT_CLASS_TBD_GLOBALSCOPE - }; - Type type; + LookupResultType type; Ref<Script> script; String class_name; String class_member; + String class_path; int location; }; @@ -379,6 +402,7 @@ public: virtual void get_recognized_extensions(List<String> *p_extensions) const = 0; virtual void get_public_functions(List<MethodInfo> *p_functions) const = 0; virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const = 0; + virtual void get_public_annotations(List<MethodInfo> *p_annotations) const = 0; struct ProfilingInfo { StringName signature; @@ -409,52 +433,42 @@ public: extern uint8_t script_encryption_key[32]; class PlaceHolderScriptInstance : public ScriptInstance { - Object *owner; + Object *owner = nullptr; List<PropertyInfo> properties; - Map<StringName, Variant> values; - Map<StringName, Variant> constants; - ScriptLanguage *language; + HashMap<StringName, Variant> values; + HashMap<StringName, Variant> constants; + ScriptLanguage *language = nullptr; Ref<Script> script; public: - virtual bool set(const StringName &p_name, const Variant &p_value); - virtual bool get(const StringName &p_name, Variant &r_ret) const; - virtual void get_property_list(List<PropertyInfo> *p_properties) const; - virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const; - - virtual void get_method_list(List<MethodInfo> *p_list) const; - virtual bool has_method(const StringName &p_method) const; - virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST) { return Variant(); } - virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + virtual bool set(const StringName &p_name, const Variant &p_value) override; + virtual bool get(const StringName &p_name, Variant &r_ret) const override; + virtual void get_property_list(List<PropertyInfo> *p_properties) const override; + virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const override; + + virtual void get_method_list(List<MethodInfo> *p_list) const override; + virtual bool has_method(const StringName &p_method) const override; + + virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override { r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } - virtual void notification(int p_notification) {} - - virtual Ref<Script> get_script() const { return script; } + virtual void notification(int p_notification) override {} - virtual ScriptLanguage *get_language() { return language; } + virtual Ref<Script> get_script() const override { return script; } - Object *get_owner() { return owner; } + virtual ScriptLanguage *get_language() override { return language; } - void update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values); //likely changed in editor + Object *get_owner() override { return owner; } - virtual bool is_placeholder() const { return true; } + void update(const List<PropertyInfo> &p_properties, const HashMap<StringName, Variant> &p_values); //likely changed in editor - 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 bool is_placeholder() const override { return true; } - 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 void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr) override; + virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid = nullptr) override; - 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 Variant get_rpc_config() const override { return Variant(); } PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner); ~PlaceHolderScriptInstance(); diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp new file mode 100644 index 0000000000..9de784ea7f --- /dev/null +++ b/core/object/script_language_extension.cpp @@ -0,0 +1,183 @@ +/*************************************************************************/ +/* script_language_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "script_language_extension.h" + +void ScriptExtension::_bind_methods() { + GDVIRTUAL_BIND(_editor_can_reload_from_file); + GDVIRTUAL_BIND(_placeholder_erased, "placeholder"); + + GDVIRTUAL_BIND(_can_instantiate); + GDVIRTUAL_BIND(_get_base_script); + GDVIRTUAL_BIND(_inherits_script, "script"); + + GDVIRTUAL_BIND(_get_instance_base_type); + GDVIRTUAL_BIND(_instance_create, "for_object"); + GDVIRTUAL_BIND(_placeholder_instance_create, "for_object"); + + GDVIRTUAL_BIND(_instance_has, "object"); + + GDVIRTUAL_BIND(_has_source_code); + GDVIRTUAL_BIND(_get_source_code); + + GDVIRTUAL_BIND(_set_source_code, "code"); + GDVIRTUAL_BIND(_reload, "keep_state"); + + GDVIRTUAL_BIND(_get_documentation); + + GDVIRTUAL_BIND(_has_method, "method"); + GDVIRTUAL_BIND(_get_method_info, "method"); + + GDVIRTUAL_BIND(_is_tool); + GDVIRTUAL_BIND(_is_valid); + GDVIRTUAL_BIND(_get_language); + + GDVIRTUAL_BIND(_has_script_signal, "signal"); + GDVIRTUAL_BIND(_get_script_signal_list); + + GDVIRTUAL_BIND(_get_property_default_value, "property"); + + GDVIRTUAL_BIND(_update_exports); + GDVIRTUAL_BIND(_get_script_method_list); + GDVIRTUAL_BIND(_get_script_property_list); + + GDVIRTUAL_BIND(_get_member_line, "member"); + + GDVIRTUAL_BIND(_get_constants); + GDVIRTUAL_BIND(_get_members); + GDVIRTUAL_BIND(_is_placeholder_fallback_enabled); + + GDVIRTUAL_BIND(_get_rpc_config); +} + +void ScriptLanguageExtension::_bind_methods() { + GDVIRTUAL_BIND(_get_name); + GDVIRTUAL_BIND(_init); + GDVIRTUAL_BIND(_get_type); + GDVIRTUAL_BIND(_get_extension); + GDVIRTUAL_BIND(_execute_file, "path"); + GDVIRTUAL_BIND(_finish); + + GDVIRTUAL_BIND(_get_reserved_words); + GDVIRTUAL_BIND(_is_control_flow_keyword, "keyword"); + GDVIRTUAL_BIND(_get_comment_delimiters); + GDVIRTUAL_BIND(_get_string_delimiters); + GDVIRTUAL_BIND(_make_template, "template", "class_name", "base_class_name"); + GDVIRTUAL_BIND(_get_built_in_templates, "object"); + GDVIRTUAL_BIND(_is_using_templates); + GDVIRTUAL_BIND(_validate, "script", "path", "validate_functions", "validate_errors", "validate_warnings", "validate_safe_lines"); + + GDVIRTUAL_BIND(_validate_path, "path"); + GDVIRTUAL_BIND(_create_script); + GDVIRTUAL_BIND(_has_named_classes); + GDVIRTUAL_BIND(_supports_builtin_mode); + GDVIRTUAL_BIND(_supports_documentation); + GDVIRTUAL_BIND(_can_inherit_from_file); + GDVIRTUAL_BIND(_find_function, "class_name", "function_name"); + GDVIRTUAL_BIND(_make_function, "class_name", "function_name", "function_args"); + GDVIRTUAL_BIND(_open_in_external_editor, "script", "line", "column"); + GDVIRTUAL_BIND(_overrides_external_editor); + + GDVIRTUAL_BIND(_complete_code, "code", "path", "owner"); + GDVIRTUAL_BIND(_lookup_code, "code", "symbol", "path", "owner"); + GDVIRTUAL_BIND(_auto_indent_code, "code", "from_line", "to_line"); + + GDVIRTUAL_BIND(_add_global_constant, "name", "value"); + GDVIRTUAL_BIND(_add_named_global_constant, "name", "value"); + GDVIRTUAL_BIND(_remove_named_global_constant, "name"); + + GDVIRTUAL_BIND(_thread_enter); + GDVIRTUAL_BIND(_thread_exit); + GDVIRTUAL_BIND(_debug_get_error); + GDVIRTUAL_BIND(_debug_get_stack_level_count); + + GDVIRTUAL_BIND(_debug_get_stack_level_line, "level"); + GDVIRTUAL_BIND(_debug_get_stack_level_function, "level"); + GDVIRTUAL_BIND(_debug_get_stack_level_locals, "level", "max_subitems", "max_depth"); + GDVIRTUAL_BIND(_debug_get_stack_level_members, "level", "max_subitems", "max_depth"); + GDVIRTUAL_BIND(_debug_get_stack_level_instance, "level"); + GDVIRTUAL_BIND(_debug_get_globals, "max_subitems", "max_depth"); + GDVIRTUAL_BIND(_debug_parse_stack_level_expression, "level", "expression", "max_subitems", "max_depth"); + + GDVIRTUAL_BIND(_debug_get_current_stack_info); + + GDVIRTUAL_BIND(_reload_all_scripts); + GDVIRTUAL_BIND(_reload_tool_script, "script", "soft_reload"); + + GDVIRTUAL_BIND(_get_recognized_extensions); + GDVIRTUAL_BIND(_get_public_functions); + GDVIRTUAL_BIND(_get_public_constants); + GDVIRTUAL_BIND(_get_public_annotations); + + GDVIRTUAL_BIND(_profiling_start); + GDVIRTUAL_BIND(_profiling_stop); + + GDVIRTUAL_BIND(_profiling_get_accumulated_data, "info_array", "info_max"); + GDVIRTUAL_BIND(_profiling_get_frame_data, "info_array", "info_max"); + + GDVIRTUAL_BIND(_alloc_instance_binding_data, "object"); + GDVIRTUAL_BIND(_free_instance_binding_data, "data"); + + GDVIRTUAL_BIND(_refcount_incremented_instance_binding, "object"); + GDVIRTUAL_BIND(_refcount_decremented_instance_binding, "object"); + + GDVIRTUAL_BIND(_frame); + + GDVIRTUAL_BIND(_handles_global_class_type, "type"); + GDVIRTUAL_BIND(_get_global_class_name, "path"); + + BIND_ENUM_CONSTANT(LOOKUP_RESULT_SCRIPT_LOCATION); + BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS); + BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_CONSTANT); + BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_PROPERTY); + BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_METHOD); + BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_SIGNAL); + BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_ENUM); + BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE); + BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_ANNOTATION); + BIND_ENUM_CONSTANT(LOOKUP_RESULT_MAX); + + BIND_ENUM_CONSTANT(LOCATION_LOCAL); + BIND_ENUM_CONSTANT(LOCATION_PARENT_MASK); + BIND_ENUM_CONSTANT(LOCATION_OTHER_USER_CODE); + BIND_ENUM_CONSTANT(LOCATION_OTHER); + + BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_CLASS); + BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_FUNCTION); + BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_SIGNAL); + BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_VARIABLE); + BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_MEMBER); + BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_ENUM); + BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_CONSTANT); + BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_NODE_PATH); + BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_FILE_PATH); + BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_PLAIN_TEXT); + BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_MAX); +} diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h new file mode 100644 index 0000000000..10eacfd9f7 --- /dev/null +++ b/core/object/script_language_extension.h @@ -0,0 +1,828 @@ +/*************************************************************************/ +/* script_language_extension.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SCRIPT_LANGUAGE_EXTENSION_H +#define SCRIPT_LANGUAGE_EXTENSION_H + +#include "core/extension/ext_wrappers.gen.inc" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" +#include "core/variant/typed_array.h" + +class ScriptExtension : public Script { + GDCLASS(ScriptExtension, Script) + +protected: + EXBIND0R(bool, editor_can_reload_from_file) + + GDVIRTUAL1(_placeholder_erased, GDNativePtr<void>) + virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override { + GDVIRTUAL_CALL(_placeholder_erased, p_placeholder); + } + + static void _bind_methods(); + +public: + EXBIND0RC(bool, can_instantiate) + EXBIND0RC(Ref<Script>, get_base_script) + EXBIND1RC(bool, inherits_script, const Ref<Script> &) + EXBIND0RC(StringName, get_instance_base_type) + + GDVIRTUAL1RC(GDNativePtr<void>, _instance_create, Object *) + virtual ScriptInstance *instance_create(Object *p_this) override { + GDNativePtr<void> ret = nullptr; + GDVIRTUAL_REQUIRED_CALL(_instance_create, p_this, ret); + return reinterpret_cast<ScriptInstance *>(ret.operator void *()); + } + GDVIRTUAL1RC(GDNativePtr<void>, _placeholder_instance_create, Object *) + PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) override { + GDNativePtr<void> ret = nullptr; + GDVIRTUAL_REQUIRED_CALL(_placeholder_instance_create, p_this, ret); + return reinterpret_cast<PlaceHolderScriptInstance *>(ret.operator void *()); + } + + EXBIND1RC(bool, instance_has, const Object *) + EXBIND0RC(bool, has_source_code) + EXBIND0RC(String, get_source_code) + EXBIND1(set_source_code, const String &) + EXBIND1R(Error, reload, bool) + + GDVIRTUAL0RC(TypedArray<Dictionary>, _get_documentation) +#ifdef TOOLS_ENABLED + virtual Vector<DocData::ClassDoc> get_documentation() const override { + TypedArray<Dictionary> doc; + GDVIRTUAL_REQUIRED_CALL(_get_documentation, doc); + + Vector<DocData::ClassDoc> class_doc; +#ifndef _MSC_VER +#warning missing conversion from documentation to ClassDoc +#endif + return class_doc; + } +#endif // TOOLS_ENABLED + + EXBIND1RC(bool, has_method, const StringName &) + + GDVIRTUAL1RC(Dictionary, _get_method_info, const StringName &) + virtual MethodInfo get_method_info(const StringName &p_method) const override { + Dictionary mi; + GDVIRTUAL_REQUIRED_CALL(_get_method_info, p_method, mi); + return MethodInfo::from_dict(mi); + } + + EXBIND0RC(bool, is_tool) + EXBIND0RC(bool, is_valid) + + EXBIND0RC(ScriptLanguage *, get_language) + EXBIND1RC(bool, has_script_signal, const StringName &) + + GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_signal_list) + + virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override { + TypedArray<Dictionary> sl; + GDVIRTUAL_REQUIRED_CALL(_get_script_signal_list, sl); + for (int i = 0; i < sl.size(); i++) { + r_signals->push_back(MethodInfo::from_dict(sl[i])); + } + } + + GDVIRTUAL1RC(bool, _has_property_default_value, const StringName &) + GDVIRTUAL1RC(Variant, _get_property_default_value, const StringName &) + + virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const override { + bool has_dv = false; + if (!GDVIRTUAL_REQUIRED_CALL(_has_property_default_value, p_property, has_dv) || !has_dv) { + return false; + } + Variant ret; + GDVIRTUAL_REQUIRED_CALL(_get_property_default_value, p_property, ret); + return ret; + } + + EXBIND0(update_exports) + + GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_method_list) + + virtual void get_script_method_list(List<MethodInfo> *r_methods) const override { + TypedArray<Dictionary> sl; + GDVIRTUAL_REQUIRED_CALL(_get_script_method_list, sl); + for (int i = 0; i < sl.size(); i++) { + r_methods->push_back(MethodInfo::from_dict(sl[i])); + } + } + + GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_property_list) + + virtual void get_script_property_list(List<PropertyInfo> *r_propertys) const override { + TypedArray<Dictionary> sl; + GDVIRTUAL_REQUIRED_CALL(_get_script_property_list, sl); + for (int i = 0; i < sl.size(); i++) { + r_propertys->push_back(PropertyInfo::from_dict(sl[i])); + } + } + + EXBIND1RC(int, get_member_line, const StringName &) + + GDVIRTUAL0RC(Dictionary, _get_constants) + + virtual void get_constants(HashMap<StringName, Variant> *p_constants) override { + Dictionary constants; + GDVIRTUAL_REQUIRED_CALL(_get_constants, constants); + List<Variant> keys; + constants.get_key_list(&keys); + for (const Variant &K : keys) { + p_constants->insert(K, constants[K]); + } + } + GDVIRTUAL0RC(TypedArray<StringName>, _get_members) + virtual void get_members(HashSet<StringName> *p_members) override { + TypedArray<StringName> members; + GDVIRTUAL_REQUIRED_CALL(_get_members, members); + for (int i = 0; i < members.size(); i++) { + p_members->insert(members[i]); + } + } + + EXBIND0RC(bool, is_placeholder_fallback_enabled) + + GDVIRTUAL0RC(Variant, _get_rpc_config) + + virtual const Variant get_rpc_config() const override { + Variant ret; + GDVIRTUAL_REQUIRED_CALL(_get_rpc_config, ret); + return ret; + } + + ScriptExtension() {} +}; + +typedef ScriptLanguage::ProfilingInfo ScriptLanguageExtensionProfilingInfo; + +GDVIRTUAL_NATIVE_PTR(ScriptLanguageExtensionProfilingInfo) + +class ScriptLanguageExtension : public ScriptLanguage { + GDCLASS(ScriptLanguageExtension, ScriptLanguage) +protected: + static void _bind_methods(); + +public: + EXBIND0RC(String, get_name) + + EXBIND0(init) + EXBIND0RC(String, get_type) + EXBIND0RC(String, get_extension) + EXBIND1R(Error, execute_file, const String &) + EXBIND0(finish) + + /* EDITOR FUNCTIONS */ + + GDVIRTUAL0RC(Vector<String>, _get_reserved_words) + + virtual void get_reserved_words(List<String> *p_words) const override { + Vector<String> ret; + GDVIRTUAL_REQUIRED_CALL(_get_reserved_words, ret); + for (int i = 0; i < ret.size(); i++) { + p_words->push_back(ret[i]); + } + } + EXBIND1RC(bool, is_control_flow_keyword, String) + + GDVIRTUAL0RC(Vector<String>, _get_comment_delimiters) + + virtual void get_comment_delimiters(List<String> *p_words) const override { + Vector<String> ret; + GDVIRTUAL_REQUIRED_CALL(_get_comment_delimiters, ret); + for (int i = 0; i < ret.size(); i++) { + p_words->push_back(ret[i]); + } + } + + GDVIRTUAL0RC(Vector<String>, _get_string_delimiters) + + virtual void get_string_delimiters(List<String> *p_words) const override { + Vector<String> ret; + GDVIRTUAL_REQUIRED_CALL(_get_string_delimiters, ret); + for (int i = 0; i < ret.size(); i++) { + p_words->push_back(ret[i]); + } + } + + EXBIND3RC(Ref<Script>, make_template, const String &, const String &, const String &) + + GDVIRTUAL1RC(TypedArray<Dictionary>, _get_built_in_templates, StringName) + + virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override { + TypedArray<Dictionary> ret; + GDVIRTUAL_REQUIRED_CALL(_get_built_in_templates, p_object, ret); + Vector<ScriptTemplate> stret; + for (int i = 0; i < ret.size(); i++) { + Dictionary d = ret[i]; + ScriptTemplate st; + ERR_CONTINUE(!d.has("inherit")); + st.inherit = d["inherit"]; + ERR_CONTINUE(!d.has("name")); + st.name = d["name"]; + ERR_CONTINUE(!d.has("description")); + st.description = d["description"]; + ERR_CONTINUE(!d.has("content")); + st.content = d["content"]; + ERR_CONTINUE(!d.has("id")); + st.id = d["id"]; + ERR_CONTINUE(!d.has("origin")); + st.origin = TemplateLocation(int(d["origin"])); + stret.push_back(st); + } + return stret; + } + + EXBIND0R(bool, is_using_templates) + + GDVIRTUAL6RC(Dictionary, _validate, const String &, const String &, bool, bool, bool, bool) + 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, HashSet<int> *r_safe_lines = nullptr) const override { + Dictionary ret; + GDVIRTUAL_REQUIRED_CALL(_validate, p_script, p_path, r_functions != nullptr, r_errors != nullptr, r_warnings != nullptr, r_safe_lines != nullptr, ret); + if (!ret.has("valid")) { + return false; + } + if (r_functions != nullptr && ret.has("functions")) { + Vector<String> functions = ret["functions"]; + for (int i = 0; i < functions.size(); i++) { + r_functions->push_back(functions[i]); + } + } + if (r_errors != nullptr && ret.has("errors")) { + Array errors = ret["errors"]; + for (int i = 0; i < errors.size(); i++) { + Dictionary err = errors[i]; + ERR_CONTINUE(!err.has("line")); + ERR_CONTINUE(!err.has("column")); + ERR_CONTINUE(!err.has("message")); + + ScriptError serr; + serr.line = err["line"]; + serr.column = err["column"]; + serr.message = err["message"]; + + r_errors->push_back(serr); + } + } + if (r_warnings != nullptr && ret.has("warnings")) { + ERR_FAIL_COND_V(!ret.has("warnings"), false); + Array warnings = ret["warnings"]; + for (int i = 0; i < warnings.size(); i++) { + Dictionary warn = warnings[i]; + ERR_CONTINUE(!warn.has("start_line")); + ERR_CONTINUE(!warn.has("end_line")); + ERR_CONTINUE(!warn.has("leftmost_column")); + ERR_CONTINUE(!warn.has("rightmost_column")); + ERR_CONTINUE(!warn.has("code")); + ERR_CONTINUE(!warn.has("string_code")); + ERR_CONTINUE(!warn.has("message")); + + Warning swarn; + swarn.start_line = warn["start_line"]; + swarn.end_line = warn["end_line"]; + swarn.leftmost_column = warn["leftmost_column"]; + swarn.rightmost_column = warn["rightmost_column"]; + swarn.code = warn["code"]; + swarn.string_code = warn["string_code"]; + swarn.message = warn["message"]; + + r_warnings->push_back(swarn); + } + } + if (r_safe_lines != nullptr && ret.has("safe_lines")) { + PackedInt32Array safe_lines = ret["safe_lines"]; + for (int i = 0; i < safe_lines.size(); i++) { + r_safe_lines->insert(safe_lines[i]); + } + } + return ret["valid"]; + } + + EXBIND1RC(String, validate_path, const String &) + GDVIRTUAL0RC(Object *, _create_script) + Script *create_script() const override { + Object *ret = nullptr; + GDVIRTUAL_REQUIRED_CALL(_create_script, ret); + return Object::cast_to<Script>(ret); + } + EXBIND0RC(bool, has_named_classes) + EXBIND0RC(bool, supports_builtin_mode) + EXBIND0RC(bool, supports_documentation) + EXBIND0RC(bool, can_inherit_from_file) + + EXBIND2RC(int, find_function, const String &, const String &) + EXBIND3RC(String, make_function, const String &, const String &, const PackedStringArray &) + EXBIND3R(Error, open_in_external_editor, const Ref<Script> &, int, int) + EXBIND0R(bool, overrides_external_editor) + + GDVIRTUAL3RC(Dictionary, _complete_code, const String &, const String &, Object *) + + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<CodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) override { + Dictionary ret; + GDVIRTUAL_REQUIRED_CALL(_complete_code, p_code, p_path, p_owner, ret); + if (!ret.has("result")) { + return ERR_UNAVAILABLE; + } + + if (r_options != nullptr && ret.has("options")) { + Array options = ret["options"]; + for (int i = 0; i < options.size(); i++) { + Dictionary op = options[i]; + CodeCompletionOption option; + ERR_CONTINUE(!op.has("kind")); + option.kind = CodeCompletionKind(int(op["kind"])); + ERR_CONTINUE(!op.has("display")); + option.display = op["display"]; + ERR_CONTINUE(!op.has("insert_text")); + option.insert_text = op["insert_text"]; + ERR_CONTINUE(!op.has("font_color")); + option.font_color = op["font_color"]; + ERR_CONTINUE(!op.has("icon")); + option.icon = op["icon"]; + ERR_CONTINUE(!op.has("default_value")); + option.default_value = op["default_value"]; + ERR_CONTINUE(!op.has("location")); + option.location = op["location"]; + if (op.has("matches")) { + PackedInt32Array matches = op["matches"]; + ERR_CONTINUE(matches.size() & 1); + for (int j = 0; j < matches.size(); j += 2) { + option.matches.push_back(Pair<int, int>(matches[j], matches[j + 1])); + } + } + r_options->push_back(option); + } + } + + ERR_FAIL_COND_V(!ret.has("force"), ERR_UNAVAILABLE); + r_force = ret["force"]; + ERR_FAIL_COND_V(!ret.has("call_hint"), ERR_UNAVAILABLE); + r_call_hint = ret["call_hint"]; + ERR_FAIL_COND_V(!ret.has("result"), ERR_UNAVAILABLE); + Error result = Error(int(ret["result"])); + + return result; + } + + GDVIRTUAL4RC(Dictionary, _lookup_code, const String &, const String &, const String &, Object *) + + virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) override { + Dictionary ret; + GDVIRTUAL_REQUIRED_CALL(_lookup_code, p_code, p_symbol, p_path, p_owner, ret); + if (!ret.has("result")) { + return ERR_UNAVAILABLE; + } + + ERR_FAIL_COND_V(!ret.has("type"), ERR_UNAVAILABLE); + r_result.type = LookupResultType(int(ret["type"])); + ERR_FAIL_COND_V(!ret.has("script"), ERR_UNAVAILABLE); + r_result.script = ret["script"]; + ERR_FAIL_COND_V(!ret.has("class_name"), ERR_UNAVAILABLE); + r_result.class_name = ret["class_name"]; + ERR_FAIL_COND_V(!ret.has("class_path"), ERR_UNAVAILABLE); + r_result.class_path = ret["class_path"]; + ERR_FAIL_COND_V(!ret.has("location"), ERR_UNAVAILABLE); + r_result.location = ret["location"]; + + Error result = Error(int(ret["result"])); + + return result; + } + + GDVIRTUAL3RC(String, _auto_indent_code, const String &, int, int) + virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override { + String ret; + GDVIRTUAL_REQUIRED_CALL(_auto_indent_code, p_code, p_from_line, p_to_line, ret); + p_code = ret; + } + EXBIND2(add_global_constant, const StringName &, const Variant &) + EXBIND2(add_named_global_constant, const StringName &, const Variant &) + EXBIND1(remove_named_global_constant, const StringName &) + + /* MULTITHREAD FUNCTIONS */ + + //some VMs need to be notified of thread creation/exiting to allocate a stack + EXBIND0(thread_enter) + EXBIND0(thread_exit) + + EXBIND0RC(String, debug_get_error) + EXBIND0RC(int, debug_get_stack_level_count) + EXBIND1RC(int, debug_get_stack_level_line, int) + EXBIND1RC(String, debug_get_stack_level_function, int) + EXBIND1RC(String, debug_get_stack_level_source, int) + + GDVIRTUAL3R(Dictionary, _debug_get_stack_level_locals, int, int, int) + virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override { + Dictionary ret; + GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_locals, p_level, p_max_subitems, p_max_depth, ret); + if (ret.size() == 0) { + return; + } + if (p_locals != nullptr && ret.has("locals")) { + PackedStringArray strings = ret["locals"]; + for (int i = 0; i < strings.size(); i++) { + p_locals->push_back(strings[i]); + } + } + if (p_values != nullptr && ret.has("values")) { + Array values = ret["values"]; + for (int i = 0; i < values.size(); i++) { + p_values->push_back(values[i]); + } + } + } + GDVIRTUAL3R(Dictionary, _debug_get_stack_level_members, int, int, int) + virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override { + Dictionary ret; + GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_members, p_level, p_max_subitems, p_max_depth, ret); + if (ret.size() == 0) { + return; + } + if (p_members != nullptr && ret.has("members")) { + PackedStringArray strings = ret["members"]; + for (int i = 0; i < strings.size(); i++) { + p_members->push_back(strings[i]); + } + } + if (p_values != nullptr && ret.has("values")) { + Array values = ret["values"]; + for (int i = 0; i < values.size(); i++) { + p_values->push_back(values[i]); + } + } + } + GDVIRTUAL1R(GDNativePtr<void>, _debug_get_stack_level_instance, int) + + virtual ScriptInstance *debug_get_stack_level_instance(int p_level) override { + GDNativePtr<void> ret = nullptr; + GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_instance, p_level, ret); + return reinterpret_cast<ScriptInstance *>(ret.operator void *()); + } + GDVIRTUAL2R(Dictionary, _debug_get_globals, int, int) + virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override { + Dictionary ret; + GDVIRTUAL_REQUIRED_CALL(_debug_get_globals, p_max_subitems, p_max_depth, ret); + if (ret.size() == 0) { + return; + } + if (p_globals != nullptr && ret.has("globals")) { + PackedStringArray strings = ret["globals"]; + for (int i = 0; i < strings.size(); i++) { + p_globals->push_back(strings[i]); + } + } + if (p_values != nullptr && ret.has("values")) { + Array values = ret["values"]; + for (int i = 0; i < values.size(); i++) { + p_values->push_back(values[i]); + } + } + } + + EXBIND4R(String, debug_parse_stack_level_expression, int, const String &, int, int) + + GDVIRTUAL0R(TypedArray<Dictionary>, _debug_get_current_stack_info) + virtual Vector<StackInfo> debug_get_current_stack_info() override { + TypedArray<Dictionary> ret; + GDVIRTUAL_REQUIRED_CALL(_debug_get_current_stack_info, ret); + Vector<StackInfo> sret; + for (int i = 0; i < ret.size(); i++) { + StackInfo si; + Dictionary d = ret[i]; + ERR_CONTINUE(!d.has("file")); + ERR_CONTINUE(!d.has("func")); + ERR_CONTINUE(!d.has("line")); + si.file = d["file"]; + si.func = d["func"]; + si.line = d["line"]; + sret.push_back(si); + } + return sret; + } + + EXBIND0(reload_all_scripts) + EXBIND2(reload_tool_script, const Ref<Script> &, bool) + /* LOADER FUNCTIONS */ + + GDVIRTUAL0RC(PackedStringArray, _get_recognized_extensions) + + virtual void get_recognized_extensions(List<String> *p_extensions) const override { + PackedStringArray ret; + GDVIRTUAL_REQUIRED_CALL(_get_recognized_extensions, ret); + for (int i = 0; i < ret.size(); i++) { + p_extensions->push_back(ret[i]); + } + } + + GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_functions) + virtual void get_public_functions(List<MethodInfo> *p_functions) const override { + TypedArray<Dictionary> ret; + GDVIRTUAL_REQUIRED_CALL(_get_public_functions, ret); + for (int i = 0; i < ret.size(); i++) { + MethodInfo mi = MethodInfo::from_dict(ret[i]); + p_functions->push_back(mi); + } + } + GDVIRTUAL0RC(Dictionary, _get_public_constants) + virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override { + Dictionary ret; + GDVIRTUAL_REQUIRED_CALL(_get_public_constants, ret); + for (int i = 0; i < ret.size(); i++) { + Dictionary d = ret[i]; + ERR_CONTINUE(!d.has("name")); + ERR_CONTINUE(!d.has("value")); + p_constants->push_back(Pair<String, Variant>(d["name"], d["value"])); + } + } + GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_annotations) + virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override { + TypedArray<Dictionary> ret; + GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret); + for (int i = 0; i < ret.size(); i++) { + MethodInfo mi = MethodInfo::from_dict(ret[i]); + p_annotations->push_back(mi); + } + } + + EXBIND0(profiling_start) + EXBIND0(profiling_stop) + + GDVIRTUAL2R(int, _profiling_get_accumulated_data, GDNativePtr<ScriptLanguageExtensionProfilingInfo>, int) + + virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override { + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_profiling_get_accumulated_data, p_info_arr, p_info_max, ret); + return ret; + } + + GDVIRTUAL2R(int, _profiling_get_frame_data, GDNativePtr<ScriptLanguageExtensionProfilingInfo>, int) + + virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override { + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_profiling_get_accumulated_data, p_info_arr, p_info_max, ret); + return ret; + } + + GDVIRTUAL1R(GDNativePtr<void>, _alloc_instance_binding_data, Object *) + + virtual void *alloc_instance_binding_data(Object *p_object) override { + GDNativePtr<void> ret = nullptr; + GDVIRTUAL_REQUIRED_CALL(_alloc_instance_binding_data, p_object, ret); + return ret.operator void *(); + } + + GDVIRTUAL1(_free_instance_binding_data, GDNativePtr<void>) + + virtual void free_instance_binding_data(void *p_data) override { + GDVIRTUAL_REQUIRED_CALL(_free_instance_binding_data, p_data); + } + + EXBIND1(refcount_incremented_instance_binding, Object *) + EXBIND1R(bool, refcount_decremented_instance_binding, Object *) + + EXBIND0(frame) + + EXBIND1RC(bool, handles_global_class_type, const String &) + + GDVIRTUAL1RC(Dictionary, _get_global_class_name, const String &) + + virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override { + Dictionary ret; + GDVIRTUAL_REQUIRED_CALL(_get_global_class_name, p_path, ret); + if (!ret.has("name")) { + return String(); + } + if (r_base_type != nullptr && ret.has("base_type")) { + *r_base_type = ret["base_type"]; + } + if (r_icon_path != nullptr && ret.has("icon_path")) { + *r_icon_path = ret["icon_path"]; + } + return ret["name"]; + } +}; + +VARIANT_ENUM_CAST(ScriptLanguageExtension::LookupResultType) +VARIANT_ENUM_CAST(ScriptLanguageExtension::CodeCompletionKind) +VARIANT_ENUM_CAST(ScriptLanguageExtension::CodeCompletionLocation) + +class ScriptInstanceExtension : public ScriptInstance { +public: + const GDNativeExtensionScriptInstanceInfo *native_info; + GDNativeExtensionScriptInstanceDataPtr instance = nullptr; + +// There should not be warnings on explicit casts. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#endif + + virtual bool set(const StringName &p_name, const Variant &p_value) override { + if (native_info->set_func) { + return native_info->set_func(instance, (const GDNativeStringNamePtr)&p_name, (const GDNativeVariantPtr)&p_value); + } + return false; + } + virtual bool get(const StringName &p_name, Variant &r_ret) const override { + if (native_info->get_func) { + return native_info->get_func(instance, (const GDNativeStringNamePtr)&p_name, (GDNativeVariantPtr)&r_ret); + } + return false; + } + virtual void get_property_list(List<PropertyInfo> *p_list) const override { + if (native_info->get_property_list_func) { + uint32_t pcount; + const GDNativePropertyInfo *pinfo = native_info->get_property_list_func(instance, &pcount); + for (uint32_t i = 0; i < pcount; i++) { + p_list->push_back(PropertyInfo(pinfo[i])); + } + if (native_info->free_property_list_func) { + native_info->free_property_list_func(instance, pinfo); + } + } + } + virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const override { + if (native_info->get_property_type_func) { + GDNativeBool is_valid = 0; + GDNativeVariantType type = native_info->get_property_type_func(instance, (const GDNativeStringNamePtr)&p_name, &is_valid); + if (r_is_valid) { + *r_is_valid = is_valid != 0; + } + + return Variant::Type(type); + } + return Variant::NIL; + } + + virtual Object *get_owner() override { + if (native_info->get_owner_func) { + return (Object *)native_info->get_owner_func(instance); + } + return nullptr; + } + static void _add_property_with_state(const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value, void *p_userdata) { + List<Pair<StringName, Variant>> *state = (List<Pair<StringName, Variant>> *)p_userdata; + state->push_back(Pair<StringName, Variant>(*(const StringName *)p_name, *(const Variant *)p_value)); + } + virtual void get_property_state(List<Pair<StringName, Variant>> &state) override { + if (native_info->get_property_state_func) { + native_info->get_property_state_func(instance, _add_property_with_state, &state); + } + } + + virtual void get_method_list(List<MethodInfo> *p_list) const override { + if (native_info->get_method_list_func) { + uint32_t mcount; + const GDNativeMethodInfo *minfo = native_info->get_method_list_func(instance, &mcount); + for (uint32_t i = 0; i < mcount; i++) { + MethodInfo m; + m.name = minfo[i].name; + m.flags = minfo[i].flags; + m.id = minfo[i].id; + m.return_val = PropertyInfo(minfo[i].return_value); + for (uint32_t j = 0; j < minfo[i].argument_count; j++) { + m.arguments.push_back(PropertyInfo(minfo[i].arguments[j])); + } + const Variant *def_values = (const Variant *)minfo[i].default_arguments; + for (uint32_t j = 0; j < minfo[i].default_argument_count; j++) { + m.default_arguments.push_back(def_values[j]); + } + p_list->push_back(m); + } + if (native_info->free_method_list_func) { + native_info->free_method_list_func(instance, minfo); + } + } + } + virtual bool has_method(const StringName &p_method) const override { + if (native_info->has_method_func) { + return native_info->has_method_func(instance, (GDNativeStringNamePtr)&p_method); + } + return false; + } + + virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override { + Variant ret; + if (native_info->call_func) { + GDNativeCallError ce; + native_info->call_func(instance, (const GDNativeStringNamePtr)&p_method, (const GDNativeVariantPtr *)p_args, p_argcount, (GDNativeVariantPtr)&ret, &ce); + r_error.error = Callable::CallError::Error(ce.error); + r_error.argument = ce.argument; + r_error.expected = ce.expected; + } + return ret; + } + + virtual void notification(int p_notification) override { + if (native_info->notification_func) { + native_info->notification_func(instance, p_notification); + } + } + virtual String to_string(bool *r_valid) override { + if (native_info->to_string_func) { + GDNativeBool valid; + String ret = native_info->to_string_func(instance, &valid); + if (r_valid) { + *r_valid = valid != 0; + } + return ret; + } + return String(); + } + + virtual void refcount_incremented() override { + if (native_info->refcount_incremented_func) { + native_info->refcount_incremented_func(instance); + } + } + virtual bool refcount_decremented() override { + if (native_info->refcount_decremented_func) { + return native_info->refcount_decremented_func(instance); + } + return false; + } + + virtual Ref<Script> get_script() const override { + if (native_info->get_script_func) { + GDNativeObjectPtr script = native_info->get_script_func(instance); + return Ref<Script>(reinterpret_cast<Script *>(script)); + } + return Ref<Script>(); + } + + virtual bool is_placeholder() const override { + if (native_info->is_placeholder_func) { + return native_info->is_placeholder_func(instance); + } + return false; + } + + virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid) override { + if (native_info->set_fallback_func) { + bool ret = native_info->set_fallback_func(instance, (const GDNativeStringNamePtr)&p_name, (const GDNativeVariantPtr)&p_value); + if (r_valid) { + *r_valid = ret; + } + } + } + virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid) override { + Variant ret; + if (native_info->get_fallback_func) { + bool valid = native_info->get_fallback_func(instance, (const GDNativeStringNamePtr)&p_name, (GDNativeVariantPtr)&ret); + if (r_valid) { + *r_valid = valid; + } + } + return ret; + } + + virtual ScriptLanguage *get_language() override { + if (native_info->get_language_func) { + GDNativeExtensionScriptLanguagePtr lang = native_info->get_language_func(instance); + return reinterpret_cast<ScriptLanguage *>(lang); + } + return nullptr; + ; + } + virtual ~ScriptInstanceExtension() { + if (native_info->free_func) { + native_info->free_func(instance); + } + } + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif +}; + +#endif // SCRIPT_LANGUAGE_EXTENSION_H diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index e8735e335c..d3c48853f1 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,6 +32,21 @@ #include "core/io/resource.h" #include "core/os/os.h" +#include "core/templates/local_vector.h" + +void UndoRedo::Operation::delete_reference() { + if (type != Operation::TYPE_REFERENCE) { + return; + } + if (ref.is_valid()) { + ref.unref(); + } else { + Object *obj = ObjectDB::get_instance(object); + if (obj) { + memdelete(obj); + } + } +} void UndoRedo::_discard_redo() { if (current_action == actions.size() - 1) { @@ -39,13 +54,8 @@ 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) { + E.delete_reference(); } //ERASE do data } @@ -65,13 +75,13 @@ bool UndoRedo::_redo(bool p_execute) { _process_operation_list(actions.write[current_action].do_ops.front()); } version++; - emit_signal("version_changed"); + 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(); + uint64_t ticks = OS::get_singleton()->get_ticks_msec(); if (action_level == 0) { _discard_redo(); @@ -81,20 +91,19 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { current_action = actions.size() - 2; if (p_mode == MERGE_ENDS) { - // Clear all do ops from last action, and delete all object references - List<Operation>::Element *E = actions.write[current_action + 1].do_ops.front(); - - while (E) { - if (E->get().type == Operation::TYPE_REFERENCE) { - Object *obj = ObjectDB::get_instance(E->get().object); - - if (obj) { - memdelete(obj); - } + // Clear all do ops from last action if they are not forced kept + LocalVector<List<Operation>::Element *> to_remove; + for (List<Operation>::Element *E = actions.write[current_action + 1].do_ops.front(); E; E = E->next()) { + if (!E->get().force_keep_in_merge_ends) { + to_remove.push_back(E); } + } - E = E->next(); - actions.write[current_action + 1].do_ops.pop_front(); + for (unsigned int i = 0; i < to_remove.size(); i++) { + List<Operation>::Element *E = to_remove[i]; + // Delete all object references + E->get().delete_reference(); + E->erase(); } } @@ -113,50 +122,51 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { } action_level++; + + force_keep_in_merge_ends = false; } -void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS +void UndoRedo::add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { ERR_FAIL_COND(p_object == nullptr); ERR_FAIL_COND(action_level <= 0); 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; do_op.name = p_method; - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - do_op.args[i] = *argptr[i]; + for (int i = 0; i < p_argcount; i++) { + do_op.args.push_back(*p_args[i]); } actions.write[current_action + 1].do_ops.push_back(do_op); } -void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) { - VARIANT_ARGPTRS +void UndoRedo::add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { ERR_FAIL_COND(p_object == nullptr); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); // No undo if the merge mode is MERGE_ENDS - if (merge_mode == MERGE_ENDS) { + if (!force_keep_in_merge_ends && merge_mode == MERGE_ENDS) { return; } 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; + undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; undo_op.name = p_method; - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - undo_op.args[i] = *argptr[i]; + for (int i = 0; i < p_argcount; i++) { + undo_op.args.push_back(*p_args[i]); } actions.write[current_action + 1].undo_ops.push_back(undo_op); } @@ -167,13 +177,13 @@ 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; do_op.name = p_property; - do_op.args[0] = p_value; + do_op.args.push_back(p_value); actions.write[current_action + 1].do_ops.push_back(do_op); } @@ -183,19 +193,20 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, ERR_FAIL_COND((current_action + 1) >= actions.size()); // No undo if the merge mode is MERGE_ENDS - if (merge_mode == MERGE_ENDS) { + if (!force_keep_in_merge_ends && merge_mode == MERGE_ENDS) { return; } 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; + undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; undo_op.name = p_property; - undo_op.args[0] = p_value; + undo_op.args.push_back(p_value); actions.write[current_action + 1].undo_ops.push_back(undo_op); } @@ -205,8 +216,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; @@ -219,20 +230,35 @@ void UndoRedo::add_undo_reference(Object *p_object) { ERR_FAIL_COND((current_action + 1) >= actions.size()); // No undo if the merge mode is MERGE_ENDS - if (merge_mode == MERGE_ENDS) { + if (!force_keep_in_merge_ends && merge_mode == MERGE_ENDS) { return; } 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; + undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; actions.write[current_action + 1].undo_ops.push_back(undo_op); } +void UndoRedo::start_force_keep_in_merge_ends() { + ERR_FAIL_COND(action_level <= 0); + ERR_FAIL_COND((current_action + 1) >= actions.size()); + + force_keep_in_merge_ends = true; +} + +void UndoRedo::end_force_keep_in_merge_ends() { + ERR_FAIL_COND(action_level <= 0); + ERR_FAIL_COND((current_action + 1) >= actions.size()); + + force_keep_in_merge_ends = false; +} + void UndoRedo::_pop_history_tail() { _discard_redo(); @@ -240,16 +266,11 @@ 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) { + E.delete_reference(); } - actions.remove(0); + actions.remove_at(0); if (current_action >= 0) { current_action--; } @@ -291,23 +312,18 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { switch (op.type) { case Operation::TYPE_METHOD: { + int argc = op.args.size(); Vector<const Variant *> argptrs; - argptrs.resize(VARIANT_ARG_MAX); - int argc = 0; + argptrs.resize(argc); - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (op.args[i].get_type() == Variant::NIL) { - break; - } + for (int i = 0; i < argc; i++) { argptrs.write[i] = &op.args[i]; - argc++; } - argptrs.resize(argc); Callable::CallError ce; - obj->call(op.name, (const Variant **)argptrs.ptr(), argc, ce); + obj->callp(op.name, (const Variant **)argptrs.ptr(), argc, ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT("Error calling method from signal '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce)); + ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce)); } #ifdef TOOLS_ENABLED Resource *res = Object::cast_to<Resource>(obj); @@ -318,7 +334,7 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { #endif if (method_callback) { - method_callback(method_callbck_ud, obj, op.name, VARIANT_ARGS_FROM_ARRAY(op.args)); + method_callback(method_callback_ud, obj, op.name, (const Variant **)argptrs.ptr(), argc); } } break; case Operation::TYPE_PROPERTY: { @@ -352,7 +368,7 @@ 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; } @@ -385,7 +401,7 @@ void UndoRedo::clear_history(bool p_increase_version) { if (p_increase_version) { version++; - emit_signal("version_changed"); + emit_signal(SNAME("version_changed")); } } @@ -397,11 +413,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(); } @@ -416,7 +432,7 @@ void UndoRedo::set_commit_notify_callback(CommitNotifyCallback p_callback, void void UndoRedo::set_method_notify_callback(MethodNotifyCallback p_method_callback, void *p_ud) { method_callback = p_method_callback; - method_callbck_ud = p_ud; + method_callback_ud = p_ud; } void UndoRedo::set_property_notify_callback(PropertyNotifyCallback p_property_callback, void *p_ud) { @@ -428,25 +444,25 @@ UndoRedo::~UndoRedo() { clear_history(); } -Variant UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +void UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 2) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 0; - return Variant(); + return; } if (p_args[0]->get_type() != Variant::OBJECT) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; r_error.expected = Variant::OBJECT; - return Variant(); + return; } if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 1; r_error.expected = Variant::STRING_NAME; - return Variant(); + return; } r_error.error = Callable::CallError::CALL_OK; @@ -454,36 +470,28 @@ Variant UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callabl Object *object = *p_args[0]; StringName method = *p_args[1]; - Variant v[VARIANT_ARG_MAX]; - - for (int i = 0; i < MIN(VARIANT_ARG_MAX, p_argcount - 2); ++i) { - 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]); - return Variant(); + add_do_methodp(object, method, p_args + 2, p_argcount - 2); } -Variant UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +void UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 2) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 0; - return Variant(); + return; } if (p_args[0]->get_type() != Variant::OBJECT) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; r_error.expected = Variant::OBJECT; - return Variant(); + return; } if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 1; r_error.expected = Variant::STRING_NAME; - return Variant(); + return; } r_error.error = Callable::CallError::CALL_OK; @@ -491,15 +499,7 @@ Variant UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Calla Object *object = *p_args[0]; StringName method = *p_args[1]; - Variant v[VARIANT_ARG_MAX]; - - for (int i = 0; i < MIN(VARIANT_ARG_MAX, p_argcount - 2); ++i) { - 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]); - return Variant(); + add_undo_methodp(object, method, p_args + 2, p_argcount - 2); } void UndoRedo::_bind_methods() { @@ -530,6 +530,9 @@ void UndoRedo::_bind_methods() { 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("start_force_keep_in_merge_ends"), &UndoRedo::start_force_keep_in_merge_ends); + ClassDB::bind_method(D_METHOD("end_force_keep_in_merge_ends"), &UndoRedo::end_force_keep_in_merge_ends); + 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); diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h index a08ca7792f..63cf3e5cbe 100644 --- a/core/object/undo_redo.h +++ b/core/object/undo_redo.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -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); @@ -46,10 +46,10 @@ public: }; typedef void (*CommitNotifyCallback)(void *p_ud, const String &p_name); - Variant _add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - Variant _add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + void _add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + void _add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - typedef void (*MethodNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE); + typedef void (*MethodNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount); typedef void (*PropertyNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value); private: @@ -61,10 +61,13 @@ private: }; Type type; - Ref<Reference> ref; + bool force_keep_in_merge_ends; + Ref<RefCounted> ref; ObjectID object; StringName name; - Variant args[VARIANT_ARG_MAX]; + Vector<Variant> args; + + void delete_reference(); }; struct Action { @@ -76,6 +79,7 @@ private: Vector<Action> actions; int current_action = -1; + bool force_keep_in_merge_ends = false; int action_level = 0; MergeMode merge_mode = MERGE_DISABLE; bool merging = false; @@ -88,7 +92,7 @@ private: CommitNotifyCallback callback = nullptr; void *callback_ud = nullptr; - void *method_callbck_ud = nullptr; + void *method_callback_ud = nullptr; void *prop_callback_ud = nullptr; MethodNotifyCallback method_callback = nullptr; @@ -102,13 +106,38 @@ protected: public: void create_action(const String &p_name = "", MergeMode p_mode = MERGE_DISABLE); - void add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST); - void add_undo_method(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST); + void add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount); + void add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount); + + template <typename... VarArgs> + void add_do_method(Object *p_object, const StringName &p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + + add_do_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); + } + template <typename... VarArgs> + void add_undo_method(Object *p_object, const StringName &p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + + add_undo_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); + } + void add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value); void add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value); void add_do_reference(Object *p_object); void add_undo_reference(Object *p_object); + void start_force_keep_in_merge_ends(); + void end_force_keep_in_merge_ends(); + bool is_committing_action() const; void commit_action(bool p_execute = true); @@ -121,8 +150,8 @@ public: 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; diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp new file mode 100644 index 0000000000..54738a673e --- /dev/null +++ b/core/object/worker_thread_pool.cpp @@ -0,0 +1,474 @@ +/*************************************************************************/ +/* worker_thread_pool.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "worker_thread_pool.h" + +#include "core/os/os.h" + +void WorkerThreadPool::Task::free_template_userdata() { + ERR_FAIL_COND(!template_userdata); + ERR_FAIL_COND(native_func_userdata == nullptr); + BaseTemplateUserdata *btu = (BaseTemplateUserdata *)native_func_userdata; + memdelete(btu); +} + +WorkerThreadPool *WorkerThreadPool::singleton = nullptr; + +void WorkerThreadPool::_process_task_queue() { + task_mutex.lock(); + Task *task = task_queue.first()->self(); + task_queue.remove(task_queue.first()); + task_mutex.unlock(); + _process_task(task); +} + +void WorkerThreadPool::_process_task(Task *p_task) { + bool low_priority = p_task->low_priority; + + if (p_task->group) { + // Handling a group + bool do_post = false; + Callable::CallError ce; + Variant ret; + Variant arg; + Variant *argptr = &arg; + + while (true) { + uint32_t work_index = p_task->group->index.postincrement(); + + if (work_index >= p_task->group->max) { + break; + } + if (p_task->native_group_func) { + p_task->native_group_func(p_task->native_func_userdata, work_index); + } else if (p_task->template_userdata) { + p_task->template_userdata->callback_indexed(work_index); + } else { + arg = work_index; + p_task->callable.call((const Variant **)&argptr, 1, ret, ce); + } + + // This is the only way to ensure posting is done when all tasks are really complete. + uint32_t completed_amount = p_task->group->completed_index.increment(); + + if (completed_amount == p_task->group->max) { + do_post = true; + } + } + + if (do_post && p_task->template_userdata) { + memdelete(p_task->template_userdata); // This is no longer needed at this point, so get rid of it. + } + + if (low_priority && use_native_low_priority_threads) { + p_task->completed = true; + p_task->done_semaphore.post(); + if (do_post) { + p_task->group->completed.set_to(true); + } + } else { + if (do_post) { + p_task->group->done_semaphore.post(); + p_task->group->completed.set_to(true); + } + uint32_t max_users = p_task->group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment. + uint32_t finished_users = p_task->group->finished.increment(); + + if (finished_users == max_users) { + // Get rid of the group, because nobody else is using it. + task_mutex.lock(); + group_allocator.free(p_task->group); + task_mutex.unlock(); + } + + // For groups, tasks get rid of themselves. + + task_mutex.lock(); + task_allocator.free(p_task); + task_mutex.unlock(); + } + } else { + if (p_task->native_func) { + p_task->native_func(p_task->native_func_userdata); + } else if (p_task->template_userdata) { + p_task->template_userdata->callback(); + memdelete(p_task->template_userdata); + } else { + Callable::CallError ce; + Variant ret; + p_task->callable.call(nullptr, 0, ret, ce); + } + + p_task->completed = true; + p_task->done_semaphore.post(); + } + + if (!use_native_low_priority_threads && low_priority) { + // A low prioriry task was freed, so see if we can move a pending one to the high priority queue. + bool post = false; + task_mutex.lock(); + if (low_priority_task_queue.first()) { + Task *low_prio_task = low_priority_task_queue.first()->self(); + low_priority_task_queue.remove(low_priority_task_queue.first()); + task_queue.add_last(&low_prio_task->task_elem); + post = true; + } else { + low_priority_threads_used.decrement(); + } + task_mutex.lock(); + if (post) { + task_available_semaphore.post(); + } + } +} + +void WorkerThreadPool::_thread_function(void *p_user) { + while (true) { + singleton->task_available_semaphore.wait(); + if (singleton->exit_threads.is_set()) { + break; + } + singleton->_process_task_queue(); + } +} + +void WorkerThreadPool::_native_low_priority_thread_function(void *p_user) { + Task *task = (Task *)p_user; + singleton->_process_task(task); +} + +void WorkerThreadPool::_post_task(Task *p_task, bool p_high_priority) { + task_mutex.lock(); + p_task->low_priority = !p_high_priority; + if (!p_high_priority && use_native_low_priority_threads) { + task_mutex.unlock(); + p_task->low_priority_thread = native_thread_allocator.alloc(); + p_task->low_priority_thread->start(_native_low_priority_thread_function, p_task); // Pask task directly to thread. + + } else if (p_high_priority || low_priority_threads_used.get() < max_low_priority_threads) { + task_queue.add_last(&p_task->task_elem); + if (!p_high_priority) { + low_priority_threads_used.increment(); + } + task_mutex.unlock(); + task_available_semaphore.post(); + } else { + // Too many threads using low priority, must go to queue. + low_priority_task_queue.add_last(&p_task->task_elem); + task_mutex.unlock(); + } +} + +WorkerThreadPool::TaskID WorkerThreadPool::add_native_task(void (*p_func)(void *), void *p_userdata, bool p_high_priority, const String &p_description) { + return _add_task(Callable(), p_func, p_userdata, nullptr, p_high_priority, p_description); +} + +WorkerThreadPool::TaskID WorkerThreadPool::_add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description) { + task_mutex.lock(); + // Get a free task + Task *task = task_allocator.alloc(); + TaskID id = last_task++; + task->callable = p_callable; + task->native_func = p_func; + task->native_func_userdata = p_userdata; + task->description = p_description; + task->template_userdata = p_template_userdata; + tasks.insert(id, task); + task_mutex.unlock(); + + _post_task(task, p_high_priority); + + return id; +} + +WorkerThreadPool::TaskID WorkerThreadPool::add_task(const Callable &p_action, bool p_high_priority, const String &p_description) { + return _add_task(p_action, nullptr, nullptr, nullptr, p_high_priority, p_description); +} + +bool WorkerThreadPool::is_task_completed(TaskID p_task_id) const { + task_mutex.lock(); + const Task *const *taskp = tasks.getptr(p_task_id); + if (!taskp) { + task_mutex.unlock(); + ERR_FAIL_V_MSG(false, "Invalid Task ID"); // Invalid task + } + + bool completed = (*taskp)->completed; + task_mutex.unlock(); + + return completed; +} + +void WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) { + task_mutex.lock(); + Task **taskp = tasks.getptr(p_task_id); + if (!taskp) { + task_mutex.unlock(); + ERR_FAIL_MSG("Invalid Task ID"); // Invalid task + } + Task *task = *taskp; + + if (task->waiting) { + String description = task->description; + task_mutex.unlock(); + if (description.is_empty()) { + ERR_FAIL_MSG("Another thread is waiting on this task: " + itos(p_task_id)); // Invalid task + } else { + ERR_FAIL_MSG("Another thread is waiting on this task: " + description + " (" + itos(p_task_id) + ")"); // Invalid task + } + } + + task->waiting = true; + + task_mutex.unlock(); + + if (use_native_low_priority_threads && task->low_priority) { + task->low_priority_thread->wait_to_finish(); + native_thread_allocator.free(task->low_priority_thread); + } else { + int *index = thread_ids.getptr(Thread::get_caller_id()); + + if (index) { + // We are an actual process thread, we must not be blocked so continue processing stuff if available. + while (true) { + if (task->done_semaphore.try_wait()) { + // If done, exit + break; + } + if (task_available_semaphore.try_wait()) { + // Solve tasks while they are around. + _process_task_queue(); + continue; + } + OS::get_singleton()->delay_usec(1); // Microsleep, this could be converted to waiting for multiple objects in supported platforms for a bit more performance. + } + } else { + task->done_semaphore.wait(); + } + } + + task_mutex.lock(); + tasks.erase(p_task_id); + task_allocator.free(task); + task_mutex.unlock(); +} + +WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) { + ERR_FAIL_COND_V(p_elements < 0, INVALID_TASK_ID); + if (p_tasks < 0) { + p_tasks = threads.size(); + } + + task_mutex.lock(); + Group *group = group_allocator.alloc(); + GroupID id = last_task++; + group->max = p_elements; + group->self = id; + + Task **tasks_posted = nullptr; + if (p_elements == 0) { + // Should really not call it with zero Elements, but at least it should work. + group->completed.set_to(true); + group->done_semaphore.post(); + group->tasks_used = 0; + p_tasks = 0; + if (p_template_userdata) { + memdelete(p_template_userdata); + } + + } else { + group->tasks_used = p_tasks; + tasks_posted = (Task **)alloca(sizeof(Task *) * p_tasks); + for (int i = 0; i < p_tasks; i++) { + Task *task = task_allocator.alloc(); + task->native_group_func = p_func; + task->native_func_userdata = p_userdata; + task->description = p_description; + task->group = group; + task->callable = p_callable; + task->template_userdata = p_template_userdata; + tasks_posted[i] = task; + // No task ID is used. + } + } + + groups[id] = group; + task_mutex.unlock(); + + if (!p_high_priority && use_native_low_priority_threads) { + group->low_priority_native_tasks.resize(p_tasks); + } + + for (int i = 0; i < p_tasks; i++) { + _post_task(tasks_posted[i], p_high_priority); + if (!p_high_priority && use_native_low_priority_threads) { + group->low_priority_native_tasks[i] = tasks_posted[i]; + } + } + + return id; +} + +WorkerThreadPool::GroupID WorkerThreadPool::add_native_group_task(void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) { + return _add_group_task(Callable(), p_func, p_userdata, nullptr, p_elements, p_tasks, p_high_priority, p_description); +} + +WorkerThreadPool::GroupID WorkerThreadPool::add_group_task(const Callable &p_action, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) { + return _add_group_task(p_action, nullptr, nullptr, nullptr, p_elements, p_tasks, p_high_priority, p_description); +} + +uint32_t WorkerThreadPool::get_group_processed_element_count(GroupID p_group) const { + task_mutex.lock(); + const Group *const *groupp = groups.getptr(p_group); + if (!groupp) { + task_mutex.unlock(); + ERR_FAIL_V_MSG(0, "Invalid Group ID"); + } + uint32_t elements = (*groupp)->completed_index.get(); + task_mutex.unlock(); + return elements; +} +bool WorkerThreadPool::is_group_task_completed(GroupID p_group) const { + task_mutex.lock(); + const Group *const *groupp = groups.getptr(p_group); + if (!groupp) { + task_mutex.unlock(); + ERR_FAIL_V_MSG(false, "Invalid Group ID"); + } + bool completed = (*groupp)->completed.is_set(); + task_mutex.unlock(); + return completed; +} + +void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) { + task_mutex.lock(); + Group **groupp = groups.getptr(p_group); + task_mutex.unlock(); + if (!groupp) { + ERR_FAIL_MSG("Invalid Group ID"); + } + Group *group = *groupp; + + if (group->low_priority_native_tasks.size() > 0) { + for (uint32_t i = 0; i < group->low_priority_native_tasks.size(); i++) { + group->low_priority_native_tasks[i]->low_priority_thread->wait_to_finish(); + native_thread_allocator.free(group->low_priority_native_tasks[i]->low_priority_thread); + task_mutex.lock(); + task_allocator.free(group->low_priority_native_tasks[i]); + task_mutex.unlock(); + } + + task_mutex.lock(); + group_allocator.free(group); + task_mutex.unlock(); + } else { + group->done_semaphore.wait(); + + uint32_t max_users = group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment. + uint32_t finished_users = group->finished.increment(); // fetch happens before inc, so increment later. + + if (finished_users == max_users) { + // All tasks using this group are gone (finished before the group), so clear the gorup too. + task_mutex.lock(); + group_allocator.free(group); + task_mutex.unlock(); + } + } + + groups.erase(p_group); // Threads do not access this, so safe to erase here. +} + +void WorkerThreadPool::init(int p_thread_count, bool p_use_native_threads_low_priority, float p_low_priority_task_ratio) { + ERR_FAIL_COND(threads.size() > 0); + if (p_thread_count < 0) { + p_thread_count = OS::get_singleton()->get_default_thread_pool_size(); + } + + if (p_use_native_threads_low_priority) { + max_low_priority_threads = 0; + } else { + max_low_priority_threads = CLAMP(p_thread_count * p_low_priority_task_ratio, 1, p_thread_count); + } + + use_native_low_priority_threads = p_use_native_threads_low_priority; + + threads.resize(p_thread_count); + + for (uint32_t i = 0; i < threads.size(); i++) { + threads[i].index = i; + threads[i].thread.start(&WorkerThreadPool::_thread_function, &threads[i]); + thread_ids.insert(threads[i].thread.get_id(), i); + } +} + +void WorkerThreadPool::finish() { + if (threads.size() == 0) { + return; + } + + task_mutex.lock(); + SelfList<Task> *E = low_priority_task_queue.first(); + while (E) { + print_error("Task waiting was never re-claimed: " + E->self()->description); + E = E->next(); + } + task_mutex.unlock(); + + exit_threads.set_to(true); + + for (uint32_t i = 0; i < threads.size(); i++) { + task_available_semaphore.post(); + } + + for (uint32_t i = 0; i < threads.size(); i++) { + threads[i].thread.wait_to_finish(); + } + + threads.clear(); +} + +void WorkerThreadPool::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_task", "action", "high_priority", "description"), &WorkerThreadPool::add_task, DEFVAL(false), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("is_task_completed", "task_id"), &WorkerThreadPool::is_task_completed); + ClassDB::bind_method(D_METHOD("wait_for_task_completion", "task_id"), &WorkerThreadPool::wait_for_task_completion); + + ClassDB::bind_method(D_METHOD("add_group_task", "action", "elements", "tasks_needed", "high_priority", "description"), &WorkerThreadPool::add_group_task, DEFVAL(-1), DEFVAL(false), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("is_group_task_completed", "group_id"), &WorkerThreadPool::is_group_task_completed); + ClassDB::bind_method(D_METHOD("get_group_processed_element_count", "group_id"), &WorkerThreadPool::get_group_processed_element_count); + ClassDB::bind_method(D_METHOD("wait_for_group_task_completion", "group_id"), &WorkerThreadPool::wait_for_group_task_completion); +} + +WorkerThreadPool::WorkerThreadPool() { + singleton = this; +} + +WorkerThreadPool::~WorkerThreadPool() { + finish(); +} diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h new file mode 100644 index 0000000000..1debd9ca37 --- /dev/null +++ b/core/object/worker_thread_pool.h @@ -0,0 +1,198 @@ +/*************************************************************************/ +/* worker_thread_pool.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WORKER_THREAD_POOL_H +#define WORKER_THREAD_POOL_H + +#include "core/os/memory.h" +#include "core/os/os.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" +#include "core/templates/local_vector.h" +#include "core/templates/paged_allocator.h" +#include "core/templates/rid.h" +#include "core/templates/safe_refcount.h" + +class WorkerThreadPool : public Object { + GDCLASS(WorkerThreadPool, Object) +public: + enum { + INVALID_TASK_ID = -1 + }; + + typedef int64_t TaskID; + typedef int64_t GroupID; + +private: + struct Task; + + struct BaseTemplateUserdata { + virtual void callback() {} + virtual void callback_indexed(uint32_t p_index) {} + virtual ~BaseTemplateUserdata() {} + }; + + struct Group { + GroupID self; + SafeNumeric<uint32_t> index; + SafeNumeric<uint32_t> completed_index; + uint32_t max = 0; + Semaphore done_semaphore; + SafeFlag completed; + SafeNumeric<uint32_t> finished; + uint32_t tasks_used = 0; + TightLocalVector<Task *> low_priority_native_tasks; + }; + + struct Task { + Callable callable; + void (*native_func)(void *) = nullptr; + void (*native_group_func)(void *, uint32_t) = nullptr; + void *native_func_userdata = nullptr; + String description; + Semaphore done_semaphore; + bool completed = false; + Group *group = nullptr; + SelfList<Task> task_elem; + bool waiting = false; // Waiting for completion + bool low_priority = false; + BaseTemplateUserdata *template_userdata = nullptr; + Thread *low_priority_thread = nullptr; + + void free_template_userdata(); + Task() : + task_elem(this) {} + }; + + PagedAllocator<Task> task_allocator; + PagedAllocator<Group> group_allocator; + PagedAllocator<Thread> native_thread_allocator; + + SelfList<Task>::List low_priority_task_queue; + SelfList<Task>::List task_queue; + + Mutex task_mutex; + Semaphore task_available_semaphore; + + struct ThreadData { + uint32_t index; + Thread thread; + }; + + TightLocalVector<ThreadData> threads; + SafeFlag exit_threads; + + HashMap<Thread::ID, int> thread_ids; + HashMap<TaskID, Task *> tasks; + HashMap<GroupID, Group *> groups; + + bool use_native_low_priority_threads = false; + uint32_t max_low_priority_threads = 0; + SafeNumeric<uint32_t> low_priority_threads_used; + + uint64_t last_task = 1; + + static void _thread_function(void *p_user); + static void _native_low_priority_thread_function(void *p_user); + + void _process_task_queue(); + void _process_task(Task *task); + + void _post_task(Task *p_task, bool p_high_priority); + + static WorkerThreadPool *singleton; + + TaskID _add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description); + GroupID _add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description); + + template <class C, class M, class U> + struct TaskUserData : public BaseTemplateUserdata { + C *instance; + M method; + U userdata; + virtual void callback() override { + (instance->*method)(userdata); + } + }; + + template <class C, class M, class U> + struct GroupUserData : public BaseTemplateUserdata { + C *instance; + M method; + U userdata; + virtual void callback_indexed(uint32_t p_index) override { + (instance->*method)(p_index, userdata); + } + }; + +protected: + static void _bind_methods(); + +public: + template <class C, class M, class U> + TaskID add_template_task(C *p_instance, M p_method, U p_userdata, bool p_high_priority = false, const String &p_description = String()) { + typedef TaskUserData<C, M, U> TUD; + TUD *ud = memnew(TUD); + ud->instance = p_instance; + ud->method = p_method; + ud->userdata = p_userdata; + return _add_task(Callable(), nullptr, nullptr, ud, p_high_priority, p_description); + } + TaskID add_native_task(void (*p_func)(void *), void *p_userdata, bool p_high_priority = false, const String &p_description = String()); + TaskID add_task(const Callable &p_action, bool p_high_priority = false, const String &p_description = String()); + + bool is_task_completed(TaskID p_task_id) const; + void wait_for_task_completion(TaskID p_task_id); + + template <class C, class M, class U> + GroupID add_template_group_task(C *p_instance, M p_method, U p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()) { + typedef GroupUserData<C, M, U> GUD; + GUD *ud = memnew(GUD); + ud->instance = p_instance; + ud->method = p_method; + ud->userdata = p_userdata; + return _add_group_task(Callable(), nullptr, nullptr, ud, p_elements, p_tasks, p_high_priority, p_description); + } + GroupID add_native_group_task(void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()); + GroupID add_group_task(const Callable &p_action, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()); + uint32_t get_group_processed_element_count(GroupID p_group) const; + bool is_group_task_completed(GroupID p_group) const; + void wait_for_group_task_completion(GroupID p_group); + + _FORCE_INLINE_ int get_thread_count() const { return threads.size(); } + + static WorkerThreadPool *get_singleton() { return singleton; } + void init(int p_thread_count = -1, bool p_use_native_threads_low_priority = true, float p_low_priority_task_ratio = 0.3); + void finish(); + WorkerThreadPool(); + ~WorkerThreadPool(); +}; + +#endif // WORKER_THREAD_POOL_H |