[Lldb-commits] [lldb] 2011cbc - [lldb-dap] Add feature to remember last non-empty expression. (#107485)

via lldb-commits lldb-commits at lists.llvm.org
Fri Sep 20 14:04:48 PDT 2024


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.


        


More information about the lldb-commits mailing list