2
0
Эх сурвалжийг харах

Merge pull request #1465 from dsnopek/4.2-cherrypicks-4

Cherry-picks for the godot-cpp 4.2 branch - 4th batch
David Snopek 1 жил өмнө
parent
commit
2b6eb6832e

+ 78 - 12
binding_generator.py

@@ -181,6 +181,10 @@ def generate_bindings(api_filepath, use_template_get_node, bits="64", precision=
     generate_utility_functions(api, target_dir)
 
 
+CLASS_ALIASES = {
+    "ClassDB": "ClassDBSingleton",
+}
+
 builtin_classes = []
 
 # Key is class name, value is boolean where True means the class is refcounted.
@@ -1127,9 +1131,9 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
     # First create map of classes and singletons.
     for class_api in api["classes"]:
         # Generate code for the ClassDB singleton under a different name.
-        if class_api["name"] == "ClassDB":
-            class_api["name"] = "ClassDBSingleton"
-            class_api["alias_for"] = "ClassDB"
+        if class_api["name"] in CLASS_ALIASES:
+            class_api["alias_for"] = class_api["name"]
+            class_api["name"] = CLASS_ALIASES[class_api["alias_for"]]
         engine_classes[class_api["name"]] = class_api["is_refcounted"]
     for native_struct in api["native_structures"]:
         if native_struct["name"] == "ObjectID":
@@ -1139,9 +1143,9 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
 
     for singleton in api["singletons"]:
         # Generate code for the ClassDB singleton under a different name.
-        if singleton["name"] == "ClassDB":
-            singleton["name"] = "ClassDBSingleton"
-            singleton["alias_for"] = "ClassDB"
+        if singleton["name"] in CLASS_ALIASES:
+            singleton["alias_for"] = singleton["name"]
+            singleton["name"] = CLASS_ALIASES[singleton["name"]]
         singletons.append(singleton["name"])
 
     for class_api in api["classes"]:
@@ -1346,6 +1350,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
         result.append("#include <type_traits>")
         result.append("")
 
+    if class_name == "ClassDBSingleton":
+        result.append("#include <godot_cpp/core/binder_common.hpp>")
+        result.append("")
+
     result.append("namespace godot {")
     result.append("")
 
@@ -1367,6 +1375,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
         result.append(f"\tGDEXTENSION_CLASS({class_name}, {inherits})")
     result.append("")
 
+    if is_singleton:
+        result.append(f"\tstatic {class_name} *singleton;")
+        result.append("")
+
     result.append("public:")
     result.append("")
 
@@ -1447,6 +1459,11 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
 
     result.append("\t}")
     result.append("")
+
+    if is_singleton:
+        result.append(f"\t~{class_name}();")
+        result.append("")
+
     result.append("public:")
 
     # Special cases.
@@ -1504,6 +1521,19 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
 
     if class_name == "ClassDBSingleton":
         result.append("#define CLASSDB_SINGLETON_FORWARD_METHODS \\")
+
+        if "enums" in class_api:
+            for enum_api in class_api["enums"]:
+                if enum_api["is_bitfield"]:
+                    result.append(f'\tenum {enum_api["name"]} : uint64_t {{ \\')
+                else:
+                    result.append(f'\tenum {enum_api["name"]} {{ \\')
+
+                for value in enum_api["values"]:
+                    result.append(f'\t\t{value["name"]} = {value["value"]}, \\')
+                result.append("\t}; \\")
+                result.append("\t \\")
+
         for method in class_api["methods"]:
             # ClassDBSingleton shouldn't have any static or vararg methods, but if some appear later, lets skip them.
             if vararg:
@@ -1512,12 +1542,17 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
                 continue
 
             method_signature = "\tstatic "
+            return_type = None
             if "return_type" in method:
-                method_signature += f'{correct_type(method["return_type"])} '
+                return_type = correct_type(method["return_type"].replace("ClassDBSingleton", "ClassDB"), None, False)
             elif "return_value" in method:
-                method_signature += (
-                    correct_type(method["return_value"]["type"], method["return_value"].get("meta", None)) + " "
+                return_type = correct_type(
+                    method["return_value"]["type"].replace("ClassDBSingleton", "ClassDB"),
+                    method["return_value"].get("meta", None),
+                    False,
                 )
+            if return_type is not None:
+                method_signature += return_type + " "
             else:
                 method_signature += "void "
 
@@ -1536,8 +1571,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
             result.append(method_signature)
 
             method_body = "\t\t"
-            if "return_type" in method or "return_value" in method:
+            if return_type is not None:
                 method_body += "return "
+                if "alias_for" in class_api and return_type.startswith(class_api["alias_for"] + "::"):
+                    method_body += f"({return_type})"
             method_body += f'ClassDBSingleton::get_singleton()->{method["name"]}('
             method_body += ", ".join(map(lambda x: escape_identifier(x["name"]), method_arguments))
             method_body += "); \\"
@@ -1547,6 +1584,18 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
         result.append("\t;")
         result.append("")
 
+        result.append("#define CLASSDB_SINGLETON_VARIANT_CAST \\")
+
+        if "enums" in class_api:
+            for enum_api in class_api["enums"]:
+                if enum_api["is_bitfield"]:
+                    result.append(f'\tVARIANT_BITFIELD_CAST({class_api["alias_for"]}::{enum_api["name"]}); \\')
+                else:
+                    result.append(f'\tVARIANT_ENUM_CAST({class_api["alias_for"]}::{enum_api["name"]}); \\')
+
+        result.append("\t;")
+        result.append("")
+
     result.append(f"#endif // ! {header_guard}")
 
     return "\n".join(result)
@@ -1564,6 +1613,7 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
 
     result.append(f"#include <godot_cpp/classes/{snake_class_name}.hpp>")
     result.append("")
+    result.append("#include <godot_cpp/core/class_db.hpp>")
     result.append("#include <godot_cpp/core/engine_ptrcall.hpp>")
     result.append("#include <godot_cpp/core/error_macros.hpp>")
     result.append("")
@@ -1578,9 +1628,10 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
     result.append("")
 
     if is_singleton:
+        result.append(f"{class_name} *{class_name}::singleton = nullptr;")
+        result.append("")
         result.append(f"{class_name} *{class_name}::get_singleton() {{")
         # We assume multi-threaded access is OK because each assignment will assign the same value every time
-        result.append(f"\tstatic {class_name} *singleton = nullptr;")
         result.append("\tif (unlikely(singleton == nullptr)) {")
         result.append(
             f"\t\tGDExtensionObjectPtr singleton_obj = internal::gdextension_interface_global_get_singleton({class_name}::get_class_static()._native_ptr());"
@@ -1594,11 +1645,22 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
         result.append("#ifdef DEBUG_ENABLED")
         result.append("\t\tERR_FAIL_NULL_V(singleton, nullptr);")
         result.append("#endif // DEBUG_ENABLED")
+        result.append("\t\tif (likely(singleton)) {")
+        result.append(f"\t\t\tClassDB::_register_engine_singleton({class_name}::get_class_static(), singleton);")
+        result.append("\t\t}")
         result.append("\t}")
         result.append("\treturn singleton;")
         result.append("}")
         result.append("")
 
+        result.append(f"{class_name}::~{class_name}() {{")
+        result.append("\tif (singleton == this) {")
+        result.append(f"\t\tClassDB::_unregister_engine_singleton({class_name}::get_class_static());")
+        result.append("\t\tsingleton = nullptr;")
+        result.append("\t}")
+        result.append("}")
+        result.append("")
+
     if "methods" in class_api:
         for method in class_api["methods"]:
             if method["is_virtual"]:
@@ -2384,7 +2446,7 @@ def correct_typed_array(type_name):
     return type_name
 
 
-def correct_type(type_name, meta=None):
+def correct_type(type_name, meta=None, use_alias=True):
     type_conversion = {"float": "double", "int": "int64_t", "Nil": "Variant"}
     if meta != None:
         if "int" in meta:
@@ -2400,11 +2462,15 @@ def correct_type(type_name, meta=None):
     if is_enum(type_name):
         if is_bitfield(type_name):
             base_class = get_enum_class(type_name)
+            if use_alias and base_class in CLASS_ALIASES:
+                base_class = CLASS_ALIASES[base_class]
             if base_class == "GlobalConstants":
                 return f"BitField<{get_enum_name(type_name)}>"
             return f"BitField<{base_class}::{get_enum_name(type_name)}>"
         else:
             base_class = get_enum_class(type_name)
+            if use_alias and base_class in CLASS_ALIASES:
+                base_class = CLASS_ALIASES[base_class]
             if base_class == "GlobalConstants":
                 return f"{get_enum_name(type_name)}"
             return f"{base_class}::{get_enum_name(type_name)}"

+ 19 - 8
include/godot_cpp/classes/wrapped.hpp

@@ -88,14 +88,15 @@ protected:
 	::godot::List<::godot::PropertyInfo> plist_owned;
 
 	void _postinitialize();
+	virtual void _notificationv(int32_t p_what, bool p_reversed = false) {}
 
 	Wrapped(const StringName p_godot_class);
 	Wrapped(GodotObject *p_godot_object);
 	virtual ~Wrapped() {}
 
 public:
-	static StringName &get_class_static() {
-		static StringName string_name = StringName("Wrapped");
+	static const StringName &get_class_static() {
+		static const StringName string_name = StringName("Wrapped");
 		return string_name;
 	}
 
@@ -193,6 +194,7 @@ protected:
                                                                                                                                                                                        \
 public:                                                                                                                                                                                \
 	typedef m_class self_type;                                                                                                                                                         \
+	typedef m_inherits parent_type;                                                                                                                                                    \
                                                                                                                                                                                        \
 	static void initialize_class() {                                                                                                                                                   \
 		static bool initialized = false;                                                                                                                                               \
@@ -207,12 +209,12 @@ public:
 		initialized = true;                                                                                                                                                            \
 	}                                                                                                                                                                                  \
                                                                                                                                                                                        \
-	static ::godot::StringName &get_class_static() {                                                                                                                                   \
-		static ::godot::StringName string_name = ::godot::StringName(#m_class);                                                                                                        \
+	static const ::godot::StringName &get_class_static() {                                                                                                                             \
+		static const ::godot::StringName string_name = ::godot::StringName(#m_class);                                                                                                  \
 		return string_name;                                                                                                                                                            \
 	}                                                                                                                                                                                  \
                                                                                                                                                                                        \
-	static ::godot::StringName &get_parent_class_static() {                                                                                                                            \
+	static const ::godot::StringName &get_parent_class_static() {                                                                                                                      \
 		return m_inherits::get_class_static();                                                                                                                                         \
 	}                                                                                                                                                                                  \
                                                                                                                                                                                        \
@@ -352,6 +354,11 @@ public:
 		_gde_binding_reference_callback,                                                                                                                                               \
 	};                                                                                                                                                                                 \
                                                                                                                                                                                        \
+protected:                                                                                                                                                                             \
+	virtual void _notificationv(int32_t p_what, bool p_reversed = false) override {                                                                                                    \
+		m_class::notification_bind(this, p_what, p_reversed);                                                                                                                          \
+	}                                                                                                                                                                                  \
+                                                                                                                                                                                       \
 private:
 
 // Don't use this for your classes, use GDCLASS() instead.
@@ -359,6 +366,7 @@ private:
 private:                                                                                                                                                                               \
 	inline static ::godot::internal::EngineClassRegistration<m_class> _gde_engine_class_registration_helper;                                                                           \
 	void operator=(const m_class &p_rval) {}                                                                                                                                           \
+	friend class ::godot::ClassDB;                                                                                                                                                     \
                                                                                                                                                                                        \
 protected:                                                                                                                                                                             \
 	virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override {                                                                                      \
@@ -368,6 +376,8 @@ protected:
 	m_class(const char *p_godot_class) : m_inherits(p_godot_class) {}                                                                                                                  \
 	m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {}                                                                                                               \
                                                                                                                                                                                        \
+	static void _bind_methods() {}                                                                                                                                                     \
+                                                                                                                                                                                       \
 	static void (*_get_bind_methods())() {                                                                                                                                             \
 		return nullptr;                                                                                                                                                                \
 	}                                                                                                                                                                                  \
@@ -410,15 +420,16 @@ protected:
                                                                                                                                                                                        \
 public:                                                                                                                                                                                \
 	typedef m_class self_type;                                                                                                                                                         \
+	typedef m_inherits parent_type;                                                                                                                                                    \
                                                                                                                                                                                        \
 	static void initialize_class() {}                                                                                                                                                  \
                                                                                                                                                                                        \
-	static ::godot::StringName &get_class_static() {                                                                                                                                   \
-		static ::godot::StringName string_name = ::godot::StringName(#m_alias_for);                                                                                                    \
+	static const ::godot::StringName &get_class_static() {                                                                                                                             \
+		static const ::godot::StringName string_name = ::godot::StringName(#m_alias_for);                                                                                              \
 		return string_name;                                                                                                                                                            \
 	}                                                                                                                                                                                  \
                                                                                                                                                                                        \
-	static ::godot::StringName &get_parent_class_static() {                                                                                                                            \
+	static const ::godot::StringName &get_parent_class_static() {                                                                                                                      \
 		return m_inherits::get_class_static();                                                                                                                                         \
 	}                                                                                                                                                                                  \
                                                                                                                                                                                        \

+ 22 - 0
include/godot_cpp/core/class_db.hpp

@@ -45,6 +45,7 @@
 #include <godot_cpp/variant/callable_method_pointer.hpp>
 
 #include <list>
+#include <mutex>
 #include <set>
 #include <string>
 #include <unordered_map>
@@ -104,6 +105,8 @@ private:
 	static std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *> instance_binding_callbacks;
 	// Used to remember the custom class registration order.
 	static std::vector<StringName> class_register_order;
+	static std::unordered_map<StringName, Object *> engine_singletons;
+	static std::mutex engine_singletons_mutex;
 
 	static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const void **p_defs, int p_defcount);
 	static void initialize_class(const ClassInfo &cl);
@@ -151,6 +154,21 @@ public:
 		instance_binding_callbacks[p_name] = p_callbacks;
 	}
 
+	static void _register_engine_singleton(const StringName &p_class_name, Object *p_singleton) {
+		std::lock_guard<std::mutex> lock(engine_singletons_mutex);
+		std::unordered_map<StringName, Object *>::const_iterator i = engine_singletons.find(p_class_name);
+		if (i != engine_singletons.end()) {
+			ERR_FAIL_COND((*i).second != p_singleton);
+			return;
+		}
+		engine_singletons[p_class_name] = p_singleton;
+	}
+
+	static void _unregister_engine_singleton(const StringName &p_class_name) {
+		std::lock_guard<std::mutex> lock(engine_singletons_mutex);
+		engine_singletons.erase(p_class_name);
+	}
+
 	template <typename N, typename M, typename... VarArgs>
 	static MethodBind *bind_method(N p_method_name, M p_method, VarArgs... p_args);
 
@@ -198,6 +216,8 @@ public:
 template <typename T, bool is_abstract>
 void ClassDB::_register_class(bool p_virtual, bool p_exposed) {
 	static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
+	static_assert(!FunctionsAreSame<T::self_type::_bind_methods, T::parent_type::_bind_methods>::value, "Class must declare 'static void _bind_methods'.");
+	static_assert(!std::is_abstract_v<T> || is_abstract, "Class is abstract, please use GDREGISTER_ABSTRACT_CLASS.");
 	instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks;
 
 	// Register this class within our plugin
@@ -325,4 +345,6 @@ MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p
 
 } // namespace godot
 
+CLASSDB_SINGLETON_VARIANT_CAST;
+
 #endif // GODOT_CLASS_DB_HPP

+ 8 - 0
include/godot_cpp/core/math.hpp

@@ -613,6 +613,14 @@ inline bool is_inf(double p_val) {
 	return std::isinf(p_val);
 }
 
+inline bool is_finite(float p_val) {
+	return std::isfinite(p_val);
+}
+
+inline bool is_finite(double p_val) {
+	return std::isfinite(p_val);
+}
+
 inline bool is_equal_approx(float a, float b) {
 	// Check for exact equality first, required to handle "infinity" values.
 	if (a == b) {

+ 4 - 4
include/godot_cpp/core/method_ptrcall.hpp

@@ -170,11 +170,11 @@ template <typename T>
 struct PtrToArg<T *> {
 	static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object");
 	_FORCE_INLINE_ static T *convert(const void *p_ptr) {
-		return reinterpret_cast<T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr))));
+		return likely(p_ptr) ? reinterpret_cast<T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr)))) : nullptr;
 	}
 	typedef Object *EncodeT;
 	_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
-		*reinterpret_cast<const void **>(p_ptr) = p_var ? p_var->_owner : nullptr;
+		*reinterpret_cast<const void **>(p_ptr) = likely(p_var) ? p_var->_owner : nullptr;
 	}
 };
 
@@ -182,11 +182,11 @@ template <typename T>
 struct PtrToArg<const T *> {
 	static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object");
 	_FORCE_INLINE_ static const T *convert(const void *p_ptr) {
-		return reinterpret_cast<const T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr))));
+		return likely(p_ptr) ? reinterpret_cast<const T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr)))) : nullptr;
 	}
 	typedef const Object *EncodeT;
 	_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
