Bladeren bron

added type tags and safe object casting

karroffel 7 jaren geleden
bovenliggende
commit
dabc96ebd9
8 gewijzigde bestanden met toevoegingen van 196 en 27 verwijderingen
  1. 63 12
      binding_generator.py
  2. 2 0
      include/core/CoreTypes.hpp
  3. 27 1
      include/core/Godot.hpp
  4. 2 2
      include/core/Ref.hpp
  5. 17 0
      include/core/TagDB.hpp
  6. 16 0
      include/core/Wrapped.hpp
  7. 15 12
      src/core/GodotGlobal.cpp
  8. 54 0
      src/core/TagDB.cpp

+ 63 - 12
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 <core/Ref.hpp>")
+    else:
+        source.append("#include <TagDB.hpp>")
 
 
     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<class T>")
+        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)
 
 
 

+ 2 - 0
include/core/CoreTypes.hpp

@@ -21,5 +21,7 @@
 #include "Vector2.hpp"
 #include "Vector3.hpp"
 
+#include "Wrapped.hpp"
+
 
 #endif // CORETYPES_H

+ 27 - 1
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<class T>
-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<const char *>(#Name); } \
+	enum { ___CLASS_IS_SCRIPT = 1, }; \
 	inline static Name *_new() { godot::NativeScript *script = godot::NativeScript::_new(); script->set_library(godot::get_wrapper<godot::GDNativeLibrary>((godot_object *) godot::gdnlib)); script->set_class_name(#Name); Name *instance = godot::as<Name>(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<Name>(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<T>;
 
+	_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<T>;
 
+	_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<T>(name, Dictionary::make(varargs...));
 }
 
+
+
+
+
+#ifndef GODOT_CPP_NO_OBJECT_CAST
+template<class T>
+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<T>(obj) : (T *) obj;
+	} else {
+		return nullptr;
+	}
+}
+#endif
+
+
 }
 
 #endif // GODOT_H

+ 2 - 2
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;

+ 17 - 0
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

+ 16 - 0
include/core/Wrapped.hpp

@@ -0,0 +1,16 @@
+#ifndef WRAPPED_HPP
+#define WRAPPED_HPP
+
+#include <gdnative/gdnative.h>
+
+namespace godot {
+
+class _Wrapped {
+public:
+	godot_object *_owner;
+	const void *_type_tag;
+};
+
+}
+
+#endif // WRAPPED_HPP

+ 15 - 12
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)

+ 54 - 0
src/core/TagDB.cpp

@@ -0,0 +1,54 @@
+#include "TagDB.hpp"
+
+#include <unordered_map>
+
+#include <GodotGlobal.hpp>
+
+namespace godot {
+
+namespace _TagDB {
+
+std::unordered_map<const void *, const void *> 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;
+}
+
+}
+
+
+}