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()) { if (!p_prefix.is_valid_filename()) {
*r_error = ERR_FILE_BAD_PATH; *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()); 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); Error err = dir_access->make_dir(path);
if (err != OK) { if (err != OK) {
*r_error = err; *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); err = dir_access->change_dir(path);
if (err != OK) { if (err != OK) {

View file

@ -829,16 +829,27 @@ public:
void detach_from_objectdb(); void detach_from_objectdb();
_FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_id; } _FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_id; }
template <typename T> template <typename T, typename O>
static T *cast_to(Object *p_object) { static T *cast_to(O *p_object) {
// This is like dynamic_cast, but faster. // This is like dynamic_cast, but faster.
// The reason is that we can assume no virtual and multiple inheritance. // 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> template <typename T>
static const T *cast_to(const Object *p_object) { 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 { enum {
@ -871,7 +882,7 @@ public:
bool is_class(const String &p_class) const; bool is_class(const String &p_class) const;
virtual bool is_class_ptr(void *p_ptr) const { return get_class_ptr_static() == p_ptr; } 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; bool derives_from() const;
const StringName &get_class_name() const; const StringName &get_class_name() const;
@ -1040,29 +1051,25 @@ public:
bool predelete_handler(Object *p_object); bool predelete_handler(Object *p_object);
void postinitialize_handler(Object *p_object); void postinitialize_handler(Object *p_object);
template <typename T> template <typename T, typename O>
bool Object::derives_from() const { bool Object::derives_from() const {
static_assert(std::is_base_of_v<Object, T>, "T must be derived from Object."); if constexpr (std::is_base_of_v<T, O>) {
static_assert(std::is_same_v<std::decay_t<T>, typename T::self_type>, "T must use GDCLASS or GDSOFTCLASS."); // We derive statically from T (or are the same class), so casting to it is trivial.
return true;
// 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 { } 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 { class ObjectDB {
// This needs to add up to 63, 1 bit is for reference. // This needs to add up to 63, 1 bit is for reference.
#define OBJECTDB_VALIDATOR_BITS 39 #define OBJECTDB_VALIDATOR_BITS 39

View file

@ -645,7 +645,7 @@ EditorExportPlatform::ExportNotifier::~ExportNotifier() {
export_plugins.write[i]->_export_end(); export_plugins.write[i]->_export_end();
} }
export_plugins.write[i]->_export_end_clear(); export_plugins.write[i]->_export_end_clear();
export_plugins.write[i]->set_export_preset(Ref<EditorExportPlugin>()); export_plugins.write[i]->set_export_preset(Ref<EditorExportPreset>());
} }
} }

View file

@ -1131,7 +1131,9 @@ void SceneImportSettingsDialog::_animation_slider_value_changed(double p_value)
void SceneImportSettingsDialog::_skeleton_tree_entered(Skeleton3D *p_skeleton) { void SceneImportSettingsDialog::_skeleton_tree_entered(Skeleton3D *p_skeleton) {
bones_mesh_preview->set_skeleton_path(p_skeleton->get_path()); bones_mesh_preview->set_skeleton_path(p_skeleton->get_path());
bones_mesh_preview->set_skin(p_skeleton->register_skin(p_skeleton->create_skin_from_rest_transforms())); Ref<Skin> skin = p_skeleton->create_skin_from_rest_transforms();
p_skeleton->register_skin(skin);
bones_mesh_preview->set_skin(skin);
} }
void SceneImportSettingsDialog::_animation_finished(const StringName &p_name) { void SceneImportSettingsDialog::_animation_finished(const StringName &p_name) {

View file

@ -192,7 +192,7 @@ Ref<Texture2D> EditorImagePreviewPlugin::generate(const Ref<Resource> &p_from, c
Ref<Image> img = p_from; Ref<Image> img = p_from;
if (img.is_null() || img->is_empty()) { if (img.is_null() || img->is_empty()) {
return Ref<Image>(); return Ref<Texture2D>();
} }
img = img->duplicate(); img = img->duplicate();
@ -200,7 +200,7 @@ Ref<Texture2D> EditorImagePreviewPlugin::generate(const Ref<Resource> &p_from, c
if (img->is_compressed()) { if (img->is_compressed()) {
if (img->decompress() != OK) { if (img->decompress() != OK) {
return Ref<Image>(); return Ref<Texture2D>();
} }
} else if (img->get_format() != Image::FORMAT_RGB8 && img->get_format() != Image::FORMAT_RGBA8) { } else if (img->get_format() != Image::FORMAT_RGB8 && img->get_format() != Image::FORMAT_RGBA8) {
img->convert(Image::FORMAT_RGBA8); img->convert(Image::FORMAT_RGBA8);

View file

@ -497,7 +497,7 @@ void ViewportRotationControl::gui_input(const Ref<InputEvent> &p_event) {
const Ref<InputEventScreenDrag> screen_drag = p_event; const Ref<InputEventScreenDrag> screen_drag = p_event;
if (screen_drag.is_valid()) { if (screen_drag.is_valid()) {
_process_drag(screen_drag, screen_drag->get_index(), screen_drag->get_position(), screen_drag->get_relative()); _process_drag(nullptr, screen_drag->get_index(), screen_drag->get_position(), screen_drag->get_relative());
} }
} }

View file

@ -2143,10 +2143,6 @@ Dictionary GLTFDocument::_serialize_image(Ref<GLTFState> p_state, Ref<Image> p_i
bv->byte_offset = p_state->buffers[bi].size(); bv->byte_offset = p_state->buffers[bi].size();
Vector<uint8_t> buffer; Vector<uint8_t> buffer;
Ref<ImageTexture> img_tex = p_image;
if (img_tex.is_valid()) {
p_image = img_tex->get_image();
}
// Save in various image formats. Note that if the format is "None", // Save in various image formats. Note that if the format is "None",
// the state's images will be empty, so this code will not be reached. // the state's images will be empty, so this code will not be reached.
if (_image_save_extension.is_valid()) { if (_image_save_extension.is_valid()) {
@ -2755,10 +2751,6 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) {
height = ao_texture->get_height(); height = ao_texture->get_height();
width = ao_texture->get_width(); width = ao_texture->get_width();
ao_image = ao_texture->get_image(); ao_image = ao_texture->get_image();
Ref<ImageTexture> img_tex = ao_image;
if (img_tex.is_valid()) {
ao_image = img_tex->get_image();
}
if (ao_image->is_compressed()) { if (ao_image->is_compressed()) {
ao_image->decompress(); ao_image->decompress();
} }
@ -2771,10 +2763,6 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) {
height = roughness_texture->get_height(); height = roughness_texture->get_height();
width = roughness_texture->get_width(); width = roughness_texture->get_width();
roughness_image = roughness_texture->get_image(); roughness_image = roughness_texture->get_image();
Ref<ImageTexture> img_tex = roughness_image;
if (img_tex.is_valid()) {
roughness_image = img_tex->get_image();
}
if (roughness_image->is_compressed()) { if (roughness_image->is_compressed()) {
roughness_image->decompress(); roughness_image->decompress();
} }
@ -2787,10 +2775,6 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) {
height = metallic_texture->get_height(); height = metallic_texture->get_height();
width = metallic_texture->get_width(); width = metallic_texture->get_width();
metallness_image = metallic_texture->get_image(); metallness_image = metallic_texture->get_image();
Ref<ImageTexture> img_tex = metallness_image;
if (img_tex.is_valid()) {
metallness_image = img_tex->get_image();
}
if (metallness_image->is_compressed()) { if (metallness_image->is_compressed()) {
metallness_image->decompress(); metallness_image->decompress();
} }
@ -2903,10 +2887,6 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) {
// Code for uncompressing RG normal maps // Code for uncompressing RG normal maps
Ref<Image> img = normal_texture->get_image(); Ref<Image> img = normal_texture->get_image();
if (img.is_valid()) { if (img.is_valid()) {
Ref<ImageTexture> img_tex = normal_texture;
if (img_tex.is_valid()) {
img = img_tex->get_image();
}
img->decompress(); img->decompress();
img->convert(Image::FORMAT_RGBA8); img->convert(Image::FORMAT_RGBA8);
for (int32_t y = 0; y < img->get_height(); y++) { for (int32_t y = 0; y < img->get_height(); y++) {

View file

@ -255,7 +255,7 @@ AudioStreamPlaybackPolyphonic::ID AudioStreamPlaybackPolyphonic::play_stream(con
sp->bus = p_bus; sp->bus = p_bus;
if (streams[i].stream_playback->get_sample_playback().is_valid()) { if (streams[i].stream_playback->get_sample_playback().is_valid()) {
AudioServer::get_singleton()->stop_playback_stream(sp); AudioServer::get_singleton()->stop_playback_stream(streams[i].stream_playback);
} }
streams[i].stream_playback->set_sample_playback(sp); streams[i].stream_playback->set_sample_playback(sp);