-		*reinterpret_cast<const void **>(p_ptr) = p_var ? p_var->_owner : nullptr;
+		*reinterpret_cast<const void **>(p_ptr) = likely(p_var) ? p_var->_owner : nullptr;
 	}
 };
 

+ 34 - 0
include/godot_cpp/core/property_info.hpp

@@ -72,6 +72,40 @@ struct PropertyInfo {
 	PropertyInfo(const GDExtensionPropertyInfo *p_info) :
 			PropertyInfo(p_info->type, *reinterpret_cast<StringName *>(p_info->name), (PropertyHint)p_info->hint, *reinterpret_cast<String *>(p_info->hint_string), p_info->usage, *reinterpret_cast<StringName *>(p_info->class_name)) {}
 
+	operator Dictionary() const {
+		Dictionary dict;
+		dict["name"] = name;
+		dict["class_name"] = class_name;
+		dict["type"] = type;
+		dict["hint"] = hint;
+		dict["hint_string"] = hint_string;
+		dict["usage"] = usage;
+		return dict;
+	}
+
+	static PropertyInfo from_dict(const Dictionary &p_dict) {
+		PropertyInfo pi;
+		if (p_dict.has("type")) {
+			pi.type = Variant::Type(int(p_dict["type"]));
+		}
+		if (p_dict.has("name")) {
+			pi.name = p_dict["name"];
+		}
+		if (p_dict.has("class_name")) {
+			pi.class_name = p_dict["class_name"];
+		}
+		if (p_dict.has("hint")) {
+			pi.hint = PropertyHint(int(p_dict["hint"]));
+		}
+		if (p_dict.has("hint_string")) {
+			pi.hint_string = p_dict["hint_string"];
+		}
+		if (p_dict.has("usage")) {
+			pi.usage = p_dict["usage"];
+		}
+		return pi;
+	}
+
 	void _update(GDExtensionPropertyInfo *p_info) {
 		p_info->type = (GDExtensionVariantType)type;
 		*(reinterpret_cast<StringName *>(p_info->name)) = name;

+ 10 - 0
include/godot_cpp/core/type_info.hpp

@@ -58,6 +58,16 @@ struct TypesAreSame<A, A> {
 	static bool const value = true;
 };
 
+template <auto A, auto B>
+struct FunctionsAreSame {
+	static bool const value = false;
+};
+
+template <auto A>
+struct FunctionsAreSame<A, A> {
+	static bool const value = true;
+};
+
 template <typename B, typename D>
 struct TypeInherits {
 	static D *get_d();

+ 1 - 0
include/godot_cpp/godot.hpp

@@ -160,6 +160,7 @@ extern "C" GDExtensionInterfaceObjectDestroy gdextension_interface_object_destro
 extern "C" GDExtensionInterfaceGlobalGetSingleton gdextension_interface_global_get_singleton;
 extern "C" GDExtensionInterfaceObjectGetInstanceBinding gdextension_interface_object_get_instance_binding;
 extern "C" GDExtensionInterfaceObjectSetInstanceBinding gdextension_interface_object_set_instance_binding;
+extern "C" GDExtensionInterfaceObjectFreeInstanceBinding gdextension_interface_object_free_instance_binding;
 extern "C" GDExtensionInterfaceObjectSetInstance gdextension_interface_object_set_instance;
 extern "C" GDExtensionInterfaceObjectGetClassName gdextension_interface_object_get_class_name;
 extern "C" GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to;

+ 1 - 0
include/godot_cpp/variant/aabb.hpp

@@ -65,6 +65,7 @@ struct _NO_DISCARD_ AABB {
 	bool operator!=(const AABB &p_rval) const;
 
 	bool is_equal_approx(const AABB &p_aabb) const;
+	bool is_finite() const;
 	_FORCE_INLINE_ bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap
 	_FORCE_INLINE_ bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap
 	_FORCE_INLINE_ bool encloses(const AABB &p_aabb) const; /// p_aabb is completely inside this

+ 1 - 0
include/godot_cpp/variant/basis.hpp

@@ -128,6 +128,7 @@ struct _NO_DISCARD_ Basis {
 	}
 
 	bool is_equal_approx(const Basis &p_basis) const;
+	bool is_finite() const;
 
 	bool operator==(const Basis &p_matrix) const;
 	bool operator!=(const Basis &p_matrix) const;

+ 1 - 0
include/godot_cpp/variant/plane.hpp

@@ -77,6 +77,7 @@ struct _NO_DISCARD_ Plane {
 	Plane operator-() const { return Plane(-normal, -d); }
 	bool is_equal_approx(const Plane &p_plane) const;
 	bool is_equal_approx_any_side(const Plane &p_plane) const;
+	bool is_finite() const;
 
 	_FORCE_INLINE_ bool operator==(const Plane &p_plane) const;
 	_FORCE_INLINE_ bool operator!=(const Plane &p_plane) const;

+ 1 - 0
include/godot_cpp/variant/quaternion.hpp

@@ -55,6 +55,7 @@ struct _NO_DISCARD_ Quaternion {
 	}
 	_FORCE_INLINE_ real_t length_squared() const;
 	bool is_equal_approx(const Quaternion &p_quaternion) const;
+	bool is_finite() const;
 	real_t length() const;
 	void normalize();
 	Quaternion normalized() const;

+ 1 - 0
include/godot_cpp/variant/rect2.hpp

@@ -209,6 +209,7 @@ struct _NO_DISCARD_ Rect2 {
 	}
 
 	bool is_equal_approx(const Rect2 &p_rect) const;
+	bool is_finite() const;
 
 	bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; }
 	bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; }

+ 1 - 0
include/godot_cpp/variant/transform2d.hpp

@@ -99,6 +99,7 @@ struct _NO_DISCARD_ Transform2D {
 	void orthonormalize();
 	Transform2D orthonormalized() const;
 	bool is_equal_approx(const Transform2D &p_transform) const;
+	bool is_finite() const;
 
 	Transform2D looking_at(const Vector2 &p_target) const;
 

+ 1 - 0
include/godot_cpp/variant/transform3d.hpp

@@ -78,6 +78,7 @@ struct _NO_DISCARD_ Transform3D {
 	void orthogonalize();
 	Transform3D orthogonalized() const;
 	bool is_equal_approx(const Transform3D &p_transform) const;
+	bool is_finite() const;
 
 	bool operator==(const Transform3D &p_transform) const;
 	bool operator!=(const Transform3D &p_transform) const;

+ 1 - 0
include/godot_cpp/variant/vector2.hpp

@@ -123,6 +123,7 @@ struct _NO_DISCARD_ Vector2 {
 
 	bool is_equal_approx(const Vector2 &p_v) const;
 	bool is_zero_approx() const;
+	bool is_finite() const;
 
 	Vector2 operator+(const Vector2 &p_v) const;
 	void operator+=(const Vector2 &p_v);

+ 1 - 0
include/godot_cpp/variant/vector3.hpp

@@ -146,6 +146,7 @@ struct _NO_DISCARD_ Vector3 {
 
 	bool is_equal_approx(const Vector3 &p_v) const;
 	bool is_zero_approx() const;
+	bool is_finite() const;
 
 	/* Operators */
 

+ 1 - 0
include/godot_cpp/variant/vector4.hpp

@@ -81,6 +81,7 @@ struct _NO_DISCARD_ Vector4 {
 	_FORCE_INLINE_ real_t length_squared() const;
 	bool is_equal_approx(const Vector4 &p_vec4) const;
 	bool is_zero_approx() const;
+	bool is_finite() const;
 	real_t length() const;
 	void normalize();
 	Vector4 normalized() const;

+ 1 - 4
src/classes/wrapped.cpp

@@ -51,10 +51,7 @@ void Wrapped::_postinitialize() {
 	}
 	godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks());
 	if (extension_class) {
-		Object *obj = dynamic_cast<Object *>(this);
-		if (obj) {
-			obj->notification(Object::NOTIFICATION_POSTINITIALIZE);
-		}
+		_notificationv(Object::NOTIFICATION_POSTINITIALIZE);
 	}
 }
 

+ 18 - 0
src/core/class_db.cpp

@@ -42,6 +42,8 @@ namespace godot {
 std::unordered_map<StringName, ClassDB::ClassInfo> ClassDB::classes;
 std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *> ClassDB::instance_binding_callbacks;
 std::vector<StringName> ClassDB::class_register_order;
+std::unordered_map<StringName, Object *> ClassDB::engine_singletons;
+std::mutex ClassDB::engine_singletons_mutex;
 GDExtensionInitializationLevel ClassDB::current_level = GDEXTENSION_INITIALIZATION_CORE;
 
 MethodDefinition D_METHOD(StringName p_name) {
@@ -378,6 +380,22 @@ void ClassDB::deinitialize(GDExtensionInitializationLevel p_level) {
 		});
 		class_register_order.erase(it, class_register_order.end());
 	}
+
+	if (p_level == GDEXTENSION_INITIALIZATION_CORE) {
+		// Make a new list of the singleton objects, since freeing the instance bindings will lead to
+		// elements getting removed from engine_singletons.
+		std::vector<Object *> singleton_objects;
+		{
+			std::lock_guard<std::mutex> lock(engine_singletons_mutex);
+			singleton_objects.reserve(engine_singletons.size());
+			for (const std::pair<StringName, Object *> &pair : engine_singletons) {
+				singleton_objects.push_back(pair.second);
+			}
+		}
+		for (std::vector<Object *>::iterator i = singleton_objects.begin(); i != singleton_objects.end(); i++) {
+			internal::gdextension_interface_object_free_instance_binding((*i)->_owner, internal::token);
+		}
+	}
 }
 
 } // namespace godot

+ 58 - 0
src/core/object.cpp

@@ -60,8 +60,66 @@ Object *get_object_instance_binding(GodotObject *p_engine_object) {
 	return reinterpret_cast<Object *>(gdextension_interface_object_get_instance_binding(p_engine_object, token, binding_callbacks));
 }
 
+TypedArray<Dictionary> convert_property_list(const std::vector<PropertyInfo> &p_list) {
+	TypedArray<Dictionary> va;
+	for (const PropertyInfo &pi : p_list) {
+		va.push_back(Dictionary(pi));
+	}
+	return va;
+}
+
 } // namespace internal
 
+MethodInfo::operator Dictionary() const {
+	Dictionary dict;
+	dict["name"] = name;
+	dict["args"] = internal::convert_property_list(arguments);
+	Array da;
+	for (int i = 0; i < default_arguments.size(); i++) {
+		da.push_back(default_arguments[i]);
+	}
+	dict["default_args"] = da;
+	dict["flags"] = flags;
+	dict["id"] = id;
+	Dictionary r = return_val;
+	dict["return"] = r;
+	return dict;
+}
+
+MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) {
+	MethodInfo mi;
+
+	if (p_dict.has("name")) {
+		mi.name = p_dict["name"];
+	}
+	Array args;
+	if (p_dict.has("args")) {
+		args = p_dict["args"];
+	}
+
+	for (int i = 0; i < args.size(); i++) {
+		Dictionary d = args[i];
+		mi.arguments.push_back(PropertyInfo::from_dict(d));
+	}
+	Array defargs;
+	if (p_dict.has("default_args")) {
+		defargs = p_dict["default_args"];
+	}
+	for (int i = 0; i < defargs.size(); i++) {
+		mi.default_arguments.push_back(defargs[i]);
+	}
+
+	if (p_dict.has("return")) {
+		mi.return_val = PropertyInfo::from_dict(p_dict["return"]);
+	}
+
+	if (p_dict.has("flags")) {
+		mi.flags = p_dict["flags"];
+	}
+
+	return mi;
+}
+
 MethodInfo::MethodInfo() :
 		flags(GDEXTENSION_METHOD_FLAG_NORMAL) {}
 

+ 2 - 0
src/godot.cpp

@@ -166,6 +166,7 @@ GDExtensionInterfaceObjectDestroy gdextension_interface_object_destroy = nullptr
 GDExtensionInterfaceGlobalGetSingleton gdextension_interface_global_get_singleton = nullptr;
 GDExtensionInterfaceObjectGetInstanceBinding gdextension_interface_object_get_instance_binding = nullptr;
 GDExtensionInterfaceObjectSetInstanceBinding gdextension_interface_object_set_instance_binding = nullptr;
+GDExtensionInterfaceObjectFreeInstanceBinding gdextension_interface_object_free_instance_binding = nullptr;
 GDExtensionInterfaceObjectSetInstance gdextension_interface_object_set_instance = nullptr;
 GDExtensionInterfaceObjectGetClassName gdextension_interface_object_get_class_name = nullptr;
 GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to = nullptr;
@@ -403,6 +404,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
 	LOAD_PROC_ADDRESS(global_get_singleton, GDExtensionInterfaceGlobalGetSingleton);
 	LOAD_PROC_ADDRESS(object_get_instance_binding, GDExtensionInterfaceObjectGetInstanceBinding);
 	LOAD_PROC_ADDRESS(object_set_instance_binding, GDExtensionInterfaceObjectSetInstanceBinding);
+	LOAD_PROC_ADDRESS(object_free_instance_binding, GDExtensionInterfaceObjectFreeInstanceBinding);
 	LOAD_PROC_ADDRESS(object_set_instance, GDExtensionInterfaceObjectSetInstance);
 	LOAD_PROC_ADDRESS(object_get_class_name, GDExtensionInterfaceObjectGetClassName);
 	LOAD_PROC_ADDRESS(object_cast_to, GDExtensionInterfaceObjectCastTo);

+ 4 - 0
src/variant/aabb.cpp

@@ -78,6 +78,10 @@ bool AABB::is_equal_approx(const AABB &p_aabb) const {
 	return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size);
 }
 
+bool AABB::is_finite() const {
+	return position.is_finite() && size.is_finite();
+}
+
 AABB AABB::intersection(const AABB &p_aabb) const {
 #ifdef MATH_CHECKS
 	if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) {

+ 4 - 0
src/variant/basis.cpp

@@ -692,6 +692,10 @@ bool Basis::is_equal_approx(const Basis &p_basis) const {
 	return rows[0].is_equal_approx(p_basis.rows[0]) && rows[1].is_equal_approx(p_basis.rows[1]) && rows[2].is_equal_approx(p_basis.rows[2]);
 }
 
+bool Basis::is_finite() const {
+	return rows[0].is_finite() && rows[1].is_finite() && rows[2].is_finite();
+}
+
 bool Basis::operator==(const Basis &p_matrix) const {
 	for (int i = 0; i < 3; i++) {
 		for (int j = 0; j < 3; j++) {

+ 4 - 0
src/variant/plane.cpp

@@ -178,6 +178,10 @@ bool Plane::is_equal_approx(const Plane &p_plane) const {
 	return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d);
 }
 
+bool Plane::is_finite() const {
+	return normal.is_finite() && Math::is_finite(d);
+}
+
 Plane::operator String() const {
 	return "[N: " + normal.operator String() + ", D: " + String::num_real(d, false) + "]";
 }

+ 4 - 0
src/variant/quaternion.cpp

@@ -81,6 +81,10 @@ bool Quaternion::is_equal_approx(const Quaternion &p_quaternion) const {
 	return Math::is_equal_approx(x, p_quaternion.x) && Math::is_equal_approx(y, p_quaternion.y) && Math::is_equal_approx(z, p_quaternion.z) && Math::is_equal_approx(w, p_quaternion.w);
 }
 
+bool Quaternion::is_finite() const {
+	return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z) && Math::is_finite(w);
+}
+
 real_t Quaternion::length() const {
 	return Math::sqrt(length_squared());
 }

+ 4 - 0
src/variant/rect2.cpp

@@ -40,6 +40,10 @@ bool Rect2::is_equal_approx(const Rect2 &p_rect) const {
 	return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size);
 }
 
+bool Rect2::is_finite() const {
+	return position.is_finite() && size.is_finite();
+}
+
 bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const {
 #ifdef MATH_CHECKS
 	if (unlikely(size.x < 0 || size.y < 0)) {

+ 4 - 0
src/variant/transform2d.cpp

@@ -170,6 +170,10 @@ bool Transform2D::is_equal_approx(const Transform2D &p_transform) const {
 	return columns[0].is_equal_approx(p_transform.columns[0]) && columns[1].is_equal_approx(p_transform.columns[1]) && columns[2].is_equal_approx(p_transform.columns[2]);
 }
 
+bool Transform2D::is_finite() const {
+	return columns[0].is_finite() && columns[1].is_finite() && columns[2].is_finite();
+}
+
 Transform2D Transform2D::looking_at(const Vector2 &p_target) const {
 	Transform2D return_trans = Transform2D(get_rotation(), get_origin());
 	Vector2 target_position = affine_inverse().xform(p_target);

+ 4 - 0
src/variant/transform3d.cpp

@@ -175,6 +175,10 @@ bool Transform3D::is_equal_approx(const Transform3D &p_transform) const {
 	return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin);
 }
 
+bool Transform3D::is_finite() const {
+	return basis.is_finite() && origin.is_finite();
+}
+
 bool Transform3D::operator==(const Transform3D &p_transform) const {
 	return (basis == p_transform.basis && origin == p_transform.origin);
 }

+ 4 - 0
src/variant/vector2.cpp

@@ -188,6 +188,10 @@ bool Vector2::is_zero_approx() const {
 	return Math::is_zero_approx(x) && Math::is_zero_approx(y);
 }
 
+bool Vector2::is_finite() const {
+	return Math::is_finite(x) && Math::is_finite(y);
+}
+
 Vector2::operator String() const {
 	return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")";
 }

+ 4 - 0
src/variant/vector3.cpp

@@ -141,6 +141,10 @@ bool Vector3::is_zero_approx() const {
 	return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z);
 }
 
+bool Vector3::is_finite() const {
+	return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z);
+}
+
 Vector3::operator String() const {
 	return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")";
 }

+ 4 - 0
src/variant/vector4.cpp

@@ -67,6 +67,10 @@ bool Vector4::is_zero_approx() const {
 	return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z) && Math::is_zero_approx(w);
 }
 
+bool Vector4::is_finite() const {
+	return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z) && Math::is_finite(w);
+}
+
 real_t Vector4::length() const {
 	return Math::sqrt(length_squared());
 }

