Skip to content

Commit 645abdb

Browse files
KoBeWirohanrhu
andcommitted
Add expression evaluater to debugger (REPL)
Co-authored-by: rohanrhu <[email protected]>
1 parent e3213aa commit 645abdb

File tree

8 files changed

+291
-6
lines changed

8 files changed

+291
-6
lines changed

core/debugger/remote_debugger.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "core/debugger/script_debugger.h"
3838
#include "core/input/input.h"
3939
#include "core/io/resource_loader.h"
40+
#include "core/math/expression.h"
4041
#include "core/object/script_language.h"
4142
#include "core/os/os.h"
4243
#include "servers/display_server.h"
@@ -529,6 +530,41 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
529530
} else if (command == "set_skip_breakpoints") {
530531
ERR_FAIL_COND(data.is_empty());
531532
script_debugger->set_skip_breakpoints(data[0]);
533+
} else if (command == "evaluate") {
534+
String expression_str = data[0];
535+
int frame = data[1];
536+
537+
ScriptInstance *breaked_instance = script_debugger->get_break_language()->debug_get_stack_level_instance(frame);
538+
if (!breaked_instance) {
539+
break;
540+
}
541+
542+
List<String> locals;
543+
List<Variant> local_vals;
544+
545+
script_debugger->get_break_language()->debug_get_stack_level_locals(frame, &locals, &local_vals);
546+
ERR_FAIL_COND(locals.size() != local_vals.size());
547+
548+
PackedStringArray locals_vector;
549+
for (const String &S : locals) {
550+
locals_vector.append(S);
551+
}
552+
553+
Array local_vals_array;
554+
for (const Variant &V : local_vals) {
555+
local_vals_array.append(V);
556+
}
557+
558+
Expression expression;
559+
expression.parse(expression_str, locals_vector);
560+
const Variant return_val = expression.execute(local_vals_array, breaked_instance->get_owner());
561+
562+
DebuggerMarshalls::ScriptStackVariable stvar;
563+
stvar.name = expression_str;
564+
stvar.value = return_val;
565+
stvar.type = 3;
566+
567+
send_message("evaluation_return", stvar.serialize());
532568
} else {
533569
bool captured = false;
534570
ERR_CONTINUE(_try_capture(command, data, captured) != OK);

editor/debugger/debug_adapter/debug_adapter_protocol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -966,7 +966,7 @@ void DebugAdapterProtocol::on_debug_stack_frame_var(const Array &p_data) {
966966

967967
List<int> scope_ids = stackframe_list.find(frame)->value;
968968
ERR_FAIL_COND(scope_ids.size() != 3);
969-
ERR_FAIL_INDEX(stack_var.type, 3);
969+
ERR_FAIL_INDEX(stack_var.type, 4);
970970
int var_id = scope_ids.get(stack_var.type);
971971

972972
DAP::Variable variable;

editor/debugger/editor_debugger_inspector.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ Object *EditorDebuggerInspector::get_object(ObjectID p_id) {
223223
return nullptr;
224224
}
225225

226-
void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
226+
void EditorDebuggerInspector::add_stack_variable(const Array &p_array, int p_offset) {
227227
DebuggerMarshalls::ScriptStackVariable var;
228228
var.deserialize(p_array);
229229
String n = var.name;
@@ -248,6 +248,9 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
248248
case 2:
249249
type = "Globals/";
250250
break;
251+
case 3:
252+
type = "Evaluated/";
253+
break;
251254
default:
252255
type = "Unknown/";
253256
}
@@ -258,7 +261,15 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
258261
pinfo.hint = h;
259262
pinfo.hint_string = hs;
260263

261-
variables->prop_list.push_back(pinfo);
264+
if ((p_offset == -1) || variables->prop_list.is_empty()) {
265+
variables->prop_list.push_back(pinfo);
266+
} else {
267+
List<PropertyInfo>::Element *current = variables->prop_list.front();
268+
for (int i = 0; i < p_offset; i++) {
269+
current = current->next();
270+
}
271+
variables->prop_list.insert_before(current, pinfo);
272+
}
262273
variables->prop_values[type + n] = v;
263274
variables->update();
264275
edit(variables);

editor/debugger/editor_debugger_inspector.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class EditorDebuggerInspector : public EditorInspector {
9090

9191
// Stack Dump variables
9292
String get_stack_variable(const String &p_var);
93-
void add_stack_variable(const Array &p_arr);
93+
void add_stack_variable(const Array &p_arr, int p_offset = -1);
9494
void clear_stack_variables();
9595
};
9696

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/**************************************************************************/
2+
/* editor_expression_evaluator.cpp */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#include "editor_expression_evaluator.h"
32+
33+
#include "editor/debugger/editor_debugger_inspector.h"
34+
#include "editor/debugger/script_editor_debugger.h"
35+
#include "scene/gui/button.h"
36+
#include "scene/gui/check_box.h"
37+
38+
void EditorExpressionEvaluator::on_start() {
39+
expression_input->set_editable(false);
40+
evaluate_btn->set_disabled(true);
41+
42+
if (clear_on_run_checkbox->is_pressed()) {
43+
inspector->clear_stack_variables();
44+
}
45+
}
46+
47+
void EditorExpressionEvaluator::set_editor_debugger(ScriptEditorDebugger *p_editor_debugger) {
48+
editor_debugger = p_editor_debugger;
49+
}
50+
51+
void EditorExpressionEvaluator::add_value(const Array &p_array) {
52+
inspector->add_stack_variable(p_array, 0);
53+
inspector->set_v_scroll(0);
54+
inspector->set_h_scroll(0);
55+
}
56+
57+
void EditorExpressionEvaluator::_evaluate() {
58+
const String &expression = expression_input->get_text();
59+
if (expression.is_empty()) {
60+
return;
61+
}
62+
63+
if (!editor_debugger->is_session_active()) {
64+
return;
65+
}
66+
67+
Array expr_data;
68+
expr_data.push_back(expression);
69+
expr_data.push_back(editor_debugger->get_stack_script_frame());
70+
editor_debugger->send_message("evaluate", expr_data);
71+
72+
expression_input->clear();
73+
}
74+
75+
void EditorExpressionEvaluator::_clear() {
76+
inspector->clear_stack_variables();
77+
}
78+
79+
void EditorExpressionEvaluator::_remote_object_selected(ObjectID p_id) {
80+
editor_debugger->emit_signal(SNAME("remote_object_requested"), p_id);
81+
}
82+
83+
void EditorExpressionEvaluator::_on_expression_input_changed(const String &p_expression) {
84+
evaluate_btn->set_disabled(p_expression.is_empty());
85+
}
86+
87+
void EditorExpressionEvaluator::_on_debugger_breaked(bool p_breaked, bool p_can_debug) {
88+
expression_input->set_editable(p_breaked);
89+
evaluate_btn->set_disabled(!p_breaked);
90+
}
91+
92+
void EditorExpressionEvaluator::_on_debugger_clear_execution(Ref<Script> p_stack_script) {
93+
expression_input->set_editable(false);
94+
evaluate_btn->set_disabled(true);
95+
}
96+
97+
void EditorExpressionEvaluator::_notification(int p_what) {
98+
switch (p_what) {
99+
case NOTIFICATION_READY: {
100+
EditorDebuggerNode::get_singleton()->connect("breaked", callable_mp(this, &EditorExpressionEvaluator::_on_debugger_breaked));
101+
EditorDebuggerNode::get_singleton()->connect("clear_execution", callable_mp(this, &EditorExpressionEvaluator::_on_debugger_clear_execution));
102+
} break;
103+
}
104+
}
105+
106+
EditorExpressionEvaluator::EditorExpressionEvaluator() {
107+
set_h_size_flags(SIZE_EXPAND_FILL);
108+
109+
HBoxContainer *hb = memnew(HBoxContainer);
110+
add_child(hb);
111+
112+
expression_input = memnew(LineEdit);
113+
expression_input->set_h_size_flags(Control::SIZE_EXPAND_FILL);
114+
expression_input->set_placeholder(TTR("Expression to evaluate"));
115+
expression_input->set_clear_button_enabled(true);
116+
expression_input->connect("text_submitted", callable_mp(this, &EditorExpressionEvaluator::_evaluate).unbind(1));
117+
expression_input->connect(SceneStringName(text_changed), callable_mp(this, &EditorExpressionEvaluator::_on_expression_input_changed));
118+
hb->add_child(expression_input);
119+
120+
clear_on_run_checkbox = memnew(CheckBox);
121+
clear_on_run_checkbox->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
122+
clear_on_run_checkbox->set_text(TTR("Clear on Run"));
123+
clear_on_run_checkbox->set_pressed(true);
124+
hb->add_child(clear_on_run_checkbox);
125+
126+
evaluate_btn = memnew(Button);
127+
evaluate_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
128+
evaluate_btn->set_text(TTR("Evaluate"));
129+
evaluate_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorExpressionEvaluator::_evaluate));
130+
hb->add_child(evaluate_btn);
131+
132+
clear_btn = memnew(Button);
133+
clear_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
134+
clear_btn->set_text(TTR("Clear"));
135+
clear_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorExpressionEvaluator::_clear));
136+
hb->add_child(clear_btn);
137+
138+
inspector = memnew(EditorDebuggerInspector);
139+
inspector->set_v_size_flags(SIZE_EXPAND_FILL);
140+
inspector->set_property_name_style(EditorPropertyNameProcessor::STYLE_RAW);
141+
inspector->set_read_only(true);
142+
inspector->connect("object_selected", callable_mp(this, &EditorExpressionEvaluator::_remote_object_selected));
143+
inspector->set_use_filter(true);
144+
add_child(inspector);
145+
146+
expression_input->set_editable(false);
147+
evaluate_btn->set_disabled(true);
148+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**************************************************************************/
2+
/* editor_expression_evaluator.h */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#ifndef EDITOR_EXPRESSION_EVALUATOR_H
32+
#define EDITOR_EXPRESSION_EVALUATOR_H
33+
34+
#include "scene/gui/box_container.h"
35+
36+
class Button;
37+
class CheckBox;
38+
class EditorDebuggerInspector;
39+
class LineEdit;
40+
class RemoteDebuggerPeer;
41+
class ScriptEditorDebugger;
42+
43+
class EditorExpressionEvaluator : public VBoxContainer {
44+
GDCLASS(EditorExpressionEvaluator, VBoxContainer)
45+
46+
private:
47+
Ref<RemoteDebuggerPeer> peer;
48+
49+
LineEdit *expression_input = nullptr;
50+
CheckBox *clear_on_run_checkbox = nullptr;
51+
Button *evaluate_btn = nullptr;
52+
Button *clear_btn = nullptr;
53+
54+
EditorDebuggerInspector *inspector = nullptr;
55+
56+
void _evaluate();
57+
void _clear();
58+
59+
void _remote_object_selected(ObjectID p_id);
60+
void _on_expression_input_changed(const String &p_expression);
61+
void _on_debugger_breaked(bool p_breaked, bool p_can_debug);
62+
void _on_debugger_clear_execution(Ref<Script> p_stack_script);
63+
64+
protected:
65+
ScriptEditorDebugger *editor_debugger = nullptr;
66+
67+
void _notification(int p_what);
68+
69+
public:
70+
void on_start();
71+
void set_editor_debugger(ScriptEditorDebugger *p_editor_debugger);
72+
void add_value(const Array &p_array);
73+
74+
EditorExpressionEvaluator();
75+
};
76+
77+
#endif // EDITOR_EXPRESSION_EVALUATOR_H

