wallace created this revision.
wallace added reviewers: labath, clayborg.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

This adds support for commands created through the API to support autorepeat.
This covers the case of single word and multiword commands.

Comprehensive tests are included as well.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D77444

Files:
  lldb/include/lldb/API/SBCommandInterpreter.h
  lldb/source/API/SBCommandInterpreter.cpp
  lldb/test/API/api/auto-repeat-command/Makefile
  lldb/test/API/api/auto-repeat-command/TestSBCommandAutoRepeat.py
  lldb/test/API/api/auto-repeat-command/main.cpp

Index: lldb/test/API/api/auto-repeat-command/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/api/auto-repeat-command/main.cpp
@@ -0,0 +1,109 @@
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBDebugger.h"
+
+#include <cassert>
+#include <cstring>
+#include <string>
+
+using namespace lldb;
+
+class DummyCommand : public SBCommandPluginInterface {
+public:
+  DummyCommand(const char *message) : m_message(message) {}
+
+  bool DoExecute(SBDebugger dbg, char **command,
+                 SBCommandReturnObject &result) {
+    result.PutCString(m_message.c_str());
+    result.SetStatus(eReturnStatusSuccessFinishResult);
+    return result.Succeeded();
+  }
+
+private:
+  std::string m_message;
+};
+
+void testSingleWordCommand(SBCommandInterpreter &interp, SBDebugger &dbg) {
+  // We first test a command without autorepeat
+  DummyCommand dummy("It worked");
+  interp.AddCommand("dummy", &dummy, nullptr /*help*/);
+  {
+    SBCommandReturnObject result;
+    dbg.GetCommandInterpreter().HandleCommand("dummy", result,
+                                              true /*add_to_history*/);
+    assert(result.Succeeded());
+    assert(strcmp(result.GetOutput(), "It worked\n") == 0);
+  }
+  {
+    SBCommandReturnObject result;
+    dbg.GetCommandInterpreter().HandleCommand("", result);
+    assert(!result.Succeeded() &&
+           "The command should fail as a repeated command");
+    assert(strcmp(result.GetError(), "error: No auto repeat.\n") == 0);
+  }
+
+  // Now we test a command with autorepeat
+  interp.AddCommand("dummy_with_autorepeat", &dummy, nullptr /*help*/,
+                    nullptr /*syntax*/, true /*autorepeat*/);
+  {
+    SBCommandReturnObject result;
+    dbg.GetCommandInterpreter().HandleCommand("dummy_with_autorepeat", result,
+                                              true /*add_to_history*/);
+    assert(result.Succeeded());
+    assert(strcmp(result.GetOutput(), "It worked\n") == 0);
+  }
+  {
+    SBCommandReturnObject result;
+    dbg.GetCommandInterpreter().HandleCommand("", result);
+    assert(result.Succeeded());
+    assert(strcmp(result.GetOutput(), "It worked\n") == 0);
+  }
+}
+
+void testMultiWordCommand(SBCommandInterpreter &interp, SBDebugger &dbg) {
+  auto command = interp.AddMultiwordCommand("multicommand", nullptr /*help*/);
+  // We first test a subcommand without autorepeat
+  DummyCommand subcommand("It worked again");
+  command.AddCommand("subcommand", &subcommand, nullptr /*help*/);
+  {
+    SBCommandReturnObject result;
+    dbg.GetCommandInterpreter().HandleCommand("multicommand subcommand", result,
+                                              true /*add_to_history*/);
+    assert(result.Succeeded());
+    assert(strcmp(result.GetOutput(), "It worked again\n") == 0);
+  }
+  {
+    SBCommandReturnObject result;
+    dbg.GetCommandInterpreter().HandleCommand("", result);
+    assert(!result.Succeeded() &&
+           "The command should fail as a repeated command");
+    assert(strcmp(result.GetError(), "error: No auto repeat.\n") == 0);
+  }
+
+  // We first test a subcommand with autorepeat
+  command.AddCommand("subcommand_with_autorepeat", &subcommand,
+                     nullptr /*help*/, nullptr /*syntax*/, true /*autorepeat*/);
+  {
+    SBCommandReturnObject result;
+    dbg.GetCommandInterpreter().HandleCommand(
+        "multicommand subcommand_with_autorepeat", result,
+        true /*add_to_history*/);
+    assert(result.Succeeded());
+    assert(strcmp(result.GetOutput(), "It worked again\n") == 0);
+  }
+  {
+    SBCommandReturnObject result;
+    dbg.GetCommandInterpreter().HandleCommand("", result);
+    assert(result.Succeeded());
+    assert(strcmp(result.GetOutput(), "It worked again\n") == 0);
+  }
+}
+
+int main() {
+  SBDebugger::Initialize();
+  SBDebugger dbg = SBDebugger::Create(false /*source_init_files*/);
+  SBCommandInterpreter interp = dbg.GetCommandInterpreter();
+
+  testSingleWordCommand(interp, dbg);
+  testMultiWordCommand(interp, dbg);
+}
Index: lldb/test/API/api/auto-repeat-command/TestSBCommandAutoRepeat.py
===================================================================
--- /dev/null
+++ lldb/test/API/api/auto-repeat-command/TestSBCommandAutoRepeat.py
@@ -0,0 +1,34 @@
+"""Test the lldb public C++ api for returning SBCommandReturnObject."""
+
+from __future__ import print_function
+
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestSBCommandAutoRepeat(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @skipIfNoSBHeaders
+    @expectedFailureAll(
+        oslist=["windows"],
+        bugnumber="llvm.org/pr43570")
+    def test_sb_command_return_object(self):
+        env = {self.dylibPath: self.getLLDBLibraryEnvVal()}
+
+        self.driver_exe = self.getBuildArtifact("auto-repeat-command")
+        self.buildDriver('main.cpp', self.driver_exe)
+        self.addTearDownHook(lambda: os.remove(self.driver_exe))
+        self.signBinary(self.driver_exe)
+
+        if self.TraceOn():
+            print("Running test %s" % self.driver_exe)
+            check_call([self.driver_exe, self.driver_exe], env=env)
+        else:
+            with open(os.devnull, 'w') as fnull:
+                check_call([self.driver_exe, self.driver_exe],
+                           env=env, stdout=fnull, stderr=fnull)
Index: lldb/test/API/api/auto-repeat-command/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/api/auto-repeat-command/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
Index: lldb/source/API/SBCommandInterpreter.cpp
===================================================================
--- lldb/source/API/SBCommandInterpreter.cpp
+++ lldb/source/API/SBCommandInterpreter.cpp
@@ -154,12 +154,22 @@
                                        lldb::SBCommandPluginInterface *backend,
                                        const char *help = nullptr,
                                        const char *syntax = nullptr,
-                                       uint32_t flags = 0)
+                                       uint32_t flags = 0,
+                                       bool auto_repeat = false)
       : CommandObjectParsed(interpreter, name, help, syntax, flags),
