diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 9f68bc0c286c..11421615cdb4 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -1020,7 +1020,19 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { d2["is_required"] = (F.flags & METHOD_FLAG_VIRTUAL_REQUIRED) ? true : false; d2["is_vararg"] = false; d2["is_virtual"] = true; - // virtual functions have no hash since no MethodBind is involved + d2["hash"] = mi.get_compatibility_hash(); + + Vector compat_hashes = ClassDB::get_virtual_method_compatibility_hashes(class_name, method_name); + Array compatibility; + if (compat_hashes.size()) { + for (int i = 0; i < compat_hashes.size(); i++) { + compatibility.push_back(compat_hashes[i]); + } + } + if (compatibility.size() > 0) { + d2["hash_compatibility"] = compatibility; + } + bool has_return = mi.return_val.type != Variant::NIL || (mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT); if (has_return) { PropertyInfo pinfo = mi.return_val; @@ -1473,8 +1485,8 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_ if (p_compare_hashes) { if (!old_elem.has("hash")) { - if (old_elem.has("is_virtual") && bool(old_elem["is_virtual"]) && !new_elem.has("hash")) { - continue; // No hash for virtual methods, go on. + if (old_elem.has("is_virtual") && bool(old_elem["is_virtual"]) && !old_elem.has("hash")) { + continue; // Virtual methods didn't used to have hashes, so skip check if it's missing in the old file. } failed = true; diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 4cbc2e1df6e4..f40612ec3e73 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -260,7 +260,7 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */ p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ nullptr, // GDExtensionClassRecreateInstance recreate_instance_func; - p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; + nullptr, // GDExtensionClassGetVirtual get_virtual_func; nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; nullptr, // GDExtensionClassCallVirtualWithData call_virtual_func; p_extension_funcs->class_userdata, // void *class_userdata; @@ -271,6 +271,8 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func; p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; + p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; + nullptr, }; _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy); } @@ -296,8 +298,8 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */ p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func; - p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; - p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; + nullptr, // GDExtensionClassGetVirtual get_virtual_func; + nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func; p_extension_funcs->class_userdata, // void *class_userdata; }; @@ -307,6 +309,8 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func; p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; + p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; + p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtual get_virtual_func; }; _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy); } @@ -332,8 +336,8 @@ void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_librar nullptr, // GDExtensionClassCreateInstance2 create_instance_func; /* this one is mandatory */ p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func; - p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; - p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; + nullptr, // GDExtensionClassGetVirtual get_virtual_func; + nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func; p_extension_funcs->class_userdata, // void *class_userdata; }; @@ -343,6 +347,8 @@ void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_librar nullptr, // GDExtensionClassFreePropertyList free_property_list_func; p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance2 create_instance_func; p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; + p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; + p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtual get_virtual_func; }; _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy); } @@ -431,6 +437,8 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr extension->gdextension.free_property_list = p_deprecated_funcs->free_property_list_func; extension->gdextension.create_instance = p_deprecated_funcs->create_instance_func; extension->gdextension.get_rid = p_deprecated_funcs->get_rid_func; + extension->gdextension.get_virtual = p_deprecated_funcs->get_virtual_func; + extension->gdextension.get_virtual_call_data = p_deprecated_funcs->get_virtual_call_data_func; } #endif // DISABLE_DEPRECATED extension->gdextension.notification2 = p_extension_funcs->notification_func; @@ -441,8 +449,8 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr extension->gdextension.create_instance2 = p_extension_funcs->create_instance_func; extension->gdextension.free_instance = p_extension_funcs->free_instance_func; extension->gdextension.recreate_instance = p_extension_funcs->recreate_instance_func; - extension->gdextension.get_virtual = p_extension_funcs->get_virtual_func; - extension->gdextension.get_virtual_call_data = p_extension_funcs->get_virtual_call_data_func; + extension->gdextension.get_virtual2 = p_extension_funcs->get_virtual_func; + extension->gdextension.get_virtual_call_data2 = p_extension_funcs->get_virtual_call_data_func; extension->gdextension.call_virtual_with_data = p_extension_funcs->call_virtual_with_data_func; extension->gdextension.reloadable = self->reloadable; diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 706bc7e18961..bfed8424b94c 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -72,6 +72,8 @@ class GDExtension : public Resource { GDExtensionClassFreePropertyList free_property_list_func = nullptr; GDExtensionClassCreateInstance create_instance_func = nullptr; GDExtensionClassGetRID get_rid_func = nullptr; + GDExtensionClassGetVirtual get_virtual_func = nullptr; + GDExtensionClassGetVirtualCallData get_virtual_call_data_func = nullptr; #endif // DISABLE_DEPRECATED }; diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 8017733e3651..ac0181e07e13 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -273,7 +273,9 @@ typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance2)(void *p_class_us typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance); typedef GDExtensionClassInstancePtr (*GDExtensionClassRecreateInstance)(void *p_class_userdata, GDExtensionObjectPtr p_object); typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name); +typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual2)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash); typedef void *(*GDExtensionClassGetVirtualCallData)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name); +typedef void *(*GDExtensionClassGetVirtualCallData2)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash); typedef void (*GDExtensionClassCallVirtualWithData)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, void *p_virtual_call_userdata, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); typedef struct { @@ -384,14 +386,14 @@ typedef struct { GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory. GDExtensionClassRecreateInstance recreate_instance_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function. - GDExtensionClassGetVirtual get_virtual_func; + GDExtensionClassGetVirtual2 get_virtual_func; // Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that // need or benefit from extra data when calling virtual functions. // Returns user data that will be passed to `call_virtual_with_data_func`. // Returning `NULL` from this function signals to Godot that the virtual function is not overridden. // Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized. // You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`. - GDExtensionClassGetVirtualCallData get_virtual_call_data_func; + GDExtensionClassGetVirtualCallData2 get_virtual_call_data_func; // Used to call virtual functions when `get_virtual_call_data_func` is not null. GDExtensionClassCallVirtualWithData call_virtual_with_data_func; void *class_userdata; // Per-class user data, later accessible in instance bindings. diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 33f936c4758f..9a68342334d8 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -219,7 +219,7 @@ class PlaceholderExtensionInstance { memdelete(instance); } - static GDExtensionClassCallVirtual placeholder_class_get_virtual(void *p_class_userdata, GDExtensionConstStringNamePtr p_name) { + static GDExtensionClassCallVirtual placeholder_class_get_virtual(void *p_class_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash) { return nullptr; } }; @@ -713,8 +713,12 @@ ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class) #endif // DISABLE_DEPRECATED placeholder_extension->create_instance2 = &PlaceholderExtensionInstance::placeholder_class_create_instance; placeholder_extension->free_instance = &PlaceholderExtensionInstance::placeholder_class_free_instance; - placeholder_extension->get_virtual = &PlaceholderExtensionInstance::placeholder_class_get_virtual; +#ifndef DISABLE_DEPRECATED + placeholder_extension->get_virtual = nullptr; placeholder_extension->get_virtual_call_data = nullptr; +#endif // DISABLE_DEPRECATED + placeholder_extension->get_virtual2 = &PlaceholderExtensionInstance::placeholder_class_get_virtual; + placeholder_extension->get_virtual_call_data2 = nullptr; placeholder_extension->call_virtual_with_data = nullptr; placeholder_extension->recreate_instance = &PlaceholderExtensionInstance::placeholder_class_recreate_instance; @@ -938,7 +942,7 @@ void ClassDB::get_method_list_with_compatibility(const StringName &p_class, List #ifdef DEBUG_METHODS_ENABLED for (const MethodInfo &E : type->virtual_methods) { - Pair pair(E, 0); + Pair pair(E, E.get_compatibility_hash()); p_methods->push_back(pair); } @@ -2015,6 +2019,22 @@ void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_ #endif } +void ClassDB::add_virtual_compatibility_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual, const Vector &p_arg_names, bool p_object_core) { + ERR_FAIL_COND_MSG(!classes.has(p_class), vformat("Request for nonexistent class '%s'.", p_class)); + + OBJTYPE_WLOCK; + + HashMap> &virtual_methods_compat = classes[p_class].virtual_methods_compat; + + Vector *compat_hashes = virtual_methods_compat.getptr(p_method.name); + if (!compat_hashes) { + virtual_methods_compat[p_method.name] = Vector(); + compat_hashes = &virtual_methods_compat[p_method.name]; + } + + compat_hashes->push_back(p_method.get_compatibility_hash()); +} + void ClassDB::get_virtual_methods(const StringName &p_class, List *p_methods, bool p_no_inheritance) { ERR_FAIL_COND_MSG(!classes.has(p_class), vformat("Request for nonexistent class '%s'.", p_class)); @@ -2036,6 +2056,25 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List *p #endif } +Vector ClassDB::get_virtual_method_compatibility_hashes(const StringName &p_class, const StringName &p_name) { + OBJTYPE_RLOCK; + + ClassInfo *type = classes.getptr(p_class); + + while (type) { + if (type->virtual_methods_compat.has(p_name)) { + Vector *compat_hashes = type->virtual_methods_compat.getptr(p_name); + if (compat_hashes) { + return *compat_hashes; + } + break; + } + type = type->inherits_ptr; + } + + return Vector(); +} + void ClassDB::add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info) { ERR_FAIL_COND_MSG(!classes.has(p_class), vformat("Request for nonexistent class '%s'.", p_class)); diff --git a/core/object/class_db.h b/core/object/class_db.h index 81100d7586b8..ed9550cc2f8c 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -127,6 +127,7 @@ class ClassDB { HashMap> linked_properties; #endif HashMap property_setget; + HashMap> virtual_methods_compat; StringName inherits; StringName name; @@ -452,8 +453,10 @@ class ClassDB { static Vector get_method_compatibility_hashes(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, const Vector &p_arg_names = Vector(), bool p_object_core = false); + static void add_virtual_compatibility_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true, const Vector &p_arg_names = Vector(), bool p_object_core = false); static void get_virtual_methods(const StringName &p_class, List *p_methods, bool p_no_inheritance = false); static void add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info); + static Vector get_virtual_method_compatibility_hashes(const StringName &p_class, const StringName &p_name); 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 *p_constants, bool p_no_inheritance = false); diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index 2974346a46f2..325477692a6d 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -1,36 +1,46 @@ -proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\ - StringName _gdvirtual_##m_name##_sn = #m_name;\\ - mutable bool _gdvirtual_##m_name##_initialized = false;\\ - mutable void *_gdvirtual_##m_name = nullptr;\\ - _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\ - ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\ +script_call = """ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\ if (_script_instance) {\\ Callable::CallError ce;\\ $CALLSIARGS\\ - $CALLSIBEGIN_script_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\ + $CALLSIBEGIN_script_instance->callp(_gdvirtual_##$VARNAME##_sn, $CALLSIARGPASS, ce);\\ if (ce.error == Callable::CallError::CALL_OK) {\\ $CALLSIRET\\ return true;\\ }\\ - }\\ - if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ - _gdvirtual_##m_name = nullptr;\\ - if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ - _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ - } else if (_get_extension()->get_virtual) {\\ - _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ + }""" + +script_has_method = """ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\ + if (_script_instance && _script_instance->has_method(_gdvirtual_##$VARNAME##_sn)) {\\ + return true;\\ + }""" + +proto = """#define GDVIRTUAL$VER($ALIAS $RET m_name $ARG)\\ + StringName _gdvirtual_##$VARNAME##_sn = #m_name;\\ + mutable bool _gdvirtual_##$VARNAME##_initialized = false;\\ + mutable void *_gdvirtual_##$VARNAME = nullptr;\\ + _FORCE_INLINE_ bool _gdvirtual_##$VARNAME##_call($CALLARGS) $CONST {\\ + $SCRIPTCALL\\ + if (unlikely(_get_extension() && !_gdvirtual_##$VARNAME##_initialized)) {\\ + MethodInfo mi = _gdvirtual_##$VARNAME##_get_method_info();\\ + uint32_t hash = mi.get_compatibility_hash();\\ + _gdvirtual_##$VARNAME = nullptr;\\ + if (_get_extension()->get_virtual_call_data2 && _get_extension()->call_virtual_with_data) {\\ + _gdvirtual_##$VARNAME = _get_extension()->get_virtual_call_data2(_get_extension()->class_userdata, &_gdvirtual_##$VARNAME##_sn, hash);\\ + } else if (_get_extension()->get_virtual2) {\\ + _gdvirtual_##$VARNAME = (void *)_get_extension()->get_virtual2(_get_extension()->class_userdata, &_gdvirtual_##$VARNAME##_sn, hash);\\ }\\ - GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\ - _gdvirtual_##m_name##_initialized = true;\\ + _GDVIRTUAL_GET_DEPRECATED(_gdvirtual_##$VARNAME, _gdvirtual_##$VARNAME##_sn, $COMPAT)\\ + _GDVIRTUAL_TRACK(_gdvirtual_##$VARNAME, _gdvirtual_##$VARNAME##_initialized);\\ + _gdvirtual_##$VARNAME##_initialized = true;\\ }\\ - if (_gdvirtual_##m_name) {\\ + if (_gdvirtual_##$VARNAME) {\\ $CALLPTRARGS\\ $CALLPTRRETDEF\\ - if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ - _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##m_name##_sn, _gdvirtual_##m_name, $CALLPTRARGPASS, $CALLPTRRETPASS);\\ + if (_get_extension()->call_virtual_with_data) {\\ + _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##$VARNAME##_sn, _gdvirtual_##$VARNAME, $CALLPTRARGPASS, $CALLPTRRETPASS);\\ $CALLPTRRET\\ } else {\\ - ((GDExtensionClassCallVirtual)_gdvirtual_##m_name)(_get_extension_instance(), $CALLPTRARGPASS, $CALLPTRRETPASS);\\ + ((GDExtensionClassCallVirtual)_gdvirtual_##$VARNAME)(_get_extension_instance(), $CALLPTRARGPASS, $CALLPTRRETPASS);\\ $CALLPTRRET\\ }\\ return true;\\ @@ -39,27 +49,27 @@ $RVOID\\ return false;\\ }\\ - _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\ - ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\ - if (_script_instance && _script_instance->has_method(_gdvirtual_##m_name##_sn)) {\\ - return true;\\ - }\\ - if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ - _gdvirtual_##m_name = nullptr;\\ - if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ - _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ - } else if (_get_extension()->get_virtual) {\\ - _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ + _FORCE_INLINE_ bool _gdvirtual_##$VARNAME##_overridden() const {\\ + $SCRIPTHASMETHOD\\ + if (unlikely(_get_extension() && !_gdvirtual_##$VARNAME##_initialized)) {\\ + MethodInfo mi = _gdvirtual_##$VARNAME##_get_method_info();\\ + uint32_t hash = mi.get_compatibility_hash();\\ + _gdvirtual_##$VARNAME = nullptr;\\ + if (_get_extension()->get_virtual_call_data2 && _get_extension()->call_virtual_with_data) {\\ + _gdvirtual_##$VARNAME = _get_extension()->get_virtual_call_data2(_get_extension()->class_userdata, &_gdvirtual_##$VARNAME##_sn, hash);\\ + } else if (_get_extension()->get_virtual2) {\\ + _gdvirtual_##$VARNAME = (void *)_get_extension()->get_virtual2(_get_extension()->class_userdata, &_gdvirtual_##$VARNAME##_sn, hash);\\ }\\ - GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\ - _gdvirtual_##m_name##_initialized = true;\\ + _GDVIRTUAL_GET_DEPRECATED(_gdvirtual_##$VARNAME, _gdvirtual_##$VARNAME##_sn, $COMPAT)\\ + _GDVIRTUAL_TRACK(_gdvirtual_##$VARNAME, _gdvirtual_##$VARNAME##_initialized);\\ + _gdvirtual_##$VARNAME##_initialized = true;\\ }\\ - if (_gdvirtual_##m_name) {\\ + if (_gdvirtual_##$VARNAME) {\\ return true;\\ }\\ return false;\\ }\\ - _FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() {\\ + _FORCE_INLINE_ static MethodInfo _gdvirtual_##$VARNAME##_get_method_info() {\\ MethodInfo method_info;\\ method_info.name = #m_name;\\ method_info.flags = $METHOD_FLAGS;\\ @@ -70,8 +80,15 @@ """ -def generate_version(argcount, const=False, returns=False, required=False): +def generate_version(argcount, const=False, returns=False, required=False, compat=False): s = proto + if compat: + s = s.replace("$SCRIPTCALL", "") + s = s.replace("$SCRIPTHASMETHOD", "") + else: + s = s.replace("$SCRIPTCALL", script_call) + s = s.replace("$SCRIPTHASMETHOD", script_has_method) + sproto = str(argcount) method_info = "" method_flags = "METHOD_FLAG_VIRTUAL" @@ -104,6 +121,16 @@ def generate_version(argcount, const=False, returns=False, required=False): else: s = s.replace("\t\t$REQCHECK\\\n", "") + if compat: + sproto += "_COMPAT" + s = s.replace("$COMPAT", "true") + s = s.replace("$ALIAS", "m_alias,") + s = s.replace("$VARNAME", "m_alias") + else: + s = s.replace("$COMPAT", "false") + s = s.replace("$ALIAS ", "") + s = s.replace("$VARNAME", "m_name") + s = s.replace("$METHOD_FLAGS", method_flags) s = s.replace("$VER", sproto) argtext = "" @@ -188,7 +215,7 @@ def run(target, source, env): #include #ifdef TOOLS_ENABLED -#define GDVIRTUAL_TRACK(m_virtual, m_initialized)\\ +#define _GDVIRTUAL_TRACK(m_virtual, m_initialized)\\ if (_get_extension()->reloadable) {\\ VirtualMethodTracker *tracker = memnew(VirtualMethodTracker);\\ tracker->method = (void **)&m_virtual;\\ @@ -197,7 +224,20 @@ def run(target, source, env): virtual_method_list = tracker;\\ } #else -#define GDVIRTUAL_TRACK(m_virtual, m_initialized) +#define _GDVIRTUAL_TRACK(m_virtual, m_initialized) +#endif + +#ifndef DISABLE_DEPRECATED +#define _GDVIRTUAL_GET_DEPRECATED(m_virtual, m_name_sn, m_compat)\\ + else if (m_compat || ClassDB::get_virtual_method_compatibility_hashes(get_class_static(), m_name_sn).size() == 0) {\\ + if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ + m_virtual = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &m_name_sn);\\ + } else if (_get_extension()->get_virtual) {\\ + m_virtual = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &m_name_sn);\\ + }\\ + } +#else +#define _GDVIRTUAL_GET_DEPRECATED(m_name, m_name_sn, m_compat) #endif // MSVC WORKAROUND START @@ -243,6 +283,10 @@ def run(target, source, env): txt += generate_version(i, False, True, True) txt += generate_version(i, True, False, True) txt += generate_version(i, True, True, True) + txt += generate_version(i, False, False, False, True) + txt += generate_version(i, False, True, False, True) + txt += generate_version(i, True, False, False, True) + txt += generate_version(i, True, True, False, True) txt += "#endif // GDVIRTUAL_GEN_H\n" diff --git a/core/object/object.cpp b/core/object/object.cpp index 2f9d94d36a15..0f498e24cf4e 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -165,6 +165,37 @@ MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) { return mi; } +uint32_t MethodInfo::get_compatibility_hash() const { + bool has_return = (return_val.type != Variant::NIL) || (return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT); + + uint32_t hash = hash_murmur3_one_32(has_return); + hash = hash_murmur3_one_32(arguments.size(), hash); + + if (has_return) { + hash = hash_murmur3_one_32(return_val.type, hash); + if (return_val.class_name != StringName()) { + hash = hash_murmur3_one_32(return_val.class_name.operator String().hash(), hash); + } + } + + for (const PropertyInfo &arg : arguments) { + hash = hash_murmur3_one_32(arg.type, hash); + if (arg.class_name != StringName()) { + hash = hash_murmur3_one_32(arg.class_name.operator String().hash(), hash); + } + } + + hash = hash_murmur3_one_32(default_arguments.size(), hash); + for (const Variant &v : default_arguments) { + hash = hash_murmur3_one_32(v.hash(), hash); + } + + hash = hash_murmur3_one_32(flags & METHOD_FLAG_CONST ? 1 : 0, hash); + hash = hash_murmur3_one_32(flags & METHOD_FLAG_VARARG ? 1 : 0, hash); + + return hash_fmix32(hash); +} + Object::Connection::operator Variant() const { Dictionary d; d["signal"] = signal; diff --git a/core/object/object.h b/core/object/object.h index 916efdd73cad..16d8e19eaa01 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -247,6 +247,8 @@ struct MethodInfo { static MethodInfo from_dict(const Dictionary &p_dict); + uint32_t get_compatibility_hash() const; + MethodInfo() {} explicit MethodInfo(const GDExtensionMethodInfo &pinfo) : @@ -360,8 +362,12 @@ struct ObjectGDExtension { #endif // DISABLE_DEPRECATED GDExtensionClassCreateInstance2 create_instance2; GDExtensionClassFreeInstance free_instance; +#ifndef DISABLE_DEPRECATED GDExtensionClassGetVirtual get_virtual; GDExtensionClassGetVirtualCallData get_virtual_call_data; +#endif // DISABLE_DEPRECATED + GDExtensionClassGetVirtual2 get_virtual2; + GDExtensionClassGetVirtualCallData2 get_virtual_call_data2; GDExtensionClassCallVirtualWithData call_virtual_with_data; GDExtensionClassRecreateInstance recreate_instance; @@ -380,6 +386,7 @@ struct ObjectGDExtension { #else #define GDVIRTUAL_BIND(m_name, ...) #endif +#define GDVIRTUAL_BIND_COMPAT(m_alias, ...) ::ClassDB::add_virtual_compatibility_method(get_class_static(), _gdvirtual_##m_alias##_get_method_info(), true, sarray(__VA_ARGS__)); #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()