summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPedro J. Estébanez <pedrojrulez@gmail.com>2023-04-11 17:20:03 +0200
committerPedro J. Estébanez <pedrojrulez@gmail.com>2023-04-11 17:27:39 +0200
commit2f4168daeba6e70de51bb2abbad1cc0b0bc54fe4 (patch)
treed2178d64ff87b3481eb153ba41cf82540685fec6
parent59b8c700072c6bb25b0f1f95c40e54941d941383 (diff)
downloadredot-engine-2f4168daeba6e70de51bb2abbad1cc0b0bc54fe4.tar.gz
Fix edge cases of object lifetime when signals involved
-rw-r--r--core/object/object.cpp4
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp21
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h2
-rw-r--r--modules/gdscript/gdscript_codegen.h1
-rw-r--r--modules/gdscript/gdscript_compiler.cpp3
5 files changed, 30 insertions, 1 deletions
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 39cae7c5bd..2e1ea9ef5f 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -1013,6 +1013,10 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
return ERR_UNAVAILABLE;
}
+ // If this is a ref-counted object, prevent it from being destroyed during signal emission,
+ // which is needed in certain edge cases; e.g., https://github.com/godotengine/godot/issues/73889.
+ Ref<RefCounted> rc = Ref<RefCounted>(Object::cast_to<RefCounted>(this));
+
List<_ObjectSignalDisconnectData> disconnect_data;
//copy on write will ensure that disconnecting the signal or even deleting the object will not affect the signal calling.
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index eb827d375c..1414075ba8 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -142,7 +142,9 @@ void GDScriptByteCodeGenerator::pop_temporary() {
if (slot.type == Variant::NIL) {
// Avoid keeping in the stack long-lived references to objects,
// which may prevent RefCounted objects from being freed.
- write_assign_false(Address(Address::TEMPORARY, slot_idx));
+ // However, the cleanup will be performed an the end of the
+ // statement, to allow object references to survive chaining.
+ temporaries_pending_clear.push_back(slot_idx);
}
temporaries_pool[slot.type].push_back(slot_idx);
used_temporaries.pop_back();
@@ -1752,6 +1754,23 @@ void GDScriptByteCodeGenerator::end_block() {
pop_stack_identifiers();
}
+void GDScriptByteCodeGenerator::clean_temporaries() {
+ List<int>::Element *E = temporaries_pending_clear.front();
+ while (E) {
+ // The temporary may have been re-used as something else than an object
+ // since it was added to the list. In that case, there's no need to clear it.
+ int slot_idx = E->get();
+ const StackSlot &slot = temporaries[slot_idx];
+ if (slot.type == Variant::NIL) {
+ write_assign_false(Address(Address::TEMPORARY, slot_idx));
+ }
+
+ List<int>::Element *next = E->next();
+ E->erase();
+ E = next;
+ }
+}
+
GDScriptByteCodeGenerator::~GDScriptByteCodeGenerator() {
if (!ended && function != nullptr) {
memdelete(function);
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index dc05de9fc6..42c6f80455 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -88,6 +88,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
Vector<StackSlot> locals;
Vector<StackSlot> temporaries;
List<int> used_temporaries;
+ List<int> temporaries_pending_clear;
RBMap<Variant::Type, List<int>> temporaries_pool;
List<GDScriptFunction::StackDebug> stack_debug;
@@ -463,6 +464,7 @@ public:
virtual uint32_t add_or_get_name(const StringName &p_name) override;
virtual uint32_t add_temporary(const GDScriptDataType &p_type) override;
virtual void pop_temporary() override;
+ virtual void clean_temporaries() override;
virtual void start_parameters() override;
virtual void end_parameters() override;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index 7847ab28c7..e82b4b08ab 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -72,6 +72,7 @@ public:
virtual uint32_t add_or_get_name(const StringName &p_name) = 0;
virtual uint32_t add_temporary(const GDScriptDataType &p_type) = 0;
virtual void pop_temporary() = 0;
+ virtual void clean_temporaries() = 0;
virtual void start_parameters() = 0;
virtual void end_parameters() = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 5413eadf60..9e5c83d08c 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1665,6 +1665,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
Error err = OK;
GDScriptCodeGenerator *gen = codegen.generator;
+ gen->clean_temporaries();
codegen.start_block();
if (p_add_locals) {
@@ -1967,6 +1968,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
}
} break;
}
+
+ gen->clean_temporaries();
}
codegen.end_block();