[Lldb-commits] [lldb] 75ca15f - Fix backtick handling in parsed commands.

Jim Ingham via lldb-commits lldb-commits at lists.llvm.org
Fri Mar 24 10:40:19 PDT 2023


Author: Jim Ingham
Date: 2023-03-24T10:40:10-07:00
New Revision: 75ca15fcbb2e1b3671e41f971a000c6d59f5e5ae

URL: https://github.com/llvm/llvm-project/commit/75ca15fcbb2e1b3671e41f971a000c6d59f5e5ae
DIFF: https://github.com/llvm/llvm-project/commit/75ca15fcbb2e1b3671e41f971a000c6d59f5e5ae.diff

LOG: Fix backtick handling in parsed commands.

https://reviews.llvm.org/D146779

Added: 
    

Modified: 
    lldb/include/lldb/Interpreter/CommandInterpreter.h
    lldb/source/Interpreter/CommandInterpreter.cpp
    lldb/source/Interpreter/CommandObject.cpp
    lldb/test/API/commands/command/backticks/TestBackticksInAlias.py

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index cf44412b13026..e428614fbedb1 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -637,6 +637,9 @@ class CommandInterpreter : public Broadcaster,
 
   bool IOHandlerInterrupt(IOHandler &io_handler) override;
 
+  Status PreprocessCommand(std::string &command);
+  Status PreprocessToken(std::string &token);
+
 protected:
   friend class Debugger;
 
@@ -671,8 +674,6 @@ class CommandInterpreter : public Broadcaster,
 
   void RestoreExecutionContext();
 
-  Status PreprocessCommand(std::string &command);
-
   void SourceInitFile(FileSpec file, CommandReturnObject &result);
 
   // Completely resolves aliases and abbreviations, returning a pointer to the

diff  --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index cf3fff23005bf..09fc8e216bc00 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -1750,112 +1750,124 @@ Status CommandInterpreter::PreprocessCommand(std::string &command) {
 
     std::string expr_str(command, expr_content_start,
                          end_backtick - expr_content_start);
+    error = PreprocessToken(expr_str);
+    // We always stop at the first error:
+    if (error.Fail())
+      break;
 
-    ExecutionContext exe_ctx(GetExecutionContext());
+    command.erase(start_backtick, end_backtick - start_backtick + 1);
+    command.insert(start_backtick, std::string(expr_str));
+    pos = start_backtick + expr_str.size();
+  }
+  return error;                        
+}
 