-        m_backend(backend) {}
+        m_backend(backend), m_auto_repeat(auto_repeat) {}
 
   bool IsRemovable() const override { return true; }
 
+  /// More documentation is available in lldb::CommandObject::GetRepeatCommand,
+  /// but in short, if nullptr is returned, the previous command will be
+  /// repeated, and if an empty string is returned, no commands will be
+  /// executed.
+  const char *GetRepeatCommand(Args &current_command_args,
+                               uint32_t index) override {
+    return m_auto_repeat ? nullptr : "";
+  }
+
 protected:
   bool DoExecute(Args &command, CommandReturnObject &result) override {
     SBCommandReturnObject sb_return(result);
@@ -170,6 +180,7 @@
     return ret;
   }
   std::shared_ptr<lldb::SBCommandPluginInterface> m_backend;
+  bool m_auto_repeat;
 };
 
 SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter)
@@ -710,6 +721,24 @@
   return LLDB_RECORD_RESULT(lldb::SBCommand());
 }
 
+lldb::SBCommand SBCommandInterpreter::AddCommand(
+    const char *name, lldb::SBCommandPluginInterface *impl, const char *help,
+    const char *syntax, bool auto_repeat) {
+  LLDB_RECORD_METHOD(lldb::SBCommand, SBCommandInterpreter, AddCommand,
+                     (const char *, lldb::SBCommandPluginInterface *,
+                      const char *, const char *, bool),
+                     name, impl, help, syntax, auto_repeat);
+
+  lldb::CommandObjectSP new_command_sp;
+  new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
+      *m_opaque_ptr, name, impl, help, syntax, 0 /*flags*/, auto_repeat);
+
+  if (new_command_sp &&
+      m_opaque_ptr->AddUserCommand(name, new_command_sp, true))
+    return LLDB_RECORD_RESULT(lldb::SBCommand(new_command_sp));
+  return LLDB_RECORD_RESULT(lldb::SBCommand());
+}
+
 SBCommand::SBCommand() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBCommand); }
 
 SBCommand::SBCommand(lldb::CommandObjectSP cmd_sp) : m_opaque_sp(cmd_sp) {}
