mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 16:03:29 +00:00
GDExtension: Add compatibility system for virtual methods
This commit is contained in:
parent
bdf625bd54
commit
39f16e70f8
9 changed files with 202 additions and 53 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;\\
|
||||
}\\
|
||||
}"""
|
||||
|
||||
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);\\
|
||||
}\\
|
||||
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);\\
|
||||
_GDVIRTUAL_GET_DEPRECATED(_gdvirtual_##$VARNAME, _gdvirtual_##$VARNAME##_sn, $COMPAT)\\
|
||||
_GDVIRTUAL_TRACK(_gdvirtual_##$VARNAME, _gdvirtual_##$VARNAME##_initialized);\\
|
||||
_gdvirtual_##$VARNAME##_initialized = true;\\
|
||||
}\\
|
||||
GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\
|
||||
_gdvirtual_##m_name##_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;\\
|
||||
_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);\\
|
||||
}\\
|
||||
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);\\
|
||||
_GDVIRTUAL_GET_DEPRECATED(_gdvirtual_##$VARNAME, _gdvirtual_##$VARNAME##_sn, $COMPAT)\\
|
||||
_GDVIRTUAL_TRACK(_gdvirtual_##$VARNAME, _gdvirtual_##$VARNAME##_initialized);\\
|
||||
_gdvirtual_##$VARNAME##_initialized = true;\\
|
||||
}\\
|
||||
GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\
|
||||
_gdvirtual_##m_name##_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"
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue