1/**************************************************************************/
2/* gdextension.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "gdextension.h"
32#include "core/config/project_settings.h"
33#include "core/io/dir_access.h"
34#include "core/object/class_db.h"
35#include "core/object/method_bind.h"
36#include "core/os/os.h"
37#include "core/version.h"
38
39extern void gdextension_setup_interface();
40extern GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name);
41
42typedef GDExtensionBool (*GDExtensionLegacyInitializationFunction)(void *p_interface, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);
43
44String GDExtension::get_extension_list_config_file() {
45 return ProjectSettings::get_singleton()->get_project_data_path().path_join("extension_list.cfg");
46}
47
48String GDExtension::find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags) {
49 // First, check the explicit libraries.
50 if (p_config->has_section("libraries")) {
51 List<String> libraries;
52 p_config->get_section_keys("libraries", &libraries);
53
54 // Iterate the libraries, finding the best matching tags.
55 String best_library_path;
56 Vector<String> best_library_tags;
57 for (const String &E : libraries) {
58 Vector<String> tags = E.split(".");
59 bool all_tags_met = true;
60 for (int i = 0; i < tags.size(); i++) {
61 String tag = tags[i].strip_edges();
62 if (!p_has_feature(tag)) {
63 all_tags_met = false;
64 break;
65 }
66 }
67
68 if (all_tags_met && tags.size() > best_library_tags.size()) {
69 best_library_path = p_config->get_value("libraries", E);
70 best_library_tags = tags;
71 }
72 }
73
74 if (!best_library_path.is_empty()) {
75 if (best_library_path.is_relative_path()) {
76 best_library_path = p_path.get_base_dir().path_join(best_library_path);
77 }
78 if (r_tags != nullptr) {
79 r_tags->append_array(best_library_tags);
80 }
81 return best_library_path;
82 }
83 }
84
85 // Second, try to autodetect
86 String autodetect_library_prefix;
87 if (p_config->has_section_key("configuration", "autodetect_library_prefix")) {
88 autodetect_library_prefix = p_config->get_value("configuration", "autodetect_library_prefix");
89 }
90 if (!autodetect_library_prefix.is_empty()) {
91 String autodetect_path = autodetect_library_prefix;
92 if (autodetect_path.is_relative_path()) {
93 autodetect_path = p_path.get_base_dir().path_join(autodetect_path);
94 }
95
96 // Find the folder and file parts of the prefix.
97 String folder;
98 String file_prefix;
99 if (DirAccess::dir_exists_absolute(autodetect_path)) {
100 folder = autodetect_path;
101 } else if (DirAccess::dir_exists_absolute(autodetect_path.get_base_dir())) {
102 folder = autodetect_path.get_base_dir();
103 file_prefix = autodetect_path.get_file();
104 } else {
105 ERR_FAIL_V_MSG(String(), vformat("Error in extension: %s. Could not find folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
106 }
107
108 // Open the folder.
109 Ref<DirAccess> dir = DirAccess::open(folder);
110 ERR_FAIL_COND_V_MSG(!dir.is_valid(), String(), vformat("Error in extension: %s. Could not open folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix));
111
112 // Iterate the files and check the prefixes, finding the best matching file.
113 String best_file;
114 Vector<String> best_file_tags;
115 dir->list_dir_begin();
116 String file_name = dir->_get_next();
117 while (file_name != "") {
118 if (!dir->current_is_dir() && file_name.begins_with(file_prefix)) {
119 // Check if the files matches all requested feature tags.
120 String tags_str = file_name.trim_prefix(file_prefix);
121 tags_str = tags_str.trim_suffix(tags_str.get_extension());
122
123 Vector<String> tags = tags_str.split(".", false);
124 bool all_tags_met = true;
125 for (int i = 0; i < tags.size(); i++) {
126 String tag = tags[i].strip_edges();
127 if (!p_has_feature(tag)) {
128 all_tags_met = false;
129 break;
130 }
131 }
132
133 // If all tags are found in the feature list, and we found more tags than before, use this file.
134 if (all_tags_met && tags.size() > best_file_tags.size()) {
135 best_file_tags = tags;
136 best_file = file_name;
137 }
138 }
139 file_name = dir->_get_next();
140 }
141
142 if (!best_file.is_empty()) {
143 String library_path = folder.path_join(best_file);
144 if (r_tags != nullptr) {
145 r_tags->append_array(best_file_tags);
146 }
147 return library_path;
148 }
149 }
150 return String();
151}
152
153class GDExtensionMethodBind : public MethodBind {
154 GDExtensionClassMethodCall call_func;
155 GDExtensionClassMethodValidatedCall validated_call_func;
156 GDExtensionClassMethodPtrCall ptrcall_func;
157 void *method_userdata;
158 bool vararg;
159 uint32_t argument_count;
160 PropertyInfo return_value_info;
161 GodotTypeInfo::Metadata return_value_metadata;
162 List<PropertyInfo> arguments_info;
163 List<GodotTypeInfo::Metadata> arguments_metadata;
164
165protected:
166 virtual Variant::Type _gen_argument_type(int p_arg) const override {
167 if (p_arg < 0) {
168 return return_value_info.type;
169 } else {
170 return arguments_info[p_arg].type;
171 }
172 }
173 virtual PropertyInfo _gen_argument_type_info(int p_arg) const override {
174 if (p_arg < 0) {
175 return return_value_info;
176 } else {
177 return arguments_info[p_arg];
178 }
179 }
180
181public:
182#ifdef DEBUG_METHODS_ENABLED
183 virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const override {
184 if (p_arg < 0) {
185 return return_value_metadata;
186 } else {
187 return arguments_metadata[p_arg];
188 }
189 }
190#endif
191
192 virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
193 Variant ret;
194 GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance();
195 GDExtensionCallError ce{ GDEXTENSION_CALL_OK, 0, 0 };
196 call_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstVariantPtr *>(p_args), p_arg_count, (GDExtensionVariantPtr)&ret, &ce);
197 r_error.error = Callable::CallError::Error(ce.error);
198 r_error.argument = ce.argument;
199 r_error.expected = ce.expected;
200 return ret;
201 }
202 virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
203 ERR_FAIL_COND_MSG(vararg, "Validated methods don't have ptrcall support. This is most likely an engine bug.");
204 GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance();
205
206 if (validated_call_func) {
207 // This is added here, but it's unlikely to be provided by most extensions.
208 validated_call_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstVariantPtr *>(p_args), (GDExtensionVariantPtr)r_ret);
209 } else {
210#if 1
211 // Slow code-path, but works for the time being.
212 Callable::CallError ce;
213 call(p_object, p_args, argument_count, ce);
214#else
215 // This is broken, because it needs more information to do the calling properly
216
217 // If not provided, go via ptrcall, which is faster than resorting to regular call.
218 const void **argptrs = (const void **)alloca(argument_count * sizeof(void *));
219 for (uint32_t i = 0; i < argument_count; i++) {
220 argptrs[i] = VariantInternal::get_opaque_pointer(p_args[i]);
221 }
222
223 bool returns = true;
224 void *ret_opaque;
225 if (returns) {
226 ret_opaque = VariantInternal::get_opaque_pointer(r_ret);
227 } else {
228 ret_opaque = nullptr; // May be unnecessary as this is ignored, but just in case.
229 }
230
231 ptrcall(p_object, argptrs, ret_opaque);
232#endif
233 }
234 }
235
236 virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
237 ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug.");
238 GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance();
239 ptrcall_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstTypePtr *>(p_args), (GDExtensionTypePtr)r_ret);
240 }
241
242 virtual bool is_vararg() const override {
243 return false;
244 }
245
246 explicit GDExtensionMethodBind(const GDExtensionClassMethodInfo *p_method_info) {
247 method_userdata = p_method_info->method_userdata;
248 call_func = p_method_info->call_func;
249 validated_call_func = nullptr;
250 ptrcall_func = p_method_info->ptrcall_func;
251 set_name(*reinterpret_cast<StringName *>(p_method_info->name));
252
253 if (p_method_info->has_return_value) {
254 return_value_info = PropertyInfo(*p_method_info->return_value_info);
255 return_value_metadata = GodotTypeInfo::Metadata(p_method_info->return_value_metadata);
256 }
257
258 for (uint32_t i = 0; i < p_method_info->argument_count; i++) {
259 arguments_info.push_back(PropertyInfo(p_method_info->arguments_info[i]));
260 arguments_metadata.push_back(GodotTypeInfo::Metadata(p_method_info->arguments_metadata[i]));
261 }
262
263 set_hint_flags(p_method_info->method_flags);
264 argument_count = p_method_info->argument_count;
265 vararg = p_method_info->method_flags & GDEXTENSION_METHOD_FLAG_VARARG;
266 _set_returns(p_method_info->has_return_value);
267 _set_const(p_method_info->method_flags & GDEXTENSION_METHOD_FLAG_CONST);
268 _set_static(p_method_info->method_flags & GDEXTENSION_METHOD_FLAG_STATIC);
269#ifdef DEBUG_METHODS_ENABLED
270 _generate_argument_types(p_method_info->argument_count);
271#endif
272 set_argument_count(p_method_info->argument_count);
273
274 Vector<Variant> defargs;
275 defargs.resize(p_method_info->default_argument_count);
276 for (uint32_t i = 0; i < p_method_info->default_argument_count; i++) {
277 defargs.write[i] = *static_cast<Variant *>(p_method_info->default_arguments[i]);
278 }
279
280 set_default_arguments(defargs);
281 }
282};
283
284#ifndef DISABLE_DEPRECATED
285void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) {
286 const GDExtensionClassCreationInfo2 class_info2 = {
287 p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
288 p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
289 true, // GDExtensionBool is_exposed;
290 p_extension_funcs->set_func, // GDExtensionClassSet set_func;
291 p_extension_funcs->get_func, // GDExtensionClassGet get_func;
292 p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
293 p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
294 p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
295 p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
296 nullptr, // GDExtensionClassValidateProperty validate_property_func;
297 nullptr, // GDExtensionClassNotification2 notification_func;
298 p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
299 p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
300 p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
301 p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
302 p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
303 p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
304 p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
305 p_extension_funcs->class_userdata, // void *class_userdata;
306 };
307
308 const ClassCreationDeprecatedInfo legacy = {
309 p_extension_funcs->notification_func,
310 };
311 _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info2, &legacy);
312}
313#endif // DISABLE_DEPRECATED
314
315void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs) {
316 _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs);
317}
318
319void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) {
320 GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
321
322 StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
323 StringName parent_class_name = *reinterpret_cast<const StringName *>(p_parent_class_name);
324 ERR_FAIL_COND_MSG(!String(class_name).is_valid_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier.");
325 ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered.");
326
327 Extension *parent_extension = nullptr;
328
329 if (self->extension_classes.has(parent_class_name)) {
330 parent_extension = &self->extension_classes[parent_class_name];
331 } else if (ClassDB::class_exists(parent_class_name)) {
332 if (ClassDB::get_api_type(parent_class_name) == ClassDB::API_EXTENSION || ClassDB::get_api_type(parent_class_name) == ClassDB::API_EDITOR_EXTENSION) {
333 ERR_PRINT("Unimplemented yet");
334 //inheriting from another extension
335 } else {
336 //inheriting from engine class
337 }
338 } else {
339 ERR_FAIL_MSG("Attempt to register an extension class '" + String(class_name) + "' using non-existing parent class '" + String(parent_class_name) + "'");
340 }
341
342 self->extension_classes[class_name] = Extension();
343
344 Extension *extension = &self->extension_classes[class_name];
345
346 if (parent_extension) {
347 extension->gdextension.parent = &parent_extension->gdextension;
348 parent_extension->gdextension.children.push_back(&extension->gdextension);
349 }
350
351 extension->gdextension.library = self;
352 extension->gdextension.parent_class_name = parent_class_name;
353 extension->gdextension.class_name = class_name;
354 extension->gdextension.editor_class = self->level_initialized == INITIALIZATION_LEVEL_EDITOR;
355 extension->gdextension.is_virtual = p_extension_funcs->is_virtual;
356 extension->gdextension.is_abstract = p_extension_funcs->is_abstract;
357 extension->gdextension.is_exposed = p_extension_funcs->is_exposed;
358 extension->gdextension.set = p_extension_funcs->set_func;
359 extension->gdextension.get = p_extension_funcs->get_func;
360 extension->gdextension.get_property_list = p_extension_funcs->get_property_list_func;
361 extension->gdextension.free_property_list = p_extension_funcs->free_property_list_func;
362 extension->gdextension.property_can_revert = p_extension_funcs->property_can_revert_func;
363 extension->gdextension.property_get_revert = p_extension_funcs->property_get_revert_func;
364 extension->gdextension.validate_property = p_extension_funcs->validate_property_func;
365#ifndef DISABLE_DEPRECATED
366 if (p_deprecated_funcs) {
367 extension->gdextension.notification = p_deprecated_funcs->notification_func;
368 }
369#endif // DISABLE_DEPRECATED
370 extension->gdextension.notification2 = p_extension_funcs->notification_func;
371 extension->gdextension.to_string = p_extension_funcs->to_string_func;
372 extension->gdextension.reference = p_extension_funcs->reference_func;
373 extension->gdextension.unreference = p_extension_funcs->unreference_func;
374 extension->gdextension.class_userdata = p_extension_funcs->class_userdata;
375 extension->gdextension.create_instance = p_extension_funcs->create_instance_func;
376 extension->gdextension.free_instance = p_extension_funcs->free_instance_func;
377 extension->gdextension.get_virtual = p_extension_funcs->get_virtual_func;
378 extension->gdextension.get_rid = p_extension_funcs->get_rid_func;
379
380 ClassDB::register_extension_class(&extension->gdextension);
381}
382
383void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info) {
384 GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
385
386 StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
387 StringName method_name = *reinterpret_cast<const StringName *>(p_method_info->name);
388 ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension method '" + String(method_name) + "' for unexisting class '" + class_name + "'.");
389
390 //Extension *extension = &self->extension_classes[class_name];
391
392 GDExtensionMethodBind *method = memnew(GDExtensionMethodBind(p_method_info));
393 method->set_instance_class(class_name);
394
395 ClassDB::bind_method_custom(class_name, method);
396}
397void GDExtension::_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) {
398 GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
399
400 StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
401 StringName enum_name = *reinterpret_cast<const StringName *>(p_enum_name);
402 StringName constant_name = *reinterpret_cast<const StringName *>(p_constant_name);
403 ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension constant '" + constant_name + "' for unexisting class '" + class_name + "'.");
404
405 ClassDB::bind_integer_constant(class_name, enum_name, constant_name, p_constant_value, p_is_bitfield);
406}
407
408void GDExtension::_register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter) {
409 _register_extension_class_property_indexed(p_library, p_class_name, p_info, p_setter, p_getter, -1);
410}
411
412void GDExtension::_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) {
413 GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
414
415 StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
416 StringName setter = *reinterpret_cast<const StringName *>(p_setter);
417 StringName getter = *reinterpret_cast<const StringName *>(p_getter);
418 String property_name = *reinterpret_cast<const StringName *>(p_info->name);
419 ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property '" + property_name + "' for unexisting class '" + class_name + "'.");
420
421 PropertyInfo pinfo(*p_info);
422
423 ClassDB::add_property(class_name, pinfo, setter, getter, p_index);
424}
425
426void GDExtension::_register_extension_class_property_group(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_group_name, GDExtensionConstStringPtr p_prefix) {
427 GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
428
429 StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
430 String group_name = *reinterpret_cast<const String *>(p_group_name);
431 String prefix = *reinterpret_cast<const String *>(p_prefix);
432 ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property group '" + group_name + "' for unexisting class '" + class_name + "'.");
433
434 ClassDB::add_property_group(class_name, group_name, prefix);
435}
436
437void GDExtension::_register_extension_class_property_subgroup(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_subgroup_name, GDExtensionConstStringPtr p_prefix) {
438 GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
439
440 StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
441 String subgroup_name = *reinterpret_cast<const String *>(p_subgroup_name);
442 String prefix = *reinterpret_cast<const String *>(p_prefix);
443 ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property subgroup '" + subgroup_name + "' for unexisting class '" + class_name + "'.");
444
445 ClassDB::add_property_subgroup(class_name, subgroup_name, prefix);
446}
447
448void GDExtension::_register_extension_class_signal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_signal_name, const GDExtensionPropertyInfo *p_argument_info, GDExtensionInt p_argument_count) {
449 GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
450
451 StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
452 StringName signal_name = *reinterpret_cast<const StringName *>(p_signal_name);
453 ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class signal '" + signal_name + "' for unexisting class '" + class_name + "'.");
454
455 MethodInfo s;
456 s.name = signal_name;
457 for (int i = 0; i < p_argument_count; i++) {
458 PropertyInfo arg(p_argument_info[i]);
459 s.arguments.push_back(arg);
460 }
461 ClassDB::add_signal(class_name, s);
462}
463
464void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name) {
465 GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
466
467 StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
468 ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to unregister unexisting extension class '" + class_name + "'.");
469 Extension *ext = &self->extension_classes[class_name];
470 ERR_FAIL_COND_MSG(ext->gdextension.children.size(), "Attempt to unregister class '" + class_name + "' while other extension classes inherit from it.");
471
472 ClassDB::unregister_extension_class(class_name);
473 if (ext->gdextension.parent != nullptr) {
474 ext->gdextension.parent->children.erase(&ext->gdextension);
475 }
476 self->extension_classes.erase(class_name);
477}
478
479void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path) {
480 GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
481
482 memnew_placement(r_path, String(self->library_path));
483}
484
485HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions;
486
487void GDExtension::register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) {
488 ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), "Attempt to register interface function '" + p_function_name + "', which appears to be already registered.");
489 gdextension_interface_functions.insert(p_function_name, p_function_pointer);
490}
491
492GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(StringName p_function_name) {
493 GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name);
494 ERR_FAIL_NULL_V_MSG(function, nullptr, "Attempt to get non-existent interface function: " + String(p_function_name) + ".");
495 return *function;
496}
497
498Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol) {
499 Error err = OS::get_singleton()->open_dynamic_library(p_path, library, true, &library_path);
500 if (err != OK) {
501 ERR_PRINT("GDExtension dynamic library not found: " + p_path);
502 return err;
503 }
504
505 void *entry_funcptr = nullptr;
506
507 err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false);
508
509 if (err != OK) {
510 ERR_PRINT("GDExtension entry point '" + p_entry_symbol + "' not found in library " + p_path);
511 OS::get_singleton()->close_dynamic_library(library);
512 return err;
513 }
514
515 GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr;
516 GDExtensionBool ret = initialization_function(&gdextension_get_proc_address, this, &initialization);
517
518 if (ret) {
519 level_initialized = -1;
520 return OK;
521 } else {
522 ERR_PRINT("GDExtension initialization function '" + p_entry_symbol + "' returned an error.");
523 return FAILED;
524 }
525}
526
527void GDExtension::close_library() {
528 ERR_FAIL_NULL(library);
529 OS::get_singleton()->close_dynamic_library(library);
530
531#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED)
532 // Delete temporary copy of library if it exists.
533 if (!temp_lib_path.is_empty() && Engine::get_singleton()->is_editor_hint()) {
534 DirAccess::remove_absolute(temp_lib_path);
535 }
536#endif
537
538 library = nullptr;
539}
540
541bool GDExtension::is_library_open() const {
542 return library != nullptr;
543}
544
545GDExtension::InitializationLevel GDExtension::get_minimum_library_initialization_level() const {
546 ERR_FAIL_NULL_V(library, INITIALIZATION_LEVEL_CORE);
547 return InitializationLevel(initialization.minimum_initialization_level);
548}
549
550void GDExtension::initialize_library(InitializationLevel p_level) {
551 ERR_FAIL_NULL(library);
552 ERR_FAIL_COND_MSG(p_level <= int32_t(level_initialized), vformat("Level '%d' must be higher than the current level '%d'", p_level, level_initialized));
553
554 level_initialized = int32_t(p_level);
555
556 ERR_FAIL_COND(initialization.initialize == nullptr);
557
558 initialization.initialize(initialization.userdata, GDExtensionInitializationLevel(p_level));
559}
560void GDExtension::deinitialize_library(InitializationLevel p_level) {
561 ERR_FAIL_NULL(library);
562 ERR_FAIL_COND(p_level > int32_t(level_initialized));
563
564 level_initialized = int32_t(p_level) - 1;
565 initialization.deinitialize(initialization.userdata, GDExtensionInitializationLevel(p_level));
566}
567
568void GDExtension::_bind_methods() {
569 ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol"), &GDExtension::open_library);
570 ClassDB::bind_method(D_METHOD("close_library"), &GDExtension::close_library);
571 ClassDB::bind_method(D_METHOD("is_library_open"), &GDExtension::is_library_open);
572
573 ClassDB::bind_method(D_METHOD("get_minimum_library_initialization_level"), &GDExtension::get_minimum_library_initialization_level);
574 ClassDB::bind_method(D_METHOD("initialize_library", "level"), &GDExtension::initialize_library);
575
576 BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_CORE);
577 BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_SERVERS);
578 BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_SCENE);
579 BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_EDITOR);
580}
581
582GDExtension::GDExtension() {
583}
584
585GDExtension::~GDExtension() {
586 if (library != nullptr) {
587 close_library();
588 }
589}
590
591void GDExtension::initialize_gdextensions() {
592 gdextension_setup_interface();
593
594#ifndef DISABLE_DEPRECATED
595 register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class);
596#endif // DISABLE_DEPRECATED
597 register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2);
598 register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method);
599 register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant);
600 register_interface_function("classdb_register_extension_class_property", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property);
601 register_interface_function("classdb_register_extension_class_property_indexed", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_indexed);
602 register_interface_function("classdb_register_extension_class_property_group", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_group);
603 register_interface_function("classdb_register_extension_class_property_subgroup", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_subgroup);
604 register_interface_function("classdb_register_extension_class_signal", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_signal);
605 register_interface_function("classdb_unregister_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_unregister_extension_class);
606 register_interface_function("get_library_path", (GDExtensionInterfaceFunctionPtr)&GDExtension::_get_library_path);
607}
608
609Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
610 Ref<ConfigFile> config;
611 config.instantiate();
612
613 Error err = config->load(p_path);
614
615 if (r_error) {
616 *r_error = err;
617 }
618
619 if (err != OK) {
620 ERR_PRINT("Error loading GDExtension configuration file: " + p_path);
621 return Ref<Resource>();
622 }
623
624 if (!config->has_section_key("configuration", "entry_symbol")) {
625 if (r_error) {
626 *r_error = ERR_INVALID_DATA;
627 }
628 ERR_PRINT("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: " + p_path);
629 return Ref<Resource>();
630 }
631
632 String entry_symbol = config->get_value("configuration", "entry_symbol");
633
634 uint32_t compatibility_minimum[3] = { 0, 0, 0 };
635 if (config->has_section_key("configuration", "compatibility_minimum")) {
636 String compat_string = config->get_value("configuration", "compatibility_minimum");
637 Vector<int> parts = compat_string.split_ints(".");
638 for (int i = 0; i < parts.size(); i++) {
639 if (i >= 3) {
640 break;
641 }
642 if (parts[i] >= 0) {
643 compatibility_minimum[i] = parts[i];
644 }
645 }
646 } else {
647 if (r_error) {
648 *r_error = ERR_INVALID_DATA;
649 }
650 ERR_PRINT("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: " + p_path);
651 return Ref<Resource>();
652 }
653
654 if (compatibility_minimum[0] < 4 || (compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0)) {
655 if (r_error) {
656 *r_error = ERR_INVALID_DATA;
657 }
658 ERR_PRINT(vformat("GDExtension's compatibility_minimum (%d.%d.%d) must be at least 4.1.0: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
659 return Ref<Resource>();
660 }
661
662 bool compatible = true;
663 // Check version lexicographically.
664 if (VERSION_MAJOR != compatibility_minimum[0]) {
665 compatible = VERSION_MAJOR > compatibility_minimum[0];
666 } else if (VERSION_MINOR != compatibility_minimum[1]) {
667 compatible = VERSION_MINOR > compatibility_minimum[1];
668 } else {
669 compatible = VERSION_PATCH >= compatibility_minimum[2];
670 }
671 if (!compatible) {
672 if (r_error) {
673 *r_error = ERR_INVALID_DATA;
674 }
675 ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path));
676 return Ref<Resource>();
677 }
678
679 String library_path = GDExtension::find_extension_library(p_path, config, [](String p_feature) { return OS::get_singleton()->has_feature(p_feature); });
680
681 if (library_path.is_empty()) {
682 if (r_error) {
683 *r_error = ERR_FILE_NOT_FOUND;
684 }
685 const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name();
686 ERR_PRINT(vformat("No GDExtension library found for current OS and architecture (%s) in configuration file: %s", os_arch, p_path));
687 return Ref<Resource>();
688 }
689
690 if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
691 library_path = p_path.get_base_dir().path_join(library_path);
692 }
693
694 Ref<GDExtension> lib;
695 lib.instantiate();
696 String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path);
697
698#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
699 // If running on the editor on Windows, we copy the library and open the copy.
700 // This is so the original file isn't locked and can be updated by a compiler.
701 if (Engine::get_singleton()->is_editor_hint()) {
702 if (!FileAccess::exists(abs_path)) {
703 if (r_error) {
704 *r_error = ERR_FILE_NOT_FOUND;
705 }
706 ERR_PRINT("GDExtension library not found: " + library_path);
707 return Ref<Resource>();
708 }
709
710 // Copy the file to the same directory as the original with a prefix in the name.
711 // This is so relative path to dependencies are satisfied.
712 String copy_path = abs_path.get_base_dir().path_join("~" + abs_path.get_file());
713
714 // If there's a left-over copy (possibly from a crash) then delete it first.
715 if (FileAccess::exists(copy_path)) {
716 DirAccess::remove_absolute(copy_path);
717 }
718
719 Error copy_err = DirAccess::copy_absolute(abs_path, copy_path);
720 if (copy_err) {
721 if (r_error) {
722 *r_error = ERR_CANT_CREATE;
723 }
724 ERR_PRINT("Error copying GDExtension library: " + library_path);
725 return Ref<Resource>();
726 }
727 FileAccess::set_hidden_attribute(copy_path, true);
728
729 // Save the copied path so it can be deleted later.
730 lib->set_temp_library_path(copy_path);
731
732 // Use the copy to open the library.
733 abs_path = copy_path;
734 }
735#endif
736 err = lib->open_library(abs_path, entry_symbol);
737
738 if (r_error) {
739 *r_error = err;
740 }
741
742 if (err != OK) {
743#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
744 // If the DLL fails to load, make sure that temporary DLL copies are cleaned up.
745 if (Engine::get_singleton()->is_editor_hint()) {
746 DirAccess::remove_absolute(lib->get_temp_library_path());
747 }
748#endif
749 // Errors already logged in open_library()
750 return Ref<Resource>();
751 }
752
753 // Handle icons if any are specified.
754 if (config->has_section("icons")) {
755 List<String> keys;
756 config->get_section_keys("icons", &keys);
757 for (const String &key : keys) {
758 lib->class_icon_paths[key] = config->get_value("icons", key);
759 }
760 }
761
762 return lib;
763}
764
765void GDExtensionResourceLoader::get_recognized_extensions(List<String> *p_extensions) const {
766 p_extensions->push_back("gdextension");
767}
768
769bool GDExtensionResourceLoader::handles_type(const String &p_type) const {
770 return p_type == "GDExtension";
771}
772
773String GDExtensionResourceLoader::get_resource_type(const String &p_path) const {
774 String el = p_path.get_extension().to_lower();
775 if (el == "gdextension") {
776 return "GDExtension";
777 }
778 return "";
779}
780
781#ifdef TOOLS_ENABLED
782Vector<StringName> GDExtensionEditorPlugins::extension_classes;
783GDExtensionEditorPlugins::EditorPluginRegisterFunc GDExtensionEditorPlugins::editor_node_add_plugin = nullptr;
784GDExtensionEditorPlugins::EditorPluginRegisterFunc GDExtensionEditorPlugins::editor_node_remove_plugin = nullptr;
785
786void GDExtensionEditorPlugins::add_extension_class(const StringName &p_class_name) {
787 if (editor_node_add_plugin) {
788 editor_node_add_plugin(p_class_name);
789 } else {
790 extension_classes.push_back(p_class_name);
791 }
792}
793
794void GDExtensionEditorPlugins::remove_extension_class(const StringName &p_class_name) {
795 if (editor_node_remove_plugin) {
796 editor_node_remove_plugin(p_class_name);
797 } else {
798 extension_classes.erase(p_class_name);
799 }
800}
801#endif // TOOLS_ENABLED
802