Explorar o código

GDExtension: Add compatibility system for virtual methods

David Snopek hai 8 meses
pai
achega
39f16e70f8

+ 15 - 3
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<uint32_t> 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 use to have hashes, so skip check if it's missing in the old file.
 				}
 
 				failed = true;

+ 15 - 7
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;

+ 2 - 0
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
 	};
 

+ 4 - 2
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.

+ 42 - 3
core/object/class_db.cpp

@@ -219,7 +219,7 @@ public:
 		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<MethodInfo, uint32_t> pair(E, 0);
+			Pair<MethodInfo, uint32_t> 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<String> &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<StringName, Vector<uint32_t>> &virtual_methods_compat = classes[p_class].virtual_methods_compat;
+
+	Vector<uint32_t> *compat_hashes = virtual_methods_compat.getptr(p_method.name);
+	if (!compat_hashes) {
+		virtual_methods_compat[p_method.name] = Vector<uint32_t>();
+		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<MethodInfo> *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<MethodInfo> *p
 #endif
 }
 
+Vector<uint32_t> 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<uint32_t> *compat_hashes = type->virtual_methods_compat.getptr(p_name);
+			if (compat_hashes) {
+				return *compat_hashes;
+			}
+			break;
+		}
+		type = type->inherits_ptr;
+	}
+
+	return Vector<uint32_t>();
+}
+
 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));
 

+ 3 - 0
core/object/class_db.h

@@ -127,6 +127,7 @@ public:
 		HashMap<StringName, List<StringName>> linked_properties;
 #endif
 		HashMap<StringName, PropertySetGet> property_setget;
+		HashMap<StringName, Vector<uint32_t>> virtual_methods_compat;
 
 		StringName inherits;
 		StringName name;
@@ -452,8 +453,10 @@ public:
 	static Vector<uint32_t> 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<String> &p_arg_names = Vector<String>(), 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<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 add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info);
+	static Vector<uint32_t> 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<String> *p_constants, bool p_no_inheritance = false);

+ 82 - 38
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 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
 		$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 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
 """
 
 
-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 <utility>
 
 #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 @@ _to_variant(T&& t) {
         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"
 

+ 32 - 0
core/object/object.cpp

@@ -165,6 +165,38 @@ MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) {
 	return mi;
 }
 
+// This was copied from MethodBind::get_hash() so that the compatibility hashes for virtual and non-virtual methods would be the same.
+uint32_t MethodInfo::get_compatibility_hash() const {
+	bool has_return = (return_val.type != Variant::NIL) || (return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT);
+
+	uint32_t hash = hash_murmur3_one_32(has_return);
+	hash = hash_murmur3_one_32(arguments.size(), hash);
+
+	if (has_return) {
+		hash = hash_murmur3_one_32(return_val.type, hash);
+		if (return_val.class_name != StringName()) {
+			hash = hash_murmur3_one_32(return_val.class_name.hash(), hash);
+		}
+	}
+
+	for (const PropertyInfo &arg : arguments) {
+		hash = hash_murmur3_one_32(arg.type, hash);
+		if (arg.class_name != StringName()) {
+			hash = hash_murmur3_one_32(arg.class_name.hash(), hash);
+		}
+	}
+
+	hash = hash_murmur3_one_32(default_arguments.size(), hash);
+	for (const Variant &v : default_arguments) {
+		hash = hash_murmur3_one_32(v.hash(), hash);
+	}
+
+	hash = hash_murmur3_one_32(flags & METHOD_FLAG_CONST ? 1 : 0, hash);
+	hash = hash_murmur3_one_32(flags & METHOD_FLAG_VARARG ? 1 : 0, hash);
+
+	return hash_fmix32(hash);
+}
+
 Object::Connection::operator Variant() const {
 	Dictionary d;
 	d["signal"] = signal;

+ 7 - 0
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()