-    // Get a dummy target to allow for calculator mode while processing
-    // backticks. This also helps break the infinite loop caused when target is
-    // null.
-    Target *exe_target = exe_ctx.GetTargetPtr();
-    Target &target = exe_target ? *exe_target : m_debugger.GetDummyTarget();
-
-    ValueObjectSP expr_result_valobj_sp;
-
-    EvaluateExpressionOptions options;
-    options.SetCoerceToId(false);
-    options.SetUnwindOnError(true);
-    options.SetIgnoreBreakpoints(true);
-    options.SetKeepInMemory(false);
-    options.SetTryAllThreads(true);
-    options.SetTimeout(std::nullopt);
-
-    ExpressionResults expr_result =
-        target.EvaluateExpression(expr_str.c_str(), exe_ctx.GetFramePtr(),
-                                  expr_result_valobj_sp, options);
-
-    if (expr_result == eExpressionCompleted) {
-      Scalar scalar;
-      if (expr_result_valobj_sp)
-        expr_result_valobj_sp =
-            expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable(
-                expr_result_valobj_sp->GetDynamicValueType(), true);
-      if (expr_result_valobj_sp->ResolveValue(scalar)) {
-        command.erase(start_backtick, end_backtick - start_backtick + 1);
-        StreamString value_strm;
-        const bool show_type = false;
-        scalar.GetValue(&value_strm, show_type);
-        size_t value_string_size = value_strm.GetSize();
-        if (value_string_size) {
-          command.insert(start_backtick, std::string(value_strm.GetString()));
-          pos = start_backtick + value_string_size;
-          continue;
-        } else {
-          error.SetErrorStringWithFormat("expression value didn't result "
-                                         "in a scalar value for the "
-                                         "expression '%s'",
-                                         expr_str.c_str());
-          break;
-        }
-      } else {
-        error.SetErrorStringWithFormat("expression value didn't result "
-                                       "in a scalar value for the "
-                                       "expression '%s'",
-                                       expr_str.c_str());
-        break;
-      }
+Status
+CommandInterpreter::PreprocessToken(std::string &expr_str) {
+  Status error;
+  ExecutionContext exe_ctx(GetExecutionContext());
 
-      continue;
-    }
+  // Get a dummy target to allow for calculator mode while processing
+  // backticks. This also helps break the infinite loop caused when target is
+  // null.
+  Target *exe_target = exe_ctx.GetTargetPtr();
+  Target &target = exe_target ? *exe_target : m_debugger.GetDummyTarget();
 
-    if (expr_result_valobj_sp)
-      error = expr_result_valobj_sp->GetError();
+  ValueObjectSP expr_result_valobj_sp;
 
-    if (error.Success()) {
-      switch (expr_result) {
-      case eExpressionSetupError:
-        error.SetErrorStringWithFormat(
-            "expression setup error for the expression '%s'", expr_str.c_str());
-        break;
-      case eExpressionParseError:
-        error.SetErrorStringWithFormat(
-            "expression parse error for the expression '%s'", expr_str.c_str());
-        break;
-      case eExpressionResultUnavailable:
-        error.SetErrorStringWithFormat(
-            "expression error fetching result for the expression '%s'",
-            expr_str.c_str());
-        break;
-      case eExpressionCompleted:
-        break;
-      case eExpressionDiscarded:
-        error.SetErrorStringWithFormat(
-            "expression discarded for the expression '%s'", expr_str.c_str());
-        break;
-      case eExpressionInterrupted:
-        error.SetErrorStringWithFormat(
-            "expression interrupted for the expression '%s'", expr_str.c_str());
-        break;
-      case eExpressionHitBreakpoint:
-        error.SetErrorStringWithFormat(
-            "expression hit breakpoint for the expression '%s'",
-            expr_str.c_str());
-        break;
-      case eExpressionTimedOut:
-        error.SetErrorStringWithFormat(
-            "expression timed out for the expression '%s'", expr_str.c_str());
-        break;
-      case eExpressionStoppedForDebug:
-        error.SetErrorStringWithFormat("expression stop at entry point "
-                                       "for debugging for the "
+  EvaluateExpressionOptions options;
+  options.SetCoerceToId(false);
+  options.SetUnwindOnError(true);
+  options.SetIgnoreBreakpoints(true);
+  options.SetKeepInMemory(false);
+  options.SetTryAllThreads(true);
+  options.SetTimeout(std::nullopt);
+
+  ExpressionResults expr_result =
+      target.EvaluateExpression(expr_str.c_str(), exe_ctx.GetFramePtr(),
+                                expr_result_valobj_sp, options);
+
+  if (expr_result == eExpressionCompleted) {
+    Scalar scalar;
+    if (expr_result_valobj_sp)
+      expr_result_valobj_sp =
+          expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable(
+              expr_result_valobj_sp->GetDynamicValueType(), true);
+    if (expr_result_valobj_sp->ResolveValue(scalar)) {
+      
+      StreamString value_strm;
+      const bool show_type = false;
+      scalar.GetValue(&value_strm, show_type);
+      size_t value_string_size = value_strm.GetSize();
+      if (value_string_size) {
+        expr_str = value_strm.GetData();
+      } else {
+        error.SetErrorStringWithFormat("expression value didn't result "
+                                       "in a scalar value for the "
                                        "expression '%s'",
                                        expr_str.c_str());
-        break;
-      case eExpressionThreadVanished:
-        error.SetErrorStringWithFormat(
-            "expression thread vanished for the expression '%s'",
-            expr_str.c_str());
-        break;
       }
+    } else {
+      error.SetErrorStringWithFormat("expression value didn't result "
+                                     "in a scalar value for the "
+                                     "expression '%s'",
+                                     expr_str.c_str());
+    }
+    return error;
+  }
+
+  // If we have an error from the expression evaluation it will be in the
+  // ValueObject error, which won't be success and we will just report it.
+  // But if for some reason we didn't get a value object at all, then we will
+  // make up some helpful errors from the expression result.
+  if (expr_result_valobj_sp)
+    error = expr_result_valobj_sp->GetError();
+
+  if (error.Success()) {
+    switch (expr_result) {
+    case eExpressionSetupError:
+      error.SetErrorStringWithFormat(
+          "expression setup error for the expression '%s'", expr_str.c_str());
+      break;
+    case eExpressionParseError:
+      error.SetErrorStringWithFormat(
+          "expression parse error for the expression '%s'", expr_str.c_str());
+      break;
+    case eExpressionResultUnavailable:
+      error.SetErrorStringWithFormat(
+          "expression error fetching result for the expression '%s'",
+          expr_str.c_str());
+      break;
+    case eExpressionCompleted:
+      break;
+    case eExpressionDiscarded:
+      error.SetErrorStringWithFormat(
+          "expression discarded for the expression '%s'", expr_str.c_str());
+      break;
+    case eExpressionInterrupted:
+      error.SetErrorStringWithFormat(
+          "expression interrupted for the expression '%s'", expr_str.c_str());
+      break;
+    case eExpressionHitBreakpoint:
+      error.SetErrorStringWithFormat(
+          "expression hit breakpoint for the expression '%s'",
+          expr_str.c_str());
+      break;
+    case eExpressionTimedOut:
+      error.SetErrorStringWithFormat(
+          "expression timed out for the expression '%s'", expr_str.c_str());
+      break;
+    case eExpressionStoppedForDebug:
+      error.SetErrorStringWithFormat("expression stop at entry point "
+                                     "for debugging for the "
+                                     "expression '%s'",
+                                     expr_str.c_str());
+      break;
+    case eExpressionThreadVanished:
+      error.SetErrorStringWithFormat(
+          "expression thread vanished for the expression '%s'",
+          expr_str.c_str());
+      break;
     }
   }
   return error;

diff  --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp
index 4500378c62298..c13ce4ece4297 100644
--- a/lldb/source/Interpreter/CommandObject.cpp
+++ b/lldb/source/Interpreter/CommandObject.cpp
@@ -727,10 +727,14 @@ bool CommandObjectParsed::Execute(const char *args_string,
   }
   if (!handled) {
     for (auto entry : llvm::enumerate(cmd_args.entries())) {
-      if (!entry.value().ref().empty() && entry.value().GetQuoteChar() == '`') {
-        cmd_args.ReplaceArgumentAtIndex(
-            entry.index(),
-            m_interpreter.ProcessEmbeddedScriptCommands(entry.value().c_str()));
+      const Args::ArgEntry &value = entry.value();
+      if (!value.ref().empty() && value.GetQuoteChar() == '`') {
+        // We have to put the backtick back in place for PreprocessCommand.
+        std::string opt_string = value.c_str();
+        Status error;
+        error = m_interpreter.PreprocessToken(opt_string);
+        if (error.Success())
+          cmd_args.ReplaceArgumentAtIndex(entry.index(), opt_string);
       }
     }
 

diff  --git a/lldb/test/API/commands/command/backticks/TestBackticksInAlias.py b/lldb/test/API/commands/command/backticks/TestBackticksInAlias.py
index 495e52db53aa3..4a95cff21e713 100644
--- a/lldb/test/API/commands/command/backticks/TestBackticksInAlias.py
+++ b/lldb/test/API/commands/command/backticks/TestBackticksInAlias.py
@@ -29,4 +29,53 @@ def test_backticks_in_alias(self):
         interp.HandleCommand("_test-argv-parray-cmd", result)
         self.assertFalse(result.Succeeded(), "CommandAlias::Desugar currently fails if a alias substitutes %N arguments in another alias")
 
+    def test_backticks_in_parsed_cmd_argument(self):
+        """ break list is a parsed command, use a variable for the breakpoint number
+            and make sure that and the direct use of the ID get the same result. """
+        self.build()
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
+        # Make a second breakpoint so that if the backtick part -> nothing we'll print too much:
+        # It doesn't need to resolve to anything.
+        dummy_bkpt = target.BreakpointCreateByName("dont_really_care_if_this_exists")
+        
+        bkpt_id = bkpt.GetID()
+        self.runCmd(f"expr int $number = {bkpt_id}")
+        direct_result = lldb.SBCommandReturnObject()
+        backtick_result = lldb.SBCommandReturnObject()
+        interp = self.dbg.GetCommandInterpreter()
+        interp.HandleCommand(f"break list {bkpt_id}", direct_result)
+        self.assertTrue(direct_result.Succeeded(), "Break list with id works")
+        interp.HandleCommand("break list `$number`", backtick_result)
+        self.assertTrue(direct_result.Succeeded(), "Break list with backtick works")
+        self.assertEqual(direct_result.GetOutput(), backtick_result.GetOutput(), "Output is the same")
+
+    def test_backticks_in_parsed_cmd_option(self):
+        # The script interpreter is a raw command, so try that one:
+        self.build()
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
+
+        self.runCmd(f"expr int $number = 2")
+        direct_result = lldb.SBCommandReturnObject()
+        backtick_result = lldb.SBCommandReturnObject()
+        interp = self.dbg.GetCommandInterpreter()
+        interp.HandleCommand(f"memory read --count 2 argv", direct_result)
+        self.assertTrue(direct_result.Succeeded(), "memory read with direct count works")
+        interp.HandleCommand("memory read --count `$number` argv", backtick_result)
+        self.assertTrue(direct_result.Succeeded(), "memory read with backtick works")
+        self.assertEqual(direct_result.GetOutput(), backtick_result.GetOutput(), "Output is the same")
+
+    def test_backticks_in_raw_cmd(self):
+        # The script interpreter is a raw command, so try that one:
+        self.build()
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
+        argc_valobj = thread.frames[0].FindVariable("argc")
+        self.assertTrue(argc_valobj.GetError().Success(), "Made argc valobj")
+        argc_value = argc_valobj.GetValueAsUnsigned(0)
+        self.assertNotEqual(argc_value, 0, "Got a value for argc")
+        result = lldb.SBCommandReturnObject()
+        interp = self.dbg.GetCommandInterpreter()
+        interp.HandleCommand(f"script {argc_value} - `argc`", result)
+        self.assertTrue(result.Succeeded(), "Command succeeded")
+        self.assertEqual("0\n", result.GetOutput(), "Substitution worked")
+
         


        


More information about the lldb-commits mailing list