editor/debugger/script_editor_debugger.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "core/string/ustring.h"
3838
#include "core/version.h"
3939
#include "editor/debugger/debug_adapter/debug_adapter_protocol.h"
40+
#include "editor/debugger/editor_expression_evaluator.h"
4041
#include "editor/debugger/editor_performance_profiler.h"
4142
#include "editor/debugger/editor_profiler.h"
4243
#include "editor/debugger/editor_visual_profiler.h"
@@ -811,6 +812,8 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
811812
if (EditorFileSystem::get_singleton()) {
812813
EditorFileSystem::get_singleton()->update_file(p_data[0]);
813814
}
815+
} else if (p_msg == "evaluation_return") {
816+
expression_evaluator->add_value(p_data);
814817
} else {
815818
int colon_index = p_msg.find_char(':');
816819
ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received");
@@ -854,8 +857,9 @@ void ScriptEditorDebugger::_notification(int p_what) {
854857
error_tree->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_error_selected));
855858
error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated));
856859
breakpoints_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_breakpoint_tree_clicked));
857-
[[fallthrough]];
858-
}
860+
connect("started", callable_mp(expression_evaluator, &EditorExpressionEvaluator::on_start));
861+
} break;
862+
859863
case NOTIFICATION_THEME_CHANGED: {
860864
tabs->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));
861865

