Author: cmtice Date: 2024-09-20T14:04:44-07:00 New Revision: 2011cbcd84102236dd6d58e2079ac676a3403f25
URL: https://github.com/llvm/llvm-project/commit/2011cbcd84102236dd6d58e2079ac676a3403f25 DIFF: https://github.com/llvm/llvm-project/commit/2011cbcd84102236dd6d58e2079ac676a3403f25.diff LOG: [lldb-dap] Add feature to remember last non-empty expression. (#107485) Update lldb-dap so if the user just presses return, which sends an empty expression, it re-evaluates the most recent non-empty expression/command. Also udpated test to test this case. Added: Modified: lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py lldb/test/API/tools/lldb-dap/evaluate/main.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/LLDBUtils.cpp lldb/tools/lldb-dap/lldb-dap.cpp Removed: ################################################################################ diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index 29548a835c6919..0126d40d86fca2 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -54,13 +54,22 @@ def run_test_evaluate_expressions( line_number(source, "// breakpoint 5"), line_number(source, "// breakpoint 6"), line_number(source, "// breakpoint 7"), + line_number(source, "// breakpoint 8"), ], ) self.continue_to_next_stop() # Expressions at breakpoint 1, which is in main self.assertEvaluate("var1", "20") + # Empty expression should equate to the previous expression. + if context == "repl": + self.assertEvaluate("", "20") + else: + self.assertEvaluateFailure("") self.assertEvaluate("var2", "21") + if context == "repl": + self.assertEvaluate("", "21") + self.assertEvaluate("", "21") self.assertEvaluate("static_int", "42") self.assertEvaluate("non_static_int", "43") self.assertEvaluate("struct1.foo", "15") @@ -191,6 +200,15 @@ def run_test_evaluate_expressions( self.continue_to_next_stop() self.assertEvaluate("my_bool_vec", "size=2") + # Test memory read, especially with 'empty' repeat commands. + if context == "repl": + self.continue_to_next_stop() + self.assertEvaluate("memory read -c 1 &my_ints", ".* 05 .*\n") + self.assertEvaluate("", ".* 0a .*\n") + self.assertEvaluate("", ".* 0f .*\n") + self.assertEvaluate("", ".* 14 .*\n") + self.assertEvaluate("", ".* 19 .*\n") + @skipIfWindows def test_generic_evaluate_expressions(self): # Tests context-less expression evaluations diff --git a/lldb/test/API/tools/lldb-dap/evaluate/main.cpp b/lldb/test/API/tools/lldb-dap/evaluate/main.cpp index ca27b5ba5ca19d..1c68716e3a6e11 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/main.cpp +++ b/lldb/test/API/tools/lldb-dap/evaluate/main.cpp @@ -1,5 +1,6 @@ #include "foo.h" +#include <cstdint> #include <map> #include <vector> @@ -45,5 +46,6 @@ int main(int argc, char const *argv[]) { my_bool_vec.push_back(false); // breakpoint 6 my_bool_vec.push_back(true); // breakpoint 7 - return 0; + uint8_t my_ints[] = {5, 10, 15, 20, 25, 30}; + return 0; // breakpoint 8 } diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index e28fd593ed74ce..bc2c055e2c56d5 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -205,6 +205,12 @@ struct DAP { std::string command_escape_prefix = "`"; lldb::SBFormat frame_format; lldb::SBFormat thread_format; + // This is used to allow request_evaluate to handle empty expressions + // (ie the user pressed 'return' and expects the previous expression to + // repeat). If the previous expression was a command, this string will be + // empty; if the previous expression was a variable expression, this string + // will contain that expression. + std::string last_nonempty_var_expression; DAP(); ~DAP(); diff --git a/lldb/tools/lldb-dap/LLDBUtils.cpp b/lldb/tools/lldb-dap/LLDBUtils.cpp index ff6bea1b23eee8..a74b32609a167b 100644 --- a/lldb/tools/lldb-dap/LLDBUtils.cpp +++ b/lldb/tools/lldb-dap/LLDBUtils.cpp @@ -45,7 +45,8 @@ bool RunLLDBCommands(llvm::StringRef prefix, // RunTerminateCommands. static std::mutex handle_command_mutex; std::lock_guard<std::mutex> locker(handle_command_mutex); - interp.HandleCommand(command.str().c_str(), result); + interp.HandleCommand(command.str().c_str(), result, + /*add_to_history=*/true); } const bool got_error = !result.Succeeded(); diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index 2fb86f675b4516..93676d7add239d 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -1568,9 +1568,16 @@ void request_evaluate(const llvm::json::Object &request) { lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments); std::string expression = GetString(arguments, "expression").str(); llvm::StringRef context = GetString(arguments, "context"); - - if (context == "repl" && g_dap.DetectExpressionContext(frame, expression) == - ExpressionContext::Command) { + bool repeat_last_command = + expression.empty() && g_dap.last_nonempty_var_expression.empty(); + + if (context == "repl" && (repeat_last_command || + (!expression.empty() && + g_dap.DetectExpressionContext(frame, expression) == + ExpressionContext::Command))) { + // Since the current expression is not for a variable, clear the + // last_nonempty_var_expression field. + g_dap.last_nonempty_var_expression.clear(); // If we're evaluating a command relative to the current frame, set the // focus_tid to the current frame for any thread related events. if (frame.IsValid()) { @@ -1581,6 +1588,16 @@ void request_evaluate(const llvm::json::Object &request) { EmplaceSafeString(body, "result", result); body.try_emplace("variablesReference", (int64_t)0); } else { + if (context == "repl") { + // If the expression is empty and the last expression was for a + // variable, set the expression to the previous expression (repeat the + // evaluation); otherwise save the current non-empty expression for the + // next (possibly empty) variable expression. + if (expression.empty()) + expression = g_dap.last_nonempty_var_expression; + else + g_dap.last_nonempty_var_expression = expression; + } // Always try to get the answer from the local variables if possible. If // this fails, then if the context is not "hover", actually evaluate an // expression using the expression parser. _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits