From 123d3ef93567144a400191b2801f063daa92a46c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Sat, 31 Jul 2021 11:54:41 +0200 Subject: [PATCH] Complain if casting a freed object in a debug session The idea is to give the user a chance to realize a mistake that will cause a crash in a release build (or with no debugger attached). --- core/variant.cpp | 19 +++++++++++++++++++ core/variant.h | 2 ++ modules/gdscript/gdscript_function.cpp | 12 ++++++++++++ 3 files changed, 33 insertions(+) diff --git a/core/variant.cpp b/core/variant.cpp index ae757666d30..032e0c447cb 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -803,6 +803,25 @@ bool Variant::is_one() const { return false; } +ObjectID Variant::get_object_instance_id() const { + if (type != OBJECT) { + return 0; + } +#ifdef DEBUG_ENABLED + if (is_ref()) { + return !_get_obj().ref.is_null() ? _REF_OBJ_PTR(*this)->get_instance_id() : 0; + } else { + return _get_obj().rc->instance_id; + } +#else + if (is_ref() && _get_obj().ref.is_null()) { + return 0; + } else { + return _get_obj().obj->get_instance_id(); + } +#endif +} + void Variant::reference(const Variant &p_variant) { switch (type) { case NIL: diff --git a/core/variant.h b/core/variant.h index 8db69886b07..133335951ea 100644 --- a/core/variant.h +++ b/core/variant.h @@ -187,6 +187,8 @@ public: bool is_zero() const; bool is_one() const; + ObjectID get_object_instance_id() const; + operator bool() const; operator signed int() const; operator unsigned int() const; // this is the real one diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index ac428df6d2d..27ad6060d2a 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -846,6 +846,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a *dst = Variant::construct(to_type, (const Variant **)&src, 1, err); #ifdef DEBUG_ENABLED + if (src->get_type() == Variant::OBJECT && !src->is_ref() && ObjectDB::get_instance(src->get_object_instance_id()) == nullptr) { + err_text = "Trying to cast a deleted object."; + OPCODE_BREAK; + } if (err.error != Variant::CallError::CALL_OK) { err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'."; OPCODE_BREAK; @@ -866,6 +870,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(!nc); #ifdef DEBUG_ENABLED + if (src->get_type() == Variant::OBJECT && !src->is_ref() && ObjectDB::get_instance(src->get_object_instance_id()) == nullptr) { + err_text = "Trying to cast a deleted object."; + OPCODE_BREAK; + } if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) { err_text = "Invalid cast: can't convert a non-object value to an object type."; OPCODE_BREAK; @@ -894,6 +902,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(!base_type); #ifdef DEBUG_ENABLED + if (src->get_type() == Variant::OBJECT && !src->is_ref() && ObjectDB::get_instance(src->get_object_instance_id()) == nullptr) { + err_text = "Trying to cast a deleted object."; + OPCODE_BREAK; + } if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) { err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'."; OPCODE_BREAK;