tab completion for register read/write

Summary:
1. Created a new common completion for the registers of the current context;
2. Apply this new common completion to the commands register read/write;
3. Unit test.

Reviewers: teemperor, JDevlieghere

Reviewed By: teemperor

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D79490
This commit is contained in:
Gongyu Deng 2020-05-07 18:12:39 +02:00 committed by Raphael Isemann
parent 83564056d4
commit a14f4a7531
4 changed files with 109 additions and 1 deletions

View File

@ -34,10 +34,11 @@ public:
ePlatformPluginCompletion = (1u << 6),
eArchitectureCompletion = (1u << 7),
eVariablePathCompletion = (1u << 8),
eRegisterCompletion = (1u << 9),
// This item serves two purposes. It is the last element in the enum, so
// you can add custom enums starting from here in your Option class. Also
// if you & in this bit the base code will not process the option.
eCustomCompletion = (1u << 9)
eCustomCompletion = (1u << 10)
};
static bool InvokeCommonCompletionCallbacks(
@ -81,6 +82,9 @@ public:
static void VariablePath(CommandInterpreter &interpreter,
CompletionRequest &request, SearchFilter *searcher);
static void Registers(CommandInterpreter &interpreter,
CompletionRequest &request, SearchFilter *searcher);
};
} // namespace lldb_private

View File

@ -18,6 +18,7 @@
#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/Variable.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/TildeExpressionResolver.h"
@ -55,6 +56,7 @@ bool CommandCompletions::InvokeCommonCompletionCallbacks(
{ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames},
{eArchitectureCompletion, CommandCompletions::ArchitectureNames},
{eVariablePathCompletion, CommandCompletions::VariablePath},
{eRegisterCompletion, CommandCompletions::Registers},
{eNoCompletion, nullptr} // This one has to be last in the list.
};
@ -531,3 +533,20 @@ void CommandCompletions::VariablePath(CommandInterpreter &interpreter,
SearchFilter *searcher) {
Variable::AutoComplete(interpreter.GetExecutionContext(), request);
}
void CommandCompletions::Registers(CommandInterpreter &interpreter,
CompletionRequest &request,
SearchFilter *searcher) {
std::string reg_prefix = "";
if (request.GetCursorArgumentPrefix().startswith("$"))
reg_prefix = "$";
RegisterContext *reg_ctx =
interpreter.GetExecutionContext().GetRegisterContext();
const size_t reg_num = reg_ctx->GetRegisterCount();
for (size_t reg_idx = 0; reg_idx < reg_num; ++reg_idx) {
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx);
request.TryCompleteCurrentArg(reg_prefix + reg_info->name,
reg_info->alt_name);
}
}

View File

@ -70,6 +70,17 @@ public:
~CommandObjectRegisterRead() override = default;
void
HandleArgumentCompletion(CompletionRequest &request,
OptionElementVector &opt_element_vector) override {
if (!m_exe_ctx.HasProcessScope())
return;
CommandCompletions::InvokeCommonCompletionCallbacks(
GetCommandInterpreter(), CommandCompletions::eRegisterCompletion,
request, nullptr);
}
Options *GetOptions() override { return &m_option_group; }
bool DumpRegister(const ExecutionContext &exe_ctx, Stream &strm,
@ -323,6 +334,17 @@ public:
~CommandObjectRegisterWrite() override = default;
void
HandleArgumentCompletion(CompletionRequest &request,
OptionElementVector &opt_element_vector) override {
if (!m_exe_ctx.HasProcessScope() || request.GetCursorIndex() != 0)
return;
CommandCompletions::InvokeCommonCompletionCallbacks(
GetCommandInterpreter(), CommandCompletions::eRegisterCompletion,
request, nullptr);
}
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override {
DataExtractor reg_data;

View File

@ -414,3 +414,66 @@ class CommandLineCompletionTestCase(TestBase):
# No completion for Qu because the candidate is
# (anonymous namespace)::Quux().
self.complete_from_to('breakpoint set -n Qu', '')
@skipIf(archs=no_match(['x86_64']))
def test_register_read_and_write_on_x86(self):
"""Test the completion of the commands register read and write on x86"""
# The tab completion for "register read/write" won't work without a running process.
self.complete_from_to('register read ',
'register read ')
self.complete_from_to('register write ',
'register write ')
self.build()
self.main_source_spec = lldb.SBFileSpec("main.cpp")
lldbutil.run_to_source_breakpoint(self, '// Break here', self.main_source_spec)
# test cases for register read
self.complete_from_to('register read ',
['rax',
'rbx',
'rcx'])
self.complete_from_to('register read r',
['rax',
'rbx',
'rcx'])
self.complete_from_to('register read ra',
'register read rax')
# register read can take multiple register names as arguments
self.complete_from_to('register read rax ',
['rax',
'rbx',
'rcx'])
# complete with prefix '$'
self.completions_match('register read $rb',
['$rbx',
'$rbp'])
self.completions_match('register read $ra',
['$rax'])
self.complete_from_to('register read rax $',
['\$rax',
'\$rbx',
'\$rcx'])
self.complete_from_to('register read $rax ',
['rax',
'rbx',
'rcx'])
# test cases for register write
self.complete_from_to('register write ',
['rax',
'rbx',
'rcx'])
self.complete_from_to('register write r',
['rax',
'rbx',
'rcx'])
self.complete_from_to('register write ra',
'register write rax')
self.complete_from_to('register write rb',
['rbx',
'rbp'])
# register write can only take exact one register name as argument
self.complete_from_to('register write rbx ',
[])