Statically protect Object::cast_to for unrelated Object types.

Fix a handful of bugs associated with it.
This commit is contained in:
Lukas Tenbrink
2025-10-23 22:30:54 +02:00
parent bbe9654327
commit 0f047944e4
8 changed files with 40 additions and 51 deletions

View File

@ -326,7 +326,7 @@ Ref<DirAccess> DirAccess::create_temp(const String &p_prefix, bool p_keep, Error
if (!p_prefix.is_valid_filename()) {
*r_error = ERR_FILE_BAD_PATH;
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" is not a valid prefix.)", ERROR_COMMON_PREFIX, p_prefix));
ERR_FAIL_V_MSG(Ref<DirAccess>(), vformat(R"(%s: "%s" is not a valid prefix.)", ERROR_COMMON_PREFIX, p_prefix));
}
Ref<DirAccess> dir_access = DirAccess::open(OS::get_singleton()->get_temp_path());
@ -351,7 +351,7 @@ Ref<DirAccess> DirAccess::create_temp(const String &p_prefix, bool p_keep, Error
Error err = dir_access->make_dir(path);
if (err != OK) {
*r_error = err;
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" couldn't create directory "%s".)", ERROR_COMMON_PREFIX, path));
ERR_FAIL_V_MSG(Ref<DirAccess>(), vformat(R"(%s: "%s" couldn't create directory "%s".)", ERROR_COMMON_PREFIX, path));
}
err = dir_access->change_dir(path);
if (err != OK) {

View File

@ -829,16 +829,27 @@ public:
void detach_from_objectdb();
_FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_id; }
template <typename T>
static T *cast_to(Object *p_object) {
template <typename T, typename O>
static T *cast_to(O *p_object) {
// This is like dynamic_cast, but faster.
// The reason is that we can assume no virtual and multiple inheritance.
return p_object && p_object->derives_from<T>() ? static_cast<T *>(p_object) : nullptr;
return p_object && p_object->template derives_from<T, O>() ? static_cast<T *>(p_object) : nullptr;
}
template <typename T, typename O>
static const T *cast_to(const O *p_object) {
return p_object && p_object->template derives_from<T, O>() ? static_cast<const T *>(p_object) : nullptr;
}
// cast_to versions for types that implicitly convert to Object.
template <typename T>
static T *cast_to(Object *p_object) {
return p_object && p_object->template derives_from<T, Object>() ? static_cast<T *>(p_object) : nullptr;
}
template <typename T>
static const T *cast_to(const Object *p_object) {
return p_object && p_object->derives_from<T>() ? static_cast<const T *>(p_object) : nullptr;
return p_object && p_object->template derives_from<T, Object>() ? static_cast<const T *>(p_object) : nullptr;
}
enum {
@ -871,7 +882,7 @@ public:
bool is_class(const String &p_class) const;
virtual bool is_class_ptr(void *p_ptr) const { return get_class_ptr_static() == p_ptr; }
template <typename T>
template <typename T, typename O>
bool derives_from() const;
const StringName &get_class_name() const;
@ -1040,29 +1051,25 @@ public:
bool predelete_handler(Object *p_object);
void postinitialize_handler(Object *p_object);
template <typename T>
template <typename T, typename O>
bool Object::derives_from() const {
static_assert(std::is_base_of_v<Object, T>, "T must be derived from Object.");
static_assert(std::is_same_v<std::decay_t<T>, typename T::self_type>, "T must use GDCLASS or GDSOFTCLASS.");
// If there is an explicitly set ancestral class on the type, we can use that.
if constexpr (T::static_ancestral_class != T::super_type::static_ancestral_class) {
return _has_ancestry(T::static_ancestral_class);
if constexpr (std::is_base_of_v<T, O>) {
// We derive statically from T (or are the same class), so casting to it is trivial.
return true;
} else {
return is_class_ptr(T::get_class_ptr_static());
static_assert(std::is_base_of_v<Object, O>, "derives_from can only be used with Object subclasses.");
static_assert(std::is_base_of_v<O, T>, "Cannot cast argument to T because T does not derive from the argument's known class.");
static_assert(std::is_same_v<std::decay_t<T>, typename T::self_type>, "T must use GDCLASS or GDSOFTCLASS.");
// If there is an explicitly set ancestral class on the type, we can use that.
if constexpr (T::static_ancestral_class != T::super_type::static_ancestral_class) {
return _has_ancestry(T::static_ancestral_class);
} else {
return is_class_ptr(T::get_class_ptr_static());
}
}
}
template <>
inline bool Object::derives_from<Object>() const {
return true;
}
template <>
inline bool Object::derives_from<const Object>() const {
return true;
}
class ObjectDB {
// This needs to add up to 63, 1 bit is for reference.
#define OBJECTDB_VALIDATOR_BITS 39