@@ -816,6 +845,28 @@
   return LLDB_RECORD_RESULT(lldb::SBCommand());
 }
 
+lldb::SBCommand SBCommand::AddCommand(const char *name,
+                                      lldb::SBCommandPluginInterface *impl,
+                                      const char *help, const char *syntax,
+                                      bool auto_repeat) {
+  LLDB_RECORD_METHOD(lldb::SBCommand, SBCommand, AddCommand,
+                     (const char *, lldb::SBCommandPluginInterface *,
+                      const char *, const char *, bool),
+                     name, impl, help, syntax, auto_repeat);
+
+  if (!IsValid())
+    return LLDB_RECORD_RESULT(lldb::SBCommand());
+  if (!m_opaque_sp->IsMultiwordObject())
+    return LLDB_RECORD_RESULT(lldb::SBCommand());
+  lldb::CommandObjectSP new_command_sp;
+  new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
+      m_opaque_sp->GetCommandInterpreter(), name, impl, help, syntax,
+      0 /*flags*/, auto_repeat);
+  if (new_command_sp && m_opaque_sp->LoadSubCommand(name, new_command_sp))
+    return LLDB_RECORD_RESULT(lldb::SBCommand(new_command_sp));
+  return LLDB_RECORD_RESULT(lldb::SBCommand());
+}
+
 uint32_t SBCommand::GetFlags() {
   LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBCommand, GetFlags);
 
@@ -946,6 +997,9 @@
   LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommandInterpreter, AddCommand,
                        (const char *, lldb::SBCommandPluginInterface *,
                         const char *, const char *));
+  LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommandInterpreter, AddCommand,
+                       (const char *, lldb::SBCommandPluginInterface *,
+                        const char *, const char *, bool));
   LLDB_REGISTER_CONSTRUCTOR(SBCommand, ());
   LLDB_REGISTER_METHOD(bool, SBCommand, IsValid, ());
   LLDB_REGISTER_METHOD_CONST(bool, SBCommand, operator bool, ());
@@ -962,6 +1016,9 @@
   LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommand, AddCommand,
                        (const char *, lldb::SBCommandPluginInterface *,
                         const char *, const char *));
+  LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommand, AddCommand,
+                       (const char *, lldb::SBCommandPluginInterface *,
+                        const char *, const char *, bool));
   LLDB_REGISTER_METHOD(uint32_t, SBCommand, GetFlags, ());
   LLDB_REGISTER_METHOD(void, SBCommand, SetFlags, (uint32_t));
 }
