From 645abdbb801c29ba93adefb01e422dc66fb22fc2 Mon Sep 17 00:00:00 2001 From: kobewi Date: Mon, 30 Sep 2024 17:48:27 +0200 Subject: [PATCH] Add expression evaluater to debugger (REPL) Co-authored-by: rohanrhu --- core/debugger/remote_debugger.cpp | 36 +++++ .../debug_adapter/debug_adapter_protocol.cpp | 2 +- editor/debugger/editor_debugger_inspector.cpp | 15 +- editor/debugger/editor_debugger_inspector.h | 2 +- .../debugger/editor_expression_evaluator.cpp | 148 ++++++++++++++++++ editor/debugger/editor_expression_evaluator.h | 77 +++++++++ editor/debugger/script_editor_debugger.cpp | 15 +- editor/debugger/script_editor_debugger.h | 2 + 8 files changed, 291 insertions(+), 6 deletions(-) create mode 100644 editor/debugger/editor_expression_evaluator.cpp create mode 100644 editor/debugger/editor_expression_evaluator.h diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index e2ed7245a24..fc1b7b74f99 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -37,6 +37,7 @@ #include "core/debugger/script_debugger.h" #include "core/input/input.h" #include "core/io/resource_loader.h" +#include "core/math/expression.h" #include "core/object/script_language.h" #include "core/os/os.h" #include "servers/display_server.h" @@ -529,6 +530,41 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } else if (command == "set_skip_breakpoints") { ERR_FAIL_COND(data.is_empty()); script_debugger->set_skip_breakpoints(data[0]); + } else if (command == "evaluate") { + String expression_str = data[0]; + int frame = data[1]; + + ScriptInstance *breaked_instance = script_debugger->get_break_language()->debug_get_stack_level_instance(frame); + if (!breaked_instance) { + break; + } + + List locals; + List local_vals; + + script_debugger->get_break_language()->debug_get_stack_level_locals(frame, &locals, &local_vals); + ERR_FAIL_COND(locals.size() != local_vals.size()); + + PackedStringArray locals_vector; + for (const String &S : locals) { + locals_vector.append(S); + } + + Array local_vals_array; + for (const Variant &V : local_vals) { + local_vals_array.append(V); + } + + Expression expression; + expression.parse(expression_str, locals_vector); + const Variant return_val = expression.execute(local_vals_array, breaked_instance->get_owner()); + + DebuggerMarshalls::ScriptStackVariable stvar; + stvar.name = expression_str; + stvar.value = return_val; + stvar.type = 3; + + send_message("evaluation_return", stvar.serialize()); } else { bool captured = false; ERR_CONTINUE(_try_capture(command, data, captured) != OK); diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp index 4febb8bf047..f847d3be7b1 100644 --- a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp @@ -966,7 +966,7 @@ void DebugAdapterProtocol::on_debug_stack_frame_var(const Array &p_data) { List scope_ids = stackframe_list.find(frame)->value; ERR_FAIL_COND(scope_ids.size() != 3); - ERR_FAIL_INDEX(stack_var.type, 3); + ERR_FAIL_INDEX(stack_var.type, 4); int var_id = scope_ids.get(stack_var.type); DAP::Variable variable; diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp index cb5e4375a64..e085e2e4480 100644 --- a/editor/debugger/editor_debugger_inspector.cpp +++ b/editor/debugger/editor_debugger_inspector.cpp @@ -223,7 +223,7 @@ Object *EditorDebuggerInspector::get_object(ObjectID p_id) { return nullptr; } -void EditorDebuggerInspector::add_stack_variable(const Array &p_array) { +void EditorDebuggerInspector::add_stack_variable(const Array &p_array, int p_offset) { DebuggerMarshalls::ScriptStackVariable var; var.deserialize(p_array); String n = var.name; @@ -248,6 +248,9 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) { case 2: type = "Globals/"; break; + case 3: + type = "Evaluated/"; + break; default: type = "Unknown/"; } @@ -258,7 +261,15 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) { pinfo.hint = h; pinfo.hint_string = hs; - variables->prop_list.push_back(pinfo); + if ((p_offset == -1) || variables->prop_list.is_empty()) { + variables->prop_list.push_back(pinfo); + } else { + List::Element *current = variables->prop_list.front(); + for (int i = 0; i < p_offset; i++) { + current = current->next(); + } + variables->prop_list.insert_before(current, pinfo); + } variables->prop_values[type + n] = v; variables->update(); edit(variables); diff --git a/editor/debugger/editor_debugger_inspector.h b/editor/debugger/editor_debugger_inspector.h index 73dd773750d..fac95259436 100644 --- a/editor/debugger/editor_debugger_inspector.h +++ b/editor/debugger/editor_debugger_inspector.h @@ -90,7 +90,7 @@ public: // Stack Dump variables String get_stack_variable(const String &p_var); - void add_stack_variable(const Array &p_arr); + void add_stack_variable(const Array &p_arr, int p_offset = -1); void clear_stack_variables(); }; diff --git a/editor/debugger/editor_expression_evaluator.cpp b/editor/debugger/editor_expression_evaluator.cpp new file mode 100644 index 00000000000..e8b1e33d205 --- /dev/null +++ b/editor/debugger/editor_expression_evaluator.cpp @@ -0,0 +1,148 @@ +/**************************************************************************/ +/* editor_expression_evaluator.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "editor_expression_evaluator.h" + +#include "editor/debugger/editor_debugger_inspector.h" +#include "editor/debugger/script_editor_debugger.h" +#include "scene/gui/button.h" +#include "scene/gui/check_box.h" + +void EditorExpressionEvaluator::on_start() { + expression_input->set_editable(false); + evaluate_btn->set_disabled(true); + + if (clear_on_run_checkbox->is_pressed()) { + inspector->clear_stack_variables(); + } +} + +void EditorExpressionEvaluator::set_editor_debugger(ScriptEditorDebugger *p_editor_debugger) { + editor_debugger = p_editor_debugger; +} + +void EditorExpressionEvaluator::add_value(const Array &p_array) { + inspector->add_stack_variable(p_array, 0); + inspector->set_v_scroll(0); + inspector->set_h_scroll(0); +} + +void EditorExpressionEvaluator::_evaluate() { + const String &expression = expression_input->get_text(); + if (expression.is_empty()) { + return; + } + + if (!editor_debugger->is_session_active()) { + return; + } + + Array expr_data; + expr_data.push_back(expression); + expr_data.push_back(editor_debugger->get_stack_script_frame()); + editor_debugger->send_message("evaluate", expr_data); + + expression_input->clear(); +} + +void EditorExpressionEvaluator::_clear() { + inspector->clear_stack_variables(); +} + +void EditorExpressionEvaluator::_remote_object_selected(ObjectID p_id) { + editor_debugger->emit_signal(SNAME("remote_object_requested"), p_id); +} + +void EditorExpressionEvaluator::_on_expression_input_changed(const String &p_expression) { + evaluate_btn->set_disabled(p_expression.is_empty()); +} + +void EditorExpressionEvaluator::_on_debugger_breaked(bool p_breaked, bool p_can_debug) { + expression_input->set_editable(p_breaked); + evaluate_btn->set_disabled(!p_breaked); +} + +void EditorExpressionEvaluator::_on_debugger_clear_execution(Ref