GDExtension: Add compatibility system for virtual methods

This commit is contained in:
David Snopek 2024-12-20 17:10:46 -06:00
parent bdf625bd54
commit 39f16e70f8
9 changed files with 202 additions and 53 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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
};

View file

@ -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.

View file

@ -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));

View file

@ -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);

View file

@ -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"

View file

@ -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;

View file

@ -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()