From cb1c900ff4dce98fb124afe9dd989a54b6b16d36 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Sat, 28 Jun 2025 11:12:53 -0500 Subject: [PATCH] GDExtension: Check conversions to/from GDExtension pointer types --- core/extension/gdextension.cpp | 2 +- core/extension/gdextension.h | 6 +- core/extension/gdextension_interface.cpp | 14 +-- core/extension/gdextension_interface.json | 10 ++- core/extension/gdextension_interface_conv.h | 87 +++++++++++++++++++ core/extension/gdextension_library_loader.cpp | 2 +- core/extension/make_interface_header.py | 10 ++- core/object/class_db.cpp | 4 +- core/object/make_virtuals.py | 7 +- core/object/object.cpp | 22 ++--- core/object/script_language_extension.h | 10 +-- core/variant/variant_internal.h | 7 +- 12 files changed, 140 insertions(+), 41 deletions(-) create mode 100644 core/extension/gdextension_interface_conv.h diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 254095e9158f..4afa16161b6c 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -227,7 +227,7 @@ class GDExtensionMethodBind : public MethodBind { Vector defargs; defargs.resize(p_method_info->default_argument_count); for (uint32_t i = 0; i < p_method_info->default_argument_count; i++) { - defargs.write[i] = *static_cast(p_method_info->default_arguments[i]); + defargs.write[i] = *from_gdextension(p_method_info->default_arguments[i]); } set_default_arguments(defargs); diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 2f0835a0d2c5..5fc0d01ef533 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -90,11 +90,11 @@ class GDExtension : public Resource { static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield); static void _register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter); static void _register_extension_class_property_indexed(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter, GDExtensionInt p_index); - static void _register_extension_class_property_group(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_group_name, GDExtensionConstStringNamePtr p_prefix); - static void _register_extension_class_property_subgroup(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_subgroup_name, GDExtensionConstStringNamePtr p_prefix); + static void _register_extension_class_property_group(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_group_name, GDExtensionConstStringPtr p_prefix); + static void _register_extension_class_property_subgroup(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_subgroup_name, GDExtensionConstStringPtr p_prefix); static void _register_extension_class_signal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_signal_name, const GDExtensionPropertyInfo *p_argument_info, GDExtensionInt p_argument_count); static void _unregister_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name); - static void _get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionStringPtr r_path); + static void _get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path); static void _register_get_classes_used_callback(GDExtensionClassLibraryPtr p_library, GDExtensionEditorGetClassesUsedCallback p_callback); static void _register_main_loop_callbacks(GDExtensionClassLibraryPtr p_library, const GDExtensionMainLoopCallbacks *p_callbacks); diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index 539e52efe2eb..eb3eeda2f769 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -166,7 +166,7 @@ class CallableCustomExtension : public CallableCustom { void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override { GDExtensionCallError error; - call_func(userdata, (GDExtensionConstVariantPtr *)p_arguments, p_argcount, (GDExtensionVariantPtr)&r_return_value, &error); + call_func(userdata, to_gdextension(p_arguments), p_argcount, to_gdextension(&r_return_value), &error); r_call_error.error = (Callable::CallError::Error)error.error; r_call_error.argument = error.argument; @@ -321,15 +321,15 @@ static void gdextension_variant_new_nil(GDExtensionUninitializedVariantPtr r_des memnew_placement(reinterpret_cast(r_dest), Variant); } static void gdextension_variant_destroy(GDExtensionVariantPtr p_self) { - reinterpret_cast(p_self)->~Variant(); + from_gdextension(p_self)->~Variant(); } // variant type static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) { - Variant *self = (Variant *)p_self; - const StringName method = *reinterpret_cast(p_method); - const Variant **args = (const Variant **)p_args; + Variant *self = from_gdextension(p_self); + const StringName method = *from_gdextension(p_method); + const Variant **args = from_gdextension(p_args); Callable::CallError error; memnew_placement(r_return, Variant); Variant *ret = reinterpret_cast(r_return); @@ -1573,7 +1573,7 @@ static void gdextension_placeholder_script_instance_update(GDExtensionScriptInst placeholder->update(properties_list, values_map); } -static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExtensionConstObjectPtr p_object, GDExtensionConstObjectPtr p_language) { +static GDExtensionScriptInstanceDataPtr gdextension_object_get_script_instance(GDExtensionConstObjectPtr p_object, GDExtensionConstObjectPtr p_language) { if (!p_object || !p_language) { return nullptr; } @@ -1585,7 +1585,7 @@ static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExt } const ScriptLanguage *language = script_instance_extension->get_language(); - if (language != p_language) { + if (language != from_gdextension(p_language)) { return nullptr; } diff --git a/core/extension/gdextension_interface.json b/core/extension/gdextension_interface.json index 04c38794e468..510fc0f8ebe7 100644 --- a/core/extension/gdextension_interface.json +++ b/core/extension/gdextension_interface.json @@ -817,7 +817,10 @@ }, { "name": "GDExtensionClassInstancePtr", - "kind": "handle" + "kind": "handle", + "description": [ + "An opaque pointer to an instance of a native class within the extension, which is bound to a Godot Object." + ] }, { "name": "GDExtensionClassSet", @@ -1780,7 +1783,10 @@ }, { "name": "GDExtensionClassLibraryPtr", - "kind": "handle" + "kind": "handle", + "description": [ + "A handle which represents the extension within Godot." + ] }, { "name": "GDExtensionEditorGetClassesUsedCallback", diff --git a/core/extension/gdextension_interface_conv.h b/core/extension/gdextension_interface_conv.h new file mode 100644 index 000000000000..fc67a3ad39c6 --- /dev/null +++ b/core/extension/gdextension_interface_conv.h @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* gdextension_interface_conv.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#pragma once + +#include "gdextension_interface.gen.h" + +class GDExtension; +class MethodBind; +class Object; +class StringName; +class String; +class Variant; + +template +class Ref; + +template +struct GDExtensionPtrTraits; + +#define GDEXTENSION_PTR_CONV(m_type, m_opaque_ptr) \ + template <> \ + struct GDExtensionPtrTraits { \ + using OpaquePtr = m_opaque_ptr; \ + \ + static inline OpaquePtr to(m_type *p_godot_type) { \ + return reinterpret_cast(p_godot_type); \ + } \ + static inline m_type *from(OpaquePtr p_gdextension_type) { \ + return reinterpret_cast(p_gdextension_type); \ + } \ + }; + +GDEXTENSION_PTR_CONV(Variant, GDExtensionVariantPtr); +GDEXTENSION_PTR_CONV(const Variant, GDExtensionConstVariantPtr); +GDEXTENSION_PTR_CONV(const Variant *, GDExtensionConstVariantPtr *); +GDEXTENSION_PTR_CONV(StringName, GDExtensionStringNamePtr); +GDEXTENSION_PTR_CONV(const StringName, GDExtensionConstStringNamePtr); +GDEXTENSION_PTR_CONV(String, GDExtensionStringPtr); +GDEXTENSION_PTR_CONV(const String, GDExtensionConstStringPtr); +GDEXTENSION_PTR_CONV(GDExtension, GDExtensionClassLibraryPtr); +GDEXTENSION_PTR_CONV(Object, GDExtensionObjectPtr); +GDEXTENSION_PTR_CONV(const Object, GDExtensionConstObjectPtr); +GDEXTENSION_PTR_CONV(const MethodBind, GDExtensionMethodBindPtr); + +template +inline typename GDExtensionPtrTraits::OpaquePtr to_gdextension(T *p_godot_type) { + return GDExtensionPtrTraits::to(p_godot_type); +} + +template +inline T *from_gdextension(typename GDExtensionPtrTraits::OpaquePtr p_gdextension_type) { + return GDExtensionPtrTraits::from(p_gdextension_type); +} + +// Any encoded type (ie `PtrToArg::EncodedT`) can be converted to `GDExtensionTypePtr`. +template +inline GDExtensionTypePtr to_gdextension_type_ptr(typename PtrToArg::EncodeT *p_encoded_value) { + return reinterpret_cast(p_encoded_value); +} diff --git a/core/extension/gdextension_library_loader.cpp b/core/extension/gdextension_library_loader.cpp index 8db6ef390ef4..2ed16be620ec 100644 --- a/core/extension/gdextension_library_loader.cpp +++ b/core/extension/gdextension_library_loader.cpp @@ -227,7 +227,7 @@ Error GDExtensionLibraryLoader::initialize(GDExtensionInterfaceGetProcAddress p_ GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr; - GDExtensionBool ret = initialization_function(p_get_proc_address, p_extension.ptr(), r_initialization); + GDExtensionBool ret = initialization_function(p_get_proc_address, to_gdextension(p_extension.ptr()), r_initialization); if (ret) { return OK; diff --git a/core/extension/make_interface_header.py b/core/extension/make_interface_header.py index 031a3498fd17..1038edf28047 100644 --- a/core/extension/make_interface_header.py +++ b/core/extension/make_interface_header.py @@ -74,9 +74,7 @@ def run(target, source, env): ) if "parent" in type and type["parent"] not in handles: raise UnknownTypeError(type["parent"], type["name"]) - # @todo In the future, let's write these as `struct *` so the compiler can help us with type checking. - type["type"] = "void*" if not type.get("is_const", False) else "const void*" - write_simple_type(file, type) + write_handle_type(file, type) handles.append(type["name"]) elif kind == "alias": check_allowed_keys(type, ["name", "kind", "type"], ["description", "deprecated"]) @@ -259,6 +257,12 @@ def make_deprecated_comment_for_type(type): return f" /* {message} */" +def write_handle_type(file, type): + base_name = type.get("parent", type["name"]) + const = "const " if type.get("is_const", False) else "" + file.write(f"typedef {const}struct {base_name}_T *{type['name']};{make_deprecated_comment_for_type(type)}\n") + + def write_simple_type(file, type): file.write(f"typedef {format_type_and_name(type['type'], type['name'])};{make_deprecated_comment_for_type(type)}\n") diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 61dd09801b2a..30353dbfdecc 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -194,7 +194,7 @@ class PlaceholderExtensionInstance { // done in a different way to support placeholders) will also be done here. obj->_extension = ClassDB::get_placeholder_extension(ti->name); - obj->_extension_instance = memnew(PlaceholderExtensionInstance(ti->name)); + obj->_extension_instance = reinterpret_cast(memnew(PlaceholderExtensionInstance(ti->name))); obj->_reset_gdtype(); @@ -204,7 +204,7 @@ class PlaceholderExtensionInstance { } #endif - return obj; + return to_gdextension(obj); } static GDExtensionObjectPtr placeholder_class_recreate_instance(void *p_class_userdata, GDExtensionObjectPtr p_object) { diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index 9ba6330c4321..c6ce8544bbdf 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -27,7 +27,7 @@ $CALLPTRARGS\\ $CALLPTRRETDEF\\ if (_get_extension()->call_virtual_with_data) {\\ - _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##$VARNAME##_sn, _gdvirtual_##$VARNAME, $CALLPTRARGPASS, $CALLPTRRETPASS);\\ + _get_extension()->call_virtual_with_data(_get_extension_instance(), to_gdextension(&_gdvirtual_##$VARNAME##_sn), _gdvirtual_##$VARNAME, $CALLPTRARGPASS, $CALLPTRRETPASS);\\ $CALLPTRRET\\ } else {\\ ((GDExtensionClassCallVirtual)_gdvirtual_##$VARNAME)(_get_extension_instance(), $CALLPTRARGPASS, $CALLPTRRETPASS);\\ @@ -150,7 +150,7 @@ def generate_version(argcount, const=False, returns=False, required=False, compa callsiargs += f"VariantInternal::make(arg{i + 1})" callsiargptrs += f"&vargs[{i}]" callptrargs += f"PtrToArg::EncodeT argval{i + 1}; PtrToArg::encode(arg{i + 1}, &argval{i + 1});\\\n" - callptrargsptr += f"&argval{i + 1}" + callptrargsptr += f"to_gdextension_type_ptr(&argval{i + 1})" if argcount: callsiargs += " };\\\n" @@ -172,7 +172,7 @@ def generate_version(argcount, const=False, returns=False, required=False, compa callargtext += "m_ret &r_ret" s = s.replace("$CALLSIBEGIN", "Variant ret = ") s = s.replace("$CALLSIRET", "r_ret = VariantCaster::cast(ret);") - s = s.replace("$CALLPTRRETPASS", "&ret") + s = s.replace("$CALLPTRRETPASS", "to_gdextension_type_ptr(&ret)") s = s.replace("$CALLPTRRET", "r_ret = (m_ret)ret;") else: s = s.replace("$CALLSIBEGIN", "") @@ -197,6 +197,7 @@ def run(target, source, env): #pragma once #include "core/object/script_instance.h" +#include "core/extension/gdextension_interface_conv.h" inline constexpr uintptr_t _INVALID_GDVIRTUAL_FUNC_ADDR = static_cast(-1); diff --git a/core/object/object.cpp b/core/object/object.cpp index 4729eecf87df..1efc46d40f4f 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -648,18 +648,18 @@ void Object::validate_property(PropertyInfo &p_property) const { StringName prop_name = p_property.name; GDExtensionPropertyInfo gdext_prop = { (GDExtensionVariantType)p_property.type, - &prop_name, - &p_property.class_name, + to_gdextension(&prop_name), + to_gdextension(&p_property.class_name), (uint32_t)p_property.hint, - &p_property.hint_string, + to_gdextension(&p_property.hint_string), p_property.usage, }; if (_extension->validate_property(_extension_instance, &gdext_prop)) { p_property.type = (Variant::Type)gdext_prop.type; - p_property.name = *reinterpret_cast(gdext_prop.name); - p_property.class_name = *reinterpret_cast(gdext_prop.class_name); + p_property.name = *from_gdextension(gdext_prop.name); + p_property.class_name = *from_gdextension(gdext_prop.class_name); p_property.hint = (PropertyHint)gdext_prop.hint; - p_property.hint_string = *reinterpret_cast(gdext_prop.hint_string); + p_property.hint_string = *from_gdextension(gdext_prop.hint_string); p_property.usage = gdext_prop.usage; }; } @@ -982,15 +982,15 @@ Variant Object::call_const(const StringName &p_method, const Variant **p_args, i void Object::_gdvirtual_init_method_ptr(uint32_t p_compat_hash, void *&r_fn_ptr, const StringName &p_fn_name, bool p_compat) const { r_fn_ptr = nullptr; if (_extension->get_virtual_call_data2 && _extension->call_virtual_with_data) { - r_fn_ptr = _extension->get_virtual_call_data2(_extension->class_userdata, &p_fn_name, p_compat_hash); + r_fn_ptr = _extension->get_virtual_call_data2(_extension->class_userdata, to_gdextension(&p_fn_name), p_compat_hash); } else if (_extension->get_virtual2) { - r_fn_ptr = (void *)_extension->get_virtual2(_extension->class_userdata, &p_fn_name, p_compat_hash); + r_fn_ptr = (void *)_extension->get_virtual2(_extension->class_userdata, to_gdextension(&p_fn_name), p_compat_hash); #ifndef DISABLE_DEPRECATED } else if (p_compat || ClassDB::get_virtual_method_compatibility_hashes(get_class_name(), p_fn_name).size() == 0) { if (_extension->get_virtual_call_data && _extension->call_virtual_with_data) { - r_fn_ptr = _extension->get_virtual_call_data(_extension->class_userdata, &p_fn_name); + r_fn_ptr = _extension->get_virtual_call_data(_extension->class_userdata, to_gdextension(&p_fn_name)); } else if (_extension->get_virtual) { - r_fn_ptr = (void *)_extension->get_virtual(_extension->class_userdata, &p_fn_name); + r_fn_ptr = (void *)_extension->get_virtual(_extension->class_userdata, to_gdextension(&p_fn_name)); } #endif } @@ -1059,7 +1059,7 @@ String Object::to_string() { if (_extension && _extension->to_string) { String ret; GDExtensionBool is_valid; - _extension->to_string(_extension_instance, &is_valid, &ret); + _extension->to_string(_extension_instance, &is_valid, to_gdextension(&ret)); if (is_valid) { return ret; } diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index b74261e92c93..4748e6e69518 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -762,10 +762,10 @@ class ScriptInstanceExtension : public ScriptInstance { StringName prop_name = p_property.name; GDExtensionPropertyInfo gdext_prop = { (GDExtensionVariantType)p_property.type, - &prop_name, - &p_property.class_name, + to_gdextension(&prop_name), + to_gdextension(&p_property.class_name), (uint32_t)p_property.hint, - &p_property.hint_string, + to_gdextension(&p_property.hint_string), p_property.usage, }; if (native_info->validate_property_func(instance, &gdext_prop)) { @@ -781,13 +781,13 @@ class ScriptInstanceExtension : public ScriptInstance { virtual bool property_can_revert(const StringName &p_name) const override { if (native_info->property_can_revert_func) { - return native_info->property_can_revert_func(instance, (GDExtensionConstStringNamePtr)&p_name); + return native_info->property_can_revert_func(instance, to_gdextension(&p_name)); } return false; } virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const override { if (native_info->property_get_revert_func) { - return native_info->property_get_revert_func(instance, (GDExtensionConstStringNamePtr)&p_name, (GDExtensionVariantPtr)&r_ret); + return native_info->property_get_revert_func(instance, to_gdextension(&p_name), to_gdextension(&r_ret)); } return false; } diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 50205dcb5f7c..d25aae0d02d0 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -33,6 +33,7 @@ #include "type_info.h" #include "variant.h" +#include "core/extension/gdextension_interface_conv.h" #include "core/templates/simple_type.h" // For use when you want to access the internal pointer of a Variant directly. @@ -985,13 +986,13 @@ struct VariantTypeAdjust { template struct VariantTypeConstructor { - _FORCE_INLINE_ static void variant_from_type(void *r_variant, void *p_value) { + _FORCE_INLINE_ static void variant_from_type(GDExtensionUninitializedVariantPtr r_variant, GDExtensionTypePtr p_value) { // r_variant is provided by caller as uninitialized memory memnew_placement(r_variant, Variant(*((T *)p_value))); } - _FORCE_INLINE_ static void type_from_variant(void *r_value, void *p_variant) { + _FORCE_INLINE_ static void type_from_variant(GDExtensionUninitializedTypePtr r_value, GDExtensionVariantPtr p_variant) { // r_value is provided by caller as uninitialized memory - memnew_placement(r_value, T(*reinterpret_cast(p_variant))); + memnew_placement(r_value, T(*from_gdextension(p_variant))); } };