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,86 @@ 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_command + /// 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_command + /// 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_command + /// Autorepeating is triggered when the user presses Enter successively + /// after executing a command. If \b nullptr is provided, the previous + /// exact command will be repeated. If \b "" is provided, autorepeating + /// is disabled. Otherwise, the provided string is used as a repeat + /// 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, + const char *auto_repeat_command); + void SourceInitFileInHomeDirectory(lldb::SBCommandReturnObject &result); void @@ -283,14 +355,90 @@ 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_command + /// 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_command + /// 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_command + /// Autorepeating is triggered when the user presses Enter successively + /// after executing a command. If \b nullptr is provided, the previous + /// exact command will be repeated. If \b "" is provided, autorepeating + /// is disabled. Otherwise, the provided string is used as a repeat + /// 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, + const char *auto_repeat_command); + 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,30 @@ lldb::SBCommandPluginInterface *backend, const char *help = nullptr, const char *syntax = nullptr, - uint32_t flags = 0) + uint32_t flags = 0, + const char *auto_repeat_command = "") : CommandObjectParsed(interpreter, name, help, syntax, flags), - m_backend(backend) {} + m_backend(backend) { + m_auto_repeat_command = + auto_repeat_command == nullptr + ? llvm::None + : llvm::Optional(auto_repeat_command); + } 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 { + if (!m_auto_repeat_command) + return nullptr; + else + return m_auto_repeat_command->c_str(); + } + protected: bool DoExecute(Args &command, CommandReturnObject &result) override { SBCommandReturnObject sb_return(result); @@ -170,6 +188,7 @@ return ret; } std::shared_ptr m_backend; + llvm::Optional m_auto_repeat_command; }; SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter) @@ -681,14 +700,8 @@ (const char *, lldb::SBCommandPluginInterface *, const char *), name, impl, help); - lldb::CommandObjectSP new_command_sp; - new_command_sp = std::make_shared( - *m_opaque_ptr, name, impl, help); - - 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()); + return LLDB_RECORD_RESULT(AddCommand(name, impl, help, /*syntax=*/nullptr, + /*auto_repeat_command=*/"")) } lldb::SBCommand @@ -699,10 +712,22 @@ (const char *, lldb::SBCommandPluginInterface *, const char *, const char *), name, impl, help, syntax); + return LLDB_RECORD_RESULT( + AddCommand(name, impl, help, syntax, /*auto_repeat_command=*/"")) +} + +lldb::SBCommand SBCommandInterpreter::AddCommand( + const char *name, lldb::SBCommandPluginInterface *impl, const char *help, + const char *syntax, const char *auto_repeat_command) { + LLDB_RECORD_METHOD(lldb::SBCommand, SBCommandInterpreter, AddCommand, + (const char *, lldb::SBCommandPluginInterface *, + const char *, const char *, const char *), + name, impl, help, syntax, auto_repeat_command); lldb::CommandObjectSP new_command_sp; new_command_sp = std::make_shared( - *m_opaque_ptr, name, impl, help, syntax); + *m_opaque_ptr, name, impl, help, syntax, /*flags=*/0, + auto_repeat_command); if (new_command_sp && m_opaque_ptr->AddUserCommand(name, new_command_sp, true)) @@ -783,17 +808,8 @@ lldb::SBCommand, SBCommand, AddCommand, (const char *, lldb::SBCommandPluginInterface *, const char *), name, impl, help); - - 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); - 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()); + return LLDB_RECORD_RESULT(AddCommand(name, impl, help, /*syntax=*/nullptr, + /*auto_repeat_command=*/"")) } lldb::SBCommand SBCommand::AddCommand(const char *name, @@ -803,6 +819,18 @@ (const char *, lldb::SBCommandPluginInterface *, const char *, const char *), name, impl, help, syntax); + return LLDB_RECORD_RESULT( + AddCommand(name, impl, help, syntax, /*auto_repeat_command=*/"")) +} + +lldb::SBCommand SBCommand::AddCommand(const char *name, + lldb::SBCommandPluginInterface *impl, + const char *help, const char *syntax, + const char *auto_repeat_command) { + LLDB_RECORD_METHOD(lldb::SBCommand, SBCommand, AddCommand, + (const char *, lldb::SBCommandPluginInterface *, + const char *, const char *, const char *), + name, impl, help, syntax, auto_repeat_command); if (!IsValid()) return LLDB_RECORD_RESULT(lldb::SBCommand()); @@ -810,7 +838,8 @@ 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); + m_opaque_sp->GetCommandInterpreter(), name, impl, help, syntax, + /*flags=*/0, auto_repeat_command); 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()); @@ -946,6 +975,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 *, const char *)); LLDB_REGISTER_CONSTRUCTOR(SBCommand, ()); LLDB_REGISTER_METHOD(bool, SBCommand, IsValid, ()); LLDB_REGISTER_METHOD_CONST(bool, SBCommand, operator bool, ()); @@ -962,6 +994,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 *, const char *)); LLDB_REGISTER_METHOD(uint32_t, SBCommand, GetFlags, ()); LLDB_REGISTER_METHOD(void, SBCommand, SetFlags, (uint32_t)); } diff --git a/lldb/unittests/API/CMakeLists.txt b/lldb/unittests/API/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/unittests/API/CMakeLists.txt @@ -0,0 +1,6 @@ +add_lldb_unittest(APITests + TestSBCommandInterpreterTest.cpp + + LINK_LIBS + liblldb + ) diff --git a/lldb/unittests/API/TestSBCommandInterpreterTest.cpp b/lldb/unittests/API/TestSBCommandInterpreterTest.cpp new file mode 100644 --- /dev/null +++ b/lldb/unittests/API/TestSBCommandInterpreterTest.cpp @@ -0,0 +1,138 @@ +//===-- TestSBCommandInterpreterTest.cpp ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===/ + +#include "gtest/gtest.h" + +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBDebugger.h" + +#include +#include + +using namespace lldb; + +class TestSBCommandInterpreterTest : public testing::Test { +protected: + void SetUp() override { + SBDebugger::Initialize(); + m_dbg = SBDebugger::Create(/*source_init_files=*/false); + m_interp = m_dbg.GetCommandInterpreter(); + } + + SBDebugger m_dbg; + SBCommandInterpreter m_interp; +}; + +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; +}; + +TEST_F(TestSBCommandInterpreterTest, SingleWordCommand) { + // We first test a command without autorepeat + DummyCommand dummy("It worked"); + m_interp.AddCommand("dummy", &dummy, /*help=*/nullptr); + { + SBCommandReturnObject result; + m_interp.HandleCommand("dummy", result, /*add_to_history=*/true); + EXPECT_TRUE(result.Succeeded()); + EXPECT_STREQ(result.GetOutput(), "It worked\n"); + } + { + SBCommandReturnObject result; + m_interp.HandleCommand("", result); + EXPECT_FALSE(result.Succeeded()); + EXPECT_STREQ(result.GetError(), "error: No auto repeat.\n"); + } + + // Now we test a command with autorepeat + m_interp.AddCommand("dummy_with_autorepeat", &dummy, /*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"); + } + { + SBCommandReturnObject result; + m_interp.HandleCommand("", result); + EXPECT_TRUE(result.Succeeded()); + EXPECT_STREQ(result.GetOutput(), "It worked\n"); + } +} + +TEST_F(TestSBCommandInterpreterTest, 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); + { + SBCommandReturnObject result; + m_interp.HandleCommand("multicommand subcommand", result, + /*add_to_history=*/true); + EXPECT_TRUE(result.Succeeded()); + EXPECT_STREQ(result.GetOutput(), "It worked again\n"); + } + { + SBCommandReturnObject result; + m_interp.HandleCommand("", result); + EXPECT_FALSE(result.Succeeded()); + EXPECT_STREQ(result.GetError(), "error: No auto repeat.\n"); + } + + // We first test a subcommand with autorepeat + command.AddCommand("subcommand_with_autorepeat", &subcommand, + /*help=*/nullptr, /*syntax=*/nullptr, + /*auto_repeat_command=*/nullptr); + { + SBCommandReturnObject result; + m_interp.HandleCommand("multicommand subcommand_with_autorepeat", result, + /*add_to_history=*/true); + EXPECT_TRUE(result.Succeeded()); + EXPECT_STREQ(result.GetOutput(), "It worked again\n"); + } + { + SBCommandReturnObject result; + m_interp.HandleCommand("", result); + EXPECT_TRUE(result.Succeeded()); + EXPECT_STREQ(result.GetOutput(), "It worked again\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, + /*syntax=*/nullptr, + /*auto_repeat_command=*/"multicommand subcommand_with_autorepeat"); + { + SBCommandReturnObject result; + 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"); + } + { + SBCommandReturnObject result; + m_interp.HandleCommand("", result); + EXPECT_TRUE(result.Succeeded()); + EXPECT_STREQ(result.GetOutput(), "It worked again\n"); + } +} diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt --- a/lldb/unittests/CMakeLists.txt +++ b/lldb/unittests/CMakeLists.txt @@ -59,6 +59,7 @@ endfunction() add_subdirectory(TestingSupport) +add_subdirectory(API) add_subdirectory(Breakpoint) add_subdirectory(Core) add_subdirectory(DataFormatter)