From bdc6649b7991b1c11549a8efb0a708345bf12783 Mon Sep 17 00:00:00 2001 From: George Marques Date: Fri, 10 May 2019 16:38:48 -0300 Subject: [PATCH 1/2] Keep GDScript functions in stack while yielding This prevents GDScript functions from leaving the stack too soon when they are resuming from yield, allowing the ones expecting it to finish to know the caller. Helps debugging cases when you use: `yield(function_which_yields(), "completed")` since now it shows the call that resumed that function. --- modules/gdscript/gdscript_function.cpp | 45 ++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index cff9ba55b86..2b9d40b10e3 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -1583,15 +1583,26 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time; } - if (ScriptDebugger::get_singleton()) - GDScriptLanguage::get_singleton()->exit_function(); + bool yielded = retvalue.is_ref() && Object::cast_to(retvalue); + + // Check if this is the last time the function is resuming from yield + // Will be true if never yielded as well + // When it's the last resume it will postpone the exit from stack, + // so the debugger knows which function triggered the resume of the next function (if any) + if (!p_state || yielded) { + if (ScriptDebugger::get_singleton()) + GDScriptLanguage::get_singleton()->exit_function(); #endif - if (_stack_size) { - //free stack - for (int i = 0; i < _stack_size; i++) - stack[i].~Variant(); + if (_stack_size) { + //free stack + for (int i = 0; i < _stack_size; i++) + stack[i].~Variant(); + } + +#ifdef DEBUG_ENABLED } +#endif return retvalue; } @@ -1830,6 +1841,17 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar } } +#ifdef DEBUG_ENABLED + if (ScriptDebugger::get_singleton()) + GDScriptLanguage::get_singleton()->exit_function(); + if (state.stack_size) { + //free stack + Variant *stack = (Variant *)state.stack.ptr(); + for (int i = 0; i < state.stack_size; i++) + stack[i].~Variant(); + } +#endif + return ret; } @@ -1884,6 +1906,17 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { } else { emit_signal("completed", ret); } + +#ifdef DEBUG_ENABLED + if (ScriptDebugger::get_singleton()) + GDScriptLanguage::get_singleton()->exit_function(); + if (state.stack_size) { + //free stack + Variant *stack = (Variant *)state.stack.ptr(); + for (int i = 0; i < state.stack_size; i++) + stack[i].~Variant(); + } +#endif } return ret; From 9df1a2442b480f9ed8a8d4ec8992ea5046b98719 Mon Sep 17 00:00:00 2001 From: George Marques Date: Fri, 10 May 2019 16:39:39 -0300 Subject: [PATCH 2/2] Show function name in debugger stack trace Also show script and line when the instance is gone when resuming from yield. --- editor/script_editor_debugger.cpp | 3 +-- modules/gdscript/gdscript_function.cpp | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 1da8bf874c1..a283f39dffe 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -622,8 +622,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da d["frame"] = i; s->set_metadata(0, d); - //String line = itos(i)+" - "+String(d["file"])+":"+itos(d["line"])+" - at func: "+d["function"]; - String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]); + String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]) + " - at function: " + d["function"]; s->set_text(0, line); if (i == 0) diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 2b9d40b10e3..cd537ac6284 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -1778,7 +1778,7 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { #ifdef DEBUG_ENABLED - ERR_EXPLAIN("Resumed after yield, but class instance is gone"); + ERR_EXPLAIN("Resumed function '" + String(function->get_name()) + "()' after yield, but class instance is gone. At script: " + state.script->get_path() + ":" + itos(state.line)); ERR_FAIL_V(Variant()); #else return Variant(); @@ -1874,7 +1874,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { ERR_FAIL_COND_V(!function, Variant()); if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { #ifdef DEBUG_ENABLED - ERR_EXPLAIN("Resumed after yield, but class instance is gone"); + ERR_EXPLAIN("Resumed function '" + String(function->get_name()) + "()' after yield, but class instance is gone. At script: " + state.script->get_path() + ":" + itos(state.line)); ERR_FAIL_V(Variant()); #else return Variant();