summaryrefslogtreecommitdiffstats
path: root/core/extension/gdextension_interface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/extension/gdextension_interface.cpp')
-rw-r--r--core/extension/gdextension_interface.cpp215
1 files changed, 204 insertions, 11 deletions
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index f59ee291ed..1a806791c9 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -32,6 +32,7 @@
#include "core/config/engine.h"
#include "core/extension/gdextension.h"
+#include "core/extension/gdextension_compat_hashes.h"
#include "core/io/file_access.h"
#include "core/io/xml_parser.h"
#include "core/object/class_db.h"
@@ -41,6 +42,152 @@
#include "core/variant/variant.h"
#include "core/version.h"
+class CallableCustomExtension : public CallableCustom {
+ void *userdata;
+ void *token;
+
+ ObjectID object;
+
+ GDExtensionCallableCustomCall call_func;
+ GDExtensionCallableCustomIsValid is_valid_func;
+ GDExtensionCallableCustomFree free_func;
+
+ GDExtensionCallableCustomEqual equal_func;
+ GDExtensionCallableCustomLessThan less_than_func;
+
+ GDExtensionCallableCustomToString to_string_func;
+
+ uint32_t _hash;
+
+ static bool default_compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->call_func != b->call_func || a->userdata != b->userdata) {
+ return false;
+ }
+ return true;
+ }
+
+ static bool default_compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->call_func != b->call_func) {
+ return a->call_func < b->call_func;
+ }
+ return a->userdata < b->userdata;
+ }
+
+ static bool custom_compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->equal_func != b->equal_func) {
+ return false;
+ }
+ return a->equal_func(a->userdata, b->userdata);
+ }
+
+ static bool custom_compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->less_than_func != b->less_than_func) {
+ return default_compare_less(p_a, p_b);
+ }
+ return a->less_than_func(a->userdata, b->userdata);
+ }
+
+public:
+ uint32_t hash() const override {
+ return _hash;
+ }
+
+ String get_as_text() const override {
+ if (to_string_func != nullptr) {
+ String out;
+ GDExtensionBool is_valid = false;
+
+ to_string_func(userdata, &is_valid, (GDExtensionStringPtr)&out);
+
+ if (is_valid) {
+ return out;
+ }
+ }
+ return "<CallableCustom>";
+ }
+
+ CompareEqualFunc get_compare_equal_func() const override {
+ return (equal_func != nullptr) ? custom_compare_equal : default_compare_equal;
+ }
+
+ CompareLessFunc get_compare_less_func() const override {
+ return (less_than_func != nullptr) ? custom_compare_less : default_compare_less;
+ }
+
+ bool is_valid() const override {
+ if (is_valid_func != nullptr && !is_valid_func(userdata)) {
+ return false;
+ }
+ return call_func != nullptr;
+ }
+
+ StringName get_method() const override {
+ return StringName();
+ }
+
+ ObjectID get_object() const override {
+ return object;
+ }
+
+ void *get_userdata(void *p_token) const {
+ return (p_token == token) ? userdata : nullptr;
+ }
+
+ 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);
+
+ r_call_error.error = (Callable::CallError::Error)error.error;
+ r_call_error.argument = error.argument;
+ r_call_error.expected = error.expected;
+ }
+
+ CallableCustomExtension(GDExtensionCallableCustomInfo *p_info) {
+ userdata = p_info->callable_userdata;
+ token = p_info->token;
+
+ if (p_info->object != nullptr) {
+ object = ((Object *)p_info->object)->get_instance_id();
+ }
+
+ call_func = p_info->call_func;
+ is_valid_func = p_info->is_valid_func;
+ free_func = p_info->free_func;
+
+ equal_func = p_info->equal_func;
+ less_than_func = p_info->less_than_func;
+
+ to_string_func = p_info->to_string_func;
+
+ // Pre-calculate the hash.
+ if (p_info->hash_func != nullptr) {
+ _hash = p_info->hash_func(userdata);
+ } else {
+ _hash = hash_murmur3_one_64((uint64_t)call_func);
+ _hash = hash_murmur3_one_64((uint64_t)userdata, _hash);
+ }
+ }
+
+ ~CallableCustomExtension() {
+ if (free_func != nullptr) {
+ free_func(userdata);
+ }
+ }
+};
+
// Core interface functions.
GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name) {
return GDExtension::get_interface_function(p_name);
@@ -607,25 +754,25 @@ static void gdextension_string_new_with_utf8_chars_and_len(GDExtensionUninitiali
dest->parse_utf8(p_contents, p_size);
}
-static void gdextension_string_new_with_utf16_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size) {
+static void gdextension_string_new_with_utf16_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_char_count) {
memnew_placement(r_dest, String);
String *dest = reinterpret_cast<String *>(r_dest);
- dest->parse_utf16(p_contents, p_size);
+ dest->parse_utf16(p_contents, p_char_count);
}
-static void gdextension_string_new_with_utf32_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size) {
- memnew_placement(r_dest, String((const char32_t *)p_contents, p_size));
+static void gdextension_string_new_with_utf32_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_char_count) {
+ memnew_placement(r_dest, String((const char32_t *)p_contents, p_char_count));
}
-static void gdextension_string_new_with_wide_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size) {
+static void gdextension_string_new_with_wide_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_char_count) {
if constexpr (sizeof(wchar_t) == 2) {
// wchar_t is 16 bit, parse.
memnew_placement(r_dest, String);
String *dest = reinterpret_cast<String *>(r_dest);
- dest->parse_utf16((const char16_t *)p_contents, p_size);
+ dest->parse_utf16((const char16_t *)p_contents, p_char_count);
} else {
// wchar_t is 32 bit, copy.
- memnew_placement(r_dest, String((const char32_t *)p_contents, p_size));
+ memnew_placement(r_dest, String((const char32_t *)p_contents, p_char_count));
}
}
@@ -731,6 +878,24 @@ static GDExtensionInt gdextension_string_resize(GDExtensionStringPtr p_self, GDE
return (*self).resize(p_length);
}
+static void gdextension_string_name_new_with_latin1_chars(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents, GDExtensionBool p_is_static) {
+ memnew_placement(r_dest, StringName(p_contents, static_cast<bool>(p_is_static)));
+}
+
+static void gdextension_string_name_new_with_utf8_chars(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents) {
+ String tmp;
+ tmp.parse_utf8(p_contents);
+
+ memnew_placement(r_dest, StringName(tmp));
+}
+
+static void gdextension_string_name_new_with_utf8_chars_and_len(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents, GDExtensionInt p_size) {
+ String tmp;
+ tmp.parse_utf8(p_contents, p_size);
+
+ memnew_placement(r_dest, StringName(tmp));
+}
+
static GDExtensionInt gdextension_xml_parser_open_buffer(GDExtensionObjectPtr p_instance, const uint8_t *p_buffer, size_t p_size) {
XMLParser *xml = (XMLParser *)p_instance;
return (GDExtensionInt)xml->_open_buffer(p_buffer, p_size);
@@ -1139,20 +1304,43 @@ static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExt
return script_instance_extension->instance;
}
+static void gdextension_callable_custom_create(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo *p_custom_callable_info) {
+ memnew_placement(r_callable, Callable(memnew(CallableCustomExtension(p_custom_callable_info))));
+}
+
+static void *gdextension_callable_custom_get_userdata(GDExtensionTypePtr p_callable, void *p_token) {
+ const Callable &callable = *reinterpret_cast<const Callable *>(p_callable);
+ if (!callable.is_custom()) {
+ return nullptr;
+ }
+ const CallableCustomExtension *custom_callable = dynamic_cast<const CallableCustomExtension *>(callable.get_custom());
+ if (!custom_callable) {
+ return nullptr;
+ }
+ return custom_callable->get_userdata(p_token);
+}
+
static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
const StringName methodname = *reinterpret_cast<const StringName *>(p_methodname);
bool exists = false;
MethodBind *mb = ClassDB::get_method_with_compatibility(classname, methodname, p_hash, &exists);
+
+#ifndef DISABLE_DEPRECATED
+ // If lookup failed, see if this is one of the broken hashes from issue #81386.
+ if (!mb && exists) {
+ uint32_t mapped_hash;
+ if (GDExtensionCompatHashes::lookup_current_hash(classname, methodname, p_hash, &mapped_hash)) {
+ mb = ClassDB::get_method_with_compatibility(classname, methodname, mapped_hash, &exists);
+ }
+ }
+#endif
+
if (!mb && exists) {
ERR_PRINT("Method '" + classname + "." + methodname + "' has changed and no compatibility fallback has been provided. Please open an issue.");
return nullptr;
}
ERR_FAIL_NULL_V(mb, nullptr);
- if (mb->get_hash() != p_hash) {
- ERR_PRINT("Hash mismatch for method '" + classname + "." + methodname + "'.");
- return nullptr;
- }
return (GDExtensionMethodBindPtr)mb;
}
@@ -1264,6 +1452,9 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(string_operator_plus_eq_wcstr);
REGISTER_INTERFACE_FUNC(string_operator_plus_eq_c32str);
REGISTER_INTERFACE_FUNC(string_resize);
+ REGISTER_INTERFACE_FUNC(string_name_new_with_latin1_chars);
+ REGISTER_INTERFACE_FUNC(string_name_new_with_utf8_chars);
+ REGISTER_INTERFACE_FUNC(string_name_new_with_utf8_chars_and_len);
REGISTER_INTERFACE_FUNC(xml_parser_open_buffer);
REGISTER_INTERFACE_FUNC(file_access_store_buffer);
REGISTER_INTERFACE_FUNC(file_access_get_buffer);
@@ -1313,6 +1504,8 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(placeholder_script_instance_create);
REGISTER_INTERFACE_FUNC(placeholder_script_instance_update);
REGISTER_INTERFACE_FUNC(object_get_script_instance);
+ REGISTER_INTERFACE_FUNC(callable_custom_create);
+ REGISTER_INTERFACE_FUNC(callable_custom_get_userdata);
REGISTER_INTERFACE_FUNC(classdb_construct_object);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
REGISTER_INTERFACE_FUNC(classdb_get_class_tag);