@@ -2010,6 +2014,13 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
20102014
add_child(file_dialog);
20112015
}
20122016

2017+
{ // Expression evaluator
2018+
expression_evaluator = memnew(EditorExpressionEvaluator);
2019+
expression_evaluator->set_name(TTR("Evaluator"));
2020+
expression_evaluator->set_editor_debugger(this);
2021+
tabs->add_child(expression_evaluator);
2022+
}
2023+
20132024
{ //profiler
20142025
profiler = memnew(EditorProfiler);
20152026
profiler->set_name(TTR("Profiler"));

editor/debugger/script_editor_debugger.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class SceneDebuggerTree;
5656
class EditorDebuggerPlugin;
5757
class DebugAdapterProtocol;
5858
class DebugAdapterParser;
59+
class EditorExpressionEvaluator;
5960

6061
class ScriptEditorDebugger : public MarginContainer {
6162
GDCLASS(ScriptEditorDebugger, MarginContainer);
@@ -152,6 +153,7 @@ class ScriptEditorDebugger : public MarginContainer {
152153
EditorProfiler *profiler = nullptr;
153154
EditorVisualProfiler *visual_profiler = nullptr;
154155
EditorPerformanceProfiler *performance_profiler = nullptr;
156+
EditorExpressionEvaluator *expression_evaluator = nullptr;
155157

156158
OS::ProcessID remote_pid = 0;
157159
bool move_to_foreground = true;

0 commit comments

Comments
 (0)