+ 7 - 0
test/project/main.gd

@@ -185,6 +185,10 @@ func _ready():
 	control.queue_free()
 	sprite.queue_free()
 
+	# Test that passing null for objects works as expected too.
+	var example_null : Example = null
+	assert_equal(example.test_object_cast_to_node(example_null), false)
+
 	# Test conversions to and from Variant.
 	assert_equal(example.test_variant_vector2i_conversion(Vector2i(1, 1)), Vector2i(1, 1))
 	assert_equal(example.test_variant_vector2i_conversion(Vector2(1.0, 1.0)), Vector2i(1, 1))
@@ -242,6 +246,9 @@ func _ready():
 	assert_equal(new_example_ref.was_post_initialized(), true)
 	assert_equal(example.test_post_initialize(), true)
 
+	# Test that we can access an engine singleton.
+	assert_equal(example.test_use_engine_singleton(), OS.get_name())
+
 	# Test that notifications happen on both parent and child classes.
 	var example_child = $ExampleChild
 	assert_equal(example_child.get_value1(), 11)

+ 7 - 0
test/src/example.cpp

@@ -11,6 +11,7 @@
 #include <godot_cpp/classes/label.hpp>
 #include <godot_cpp/classes/multiplayer_api.hpp>
 #include <godot_cpp/classes/multiplayer_peer.hpp>
