[commands] Support autorepeat in SBCommands

Summary:
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.

Reviewers: labath, clayborg

Subscribers: lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D77444
This commit is contained in:
Walter Erquinigo 2020-04-03 17:16:15 -07:00
parent 8e40987e18
commit be3f8a8e1b
5 changed files with 351 additions and 23 deletions

View File

@ -111,14 +111,86 @@ public:
lldb::SBCommand AddMultiwordCommand(const char *name, const char *help); 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::SBCommand AddCommand(const char *name,
lldb::SBCommandPluginInterface *impl, lldb::SBCommandPluginInterface *impl,
const char *help); 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::SBCommand AddCommand(const char *name,
lldb::SBCommandPluginInterface *impl, lldb::SBCommandPluginInterface *impl,
const char *help, const char *syntax); 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 SourceInitFileInHomeDirectory(lldb::SBCommandReturnObject &result);
void void
@ -283,14 +355,90 @@ public:
lldb::SBCommand AddMultiwordCommand(const char *name, lldb::SBCommand AddMultiwordCommand(const char *name,
const char *help = nullptr); 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::SBCommand AddCommand(const char *name,
lldb::SBCommandPluginInterface *impl, lldb::SBCommandPluginInterface *impl,
const char *help = nullptr); 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::SBCommand AddCommand(const char *name,
lldb::SBCommandPluginInterface *impl, lldb::SBCommandPluginInterface *impl,
const char *help, const char *syntax); 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: private:
friend class SBDebugger; friend class SBDebugger;
friend class SBCommandInterpreter; friend class SBCommandInterpreter;

View File

@ -154,12 +154,30 @@ public:
lldb::SBCommandPluginInterface *backend, lldb::SBCommandPluginInterface *backend,
const char *help = nullptr, const char *help = nullptr,
const char *syntax = nullptr, const char *syntax = nullptr,
uint32_t flags = 0) uint32_t flags = 0,
const char *auto_repeat_command = "")
: CommandObjectParsed(interpreter, name, help, syntax, flags), : CommandObjectParsed(interpreter, name, help, syntax, flags),
m_backend(backend) {} m_backend(backend) {
m_auto_repeat_command =
auto_repeat_command == nullptr
? llvm::None
: llvm::Optional<std::string>(auto_repeat_command);
}
bool IsRemovable() const override { return true; } 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 {
if (!m_auto_repeat_command)
return nullptr;
else
return m_auto_repeat_command->c_str();
}
protected: protected:
bool DoExecute(Args &command, CommandReturnObject &result) override { bool DoExecute(Args &command, CommandReturnObject &result) override {
SBCommandReturnObject sb_return(result); SBCommandReturnObject sb_return(result);
@ -170,6 +188,7 @@ protected:
return ret; return ret;
} }
std::shared_ptr<lldb::SBCommandPluginInterface> m_backend; std::shared_ptr<lldb::SBCommandPluginInterface> m_backend;
llvm::Optional<std::string> m_auto_repeat_command;
}; };
SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter) SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter)
@ -681,14 +700,8 @@ lldb::SBCommand SBCommandInterpreter::AddCommand(
(const char *, lldb::SBCommandPluginInterface *, const char *), name, (const char *, lldb::SBCommandPluginInterface *, const char *), name,
impl, help); impl, help);
lldb::CommandObjectSP new_command_sp; return LLDB_RECORD_RESULT(AddCommand(name, impl, help, /*syntax=*/nullptr,
new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>( /*auto_repeat_command=*/""))
*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());
} }
lldb::SBCommand lldb::SBCommand
@ -699,10 +712,22 @@ SBCommandInterpreter::AddCommand(const char *name,
(const char *, lldb::SBCommandPluginInterface *, (const char *, lldb::SBCommandPluginInterface *,
const char *, const char *), const char *, const char *),
name, impl, help, syntax); 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; lldb::CommandObjectSP new_command_sp;
new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>( new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
*m_opaque_ptr, name, impl, help, syntax); *m_opaque_ptr, name, impl, help, syntax, /*flags=*/0,
auto_repeat_command);
if (new_command_sp && if (new_command_sp &&
m_opaque_ptr->AddUserCommand(name, new_command_sp, true)) m_opaque_ptr->AddUserCommand(name, new_command_sp, true))
@ -783,17 +808,8 @@ lldb::SBCommand SBCommand::AddCommand(const char *name,
lldb::SBCommand, SBCommand, AddCommand, lldb::SBCommand, SBCommand, AddCommand,
(const char *, lldb::SBCommandPluginInterface *, const char *), name, (const char *, lldb::SBCommandPluginInterface *, const char *), name,
impl, help); impl, help);
return LLDB_RECORD_RESULT(AddCommand(name, impl, help, /*syntax=*/nullptr,
if (!IsValid()) /*auto_repeat_command=*/""))
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);
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());
} }
lldb::SBCommand SBCommand::AddCommand(const char *name, lldb::SBCommand SBCommand::AddCommand(const char *name,
@ -803,6 +819,18 @@ lldb::SBCommand SBCommand::AddCommand(const char *name,
(const char *, lldb::SBCommandPluginInterface *, (const char *, lldb::SBCommandPluginInterface *,
const char *, const char *), const char *, const char *),
name, impl, help, syntax); 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()) if (!IsValid())
return LLDB_RECORD_RESULT(lldb::SBCommand()); return LLDB_RECORD_RESULT(lldb::SBCommand());
@ -810,7 +838,8 @@ lldb::SBCommand SBCommand::AddCommand(const char *name,
return LLDB_RECORD_RESULT(lldb::SBCommand()); return LLDB_RECORD_RESULT(lldb::SBCommand());
lldb::CommandObjectSP new_command_sp; lldb::CommandObjectSP new_command_sp;
new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>( new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
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)) 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(new_command_sp));
return LLDB_RECORD_RESULT(lldb::SBCommand()); return LLDB_RECORD_RESULT(lldb::SBCommand());
@ -946,6 +975,9 @@ void RegisterMethods<SBCommandInterpreterRunOptions>(Registry &R) {
LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommandInterpreter, AddCommand, LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommandInterpreter, AddCommand,
(const char *, lldb::SBCommandPluginInterface *, (const char *, lldb::SBCommandPluginInterface *,
const char *, const char *)); 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_CONSTRUCTOR(SBCommand, ());
LLDB_REGISTER_METHOD(bool, SBCommand, IsValid, ()); LLDB_REGISTER_METHOD(bool, SBCommand, IsValid, ());
LLDB_REGISTER_METHOD_CONST(bool, SBCommand, operator bool, ()); LLDB_REGISTER_METHOD_CONST(bool, SBCommand, operator bool, ());
@ -962,6 +994,9 @@ void RegisterMethods<SBCommandInterpreterRunOptions>(Registry &R) {
LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommand, AddCommand, LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommand, AddCommand,
(const char *, lldb::SBCommandPluginInterface *, (const char *, lldb::SBCommandPluginInterface *,
const char *, const char *)); 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(uint32_t, SBCommand, GetFlags, ());
LLDB_REGISTER_METHOD(void, SBCommand, SetFlags, (uint32_t)); LLDB_REGISTER_METHOD(void, SBCommand, SetFlags, (uint32_t));
} }

View File

@ -0,0 +1,6 @@
add_lldb_unittest(APITests
TestSBCommandInterpreterTest.cpp
LINK_LIBS
liblldb
)

View File

@ -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 <cstring>
#include <string>
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");
}
}

View File

@ -59,6 +59,7 @@ function(add_unittest_inputs test_name inputs)
endfunction() endfunction()
add_subdirectory(TestingSupport) add_subdirectory(TestingSupport)
add_subdirectory(API)
add_subdirectory(Breakpoint) add_subdirectory(Breakpoint)
add_subdirectory(Core) add_subdirectory(Core)
add_subdirectory(DataFormatter) add_subdirectory(DataFormatter)