diff options
Diffstat (limited to 'core/extension')
-rw-r--r-- | core/extension/SCsub | 7 | ||||
-rw-r--r-- | core/extension/extension_api_dump.cpp | 805 | ||||
-rw-r--r-- | core/extension/extension_api_dump.h | 45 | ||||
-rw-r--r-- | core/extension/gdnative_interface.cpp | 688 | ||||
-rw-r--r-- | core/extension/gdnative_interface.h | 427 | ||||
-rw-r--r-- | core/extension/native_extension.cpp | 411 | ||||
-rw-r--r-- | core/extension/native_extension.h | 94 | ||||
-rw-r--r-- | core/extension/native_extension_manager.cpp | 130 | ||||
-rw-r--r-- | core/extension/native_extension_manager.h | 71 |
9 files changed, 2678 insertions, 0 deletions
diff --git a/core/extension/SCsub b/core/extension/SCsub new file mode 100644 index 0000000000..a3a54250c1 --- /dev/null +++ b/core/extension/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import("env") + +env_extension = env.Clone() + +env_extension.add_source_files(env.core_sources, "*.cpp") diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp new file mode 100644 index 0000000000..3c132a619d --- /dev/null +++ b/core/extension/extension_api_dump.cpp @@ -0,0 +1,805 @@ +/*************************************************************************/ +/* extension_api_dump.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "extension_api_dump.h" +#include "core/config/engine.h" +#include "core/core_constants.h" +#include "core/io/file_access.h" +#include "core/io/json.h" +#include "core/templates/pair.h" +#include "core/version.h" + +#ifdef TOOLS_ENABLED + +Dictionary NativeExtensionAPIDump::generate_extension_api() { + Dictionary api_dump; + + { + //header + Dictionary header; + header["version_major"] = VERSION_MAJOR; + header["version_minor"] = VERSION_MINOR; +#if VERSION_PATCH + header["version_patch"] = VERSION_PATCH; +#else + header["version_patch"] = 0; +#endif + header["version_status"] = VERSION_STATUS; + header["version_build"] = VERSION_BUILD; + header["version_full_name"] = VERSION_FULL_NAME; + + api_dump["header"] = header; + } + + const uint32_t vec3_elems = 3; + const uint32_t ptrsize_32 = 4; + const uint32_t ptrsize_64 = 4; + static const char *build_config_name[4] = { "float_32", "float_64", "double_32", "double_64" }; + + { + //type sizes + struct { + Variant::Type type; + uint32_t size_32_bits_real_float; + uint32_t size_64_bits_real_float; + uint32_t size_32_bits_real_double; + uint32_t size_64_bits_real_double; + } type_size_array[Variant::VARIANT_MAX + 1] = { + { Variant::NIL, 0, 0, 0, 0 }, + { Variant::BOOL, sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t) }, + { Variant::INT, sizeof(int64_t), sizeof(int64_t), sizeof(int64_t), sizeof(int64_t) }, + { Variant::FLOAT, sizeof(double), sizeof(double), sizeof(double), sizeof(double) }, + { Variant::STRING, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::VECTOR2, 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, + { Variant::VECTOR2I, 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) }, + { Variant::RECT2, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) }, + { Variant::RECT2I, 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t) }, + { Variant::VECTOR3, vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, + { Variant::VECTOR3I, 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t) }, + { Variant::TRANSFORM2D, 6 * sizeof(float), 6 * sizeof(float), 6 * sizeof(double), 6 * sizeof(double) }, + { Variant::PLANE, (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(double), (vec3_elems + 1) * sizeof(double) }, + { Variant::QUATERNION, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) }, + { Variant::AABB, (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(double), (vec3_elems * 2) * sizeof(double) }, + { Variant::BASIS, (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(double), (vec3_elems * 3) * sizeof(double) }, + { Variant::TRANSFORM3D, (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(double), (vec3_elems * 4) * sizeof(double) }, + { Variant::COLOR, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float) }, + { Variant::STRING_NAME, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::NODE_PATH, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::RID, sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t) }, + { Variant::OBJECT, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::CALLABLE, sizeof(Callable), sizeof(Callable), sizeof(Callable), sizeof(Callable) }, //harcoded align + { Variant::SIGNAL, sizeof(Signal), sizeof(Signal), sizeof(Signal), sizeof(Signal) }, //harcoded align + { Variant::DICTIONARY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_BYTE_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_INT32_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_INT64_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_FLOAT32_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_FLOAT64_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_STRING_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_VECTOR2_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_VECTOR3_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_COLOR_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::VARIANT_MAX, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(double) * 4, sizeof(uint64_t) + sizeof(double) * 4 }, + }; + + Array core_type_sizes; + + for (int i = 0; i < 4; i++) { + Dictionary d; + d["build_configuration"] = build_config_name[i]; + Array sizes; + for (int j = 0; j < Variant::VARIANT_MAX; j++) { + Variant::Type t = type_size_array[j].type; + String name = t == Variant::VARIANT_MAX ? String("Variant") : Variant::get_type_name(t); + Dictionary d2; + d2["name"] = name; + uint32_t size; + switch (i) { + case 0: + size = type_size_array[j].size_32_bits_real_float; + break; + case 1: + size = type_size_array[j].size_64_bits_real_float; + break; + case 2: + size = type_size_array[j].size_32_bits_real_double; + break; + case 3: + size = type_size_array[j].size_64_bits_real_double; + break; + } + d2["size"] = size; + sizes.push_back(d2); + } + d["sizes"] = sizes; + core_type_sizes.push_back(d); + } + api_dump["builtin_class_sizes"] = core_type_sizes; + } + + { + //member offsets sizes + struct { + Variant::Type type; + const char *member; + uint32_t offset_32_bits_real_float; + uint32_t offset_64_bits_real_float; + uint32_t offset_32_bits_real_double; + uint32_t offset_64_bits_real_double; + } member_offset_array[] = { + { Variant::VECTOR2, "x", 0, 0, 0, 0 }, + { Variant::VECTOR2, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) }, + { Variant::VECTOR2I, "x", 0, 0, 0, 0 }, + { Variant::VECTOR2I, "y", sizeof(int32_t), sizeof(int32_t), sizeof(int32_t), sizeof(int32_t) }, + { Variant::RECT2, "position", 0, 0, 0, 0 }, + { Variant::RECT2, "size", 2 * sizeof(Vector2), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, + { Variant::RECT2I, "position", 0, 0, 0, 0 }, + { Variant::RECT2I, "size", 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) }, + { Variant::VECTOR3, "x", 0, 0, 0, 0 }, + { Variant::VECTOR3, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) }, + { Variant::VECTOR3, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, + { Variant::VECTOR3I, "x", 0, 0, 0, 0 }, + { Variant::VECTOR3I, "y", sizeof(int32_t), sizeof(int32_t), sizeof(int32_t), sizeof(int32_t) }, + { Variant::VECTOR3I, "z", 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) }, + { Variant::TRANSFORM2D, "x", 0, 0, 0, 0 }, + { Variant::TRANSFORM2D, "y", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, + { Variant::TRANSFORM2D, "origin", 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) }, + { Variant::PLANE, "normal", 0, 0, 0, 0 }, + { Variant::PLANE, "d", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, + { Variant::QUATERNION, "x", 0, 0, 0, 0 }, + { Variant::QUATERNION, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) }, + { Variant::QUATERNION, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, + { Variant::QUATERNION, "w", 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(double), 3 * sizeof(double) }, + { Variant::AABB, "position", 0, 0, 0, 0 }, + { Variant::AABB, "size", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, + //rememer that basis vectors are flipped! + { Variant::BASIS, "x", 0, 0, 0, 0 }, + { Variant::BASIS, "y", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, + { Variant::BASIS, "z", vec3_elems * 2 * sizeof(float), vec3_elems * 2 * sizeof(float), vec3_elems * 2 * sizeof(double), vec3_elems * 2 * sizeof(double) }, + { Variant::TRANSFORM3D, "basis", 0, 0, 0, 0 }, + { Variant::TRANSFORM3D, "origin", (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(double), (vec3_elems * 3) * sizeof(double) }, + { Variant::COLOR, "x", 0, 0, 0, 0 }, + { Variant::COLOR, "y", sizeof(float), sizeof(float), sizeof(float), sizeof(float) }, + { Variant::COLOR, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(float) }, + { Variant::COLOR, "w", 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(float) }, + { Variant::NIL, nullptr, 0, 0, 0, 0 }, + }; + + Array core_type_member_offsets; + + for (int i = 0; i < 4; i++) { + Dictionary d; + d["build_configuration"] = build_config_name[i]; + Array type_offsets; + uint32_t idx = 0; + + Variant::Type last_type = Variant::NIL; + + Dictionary d2; + Array members; + + while (true) { + Variant::Type t = member_offset_array[idx].type; + if (t != last_type) { + if (last_type != Variant::NIL) { + d2["members"] = members; + type_offsets.push_back(d2); + } + if (t == Variant::NIL) { + break; + } + + String name = t == Variant::VARIANT_MAX ? String("Variant") : Variant::get_type_name(t); + d2 = Dictionary(); + members = Array(); + d2["name"] = name; + last_type = t; + } + Dictionary d3; + uint32_t offset; + switch (i) { + case 0: + offset = member_offset_array[idx].offset_32_bits_real_float; + break; + case 1: + offset = member_offset_array[idx].offset_64_bits_real_float; + break; + case 2: + offset = member_offset_array[idx].offset_32_bits_real_double; + break; + case 3: + offset = member_offset_array[idx].offset_64_bits_real_double; + break; + } + d3["member"] = member_offset_array[idx].member; + d3["offset"] = offset; + members.push_back(d3); + idx++; + } + d["classes"] = type_offsets; + core_type_member_offsets.push_back(d); + } + api_dump["builtin_class_member_offsets"] = core_type_member_offsets; + } + + { + // global enums and constants + Array constants; + Map<String, List<Pair<String, int>>> enum_list; + + for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) { + int value = CoreConstants::get_global_constant_value(i); + String enum_name = CoreConstants::get_global_constant_enum(i); + String name = CoreConstants::get_global_constant_name(i); + if (enum_name != String()) { + enum_list[enum_name].push_back(Pair<String, int>(name, value)); + } else { + Dictionary d; + d["name"] = name; + d["value"] = value; + constants.push_back(d); + } + } + + api_dump["global_constants"] = constants; + + Array enums; + for (Map<String, List<Pair<String, int>>>::Element *E = enum_list.front(); E; E = E->next()) { + Dictionary d1; + d1["name"] = E->key(); + Array values; + for (List<Pair<String, int>>::Element *F = E->get().front(); F; F = F->next()) { + Dictionary d2; + d2["name"] = F->get().first; + d2["value"] = F->get().second; + values.push_back(d2); + } + d1["values"] = values; + enums.push_back(d1); + } + + api_dump["global_enums"] = enums; + } + { + Array utility_funcs; + + List<StringName> utility_func_names; + Variant::get_utility_function_list(&utility_func_names); + + for (List<StringName>::Element *E = utility_func_names.front(); E; E = E->next()) { + StringName name = E->get(); + Dictionary func; + func["name"] = String(name); + if (Variant::has_utility_function_return_value(name)) { + Variant::Type rt = Variant::get_utility_function_return_type(name); + func["return_type"] = rt == Variant::NIL ? String("Variant") : Variant::get_type_name(rt); + } + switch (Variant::get_utility_function_type(name)) { + case Variant::UTILITY_FUNC_TYPE_MATH: + func["category"] = "math"; + break; + case Variant::UTILITY_FUNC_TYPE_RANDOM: + func["category"] = "random"; + break; + case Variant::UTILITY_FUNC_TYPE_GENERAL: + func["category"] = "general"; + break; + } + bool vararg = Variant::is_utility_function_vararg(name); + func["is_vararg"] = Variant::is_utility_function_vararg(name); + func["hash"] = Variant::get_utility_function_hash(name); + Array arguments; + int argcount = Variant::get_utility_function_argument_count(name); + for (int i = 0; i < argcount; i++) { + Dictionary arg; + String argname = vararg ? "arg" + itos(i + 1) : Variant::get_utility_function_argument_name(name, i); + arg["name"] = argname; + Variant::Type argtype = Variant::get_utility_function_argument_type(name, i); + arg["type"] = argtype == Variant::NIL ? String("Variant") : Variant::get_type_name(argtype); + //no default value support in utility functions + arguments.push_back(arg); + } + + if (arguments.size()) { + func["arguments"] = arguments; + } + + utility_funcs.push_back(func); + } + + api_dump["utility_functions"] = utility_funcs; + } + + { + // builtin types + + Array builtins; + + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (i == Variant::OBJECT) { + continue; + } + + Variant::Type type = Variant::Type(i); + + Dictionary d; + d["name"] = Variant::get_type_name(type); + if (Variant::has_indexing(type)) { + Variant::Type index_type = Variant::get_indexed_element_type(type); + d["indexing_return_type"] = index_type == Variant::NIL ? String("Variant") : Variant::get_type_name(index_type); + } + + { + //members + Array members; + + List<StringName> member_names; + Variant::get_member_list(type, &member_names); + for (List<StringName>::Element *E = member_names.front(); E; E = E->next()) { + StringName member_name = E->get(); + Dictionary d2; + d2["name"] = String(member_name); + d2["type"] = Variant::get_type_name(Variant::get_member_type(type, member_name)); + members.push_back(d2); + } + if (members.size()) { + d["members"] = members; + } + } + { + //constants + Array constants; + + List<StringName> constant_names; + Variant::get_constants_for_type(type, &constant_names); + for (List<StringName>::Element *E = constant_names.front(); E; E = E->next()) { + StringName constant_name = E->get(); + Dictionary d2; + d2["name"] = String(constant_name); + Variant constant = Variant::get_constant_value(type, constant_name); + d2["type"] = Variant::get_type_name(constant.get_type()); + d2["value"] = constant.get_construct_string(); + constants.push_back(d2); + } + if (constants.size()) { + d["constants"] = constants; + } + } + { + //operators + Array operators; + + for (int j = 0; j < Variant::VARIANT_MAX; j++) { + for (int k = 0; k < Variant::OP_MAX; k++) { + Variant::Type rt = Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j)); + if (rt != Variant::NIL) { + Dictionary d2; + d2["name"] = Variant::get_operator_name(Variant::Operator(k)); + if (k != Variant::OP_NEGATE && k != Variant::OP_POSITIVE && k != Variant::OP_NOT && k != Variant::OP_BIT_NEGATE) { + d2["right_type"] = Variant::get_type_name(Variant::Type(j)); + } + operators.push_back(d2); + } + } + } + if (operators.size()) { + d["operators"] = operators; + } + } + { + //methods + Array methods; + + List<StringName> method_names; + Variant::get_builtin_method_list(type, &method_names); + for (List<StringName>::Element *E = method_names.front(); E; E = E->next()) { + StringName method_name = E->get(); + Dictionary d2; + d2["name"] = String(method_name); + if (Variant::has_builtin_method_return_value(type, method_name)) { + Variant::Type ret_type = Variant::get_builtin_method_return_type(type, method_name); + d2["return_type"] = ret_type == Variant::NIL ? String("Variant") : Variant::get_type_name(ret_type); + } + d2["is_vararg"] = Variant::is_builtin_method_vararg(type, method_name); + d2["is_const"] = Variant::is_builtin_method_const(type, method_name); + d2["is_static"] = Variant::is_builtin_method_static(type, method_name); + d2["hash"] = Variant::get_builtin_method_hash(type, method_name); + + Vector<Variant> default_args = Variant::get_builtin_method_default_arguments(type, method_name); + + Array arguments; + int argcount = Variant::get_builtin_method_argument_count(type, method_name); + for (int j = 0; j < argcount; j++) { + Dictionary d3; + d3["name"] = Variant::get_builtin_method_argument_name(type, method_name, j); + Variant::Type argtype = Variant::get_builtin_method_argument_type(type, method_name, j); + d3["type"] = argtype == Variant::NIL ? String("Variant") : Variant::get_type_name(argtype); + + if (j >= (argcount - default_args.size())) { + int dargidx = j - (argcount - default_args.size()); + d3["default_value"] = default_args[dargidx].get_construct_string(); + } + arguments.push_back(d3); + } + + if (arguments.size()) { + d2["arguments"] = arguments; + } + + methods.push_back(d2); + } + if (methods.size()) { + d["methods"] = methods; + } + } + { + //constructors + Array constructors; + + for (int j = 0; j < Variant::get_constructor_count(type); j++) { + Dictionary d2; + d2["index"] = j; + + Array arguments; + int argcount = Variant::get_constructor_argument_count(type, j); + for (int k = 0; k < argcount; k++) { + Dictionary d3; + d3["name"] = Variant::get_constructor_argument_name(type, j, k); + d3["type"] = Variant::get_type_name(Variant::get_constructor_argument_type(type, j, k)); + arguments.push_back(d3); + } + if (arguments.size()) { + d2["arguments"] = arguments; + } + constructors.push_back(d2); + } + + if (constructors.size()) { + d["constructors"] = constructors; + } + } + + builtins.push_back(d); + } + + api_dump["builtin_classes"] = builtins; + } + + { + // classes + Array classes; + + List<StringName> class_list; + + ClassDB::get_class_list(&class_list); + + class_list.sort_custom<StringName::AlphCompare>(); + + for (List<StringName>::Element *E = class_list.front(); E; E = E->next()) { + Dictionary d; + StringName class_name = E->get(); + d["name"] = String(class_name); + d["is_refcounted"] = ClassDB::is_parent_class(class_name, "RefCounted"); + d["is_instantiable"] = ClassDB::can_instantiate(class_name); + StringName parent_class = ClassDB::get_parent_class(class_name); + if (parent_class != StringName()) { + d["inherits"] = String(parent_class); + } + + { + ClassDB::APIType api = ClassDB::get_api_type(class_name); + static const char *api_type[5] = { "core", "editor", "extension", "editor_extension" }; + d["api_type"] = api_type[api]; + } + + { + //constants + Array constants; + List<String> constant_list; + ClassDB::get_integer_constant_list(class_name, &constant_list, true); + for (List<String>::Element *F = constant_list.front(); F; F = F->next()) { + StringName enum_name = ClassDB::get_integer_constant_enum(class_name, F->get()); + if (enum_name != StringName()) { + continue; //enums will be handled on their own + } + + Dictionary d2; + d2["name"] = String(F->get()); + d2["value"] = ClassDB::get_integer_constant(class_name, F->get()); + + constants.push_back(d2); + } + + if (constants.size()) { + d["constants"] = constants; + } + } + { + //enum + Array enums; + List<StringName> enum_list; + ClassDB::get_enum_list(class_name, &enum_list, true); + for (List<StringName>::Element *F = enum_list.front(); F; F = F->next()) { + Dictionary d2; + d2["name"] = String(F->get()); + + Array values; + List<StringName> enum_constant_list; + ClassDB::get_enum_constants(class_name, F->get(), &enum_constant_list, true); + for (List<StringName>::Element *G = enum_constant_list.front(); G; G = G->next()) { + Dictionary d3; + d3["name"] = String(G->get()); + d3["value"] = ClassDB::get_integer_constant(class_name, G->get()); + values.push_back(d3); + } + + d2["values"] = values; + + enums.push_back(d2); + } + + if (enums.size()) { + d["enums"] = enums; + } + } + { + //methods + Array methods; + List<MethodInfo> method_list; + ClassDB::get_method_list(class_name, &method_list, true); + for (List<MethodInfo>::Element *F = method_list.front(); F; F = F->next()) { + StringName method_name = F->get().name; + if (F->get().flags & METHOD_FLAG_VIRTUAL) { + //virtual method + const MethodInfo &mi = F->get(); + Dictionary d2; + d2["name"] = String(method_name); + d2["is_const"] = (F->get().flags & METHOD_FLAG_CONST) ? true : false; + d2["is_vararg"] = false; + d2["is_virtual"] = true; + // virtual functions have no hash since no MethodBind is involved + bool has_return = mi.return_val.type != Variant::NIL || (mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT); + Array arguments; + for (int i = (has_return ? -1 : 0); i < mi.arguments.size(); i++) { + PropertyInfo pinfo = i == -1 ? mi.return_val : mi.arguments[i]; + Dictionary d3; + + if (i >= 0) { + d3["name"] = pinfo.name; + } + if (pinfo.class_name != StringName()) { + d3["type"] = String(pinfo.class_name); + } else { + Variant::Type type = pinfo.type; + if (type == Variant::NIL) { + d3["type"] = "Variant"; + } else { + d3["type"] = Variant::get_type_name(type); + } + } + + if (i == -1) { + d2["return_value"] = d3; + } else { + arguments.push_back(d3); + } + } + + if (arguments.size()) { + d2["arguments"] = arguments; + } + + methods.push_back(d2); + + } else if (F->get().name.begins_with("_")) { + //hidden method, ignore + + } else { + Dictionary d2; + d2["name"] = String(method_name); + + MethodBind *method = ClassDB::get_method(class_name, method_name); + if (!method) { + continue; + } + + d2["is_const"] = method->is_const(); + d2["is_vararg"] = method->is_vararg(); + d2["is_virtual"] = false; + d2["hash"] = method->get_hash(); + + Vector<Variant> default_args = method->get_default_arguments(); + + Array arguments; + for (int i = (method->has_return() ? -1 : 0); i < method->get_argument_count(); i++) { + PropertyInfo pinfo = i == -1 ? method->get_return_info() : method->get_argument_info(i); + Dictionary d3; + + if (i >= 0) { + d3["name"] = pinfo.name; + } + if (pinfo.class_name != StringName()) { + d3["type"] = String(pinfo.class_name); + } else { + Variant::Type type = pinfo.type; + if (type == Variant::NIL) { + d3["type"] = "Variant"; + } else { + d3["type"] = Variant::get_type_name(type); + } + } + + if (method->get_argument_meta(i) > 0) { + static const char *argmeta[11] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double" }; + d3["meta"] = argmeta[method->get_argument_meta(i)]; + } + + if (i >= 0 && i >= (method->get_argument_count() - default_args.size())) { + int dargidx = i - (method->get_argument_count() - default_args.size()); + d3["default_value"] = default_args[dargidx].get_construct_string(); + } + + if (i == -1) { + d2["return_value"] = d3; + } else { + arguments.push_back(d3); + } + } + + if (arguments.size()) { + d2["arguments"] = arguments; + } + + methods.push_back(d2); + } + } + + if (methods.size()) { + d["methods"] = methods; + } + } + + { + //signals + Array signals; + List<MethodInfo> signal_list; + ClassDB::get_signal_list(class_name, &signal_list, true); + for (List<MethodInfo>::Element *F = signal_list.front(); F; F = F->next()) { + StringName signal_name = F->get().name; + Dictionary d2; + d2["name"] = String(signal_name); + + Array arguments; + + for (int i = 0; i < F->get().arguments.size(); i++) { + Dictionary d3; + d3["name"] = F->get().arguments[i].name; + Variant::Type type = F->get().arguments[i].type; + if (F->get().arguments[i].class_name != StringName()) { + d3["type"] = String(F->get().arguments[i].class_name); + } else if (type == Variant::NIL) { + d3["type"] = "Variant"; + } else { + d3["type"] = Variant::get_type_name(type); + } + arguments.push_back(d3); + } + if (arguments.size()) { + d2["arguments"] = arguments; + } + + signals.push_back(d2); + } + + if (signals.size()) { + d["signals"] = signals; + } + } + { + //properties + Array properties; + List<PropertyInfo> property_list; + ClassDB::get_property_list(class_name, &property_list, true); + for (List<PropertyInfo>::Element *F = property_list.front(); F; F = F->next()) { + if (F->get().usage & PROPERTY_USAGE_CATEGORY || F->get().usage & PROPERTY_USAGE_GROUP || F->get().usage & PROPERTY_USAGE_SUBGROUP) { + continue; //not real properties + } + if (F->get().name.begins_with("_")) { + continue; //hidden property + } + StringName property_name = F->get().name; + Dictionary d2; + d2["name"] = String(property_name); + + if (F->get().class_name != StringName()) { + d2["type"] = String(F->get().class_name); + } else if (F->get().type == Variant::NIL && F->get().usage & PROPERTY_USAGE_NIL_IS_VARIANT) { + d2["type"] = "Variant"; + } else { + d2["type"] = Variant::get_type_name(F->get().type); + } + + d2["setter"] = ClassDB::get_property_setter(class_name, F->get().name); + d2["getter"] = ClassDB::get_property_getter(class_name, F->get().name); + d2["index"] = ClassDB::get_property_index(class_name, F->get().name); + properties.push_back(d2); + } + + if (properties.size()) { + d["properties"] = properties; + } + } + + classes.push_back(d); + } + + api_dump["classes"] = classes; + } + + { + // singletons + + Array singletons; + List<Engine::Singleton> singleton_list; + Engine::get_singleton()->get_singletons(&singleton_list); + + for (List<Engine::Singleton>::Element *E = singleton_list.front(); E; E = E->next()) { + const Engine::Singleton &s = E->get(); + Dictionary d; + d["name"] = s.name; + if (s.class_name != StringName()) { + d["type"] = String(s.class_name); + } else { + d["type"] = String(s.ptr->get_class()); + } + singletons.push_back(d); + } + + if (singletons.size()) { + api_dump["singletons"] = singletons; + } + } + + return api_dump; +} + +void NativeExtensionAPIDump::generate_extension_json_file(const String &p_path) { + Dictionary api = generate_extension_api(); + Ref<JSON> json; + json.instantiate(); + + String text = json->stringify(api, "\t", false); + FileAccessRef fa = FileAccess::open(p_path, FileAccess::WRITE); + CharString cs = text.ascii(); + fa->store_buffer((const uint8_t *)cs.ptr(), cs.length()); + fa->close(); +} +#endif diff --git a/core/extension/extension_api_dump.h b/core/extension/extension_api_dump.h new file mode 100644 index 0000000000..a7825c10a9 --- /dev/null +++ b/core/extension/extension_api_dump.h @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* extension_api_dump.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef API_DUMP_H +#define API_DUMP_H + +#include "core/extension/native_extension.h" + +#ifdef TOOLS_ENABLED + +class NativeExtensionAPIDump { +public: + static Dictionary generate_extension_api(); + static void generate_extension_json_file(const String &p_path); +}; +#endif + +#endif // API_DUMP_H diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp new file mode 100644 index 0000000000..324933d7b7 --- /dev/null +++ b/core/extension/gdnative_interface.cpp @@ -0,0 +1,688 @@ +/*************************************************************************/ +/* gdnative_interface.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "gdnative_interface.h" + +#include "core/config/engine.h" +#include "core/object/class_db.h" +#include "core/os/memory.h" +#include "core/variant/variant.h" +#include "core/version.h" + +// Memory Functions +static void *gdnative_alloc(size_t p_size) { + return memalloc(p_size); +} + +static void *gdnative_realloc(void *p_mem, size_t p_size) { + return memrealloc(p_mem, p_size); +} + +static void gdnative_free(void *p_mem) { + memfree(p_mem); +} + +// Helper print functions. +static void gdnative_print_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { + _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_ERROR); +} +static void gdnative_print_warning(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { + _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_WARNING); +} +static void gdnative_print_script_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) { + _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_SCRIPT); +} + +// Variant functions + +static void gdnative_variant_new_copy(GDNativeVariantPtr r_dest, const GDNativeVariantPtr p_src) { + memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant(*reinterpret_cast<Variant *>(p_src))); +} +static void gdnative_variant_new_nil(GDNativeVariantPtr r_dest) { + memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant); +} +static void gdnative_variant_destroy(GDNativeVariantPtr p_self) { + reinterpret_cast<Variant *>(p_self)->~Variant(); +} + +// variant type + +#define memnew_placement_custom(m_placement, m_class, m_constr) _post_initialize(new (m_placement, sizeof(m_class), "") m_constr) + +static void gdnative_variant_call(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argcount, GDNativeVariantPtr r_return, GDNativeCallError *r_error) { + Variant *self = (Variant *)p_self; + const StringName *method = (const StringName *)p_method; + const Variant **args = (const Variant **)p_args; + Variant ret; + Callable::CallError error; + self->call(*method, args, p_argcount, ret, error); + memnew_placement_custom(r_return, Variant, Variant(ret)); + + if (r_error) { + r_error->error = (GDNativeCallErrorType)(error.error); + r_error->argument = error.argument; + r_error->expected = error.expected; + } +} + +static void gdnative_variant_call_static(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argcount, GDNativeVariantPtr r_return, GDNativeCallError *r_error) { + Variant::Type type = (Variant::Type)p_type; + const StringName *method = (const StringName *)p_method; + const Variant **args = (const Variant **)p_args; + Variant ret; + Callable::CallError error; + Variant::call_static(type, *method, args, p_argcount, ret, error); + memnew_placement_custom(r_return, Variant, Variant(ret)); + + if (r_error) { + r_error->error = (GDNativeCallErrorType)error.error; + r_error->argument = error.argument; + r_error->expected = error.expected; + } +} + +static void gdnative_variant_evaluate(GDNativeVariantOperator p_op, const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid) { + Variant::Operator op = (Variant::Operator)p_op; + const Variant *a = (const Variant *)p_a; + const Variant *b = (const Variant *)p_b; + Variant *ret = (Variant *)r_return; + bool valid; + Variant::evaluate(op, *a, *b, *ret, valid); + *r_valid = valid; +} + +static void gdnative_variant_set(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) { + Variant *self = (Variant *)p_self; + const Variant *key = (const Variant *)p_key; + const Variant *value = (const Variant *)p_value; + + bool valid; + self->set(*key, *value, &valid); + *r_valid = valid; +} + +static void gdnative_variant_set_named(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) { + Variant *self = (Variant *)p_self; + const StringName *key = (const StringName *)p_key; + const Variant *value = (const Variant *)p_value; + + bool valid; + self->set_named(*key, *value, valid); + *r_valid = valid; +} + +static void gdnative_variant_set_keyed(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid) { + Variant *self = (Variant *)p_self; + const Variant *key = (const Variant *)p_key; + const Variant *value = (const Variant *)p_value; + + bool valid; + self->set_keyed(*key, *value, valid); + *r_valid = valid; +} + +static void gdnative_variant_set_indexed(GDNativeVariantPtr p_self, GDNativeInt p_index, const GDNativeVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob) { + Variant *self = (Variant *)p_self; + const Variant *value = (const Variant *)p_value; + + bool valid; + bool oob; + self->set_indexed(p_index, value, valid, oob); + *r_valid = valid; + *r_oob = oob; +} + +static void gdnative_variant_get(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + const Variant *key = (const Variant *)p_key; + + bool valid; + memnew_placement_custom(r_ret, Variant, Variant(self->get(*key, &valid))); + *r_valid = valid; +} + +static void gdnative_variant_get_named(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + const StringName *key = (const StringName *)p_key; + + bool valid; + memnew_placement_custom(r_ret, Variant, Variant(self->get_named(*key, valid))); + *r_valid = valid; +} + +static void gdnative_variant_get_keyed(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + const Variant *key = (const Variant *)p_key; + + bool valid; + memnew_placement_custom(r_ret, Variant, Variant(self->get_keyed(*key, valid))); + *r_valid = valid; +} + +static void gdnative_variant_get_indexed(const GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob) { + const Variant *self = (const Variant *)p_self; + + bool valid; + bool oob; + memnew_placement_custom(r_ret, Variant, Variant(self->get_indexed(p_index, valid, oob))); + *r_valid = valid; + *r_oob = oob; +} + +/// Iteration. +static GDNativeBool gdnative_variant_iter_init(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + Variant *iter = (Variant *)r_iter; + + bool valid; + bool ret = self->iter_init(*iter, valid); + *r_valid = valid; + return ret; +} + +static GDNativeBool gdnative_variant_iter_next(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + Variant *iter = (Variant *)r_iter; + + bool valid; + bool ret = self->iter_next(*iter, valid); + *r_valid = valid; + return ret; +} + +static void gdnative_variant_iter_get(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + Variant *iter = (Variant *)r_iter; + + bool valid; + memnew_placement_custom(r_ret, Variant, Variant(self->iter_next(*iter, valid))); + *r_valid = valid; +} + +/// Variant functions. +static GDNativeBool gdnative_variant_hash_compare(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other) { + const Variant *self = (const Variant *)p_self; + const Variant *other = (const Variant *)p_other; + return self->hash_compare(*other); +} + +static GDNativeBool gdnative_variant_booleanize(const GDNativeVariantPtr p_self) { + const Variant *self = (const Variant *)p_self; + return self->booleanize(); +} + +static void gdnative_variant_blend(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst) { + const Variant *a = (const Variant *)p_a; + const Variant *b = (const Variant *)p_b; + memnew_placement(r_dst, Variant); + Variant::blend(*a, *b, p_c, *(Variant *)r_dst); +} + +static void gdnative_variant_interpolate(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst) { + const Variant *a = (const Variant *)p_a; + const Variant *b = (const Variant *)p_b; + memnew_placement(r_dst, Variant); + Variant::interpolate(*a, *b, p_c, *(Variant *)r_dst); +} + +static void gdnative_variant_duplicate(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep) { + const Variant *self = (const Variant *)p_self; + memnew_placement_custom(r_ret, Variant, Variant(self->duplicate(p_deep))); +} + +static void gdnative_variant_stringify(const GDNativeVariantPtr p_self, GDNativeStringPtr r_ret) { + const Variant *self = (const Variant *)p_self; + memnew_placement_custom(r_ret, String, String(*self)); +} + +static GDNativeVariantType gdnative_variant_get_type(const GDNativeVariantPtr p_self) { + const Variant *self = (const Variant *)p_self; + return (GDNativeVariantType)self->get_type(); +} + +static GDNativeBool gdnative_variant_has_method(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method) { + const Variant *self = (const Variant *)p_self; + const StringName *method = (const StringName *)p_method; + return self->has_method(*method); +} + +static GDNativeBool gdnative_variant_has_member(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member) { + return Variant::has_member((Variant::Type)p_type, *((const StringName *)p_member)); +} + +static GDNativeBool gdnative_variant_has_key(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeBool *r_valid) { + const Variant *self = (const Variant *)p_self; + const Variant *key = (const Variant *)p_key; + bool valid; + return self->has_key(*key, valid); + *r_valid = valid; +} + +static void gdnative_variant_get_type_name(GDNativeVariantType p_type, GDNativeStringPtr r_ret) { + String name = Variant::get_type_name((Variant::Type)p_type); + memnew_placement_custom(r_ret, String, String(name)); +} + +static GDNativeBool gdnative_variant_can_convert(GDNativeVariantType p_from, GDNativeVariantType p_to) { + return Variant::can_convert((Variant::Type)p_from, (Variant::Type)p_to); +} + +static GDNativeBool gdnative_variant_can_convert_strict(GDNativeVariantType p_from, GDNativeVariantType p_to) { + return Variant::can_convert_strict((Variant::Type)p_from, (Variant::Type)p_to); +} + +// ptrcalls +static GDNativePtrOperatorEvaluator gdnative_variant_get_ptr_operator_evaluator(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b) { + return (GDNativePtrOperatorEvaluator)Variant::get_ptr_operator_evaluator(Variant::Operator(p_operator), Variant::Type(p_type_a), Variant::Type(p_type_b)); +} +static GDNativePtrBuiltInMethod gdnative_variant_get_ptr_builtin_method(GDNativeVariantType p_type, const char *p_method, GDNativeInt p_hash) { + StringName method = p_method; + uint32_t hash = Variant::get_builtin_method_hash(Variant::Type(p_type), method); + if (hash != p_hash) { + ERR_PRINT_ONCE("Error getting method " + String(method) + ", hash mismatch."); + return nullptr; + } + + return (GDNativePtrBuiltInMethod)Variant::get_ptr_builtin_method(Variant::Type(p_type), method); +} +static GDNativePtrConstructor gdnative_variant_get_ptr_constructor(GDNativeVariantType p_type, int32_t p_constructor) { + return (GDNativePtrConstructor)Variant::get_ptr_constructor(Variant::Type(p_type), p_constructor); +} +static void gdnative_variant_construct(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error) { + memnew_placement(p_base, Variant); + + Callable::CallError error; + Variant::construct(Variant::Type(p_type), *(Variant *)p_base, (const Variant **)p_args, p_argument_count, error); + + if (r_error) { + r_error->error = (GDNativeCallErrorType)(error.error); + r_error->argument = error.argument; + r_error->expected = error.expected; + } +} +static GDNativePtrSetter gdnative_variant_get_ptr_setter(GDNativeVariantType p_type, const char *p_member) { + return (GDNativePtrSetter)Variant::get_member_ptr_setter(Variant::Type(p_type), p_member); +} +static GDNativePtrGetter gdnative_variant_get_ptr_getter(GDNativeVariantType p_type, const char *p_member) { + return (GDNativePtrGetter)Variant::get_member_ptr_getter(Variant::Type(p_type), p_member); +} +static GDNativePtrIndexedSetter gdnative_variant_get_ptr_indexed_setter(GDNativeVariantType p_type) { + return (GDNativePtrIndexedSetter)Variant::get_member_ptr_indexed_setter(Variant::Type(p_type)); +} +static GDNativePtrIndexedGetter gdnative_variant_get_ptr_indexed_getter(GDNativeVariantType p_type) { + return (GDNativePtrIndexedGetter)Variant::get_member_ptr_indexed_getter(Variant::Type(p_type)); +} +static GDNativePtrKeyedSetter gdnative_variant_get_ptr_keyed_setter(GDNativeVariantType p_type) { + return (GDNativePtrKeyedSetter)Variant::get_member_ptr_keyed_setter(Variant::Type(p_type)); +} +static GDNativePtrKeyedGetter gdnative_variant_get_ptr_keyed_getter(GDNativeVariantType p_type) { + return (GDNativePtrKeyedGetter)Variant::get_member_ptr_keyed_getter(Variant::Type(p_type)); +} +static GDNativePtrKeyedChecker gdnative_variant_get_ptr_keyed_checker(GDNativeVariantType p_type) { + return (GDNativePtrKeyedChecker)Variant::get_member_ptr_keyed_checker(Variant::Type(p_type)); +} +static void gdnative_variant_get_constant_value(GDNativeVariantType p_type, const char *p_constant, GDNativeVariantPtr r_ret) { + memnew_placement_custom(r_ret, Variant, Variant(Variant::get_constant_value(Variant::Type(p_type), p_constant))); +} +static GDNativePtrUtilityFunction gdnative_variant_get_ptr_utility_function(const char *p_function, GDNativeInt p_hash) { + StringName function = p_function; + uint32_t hash = Variant::get_utility_function_hash(function); + if (hash != p_hash) { + ERR_PRINT_ONCE("Error getting utility function " + String(function) + ", hash mismatch."); + return nullptr; + } + return (GDNativePtrUtilityFunction)Variant::get_ptr_utility_function(function); +} + +//string helpers + +static void gdnative_string_new_with_latin1_chars(GDNativeStringPtr r_dest, const char *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String(p_contents); +} + +static void gdnative_string_new_with_utf8_chars(GDNativeStringPtr r_dest, const char *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf8(p_contents); +} + +static void gdnative_string_new_with_utf16_chars(GDNativeStringPtr r_dest, const char16_t *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf16(p_contents); +} + +static void gdnative_string_new_with_utf32_chars(GDNativeStringPtr r_dest, const char32_t *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents); +} + +static void gdnative_string_new_with_wide_chars(GDNativeStringPtr r_dest, const wchar_t *p_contents) { + String *dest = (String *)r_dest; + if (sizeof(wchar_t) == 2) { + // wchar_t is 16 bit, parse. + memnew_placement(dest, String); + dest->parse_utf16((const char16_t *)p_contents); + } else { + // wchar_t is 32 bit, copy. + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents); + } +} + +static void gdnative_string_new_with_latin1_chars_and_len(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String(p_contents, p_size); +} + +static void gdnative_string_new_with_utf8_chars_and_len(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf8(p_contents, p_size); +} + +static void gdnative_string_new_with_utf16_chars_and_len(GDNativeStringPtr r_dest, const char16_t *p_contents, const GDNativeInt p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf16(p_contents, p_size); +} + +static void gdnative_string_new_with_utf32_chars_and_len(GDNativeStringPtr r_dest, const char32_t *p_contents, const GDNativeInt p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents, p_size); +} + +static void gdnative_string_new_with_wide_chars_and_len(GDNativeStringPtr r_dest, const wchar_t *p_contents, const GDNativeInt p_size) { + String *dest = (String *)r_dest; + if (sizeof(wchar_t) == 2) { + // wchar_t is 16 bit, parse. + memnew_placement(dest, String); + dest->parse_utf16((const char16_t *)p_contents, p_size); + } else { + // wchar_t is 32 bit, copy. + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents, p_size); + } +} + +static GDNativeInt gdnative_string_to_latin1_chars(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length) { + String *self = (String *)p_self; + CharString cs = self->ascii(true); + GDNativeInt len = cs.length(); + if (r_text) { + const char *s_text = cs.ptr(); + for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) { + r_text[i] = s_text[i]; + } + } + return len; +} +static GDNativeInt gdnative_string_to_utf8_chars(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length) { + String *self = (String *)p_self; + CharString cs = self->utf8(); + GDNativeInt len = cs.length(); + if (r_text) { + const char *s_text = cs.ptr(); + for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) { + r_text[i] = s_text[i]; + } + } + return len; +} +static GDNativeInt gdnative_string_to_utf16_chars(const GDNativeStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length) { + String *self = (String *)p_self; + Char16String cs = self->utf16(); + GDNativeInt len = cs.length(); + if (r_text) { + const char16_t *s_text = cs.ptr(); + for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) { + r_text[i] = s_text[i]; + } + } + return len; +} +static GDNativeInt gdnative_string_to_utf32_chars(const GDNativeStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length) { + String *self = (String *)p_self; + GDNativeInt len = self->length(); + if (r_text) { + const char32_t *s_text = self->ptr(); + for (GDNativeInt i = 0; i < MIN(len, p_max_write_length); i++) { + r_text[i] = s_text[i]; + } + } + return len; +} +static GDNativeInt gdnative_string_to_wide_chars(const GDNativeStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length) { + if (sizeof(wchar_t) == 4) { + return gdnative_string_to_utf32_chars(p_self, (char32_t *)r_text, p_max_write_length); + } else { + return gdnative_string_to_utf16_chars(p_self, (char16_t *)r_text, p_max_write_length); + } +} + +static char32_t *gdnative_string_operator_index(GDNativeStringPtr p_self, GDNativeInt p_index) { + String *self = (String *)p_self; + ERR_FAIL_INDEX_V(p_index, self->length() + 1, nullptr); + return &self->ptrw()[p_index]; +} + +static const char32_t *gdnative_string_operator_index_const(const GDNativeStringPtr p_self, GDNativeInt p_index) { + const String *self = (const String *)p_self; + ERR_FAIL_INDEX_V(p_index, self->length() + 1, nullptr); + return &self->ptr()[p_index]; +} + +/* OBJECT API */ + +static void gdnative_object_method_bind_ptrcall(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr p_ret) { + MethodBind *mb = (MethodBind *)p_method_bind; + Object *o = (Object *)p_instance; + mb->ptrcall(o, (const void **)p_args, p_ret); +} + +static void gdnative_object_destroy(GDNativeObjectPtr p_o) { + memdelete((Object *)p_o); +} + +static GDNativeObjectPtr gdnative_global_get_singleton(const char *p_name) { + return (GDNativeObjectPtr)Engine::get_singleton()->get_singleton_object(String(p_name)); +} + +static GDNativeObjectPtr gdnative_object_get_instance_from_id(GDObjectInstanceID p_instance_id) { + return (GDNativeObjectPtr)ObjectDB::get_instance(ObjectID(p_instance_id)); +} + +static GDNativeObjectPtr gdnative_object_cast_to(const GDNativeObjectPtr p_object, void *p_class_tag) { + if (!p_object) { + return nullptr; + } + Object *o = (Object *)p_object; + + return o->is_class_ptr(p_class_tag) ? (GDNativeObjectPtr)o : (GDNativeObjectPtr) nullptr; +} + +static GDObjectInstanceID gdnative_object_get_instance_id(const GDNativeObjectPtr p_object) { + const Object *o = (const Object *)p_object; + return (GDObjectInstanceID)o->get_instance_id(); +} + +static GDNativeMethodBindPtr gdnative_classdb_get_method_bind(const char *p_classname, const char *p_methodname, GDNativeInt p_hash) { + MethodBind *mb = ClassDB::get_method(StringName(p_classname), StringName(p_methodname)); + ERR_FAIL_COND_V(!mb, nullptr); + if (mb->get_hash() != p_hash) { + ERR_PRINT_ONCE("Hash mismatch for method '" + String(p_classname) + "." + String(p_methodname) + "'."); + return nullptr; + } + // MethodBind *mb = ClassDB::get_method("Node", "get_name"); + return (GDNativeMethodBindPtr)mb; +} + +static GDNativeClassConstructor gdnative_classdb_get_constructor(const char *p_classname) { + ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(StringName(p_classname)); + if (class_info) { + return (GDNativeClassConstructor)class_info->creation_func; + } + return nullptr; +} + +static void *gdnative_classdb_get_class_tag(const char *p_classname) { + ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(p_classname); + return class_info ? class_info->class_ptr : nullptr; +} + +void gdnative_setup_interface(GDNativeInterface *p_interface) { + GDNativeInterface &gdni = *p_interface; + + gdni.version_major = VERSION_MAJOR; + gdni.version_minor = VERSION_MINOR; +#if VERSION_PATCH + gdni.version_patch = VERSION_PATCH; +#else + gdni.version_patch = 0; +#endif + gdni.version_string = VERSION_FULL_NAME; + + /* GODOT CORE */ + + gdni.mem_alloc = gdnative_alloc; + gdni.mem_realloc = gdnative_realloc; + gdni.mem_free = gdnative_free; + + gdni.print_error = gdnative_print_error; + gdni.print_warning = gdnative_print_warning; + gdni.print_script_error = gdnative_print_script_error; + + /* GODOT VARIANT */ + + // variant general + gdni.variant_new_copy = gdnative_variant_new_copy; + gdni.variant_new_nil = gdnative_variant_new_nil; + gdni.variant_destroy = gdnative_variant_destroy; + + gdni.variant_call = gdnative_variant_call; + gdni.variant_call_static = gdnative_variant_call_static; + gdni.variant_evaluate = gdnative_variant_evaluate; + gdni.variant_set = gdnative_variant_set; + gdni.variant_set_named = gdnative_variant_set_named; + gdni.variant_set_keyed = gdnative_variant_set_keyed; + gdni.variant_set_indexed = gdnative_variant_set_indexed; + gdni.variant_get = gdnative_variant_get; + gdni.variant_get_named = gdnative_variant_get_named; + gdni.variant_get_keyed = gdnative_variant_get_keyed; + gdni.variant_get_indexed = gdnative_variant_get_indexed; + gdni.variant_iter_init = gdnative_variant_iter_init; + gdni.variant_iter_next = gdnative_variant_iter_next; + gdni.variant_iter_get = gdnative_variant_iter_get; + gdni.variant_hash_compare = gdnative_variant_hash_compare; + gdni.variant_booleanize = gdnative_variant_booleanize; + gdni.variant_blend = gdnative_variant_blend; + gdni.variant_interpolate = gdnative_variant_interpolate; + gdni.variant_duplicate = gdnative_variant_duplicate; + gdni.variant_stringify = gdnative_variant_stringify; + + gdni.variant_get_type = gdnative_variant_get_type; + gdni.variant_has_method = gdnative_variant_has_method; + gdni.variant_has_member = gdnative_variant_has_member; + gdni.variant_has_key = gdnative_variant_has_key; + gdni.variant_get_type_name = gdnative_variant_get_type_name; + gdni.variant_can_convert = gdnative_variant_can_convert; + gdni.variant_can_convert_strict = gdnative_variant_can_convert_strict; + + //ptrcalls +#if 0 + GDNativeVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDNativeVariantType p_type); + GDNativeTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDNativeVariantType p_type); +#endif + + gdni.variant_get_ptr_operator_evaluator = gdnative_variant_get_ptr_operator_evaluator; + gdni.variant_get_ptr_builtin_method = gdnative_variant_get_ptr_builtin_method; + gdni.variant_get_ptr_constructor = gdnative_variant_get_ptr_constructor; + gdni.variant_construct = gdnative_variant_construct; + gdni.variant_get_ptr_setter = gdnative_variant_get_ptr_setter; + gdni.variant_get_ptr_getter = gdnative_variant_get_ptr_getter; + gdni.variant_get_ptr_indexed_setter = gdnative_variant_get_ptr_indexed_setter; + gdni.variant_get_ptr_indexed_getter = gdnative_variant_get_ptr_indexed_getter; + gdni.variant_get_ptr_keyed_setter = gdnative_variant_get_ptr_keyed_setter; + gdni.variant_get_ptr_keyed_getter = gdnative_variant_get_ptr_keyed_getter; + gdni.variant_get_ptr_keyed_checker = gdnative_variant_get_ptr_keyed_checker; + gdni.variant_get_constant_value = gdnative_variant_get_constant_value; + gdni.variant_get_ptr_utility_function = gdnative_variant_get_ptr_utility_function; + + // extra utilities + + gdni.string_new_with_latin1_chars = gdnative_string_new_with_latin1_chars; + gdni.string_new_with_utf8_chars = gdnative_string_new_with_utf8_chars; + gdni.string_new_with_utf16_chars = gdnative_string_new_with_utf16_chars; + gdni.string_new_with_utf32_chars = gdnative_string_new_with_utf32_chars; + gdni.string_new_with_wide_chars = gdnative_string_new_with_wide_chars; + gdni.string_new_with_latin1_chars_and_len = gdnative_string_new_with_latin1_chars_and_len; + gdni.string_new_with_utf8_chars_and_len = gdnative_string_new_with_utf8_chars_and_len; + gdni.string_new_with_utf16_chars_and_len = gdnative_string_new_with_utf16_chars_and_len; + gdni.string_new_with_utf32_chars_and_len = gdnative_string_new_with_utf32_chars_and_len; + gdni.string_new_with_wide_chars_and_len = gdnative_string_new_with_wide_chars_and_len; + gdni.string_to_latin1_chars = gdnative_string_to_latin1_chars; + gdni.string_to_utf8_chars = gdnative_string_to_utf8_chars; + gdni.string_to_utf16_chars = gdnative_string_to_utf16_chars; + gdni.string_to_utf32_chars = gdnative_string_to_utf32_chars; + gdni.string_to_wide_chars = gdnative_string_to_wide_chars; + gdni.string_operator_index = gdnative_string_operator_index; + gdni.string_operator_index_const = gdnative_string_operator_index_const; + + /* OBJECT */ + + gdni.object_method_bind_ptrcall = gdnative_object_method_bind_ptrcall; + gdni.object_destroy = gdnative_object_destroy; + gdni.global_get_singleton = gdnative_global_get_singleton; + + gdni.object_cast_to = gdnative_object_cast_to; + gdni.object_get_instance_from_id = gdnative_object_get_instance_from_id; + gdni.object_get_instance_id = gdnative_object_get_instance_id; + + /* CLASSDB */ + + gdni.classdb_get_constructor = gdnative_classdb_get_constructor; + gdni.classdb_get_method_bind = gdnative_classdb_get_method_bind; + gdni.classdb_get_class_tag = gdnative_classdb_get_class_tag; + + /* CLASSDB EXTENSION */ + + //these are filled by implementation, since it will want to keep track of registered classes + gdni.classdb_register_extension_class = nullptr; + gdni.classdb_register_extension_class_method = nullptr; + gdni.classdb_register_extension_class_integer_constant = nullptr; + gdni.classdb_register_extension_class_property = nullptr; + gdni.classdb_register_extension_class_signal = nullptr; + gdni.classdb_unregister_extension_class = nullptr; +} diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h new file mode 100644 index 0000000000..318912e889 --- /dev/null +++ b/core/extension/gdnative_interface.h @@ -0,0 +1,427 @@ +/*************************************************************************/ +/* gdnative_interface.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef GDNATIVE_INTERFACE_H +#define GDNATIVE_INTERFACE_H + +/* This is a C class header, you can copy it and use it directly in your own binders. + * Together with the JSON file, you should be able to generate any binder. + */ + +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* VARIANT TYPES */ + +typedef enum { + GDNATIVE_VARIANT_TYPE_NIL, + + /* atomic types */ + GDNATIVE_VARIANT_TYPE_BOOL, + GDNATIVE_VARIANT_TYPE_INT, + GDNATIVE_VARIANT_TYPE_FLOAT, + GDNATIVE_VARIANT_TYPE_STRING, + + /* math types */ + GDNATIVE_VARIANT_TYPE_VECTOR2, + GDNATIVE_VARIANT_TYPE_VECTOR2I, + GDNATIVE_VARIANT_TYPE_RECT2, + GDNATIVE_VARIANT_TYPE_RECT2I, + GDNATIVE_VARIANT_TYPE_VECTOR3, + GDNATIVE_VARIANT_TYPE_VECTOR3I, + GDNATIVE_VARIANT_TYPE_TRANSFORM2D, + GDNATIVE_VARIANT_TYPE_PLANE, + GDNATIVE_VARIANT_TYPE_QUATERNION, + GDNATIVE_VARIANT_TYPE_AABB, + GDNATIVE_VARIANT_TYPE_BASIS, + GDNATIVE_VARIANT_TYPE_TRANSFORM3D, + + /* misc types */ + GDNATIVE_VARIANT_TYPE_COLOR, + GDNATIVE_VARIANT_TYPE_STRING_NAME, + GDNATIVE_VARIANT_TYPE_NODE_PATH, + GDNATIVE_VARIANT_TYPE_RID, + GDNATIVE_VARIANT_TYPE_OBJECT, + GDNATIVE_VARIANT_TYPE_CALLABLE, + GDNATIVE_VARIANT_TYPE_SIGNAL, + GDNATIVE_VARIANT_TYPE_DICTIONARY, + GDNATIVE_VARIANT_TYPE_ARRAY, + + /* typed arrays */ + GDNATIVE_VARIANT_TYPE_PACKED_BYTE_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_INT32_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_INT64_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_FLOAT32_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_FLOAT64_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_STRING_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_VECTOR2_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_VECTOR3_ARRAY, + GDNATIVE_VARIANT_TYPE_PACKED_COLOR_ARRAY, + + GDNATIVE_VARIANT_TYPE_VARIANT_MAX +} GDNativeVariantType; + +typedef enum { + /* comparison */ + GDNATIVE_VARIANT_OP_EQUAL, + GDNATIVE_VARIANT_OP_NOT_EQUAL, + GDNATIVE_VARIANT_OP_LESS, + GDNATIVE_VARIANT_OP_LESS_EQUAL, + GDNATIVE_VARIANT_OP_GREATER, + GDNATIVE_VARIANT_OP_GREATER_EQUAL, + /* mathematic */ + GDNATIVE_VARIANT_OP_ADD, + GDNATIVE_VARIANT_OP_SUBTRACT, + GDNATIVE_VARIANT_OP_MULTIPLY, + GDNATIVE_VARIANT_OP_DIVIDE, + GDNATIVE_VARIANT_OP_NEGATE, + GDNATIVE_VARIANT_OP_POSITIVE, + GDNATIVE_VARIANT_OP_MODULE, + /* bitwise */ + GDNATIVE_VARIANT_OP_SHIFT_LEFT, + GDNATIVE_VARIANT_OP_SHIFT_RIGHT, + GDNATIVE_VARIANT_OP_BIT_AND, + GDNATIVE_VARIANT_OP_BIT_OR, + GDNATIVE_VARIANT_OP_BIT_XOR, + GDNATIVE_VARIANT_OP_BIT_NEGATE, + /* logic */ + GDNATIVE_VARIANT_OP_AND, + GDNATIVE_VARIANT_OP_OR, + GDNATIVE_VARIANT_OP_XOR, + GDNATIVE_VARIANT_OP_NOT, + /* containment */ + GDNATIVE_VARIANT_OP_IN, + GDNATIVE_VARIANT_OP_MAX + +} GDNativeVariantOperator; + +typedef void *GDNativeVariantPtr; +typedef void *GDNativeStringNamePtr; +typedef void *GDNativeStringPtr; +typedef void *GDNativeObjectPtr; +typedef void *GDNativeTypePtr; +typedef void *GDNativeMethodBindPtr; +typedef int64_t GDNativeInt; +typedef uint32_t GDNativeBool; +typedef uint64_t GDObjectInstanceID; + +/* VARIANT DATA I/O */ + +typedef enum { + NATIVE_CALL_OK, + NATIVE_CALL_ERROR_INVALID_METHOD, + NATIVE_CALL_ERROR_INVALID_ARGUMENT, /* expected is variant type */ + NATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, /* expected is number of arguments */ + NATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, /* expected is number of arguments */ + NATIVE_CALL_ERROR_INSTANCE_IS_NULL, + +} GDNativeCallErrorType; + +typedef struct { + GDNativeCallErrorType error; + int32_t argument; + int32_t expected; +} GDNativeCallError; + +typedef void (*GDNativeVariantFromTypeConstructorFunc)(GDNativeVariantPtr, GDNativeTypePtr); +typedef void (*GDNativeTypeFromVariantConstructorFunc)(GDNativeTypePtr, GDNativeVariantPtr); +typedef void (*GDNativePtrOperatorEvaluator)(const GDNativeTypePtr p_left, const GDNativeTypePtr p_right, GDNativeTypePtr r_result); +typedef void (*GDNativePtrBuiltInMethod)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args, GDNativeTypePtr r_return, int p_argument_count); +typedef void (*GDNativePtrConstructor)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args); +typedef void (*GDNativePtrSetter)(GDNativeTypePtr p_base, const GDNativeTypePtr p_value); +typedef void (*GDNativePtrGetter)(const GDNativeTypePtr p_base, GDNativeTypePtr r_value); +typedef void (*GDNativePtrIndexedSetter)(GDNativeTypePtr p_base, GDNativeInt p_index, const GDNativeTypePtr p_value); +typedef void (*GDNativePtrIndexedGetter)(const GDNativeTypePtr p_base, GDNativeInt p_index, GDNativeTypePtr r_value); +typedef void (*GDNativePtrKeyedSetter)(GDNativeTypePtr p_base, const GDNativeTypePtr p_key, const GDNativeTypePtr p_value); +typedef void (*GDNativePtrKeyedGetter)(const GDNativeTypePtr p_base, const GDNativeTypePtr p_key, GDNativeTypePtr r_value); +typedef uint32_t (*GDNativePtrKeyedChecker)(const GDNativeVariantPtr p_base, const GDNativeVariantPtr p_key); +typedef void (*GDNativePtrUtilityFunction)(GDNativeTypePtr r_return, const GDNativeTypePtr *p_arguments, int p_argument_count); + +typedef GDNativeObjectPtr (*GDNativeClassConstructor)(); + +/* EXTENSION CLASSES */ + +typedef void *GDExtensionClassInstancePtr; + +typedef GDNativeBool (*GDNativeExtensionClassSet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value); +typedef GDNativeBool (*GDNativeExtensionClassGet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret); + +typedef struct { + uint32_t type; + const char *name; + const char *class_name; + uint32_t hint; + const char *hint_string; + uint32_t usage; +} GDNativePropertyInfo; + +typedef const GDNativePropertyInfo *(*GDNativeExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count); +typedef void (*GDNativeExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDNativePropertyInfo *p_list); +typedef void (*GDNativeExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what); +typedef const char *(*GDNativeExtensionClassToString)(GDExtensionClassInstancePtr p_instance); +typedef void (*GDNativeExtensionClassReference)(GDExtensionClassInstancePtr p_instance); +typedef void (*GDNativeExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance); +typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); +typedef GDExtensionClassInstancePtr (*GDNativeExtensionClassCreateInstance)(void *p_userdata); +typedef void (*GDNativeExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance); +typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, const char *p_name); + +typedef struct { + GDNativeExtensionClassSet set_func; + GDNativeExtensionClassGet get_func; + GDNativeExtensionClassGetPropertyList get_property_list_func; + GDNativeExtensionClassFreePropertyList free_property_list_func; + GDNativeExtensionClassNotification notification_func; + GDNativeExtensionClassToString to_string_func; + GDNativeExtensionClassReference reference_func; + GDNativeExtensionClassUnreference unreference_func; + GDNativeExtensionClassCreateInstance create_instance_func; /* this one is mandatory */ + GDNativeExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ + GDNativeExtensionClassGetVirtual get_firtual_func; + void *class_userdata; +} GDNativeExtensionClassCreationInfo; + +typedef void *GDNativeExtensionClassLibraryPtr; + +typedef const GDNativePropertyInfo *(*GDNativeExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count); + +/* Method */ + +typedef enum { + GDNATIVE_EXTENSION_METHOD_FLAG_NORMAL = 1, + GDNATIVE_EXTENSION_METHOD_FLAG_EDITOR = 2, + GDNATIVE_EXTENSION_METHOD_FLAG_NOSCRIPT = 4, + GDNATIVE_EXTENSION_METHOD_FLAG_CONST = 8, + GDNATIVE_EXTENSION_METHOD_FLAG_REVERSE = 16, /* used for events */ + GDNATIVE_EXTENSION_METHOD_FLAG_VIRTUAL = 32, + GDNATIVE_EXTENSION_METHOD_FLAG_FROM_SCRIPT = 64, + GDNATIVE_EXTENSION_METHOD_FLAG_VARARG = 128, + GDNATIVE_EXTENSION_METHOD_FLAG_STATIC = 256, + GDNATIVE_EXTENSION_METHOD_FLAGS_DEFAULT = GDNATIVE_EXTENSION_METHOD_FLAG_NORMAL, +} GDNativeExtensionClassMethodFlags; + +typedef enum { + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_NONE, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT, + GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE +} GDNativeExtensionClassMethodArgumentMetadata; + +typedef void (*GDNativeExtensionClassMethodCall)(GDExtensionClassInstancePtr p_instance, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); +typedef void (*GDNativeExtensionClassMethodPtrCall)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); + +/* passing -1 as argument in the following functions refers to the return type */ +typedef GDNativeVariantType (*GDNativeExtensionClassMethodGetArgumentType)(void *p_method_userdata, int32_t p_argument); +typedef void (*GDNativeExtensionClassMethodGetArgumentInfo)(void *p_method_userdata, int32_t p_argument, GDNativePropertyInfo *r_info); +typedef GDNativeExtensionClassMethodArgumentMetadata (*GDNativeExtensionClassMethodGetArgumentMetadata)(void *p_method_userdata, int32_t p_argument); + +typedef struct { + const char *name; + void *method_userdata; + GDNativeExtensionClassMethodCall call_func; + GDNativeExtensionClassMethodPtrCall ptrcall_func; + uint32_t method_flags; /* GDNativeExtensionClassMethodFlags */ + uint32_t argument_count; + GDNativeBool has_return_value; + GDNativeExtensionClassMethodGetArgumentType get_argument_type_func; + GDNativeExtensionClassMethodGetArgumentInfo get_argument_info_func; /* name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies. */ + GDNativeExtensionClassMethodGetArgumentMetadata get_argument_metadata_func; + uint32_t default_argument_count; + GDNativeVariantPtr *default_arguments; +} GDNativeExtensionClassMethodInfo; + +/* INTERFACE */ + +typedef struct { + uint32_t version_major; + uint32_t version_minor; + uint32_t version_patch; + const char *version_string; + + /* GODOT CORE */ + void *(*mem_alloc)(size_t p_bytes); + void *(*mem_realloc)(void *p_ptr, size_t p_bytes); + void (*mem_free)(void *p_ptr); + + void (*print_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line); + void (*print_warning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line); + void (*print_script_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line); + + /* GODOT VARIANT */ + + /* variant general */ + void (*variant_new_copy)(GDNativeVariantPtr r_dest, const GDNativeVariantPtr p_src); + void (*variant_new_nil)(GDNativeVariantPtr r_dest); + void (*variant_destroy)(GDNativeVariantPtr p_self); + + /* variant type */ + void (*variant_call)(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); + void (*variant_call_static)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_method, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); + void (*variant_evaluate)(GDNativeVariantOperator p_op, const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_return, GDNativeBool *r_valid); + void (*variant_set)(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid); + void (*variant_set_named)(GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid); + void (*variant_set_keyed)(GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, const GDNativeVariantPtr p_value, GDNativeBool *r_valid); + void (*variant_set_indexed)(GDNativeVariantPtr p_self, GDNativeInt p_index, const GDNativeVariantPtr p_value, GDNativeBool *r_valid, GDNativeBool *r_oob); + void (*variant_get)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + void (*variant_get_named)(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + void (*variant_get_keyed)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + void (*variant_get_indexed)(const GDNativeVariantPtr p_self, GDNativeInt p_index, GDNativeVariantPtr r_ret, GDNativeBool *r_valid, GDNativeBool *r_oob); + GDNativeBool (*variant_iter_init)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid); + GDNativeBool (*variant_iter_next)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid); + void (*variant_iter_get)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid); + GDNativeBool (*variant_hash_compare)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other); + GDNativeBool (*variant_booleanize)(const GDNativeVariantPtr p_self); + void (*variant_blend)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst); + void (*variant_interpolate)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst); + void (*variant_duplicate)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep); + void (*variant_stringify)(const GDNativeVariantPtr p_self, GDNativeStringPtr r_ret); + + GDNativeVariantType (*variant_get_type)(const GDNativeVariantPtr p_self); + GDNativeBool (*variant_has_method)(const GDNativeVariantPtr p_self, const GDNativeStringNamePtr p_method); + GDNativeBool (*variant_has_member)(GDNativeVariantType p_type, const GDNativeStringNamePtr p_member); + GDNativeBool (*variant_has_key)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_key, GDNativeBool *r_valid); + void (*variant_get_type_name)(GDNativeVariantType p_type, GDNativeStringPtr r_name); + GDNativeBool (*variant_can_convert)(GDNativeVariantType p_from, GDNativeVariantType p_to); + GDNativeBool (*variant_can_convert_strict)(GDNativeVariantType p_from, GDNativeVariantType p_to); + + /* ptrcalls */ + GDNativeVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDNativeVariantType p_type); + GDNativeTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDNativeVariantType p_type); + GDNativePtrOperatorEvaluator (*variant_get_ptr_operator_evaluator)(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b); + GDNativePtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDNativeVariantType p_type, const char *p_method, GDNativeInt p_hash); + GDNativePtrConstructor (*variant_get_ptr_constructor)(GDNativeVariantType p_type, int32_t p_constructor); + void (*variant_construct)(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error); + GDNativePtrSetter (*variant_get_ptr_setter)(GDNativeVariantType p_type, const char *p_member); + GDNativePtrGetter (*variant_get_ptr_getter)(GDNativeVariantType p_type, const char *p_member); + GDNativePtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDNativeVariantType p_type); + GDNativePtrIndexedGetter (*variant_get_ptr_indexed_getter)(GDNativeVariantType p_type); + GDNativePtrKeyedSetter (*variant_get_ptr_keyed_setter)(GDNativeVariantType p_type); + GDNativePtrKeyedGetter (*variant_get_ptr_keyed_getter)(GDNativeVariantType p_type); + GDNativePtrKeyedChecker (*variant_get_ptr_keyed_checker)(GDNativeVariantType p_type); + void (*variant_get_constant_value)(GDNativeVariantType p_type, const char *p_constant, GDNativeVariantPtr r_ret); + GDNativePtrUtilityFunction (*variant_get_ptr_utility_function)(const char *p_function, GDNativeInt p_hash); + + /* extra utilities */ + + void (*string_new_with_latin1_chars)(GDNativeStringPtr r_dest, const char *p_contents); + void (*string_new_with_utf8_chars)(GDNativeStringPtr r_dest, const char *p_contents); + void (*string_new_with_utf16_chars)(GDNativeStringPtr r_dest, const char16_t *p_contents); + void (*string_new_with_utf32_chars)(GDNativeStringPtr r_dest, const char32_t *p_contents); + void (*string_new_with_wide_chars)(GDNativeStringPtr r_dest, const wchar_t *p_contents); + void (*string_new_with_latin1_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size); + void (*string_new_with_utf8_chars_and_len)(GDNativeStringPtr r_dest, const char *p_contents, const GDNativeInt p_size); + void (*string_new_with_utf16_chars_and_len)(GDNativeStringPtr r_dest, const char16_t *p_contents, const GDNativeInt p_size); + void (*string_new_with_utf32_chars_and_len)(GDNativeStringPtr r_dest, const char32_t *p_contents, const GDNativeInt p_size); + void (*string_new_with_wide_chars_and_len)(GDNativeStringPtr r_dest, const wchar_t *p_contents, const GDNativeInt p_size); + /* Information about the following functions: + * - The return value is the resulting encoded string length. + * - The length returned is in characters, not in bytes. It also does not include a trailing zero. + * - These functions also do not write trailing zero, If you need it, write it yourself at the position indicated by the length (and make sure to allocate it). + * - Passing NULL in r_text means only the length is computed (again, without including trailing zero). + * - p_max_write_length argument is in characters, not bytes. It will be ignored if r_text is NULL. + * - p_max_write_length argument does not affect the return value, it's only to cap write length. + */ + GDNativeInt (*string_to_latin1_chars)(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_utf8_chars)(const GDNativeStringPtr p_self, char *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_utf16_chars)(const GDNativeStringPtr p_self, char16_t *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_utf32_chars)(const GDNativeStringPtr p_self, char32_t *r_text, GDNativeInt p_max_write_length); + GDNativeInt (*string_to_wide_chars)(const GDNativeStringPtr p_self, wchar_t *r_text, GDNativeInt p_max_write_length); + char32_t *(*string_operator_index)(GDNativeStringPtr p_self, GDNativeInt p_index); + const char32_t *(*string_operator_index_const)(const GDNativeStringPtr p_self, GDNativeInt p_index); + + /* OBJECT */ + + void (*object_method_bind_ptrcall)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); + void (*object_destroy)(GDNativeObjectPtr p_o); + GDNativeObjectPtr (*global_get_singleton)(const char *p_name); + + GDNativeObjectPtr (*object_cast_to)(const GDNativeObjectPtr p_object, void *p_class_tag); + GDNativeObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id); + GDObjectInstanceID (*object_get_instance_id)(const GDNativeObjectPtr p_object); + + /* CLASSDB */ + + GDNativeClassConstructor (*classdb_get_constructor)(const char *p_classname); + GDNativeMethodBindPtr (*classdb_get_method_bind)(const char *p_classname, const char *p_methodname, GDNativeInt p_hash); + void *(*classdb_get_class_tag)(const char *p_classname); + + /* CLASSDB EXTENSION */ + + void (*classdb_register_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs); + void (*classdb_register_extension_class_method)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info); + void (*classdb_register_extension_class_integer_constant)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_enum_name, const char *p_class_name, const char *p_constant_name, uint32_t p_constant_value); + void (*classdb_register_extension_class_property)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter); + void (*classdb_register_extension_class_signal)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count); + void (*classdb_unregister_extension_class)(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */ +} GDNativeInterface; + +/* INITIALIZATION */ + +typedef enum { + GDNATIVE_INITIALIZATION_CORE, + GDNATIVE_INITIALIZATION_SERVERS, + GDNATIVE_INITIALIZATION_SCENE, + GDNATIVE_INITIALIZATION_EDITOR, +} GDNativeInitializationLevel; + +typedef struct { + /* Minimum initialization level required. + * If Core or Servers, the extension needs editor or game restart to take effect */ + GDNativeInitializationLevel minimum_initialization_level; + /* Up to the user to supply when initializing */ + void *userdata; + /* This function will be called multiple times for each initialization level. */ + void (*initialize)(void *userdata, GDNativeInitializationLevel p_level); + void (*deinitialize)(void *userdata, GDNativeInitializationLevel p_level); +} GDNativeInitialization; + +/* Define a C function prototype that implements the function below and expose it to dlopen() (or similar). + * It will be called on initialization. The name must be an unique one specified in the .gdextension config file. + */ + +typedef GDNativeBool (*GDNativeInitializationFunction)(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp new file mode 100644 index 0000000000..65718a7507 --- /dev/null +++ b/core/extension/native_extension.cpp @@ -0,0 +1,411 @@ +/*************************************************************************/ +/* native_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "native_extension.h" +#include "core/io/config_file.h" +#include "core/object/class_db.h" +#include "core/object/method_bind.h" +#include "core/os/os.h" + +class NativeExtensionMethodBind : public MethodBind { + GDNativeExtensionClassMethodCall call_func; + GDNativeExtensionClassMethodPtrCall ptrcall_func; + GDNativeExtensionClassMethodGetArgumentType get_argument_type_func; + GDNativeExtensionClassMethodGetArgumentInfo get_argument_info_func; + GDNativeExtensionClassMethodGetArgumentMetadata get_argument_metadata_func; + void *method_userdata; + bool vararg; + +protected: + virtual Variant::Type _gen_argument_type(int p_arg) const { + return Variant::Type(get_argument_type_func(method_userdata, p_arg)); + } + virtual PropertyInfo _gen_argument_type_info(int p_arg) const { + GDNativePropertyInfo pinfo; + get_argument_info_func(method_userdata, p_arg, &pinfo); + PropertyInfo ret; + ret.type = Variant::Type(pinfo.type); + ret.name = pinfo.name; + ret.class_name = pinfo.class_name; + ret.hint = PropertyHint(pinfo.hint); + ret.usage = pinfo.usage; + ret.class_name = pinfo.class_name; + return ret; + } + +public: + virtual GodotTypeInfo::Metadata get_argument_meta(int p_arg) const { + return GodotTypeInfo::Metadata(get_argument_metadata_func(method_userdata, p_arg)); + } + + virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + Variant ret; + GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance(); + GDNativeCallError ce; + call_func(extension_instance, (const GDNativeVariantPtr *)p_args, p_arg_count, (GDNativeVariantPtr)&ret, &ce); + r_error.error = Callable::CallError::Error(ce.error); + r_error.argument = ce.argument; + r_error.expected = ce.expected; + return ret; + } + virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { + ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug."); + GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance(); + ptrcall_func(extension_instance, (const GDNativeTypePtr *)p_args, (GDNativeTypePtr)r_ret); + } + + virtual bool is_vararg() const { + return false; + } + NativeExtensionMethodBind(const GDNativeExtensionClassMethodInfo *p_method_info) { + method_userdata = p_method_info->method_userdata; + call_func = p_method_info->call_func; + ptrcall_func = p_method_info->ptrcall_func; + get_argument_type_func = p_method_info->get_argument_type_func; + get_argument_info_func = p_method_info->get_argument_info_func; + get_argument_metadata_func = p_method_info->get_argument_metadata_func; + + vararg = p_method_info->method_flags & GDNATIVE_EXTENSION_METHOD_FLAG_VARARG; + + _set_returns(p_method_info->has_return_value); + _set_const(p_method_info->method_flags & GDNATIVE_EXTENSION_METHOD_FLAG_CONST); +#ifdef DEBUG_METHODS_ENABLED + _generate_argument_types(p_method_info->argument_count); +#endif + set_argument_count(p_method_info->argument_count); + } +}; + +static GDNativeInterface gdnative_interface; + +void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(String(class_name).is_valid_identifier(), "Attempt to register extension clas '" + class_name + "', which is not a valid class identifier."); + ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered."); + + Extension *parent_extension = nullptr; + StringName parent_class_name = p_parent_class_name; + + if (self->extension_classes.has(parent_class_name)) { + parent_extension = &self->extension_classes[parent_class_name]; + } else if (ClassDB::class_exists(parent_class_name)) { + if (ClassDB::get_api_type(parent_class_name) == ClassDB::API_EXTENSION || ClassDB::get_api_type(parent_class_name) == ClassDB::API_EDITOR_EXTENSION) { + ERR_PRINT("Unimplemented yet"); + //inheriting from another extension + } else { + //inheriting from engine class + } + } else { + ERR_FAIL_MSG("Attempt to register an extension class '" + String(class_name) + "' using non-existing parent class '" + String(parent_class_name) + "'"); + } + + self->extension_classes[class_name] = Extension(); + + Extension *extension = &self->extension_classes[class_name]; + + if (parent_extension) { + extension->native_extension.parent = &parent_extension->native_extension; + parent_extension->native_extension.children.push_back(&extension->native_extension); + } + + extension->native_extension.parent_class_name = parent_class_name; + extension->native_extension.class_name = class_name; + extension->native_extension.editor_class = self->level_initialized == INITIALIZATION_LEVEL_EDITOR; + extension->native_extension.set = p_extension_funcs->set_func; + extension->native_extension.get = p_extension_funcs->get_func; + extension->native_extension.get_property_list = p_extension_funcs->get_property_list_func; + extension->native_extension.free_property_list = p_extension_funcs->free_property_list_func; + extension->native_extension.notification = p_extension_funcs->notification_func; + extension->native_extension.to_string = p_extension_funcs->to_string_func; + extension->native_extension.reference = p_extension_funcs->reference_func; + extension->native_extension.unreference = p_extension_funcs->unreference_func; + extension->native_extension.class_userdata = p_extension_funcs->class_userdata; + extension->native_extension.create_instance = p_extension_funcs->create_instance_func; + extension->native_extension.free_instance = p_extension_funcs->free_instance_func; + + ClassDB::register_extension_class(&extension->native_extension); +} +void NativeExtension::_register_extension_class_method(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + StringName method_name = p_method_info->name; + ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension method '" + String(method_name) + "' for unexisting class '" + class_name + "'."); + + //Extension *extension = &self->extension_classes[class_name]; + + NativeExtensionMethodBind *method = memnew(NativeExtensionMethodBind(p_method_info)); + + ClassDB::bind_method_custom(class_name, method); +} +void NativeExtension::_register_extension_class_integer_constant(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, uint32_t p_constant_value) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension constant '" + String(p_constant_name) + "' for unexisting class '" + class_name + "'."); + + //Extension *extension = &self->extension_classes[class_name]; + + ClassDB::bind_integer_constant(class_name, p_enum_name, p_constant_name, p_constant_value); +} +void NativeExtension::_register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension class property '" + String(p_info->name) + "' for unexisting class '" + class_name + "'."); + + //Extension *extension = &self->extension_classes[class_name]; + PropertyInfo pinfo; + pinfo.type = Variant::Type(p_info->type); + pinfo.name = p_info->name; + pinfo.class_name = p_info->class_name; + pinfo.hint = PropertyHint(p_info->hint); + pinfo.hint_string = p_info->hint_string; + pinfo.usage = p_info->usage; + + ClassDB::add_property(class_name, pinfo, p_setter, p_getter); +} + +void NativeExtension::_register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension class signal '" + String(p_signal_name) + "' for unexisting class '" + class_name + "'."); + + MethodInfo s; + s.name = p_signal_name; + for (int i = 0; i < p_argument_count; i++) { + PropertyInfo arg; + arg.type = Variant::Type(p_argument_info[i].type); + arg.name = p_argument_info[i].name; + arg.class_name = p_argument_info[i].class_name; + arg.hint = PropertyHint(p_argument_info[i].hint); + arg.hint_string = p_argument_info[i].hint_string; + arg.usage = p_argument_info[i].usage; + s.arguments.push_back(arg); + } + ClassDB::add_signal(class_name, s); +} + +void NativeExtension::_unregister_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name) { + NativeExtension *self = (NativeExtension *)p_library; + + StringName class_name = p_class_name; + ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to unregister unexisting extension class '" + class_name + "'."); + Extension *ext = &self->extension_classes[class_name]; + ERR_FAIL_COND_MSG(ext->native_extension.children.size(), "Attempt to unregister class '" + class_name + "' while other extension classes inherit from it."); + + ClassDB::unregister_extension_class(class_name); + if (ext->native_extension.parent != nullptr) { + ext->native_extension.parent->children.erase(&ext->native_extension); + } + self->extension_classes.erase(class_name); +} + +Error NativeExtension::open_library(const String &p_path, const String &p_entry_symbol) { + Error err = OS::get_singleton()->open_dynamic_library(p_path, library, true); + if (err != OK) { + return err; + } + + void *entry_funcptr = nullptr; + + err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false); + + if (err != OK) { + OS::get_singleton()->close_dynamic_library(library); + return err; + } + + GDNativeInitializationFunction initialization_function = (GDNativeInitializationFunction)entry_funcptr; + + initialization_function(&gdnative_interface, this, &initialization); + level_initialized = -1; + return OK; +} + +void NativeExtension::close_library() { + ERR_FAIL_COND(library == nullptr); + OS::get_singleton()->close_dynamic_library(library); + + library = nullptr; +} + +bool NativeExtension::is_library_open() const { + return library != nullptr; +} + +NativeExtension::InitializationLevel NativeExtension::get_minimum_library_initialization_level() const { + ERR_FAIL_COND_V(library == nullptr, INITIALIZATION_LEVEL_CORE); + return InitializationLevel(initialization.minimum_initialization_level); +} +void NativeExtension::initialize_library(InitializationLevel p_level) { + ERR_FAIL_COND(library == nullptr); + ERR_FAIL_COND(p_level <= int32_t(level_initialized)); + + level_initialized = int32_t(p_level); + + ERR_FAIL_COND(initialization.initialize == nullptr); + + initialization.initialize(initialization.userdata, GDNativeInitializationLevel(p_level)); +} +void NativeExtension::deinitialize_library(InitializationLevel p_level) { + ERR_FAIL_COND(library == nullptr); + ERR_FAIL_COND(p_level > int32_t(level_initialized)); + + level_initialized = int32_t(p_level) - 1; + initialization.deinitialize(initialization.userdata, GDNativeInitializationLevel(p_level)); +} + +void NativeExtension::_bind_methods() { + ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol"), &NativeExtension::open_library); + ClassDB::bind_method(D_METHOD("close_library"), &NativeExtension::close_library); + ClassDB::bind_method(D_METHOD("is_library_open"), &NativeExtension::is_library_open); + + ClassDB::bind_method(D_METHOD("get_minimum_library_initialization_level"), &NativeExtension::get_minimum_library_initialization_level); + ClassDB::bind_method(D_METHOD("initialize_library", "level"), &NativeExtension::initialize_library); + + BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_CORE); + BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_SERVERS); + BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_SCENE); + BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_EDITOR); +} + +NativeExtension::NativeExtension() { +} + +NativeExtension::~NativeExtension() { + if (library != nullptr) { + close_library(); + } +} + +extern void gdnative_setup_interface(GDNativeInterface *p_interface); + +void NativeExtension::initialize_native_extensions() { + gdnative_setup_interface(&gdnative_interface); + + gdnative_interface.classdb_register_extension_class = _register_extension_class; + gdnative_interface.classdb_register_extension_class_method = _register_extension_class_method; + gdnative_interface.classdb_register_extension_class_integer_constant = _register_extension_class_integer_constant; + gdnative_interface.classdb_register_extension_class_property = _register_extension_class_property; + gdnative_interface.classdb_register_extension_class_signal = _register_extension_class_signal; + gdnative_interface.classdb_unregister_extension_class = _unregister_extension_class; +} + +RES NativeExtensionResourceLoader::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) { + Ref<ConfigFile> config; + config.instantiate(); + + Error err = config->load(p_path); + + if (r_error) { + *r_error = err; + } + + if (err != OK) { + return RES(); + } + + if (!config->has_section_key("configuration", "entry_symbol")) { + if (r_error) { + *r_error = ERR_INVALID_DATA; + } + return RES(); + } + + String entry_symbol = config->get_value("configuration", "entry_symbol"); + + List<String> libraries; + + config->get_section_keys("libraries", &libraries); + + String library_path; + + for (List<String>::Element *E = libraries.front(); E; E = E->next()) { + Vector<String> tags = E->get().split("."); + bool all_tags_met = true; + for (int i = 0; i < tags.size(); i++) { + String tag = tags[i].strip_edges(); + if (!OS::get_singleton()->has_feature(tag)) { + all_tags_met = false; + break; + } + } + + if (all_tags_met) { + library_path = config->get_value("libraries", E->get()); + break; + } + } + + if (library_path != String()) { + if (r_error) { + *r_error = ERR_FILE_NOT_FOUND; + } + return RES(); + } + + if (!library_path.is_resource_file()) { + library_path = p_path.get_base_dir().plus_file(library_path); + } + + Ref<NativeExtension> lib; + lib.instantiate(); + err = lib->open_library(library_path, entry_symbol); + + if (r_error) { + *r_error = err; + } + + if (err != OK) { + return RES(); + } + + return lib; +} + +void NativeExtensionResourceLoader::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("gdextension"); +} + +bool NativeExtensionResourceLoader::handles_type(const String &p_type) const { + return p_type == "NativeExtension"; +} + +String NativeExtensionResourceLoader::get_resource_type(const String &p_path) const { + String el = p_path.get_extension().to_lower(); + if (el == "gdextension") { + return "NativeExtension"; + } + return ""; +} diff --git a/core/extension/native_extension.h b/core/extension/native_extension.h new file mode 100644 index 0000000000..0a23848eb2 --- /dev/null +++ b/core/extension/native_extension.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* native_extension.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef NATIVE_EXTENSION_H +#define NATIVE_EXTENSION_H + +#include "core/extension/gdnative_interface.h" +#include "core/io/resource_loader.h" +#include "core/object/ref_counted.h" + +class NativeExtension : public RefCounted { + GDCLASS(NativeExtension, RefCounted) + + void *library = nullptr; // pointer if valid, + + struct Extension { + ObjectNativeExtension native_extension; + }; + + Map<StringName, Extension> extension_classes; + + static void _register_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_parent_class_name, const GDNativeExtensionClassCreationInfo *p_extension_funcs); + static void _register_extension_class_method(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativeExtensionClassMethodInfo *p_method_info); + static void _register_extension_class_integer_constant(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_enum_name, const char *p_constant_name, uint32_t p_constant_value); + static void _register_extension_class_property(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const GDNativePropertyInfo *p_info, const char *p_setter, const char *p_getter); + static void _register_extension_class_signal(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name, const char *p_signal_name, const GDNativePropertyInfo *p_argument_info, GDNativeInt p_argument_count); + static void _unregister_extension_class(const GDNativeExtensionClassLibraryPtr p_library, const char *p_class_name); + + GDNativeInitialization initialization; + int32_t level_initialized = -1; + +protected: + static void _bind_methods(); + +public: + Error open_library(const String &p_path, const String &p_entry_symbol); + void close_library(); + + enum InitializationLevel { + INITIALIZATION_LEVEL_CORE, + INITIALIZATION_LEVEL_SERVERS, + INITIALIZATION_LEVEL_SCENE, + INITIALIZATION_LEVEL_EDITOR, + }; + + bool is_library_open() const; + + InitializationLevel get_minimum_library_initialization_level() const; + void initialize_library(InitializationLevel p_level); + void deinitialize_library(InitializationLevel p_level); + + static void initialize_native_extensions(); + NativeExtension(); + ~NativeExtension(); +}; + +VARIANT_ENUM_CAST(NativeExtension::InitializationLevel) + +class NativeExtensionResourceLoader : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + +#endif // NATIVEEXTENSION_H diff --git a/core/extension/native_extension_manager.cpp b/core/extension/native_extension_manager.cpp new file mode 100644 index 0000000000..7be2593845 --- /dev/null +++ b/core/extension/native_extension_manager.cpp @@ -0,0 +1,130 @@ +/*************************************************************************/ +/* native_extension_manager.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "native_extension_manager.h" + +NativeExtensionManager::LoadStatus NativeExtensionManager::load_extension(const String &p_path) { + if (native_extension_map.has(p_path)) { + return LOAD_STATUS_ALREADY_LOADED; + } + Ref<NativeExtension> extension = ResourceLoader::load(p_path); + if (extension.is_null()) { + return LOAD_STATUS_FAILED; + } + + if (level >= 0) { //already initialized up to some level + int32_t minimum_level = extension->get_minimum_library_initialization_level(); + if (minimum_level < MIN(level, NativeExtension::INITIALIZATION_LEVEL_SCENE)) { + return LOAD_STATUS_NEEDS_RESTART; + } + //initialize up to current level + for (int32_t i = minimum_level; i < level; i++) { + extension->initialize_library(NativeExtension::InitializationLevel(level)); + } + } + native_extension_map[p_path] = extension; + return LOAD_STATUS_OK; +} + +NativeExtensionManager::LoadStatus NativeExtensionManager::reload_extension(const String &p_path) { + return LOAD_STATUS_OK; //TODO +} +NativeExtensionManager::LoadStatus NativeExtensionManager::unload_extension(const String &p_path) { + if (!native_extension_map.has(p_path)) { + return LOAD_STATUS_NOT_LOADED; + } + + Ref<NativeExtension> extension = native_extension_map[p_path]; + + if (level >= 0) { //already initialized up to some level + int32_t minimum_level = extension->get_minimum_library_initialization_level(); + if (minimum_level < MIN(level, NativeExtension::INITIALIZATION_LEVEL_SCENE)) { + return LOAD_STATUS_NEEDS_RESTART; + } + //initialize up to current level + for (int32_t i = level; i >= minimum_level; i--) { + extension->deinitialize_library(NativeExtension::InitializationLevel(level)); + } + } + native_extension_map.erase(p_path); + return LOAD_STATUS_OK; +} +Vector<String> NativeExtensionManager::get_loaded_extensions() const { + Vector<String> ret; + for (const Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} +Ref<NativeExtension> NativeExtensionManager::get_extension(const String &p_path) { + Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.find(p_path); + ERR_FAIL_COND_V(!E, Ref<NativeExtension>()); + return E->get(); +} + +void NativeExtensionManager::initialize_extensions(NativeExtension::InitializationLevel p_level) { + ERR_FAIL_COND(int32_t(p_level) - 1 != level); + for (Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { + E->get()->initialize_library(p_level); + } + level = p_level; +} + +void NativeExtensionManager::deinitialize_extensions(NativeExtension::InitializationLevel p_level) { + ERR_FAIL_COND(int32_t(p_level) != level); + for (Map<String, Ref<NativeExtension>>::Element *E = native_extension_map.front(); E; E = E->next()) { + E->get()->deinitialize_library(p_level); + } + level = int32_t(p_level) - 1; +} + +NativeExtensionManager *NativeExtensionManager::get_singleton() { + return singleton; +} +void NativeExtensionManager::_bind_methods() { + ClassDB::bind_method(D_METHOD("load_extension", "path"), &NativeExtensionManager::load_extension); + ClassDB::bind_method(D_METHOD("reload_extension", "path"), &NativeExtensionManager::reload_extension); + ClassDB::bind_method(D_METHOD("unload_extension", "path"), &NativeExtensionManager::unload_extension); + ClassDB::bind_method(D_METHOD("get_loaded_extensions"), &NativeExtensionManager::get_loaded_extensions); + ClassDB::bind_method(D_METHOD("get_extension", "path"), &NativeExtensionManager::get_extension); + + BIND_ENUM_CONSTANT(LOAD_STATUS_OK); + BIND_ENUM_CONSTANT(LOAD_STATUS_FAILED); + BIND_ENUM_CONSTANT(LOAD_STATUS_ALREADY_LOADED); + BIND_ENUM_CONSTANT(LOAD_STATUS_NOT_LOADED); + BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART); +} + +NativeExtensionManager *NativeExtensionManager::singleton = nullptr; + +NativeExtensionManager::NativeExtensionManager() { + ERR_FAIL_COND(singleton != nullptr); + singleton = this; +} diff --git a/core/extension/native_extension_manager.h b/core/extension/native_extension_manager.h new file mode 100644 index 0000000000..78465bd5cf --- /dev/null +++ b/core/extension/native_extension_manager.h @@ -0,0 +1,71 @@ +/*************************************************************************/ +/* native_extension_manager.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef NATIVE_EXTENSION_MANAGER_H +#define NATIVE_EXTENSION_MANAGER_H + +#include "core/extension/native_extension.h" + +class NativeExtensionManager : public Object { + GDCLASS(NativeExtensionManager, Object); + + int32_t level = -1; + Map<String, Ref<NativeExtension>> native_extension_map; + + static void _bind_methods(); + + static NativeExtensionManager *singleton; + +public: + enum LoadStatus { + LOAD_STATUS_OK, + LOAD_STATUS_FAILED, + LOAD_STATUS_ALREADY_LOADED, + LOAD_STATUS_NOT_LOADED, + LOAD_STATUS_NEEDS_RESTART, + }; + + LoadStatus load_extension(const String &p_path); + LoadStatus reload_extension(const String &p_path); + LoadStatus unload_extension(const String &p_path); + Vector<String> get_loaded_extensions() const; + Ref<NativeExtension> get_extension(const String &p_path); + + void initialize_extensions(NativeExtension::InitializationLevel p_level); + void deinitialize_extensions(NativeExtension::InitializationLevel p_level); + + static NativeExtensionManager *get_singleton(); + + NativeExtensionManager(); +}; + +VARIANT_ENUM_CAST(NativeExtensionManager::LoadStatus) + +#endif // NATIVEEXTENSIONMANAGER_H |