diff --git a/binding_generator.py b/binding_generator.py
index 86d511a7e..20fd283b3 100644
--- a/binding_generator.py
+++ b/binding_generator.py
@@ -178,6 +178,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.
@@ -1079,9 +1083,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"]:
         engine_classes[native_struct["name"]] = False
@@ -1089,9 +1093,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"]:
@@ -1294,6 +1298,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("")
 
@@ -1448,6 +1456,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:
@@ -1456,12 +1477,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 "
 
@@ -1480,8 +1506,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 += "); \\"
@@ -1491,6 +1519,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)
@@ -2285,7 +2325,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:
@@ -2301,11 +2341,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)}"
diff --git a/include/godot_cpp/classes/wrapped.hpp b/include/godot_cpp/classes/wrapped.hpp
index 65c62eb53..d325fed9f 100644
--- a/include/godot_cpp/classes/wrapped.hpp
+++ b/include/godot_cpp/classes/wrapped.hpp
@@ -76,14 +76,15 @@ class Wrapped {
 	uint32_t plist_size = 0;
 
 	void _postinitialize();
+	virtual void _notificationv(int32_t p_what) {}
 
 	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;
 	}
 
@@ -171,6 +172,7 @@ protected:
                                                                                                                                                                                        \
 public:                                                                                                                                                                                \
 	typedef m_class self_type;                                                                                                                                                         \
+	typedef m_inherits parent_type;                                                                                                                                                    \
                                                                                                                                                                                        \
 	static void initialize_class() {                                                                                                                                                   \
 		static bool initialized = false;                                                                                                                                               \
@@ -185,12 +187,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();                                                                                                                                         \
 	}                                                                                                                                                                                  \
                                                                                                                                                                                        \
@@ -327,6 +329,11 @@ public:
 		_gde_binding_reference_callback,                                                                                                                                               \
 	};                                                                                                                                                                                 \
                                                                                                                                                                                        \
+protected:                                                                                                                                                                             \
+	virtual void _notificationv(int32_t p_what) override {                                                                                                                             \
+		m_class::notification_bind(this, p_what);                                                                                                                                      \
+	}                                                                                                                                                                                  \
+                                                                                                                                                                                       \
 private:
 
 // Don't use this for your classes, use GDCLASS() instead.
@@ -334,6 +341,7 @@ public:
 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 {                                                                                      \
@@ -343,6 +351,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;                                                                                                                                                                \
 	}                                                                                                                                                                                  \
@@ -385,15 +395,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();                                                                                                                                         \
 	}                                                                                                                                                                                  \
                                                                                                                                                                                        \
diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp
index 26f58e4af..752c43903 100644
--- a/include/godot_cpp/core/class_db.hpp
+++ b/include/godot_cpp/core/class_db.hpp
@@ -174,6 +174,8 @@ class ClassDB {
 template <typename T, bool is_abstract>
 void ClassDB::_register_class(bool p_virtual) {
 	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
@@ -290,4 +292,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
diff --git a/include/godot_cpp/core/math.hpp b/include/godot_cpp/core/math.hpp
index 2cbbe2726..1949360a9 100644
--- a/include/godot_cpp/core/math.hpp
+++ b/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) {
diff --git a/include/godot_cpp/core/method_bind.hpp b/include/godot_cpp/core/method_bind.hpp
index 4afd7b8a1..eabd6ece3 100644
--- a/include/godot_cpp/core/method_bind.hpp
+++ b/include/godot_cpp/core/method_bind.hpp
@@ -412,6 +412,7 @@ class MethodBindTC : public MethodBind {
 		method = p_method;
 		generate_argument_types(sizeof...(P));
 		set_argument_count(sizeof...(P));
+		set_const(true);
 	}
 };
 
@@ -578,6 +579,7 @@ class MethodBindTRC : public MethodBind {
 		generate_argument_types(sizeof...(P));
 		set_argument_count(sizeof...(P));
 		set_return(true);
+		set_const(true);
 	}
 };
 
diff --git a/include/godot_cpp/core/method_ptrcall.hpp b/include/godot_cpp/core/method_ptrcall.hpp
index ca3327ed9..b12a7e640 100644
--- a/include/godot_cpp/core/method_ptrcall.hpp
+++ b/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;
 	}
 };
 
diff --git a/include/godot_cpp/core/property_info.hpp b/include/godot_cpp/core/property_info.hpp
index 72ee27187..dd71d48af 100644
--- a/include/godot_cpp/core/property_info.hpp
+++ b/include/godot_cpp/core/property_info.hpp
@@ -68,6 +68,63 @@ struct PropertyInfo {
 
 	PropertyInfo(GDExtensionVariantType p_type, const StringName &p_name, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = "") :
 			PropertyInfo((Variant::Type)p_type, p_name, p_hint, p_hint_string, p_usage, p_class_name) {}
+
+	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;
+		p_info->hint = hint;
+		*(reinterpret_cast<String *>(p_info->hint_string)) = hint_string;
+		p_info->usage = usage;
+		*(reinterpret_cast<StringName *>(p_info->class_name)) = class_name;
+	}
+
+	GDExtensionPropertyInfo _to_gdextension() const {
+		return {
+			(GDExtensionVariantType)type,
+			name._native_ptr(),
+			class_name._native_ptr(),
+			hint,
+			hint_string._native_ptr(),
+			usage,
+		};
+	}
 };
 
 } // namespace godot
