diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h --- a/lldb/include/lldb/API/SBCommandInterpreter.h +++ b/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; diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp --- a/lldb/source/API/SBCommandInterpreter.cpp +++ b/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 ¤t_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 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( + *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( + 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)); } diff --git a/lldb/test/API/api/auto-repeat-command/Makefile b/lldb/test/API/api/auto-repeat-command/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/api/auto-repeat-command/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/api/auto-repeat-command/TestSBCommandAutoRepeat.py b/lldb/test/API/api/auto-repeat-command/TestSBCommandAutoRepeat.py new file mode 100644 --- /dev/null +++ b/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) diff --git a/lldb/test/API/api/auto-repeat-command/main.cpp b/lldb/test/API/api/auto-repeat-command/main.cpp new file mode 100644 --- /dev/null +++ b/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 +#include +#include + +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); +}