Index: lldb/include/lldb/API/SBCommandInterpreter.h
===================================================================
--- lldb/include/lldb/API/SBCommandInterpreter.h
+++ lldb/include/lldb/API/SBCommandInterpreter.h
@@ -111,14 +111,84 @@
 
   lldb::SBCommand AddMultiwordCommand(const char *name, const char *help);
 
+  /// Add a new command to the lldb::CommandInterpreter.
+  ///
+  /// The new command won't support autorepeat. If you need this functionality,
+  /// use the override of this function that accepts the \a auto_repeat
+  /// parameter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
   lldb::SBCommand AddCommand(const char *name,
                              lldb::SBCommandPluginInterface *impl,
                              const char *help);
 
+  /// Add a new command to the lldb::CommandInterpreter.
+  ///
+  /// The new command won't support autorepeat. If you need this functionality,
+  /// use the override of this function that accepts the \a auto_repeat
+  /// parameter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \param[in] syntax
+  ///     The syntax to show as part of the help message of this command. This
+  ///     could include a description of the different arguments and flags this
+  ///     command accepts.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
   lldb::SBCommand AddCommand(const char *name,
                              lldb::SBCommandPluginInterface *impl,
                              const char *help, const char *syntax);
 
+  /// Add a new command to the lldb::CommandInterpreter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \param[in] syntax
+  ///     The syntax to show as part of the help message of this command. This
+  ///     could include a description of the different arguments and flags this
+  ///     command accepts.
+  ///
+  /// \param[in] auto_repeat
+  ///     Flag that controls whether to accept autorepeating or not.
+  ///     Autorepeating is triggered when the user presses Enter successively
+  ///     after executing a command.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
+  lldb::SBCommand AddCommand(const char *name,
+                             lldb::SBCommandPluginInterface *impl,
+                             const char *help, const char *syntax,
+                             bool auto_repeat);
+
   void SourceInitFileInHomeDirectory(lldb::SBCommandReturnObject &result);
 
   void
@@ -283,14 +353,88 @@
   lldb::SBCommand AddMultiwordCommand(const char *name,
                                       const char *help = nullptr);
 
+  /// Add a new subcommand to the lldb::SBCommand.
+  ///
+  /// The new command won't support autorepeat. If you need this functionality,
+  /// use the override of this function that accepts the \a auto_repeat
+  /// parameter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
   lldb::SBCommand AddCommand(const char *name,
                              lldb::SBCommandPluginInterface *impl,
                              const char *help = nullptr);
 
+  /// Add a new subcommand to the lldb::SBCommand.
+  ///
+  /// The new command won't support autorepeat. If you need this functionality,
+  /// use the override of this function that accepts the \a auto_repeat
+  /// parameter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \param[in] syntax
+  ///     The syntax to show as part of the help message of this command. This
+  ///     could include a description of the different arguments and flags this
+  ///     command accepts.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
   lldb::SBCommand AddCommand(const char *name,
                              lldb::SBCommandPluginInterface *impl,
                              const char *help, const char *syntax);
 
+  /// Add a new subcommand to the lldb::SBCommand.
+  ///
+  /// The new command won't support autorepeat. If you need this functionality,
+  /// use the override of this function that accepts the \a auto_repeat
+  /// parameter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \param[in] syntax
+  ///     The syntax to show as part of the help message of this command. This
+  ///     could include a description of the different arguments and flags this
+  ///     command accepts.
+  ///
+  /// \param[in] auto_repeat
+  ///     Flag that controls whether to accept autorepeating or not.
+  ///     Autorepeating is triggered when the user presses Enter successively
+  ///     after executing a command.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
+  lldb::SBCommand AddCommand(const char *name,
+                             lldb::SBCommandPluginInterface *impl,
+                             const char *help, const char *syntax,
+                             bool auto_repeat);
+
 private:
   friend class SBDebugger;
   friend class SBCommandInterpreter;
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to