+#include <godot_cpp/classes/os.hpp>
 #include <godot_cpp/variant/utility_functions.hpp>
 
 using namespace godot;
@@ -231,6 +232,8 @@ void Example::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("callable_bind"), &Example::callable_bind);
 	ClassDB::bind_method(D_METHOD("test_post_initialize"), &Example::test_post_initialize);
 
+	ClassDB::bind_method(D_METHOD("test_use_engine_singleton"), &Example::test_use_engine_singleton);
+
 	ClassDB::bind_static_method("Example", D_METHOD("test_static", "a", "b"), &Example::test_static);
 	ClassDB::bind_static_method("Example", D_METHOD("test_static2"), &Example::test_static2);
 
@@ -654,3 +657,7 @@ void ExampleChild::_notification(int p_what) {
 		value2 = 33;
 	}
 }
+
+String Example::test_use_engine_singleton() const {
+	return OS::get_singleton()->get_name();
+}

+ 2 - 0
test/src/example.h

@@ -182,6 +182,8 @@ public:
 	// Virtual function override (no need to bind manually).
 	virtual bool _has_point(const Vector2 &point) const override;
 	virtual void _input(const Ref<InputEvent> &event) override;
+
+	String test_use_engine_singleton() const;
 };
 
 VARIANT_ENUM_CAST(Example::Constants);