Browse Source

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).
Pedro J. Estébanez 4 years ago
parent
commit
123d3ef935
3 changed files with 33 additions and 0 deletions
  1. 19 0
      core/variant.cpp
  2. 2 0
      core/variant.h
  3. 12 0
      modules/gdscript/gdscript_function.cpp

+ 19 - 0
core/variant.cpp

@@ -803,6 +803,25 @@ bool Variant::is_one() const {
 	return false;
 	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) {
 void Variant::reference(const Variant &p_variant) {
 	switch (type) {
 	switch (type) {
 		case NIL:
 		case NIL:

+ 2 - 0
core/variant.h

@@ -187,6 +187,8 @@ public:
 	bool is_zero() const;
 	bool is_zero() const;
 	bool is_one() const;
 	bool is_one() const;
 
 
+	ObjectID get_object_instance_id() const;
+
 	operator bool() const;
 	operator bool() const;
 	operator signed int() const;
 	operator signed int() const;
 	operator unsigned int() const; // this is the real one
 	operator unsigned int() const; // this is the real one

+ 12 - 0
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);
 				*dst = Variant::construct(to_type, (const Variant **)&src, 1, err);
 
 
 #ifdef DEBUG_ENABLED
 #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) {
 				if (err.error != Variant::CallError::CALL_OK) {
 					err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
 					err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
 					OPCODE_BREAK;
 					OPCODE_BREAK;
@@ -866,6 +870,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 				GD_ERR_BREAK(!nc);
 				GD_ERR_BREAK(!nc);
 
 
 #ifdef DEBUG_ENABLED
 #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) {
 				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.";
 					err_text = "Invalid cast: can't convert a non-object value to an object type.";
 					OPCODE_BREAK;
 					OPCODE_BREAK;
@@ -894,6 +902,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 				GD_ERR_BREAK(!base_type);
 				GD_ERR_BREAK(!base_type);
 
 
 #ifdef DEBUG_ENABLED
 #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) {
 				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() + "'.";
 					err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
 					OPCODE_BREAK;
 					OPCODE_BREAK;