From dabc96ebd99f2ac7f10f2a8c50e7796d03d27222 Mon Sep 17 00:00:00 2001 From: karroffel Date: Wed, 4 Apr 2018 21:30:28 +0200 Subject: [PATCH] added type tags and safe object casting --- binding_generator.py | 75 ++++++++++++++++++++++++++++++++------ include/core/CoreTypes.hpp | 2 + include/core/Godot.hpp | 28 +++++++++++++- include/core/Ref.hpp | 4 +- include/core/TagDB.hpp | 17 +++++++++ include/core/Wrapped.hpp | 16 ++++++++ src/core/GodotGlobal.cpp | 27 ++++++++------ src/core/TagDB.cpp | 54 +++++++++++++++++++++++++++ 8 files changed, 196 insertions(+), 27 deletions(-) create mode 100644 include/core/TagDB.hpp create mode 100644 include/core/Wrapped.hpp create mode 100644 src/core/TagDB.cpp diff --git a/binding_generator.py b/binding_generator.py index 076c733..80ea074 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -34,6 +34,9 @@ def generate_bindings(path): icall_source_file = open("src/gen/__icalls.cpp", "w+") icall_source_file.write(generate_icall_implementation(icalls)) + register_types_file = open("src/gen/__register_types.cpp", "w+") + register_types_file.write(generate_type_registry(classes)) + def is_reference_type(t): for c in classes: @@ -79,6 +82,8 @@ def generate_class_header(used_classes, c): # so don't include it here because it's not needed if class_name != "Object" and class_name != "Reference": source.append("#include ") + else: + source.append("#include ") included = [] @@ -100,7 +105,6 @@ def generate_class_header(used_classes, c): if c["base_class"] != "": source.append("#include \"" + strip_name(c["base_class"]) + ".hpp\"") - source.append("namespace godot {") source.append("") @@ -118,18 +122,13 @@ def generate_class_header(used_classes, c): vararg_templates = "" # generate the class definition here - source.append("class " + class_name + ("" if c["base_class"] == "" else (" : public " + strip_name(c["base_class"])) ) + " {") + source.append("class " + class_name + (" : public _Wrapped" if c["base_class"] == "" else (" : public " + strip_name(c["base_class"])) ) + " {") if c["base_class"] == "": - # this is Object + source.append("public: enum { ___CLASS_IS_SCRIPT = 0, };") + source.append("private:") source.append("") - source.append("public:") - source.append("\tgodot_object *_owner;") - source.append("") - # TODO decide what to do about virtual methods - # source.append("static void _register_methods();") - # source.append("") - + if c["singleton"]: source.append("\tstatic " + class_name + " *_singleton;") source.append("") @@ -140,6 +139,10 @@ def generate_class_header(used_classes, c): source.append("public:") source.append("") + source.append("\tstatic void *___get_type_tag();") + source.append("\tstatic void *___get_base_type_tag();") + + if c["singleton"]: source.append("\tstatic inline " + class_name + " *get_singleton()") source.append("\t{") @@ -180,6 +183,14 @@ def generate_class_header(used_classes, c): source.append("\tstatic " + class_name + " *_new();") source.append("\n\t// methods") + + + if class_name == "Object": + source.append("#ifndef GODOT_CPP_NO_OBJECT_CAST") + source.append("\ttemplate") + source.append("\tstatic T *cast_to(const Object *obj);") + source.append("#endif") + source.append("") for method in c["methods"]: @@ -264,11 +275,10 @@ def generate_class_header(used_classes, c): source.append("") + source.append("}") source.append("") - - source.append("#endif") @@ -314,6 +324,21 @@ def generate_class_implementation(icalls, used_classes, c): source.append("") source.append("") + + source.append("void *" + class_name + "::___get_type_tag()") + source.append("{") + source.append("\treturn (void *) &" + class_name + "::___get_type_tag;") + source.append("}") + source.append("") + + source.append("void *" + class_name + "::___get_base_type_tag()") + source.append("{") + if c["base_class"] != "": + source.append("\treturn (void *) &" + strip_name(c["base_class"]) + "::___get_type_tag;") + else: + source.append("\treturn (void *) nullptr;") + source.append("}") + source.append("") if c["singleton"]: source.append("" + class_name + " *" + class_name + "::_singleton = NULL;") @@ -640,8 +665,34 @@ def generate_icall_implementation(icalls): +def generate_type_registry(classes): + source = [] + + source.append("#include \"TagDB.hpp\"") + source.append("") + + for c in classes: + source.append("#include <" + strip_name(c["name"]) + ".hpp>") + + source.append("") + source.append("") + + source.append("namespace godot {") + + source.append("void ___register_types()") + source.append("{") + + for c in classes: + class_name = strip_name(c["name"]) + source.append("\tgodot::_TagDB::register_global_type(\"" + class_name + "\", " + class_name + "::___get_type_tag(), " + class_name + "::___get_base_type_tag());") + + source.append("}") + + source.append("") + source.append("}") + return "\n".join(source) diff --git a/include/core/CoreTypes.hpp b/include/core/CoreTypes.hpp index 8f72c4c..ec51597 100644 --- a/include/core/CoreTypes.hpp +++ b/include/core/CoreTypes.hpp @@ -21,5 +21,7 @@ #include "Vector2.hpp" #include "Vector3.hpp" +#include "Wrapped.hpp" + #endif // CORETYPES_H diff --git a/include/core/Godot.hpp b/include/core/Godot.hpp index 933a77a..aa01b27 100644 --- a/include/core/Godot.hpp +++ b/include/core/Godot.hpp @@ -11,6 +11,7 @@ #include "CoreTypes.hpp" #include "Variant.hpp" #include "Ref.hpp" +#include "TagDB.hpp" #include "Object.hpp" @@ -23,7 +24,7 @@ namespace godot { template -T *as(Object *obj) +T *as(const Object *obj) { return (T *) godot::nativescript_api->godot_nativescript_get_userdata(obj->_owner); } @@ -37,9 +38,12 @@ T *get_wrapper(godot_object *obj) #define GODOT_CLASS(Name, Base) \ public: inline static const char *___get_type_name() { return static_cast(#Name); } \ + enum { ___CLASS_IS_SCRIPT = 1, }; \ inline static Name *_new() { godot::NativeScript *script = godot::NativeScript::_new(); script->set_library(godot::get_wrapper((godot_object *) godot::gdnlib)); script->set_class_name(#Name); Name *instance = godot::as(script->new_()); return instance; } \ inline static const char *___get_base_type_name() { return Base::___get_class_name(); } \ inline static Object *___get_from_variant(godot::Variant a) { return (godot::Object *) godot::as(godot::Object::___get_from_variant(a)); } \ + inline static void *___get_type_tag() { return (void *) &Name::___get_type_tag; } \ + inline static void *___get_base_type_tag() { return (void *) &Base::___get_type_tag; } \ private: #define GODOT_SUBCLASS(Name, Base) \ @@ -83,6 +87,7 @@ void *_godot_class_instance_func(godot_object *p, void *method_data) { T *d = new T(); d->_owner = p; + d->_type_tag = T::___get_type_tag(); d->_init(); return d; } @@ -104,8 +109,10 @@ void register_class() godot_instance_destroy_func destroy = {}; destroy.destroy_func = _godot_class_destroy_func; + _TagDB::register_type(T::___get_type_tag(), T::___get_base_type_tag()); godot::nativescript_api->godot_nativescript_register_class(godot::_RegisterState::nativescript_handle, T::___get_type_name(), T::___get_base_type_name(), create, destroy); + godot::nativescript_1_1_api->godot_nativescript_set_type_tag(godot::_RegisterState::nativescript_handle, T::___get_type_name(), T::___get_type_tag()); T::_register_methods(); } @@ -118,8 +125,10 @@ void register_tool_class() godot_instance_destroy_func destroy = {}; destroy.destroy_func = _godot_class_destroy_func; + _TagDB::register_type(T::___get_type_tag(), T::___get_base_type_tag()); godot::nativescript_api->godot_nativescript_register_tool_class(godot::_RegisterState::nativescript_handle, T::___get_type_name(), T::___get_base_type_name(), create, destroy); + godot::nativescript_1_1_api->godot_nativescript_set_type_tag(godot::_RegisterState::nativescript_handle, T::___get_type_name(), T::___get_type_tag()); T::_register_methods(); } @@ -478,6 +487,23 @@ void register_signal(String name, Args... varargs) register_signal(name, Dictionary::make(varargs...)); } + + + + +#ifndef GODOT_CPP_NO_OBJECT_CAST +template +T *Object::cast_to(const Object *obj) +{ + if (godot::_TagDB::is_type_compatible(T::___get_type_tag(), obj->_type_tag)) { + return (T::___CLASS_IS_SCRIPT) ? godot::as(obj) : (T *) obj; + } else { + return nullptr; + } +} +#endif + + } #endif // GODOT_H diff --git a/include/core/Ref.hpp b/include/core/Ref.hpp index bbf2846..e99c834 100644 --- a/include/core/Ref.hpp +++ b/include/core/Ref.hpp @@ -107,7 +107,7 @@ public: void operator=(const Variant &p_variant) { // TODO We need a safe cast - Reference *refb = (Reference *) Object::___get_from_variant(p_variant); + Reference *refb = (Reference *) T::___get_from_variant(p_variant); if (!refb) { unref(); return; @@ -156,7 +156,7 @@ public: reference = nullptr; // TODO We need a safe cast - Reference *refb = (Reference *) Object::___get_from_variant(p_variant); + Reference *refb = (Reference *) T::___get_from_variant(p_variant); if (!refb) { unref(); return; diff --git a/include/core/TagDB.hpp b/include/core/TagDB.hpp new file mode 100644 index 0000000..a1c0434 --- /dev/null +++ b/include/core/TagDB.hpp @@ -0,0 +1,17 @@ +#ifndef TAGDB_HPP +#define TAGDB_HPP + +namespace godot { + +namespace _TagDB { + +void register_type(const void *type_tag, const void *base_type_tag); +void register_global_type(const char *name, const void *type_tag, const void *base_type_tag); + +bool is_type_compatible(const void *type_tag, const void *base_type_tag); + +} + +} + +#endif // TAGDB_HPP diff --git a/include/core/Wrapped.hpp b/include/core/Wrapped.hpp new file mode 100644 index 0000000..bc446e7 --- /dev/null +++ b/include/core/Wrapped.hpp @@ -0,0 +1,16 @@ +#ifndef WRAPPED_HPP +#define WRAPPED_HPP + +#include + +namespace godot { + +class _Wrapped { +public: + godot_object *_owner; + const void *_type_tag; +}; + +} + +#endif // WRAPPED_HPP diff --git a/src/core/GodotGlobal.cpp b/src/core/GodotGlobal.cpp index 3d26159..0ea1ce1 100644 --- a/src/core/GodotGlobal.cpp +++ b/src/core/GodotGlobal.cpp @@ -2,18 +2,18 @@ #include "String.hpp" -static GDCALLINGCONV void *wrapper_create(void *data, godot_object *instance) +#include "Wrapped.hpp" + +static GDCALLINGCONV void *wrapper_create(void *data, const void *type_tag, godot_object *instance) { - void *wrapper_memory = godot::api->godot_alloc(sizeof(instance)); + godot::_Wrapped *wrapper_memory = (godot::_Wrapped *) godot::api->godot_alloc(sizeof(godot::_Wrapped)); if (!wrapper_memory) return NULL; + wrapper_memory->_owner = instance; + wrapper_memory->_type_tag = type_tag; - godot_object **wrapper = (godot_object **) wrapper_memory; - - *wrapper = instance; - - return wrapper; + return (void *) wrapper_memory; } static GDCALLINGCONV void wrapper_destroy(void *data, void *wrapper) @@ -71,6 +71,8 @@ void Godot::print_error(const String& description, const String& function, const if (c_file != nullptr) godot::api->godot_free(c_file); } +void ___register_types(); + void Godot::gdnative_init(godot_gdnative_init_options *options) { godot::api = options->api_struct; @@ -91,11 +93,12 @@ void Godot::gdnative_init(godot_gdnative_init_options *options) extension = extension->next; } - } break; - default: { - }break; - }; - }; + } break; + default: break; + } + } + + ___register_types(); } void Godot::gdnative_terminate(godot_gdnative_terminate_options *options) diff --git a/src/core/TagDB.cpp b/src/core/TagDB.cpp new file mode 100644 index 0000000..28f44a2 --- /dev/null +++ b/src/core/TagDB.cpp @@ -0,0 +1,54 @@ +#include "TagDB.hpp" + +#include + +#include + +namespace godot { + +namespace _TagDB { + +std::unordered_map parent_to; + +void register_type(const void *type_tag, const void *base_type_tag) +{ + parent_to[type_tag] = base_type_tag; +} + +void register_global_type(const char *name, const void *type_tag, const void *base_type_tag) +{ + + godot::nativescript_1_1_api->godot_nativescript_set_global_type_tag(godot::_RegisterState::language_index, name, type_tag); + + register_type(type_tag, base_type_tag); +} + +bool is_type_compatible(const void *type_tag, const void *base_type_tag) +{ + if (type_tag == nullptr || base_type_tag == nullptr) + return false; + + if (type_tag == base_type_tag) + return true; + + const void *tag = type_tag; + + while (true) { + if (tag == base_type_tag) { + return true; + } + + if (tag == nullptr) { + return false; + } + + tag = parent_to[tag]; + } + + return false; +} + +} + + +}