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 @@ -241,13 +241,13 @@ lldb::SBStringList &matches, lldb::SBStringList &descriptions); - /// Returns whether an interrupt flag was raised either by the SBDebugger - + /// Returns whether an interrupt flag was raised either by the SBDebugger - /// when the function is not running on the RunCommandInterpreter thread, or /// by SBCommandInterpreter::InterruptCommand if it is. If your code is doing - /// interruptible work, check this API periodically, and interrupt if it + /// interruptible work, check this API periodically, and interrupt if it /// returns true. bool WasInterrupted() const; - + /// Interrupts the command currently executing in the RunCommandInterpreter /// thread. /// @@ -313,6 +313,25 @@ /// and aliases. If successful, result->GetOutput has the full expansion. void ResolveCommand(const char *command_line, SBCommandReturnObject &result); + /// Remove a command if it is removable (python or regex command). If \b force + /// is provided, the command is removed regardless of its removable status. + /// + /// \return + /// \b true if and only if the command was effectively removed. + bool RemoveCommand(const char *command, bool force = false); + + /// Remove a user-defined command. + /// + /// \return + /// \b true if and only if the command was effectively removed. + bool RemoveUserCommand(const char *command); + + /// Remove an alias command. + /// + /// \return + /// \b true if and only if the alias was effectively removed. + bool RemoveAlias(const char *alias); + protected: lldb_private::CommandInterpreter &ref(); 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 @@ -132,6 +132,30 @@ : false); } +bool SBCommandInterpreter::RemoveCommand(const char *command, bool force) { + LLDB_INSTRUMENT_VA(this, command, force); + + if (!IsValid()) + return false; + return m_opaque_ptr->RemoveCommand(command, force); +} + +bool SBCommandInterpreter::RemoveUserCommand(const char *command) { + LLDB_INSTRUMENT_VA(this, command); + + if (!IsValid()) + return false; + return m_opaque_ptr->RemoveUser(command); +} + +bool SBCommandInterpreter::RemoveAlias(const char *alias) { + LLDB_INSTRUMENT_VA(this, alias); + + if (!IsValid()) + return false; + return m_opaque_ptr->RemoveAlias(alias); +} + bool SBCommandInterpreter::IsActive() { LLDB_INSTRUMENT_VA(this); @@ -146,7 +170,7 @@ bool SBCommandInterpreter::InterruptCommand() { LLDB_INSTRUMENT_VA(this); - + return (IsValid() ? m_opaque_ptr->InterruptCommand() : false); } diff --git a/lldb/unittests/API/SBCommandInterpreterTest.cpp b/lldb/unittests/API/SBCommandInterpreterTest.cpp --- a/lldb/unittests/API/SBCommandInterpreterTest.cpp +++ b/lldb/unittests/API/SBCommandInterpreterTest.cpp @@ -44,15 +44,41 @@ std::string m_message; }; +TEST_F(SBCommandInterpreterTest, RemoveCommandsAndAliases) { + // We first test removing non-existent commands + EXPECT_FALSE(m_interp.RemoveUserCommand("non-existent-user-command")); + EXPECT_FALSE(m_interp.RemoveCommand("non-existent-command")); + EXPECT_FALSE(m_interp.RemoveAlias("non-existent-alias")); + + // We now test removing a built-in command + EXPECT_TRUE(m_interp.CommandExists("target")); + EXPECT_FALSE(m_interp.RemoveCommand("target")); + // Built-in commands need to be force removed. + EXPECT_TRUE(m_interp.RemoveCommand("target", /*force=*/true)); + EXPECT_FALSE(m_interp.CommandExists("target")); + + // Finally we test with a user defined command + m_interp.AddCommand("dummy", new DummyCommand("A dummy command"), + /*help=*/nullptr); + EXPECT_TRUE(m_interp.UserCommandExists("dummy")); + EXPECT_TRUE(m_interp.RemoveUserCommand("dummy")); + EXPECT_FALSE(m_interp.UserCommandExists("dummy")); + + // Now we remove an alias + EXPECT_TRUE(m_interp.AliasExists("b")); + EXPECT_TRUE(m_interp.RemoveAlias("b")); + EXPECT_FALSE(m_interp.AliasExists("b")); +} + TEST_F(SBCommandInterpreterTest, SingleWordCommand) { // We first test a command without autorepeat - DummyCommand dummy("It worked"); - m_interp.AddCommand("dummy", &dummy, /*help=*/nullptr); + m_interp.AddCommand("dummy", new DummyCommand("Dummy command"), + /*help=*/nullptr); { SBCommandReturnObject result; m_interp.HandleCommand("dummy", result, /*add_to_history=*/true); EXPECT_TRUE(result.Succeeded()); - EXPECT_STREQ(result.GetOutput(), "It worked\n"); + EXPECT_STREQ(result.GetOutput(), "Dummy command\n"); } { SBCommandReturnObject result; @@ -62,34 +88,36 @@ } // Now we test a command with autorepeat - m_interp.AddCommand("dummy_with_autorepeat", &dummy, /*help=*/nullptr, + m_interp.AddCommand("dummy_with_autorepeat", + new DummyCommand("Command with autorepeat"), + /*help=*/nullptr, /*syntax=*/nullptr, /*auto_repeat_command=*/nullptr); { SBCommandReturnObject result; m_interp.HandleCommand("dummy_with_autorepeat", result, /*add_to_history=*/true); EXPECT_TRUE(result.Succeeded()); - EXPECT_STREQ(result.GetOutput(), "It worked\n"); + EXPECT_STREQ(result.GetOutput(), "Command with autorepeat\n"); } { SBCommandReturnObject result; m_interp.HandleCommand("", result); EXPECT_TRUE(result.Succeeded()); - EXPECT_STREQ(result.GetOutput(), "It worked\n"); + EXPECT_STREQ(result.GetOutput(), "Command with autorepeat\n"); } } TEST_F(SBCommandInterpreterTest, MultiWordCommand) { auto command = m_interp.AddMultiwordCommand("multicommand", /*help=*/nullptr); // We first test a subcommand without autorepeat - DummyCommand subcommand("It worked again"); - command.AddCommand("subcommand", &subcommand, /*help=*/nullptr); + command.AddCommand("subcommand", new DummyCommand("Dummy command"), + /*help=*/nullptr); { SBCommandReturnObject result; m_interp.HandleCommand("multicommand subcommand", result, /*add_to_history=*/true); EXPECT_TRUE(result.Succeeded()); - EXPECT_STREQ(result.GetOutput(), "It worked again\n"); + EXPECT_STREQ(result.GetOutput(), "Dummy command\n"); } { SBCommandReturnObject result; @@ -99,7 +127,8 @@ } // We first test a subcommand with autorepeat - command.AddCommand("subcommand_with_autorepeat", &subcommand, + command.AddCommand("subcommand_with_autorepeat", + new DummyCommand("Dummy command with autorepeat"), /*help=*/nullptr, /*syntax=*/nullptr, /*auto_repeat_command=*/nullptr); { @@ -107,19 +136,19 @@ m_interp.HandleCommand("multicommand subcommand_with_autorepeat", result, /*add_to_history=*/true); EXPECT_TRUE(result.Succeeded()); - EXPECT_STREQ(result.GetOutput(), "It worked again\n"); + EXPECT_STREQ(result.GetOutput(), "Dummy command with autorepeat\n"); } { SBCommandReturnObject result; m_interp.HandleCommand("", result); EXPECT_TRUE(result.Succeeded()); - EXPECT_STREQ(result.GetOutput(), "It worked again\n"); + EXPECT_STREQ(result.GetOutput(), "Dummy command with autorepeat\n"); } - DummyCommand subcommand2("It worked again 2"); // We now test a subcommand with autorepeat of the command name command.AddCommand( - "subcommand_with_custom_autorepeat", &subcommand2, /*help=*/nullptr, + "subcommand_with_custom_autorepeat", + new DummyCommand("Another dummy command"), /*help=*/nullptr, /*syntax=*/nullptr, /*auto_repeat_command=*/"multicommand subcommand_with_autorepeat"); { @@ -127,12 +156,12 @@ m_interp.HandleCommand("multicommand subcommand_with_custom_autorepeat", result, /*add_to_history=*/true); EXPECT_TRUE(result.Succeeded()); - EXPECT_STREQ(result.GetOutput(), "It worked again 2\n"); + EXPECT_STREQ(result.GetOutput(), "Another dummy command\n"); } { SBCommandReturnObject result; m_interp.HandleCommand("", result); EXPECT_TRUE(result.Succeeded()); - EXPECT_STREQ(result.GetOutput(), "It worked again\n"); + EXPECT_STREQ(result.GetOutput(), "Dummy command with autorepeat\n"); } }