diff --git a/include/godot_cpp/core/type_info.hpp b/include/godot_cpp/core/type_info.hpp
index 2c4f8e404..e1f2b2052 100644
--- a/include/godot_cpp/core/type_info.hpp
+++ b/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();
diff --git a/include/godot_cpp/variant/aabb.hpp b/include/godot_cpp/variant/aabb.hpp
index dde392b59..5325bd775 100644
--- a/include/godot_cpp/variant/aabb.hpp
+++ b/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
diff --git a/include/godot_cpp/variant/basis.hpp b/include/godot_cpp/variant/basis.hpp
index a365b024f..e740a64a7 100644
--- a/include/godot_cpp/variant/basis.hpp
+++ b/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;
diff --git a/include/godot_cpp/variant/plane.hpp b/include/godot_cpp/variant/plane.hpp
index 727f4f543..829f801f4 100644
--- a/include/godot_cpp/variant/plane.hpp
+++ b/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;
diff --git a/include/godot_cpp/variant/quaternion.hpp b/include/godot_cpp/variant/quaternion.hpp
index 3816b6668..5de91b207 100644
--- a/include/godot_cpp/variant/quaternion.hpp
+++ b/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;
diff --git a/include/godot_cpp/variant/rect2.hpp b/include/godot_cpp/variant/rect2.hpp
index cfd24b2fb..c37134d00 100644
--- a/include/godot_cpp/variant/rect2.hpp
+++ b/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; }
diff --git a/include/godot_cpp/variant/transform2d.hpp b/include/godot_cpp/variant/transform2d.hpp
index 5a4839851..d73323f31 100644
--- a/include/godot_cpp/variant/transform2d.hpp
+++ b/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;
 
diff --git a/include/godot_cpp/variant/transform3d.hpp b/include/godot_cpp/variant/transform3d.hpp
index 3a54c0b2d..6fa5999ed 100644
--- a/include/godot_cpp/variant/transform3d.hpp
+++ b/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;
diff --git a/include/godot_cpp/variant/vector2.hpp b/include/godot_cpp/variant/vector2.hpp
index 13c0da6fe..fe4d05aa5 100644
--- a/include/godot_cpp/variant/vector2.hpp
+++ b/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);
diff --git a/include/godot_cpp/variant/vector3.hpp b/include/godot_cpp/variant/vector3.hpp
index a8d96ed0c..1107bca8d 100644
--- a/include/godot_cpp/variant/vector3.hpp
+++ b/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 */
 
diff --git a/include/godot_cpp/variant/vector4.hpp b/include/godot_cpp/variant/vector4.hpp
index 26c57c357..b20915aaa 100644
--- a/include/godot_cpp/variant/vector4.hpp
+++ b/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;
diff --git a/src/classes/wrapped.cpp b/src/classes/wrapped.cpp
index 8a2092c1e..f1b31aa03 100644
--- a/src/classes/wrapped.cpp
+++ b/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);
 	}
 }
 
diff --git a/src/core/object.cpp b/src/core/object.cpp
index dc3c87983..d2e10ffb9 100644
--- a/src/core/object.cpp
+++ b/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) {}
 
diff --git a/src/variant/aabb.cpp b/src/variant/aabb.cpp
index 92e751b44..ded17d2ba 100644
--- a/src/variant/aabb.cpp
+++ b/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)) {
diff --git a/src/variant/basis.cpp b/src/variant/basis.cpp
index 8f78d9f02..8d4176e67 100644
--- a/src/variant/basis.cpp
+++ b/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++) {
diff --git a/src/variant/plane.cpp b/src/variant/plane.cpp
index 53dd439a5..caea516e1 100644
--- a/src/variant/plane.cpp
+++ b/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) + "]";
 }
diff --git a/src/variant/quaternion.cpp b/src/variant/quaternion.cpp
index 9d4d838d8..c01085051 100644
--- a/src/variant/quaternion.cpp
+++ b/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());
 }
diff --git a/src/variant/rect2.cpp b/src/variant/rect2.cpp
index a70fee638..62730a9d4 100644
--- a/src/variant/rect2.cpp
+++ b/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)) {
diff --git a/src/variant/transform2d.cpp b/src/variant/transform2d.cpp
index 530a99e36..3b2c0b09e 100644
--- a/src/variant/transform2d.cpp
+++ b/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);
diff --git a/src/variant/transform3d.cpp b/src/variant/transform3d.cpp
index 1a4189e2b..d71e91911 100644
--- a/src/variant/transform3d.cpp
+++ b/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);
 }
diff --git a/src/variant/vector2.cpp b/src/variant/vector2.cpp
index df8708053..ca1ab8f33 100644
--- a/src/variant/vector2.cpp
+++ b/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) + ")";
 }
diff --git a/src/variant/vector3.cpp b/src/variant/vector3.cpp
index 615365802..9f04340a3 100644
--- a/src/variant/vector3.cpp
+++ b/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) + ")";
 }
diff --git a/src/variant/vector4.cpp b/src/variant/vector4.cpp
index 72c79d1e5..483545e27 100644
--- a/src/variant/vector4.cpp
+++ b/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());
 }
diff --git a/test/project/main.gd b/test/project/main.gd
index 9717eca22..eb8f734a8 100644
--- a/test/project/main.gd
+++ b/test/project/main.gd
@@ -122,6 +122,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))
